内嵌汇编

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

#![no_main]
#![no_std]

use core::arch::asm;
use core::panic::PanicInfo;

mod exceptions;

const PSCI_SYSTEM_OFF: u32 = 0x84000008;

#[no_mangle]
extern "C" fn main(_x0: u64, _x1: u64, _x2: u64, _x3: u64) {
    // SAFETY: this only uses the declared registers and doesn't do anything
    // with memory.
    unsafe {
        asm!("hvc #0",
            inout("w0") PSCI_SYSTEM_OFF => _,
            inout("w1") 0 => _,
            inout("w2") 0 => _,
            inout("w3") 0 => _,
            inout("w4") 0 => _,
            inout("w5") 0 => _,
            inout("w6") 0 => _,
            inout("w7") 0 => _,
            options(nomem, nostack)
        );
    }

    loop {}
}

(如果确实想要这样做,请使用 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 运行该示例。