#![no_std]
#![feature(allocator_api)]
#[cfg_attr(test, macro_use)]
#[cfg(test)]
extern crate std;
#[cfg(feature = "alloc")]
extern crate alloc;
pub mod address;
#[cfg(feature = "aml")]
pub mod aml;
#[cfg(feature = "alloc")]
pub mod platform;
pub mod registers;
pub mod rsdp;
pub mod sdt;
pub use pci_types::PciAddress;
pub use sdt::{fadt::PowerProfile, hpet::HpetInfo, madt::MadtError};
use crate::sdt::{SdtHeader, Signature};
use core::{
fmt,
mem,
ops::{Deref, DerefMut},
pin::Pin,
ptr::NonNull,
};
use log::warn;
use rsdp::Rsdp;
pub struct AcpiTables<H: Handler> {
rsdt_mapping: PhysicalMapping<H, SdtHeader>,
pub rsdp_revision: u8,
handler: H,
}
unsafe impl<H> Send for AcpiTables<H> where H: Handler + Send {}
unsafe impl<H> Sync for AcpiTables<H> where H: Handler + Send {}
impl<H> AcpiTables<H>
where
H: Handler,
{
pub unsafe fn from_rsdp(handler: H, rsdp_address: usize) -> Result<AcpiTables<H>, AcpiError> {
let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(rsdp_address, mem::size_of::<Rsdp>()) };
match rsdp_mapping.validate() {
Ok(()) => (),
Err(AcpiError::RsdpIncorrectSignature) => return Err(AcpiError::RsdpIncorrectSignature),
Err(AcpiError::RsdpInvalidOemId) | Err(AcpiError::RsdpInvalidChecksum) => {
warn!("RSDP has invalid checksum or OEM ID. Continuing.");
}
Err(_) => (),
}
let rsdp_revision = rsdp_mapping.revision();
let rsdt_address = if rsdp_revision == 0 {
rsdp_mapping.rsdt_address() as usize
} else {
rsdp_mapping.xsdt_address() as usize
};
unsafe { Self::from_rsdt(handler, rsdp_revision, rsdt_address) }
}
pub unsafe fn from_rsdt(
handler: H,
rsdp_revision: u8,
rsdt_address: usize,
) -> Result<AcpiTables<H>, AcpiError> {
let rsdt_mapping =
unsafe { handler.map_physical_region::<SdtHeader>(rsdt_address, mem::size_of::<SdtHeader>()) };
let rsdt_length = rsdt_mapping.length;
let rsdt_mapping = unsafe { handler.map_physical_region::<SdtHeader>(rsdt_address, rsdt_length as usize) };
Ok(Self { rsdt_mapping, rsdp_revision, handler })
}
pub fn table_entries(&self) -> impl Iterator<Item = usize> {
let entry_size = if self.rsdp_revision == 0 { 4 } else { 8 };
let mut table_entries_ptr =
unsafe { self.rsdt_mapping.virtual_start.as_ptr().byte_add(mem::size_of::<SdtHeader>()) }.cast::<u8>();
let mut num_entries = (self.rsdt_mapping.region_length - mem::size_of::<SdtHeader>()) / entry_size;
core::iter::from_fn(move || {
if num_entries > 0 {
unsafe {
let entry = if entry_size == 4 {
*table_entries_ptr.cast::<u32>() as usize
} else {
*table_entries_ptr.cast::<u64>() as usize
};
table_entries_ptr = table_entries_ptr.byte_add(entry_size);
num_entries -= 1;
Some(entry)
}
} else {
None
}
})
}
pub fn table_headers(&self) -> impl Iterator<Item = (usize, SdtHeader)> {
self.table_entries().map(|table_phys_address| {
let mapping = unsafe {
self.handler.map_physical_region::<SdtHeader>(table_phys_address, mem::size_of::<SdtHeader>())
};
(table_phys_address, *mapping)
})
}
pub fn find_tables<T>(&self) -> impl Iterator<Item = PhysicalMapping<H, T>>
where
T: AcpiTable,
{
self.table_entries().filter_map(|table_phys_address| {
let header_mapping = unsafe {
self.handler.map_physical_region::<SdtHeader>(table_phys_address, mem::size_of::<SdtHeader>())
};
if header_mapping.signature == T::SIGNATURE {
let length = header_mapping.length;
drop(header_mapping);
Some(unsafe { self.handler.map_physical_region::<T>(table_phys_address, length as usize) })
} else {
None
}
})
}
pub fn find_table<T>(&self) -> Option<PhysicalMapping<H, T>>
where
T: AcpiTable,
{
self.find_tables().next()
}
pub fn dsdt(&self) -> Result<AmlTable, AcpiError> {
let Some(fadt) = self.find_table::<sdt::fadt::Fadt>() else {
Err(AcpiError::TableNotFound(Signature::FADT))?
};
let phys_address = fadt.dsdt_address()?;
let header =
unsafe { self.handler.map_physical_region::<SdtHeader>(phys_address, mem::size_of::<SdtHeader>()) };
Ok(AmlTable { phys_address, length: header.length, revision: header.revision })
}
pub fn ssdts(&self) -> impl Iterator<Item = AmlTable> {
self.table_headers().filter_map(|(phys_address, header)| {
if header.signature == Signature::SSDT {
Some(AmlTable { phys_address, length: header.length, revision: header.revision })
} else {
None
}
})
}
}
#[derive(Clone, Copy, Debug)]
pub struct AmlTable {
pub phys_address: usize,
pub length: u32,
pub revision: u8,
}
pub unsafe trait AcpiTable {
const SIGNATURE: Signature;
fn header(&self) -> &SdtHeader;
fn validate(&self) -> Result<(), AcpiError> {
unsafe { self.header().validate(Self::SIGNATURE) }
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum AcpiError {
NoValidRsdp,
RsdpIncorrectSignature,
RsdpInvalidOemId,
RsdpInvalidChecksum,
SdtInvalidSignature(Signature),
SdtInvalidOemId(Signature),
SdtInvalidTableId(Signature),
SdtInvalidChecksum(Signature),
SdtInvalidCreatorId(Signature),
TableNotFound(Signature),
InvalidFacsAddress,
InvalidDsdtAddress,
InvalidMadt(MadtError),
InvalidGenericAddress,
Timeout,
#[cfg(feature = "alloc")]
Aml(aml::AmlError),
LibUnimplemented,
HostUnimplemented,
}
pub struct PhysicalMapping<H, T>
where
H: Handler,
{
pub physical_start: usize,
pub virtual_start: NonNull<T>,
pub region_length: usize,
pub mapped_length: usize,
pub handler: H,
}
impl<H, T> PhysicalMapping<H, T>
where
H: Handler,
{
pub fn get(&self) -> Pin<&T> {
unsafe { Pin::new_unchecked(self.virtual_start.as_ref()) }
}
}
impl<H, T> fmt::Debug for PhysicalMapping<H, T>
where
H: Handler,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PhysicalMapping")
.field("physical_start", &self.physical_start)
.field("virtual_start", &self.virtual_start)
.field("region_length", &self.region_length)
.field("mapped_length", &self.mapped_length)
.field("handler", &())
.finish()
}
}
unsafe impl<H: Handler + Send, T: Send> Send for PhysicalMapping<H, T> {}
impl<H, T> Deref for PhysicalMapping<H, T>
where
T: Unpin,
H: Handler,
{
type Target = T;
fn deref(&self) -> &T {
unsafe { self.virtual_start.as_ref() }
}
}
impl<H, T> DerefMut for PhysicalMapping<H, T>
where
T: Unpin,
H: Handler,
{
fn deref_mut(&mut self) -> &mut T {
unsafe { self.virtual_start.as_mut() }
}
}
impl<H, T> Drop for PhysicalMapping<H, T>
where
H: Handler,
{
fn drop(&mut self) {
H::unmap_physical_region(self)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Handle(pub u32);
pub trait Handler: Clone {
unsafe fn map_physical_region<T>(&self, physical_address: usize, size: usize) -> PhysicalMapping<Self, T>;
fn unmap_physical_region<T>(region: &PhysicalMapping<Self, T>);
fn read_u8(&self, address: usize) -> u8;
fn read_u16(&self, address: usize) -> u16;
fn read_u32(&self, address: usize) -> u32;
fn read_u64(&self, address: usize) -> u64;
fn write_u8(&self, address: usize, value: u8);
fn write_u16(&self, address: usize, value: u16);
fn write_u32(&self, address: usize, value: u32);
fn write_u64(&self, address: usize, value: u64);
fn read_io_u8(&self, port: u16) -> u8;
fn read_io_u16(&self, port: u16) -> u16;
fn read_io_u32(&self, port: u16) -> u32;
fn write_io_u8(&self, port: u16, value: u8);
fn write_io_u16(&self, port: u16, value: u16);
fn write_io_u32(&self, port: u16, value: u32);
fn read_pci_u8(&self, address: PciAddress, offset: u16) -> u8;
fn read_pci_u16(&self, address: PciAddress, offset: u16) -> u16;
fn read_pci_u32(&self, address: PciAddress, offset: u16) -> u32;
fn write_pci_u8(&self, address: PciAddress, offset: u16, value: u8);
fn write_pci_u16(&self, address: PciAddress, offset: u16, value: u16);
fn write_pci_u32(&self, address: PciAddress, offset: u16, value: u32);
fn nanos_since_boot(&self) -> u64;
fn stall(&self, microseconds: u64);
fn sleep(&self, milliseconds: u64);
#[cfg(feature = "aml")]
fn create_mutex(&self) -> Handle;
#[cfg(feature = "aml")]
fn acquire(&self, mutex: Handle, timeout: u16) -> Result<(), aml::AmlError>;
#[cfg(feature = "aml")]
fn release(&self, mutex: Handle);
#[cfg(feature = "aml")]
fn breakpoint(&self) {}
#[cfg(feature = "aml")]
fn handle_debug(&self, _object: &aml::object::Object) {}
#[cfg(feature = "aml")]
fn handle_fatal_error(&self, fatal_type: u8, fatal_code: u32, fatal_arg: u64) {
panic!(
"Fatal error while executing AML (encountered DefFatalOp). fatal_type = {}, fatal_code = {}, fatal_arg = {}",
fatal_type, fatal_code, fatal_arg
);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(dead_code)]
fn test_physical_mapping_send_sync() {
fn test_send_sync<T: Send>() {}
fn caller<H: Handler + Send, T: Send>() {
test_send_sync::<PhysicalMapping<H, T>>();
}
}
}