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}