Inline assembly

گاهی اوقات برای انجام کارهایی که با کد Rust امکان پذیر نیست، باید از اسمبلی استفاده کنیم. به عنوان مثال، برای برقراری یک HVC (hypervisor call) نایز است که به firmware بگویید سیستم را خاموش کند:

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

(اگر واقعاُ می‌خواهید این کار را انجام دهید، از crate مربوطه smccc استفاده کنید که دارای بسته‌بندی(wrapper) برای همه این عملکردها است.)

  • ‏ PSCI یک رابط هدایت‌گر Arm Power State است که مجموعه‌ای استاندارد از توابع برای مدیریت وضعیت‌های power در سیستم و CPU بوده، از جمله موارد دیگری از این مورد توسط میان‌افزار EL3 و hypervisor در بسیاری از سیستم‌ها پیاده سازی شده است.
  • یک 0 => _ syntax به این معنی است که رجیستر را قبل از اجرای کد اسمبلی درون خطی به 0 مقداردهی کنید و پس از آن محتوای آن را نادیده بگیرید. ما باید از inout به جای in استفاده کنیم زیرا این فراخوانی به طور بالقوه می‌تواند محتویات رجیسترها را مخدوش کند.
  • این تابع main باید به صورت#[no_mangle] و extern "C" باشد زیرا از نقطه ورودی (entry point) ما در entry.S فراخوانی می‌شود.
  • _x0_x3 مقادیر رجیسترهای x0x3 هستند که به طور معمول توسط bootloader برای ارسال چیزهایی مانند اشاره‌گر به device tree استفاده می‌شود. طبق قرارداد فراخوانی استاندارد aarch64 (که همان چیزی است که extern "C" برای استفاده مشخص می‌کند)، رجیسترهای x0x7 برای ۸ آرگومان اول ارسال شده به یک تابع استفاده می‌شوند، بنابراین entry.S این کار را انجام نمی‌دهد. لازم نیست کار خاصی انجام دهید، جز اینکه مطمئن شوید که این مورد رجیسترها را تغییر نمی‌دهد.
  • مثال را در QEMU با make qemu_psci در زیر src/bare-metal/aps/examples اجرا کنید.