インラインアセンブリ
時折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) { // 宣言されたレジスタのみを使用し、メモリについては // 何もしないため、安全です。 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 はArmのPower State Coordination Interfaceのことであり、これはシステムやCPU電力状態管理の機能を含む標準的なセットです。これは多くのシステムでEL3ファームウェアとハイパーバイザにより実装されています。
0 => _
というシンタックスは、インラインアセンブリを実行する前にレジスタをゼロで初期化し、実行後はその値は気にしないということを示しています。in
ではなくinout
を使う必要があるのは、この実行でレジスタの値を上書きしてしまう可能性があるからです。- この
main
関数はentry.S
にあるエントリポイントから呼ばれるため、#[no_mangle]
とextern "C"
を必要とします。 _x0
–_x3
はレジスタx0
–x3
の値であり、慣習的にブートロードがデバイスツリーなどへのポインタを渡すのに利用されています。(extern "C"
により指定された)aarch64 の関数コール規約ではレジスタx0
–x7
は最初の8個の引数を関数に渡すのに利用されることになっているため、entry.S
はこれらの値を変更しないようにする以外の特別なことをする必要はありません。- この例を
src/bare-metal/aps/examples
においてmake qemu_psci
とすることでQEMUにより実行してみましょう。