内嵌汇编

有时,如果无法通过 Rust 代码实现某些操作,我们就需要使用汇编来解决。例如,如需发出 HVC(Hypervisor 调用)来指示固件关闭系统,请使用以下命令:

  1. #![no_main]
  2. #![no_std]
  3. use core::arch::asm;
  4. use core::panic::PanicInfo;
  5. mod exceptions;
  6. const PSCI_SYSTEM_OFF: u32 = 0x84000008;
  7. #[no_mangle]
  8. extern "C" fn main(_x0: u64, _x1: u64, _x2: u64, _x3: u64) {
  9. // SAFETY: this only uses the declared registers and doesn't do anything
  10. // with memory.
  11. unsafe {
  12. asm!("hvc #0",
  13. inout("w0") PSCI_SYSTEM_OFF => _,
  14. inout("w1") 0 => _,
  15. inout("w2") 0 => _,
  16. inout("w3") 0 => _,
  17. inout("w4") 0 => _,
  18. inout("w5") 0 => _,
  19. inout("w6") 0 => _,
  20. inout("w7") 0 => _,
  21. options(nomem, nostack)
  22. );
  23. }
  24. loop {}
  25. }

(如果确实想要这样做,请使用 smccc crate,其中包含适用于所有这些函数的封装容器。)

  • PSCI 是 Arm 电源状态协调接口,为一组标准函数,用于管理系统和 CPU 电源状态等。在许多系统中,通过 EL3 固件和 Hypervisor 来实现该函数。
  • 0 => _ 语法表示在运行内嵌汇编代码之前将寄存器初始化为 0,并在之后忽略寄存器中的内容。我们需要使用 inout 而非 in,因为该调用操作可能会破坏寄存器中的内容。
  • 所用 main 函数必须是 #[no_mangle]extern "C",因为是从 entry.S 中的入口点调用该函数。
  • _x0_x3 表示寄存器 x0-x3 的值,引导加载程序通常使用这些值来传递各种内容(例如将指针传递到设备树)。根据标准的 aarch64 调用规范(extern "C"指定使用此规范),需要使用寄存器 x0-x7 将前 8 个参数传递给函数,因此 entry.S 无需执行任何特殊操作,只要确保不会更改这些寄存器。
  • 在 QEMU 中,使用 src/bare-metal/aps/examples 目录下的 make qemu_psci 运行该示例。