#![no_std] #![feature(nll)] #![feature(alloc)] #![feature(exclusive_range_pattern, range_contains)] #![feature(exhaustive_integer_patterns)] #[cfg(test)] #[macro_use] extern crate std; #[macro_use] extern crate log; extern crate alloc; extern crate bit_field; mod aml; mod fadt; mod hpet; pub mod interrupt; mod madt; mod rsdp; mod rsdp_search; mod sdt; pub use aml::AmlError; pub use madt::MadtError; pub use rsdp_search::search_for_rsdp_bios; use alloc::{collections::BTreeMap, string::String, vec::Vec}; use aml::AmlValue; use core::mem; use core::ops::Deref; use core::ptr::NonNull; use interrupt::InterruptModel; use rsdp::Rsdp; use sdt::SdtHeader; #[derive(Debug)] // TODO: manually implement Debug to print signatures correctly etc. pub enum AcpiError { RsdpIncorrectSignature, RsdpInvalidOemId, RsdpInvalidChecksum, NoValidRsdp, SdtInvalidSignature([u8; 4]), SdtInvalidOemId([u8; 4]), SdtInvalidTableId([u8; 4]), SdtInvalidChecksum([u8; 4]), InvalidAmlTable([u8; 4], AmlError), InvalidMadt(MadtError), } #[repr(C, packed)] pub(crate) struct GenericAddress { address_space: u8, bit_width: u8, bit_offset: u8, access_size: u8, address: u64, } #[derive(Clone, Copy, Debug)] pub enum ProcessorState { /// A processor in this state is unusable, and you must not attempt to bring it up. Disabled, /// A processor waiting for a SIPI (Startup Inter-processor Interrupt) is currently not active, /// but may be brought up. WaitingForSipi, /// A Running processor is currently brought up and running code. Running, } #[derive(Clone, Copy, Debug)] pub struct Processor { processor_uid: u8, local_apic_id: u8, /// The state of this processor. Always check that the processor is not `Disabled` before /// attempting to bring it up! state: ProcessorState, /// Whether this processor is the Bootstrap Processor (BSP), or an Application Processor (AP). /// When the bootloader is entered, the BSP is the only processor running code. To run code on /// more than one processor, you need to "bring up" the APs. is_ap: bool, } impl Processor { pub(crate) fn new( processor_uid: u8, local_apic_id: u8, state: ProcessorState, is_ap: bool, ) -> Processor { Processor { processor_uid, local_apic_id, state, is_ap, } } } /// Describes a physical mapping created by `AcpiHandler::map_physical_region` and unmapped by /// `AcpiHandler::unmap_physical_region`. The region mapped must be at least `size_of::()` /// bytes, but may be bigger. pub struct PhysicalMapping { pub physical_start: usize, pub virtual_start: NonNull, pub region_length: usize, // Can be equal or larger than size_of::() pub mapped_length: usize, // Differs from `region_length` if padding is added for alignment } impl Deref for PhysicalMapping { type Target = T; fn deref(&self) -> &T { unsafe { self.virtual_start.as_ref() } } } /// An implementation of this trait must be provided to allow `acpi` to access platform-specific /// functionality, such as mapping regions of physical memory. You are free to implement these /// however you please, as long as they conform to the documentation of each function. pub trait AcpiHandler { /// Given a starting physical address and a size, map a region of physical memory that contains /// a `T` (but may be bigger than `size_of::()`). The address doesn't have to be page-aligned, /// so the implementation may have to add padding to either end. The given size must be greater /// or equal to the size of a `T`. The virtual address the memory is mapped to does not matter, /// as long as it is accessible from `acpi`. fn map_physical_region( &mut self, physical_address: usize, size: usize, ) -> PhysicalMapping; /// Unmap the given physical mapping. Safe because we consume the mapping, and so it can't be /// used after being passed to this function. fn unmap_physical_region(&mut self, region: PhysicalMapping); } #[derive(Debug)] pub struct Acpi { acpi_revision: u8, namespace: BTreeMap, boot_processor: Option, application_processors: Vec, /// ACPI theoretically allows for more than one interrupt model to be supported by the same /// hardware. For simplicity and because hardware practically will only support one model, we /// just error in cases that the tables detail more than one. interrupt_model: Option, } impl Acpi { /// A description of the boot processor. Until you bring any more up, this is the only processor /// running code, even on SMP systems. pub fn boot_processor<'a>(&'a self) -> &'a Option { &self.boot_processor } /// Descriptions of each of the application processors. These are not brought up until you do /// so. The application processors must be brought up in the order that they appear in this /// list. pub fn application_processors<'a>(&'a self) -> &'a Vec { &self.application_processors } /// The interrupt model supported by this system. pub fn interrupt_model<'a>(&'a self) -> &'a Option { &self.interrupt_model } } /// This is the entry point of `acpi` if you have the **physical** address of the RSDP. It maps /// the RSDP, works out what version of ACPI the hardware supports, and passes the physical /// address of the RSDT/XSDT to `parse_rsdt`. pub fn parse_rsdp(handler: &mut H, rsdp_address: usize) -> Result where H: AcpiHandler, { let rsdp_mapping = handler.map_physical_region::(rsdp_address, mem::size_of::()); (*rsdp_mapping).validate()?; parse_validated_rsdp(handler, rsdp_mapping) } fn parse_validated_rsdp( handler: &mut H, rsdp_mapping: PhysicalMapping, ) -> Result where H: AcpiHandler, { let revision = (*rsdp_mapping).revision(); if revision == 0 { /* * We're running on ACPI Version 1.0. We should use the 32-bit RSDT address. */ let rsdt_address = (*rsdp_mapping).rsdt_address(); handler.unmap_physical_region(rsdp_mapping); parse_rsdt(handler, revision, rsdt_address as usize) } else { /* * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated * to 32 bits on x86. */ let xsdt_address = (*rsdp_mapping).xsdt_address(); handler.unmap_physical_region(rsdp_mapping); parse_rsdt(handler, revision, xsdt_address as usize) } } /// This is the entry point of `acpi` if you already have the **physical** address of the /// RSDT/XSDT; it parses all the SDTs in the RSDT/XSDT, calling the relevant handlers in the /// implementation's `AcpiHandler`. /// /// If the given revision is 0, an address to the RSDT is expected. Otherwise, an address to /// the XSDT is expected. pub fn parse_rsdt( handler: &mut H, revision: u8, physical_address: usize, ) -> Result where H: AcpiHandler, { let mut acpi = Acpi { acpi_revision: revision, namespace: BTreeMap::new(), boot_processor: None, application_processors: Vec::new(), interrupt_model: None, }; let header = sdt::peek_at_sdt_header(handler, physical_address); let mapping = handler.map_physical_region::(physical_address, header.length() as usize); if revision == 0 { /* * ACPI Version 1.0. It's a RSDT! */ (*mapping).validate(b"RSDT")?; let num_tables = ((*mapping).length() as usize - mem::size_of::()) / mem::size_of::(); let tables_base = ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::()) as *const u32; for i in 0..num_tables { sdt::dispatch_sdt( &mut acpi, handler, unsafe { *tables_base.offset(i as isize) } as usize, )?; } } else { /* * ACPI Version 2.0+. It's a XSDT! */ (*mapping).validate(b"XSDT")?; let num_tables = ((*mapping).length() as usize - mem::size_of::()) / mem::size_of::(); let tables_base = ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::()) as *const u64; for i in 0..num_tables { sdt::dispatch_sdt( &mut acpi, handler, unsafe { *tables_base.offset(i as isize) } as usize, )?; } } info!("Parsed namespace: {:#?}", acpi.namespace); handler.unmap_physical_region(mapping); Ok(acpi) } #[cfg(test)] mod tests { use GenericAddress; impl GenericAddress { pub(crate) fn make_testcase() -> GenericAddress { GenericAddress { address_space: 0 as u8, bit_width: 0 as u8, bit_offset: 0 as u8, access_size: 0 as u8, address: 0 as u64, } } } }