인라인 어셈블리
가끔 Rust 코드로는 구현이 불가능한 작업들이 있으며, 이 경우 어셈블리를 사용해야 합니다. 예를 들어 펌웨어를 향해서 시스템 전원을 끄라고 HVC를 호출하는 방법은 다음과 같습니다.
#![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) { // Safe because 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
크레이트를 사용하세요.
- PSCI (Power State Coordination Interface)는 시스템 및 CPU 전원 상태를 관리하는 Arm의 표준 인터페이스입니다. 이 인터페이스는 EL3 펌웨어와 하이퍼바이저에 의해 구현됩니다.
0 => _
문법은 인라인 어셈블리 코드를 실행하기 전에 레지스터를 0으로 초기화하고 그 후에는 그 레지스터의 값을 무시함을 의미합니다. 호출 시 레지스터의 값이 덮어 써질 수 있으므로in
대신inout
을 사용해야 합니다.- 이
main
함수는#[no_mangle]
및extern "C"
여야 합니다. 왜냐하면 이 함수는 Rust 코드가 아닌, 어셈블러로 작성된entry.S
에서 호출되기 때문입니다. _x0
–_x3
는x0
에서x3
레지스터들의 값입니다. 이 레지스터들은 일반적으로 부트로더에서 디바이스 트리에 대한 포인터 등을 전달할 때 사용됩니다. 표준 aarch64 호출 규약(extern "C"
에서 사용하도록 지정)에 따라 레지스터x0
에서x7
이 함수에 전달된 처음 8개 인수에 사용되므로entry.S
는 이러한 레지스터를 변경하지 않는지 확인하는 것 외에는 특별히 할 작업이 없습니다.src/bare-metal/aps/examples
에서make qemu_psci
를 사용하여 QEMU에서 예시를 실행합니다.