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