インラインアセンブリ

Rust のコードでは実現できないことを行うために、アセンブリを使う必要が ある場合があります。たとえば、システムの電源を切るようファームウェアに 伝えるために HVC(ハイパーバイザー呼び出し)を行う場合です:

// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

#![no_main]
#![no_std]

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

mod asm;
mod exceptions;

const PSCI_SYSTEM_OFF: u32 = 0x84000008;

// SAFETY: There is no other global function of this name.
#[unsafe(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 クレートを使ってください。)

  • PSCI は Arm Power State Coordination Interface のことで、システムや CPU の 電源状態などを管理するための標準的な関数群です。多くのシステムでは、 EL3 ファームウェアやハイパーバイザーによって実装されています。
  • 0 => _ 構文は、インラインアセンブリコードを実行する前にレジスタを 0 で 初期化し、その後はその内容を無視することを意味します。呼び出しによって レジスタの内容が破壊される可能性があるため、in ではなく inout を 使う必要があります。
  • この main 関数は、entry.S 内のエントリポイントから呼び出されるため、 #[unsafe(no_mangle)]extern "C" である必要があります。
    • #[no_mangle] だけでも十分ですが、 RFC3325 では、 誤って使用すると未定義動作を引き起こす可能性がある属性にレビュアーの 注意を向けるために、この表記が使われています。
  • _x0_x3 はレジスタ x0x3 の値であり、これらは慣例的に ブートローダーがデバイスツリーへのポインタのような値を渡すために使用します。 標準の aarch64 呼び出し規約(これは extern "C" が使用を指定するものです) では、関数に渡される最初の 8 個の引数にレジスタ x0x7 が使われるため、 entry.S はこれらのレジスタを変更しないようにする以外、特別なことを行う 必要はありません。
  • src/bare-metal/aps/examplesmake qemu_psci を実行して、QEMU 上で この例を実行してください。