diff options
Diffstat (limited to 'src/madt.rs')
-rw-r--r-- | src/madt.rs | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/src/madt.rs b/src/madt.rs new file mode 100644 index 0000000..7debcf6 --- /dev/null +++ b/src/madt.rs @@ -0,0 +1,646 @@ +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<ProcessorInfo<'a, A>>)> + 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<ProcessorInfo<'a, A>>)> + 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, + _ => (), + } + } + + 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::<Madt>()) }, + remaining_length: self.header.length - mem::size_of::<Madt>() 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<Self::Item> { + 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<u16, 6>, +} + +#[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)) +} |