Skip to main content

ostd/arch/x86/
power.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Power management.
4
5mod qemu_isa_debug {
6    //! The isa-debug-exit device in QEMU.
7    //!
8    //! Reference: <https://elixir.bootlin.com/qemu/v10.1.2/source/hw/misc/debugexit.c>
9
10    use spin::Once;
11
12    use crate::{arch::device::io_port::WriteOnlyAccess, io::IoPort, power::ExitCode};
13
14    // For `qemu-system-x86_64`, the exit code will be `(code << 1) | 1`. So it is not possible to
15    // let QEMU invoke `exit(0)`. We also need to check if the exit code is returned by the kernel,
16    // so we cannot use `0` as `EXIT_SUCCESS` because it may conflict with QEMU's return value `1`,
17    // which indicates that QEMU itself fails.
18    const EXIT_SUCCESS: u32 = 0x10;
19    const EXIT_FAILURE: u32 = 0x20;
20
21    static DEBUG_EXIT_PORT: Once<IoPort<u32, WriteOnlyAccess>> = Once::new();
22
23    pub(super) fn try_exit_qemu(code: ExitCode) {
24        let value = match code {
25            ExitCode::Success => EXIT_SUCCESS,
26            ExitCode::Failure => EXIT_FAILURE,
27        };
28
29        // If possible, keep this method panic-free because it may be called by the panic handler.
30        if let Some(port) = DEBUG_EXIT_PORT.get() {
31            port.write(value);
32        }
33    }
34
35    pub(super) fn init() {
36        const DEBUG_EXIT_PORT_NUM: u16 = 0xF4;
37
38        let debug_exit_port = IoPort::acquire(DEBUG_EXIT_PORT_NUM).unwrap();
39
40        DEBUG_EXIT_PORT.call_once(|| debug_exit_port);
41    }
42}
43
44pub(super) fn init() {
45    use super::cpu::cpuid;
46
47    if !cpuid::query_if_running_in_qemu() {
48        return;
49    }
50
51    // FIXME: We assume that the kernel is running in QEMU with the following QEMU command line
52    // arguments that specify the isa-debug-exit device:
53    // `-device isa-debug-exit,iobase=0xf4,iosize=0x04`.
54    crate::info!("QEMU hypervisor detected, assuming that the isa-debug-exit device exists");
55
56    qemu_isa_debug::init();
57}
58
59/// Attempts to power off the system using an architecture-specific mechanism.
60///
61/// On x86, this function attempts to power off the system through QEMU's ISA debug-exit device if
62/// QEMU was detected during initialization. Otherwise, it does nothing and returns.
63pub fn try_poweroff(code: crate::power::ExitCode) {
64    qemu_isa_debug::try_exit_qemu(code);
65}
66
67/// Attempts to restart the system using an architecture-specific mechanism.
68///
69/// On x86, this function currently does nothing and returns.
70pub fn try_restart(_code: crate::power::ExitCode) {
71    // TODO: Add an OSTD-level restart mechanism for x86.
72}