use crate::{
interrupt::{
Apic,
InterruptModel,
InterruptSourceOverride,
IoApic,
NmiLine,
NmiProcessor,
NmiSource,
Polarity,
TriggerMode,
},
sdt::SdtHeader,
Acpi,
AcpiError,
AcpiHandler,
PhysicalMapping,
Processor,
ProcessorState,
};
use alloc::vec::Vec;
use bit_field::BitField;
use core::{marker::PhantomData, mem};
#[derive(Debug)]
pub enum MadtError {
UnexpectedEntry,
InterruptOverrideEntryHasInvalidBus,
InvalidLocalNmiLine,
MpsIntiInvalidPolarity,
MpsIntiInvalidTriggerMode,
}
#[repr(C, packed)]
pub(crate) struct Madt {
header: SdtHeader,
local_apic_address: u32,
flags: u32,
}
impl Madt {
fn entries(&self) -> MadtEntryIter {
MadtEntryIter {
pointer: unsafe { (self as *const Madt as *const u8).offset(mem::size_of::<Madt>() as isize) },
remaining_length: self.header.length - mem::size_of::<Madt>() as u32,
_phantom: PhantomData,
}
}
fn supports_8259(&self) -> bool {
unsafe { self.flags.get_bit(0) }
}
}
struct MadtEntryIter<'a> {
pointer: *const u8,
remaining_length: u32,
_phantom: PhantomData<&'a ()>,
}
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),
}
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)
}))
}
)*
0x10..=0x7f => {}
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)
);
}
None
}
}
#[derive(Clone, Copy)]
#[repr(C, packed)]
struct EntryHeader {
entry_type: u8,
length: u8,
}
#[repr(C, packed)]
struct LocalApicEntry {
header: EntryHeader,
processor_id: u8,
apic_id: u8,
flags: u32,
}
#[repr(C, packed)]
struct IoApicEntry {
header: EntryHeader,
io_apic_id: u8,
_reserved: u8,
io_apic_address: u32,
global_system_interrupt_base: u32,
}
#[repr(C, packed)]
struct InterruptSourceOverrideEntry {
header: EntryHeader,
bus: u8,
irq: u8,
global_system_interrupt: u32,
flags: u16,
}
#[repr(C, packed)]
struct NmiSourceEntry {
header: EntryHeader,
flags: u16,
global_system_interrupt: u32,
}
#[repr(C, packed)]
struct LocalApicNmiEntry {
header: EntryHeader,
processor_id: u8,
flags: u16,
nmi_line: u8,
}
#[repr(C, packed)]
struct LocalApicAddressOverrideEntry {
header: EntryHeader,
_reserved: u16,
local_apic_address: u64,
}
#[repr(C, packed)]
struct IoSapicEntry {
header: EntryHeader,
io_apic_id: u8,
_reserved: u8,
global_system_interrupt_base: u32,
io_sapic_address: u64,
}
#[repr(C, packed)]
struct LocalSapicEntry {
header: EntryHeader,
processor_id: u8,
local_sapic_id: u8,
local_sapic_eid: u8,
_reserved: [u8; 3],
flags: u32,
processor_uid: u32,
processor_uid_string: u8,
}
#[repr(C, packed)]
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)]
struct LocalX2ApicEntry {
header: EntryHeader,
_reserved: u16,
x2apic_id: u32,
flags: u32,
processor_uid: u32,
}
#[repr(C, packed)]
struct X2ApicNmiEntry {
header: EntryHeader,
flags: u16,
processor_uid: u32,
nmi_line: u8,
_reserved: [u8; 3],
}
#[repr(C, packed)]
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)]
struct GicdEntry {
header: EntryHeader,
_reserved1: u16,
gic_id: u32,
physical_base_address: u64,
system_vector_base: u32,
gic_version: u8,
_reserved2: [u8; 3],
}
#[repr(C, packed)]
struct GicMsiFrameEntry {
header: EntryHeader,
_reserved: u16,
frame_id: u32,
physical_base_address: u64,
flags: u32,
spi_count: u16,
spi_base: u16,
}
#[repr(C, packed)]
struct GicRedistributorEntry {
header: EntryHeader,
_reserved: u16,
discovery_range_base_address: u64,
discovery_range_length: u32,
}
#[repr(C, packed)]
struct GicInterruptTranslationServiceEntry {
header: EntryHeader,
_reserved1: u16,
id: u32,
physical_base_address: u64,
_reserved2: u32,
}
pub(crate) fn parse_madt<H>(
acpi: &mut Acpi,
_handler: &mut H,
mapping: &PhysicalMapping<Madt>,
) -> Result<(), AcpiError>
where
H: AcpiHandler,
{
(*mapping).header.validate(crate::sdt::Signature::MADT)?;
if (*mapping).supports_8259() {
acpi.interrupt_model = Some(InterruptModel::Pic);
}
for entry in (*mapping).entries() {
match entry {
MadtEntry::LocalApic(_) |
MadtEntry::IoApic(_) |
MadtEntry::InterruptSourceOverride(_) |
MadtEntry::NmiSource(_) |
MadtEntry::LocalApicNmi(_) |
MadtEntry::LocalApicAddressOverride(_) => {
acpi.interrupt_model = Some(parse_apic_model(acpi, mapping)?);
break;
}
MadtEntry::IoSapic(_) |
MadtEntry::LocalSapic(_) |
MadtEntry::PlatformInterruptSource(_) => {
unimplemented!();
}
MadtEntry::LocalX2Apic(_) |
MadtEntry::X2ApicNmi(_) => {
unimplemented!();
}
MadtEntry::Gicc(_) |
MadtEntry::Gicd(_) |
MadtEntry::GicMsiFrame(_) |
MadtEntry::GicRedistributor(_) |
MadtEntry::GicInterruptTranslationService(_) => {
unimplemented!();
}
}
}
Ok(())
}
fn parse_apic_model(acpi: &mut Acpi, mapping: &PhysicalMapping<Madt>) -> Result<InterruptModel, AcpiError> {
use crate::interrupt::LocalInterruptLine;
let mut local_apic_address = (*mapping).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;
for entry in (*mapping).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);
acpi.application_processors = Vec::with_capacity(processor_count.saturating_sub(1));
for entry in (*mapping).entries() {
match entry {
MadtEntry::LocalApic(ref entry) => {
let is_ap = acpi.boot_processor.is_some();
let is_disabled = !unsafe { 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 {
acpi.application_processors.push(processor);
} else {
acpi.boot_processor = Some(processor);
}
}
MadtEntry::IoApic(ref 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(ref 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(ref 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(ref 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(ref 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: (*mapping).supports_8259(),
}))
}
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))
}