准备使用 Rust

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

  1. .section .init.entry, "ax"
  2. .global entry
  3. entry:
  4. /*
  5. * Load and apply the memory management configuration, ready to enable MMU and
  6. * caches.
  7. */
  8. adrp x30, idmap
  9. msr ttbr0_el1, x30
  10. mov_i x30, .Lmairval
  11. msr mair_el1, x30
  12. mov_i x30, .Ltcrval
  13. /* Copy the supported PA range into TCR_EL1.IPS.*/
  14. mrs x29, id_aa64mmfr0_el1
  15. bfi x30, x29, #32, #4
  16. msr tcr_el1, x30
  17. mov_i x30, .Lsctlrval
  18. /*
  19. * Ensure everything before this point has completed, then invalidate any
  20. * potentially stale local TLB entries before they start being used.
  21. */
  22. isb
  23. tlbi vmalle1
  24. ic iallu
  25. dsb nsh
  26. isb
  27. /*
  28. * Configure sctlr_el1 to enable MMU and cache and don't proceed until this
  29. * has completed.
  30. */
  31. msr sctlr_el1, x30
  32. isb
  33. /* Disable trapping floating point access in EL1.*/
  34. mrs x30, cpacr_el1
  35. orr x30, x30, #(0x3 << 20)
  36. msr cpacr_el1, x30
  37. isb
  38. /* Zero out the bss section.*/
  39. adr_l x29, bss_begin
  40. adr_l x30, bss_end
  41. 0: cmp x29, x30
  42. b.hs 1f
  43. stp xzr, xzr, [x29], #16
  44. b 0b
  45. 1: /* Prepare the stack.*/
  46. adr_l x30, boot_stack_end
  47. mov sp, x30
  48. /* Set up exception vector.*/
  49. adr x30, vector_table_el1
  50. msr vbar_el1, x30
  51. /* Call into Rust code.*/
  52. bl main
  53. /* Loop forever waiting for interrupts.*/
  54. 2: wfi
  55. 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