ostd/arch/x86/kernel/acpi/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2
3pub(in crate::arch) mod dmar;
4pub(in crate::arch) mod remapping;
5
6use core::{num::NonZeroU8, ptr::NonNull};
7
8use acpi::{
9    AcpiHandler, AcpiTables,
10    address::AddressSpace,
11    fadt::{Fadt, IaPcBootArchFlags},
12    rsdp::Rsdp,
13};
14use log::warn;
15use spin::Once;
16
17use crate::{
18    boot::{self, BootloaderAcpiArg},
19    mm::paddr_to_vaddr,
20};
21
22#[derive(Debug, Clone)]
23pub(crate) struct AcpiMemoryHandler {}
24
25impl AcpiHandler for AcpiMemoryHandler {
26    unsafe fn map_physical_region<T>(
27        &self,
28        physical_address: usize,
29        size: usize,
30    ) -> acpi::PhysicalMapping<Self, T> {
31        let virtual_address = NonNull::new(paddr_to_vaddr(physical_address) as *mut T).unwrap();
32
33        // SAFETY: The caller should guarantee that `physical_address..physical_address + size` is
34        // part of the ACPI table. Then the memory region is mapped to `virtual_address` and is
35        // valid for read and immutable dereferencing.
36        // FIXME: The caller guarantee only holds if we trust the hardware to provide a valid ACPI
37        // table. Otherwise, if the table is corrupted, it may reference arbitrary memory regions.
38        unsafe {
39            acpi::PhysicalMapping::new(physical_address, virtual_address, size, size, self.clone())
40        }
41    }
42
43    fn unmap_physical_region<T>(_region: &acpi::PhysicalMapping<Self, T>) {}
44}
45
46pub(crate) fn get_acpi_tables() -> Option<AcpiTables<AcpiMemoryHandler>> {
47    let acpi_tables = match boot::EARLY_INFO.get().unwrap().acpi_arg {
48        BootloaderAcpiArg::Rsdp(addr) => unsafe {
49            AcpiTables::from_rsdp(AcpiMemoryHandler {}, addr).unwrap()
50        },
51        BootloaderAcpiArg::Rsdt(addr) => unsafe {
52            AcpiTables::from_rsdt(AcpiMemoryHandler {}, 0, addr).unwrap()
53        },
54        BootloaderAcpiArg::Xsdt(addr) => unsafe {
55            AcpiTables::from_rsdt(AcpiMemoryHandler {}, 1, addr).unwrap()
56        },
57        BootloaderAcpiArg::NotProvided => {
58            // We search by ourselves if the bootloader decides not to provide a rsdp location.
59            let rsdp = unsafe { Rsdp::search_for_on_bios(AcpiMemoryHandler {}) };
60            match rsdp {
61                Ok(map) => unsafe {
62                    AcpiTables::from_rsdp(AcpiMemoryHandler {}, map.physical_start()).unwrap()
63                },
64                Err(_) => {
65                    warn!("ACPI info not found!");
66                    return None;
67                }
68            }
69        }
70    };
71
72    Some(acpi_tables)
73}
74
75/// The platform information provided by the ACPI tables.
76///
77/// Currently, this structure contains only a limited set of fields, far fewer than those in all
78/// ACPI tables. However, the goal is to expand it properly to keep the simplicity of the OSTD code
79/// while enabling OSTD users to safely retrieve information from the ACPI tables.
80#[derive(Debug)]
81pub struct AcpiInfo {
82    /// The RTC CMOS RAM index to the century of data value; the "CENTURY" field in the FADT.
83    pub century_register: Option<NonZeroU8>,
84    /// IA-PC Boot Architecture Flags; the "IAPC_BOOT_ARCH" field in the FADT.
85    pub boot_flags: Option<IaPcBootArchFlags>,
86    /// An I/O port to reset the machine by writing the specified value.
87    pub reset_port_and_val: Option<(u16, u8)>,
88}
89
90/// The [`AcpiInfo`] singleton.
91pub static ACPI_INFO: Once<AcpiInfo> = Once::new();
92
93pub(in crate::arch) fn init() {
94    let mut acpi_info = AcpiInfo {
95        century_register: None,
96        boot_flags: None,
97        reset_port_and_val: None,
98    };
99
100    if let Some(acpi_tables) = get_acpi_tables()
101        && let Ok(fadt) = acpi_tables.find_table::<Fadt>()
102    {
103        // A zero means that the century register does not exist.
104        acpi_info.century_register = NonZeroU8::new(fadt.century);
105        acpi_info.boot_flags = Some(fadt.iapc_boot_arch);
106        if let Ok(reset_reg) = fadt.reset_register()
107            && reset_reg.address_space == AddressSpace::SystemIo
108            && let Ok(reset_port) = reset_reg.address.try_into()
109        {
110            acpi_info.reset_port_and_val = Some((reset_port, fadt.reset_value));
111        }
112    };
113
114    log::info!("[ACPI]: Collected information {:?}", acpi_info);
115
116    ACPI_INFO.call_once(|| acpi_info);
117}