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
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2019-2020 Andre Richter <andre.o.richter@gmail.com>

//! x86.

use crate::QEMUExit;

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)
    }
}