qemu_i486_q35/
io.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2024.
4
5use core::fmt::Write;
6use core::ptr;
7use core::{arch::asm, panic::PanicInfo};
8
9use kernel::debug;
10
11use x86_q35::serial::{BlockingSerialPort, COM1_BASE};
12
13use crate::{CHIP, PROCESSES, PROCESS_PRINTER};
14
15/// Exists QEMU
16///
17/// This function requires the `-device isa-debug-exit,iobase=0xf4,iosize=0x04`
18/// device enabled.
19fn exit_qemu() -> ! {
20    unsafe {
21        asm!(
22            "
23        mov dx, 0xf4
24        mov al, 0x01
25        out dx,al
26        "
27        );
28    }
29
30    // We prefer the infinite loop to the `options(noreturn)` for `asm!` as
31    // the required `isa-debug-exit` device might be missing in which case
32    // the execution does not stop and generates undefined behaviour.
33    let mut com1 = unsafe { BlockingSerialPort::new(COM1_BASE) };
34    let _ = com1.write_fmt(format_args!(
35        "BUG:  QEMU did not exit.\
36        \r\n      The isa-debug-exit device is missing or is at a wrong address.\
37        \r\n      Please make sure the QEMU command line uses\
38        \r\n      the `-device isa-debug-exit,iobase=0xf4,iosize=0x04` argument.\
39        \r\nHINT: Use `killall qemu-system-i386` or the Task Manager to stop.\
40        \r\n"
41    ));
42
43    // We use the `htl` instruction in the infinite loop to prevent high CPU usage
44    // if QEMU did not exit.
45    loop {
46        unsafe { asm!("hlt") }
47    }
48}
49
50/// Panic handler.
51#[cfg(not(test))]
52#[panic_handler]
53unsafe fn panic_handler(pi: &PanicInfo) -> ! {
54    let mut com1 = BlockingSerialPort::new(COM1_BASE);
55
56    debug::panic_print(
57        &mut com1,
58        pi,
59        &x86::support::nop,
60        &*ptr::addr_of!(PROCESSES),
61        &*ptr::addr_of!(CHIP),
62        &*ptr::addr_of!(PROCESS_PRINTER),
63    );
64
65    exit_qemu();
66}