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
مقادیر رجیسترهایx0
–x3
هستند که به طور معمول توسط bootloader برای ارسال چیزهایی مانند اشارهگر به device tree استفاده میشود. طبق قرارداد فراخوانی استاندارد aarch64 (که همان چیزی است کهextern "C"
برای استفاده مشخص میکند)، رجیسترهایx0
–x7
برای ۸ آرگومان اول ارسال شده به یک تابع استفاده میشوند، بنابراینentry.S
این کار را انجام نمیدهد. لازم نیست کار خاصی انجام دهید، جز اینکه مطمئن شوید که این مورد رجیسترها را تغییر نمیدهد. - مثال را در QEMU با
make qemu_psci
در زیرsrc/bare-metal/aps/examples
اجرا کنید.