Ensamblaje integrado

Sometimes we need to use assembly to do things that aren't possible with Rust code. For example, to make an HVC (hypervisor call) to tell the firmware to power off the system:

#![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 {}
}

(Si realmente quieres hacer esto, utiliza el crate smccc que tiene envoltorios para todas estas funciones).

  • PSCI es la interfaz de coordinación de estado de alimentación de Arm, un conjunto estándar de funciones para gestionar los estados de alimentación del sistema y de la CPU, entre otras cosas. Lo implementan el firmware EL3 y los hipervisores en muchos sistemas.
  • La sintaxis 0 => _ significa inicializar el registro a 0 antes de ejecutar el código de ensamblaje integrado e ignorar su contenido después. Necesitamos utilizar inout en lugar de in porque la llamada podría alterar el contenido de los registros.
  • Esta función main debe ser #[no_mangle] y extern "C", ya que se llama desde nuestro punto de entrada en entry.S.
  • _x0_x3 son los valores de los registros x0x3, que el bootloader utiliza habitualmente para pasar elementos al árbol de dispositivos, como un puntero. De acuerdo con la convención de llamadas estándar de aarch64 (que es lo que extern "C" usa), los registros x0x7 se utilizan para los primeros ocho argumentos que se pasan a una función, de modo que entry.S no tiene que hacer nada especial, salvo asegurarse de que no cambia estos registros.
  • Ejecuta el ejemplo en QEMU con make qemu_psci en src/bare-metal/aps/examples.