Browse Source

Add Madt structures and entry enumeration

Isaac Woods 6 years ago
parent
commit
8f745a7732
3 changed files with 392 additions and 0 deletions
  1. 1 0
      src/lib.rs
  2. 382 0
      src/madt.rs
  3. 9 0
      src/sdt.rs

+ 1 - 0
src/lib.rs

@@ -15,6 +15,7 @@ extern crate bit_field;
 mod aml;
 mod fadt;
 mod hpet;
+mod madt;
 mod rsdp;
 mod rsdp_search;
 mod sdt;

+ 382 - 0
src/madt.rs

@@ -0,0 +1,382 @@
+use core::marker::PhantomData;
+use core::mem;
+use sdt::SdtHeader;
+use {Acpi, AcpiError, AcpiHandler, PhysicalMapping};
+
+/// Represents the MADT - this contains the MADT header fields. You can then iterate over a `Madt`
+/// to read each entry from it.
+#[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,
+        }
+    }
+}
+
+struct MadtEntryIter<'a> {
+    pointer: *const u8,
+    remaining_length: u32,
+    _phantom: PhantomData<&'a u8>,
+}
+
+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;
+            info!(
+                "Found MADT entry of {} bytes (id={}), {} bytes remaining",
+                header.length, header.entry_type, self.remaining_length
+            );
+
+            match header.entry_type {
+                0x0 => {
+                    return Some(MadtEntry::LocalApic(unsafe {
+                        &*(entry_pointer as *const LocalApicEntry)
+                    }))
+                }
+
+                0x1 => {
+                    return Some(MadtEntry::IoApic(unsafe {
+                        &*(entry_pointer as *const IoApicEntry)
+                    }))
+                }
+
+                0x2 => {
+                    return Some(MadtEntry::InterruptSourceOverride(unsafe {
+                        &*(entry_pointer as *const InterruptSourceOverrideEntry)
+                    }))
+                }
+
+                0x3 => {
+                    return Some(MadtEntry::NmiSource(unsafe {
+                        &*(entry_pointer as *const NmiSourceEntry)
+                    }))
+                }
+
+                0x4 => {
+                    return Some(MadtEntry::LocalApicNmi(unsafe {
+                        &*(entry_pointer as *const LocalApicNmiEntry)
+                    }))
+                }
+
+                0x5 => {
+                    return Some(MadtEntry::LocalApicAddressOverride(unsafe {
+                        &*(entry_pointer as *const LocalApicAddressOverrideEntry)
+                    }))
+                }
+
+                0x6 => {
+                    return Some(MadtEntry::IoSapic(unsafe {
+                        &*(entry_pointer as *const IoSapicEntry)
+                    }))
+                }
+
+                0x7 => {
+                    return Some(MadtEntry::LocalSapic(unsafe {
+                        &*(entry_pointer as *const LocalSapicEntry)
+                    }))
+                }
+
+                0x8 => {
+                    return Some(MadtEntry::PlatformInterruptSource(unsafe {
+                        &*(entry_pointer as *const PlatformInterruptSourceEntry)
+                    }))
+                }
+
+                0x9 => {
+                    return Some(MadtEntry::LocalX2Apic(unsafe {
+                        &*(entry_pointer as *const LocalX2ApicEntry)
+                    }))
+                }
+
+                0xa => {
+                    return Some(MadtEntry::X2ApicNmi(unsafe {
+                        &*(entry_pointer as *const X2ApicNmiEntry)
+                    }))
+                }
+
+                0xb => {
+                    return Some(MadtEntry::Gicc(unsafe {
+                        &*(entry_pointer as *const GiccEntry)
+                    }))
+                }
+
+                0xc => {
+                    return Some(MadtEntry::Gicd(unsafe {
+                        &*(entry_pointer as *const GicdEntry)
+                    }))
+                }
+
+                0xd => {
+                    return Some(MadtEntry::GicMsiFrame(unsafe {
+                        &*(entry_pointer as *const GicMsiFrameEntry)
+                    }))
+                }
+
+                0xe => {
+                    return Some(MadtEntry::GicRedistributor(unsafe {
+                        &*(entry_pointer as *const GicRedistributorEntry)
+                    }))
+                }
+
+                0xf => {
+                    return Some(MadtEntry::GicInterruptTranslationService(unsafe {
+                        &*(entry_pointer as *const GicInterruptTranslationServiceEntry)
+                    }))
+                }
+
+                /*
+                 * These entry types are reserved by the ACPI standard. We should skip them if they
+                 * appear in a real MADT.
+                 */
+                0x10..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 => {}
+
+                // TODO: remove when support for exhaustive integer patterns is merged
+                _ => unreachable!(),
+            }
+        }
+
+        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, // 0 - ISA bus
+    irq: u8, // This is bus-relative
+    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, // Describes which LINTn is the NMI connected to
+}
+
+#[repr(C, packed)]
+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)]
+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,
+
+    /// 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)]
+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],
+}
+
+/// 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)]
+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,
+
+    /// 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)]
+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<'a, 'h, H>(
+    acpi: &'a mut Acpi<'h, H>,
+    mapping: &PhysicalMapping<Madt>,
+) -> Result<(), AcpiError>
+where
+    'h: 'a,
+    H: AcpiHandler + 'a,
+{
+    (*mapping).header.validate(b"APIC")?;
+
+    for entry in (*mapping).entries() {
+        info!("Found MADT entry");
+        // TODO: parse entries
+    }
+
+    Ok(())
+}

+ 9 - 0
src/sdt.rs

@@ -1,6 +1,7 @@
 use core::{mem, str};
 use fadt::Fadt;
 use hpet::Hpet;
+use madt::Madt;
 use {Acpi, AcpiError, AcpiHandler};
 
 /// All SDTs share the same header, and are `length` bytes long. The signature tells us which SDT
@@ -191,6 +192,14 @@ where
             acpi.handler.unmap_physical_region(hpet_mapping);
         }
 
+        "APIC" => {
+            let madt_mapping = acpi
+                .handler
+                .map_physical_region::<Madt>(physical_address, header.length() as usize);
+            ::madt::parse_madt(acpi, &madt_mapping)?;
+            acpi.handler.unmap_physical_region(madt_mapping);
+        }
+
         signature => {
             /*
              * We don't recognise this signature. Early on, this probably just means we don't