Files
RedBear-OS/src/acpi/mod.rs
T

253 lines
7.8 KiB
Rust

//! # ACPI
//! Code to parse the ACPI tables
use alloc::{boxed::Box, string::String, vec::Vec};
use core::ptr::NonNull;
use hashbrown::HashMap;
use spin::{Once, RwLock};
use crate::memory::{KernelMapper, PageFlags, PhysicalAddress, RmmA, RmmArch};
use self::{hpet::Hpet, madt::Madt, rsdp::Rsdp, rsdt::Rsdt, rxsdt::Rxsdt, sdt::Sdt, xsdt::Xsdt};
#[cfg(target_arch = "aarch64")]
mod gtdt;
pub mod fadt;
pub mod facs;
pub mod hpet;
pub mod madt;
mod rsdp;
mod rsdt;
mod rxsdt;
pub mod sdt;
#[cfg(target_arch = "aarch64")]
mod spcr;
mod xsdt;
unsafe fn map_linearly(addr: PhysicalAddress, len: usize, mapper: &mut crate::memory::PageMapper) {
unsafe {
let base = PhysicalAddress::new(crate::memory::round_down_pages(addr.data()));
let aligned_len = crate::memory::round_up_pages(len + (addr.data() - base.data()));
for page_idx in 0..aligned_len / crate::memory::PAGE_SIZE {
let (_, flush) = mapper
.map_linearly(
base.add(page_idx * crate::memory::PAGE_SIZE),
PageFlags::new(),
)
.expect("failed to linearly map SDT");
flush.flush();
}
}
}
pub fn get_sdt(sdt_address: PhysicalAddress, mapper: &mut KernelMapper<true>) -> &'static Sdt {
let sdt;
unsafe {
const SDT_SIZE: usize = size_of::<Sdt>();
map_linearly(sdt_address, SDT_SIZE, mapper);
sdt = &*(RmmA::phys_to_virt(sdt_address).data() as *const Sdt);
map_linearly(
sdt_address.add(SDT_SIZE),
sdt.length as usize - SDT_SIZE,
mapper,
);
}
sdt
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct GenericAddressStructure {
pub address_space: u8,
pub bit_width: u8,
pub bit_offset: u8,
pub access_size: u8,
pub address: u64,
}
pub enum RxsdtEnum {
Rsdt(Rsdt),
Xsdt(Xsdt),
}
impl Rxsdt for RxsdtEnum {
fn iter(&self) -> Box<dyn Iterator<Item = PhysicalAddress>> {
match self {
Self::Rsdt(rsdt) => <Rsdt as Rxsdt>::iter(rsdt),
Self::Xsdt(xsdt) => <Xsdt as Rxsdt>::iter(xsdt),
}
}
}
pub static RXSDT_ENUM: Once<RxsdtEnum> = Once::new();
/// Parse the ACPI tables to gather CPU, interrupt, and timer information
pub unsafe fn init(already_supplied_rsdp: Option<NonNull<u8>>) {
unsafe {
{
let mut sdt_ptrs = SDT_POINTERS.write();
*sdt_ptrs = Some(HashMap::new());
}
// Search for RSDP
let rsdp_opt = Rsdp::get_rsdp(already_supplied_rsdp);
if let Some(rsdp) = rsdp_opt {
debug!("SDT address: {:#x}", rsdp.sdt_address().data());
let rxsdt = get_sdt(rsdp.sdt_address(), &mut KernelMapper::lock_rw());
let rxsdt = if let Some(rsdt) = Rsdt::new(rxsdt) {
let mut initialized = false;
let rsdt = RXSDT_ENUM.call_once(|| {
initialized = true;
RxsdtEnum::Rsdt(rsdt)
});
if !initialized {
error!("RXSDT_ENUM already initialized");
}
rsdt
} else if let Some(xsdt) = Xsdt::new(rxsdt) {
let mut initialized = false;
let xsdt = RXSDT_ENUM.call_once(|| {
initialized = true;
RxsdtEnum::Xsdt(xsdt)
});
if !initialized {
error!("RXSDT_ENUM already initialized");
}
xsdt
} else {
warn!("UNKNOWN RSDT OR XSDT SIGNATURE");
return;
};
// TODO: Don't touch ACPI tables in kernel?
for sdt in rxsdt.iter() {
get_sdt(sdt, &mut KernelMapper::lock_rw());
}
for sdt_address in rxsdt.iter() {
let sdt = &*(RmmA::phys_to_virt(sdt_address).data() as *const Sdt);
let signature = get_sdt_signature(sdt);
if let Some(ref mut ptrs) = *(SDT_POINTERS.write()) {
ptrs.insert(signature, sdt);
}
}
// TODO: Enumerate processors in userspace, and then provide an ACPI-independent interface
// to initialize enumerated processors to userspace?
Madt::init();
//TODO: support this on any arch
// SPCR must be initialized after MADT for interrupt controllers
#[cfg(target_arch = "aarch64")]
spcr::Spcr::init();
// TODO: Let userspace setup HPET, and then provide an interface to specify which timer to
// use?
Hpet::init();
#[cfg(target_arch = "aarch64")]
gtdt::Gtdt::init();
// Phase II: parse the FADT to extract the PM1a_CNT
// and PM1a_STS port addresses used by the S3 entry
// path. Hardware-agnostic — works on any platform
// with a working FADT.
if let Some(fadt_sdts) = find_sdt("FACP").first() {
fadt::init(fadt_sdts);
} else {
warn!("ACPI: no FADT (FACP) found, S3 entry path disabled");
}
// Phase II.X.W: parse the FACS to extract the
// xfirmware_waking_vector. This is the address the
// platform firmware jumps to on S3 wake. The kernel's
// S3 resume trampoline in arch/x86_shared/s3_resume.rs
// is written to this address by acpid via the
// SetS3WakingVector AcPiVerb.
//
// The FACS is found via the FADT's x_firmware_ctrl
// field (64-bit) or firmware_ctrl field (32-bit).
// The FADT parser caches the FACS address. We map the
// FACS here because it is not listed in the RSDT/XSDT
// and therefore was not mapped during table discovery.
const FACS_MIN_SIZE: usize = 64;
let mut facs_addr = fadt::x_firmware_ctrl();
if facs_addr == 0 {
facs_addr = fadt::firmware_ctrl() as u64;
}
if facs_addr != 0 {
let facs_phys = PhysicalAddress::new(facs_addr as usize);
map_linearly(facs_phys, FACS_MIN_SIZE, &mut KernelMapper::lock_rw());
let facs_sdt = unsafe {
&*(RmmA::phys_to_virt(facs_phys).data() as *const Sdt)
};
facs::init(facs_sdt);
} else {
warn!("ACPI: no FACS found (neither x_firmware_ctrl nor firmware_ctrl), S3 resume path disabled");
}
} else {
error!("NO RSDP FOUND");
}
}
}
pub type SdtSignature = (String, [u8; 6], [u8; 8]);
pub static SDT_POINTERS: RwLock<Option<HashMap<SdtSignature, &'static Sdt>>> = RwLock::new(None);
pub fn find_sdt(name: &str) -> Vec<&'static Sdt> {
let mut sdts: Vec<&'static Sdt> = vec![];
if let Some(ref ptrs) = *(SDT_POINTERS.read()) {
for (signature, sdt) in ptrs {
if signature.0 == name {
sdts.push(sdt);
}
}
}
sdts
}
#[macro_export]
macro_rules! find_one_sdt {
($name:expr) => {{
use $crate::acpi::find_sdt;
match find_sdt($name).as_slice() {
[] => {
println!("Unable to find {}", $name);
return;
}
[x] => *x,
x => {
println!("{} {} found, expected 1", x.len(), $name);
return;
}
}
}};
}
pub fn get_sdt_signature(sdt: &'static Sdt) -> SdtSignature {
let signature =
String::from_utf8(sdt.signature.to_vec()).expect("Error converting signature to string");
(signature, sdt.oem_id, sdt.oem_table_id)
}
pub struct Acpi {
pub hpet: RwLock<Option<Hpet>>,
}
pub static ACPI_TABLE: Acpi = Acpi {
hpet: RwLock::new(None),
};