Back to Comprehensive Rust

Getting Ready to Rust

src/bare-metal/aps/entry-point.md

latest2.4 KB
Original Source
<!-- Copyright 2023 Google LLC SPDX-License-Identifier: CC-BY-4.0 -->

Getting Ready to Rust

Before we can start running Rust code, we need to do some initialization.

armasm
{{#include examples/src/entry.S:entry}}
<details>

This code is in src/bare-metal/aps/examples/src/entry.S. It's not necessary to understand this in detail -- the takeaway is that some low-level setup is needed to meet Rust's expectations of the system.

  • This is the same as it would be for C: initializing the processor state, zeroing the BSS, and setting up the stack pointer.
    • The BSS (block starting symbol, for historical reasons) is the part of the object file that contains statically allocated variables that are initialized to zero. They are omitted from the image, to avoid wasting space on zeroes. The compiler assumes that the loader will take care of zeroing them.
  • The BSS may already be zeroed, depending on how memory is initialized and the image is loaded, but we zero it to be sure.
  • We need to enable the MMU and cache before reading or writing any memory. If we don't:
    • Unaligned accesses will fault. We build the Rust code for the aarch64-unknown-none target that sets +strict-align to prevent the compiler from generating unaligned accesses, so it should be fine in this case, but this is not necessarily the case in general.
    • If it were running in a VM, this can lead to cache coherency issues. The problem is that the VM is accessing memory directly with the cache disabled, while the host has cacheable aliases to the same memory. Even if the host doesn't explicitly access the memory, speculative accesses can lead to cache fills, and then changes from one or the other will get lost when the cache is cleaned or the VM enables the cache. (Cache is keyed by physical address, not VA or IPA.)
  • For simplicity, we just use a hardcoded pagetable (see idmap.S) that identity maps the first 1 GiB of address space for devices, the next 1 GiB for DRAM, and another 1 GiB higher up for more devices. This matches the memory layout that QEMU uses.
  • We also set up the exception vector (vbar_el1), which we'll see more about later.
  • All examples this afternoon assume we will be running at exception level 1 (EL1). If you need to run at a different exception level, you'll need to modify entry.S accordingly.
</details>