Browse Source

Merge #20

20: Add support for the MADT r=IsaacWoods a=IsaacWoods

This starts adding support for the MADT

- [x] Add definitions for the structures of the header and entries
- [x] Add safe way to enumerate the entries
- [x] Decide how we're going to relay this information to the OSPM
- [x] Parse the entries and relay that information
- [x] Sanity check the entries? E.g. we don't expect a `IoApic` and a `GicMsiFrame` to appear in the same MADT
- [x] Fully parse information for the APIC model

Thank you to @Sh4d1 for his initial work on this!

Co-authored-by: Isaac Woods <isaacwoods.home@gmail.com>
bors[bot] 6 years ago
parent
commit
335b9cdbbc
8 changed files with 727 additions and 61 deletions
  1. 5 5
      src/aml/mod.rs
  2. 6 5
      src/aml/parser.rs
  3. 8 9
      src/fadt.rs
  4. 72 0
      src/interrupt.rs
  5. 108 27
      src/lib.rs
  6. 506 0
      src/madt.rs
  7. 2 1
      src/rsdp_search.rs
  8. 20 14
      src/sdt.rs

+ 5 - 5
src/aml/mod.rs

@@ -42,18 +42,18 @@ impl AmlTable {
     }
 }
 
-pub(crate) fn parse_aml_table<'a, 'h, H>(
-    acpi: &'a mut Acpi<'h, H>,
+pub(crate) fn parse_aml_table<H>(
+    acpi: &mut Acpi,
+    handler: &mut H,
     mapping: &PhysicalMapping<AmlTable>,
     signature: &[u8; 4],
 ) -> Result<(), AcpiError>
 where
-    'h: 'a,
-    H: AcpiHandler + 'a,
+    H: AcpiHandler,
 {
     (*mapping).header.validate(signature)?;
 
-    match AmlParser::parse(acpi, "\\", (*mapping).stream()) {
+    match AmlParser::parse(acpi, handler, "\\", (*mapping).stream()) {
         Ok(_) => Ok(()),
         Err(error) => Err(AcpiError::InvalidAmlTable(*signature, error)),
     }

+ 6 - 5
src/aml/parser.rs

@@ -24,10 +24,10 @@ struct PkgLength {
 
 pub(crate) struct AmlParser<'s, 'a, 'h, H>
 where
-    'h: 'a,
     H: AcpiHandler + 'h,
 {
-    acpi: &'a mut Acpi<'h, H>,
+    acpi: &'a mut Acpi,
+    handler: &'h mut H,
     scope: String,
     stream: AmlStream<'s>,
 }
@@ -48,16 +48,17 @@ macro_rules! try_parse {
 
 impl<'s, 'a, 'h, H> AmlParser<'s, 'a, 'h, H>
 where
-    'h: 'a,
-    H: AcpiHandler + 'h,
+    H: AcpiHandler,
 {
     pub(crate) fn parse(
-        acpi: &'a mut Acpi<'h, H>,
+        acpi: &'a mut Acpi,
+        handler: &'h mut H,
         scope: &str,
         stream: AmlStream<'s>,
     ) -> Result<(), AmlError> {
         let mut parser = AmlParser {
             acpi,
+            handler,
             scope: String::from(scope),
             stream,
         };

+ 8 - 9
src/fadt.rs

@@ -74,13 +74,13 @@ pub struct Fadt {
     hypervisor_vendor_id: u64,
 }
 
-pub(crate) fn parse_fadt<'a, 'h, H>(
-    acpi: &'a mut Acpi<'h, H>,
+pub(crate) fn parse_fadt<H>(
+    acpi: &mut Acpi,
+    handler: &mut H,
     mapping: &PhysicalMapping<Fadt>,
 ) -> Result<(), AcpiError>
 where
-    'h: 'a,
-    H: AcpiHandler + 'a,
+    H: AcpiHandler,
 {
     (*mapping).header.validate(b"FACP")?;
 
@@ -91,14 +91,13 @@ where
     };
 
     // Parse the DSDT
-    let dsdt_header = sdt::peek_at_sdt_header(acpi.handler, dsdt_physical_address);
-    let dsdt_mapping = acpi
-        .handler
+    let dsdt_header = sdt::peek_at_sdt_header(handler, dsdt_physical_address);
+    let dsdt_mapping = handler
         .map_physical_region::<AmlTable>(dsdt_physical_address, dsdt_header.length() as usize);
-    if let Err(error) = parse_aml_table(acpi, &dsdt_mapping, b"DSDT") {
+    if let Err(error) = parse_aml_table(acpi, handler, &dsdt_mapping, b"DSDT") {
         error!("Failed to parse DSDT: {:?}. At this stage, this is expected, but should be fatal in the future", error);
     }
-    acpi.handler.unmap_physical_region(dsdt_mapping);
+    handler.unmap_physical_region(dsdt_mapping);
 
     Ok(())
 }

+ 72 - 0
src/interrupt.rs

@@ -0,0 +1,72 @@
+use alloc::vec::Vec;
+
+#[derive(Debug)]
+pub struct IoApic {
+    pub id: u8,
+    pub address: u32,
+    pub global_system_interrupt_base: u32,
+}
+
+#[derive(Debug)]
+pub enum LocalInterruptLine {
+    Lint0,
+    Lint1,
+}
+
+#[derive(Debug)]
+pub enum Polarity {
+    SameAsBus,
+    ActiveHigh,
+    ActiveLow,
+}
+
+#[derive(Debug)]
+pub enum TriggerMode {
+    SameAsBus,
+    Edge,
+    Level,
+}
+
+/// Describes a difference in the mapping of an ISA interrupt to how it's mapped in other interrupt
+/// models. For example, if a device is connected to ISA IRQ 0 and IOAPIC input 2, an override will
+/// appear mapping source 0 to GSI 2. Currently these will only be created for ISA interrupt
+/// sources.
+#[derive(Debug)]
+pub struct InterruptSourceOverride {
+    pub isa_source: u8,
+    pub global_system_interrupt: u32,
+    pub polarity: Polarity,
+    pub trigger_mode: TriggerMode,
+}
+
+/// Describes a Global System Interrupt that should be enabled as non-maskable. Any source that is
+/// non-maskable can not be used by devices.
+#[derive(Debug)]
+pub struct NmiSource {
+    pub global_system_interrupt: u32,
+    pub polarity: Polarity,
+    pub trigger_mode: TriggerMode,
+}
+
+#[derive(Debug)]
+pub enum InterruptModel {
+    /// This model is only chosen when a newer one can not be found and the system supports the
+    /// legacy dual-8259 PIC.
+    Pic,
+
+    /// Describes an interrupt controller based around the Advanced Programmable Interrupt
+    /// Controllers. These are likely to be found on x86 and x86_64 systems and are made up of a
+    /// Local APIC for each core and one or more I/O APICs to handle external interrupts.
+    Apic {
+        local_apic_address: u64,
+        io_apics: Vec<IoApic>,
+        local_apic_nmi_line: LocalInterruptLine,
+        interrupt_source_overrides: Vec<InterruptSourceOverride>,
+        nmi_sources: Vec<NmiSource>,
+
+        /// If this field is set, you must remap and mask all the lines of the legacy PIC, even if
+        /// you choose to use the APIC. It's recommended that you do this even if ACPI does not
+        /// require you to.
+        also_has_legacy_pics: bool,
+    },
+}

+ 108 - 27
src/lib.rs

@@ -2,6 +2,7 @@
 #![feature(nll)]
 #![feature(alloc)]
 #![feature(exclusive_range_pattern, range_contains)]
+#![feature(exhaustive_integer_patterns)]
 
 #[cfg(test)]
 #[macro_use]
@@ -15,17 +16,22 @@ extern crate bit_field;
 mod aml;
 mod fadt;
 mod hpet;
+pub mod interrupt;
+mod madt;
 mod rsdp;
 mod rsdp_search;
 mod sdt;
 
+pub use aml::AmlError;
+pub use madt::MadtError;
 pub use rsdp_search::search_for_rsdp_bios;
 
-use alloc::{collections::BTreeMap, string::String};
-use aml::{AmlError, AmlValue};
+use alloc::{collections::BTreeMap, string::String, vec::Vec};
+use aml::AmlValue;
 use core::mem;
 use core::ops::Deref;
 use core::ptr::NonNull;
+use interrupt::InterruptModel;
 use rsdp::Rsdp;
 use sdt::SdtHeader;
 
@@ -43,10 +49,12 @@ pub enum AcpiError {
     SdtInvalidChecksum([u8; 4]),
 
     InvalidAmlTable([u8; 4], AmlError),
+
+    InvalidMadt(MadtError),
 }
 
 #[repr(C, packed)]
-pub struct GenericAddress {
+pub(crate) struct GenericAddress {
     address_space: u8,
     bit_width: u8,
     bit_offset: u8,
@@ -54,6 +62,50 @@ pub struct GenericAddress {
     address: u64,
 }
 
+#[derive(Clone, Copy, Debug)]
+pub enum ProcessorState {
+    /// A processor in this state is unusable, and you must not attempt to bring it up.
+    Disabled,
+
+    /// A processor waiting for a SIPI (Startup Inter-processor Interrupt) is currently not active,
+    /// but may be brought up.
+    WaitingForSipi,
+
+    /// A Running processor is currently brought up and running code.
+    Running,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct Processor {
+    processor_uid: u8,
+    local_apic_id: u8,
+
+    /// The state of this processor. Always check that the processor is not `Disabled` before
+    /// attempting to bring it up!
+    state: ProcessorState,
+
+    /// Whether this processor is the Bootstrap Processor (BSP), or an Application Processor (AP).
+    /// When the bootloader is entered, the BSP is the only processor running code. To run code on
+    /// more than one processor, you need to "bring up" the APs.
+    is_ap: bool,
+}
+
+impl Processor {
+    pub(crate) fn new(
+        processor_uid: u8,
+        local_apic_id: u8,
+        state: ProcessorState,
+        is_ap: bool,
+    ) -> Processor {
+        Processor {
+            processor_uid,
+            local_apic_id,
+            state,
+            is_ap,
+        }
+    }
+}
+
 /// Describes a physical mapping created by `AcpiHandler::map_physical_region` and unmapped by
 /// `AcpiHandler::unmap_physical_region`. The region mapped must be at least `size_of::<T>()`
 /// bytes, but may be bigger.
@@ -72,10 +124,9 @@ impl<T> Deref for PhysicalMapping<T> {
     }
 }
 
-/// The kernel must provide an implementation of this trait for `acpi` to interface with. It has
-/// utility methods `acpi` uses to for e.g. mapping physical memory, but also an interface for
-/// `acpi` to tell the kernel about the tables it's parsing, such as how the kernel should
-/// configure the APIC or PCI routing.
+/// An implementation of this trait must be provided to allow `acpi` to access platform-specific
+/// functionality, such as mapping regions of physical memory. You are free to implement these
+/// however you please, as long as they conform to the documentation of each function.
 pub trait AcpiHandler {
     /// Given a starting physical address and a size, map a region of physical memory that contains
     /// a `T` (but may be bigger than `size_of::<T>()`). The address doesn't have to be page-aligned,
@@ -93,20 +144,43 @@ pub trait AcpiHandler {
     fn unmap_physical_region<T>(&mut self, region: PhysicalMapping<T>);
 }
 
-/// This struct manages the internal state of `acpi`. It is not visible to the user of the library.
-pub(crate) struct Acpi<'a, H>
-where
-    H: AcpiHandler + 'a,
-{
-    handler: &'a mut H,
+#[derive(Debug)]
+pub struct Acpi {
     acpi_revision: u8,
     namespace: BTreeMap<String, AmlValue>,
+    boot_processor: Option<Processor>,
+    application_processors: Vec<Processor>,
+
+    /// ACPI theoretically allows for more than one interrupt model to be supported by the same
+    /// hardware. For simplicity and because hardware practically will only support one model, we
+    /// just error in cases that the tables detail more than one.
+    interrupt_model: Option<InterruptModel>,
+}
+
+impl Acpi {
+    /// A description of the boot processor. Until you bring any more up, this is the only processor
+    /// running code, even on SMP systems.
+    pub fn boot_processor<'a>(&'a self) -> &'a Option<Processor> {
+        &self.boot_processor
+    }
+
+    /// Descriptions of each of the application processors. These are not brought up until you do
+    /// so. The application processors must be brought up in the order that they appear in this
+    /// list.
+    pub fn application_processors<'a>(&'a self) -> &'a Vec<Processor> {
+        &self.application_processors
+    }
+
+    /// The interrupt model supported by this system.
+    pub fn interrupt_model<'a>(&'a self) -> &'a Option<InterruptModel> {
+        &self.interrupt_model
+    }
 }
 
 /// This is the entry point of `acpi` if you have the **physical** address of the RSDP. It maps
 /// the RSDP, works out what version of ACPI the hardware supports, and passes the physical
 /// address of the RSDT/XSDT to `parse_rsdt`.
-pub fn parse_rsdp<H>(handler: &mut H, rsdp_address: usize) -> Result<(), AcpiError>
+pub fn parse_rsdp<H>(handler: &mut H, rsdp_address: usize) -> Result<Acpi, AcpiError>
 where
     H: AcpiHandler,
 {
@@ -119,7 +193,7 @@ where
 fn parse_validated_rsdp<H>(
     handler: &mut H,
     rsdp_mapping: PhysicalMapping<Rsdp>,
-) -> Result<(), AcpiError>
+) -> Result<Acpi, AcpiError>
 where
     H: AcpiHandler,
 {
@@ -153,20 +227,21 @@ pub fn parse_rsdt<H>(
     handler: &mut H,
     revision: u8,
     physical_address: usize,
-) -> Result<(), AcpiError>
+) -> Result<Acpi, AcpiError>
 where
     H: AcpiHandler,
 {
     let mut acpi = Acpi {
-        handler,
         acpi_revision: revision,
         namespace: BTreeMap::new(),
+        boot_processor: None,
+        application_processors: Vec::new(),
+        interrupt_model: None,
     };
 
-    let header = sdt::peek_at_sdt_header(acpi.handler, physical_address);
-    let mapping = acpi
-        .handler
-        .map_physical_region::<SdtHeader>(physical_address, header.length() as usize);
+    let header = sdt::peek_at_sdt_header(handler, physical_address);
+    let mapping =
+        handler.map_physical_region::<SdtHeader>(physical_address, header.length() as usize);
 
     if revision == 0 {
         /*
@@ -180,8 +255,11 @@ where
             ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u32;
 
         for i in 0..num_tables {
-            sdt::dispatch_sdt(&mut acpi, unsafe { *tables_base.offset(i as isize) }
-                as usize)?;
+            sdt::dispatch_sdt(
+                &mut acpi,
+                handler,
+                unsafe { *tables_base.offset(i as isize) } as usize,
+            )?;
         }
     } else {
         /*
@@ -195,15 +273,18 @@ where
             ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u64;
 
         for i in 0..num_tables {
-            sdt::dispatch_sdt(&mut acpi, unsafe { *tables_base.offset(i as isize) }
-                as usize)?;
+            sdt::dispatch_sdt(
+                &mut acpi,
+                handler,
+                unsafe { *tables_base.offset(i as isize) } as usize,
+            )?;
         }
     }
 
     info!("Parsed namespace: {:#?}", acpi.namespace);
 
-    acpi.handler.unmap_physical_region(mapping);
-    Ok(())
+    handler.unmap_physical_region(mapping);
+    Ok(acpi)
 }
 
 #[cfg(test)]

+ 506 - 0
src/madt.rs

@@ -0,0 +1,506 @@
+use alloc::vec::Vec;
+use bit_field::BitField;
+use core::marker::PhantomData;
+use core::mem;
+use interrupt::{
+    InterruptModel, InterruptSourceOverride, IoApic, NmiSource, Polarity, TriggerMode,
+};
+use sdt::SdtHeader;
+use {Acpi, AcpiError, AcpiHandler, PhysicalMapping, Processor, ProcessorState};
+
+#[derive(Debug)]
+pub enum MadtError {
+    UnexpectedEntry,
+    InterruptOverrideEntryHasInvalidBus,
+    InvalidLocalNmiLine,
+    NoLocalNmiLineSpecified,
+    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
+///     * The Generic Interrupt Controller (GIC) model (ARM systems only)
+#[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,
+    /*
+     * 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 ()>,
+}
+
+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)
+                                }))
+                            }
+                         )*
+
+                        /*
+                         * 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 => {}
+                    }
+                }
+            }
+
+            #[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, // 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<H>(
+    acpi: &mut Acpi,
+    handler: &mut H,
+    mapping: &PhysicalMapping<Madt>,
+) -> Result<(), AcpiError>
+where
+    H: AcpiHandler,
+{
+    (*mapping).header.validate(b"APIC")?;
+
+    /*
+     * If the MADT doesn't contain another supported interrupt model (either APIC, SAPIC, X2APIC or
+     * GIC), and the system supports the legacy i8259 PIC, recommend that.
+     * TODO: It's not clear how trustworthy this field is - should we be relying on it in any way?
+     */
+    if (*mapping).supports_8259() {
+        acpi.interrupt_model = Some(InterruptModel::Pic);
+    }
+
+    /*
+     * We first do a pass through the MADT to determine which interrupt model is being used.
+     */
+    for entry in (*mapping).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(_) => {
+                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(())
+}
+
+/// This parses the MADT and gathers information about a APIC interrupt model. We error if we
+/// encounter an entry that doesn't configure the APIC.
+fn parse_apic_model(
+    acpi: &mut Acpi,
+    mapping: &PhysicalMapping<Madt>,
+) -> Result<InterruptModel, AcpiError> {
+    use interrupt::LocalInterruptLine;
+
+    let mut local_apic_address = (*mapping).local_apic_address as u64;
+    let mut io_apics = Vec::new();
+    let mut local_apic_nmi_line = None;
+    let mut interrupt_source_overrides = Vec::new();
+    let mut nmi_sources = Vec::new();
+
+    for entry in (*mapping).entries() {
+        match entry {
+            MadtEntry::LocalApic(ref 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 = 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::new(entry.processor_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_line = Some(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 {
+        local_apic_address,
+        io_apics,
+        local_apic_nmi_line: local_apic_nmi_line
+            .ok_or(AcpiError::InvalidMadt(MadtError::NoLocalNmiLineSpecified))?,
+        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))
+}

+ 2 - 1
src/rsdp_search.rs

@@ -1,5 +1,6 @@
 use core::{mem, ops::RangeInclusive};
 use rsdp::Rsdp;
+use Acpi;
 use {parse_validated_rsdp, AcpiError, AcpiHandler};
 
 /// The pointer to the EBDA (Extended Bios Data Area) start segment pointer
@@ -55,7 +56,7 @@ where
 ///
 /// This function is unsafe because it may read from protected memory if the computer is using UEFI.
 /// Only use this function if you are sure the computer is using BIOS.
-pub unsafe fn search_for_rsdp_bios<H>(handler: &mut H) -> Result<(), AcpiError>
+pub unsafe fn search_for_rsdp_bios<H>(handler: &mut H) -> Result<Acpi, AcpiError>
 where
     H: AcpiHandler,
 {

+ 20 - 14
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
@@ -155,15 +156,15 @@ where
 
 /// This takes the physical address of an SDT, maps it correctly and dispatches it to whatever
 /// function parses that table.
-pub(crate) fn dispatch_sdt<'a, 'h, H>(
-    acpi: &'a mut Acpi<'h, H>,
+pub(crate) fn dispatch_sdt<H>(
+    acpi: &mut Acpi,
+    handler: &mut H,
     physical_address: usize,
 ) -> Result<(), AcpiError>
 where
-    'h: 'a,
-    H: AcpiHandler + 'a,
+    H: AcpiHandler,
 {
-    let header = peek_at_sdt_header(acpi.handler, physical_address);
+    let header = peek_at_sdt_header(handler, physical_address);
     info!(
         "Dispatching SDT with signature {:?} and length {:?}",
         header.signature(),
@@ -176,19 +177,24 @@ where
      */
     match header.signature() {
         "FACP" => {
-            let fadt_mapping = acpi
-                .handler
-                .map_physical_region::<Fadt>(physical_address, mem::size_of::<Fadt>());
-            ::fadt::parse_fadt(acpi, &fadt_mapping)?;
-            acpi.handler.unmap_physical_region(fadt_mapping);
+            let fadt_mapping =
+                handler.map_physical_region::<Fadt>(physical_address, mem::size_of::<Fadt>());
+            ::fadt::parse_fadt(acpi, handler, &fadt_mapping)?;
+            handler.unmap_physical_region(fadt_mapping);
         }
 
         "HPET" => {
-            let hpet_mapping = acpi
-                .handler
-                .map_physical_region::<Hpet>(physical_address, mem::size_of::<Hpet>());
+            let hpet_mapping =
+                handler.map_physical_region::<Hpet>(physical_address, mem::size_of::<Hpet>());
             ::hpet::parse_hpet(&hpet_mapping)?;
-            acpi.handler.unmap_physical_region(hpet_mapping);
+            handler.unmap_physical_region(hpet_mapping);
+        }
+
+        "APIC" => {
+            let madt_mapping =
+                handler.map_physical_region::<Madt>(physical_address, header.length() as usize);
+            ::madt::parse_madt(acpi, handler, &madt_mapping)?;
+            handler.unmap_physical_region(madt_mapping);
         }
 
         signature => {