Вбудований асемблер
Іноді нам потрібно використовувати асемблер для того, щоб робити речі, які неможливо зробити за допомогою коду на 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 => _
означає ініціалізацію реєстру до 0 перед виконанням вбудованого асемблеру та ігнорування його вмісту після цього. Нам потрібно використовуватиinout
, а неin
, оскільки виклик потенційно може знищити вміст реєстрів. - Ця
main
функція має бути#[no_mangle]
іextern "C"
, оскільки вона викликається з нашої точки входу вentry.S
. _x0
–_x3
– це значення регістрівx0
–x3
, які традиційно використовуються завантажувачем для передачі таких речей, як покажчик на дерево пристроїв. Відповідно до стандартної угоди про виклики aarch64 (це те, що вказуєextern "C"
), регістриx0
–x7
використовуються для перших 8 аргументів, що передаються до функції, томуentry.S
не потрібно робити нічого особливого, окрім як переконатися, що він не змінює ці регістри.- Запустіть приклад у QEMU за допомогою
make qemu_psci
вsrc/bare-metal/aps/examples
.