准备使用 Rust

在开始运行 Rust 代码之前,我们需要进行一些初始化设置。

.section .init.entry, "ax"
.global entry
entry:
    /*
     * Load and apply the memory management configuration, ready to enable MMU and
     * caches.
     */
    adrp x30, idmap
    msr ttbr0_el1, x30

    mov_i x30, .Lmairval
    msr mair_el1, x30

    mov_i x30, .Ltcrval
    /* Copy the supported PA range into TCR_EL1.IPS.*/
    mrs x29, id_aa64mmfr0_el1
    bfi x30, x29, #32, #4

    msr tcr_el1, x30

    mov_i x30, .Lsctlrval

    /*
     * Ensure everything before this point has completed, then invalidate any
     * potentially stale local TLB entries before they start being used.
     */
    isb
    tlbi vmalle1
    ic iallu
    dsb nsh
    isb

    /*
     * Configure sctlr_el1 to enable MMU and cache and don't proceed until this
     * has completed.
     */
    msr sctlr_el1, x30
    isb

    /* Disable trapping floating point access in EL1.*/
    mrs x30, cpacr_el1
    orr x30, x30, #(0x3 << 20)
    msr cpacr_el1, x30
    isb

    /* Zero out the bss section.*/
    adr_l x29, bss_begin
    adr_l x30, bss_end
0:  cmp x29, x30
    b.hs 1f
    stp xzr, xzr, [x29], #16
    b 0b

1:  /* Prepare the stack.*/
    adr_l x30, boot_stack_end
    mov sp, x30

    /* Set up exception vector.*/
    adr x30, vector_table_el1
    msr vbar_el1, x30

    /* Call into Rust code.*/
    bl main

    /* Loop forever waiting for interrupts.*/
2:  wfi
    b 2b
  • 这与 C 语言的情况相同:初始化处理器状态,将 BSS 清零,然后设置堆栈指针。
    • BSS(由于历史原因,称为代码块起始符)属于对象文件的一部分,其中包含静态分配的变量,这些变量被初始化为零。图像中省略了这些符号,以避免因存储零值而占用过多空间。编译器假定加载器会负责将它们清零。
  • BSS 可能已经被清零,具体取决于内存的初始化方式以及图像的加载方式,但为了确保起见,我们会将其手动清零。
  • 我们需要先启用 MMU 和缓存功能,然后才能读取或写入任何内存。否则:
    • 非对齐访问将会出错。我们为 aarch64-unknown-none 目标构建 Rust 代码,该目标会设置 +Strict-align 以防止编译器生成非对齐访问,因此在本例中应该没有问题,但一般情况下并不一定如此。
    • 如果是在虚拟机中运行该命令,可能会导致缓存一致性问题。问题在于,虚拟机是在禁用缓存的情况下直接访问内存,而主机具有同一内存的缓存别名。即使主机并没有明确访问该内存,推测性访问仍然会导致缓存被填充,然后在清除缓存或虚拟机启用缓存时,任何一方对于该内存进行的更改就会丢失。(使用物理地址来键控缓存,而 VA 或 IPA。)
  • 为简单起见,我们只使用硬编码的分页表(请参阅 dmap.S),其通过身份映射将前一个 1 GiB 的地址空间用于设备,紧接着的 1 GiB 用于 DRAM,然后在更高位置预留了 1 GiB 给其他设备。这与 QEMU 使用的内存布局一致。
  • 我们还设置了异常矢量 (vbar_el1),稍后会对此进行详细介绍。
  • 今天下午的所有示例都假定我们将在异常级别 1 (EL1) 下运行。如果您需要在其他异常级别下运行,则需要修改相应的 entry.S