インラインアセンブリ

時折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はレジスタx0x3の値であり、慣習的にブートロードがデバイスツリーなどへのポインタを渡すのに利用されています。(extern "C"により指定された)aarch64 の関数コール規約ではレジスタx0x7は最初の8個の引数を関数に渡すのに利用されることになっているため、entry.S はこれらの値を変更しないようにする以外の特別なことをする必要はありません。
  • この例をsrc/bare-metal/aps/examplesにおいてmake qemu_psciとすることでQEMUにより実行してみましょう。