qemu_exit/x86.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2//
3// Copyright (c) 2019-2022 Andre Richter <andre.o.richter@gmail.com>
4
5//! x86 (i386) and x86_64.
6
7use crate::QEMUExit;
8use core::arch::asm;
9
10const EXIT_FAILURE: u32 = 0; // since ((0 << 1) | 1) = 1.
11
12/// x86/x86_64 configuration.
13pub struct X86 {
14 /// Port number of the isa-debug-exit device.
15 io_base: u16,
16 /// Since QEMU's isa-debug-exit cannot exit(0), choose a value that represents success for you.
17 ///
18 /// Note: Only odd values will work.
19 custom_exit_success: u32,
20}
21
22/// Output a long on an io port
23fn outl(io_base: u16, code: u32) {
24 unsafe {
25 asm!(
26 "out dx, eax",
27 in("dx") io_base,
28 in("eax") code,
29 options(nomem, nostack)
30 );
31 }
32}
33
34impl X86 {
35 /// Create an instance.
36 pub const fn new(io_base: u16, custom_exit_success: u32) -> Self {
37 assert!((custom_exit_success & 1) == 1);
38
39 X86 {
40 io_base,
41 custom_exit_success,
42 }
43 }
44}
45
46impl QEMUExit for X86 {
47 fn exit(&self, code: u32) -> ! {
48 outl(self.io_base, code); // QEMU will execute `exit(((code << 1) | 1))`.
49
50 // For the case that the QEMU exit attempt did not work, transition into an infinite loop.
51 // Calling `panic!()` here is unfeasible, since there is a good chance this function here is
52 // the last expression in the `panic!()` handler itself. This prevents a possible infinite
53 // loop.
54 loop {
55 unsafe {
56 asm!("hlt", options(nomem, nostack));
57 }
58 }
59 }
60
61 fn exit_success(&self) -> ! {
62 self.exit(self.custom_exit_success >> 1) // Shift because QEMU does ((code << 1) | 1).
63 }
64
65 fn exit_failure(&self) -> ! {
66 self.exit(EXIT_FAILURE)
67 }
68}