123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542 |
- use crate::{
- platform::{
- interrupt::{
- Apic,
- InterruptModel,
- InterruptSourceOverride,
- IoApic,
- LocalInterruptLine,
- NmiLine,
- NmiProcessor,
- NmiSource,
- Polarity,
- TriggerMode,
- },
- Processor,
- ProcessorInfo,
- ProcessorState,
- },
- sdt::SdtHeader,
- AcpiError,
- AcpiTable,
- };
- use alloc::vec::Vec;
- use bit_field::BitField;
- use core::{marker::PhantomData, mem};
- #[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)]
- pub struct Madt {
- header: SdtHeader,
- local_apic_address: u32,
- flags: u32,
- }
- impl AcpiTable for Madt {
- fn header(&self) -> &SdtHeader {
- &self.header
- }
- }
- impl Madt {
- pub fn parse_interrupt_model(&self) -> Result<(InterruptModel, Option<ProcessorInfo>), AcpiError> {
- /*
- * 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::IoApic(_) |
- MadtEntry::InterruptSourceOverride(_) |
- MadtEntry::NmiSource(_) | // TODO: is this one used by more than one model?
- MadtEntry::LocalApicNmi(_) |
- MadtEntry::LocalApicAddressOverride(_) => {
- return self.parse_apic_model();
- }
- MadtEntry::IoSapic(_) |
- MadtEntry::LocalSapic(_) |
- MadtEntry::PlatformInterruptSource(_) => {
- unimplemented!();
- }
- MadtEntry::LocalX2Apic(_) |
- MadtEntry::X2ApicNmi(_) => {
- unimplemented!();
- }
- MadtEntry::Gicc(_) |
- MadtEntry::Gicd(_) |
- MadtEntry::GicMsiFrame(_) |
- MadtEntry::GicRedistributor(_) |
- MadtEntry::GicInterruptTranslationService(_) => {
- unimplemented!();
- }
- MadtEntry::MultiprocessorWakeup(_) => ()
- }
- }
- Ok((InterruptModel::Unknown, None))
- }
- fn parse_apic_model(&self) -> Result<(InterruptModel, Option<ProcessorInfo>), 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 = Vec::with_capacity(io_apic_count);
- let mut interrupt_source_overrides = Vec::with_capacity(iso_count);
- let mut nmi_sources = Vec::with_capacity(nmi_source_count);
- let mut local_apic_nmi_lines = Vec::with_capacity(local_nmi_line_count);
- let mut boot_processor = None;
- let mut application_processors = Vec::with_capacity(processor_count.saturating_sub(1)); // Subtract one for the BSP
- 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,
- local_apic_id: entry.apic_id,
- state,
- is_ap,
- };
- if is_ap {
- application_processors.push(processor);
- } else {
- boot_processor = Some(processor);
- }
- }
- MadtEntry::IoApic(entry) => {
- io_apics.push(IoApic {
- id: entry.io_apic_id,
- address: entry.io_apic_address,
- global_system_interrupt_base: entry.global_system_interrupt_base,
- });
- }
- 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.push(InterruptSourceOverride {
- isa_source: entry.irq,
- global_system_interrupt: entry.global_system_interrupt,
- polarity,
- trigger_mode,
- });
- }
- MadtEntry::NmiSource(entry) => {
- let (polarity, trigger_mode) = parse_mps_inti_flags(entry.flags)?;
- nmi_sources.push(NmiSource {
- global_system_interrupt: entry.global_system_interrupt,
- polarity,
- trigger_mode,
- });
- }
- MadtEntry::LocalApicNmi(entry) => local_apic_nmi_lines.push(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)),
- },
- }),
- MadtEntry::LocalApicAddressOverride(entry) => {
- local_apic_address = entry.local_apic_address;
- }
- _ => {
- return Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry));
- }
- }
- }
- Ok((
- InterruptModel::Apic(Apic {
- local_apic_address,
- io_apics,
- local_apic_nmi_lines,
- interrupt_source_overrides,
- nmi_sources,
- also_has_legacy_pics: self.supports_8259(),
- }),
- Some(ProcessorInfo { boot_processor: 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)
- }
- }
- 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 ()>,
- }
- 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)]
- #[repr(C, packed)]
- pub struct EntryHeader {
- entry_type: u8,
- length: u8,
- }
- #[repr(C, packed)]
- pub struct LocalApicEntry {
- header: EntryHeader,
- processor_id: u8,
- apic_id: u8,
- flags: u32,
- }
- #[repr(C, packed)]
- pub struct IoApicEntry {
- header: EntryHeader,
- io_apic_id: u8,
- _reserved: u8,
- io_apic_address: u32,
- global_system_interrupt_base: u32,
- }
- #[repr(C, packed)]
- pub struct InterruptSourceOverrideEntry {
- header: EntryHeader,
- bus: u8, // 0 - ISA bus
- irq: u8, // This is bus-relative
- global_system_interrupt: u32,
- flags: u16,
- }
- #[repr(C, packed)]
- pub struct NmiSourceEntry {
- header: EntryHeader,
- flags: u16,
- global_system_interrupt: u32,
- }
- #[repr(C, packed)]
- pub struct LocalApicNmiEntry {
- header: EntryHeader,
- processor_id: u8,
- flags: u16,
- nmi_line: u8, // Describes which LINTn is the NMI connected to
- }
- #[repr(C, packed)]
- pub struct LocalApicAddressOverrideEntry {
- header: EntryHeader,
- _reserved: u16,
- 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.
- #[repr(C, packed)]
- pub struct IoSapicEntry {
- header: EntryHeader,
- io_apic_id: u8,
- _reserved: u8,
- global_system_interrupt_base: u32,
- io_sapic_address: u64,
- }
- #[repr(C, packed)]
- pub struct LocalSapicEntry {
- header: EntryHeader,
- processor_id: u8,
- local_sapic_id: u8,
- local_sapic_eid: u8,
- _reserved: [u8; 3],
- flags: u32,
- 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,
- }
- #[repr(C, packed)]
- pub struct PlatformInterruptSourceEntry {
- header: EntryHeader,
- flags: u16,
- interrupt_type: u8,
- processor_id: u8,
- processor_eid: u8,
- io_sapic_vector: u8,
- global_system_interrupt: u32,
- platform_interrupt_source_flags: u32,
- }
- #[repr(C, packed)]
- pub struct LocalX2ApicEntry {
- header: EntryHeader,
- _reserved: u16,
- x2apic_id: u32,
- flags: u32,
- processor_uid: u32,
- }
- #[repr(C, packed)]
- pub struct X2ApicNmiEntry {
- header: EntryHeader,
- flags: u16,
- processor_uid: u32,
- 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.
- #[repr(C, packed)]
- pub struct GiccEntry {
- header: EntryHeader,
- _reserved1: u16,
- cpu_interface_number: u32,
- processor_uid: u32,
- flags: u32,
- parking_protocol_version: u32,
- performance_interrupt_gsiv: u32,
- parked_address: u64,
- gic_registers_address: u64,
- gic_control_block_address: u64,
- vgic_maintenance_interrupt: u32,
- gicr_base_address: u64,
- mpidr: u64,
- processor_power_efficiency_class: u8,
- _reserved2: [u8; 3],
- }
- #[repr(C, packed)]
- pub struct GicdEntry {
- header: EntryHeader,
- _reserved1: u16,
- gic_id: u32,
- physical_base_address: u64,
- 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
- gic_version: u8,
- _reserved2: [u8; 3],
- }
- #[repr(C, packed)]
- pub struct GicMsiFrameEntry {
- header: EntryHeader,
- _reserved: u16,
- frame_id: u32,
- physical_base_address: u64,
- flags: u32,
- spi_count: u16,
- spi_base: u16,
- }
- #[repr(C, packed)]
- pub struct GicRedistributorEntry {
- header: EntryHeader,
- _reserved: u16,
- discovery_range_base_address: u64,
- discovery_range_length: u32,
- }
- #[repr(C, packed)]
- pub struct GicInterruptTranslationServiceEntry {
- header: EntryHeader,
- _reserved1: u16,
- id: u32,
- physical_base_address: u64,
- _reserved2: u32,
- }
- #[repr(C, packed)]
- pub struct MultiprocessorWakeupEntry {
- header: EntryHeader,
- mailbox_version: u16,
- _reserved: u32,
- mailbox_address: u64,
- }
- fn parse_mps_inti_flags(flags: u16) -> Result<(Polarity, TriggerMode), AcpiError> {
- let polarity = match flags.get_bits(0..2) {
- 0b00 => Polarity::SameAsBus,
- 0b01 => Polarity::ActiveHigh,
- 0b11 => Polarity::ActiveLow,
- _ => return Err(AcpiError::InvalidMadt(MadtError::MpsIntiInvalidPolarity)),
- };
- let trigger_mode = match flags.get_bits(2..4) {
- 0b00 => TriggerMode::SameAsBus,
- 0b01 => TriggerMode::Edge,
- 0b11 => TriggerMode::Level,
- _ => return Err(AcpiError::InvalidMadt(MadtError::MpsIntiInvalidTriggerMode)),
- };
- Ok((polarity, trigger_mode))
- }
|