use crate::{ sdt::{ExtendedField, SdtHeader, Signature}, AcpiTable, }; use bit_field::BitField; use core::{marker::PhantomData, mem}; #[cfg(feature = "allocator_api")] use crate::{ platform::{ interrupt::{InterruptModel, Polarity, TriggerMode}, ProcessorInfo, }, AcpiResult, }; #[derive(Debug)] pub enum MadtError { UnexpectedEntry, InterruptOverrideEntryHasInvalidBus, InvalidLocalNmiLine, MpsIntiInvalidPolarity, MpsIntiInvalidTriggerMode, } /// Represents the MADT - this contains the MADT header fields. You can then iterate over a `Madt` /// to read each entry from it. /// /// In modern versions of ACPI, the MADT can detail one of four interrupt models: /// * The ancient dual-i8259 legacy PIC model /// * The Advanced Programmable Interrupt Controller (APIC) model /// * The Streamlined Advanced Programmable Interrupt Controller (SAPIC) model (for Itanium systems) /// * The Generic Interrupt Controller (GIC) model (for ARM systems) #[repr(C, packed)] #[derive(Debug, Clone, Copy)] pub struct Madt { pub header: SdtHeader, pub local_apic_address: u32, pub flags: u32, } /// ### Safety: Implementation properly represents a valid MADT. unsafe impl AcpiTable for Madt { const SIGNATURE: Signature = Signature::MADT; fn header(&self) -> &SdtHeader { &self.header } } impl Madt { #[cfg(feature = "allocator_api")] pub fn parse_interrupt_model_in<'a, A>( &self, allocator: A, ) -> AcpiResult<(InterruptModel<'a, A>, Option>)> where A: core::alloc::Allocator + Clone, { /* * We first do a pass through the MADT to determine which interrupt model is being used. */ for entry in self.entries() { match entry { MadtEntry::LocalApic(_) | MadtEntry::LocalX2Apic(_) | MadtEntry::IoApic(_) | MadtEntry::InterruptSourceOverride(_) | MadtEntry::NmiSource(_) | // TODO: is this one used by more than one model? MadtEntry::LocalApicNmi(_) | MadtEntry::X2ApicNmi(_) | MadtEntry::LocalApicAddressOverride(_) => { return self.parse_apic_model_in(allocator); } MadtEntry::IoSapic(_) | MadtEntry::LocalSapic(_) | MadtEntry::PlatformInterruptSource(_) => { unimplemented!(); } MadtEntry::Gicc(_) | MadtEntry::Gicd(_) | MadtEntry::GicMsiFrame(_) | MadtEntry::GicRedistributor(_) | MadtEntry::GicInterruptTranslationService(_) => { unimplemented!(); } MadtEntry::MultiprocessorWakeup(_) => () } } Ok((InterruptModel::Unknown, None)) } #[cfg(feature = "allocator_api")] fn parse_apic_model_in<'a, A>( &self, allocator: A, ) -> AcpiResult<(InterruptModel<'a, A>, Option>)> where A: core::alloc::Allocator + Clone, { use crate::{ platform::{ interrupt::{ Apic, InterruptSourceOverride, IoApic, LocalInterruptLine, NmiLine, NmiProcessor, NmiSource, }, Processor, ProcessorState, }, AcpiError, }; let mut local_apic_address = self.local_apic_address as u64; let mut io_apic_count = 0; let mut iso_count = 0; let mut nmi_source_count = 0; let mut local_nmi_line_count = 0; let mut processor_count = 0usize; // Do a pass over the entries so we know how much space we should reserve in the vectors for entry in self.entries() { match entry { MadtEntry::IoApic(_) => io_apic_count += 1, MadtEntry::InterruptSourceOverride(_) => iso_count += 1, MadtEntry::NmiSource(_) => nmi_source_count += 1, MadtEntry::LocalApicNmi(_) => local_nmi_line_count += 1, MadtEntry::LocalApic(_) => processor_count += 1, MadtEntry::LocalX2Apic(_) => processor_count += 1, _ => (), } } let mut io_apics = crate::ManagedSlice::new_in(io_apic_count, allocator.clone())?; let mut interrupt_source_overrides = crate::ManagedSlice::new_in(iso_count, allocator.clone())?; let mut nmi_sources = crate::ManagedSlice::new_in(nmi_source_count, allocator.clone())?; let mut local_apic_nmi_lines = crate::ManagedSlice::new_in(local_nmi_line_count, allocator.clone())?; let mut application_processors = crate::ManagedSlice::new_in(processor_count.saturating_sub(1), allocator)?; // Subtract one for the BSP let mut boot_processor = None; io_apic_count = 0; iso_count = 0; nmi_source_count = 0; local_nmi_line_count = 0; processor_count = 0; for entry in self.entries() { match entry { MadtEntry::LocalApic(entry) => { /* * The first processor is the BSP. Subsequent ones are APs. If we haven't found * the BSP yet, this must be it. */ let is_ap = boot_processor.is_some(); let is_disabled = !{ entry.flags }.get_bit(0); let state = match (is_ap, is_disabled) { (_, true) => ProcessorState::Disabled, (true, false) => ProcessorState::WaitingForSipi, (false, false) => ProcessorState::Running, }; let processor = Processor { processor_uid: entry.processor_id as u32, local_apic_id: entry.apic_id as u32, state, is_ap, }; if is_ap { application_processors[processor_count] = processor; processor_count += 1; } else { boot_processor = Some(processor); } } MadtEntry::LocalX2Apic(entry) => { let is_ap = boot_processor.is_some(); let is_disabled = !{ entry.flags }.get_bit(0); let state = match (is_ap, is_disabled) { (_, true) => ProcessorState::Disabled, (true, false) => ProcessorState::WaitingForSipi, (false, false) => ProcessorState::Running, }; let processor = Processor { processor_uid: entry.processor_uid, local_apic_id: entry.x2apic_id, state, is_ap, }; if is_ap { application_processors[processor_count] = processor; processor_count += 1; } else { boot_processor = Some(processor); } } MadtEntry::IoApic(entry) => { io_apics[io_apic_count] = IoApic { id: entry.io_apic_id, address: entry.io_apic_address, global_system_interrupt_base: entry.global_system_interrupt_base, }; io_apic_count += 1; } MadtEntry::InterruptSourceOverride(entry) => { if entry.bus != 0 { return Err(AcpiError::InvalidMadt(MadtError::InterruptOverrideEntryHasInvalidBus)); } let (polarity, trigger_mode) = parse_mps_inti_flags(entry.flags)?; interrupt_source_overrides[iso_count] = InterruptSourceOverride { isa_source: entry.irq, global_system_interrupt: entry.global_system_interrupt, polarity, trigger_mode, }; iso_count += 1; } MadtEntry::NmiSource(entry) => { let (polarity, trigger_mode) = parse_mps_inti_flags(entry.flags)?; nmi_sources[nmi_source_count] = NmiSource { global_system_interrupt: entry.global_system_interrupt, polarity, trigger_mode, }; nmi_source_count += 1; } MadtEntry::LocalApicNmi(entry) => { local_apic_nmi_lines[local_nmi_line_count] = NmiLine { processor: if entry.processor_id == 0xff { NmiProcessor::All } else { NmiProcessor::ProcessorUid(entry.processor_id as u32) }, line: match entry.nmi_line { 0 => LocalInterruptLine::Lint0, 1 => LocalInterruptLine::Lint1, _ => return Err(AcpiError::InvalidMadt(MadtError::InvalidLocalNmiLine)), }, }; local_nmi_line_count += 1; } MadtEntry::X2ApicNmi(entry) => { local_apic_nmi_lines[local_nmi_line_count] = NmiLine { processor: if entry.processor_uid == 0xffffffff { NmiProcessor::All } else { NmiProcessor::ProcessorUid(entry.processor_uid) }, line: match entry.nmi_line { 0 => LocalInterruptLine::Lint0, 1 => LocalInterruptLine::Lint1, _ => return Err(AcpiError::InvalidMadt(MadtError::InvalidLocalNmiLine)), }, }; local_nmi_line_count += 1; } MadtEntry::LocalApicAddressOverride(entry) => { local_apic_address = entry.local_apic_address; } _ => { return Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry)); } } } Ok(( InterruptModel::Apic(Apic::new( local_apic_address, io_apics, local_apic_nmi_lines, interrupt_source_overrides, nmi_sources, self.supports_8259(), )), Some(ProcessorInfo::new(boot_processor.unwrap(), application_processors)), )) } pub fn entries(&self) -> MadtEntryIter { MadtEntryIter { pointer: unsafe { (self as *const Madt as *const u8).add(mem::size_of::()) }, remaining_length: self.header.length - mem::size_of::() as u32, _phantom: PhantomData, } } pub fn supports_8259(&self) -> bool { { self.flags }.get_bit(0) } } #[derive(Debug)] pub struct MadtEntryIter<'a> { pointer: *const u8, /* * The iterator can only have at most `u32::MAX` remaining bytes, because the length of the * whole SDT can only be at most `u32::MAX`. */ remaining_length: u32, _phantom: PhantomData<&'a ()>, } #[derive(Debug)] pub enum MadtEntry<'a> { LocalApic(&'a LocalApicEntry), IoApic(&'a IoApicEntry), InterruptSourceOverride(&'a InterruptSourceOverrideEntry), NmiSource(&'a NmiSourceEntry), LocalApicNmi(&'a LocalApicNmiEntry), LocalApicAddressOverride(&'a LocalApicAddressOverrideEntry), IoSapic(&'a IoSapicEntry), LocalSapic(&'a LocalSapicEntry), PlatformInterruptSource(&'a PlatformInterruptSourceEntry), LocalX2Apic(&'a LocalX2ApicEntry), X2ApicNmi(&'a X2ApicNmiEntry), Gicc(&'a GiccEntry), Gicd(&'a GicdEntry), GicMsiFrame(&'a GicMsiFrameEntry), GicRedistributor(&'a GicRedistributorEntry), GicInterruptTranslationService(&'a GicInterruptTranslationServiceEntry), MultiprocessorWakeup(&'a MultiprocessorWakeupEntry), } impl<'a> Iterator for MadtEntryIter<'a> { type Item = MadtEntry<'a>; fn next(&mut self) -> Option { while self.remaining_length > 0 { let entry_pointer = self.pointer; let header = unsafe { *(self.pointer as *const EntryHeader) }; self.pointer = unsafe { self.pointer.offset(header.length as isize) }; self.remaining_length -= header.length as u32; macro_rules! construct_entry { ($entry_type:expr, $entry_pointer:expr, $(($value:expr => $variant:path as $type:ty)),* ) => { match $entry_type { $( $value => { return Some($variant(unsafe { &*($entry_pointer as *const $type) })) } )* /* * These entry types are reserved by the ACPI standard. We should skip them * if they appear in a real MADT. */ 0x11..=0x7f => {} /* * These entry types are reserved for OEM use. Atm, we just skip them too. * TODO: work out if we should ever do anything else here */ 0x80..=0xff => {} } } } #[rustfmt::skip] construct_entry!( header.entry_type, entry_pointer, (0x0 => MadtEntry::LocalApic as LocalApicEntry), (0x1 => MadtEntry::IoApic as IoApicEntry), (0x2 => MadtEntry::InterruptSourceOverride as InterruptSourceOverrideEntry), (0x3 => MadtEntry::NmiSource as NmiSourceEntry), (0x4 => MadtEntry::LocalApicNmi as LocalApicNmiEntry), (0x5 => MadtEntry::LocalApicAddressOverride as LocalApicAddressOverrideEntry), (0x6 => MadtEntry::IoSapic as IoSapicEntry), (0x7 => MadtEntry::LocalSapic as LocalSapicEntry), (0x8 => MadtEntry::PlatformInterruptSource as PlatformInterruptSourceEntry), (0x9 => MadtEntry::LocalX2Apic as LocalX2ApicEntry), (0xa => MadtEntry::X2ApicNmi as X2ApicNmiEntry), (0xb => MadtEntry::Gicc as GiccEntry), (0xc => MadtEntry::Gicd as GicdEntry), (0xd => MadtEntry::GicMsiFrame as GicMsiFrameEntry), (0xe => MadtEntry::GicRedistributor as GicRedistributorEntry), (0xf => MadtEntry::GicInterruptTranslationService as GicInterruptTranslationServiceEntry), (0x10 => MadtEntry::MultiprocessorWakeup as MultiprocessorWakeupEntry) ); } None } } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct EntryHeader { pub entry_type: u8, pub length: u8, } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct LocalApicEntry { pub header: EntryHeader, pub processor_id: u8, pub apic_id: u8, pub flags: u32, } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct IoApicEntry { pub header: EntryHeader, pub io_apic_id: u8, _reserved: u8, pub io_apic_address: u32, pub global_system_interrupt_base: u32, } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct InterruptSourceOverrideEntry { pub header: EntryHeader, pub bus: u8, // 0 - ISA bus pub irq: u8, // This is bus-relative pub global_system_interrupt: u32, pub flags: u16, } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct NmiSourceEntry { pub header: EntryHeader, pub flags: u16, pub global_system_interrupt: u32, } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct LocalApicNmiEntry { pub header: EntryHeader, pub processor_id: u8, pub flags: u16, pub nmi_line: u8, // Describes which LINTn is the NMI connected to } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct LocalApicAddressOverrideEntry { pub header: EntryHeader, _reserved: u16, pub local_apic_address: u64, } /// If this entry is present, the system has an I/O SAPIC, which must be used instead of the I/O /// APIC. #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct IoSapicEntry { pub header: EntryHeader, pub io_apic_id: u8, _reserved: u8, pub global_system_interrupt_base: u32, pub io_sapic_address: u64, } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct LocalSapicEntry { pub header: EntryHeader, pub processor_id: u8, pub local_sapic_id: u8, pub local_sapic_eid: u8, _reserved: [u8; 3], pub flags: u32, pub processor_uid: u32, /// This string can be used to associate this local SAPIC to a processor defined in the /// namespace when the `_UID` object is a string. It is a null-terminated ASCII string, and so /// this field will be `'\0'` if the string is not present, otherwise it extends from the /// address of this field. processor_uid_string: u8, } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct PlatformInterruptSourceEntry { pub header: EntryHeader, pub flags: u16, pub interrupt_type: u8, pub processor_id: u8, pub processor_eid: u8, pub io_sapic_vector: u8, pub global_system_interrupt: u32, pub platform_interrupt_source_flags: u32, } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct LocalX2ApicEntry { pub header: EntryHeader, _reserved: u16, pub x2apic_id: u32, pub flags: u32, pub processor_uid: u32, } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct X2ApicNmiEntry { pub header: EntryHeader, pub flags: u16, pub processor_uid: u32, pub nmi_line: u8, _reserved: [u8; 3], } /// This field will appear for ARM processors that support ACPI and use the Generic Interrupt /// Controller. In the GICC interrupt model, each logical process has a Processor Device object in /// the namespace, and uses this structure to convey its GIC information. #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct GiccEntry { pub header: EntryHeader, _reserved1: u16, pub cpu_interface_number: u32, pub processor_uid: u32, pub flags: u32, pub parking_protocol_version: u32, pub performance_interrupt_gsiv: u32, pub parked_address: u64, pub gic_registers_address: u64, pub gic_virtual_registers_address: u64, pub gic_hypervisor_registers_address: u64, pub vgic_maintenance_interrupt: u32, pub gicr_base_address: u64, pub mpidr: u64, pub processor_power_efficiency_class: u8, _reserved2: u8, /// SPE overflow Interrupt. /// /// ACPI 6.3 defined this field. It is zero in prior versions or /// if this processor does not support SPE. pub spe_overflow_interrupt: u16, pub trbe_interrupt: ExtendedField, } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct GicdEntry { pub header: EntryHeader, _reserved1: u16, pub gic_id: u32, pub physical_base_address: u64, pub system_vector_base: u32, /// The GIC version /// 0x00: Fall back to hardware discovery /// 0x01: GICv1 /// 0x02: GICv2 /// 0x03: GICv3 /// 0x04: GICv4 /// 0x05-0xff: Reserved for future use pub gic_version: u8, _reserved2: [u8; 3], } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct GicMsiFrameEntry { pub header: EntryHeader, _reserved: u16, pub frame_id: u32, pub physical_base_address: u64, pub flags: u32, pub spi_count: u16, pub spi_base: u16, } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct GicRedistributorEntry { pub header: EntryHeader, _reserved: u16, pub discovery_range_base_address: u64, pub discovery_range_length: u32, } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct GicInterruptTranslationServiceEntry { pub header: EntryHeader, _reserved1: u16, pub id: u32, pub physical_base_address: u64, _reserved2: u32, } #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct MultiprocessorWakeupEntry { pub header: EntryHeader, pub mailbox_version: u16, _reserved: u32, pub mailbox_address: u64, } #[cfg(feature = "allocator_api")] fn parse_mps_inti_flags(flags: u16) -> crate::AcpiResult<(Polarity, TriggerMode)> { let polarity = match flags.get_bits(0..2) { 0b00 => Polarity::SameAsBus, 0b01 => Polarity::ActiveHigh, 0b11 => Polarity::ActiveLow, _ => return Err(crate::AcpiError::InvalidMadt(MadtError::MpsIntiInvalidPolarity)), }; let trigger_mode = match flags.get_bits(2..4) { 0b00 => TriggerMode::SameAsBus, 0b01 => TriggerMode::Edge, 0b11 => TriggerMode::Level, _ => return Err(crate::AcpiError::InvalidMadt(MadtError::MpsIntiInvalidTriggerMode)), }; Ok((polarity, trigger_mode)) }