1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
//ref:: https://github.com/andre-richter/qemu-exit
use core::arch::asm;
const EXIT_SUCCESS: u32 = 0x5555; // Equals `exit(0)`. qemu successful exit
const EXIT_FAILURE_FLAG: u32 = 0x3333;
const EXIT_FAILURE: u32 = exit_code_encode(1); // Equals `exit(1)`. qemu failed exit
const EXIT_RESET: u32 = 0x7777; // qemu reset
pub trait QEMUExit {
/// Exit with specified return code.
///
/// Note: For `X86`, code is binary-OR'ed with `0x1` inside QEMU.
fn exit(&self, code: u32) -> !;
/// Exit QEMU using `EXIT_SUCCESS`, aka `0`, if possible.
///
/// Note: Not possible for `X86`.
fn exit_success(&self) -> !;
/// Exit QEMU using `EXIT_FAILURE`, aka `1`.
fn exit_failure(&self) -> !;
}
/// RISCV64 configuration
pub struct RISCV64 {
/// Address of the sifive_test mapped device.
addr: u64,
}
/// Encode the exit code using EXIT_FAILURE_FLAG.
const fn exit_code_encode(code: u32) -> u32 {
(code << 16) | EXIT_FAILURE_FLAG
}
impl RISCV64 {
/// Create an instance.
pub const fn new(addr: u64) -> Self {
RISCV64 { addr }
}
}
impl QEMUExit for RISCV64 {
/// Exit qemu with specified exit code.
fn exit(&self, code: u32) -> ! {
// If code is not a special value, we need to encode it with EXIT_FAILURE_FLAG.
let code_new = match code {
EXIT_SUCCESS | EXIT_FAILURE | EXIT_RESET => code,
_ => exit_code_encode(code),
};
unsafe {
asm!(
"sw {0}, 0({1})",
in(reg)code_new, in(reg)self.addr
);
// For the case that the QEMU exit attempt did not work, transition into an infinite
// loop. Calling `panic!()` here is unfeasible, since there is a good chance
// this function here is the last expression in the `panic!()` handler
// itself. This prevents a possible infinite loop.
loop {
asm!("wfi", options(nomem, nostack));
}
}
}
fn exit_success(&self) -> ! {
self.exit(EXIT_SUCCESS);
}
fn exit_failure(&self) -> ! {
self.exit(EXIT_FAILURE);
}
}
const VIRT_TEST: u64 = 0x100000;
pub const QEMU_EXIT_HANDLE: RISCV64 = RISCV64::new(VIRT_TEST);