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}