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
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2019-2022 Andre Richter <andre.o.richter@gmail.com>
//! x86.
use crate::QEMUExit;
use core::arch::asm;
const EXIT_FAILURE: u32 = 0; // since ((0 << 1) | 1) = 1.
/// x86 configuration.
pub struct X86 {
/// Port number of the isa-debug-exit device.
io_base: u16,
/// Since QEMU's isa-debug-exit cannot exit(0), choose a value that represents success for you.
///
/// Note: Only odd values will work.
custom_exit_success: u32,
}
/// Output a long on an io port
fn outl(io_base: u16, code: u32) {
unsafe {
asm!(
"out dx, eax",
in("dx") io_base,
in("eax") code,
options(nomem, nostack)
);
}
}
impl X86 {
/// Create an instance.
pub const fn new(io_base: u16, custom_exit_success: u32) -> Self {
assert!((custom_exit_success & 1) == 1);
X86 {
io_base,
custom_exit_success,
}
}
}
impl QEMUExit for X86 {
fn exit(&self, code: u32) -> ! {
outl(self.io_base, code); // QEMU will execute `exit(((code << 1) | 1))`.
// 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 {
unsafe {
asm!("hlt", options(nomem, nostack));
}
}
}
fn exit_success(&self) -> ! {
self.exit(self.custom_exit_success >> 1) // Shift because QEMU does ((code << 1) | 1).
}
fn exit_failure(&self) -> ! {
self.exit(EXIT_FAILURE)
}
}