Browse Source

Merge branch 'master' into implement_acpi_pm_timer

toku-sa-n 4 years ago
parent
commit
854c36c7c1
10 changed files with 260 additions and 163 deletions
  1. 12 12
      acpi/src/fadt.rs
  2. 2 2
      acpi/src/hpet.rs
  3. 1 10
      acpi/src/lib.rs
  4. 97 0
      acpi/src/platform/address.rs
  5. 90 0
      acpi/src/platform/interrupt.rs
  6. 16 89
      acpi/src/platform/mod.rs
  7. 0 0
      acpi/src/pm_timer.rs
  8. 1 1
      aml/Cargo.toml
  9. 1 1
      rsdp/Cargo.toml
  10. 40 48
      rsdp/src/lib.rs

+ 12 - 12
acpi/src/fadt.rs

@@ -1,9 +1,9 @@
 use crate::{
+    platform::address::RawGenericAddress,
     sdt::{ExtendedField, SdtHeader},
     AcpiError,
     AcpiTable,
     AcpiTables,
-    GenericAddress,
 };
 use bit_field::BitField;
 use rsdp::handler::AcpiHandler;
@@ -74,22 +74,22 @@ pub struct Fadt {
     iapc_boot_arch: u16,
     _reserved2: u8, // must be 0
     flags: u32,
-    reset_reg: GenericAddress,
+    reset_reg: RawGenericAddress,
     reset_value: u8,
     arm_boot_arch: u16,
     fadt_minor_version: u8,
     x_firmware_ctrl: ExtendedField<u64, 2>,
     x_dsdt_address: ExtendedField<u64, 2>,
-    x_pm1a_event_block: ExtendedField<GenericAddress, 2>,
-    x_pm1b_event_block: ExtendedField<GenericAddress, 2>,
-    x_pm1a_control_block: ExtendedField<GenericAddress, 2>,
-    x_pm1b_control_block: ExtendedField<GenericAddress, 2>,
-    x_pm2_control_block: ExtendedField<GenericAddress, 2>,
-    x_pm_timer_block: ExtendedField<GenericAddress, 2>,
-    x_gpe0_block: ExtendedField<GenericAddress, 2>,
-    x_gpe1_block: ExtendedField<GenericAddress, 2>,
-    sleep_control_reg: ExtendedField<GenericAddress, 2>,
-    sleep_status_reg: ExtendedField<GenericAddress, 2>,
+    x_pm1a_event_block: ExtendedField<RawGenericAddress, 2>,
+    x_pm1b_event_block: ExtendedField<RawGenericAddress, 2>,
+    x_pm1a_control_block: ExtendedField<RawGenericAddress, 2>,
+    x_pm1b_control_block: ExtendedField<RawGenericAddress, 2>,
+    x_pm2_control_block: ExtendedField<RawGenericAddress, 2>,
+    x_pm_timer_block: ExtendedField<RawGenericAddress, 2>,
+    x_gpe0_block: ExtendedField<RawGenericAddress, 2>,
+    x_gpe1_block: ExtendedField<RawGenericAddress, 2>,
+    sleep_control_reg: ExtendedField<RawGenericAddress, 2>,
+    sleep_status_reg: ExtendedField<RawGenericAddress, 2>,
     hypervisor_vendor_id: ExtendedField<u64, 2>,
 }
 

+ 2 - 2
acpi/src/hpet.rs

@@ -1,4 +1,4 @@
-use crate::{sdt::SdtHeader, AcpiError, AcpiHandler, AcpiTable, AcpiTables, GenericAddress};
+use crate::{platform::address::RawGenericAddress, sdt::SdtHeader, AcpiError, AcpiHandler, AcpiTable, AcpiTables};
 use bit_field::BitField;
 
 #[derive(Debug)]
@@ -56,7 +56,7 @@ impl HpetInfo {
 pub(crate) struct HpetTable {
     header: SdtHeader,
     event_timer_block_id: u32,
-    base_address: GenericAddress,
+    base_address: RawGenericAddress,
     hpet_number: u8,
     clock_tick_unit: u16,
     page_protection_oem: u8,

+ 1 - 10
acpi/src/lib.rs

@@ -83,6 +83,7 @@ pub enum AcpiError {
     TableMissing(Signature),
     InvalidDsdtAddress,
     InvalidMadt(MadtError),
+    InvalidGenericAddress,
 }
 
 pub struct AcpiTables<H>
@@ -301,13 +302,3 @@ impl AmlTable {
         }
     }
 }
-
-#[derive(Clone, Copy, Debug)]
-#[repr(C, packed)]
-pub(crate) struct GenericAddress {
-    address_space: u8,
-    bit_width: u8,
-    bit_offset: u8,
-    access_size: u8,
-    address: u64,
-}

+ 97 - 0
acpi/src/platform/address.rs

@@ -0,0 +1,97 @@
+//! ACPI defines a Generic Address Structure (GAS), which provides a versatile way to describe register locations
+//! in a wide range of address spaces.
+
+use crate::AcpiError;
+
+/// This is the raw form of a Generic Address Structure, and follows the layout found in the ACPI tables. It does
+/// not form part of the public API, and should be turned into a `GenericAddress` for most use-cases.
+#[derive(Clone, Copy, Debug)]
+#[repr(C, packed)]
+pub(crate) struct RawGenericAddress {
+    pub address_space: u8,
+    pub bit_width: u8,
+    pub bit_offset: u8,
+    pub access_size: u8,
+    pub address: u64,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum AddressSpace {
+    SystemMemory,
+    SystemIo,
+    /// Describes a register in the configuration space of a PCI device in segment `0`, on bus `0`. The `address`
+    /// field is of the format:
+    /// ```ignore
+    /// 64              48              32              16               0
+    ///  +---------------+---------------+---------------+---------------+
+    ///  |  reserved (0) |    device     |   function    |    offset     |
+    ///  +---------------+---------------+---------------+---------------+
+    /// ```
+    PciConfigSpace,
+    EmbeddedController,
+    SMBus,
+    SystemCmos,
+    PciBarTarget,
+    Ipmi,
+    GeneralIo,
+    GenericSerialBus,
+    PlatformCommunicationsChannel,
+    FunctionalFixedHardware,
+    OemDefined(u8),
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum AccessSize {
+    Undefined,
+    ByteAccess,
+    WordAccess,
+    DWordAccess,
+    QWordAccess,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct GenericAddress {
+    pub address_space: AddressSpace,
+    pub bit_width: u8,
+    pub bit_offset: u8,
+    pub access_size: AccessSize,
+    pub address: u64,
+}
+
+impl GenericAddress {
+    pub(crate) fn from_raw(raw: RawGenericAddress) -> Result<GenericAddress, AcpiError> {
+        let address_space = match raw.address_space {
+            0x00 => AddressSpace::SystemMemory,
+            0x01 => AddressSpace::SystemIo,
+            0x02 => AddressSpace::PciConfigSpace,
+            0x03 => AddressSpace::EmbeddedController,
+            0x04 => AddressSpace::SMBus,
+            0x05 => AddressSpace::SystemCmos,
+            0x06 => AddressSpace::PciBarTarget,
+            0x07 => AddressSpace::Ipmi,
+            0x08 => AddressSpace::GeneralIo,
+            0x09 => AddressSpace::GenericSerialBus,
+            0x0a => AddressSpace::PlatformCommunicationsChannel,
+            0x0b..=0x7e => return Err(AcpiError::InvalidGenericAddress),
+            0x7f => AddressSpace::FunctionalFixedHardware,
+            0x80..=0xbf => return Err(AcpiError::InvalidGenericAddress),
+            0xc0..=0xff => AddressSpace::OemDefined(raw.address_space),
+        };
+        let access_size = match raw.access_size {
+            0 => AccessSize::Undefined,
+            1 => AccessSize::ByteAccess,
+            2 => AccessSize::WordAccess,
+            3 => AccessSize::DWordAccess,
+            4 => AccessSize::QWordAccess,
+            _ => return Err(AcpiError::InvalidGenericAddress),
+        };
+
+        Ok(GenericAddress {
+            address_space,
+            bit_width: raw.bit_width,
+            bit_offset: raw.bit_offset,
+            access_size,
+            address: raw.address,
+        })
+    }
+}

+ 90 - 0
acpi/src/platform/interrupt.rs

@@ -0,0 +1,90 @@
+use alloc::vec::Vec;
+
+#[derive(Debug)]
+pub struct IoApic {
+    pub id: u8,
+    pub address: u32,
+    pub global_system_interrupt_base: u32,
+}
+
+#[derive(Debug)]
+pub struct NmiLine {
+    pub processor: NmiProcessor,
+    pub line: LocalInterruptLine,
+}
+
+#[derive(Debug)]
+pub enum LocalInterruptLine {
+    Lint0,
+    Lint1,
+}
+
+#[derive(Debug)]
+pub enum NmiProcessor {
+    All,
+    /// Refers to a processor with the given UID. This is stored as a `u32`, but should be casted to `u8` when the
+    /// DSDT uses the deprecated `DefProcessor` operator to define processor UIDs.
+    ProcessorUid(u32),
+}
+
+#[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 struct Apic {
+    pub local_apic_address: u64,
+    pub io_apics: Vec<IoApic>,
+    pub local_apic_nmi_lines: Vec<NmiLine>,
+    pub interrupt_source_overrides: Vec<InterruptSourceOverride>,
+    pub 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.
+    pub also_has_legacy_pics: bool,
+}
+
+#[derive(Debug)]
+#[non_exhaustive]
+pub enum InterruptModel {
+    /// This model is only chosen when the MADT does not describe another interrupt model. On `x86_64` platforms,
+    /// this probably means only the legacy i8259 PIC is present.
+    Unknown,
+
+    /// 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(Apic),
+}

+ 16 - 89
acpi/src/platform.rs → acpi/src/platform/mod.rs

@@ -1,95 +1,22 @@
+pub mod address;
+pub mod interrupt;
+
+pub use interrupt::{
+    Apic,
+    InterruptModel,
+    InterruptSourceOverride,
+    IoApic,
+    LocalInterruptLine,
+    NmiLine,
+    NmiProcessor,
+    NmiSource,
+    Polarity,
+    TriggerMode,
+};
+
 use crate::{fadt::Fadt, madt::Madt, AcpiError, AcpiHandler, AcpiTables, PowerProfile};
 use alloc::vec::Vec;
 
-#[derive(Debug)]
-pub struct IoApic {
-    pub id: u8,
-    pub address: u32,
-    pub global_system_interrupt_base: u32,
-}
-
-#[derive(Debug)]
-pub struct NmiLine {
-    pub processor: NmiProcessor,
-    pub line: LocalInterruptLine,
-}
-
-#[derive(Debug)]
-pub enum LocalInterruptLine {
-    Lint0,
-    Lint1,
-}
-
-#[derive(Debug)]
-pub enum NmiProcessor {
-    All,
-    /// Refers to a processor with the given UID. This is stored as a `u32`, but should be casted to `u8` when the
-    /// DSDT uses the deprecated `DefProcessor` operator to define processor UIDs.
-    ProcessorUid(u32),
-}
-
-#[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 struct Apic {
-    pub local_apic_address: u64,
-    pub io_apics: Vec<IoApic>,
-    pub local_apic_nmi_lines: Vec<NmiLine>,
-    pub interrupt_source_overrides: Vec<InterruptSourceOverride>,
-    pub 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.
-    pub also_has_legacy_pics: bool,
-}
-
-#[derive(Debug)]
-#[non_exhaustive]
-pub enum InterruptModel {
-    /// This model is only chosen when the MADT does not describe another interrupt model. On `x86_64` platforms,
-    /// this probably means only the legacy i8259 PIC is present.
-    Unknown,
-
-    /// 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(Apic),
-}
-
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum ProcessorState {
     /// A processor in this state is unusable, and you must not attempt to bring it up.

+ 0 - 0
acpi/src/pm_timer.rs


+ 1 - 1
aml/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "aml"
-version = "0.9.0"
+version = "0.10.0"
 authors = ["Isaac Woods"]
 repository = "https://github.com/rust-osdev/acpi"
 description = "Library for parsing AML"

+ 1 - 1
rsdp/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "rsdp"
-version = "1.0.0"
+version = "1.1.0-pre0"
 authors = ["Isaac Woods", "Restioson"]
 repository = "https://github.com/rust-osdev/acpi"
 description = "Zero-allocation library for locating and parsing the RSDP, the first ACPI table"

+ 40 - 48
rsdp/src/lib.rs

@@ -7,10 +7,12 @@
 //! used in the `acpi` crate.
 
 #![no_std]
+#![feature(unsafe_block_in_unsafe_fn)]
+#![deny(unsafe_op_in_unsafe_fn)]
 
 pub mod handler;
 
-use core::{mem, ops::RangeInclusive, slice, str};
+use core::{mem, ops::Range, slice, str};
 use handler::{AcpiHandler, PhysicalMapping};
 use log::warn;
 
@@ -36,6 +38,7 @@ pub enum RsdpError {
 /// some new fields. For ACPI Version 1.0, these fields are not valid and should not be accessed.
 /// For ACPI Version 2.0+, `xsdt_address` should be used (truncated to `u32` on x86) instead of
 /// `rsdt_address`.
+#[derive(Clone, Copy, Debug)]
 #[repr(C, packed)]
 pub struct Rsdp {
     signature: [u8; 8],
@@ -71,51 +74,40 @@ impl Rsdp {
     where
         H: AcpiHandler,
     {
-        let areas = find_search_areas(handler.clone());
-
-        /*
-         * We map a page at a time, as mapping the entire areas puts a lot of burden on a naive paging
-         * implementation.
-         */
-        let mut area_mapping = handler.map_physical_region::<[[u8; 8]; 0x1000 / 8]>(
-            areas[0].clone().next().unwrap() & !0xfff, // Get frame addr
-            0x1000,
-        );
-
-        // Signature is always on a 16 byte boundary so only search there
-        for address in areas.iter().flat_map(|i| i.clone()).step_by(16) {
-            let mut mapping_start = area_mapping.physical_start as usize;
-            if !(mapping_start..mapping_start + 0x1000).contains(&address) {
-                /*
-                 * This replaces the current mapping, unmapping the last one.
-                 */
-                area_mapping = handler.map_physical_region::<[[u8; 8]; 0x1000 / 8]>(
-                    address & !0xfff, // Get frame addr
-                    0x1000,
-                );
-
-                // Update if mapping remapped
-                mapping_start = area_mapping.physical_start as usize;
+        let rsdp_address = {
+            let mut rsdp_address = None;
+            let areas = find_search_areas(handler.clone());
+
+            'areas: for area in areas.iter() {
+                let mapping = unsafe { handler.map_physical_region::<u8>(area.start, area.end - area.start) };
+
+                for address in area.clone().step_by(16) {
+                    let ptr_in_mapping =
+                        unsafe { mapping.virtual_start.as_ptr().offset((address - area.start) as isize) };
+                    let signature = unsafe { *(ptr_in_mapping as *const [u8; 8]) };
+
+                    if signature == *RSDP_SIGNATURE {
+                        match unsafe { *(ptr_in_mapping as *const Rsdp) }.validate() {
+                            Ok(()) => {
+                                rsdp_address = Some(address);
+                                break 'areas;
+                            }
+                            Err(err) => warn!("Invalid RSDP found at {:#x}: {:?}", address, err),
+                        }
+                    }
+                }
             }
 
-            let index = (address - mapping_start) / 8;
-            let signature = (*area_mapping)[index];
-
-            if signature != *RSDP_SIGNATURE {
-                continue;
-            }
-
-            let rsdp_mapping = handler.map_physical_region::<Rsdp>(address, mem::size_of::<Rsdp>());
+            rsdp_address
+        };
 
-            if let Err(e) = (*rsdp_mapping).validate() {
-                warn!("Invalid RSDP found at 0x{:x}: {:?}", address, e);
-                continue;
+        match rsdp_address {
+            Some(address) => {
+                let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(address, mem::size_of::<Rsdp>()) };
+                Ok(rsdp_mapping)
             }
-
-            return Ok(rsdp_mapping);
+            None => Err(RsdpError::NoValidRsdp),
         }
-
-        Err(RsdpError::NoValidRsdp)
     }
 
     /// Checks that:
@@ -126,7 +118,7 @@ impl Rsdp {
         const RSDP_V1_LENGTH: usize = 20;
 
         // Check the signature
-        if &self.signature != b"RSD PTR " {
+        if &self.signature != RSDP_SIGNATURE {
             return Err(RsdpError::IncorrectSignature);
         }
 
@@ -175,7 +167,7 @@ impl Rsdp {
 }
 
 /// Find the areas we should search for the RSDP in.
-pub fn find_search_areas<H>(handler: H) -> [RangeInclusive<usize>; 2]
+pub fn find_search_areas<H>(handler: H) -> [Range<usize>; 2]
 where
     H: AcpiHandler,
 {
@@ -189,18 +181,18 @@ where
 
     [
         /*
-         * The main BIOS area below 1mb. In practice, from my [Restioson's] testing, the RSDP is more often here
+         * The main BIOS area below 1MiB. In practice, from my [Restioson's] testing, the RSDP is more often here
          * than the EBDA. We also don't want to search the entire possibele EBDA range, if we've failed to find it
          * from the BDA.
          */
-        RSDP_BIOS_AREA_START..=RSDP_BIOS_AREA_END,
+        RSDP_BIOS_AREA_START..(RSDP_BIOS_AREA_END + 1),
         // Check if base segment ptr is in valid range for EBDA base
         if (EBDA_EARLIEST_START..EBDA_END).contains(&ebda_start) {
-            // First kb of EBDA
-            ebda_start..=ebda_start + 1024
+            // First KiB of EBDA
+            ebda_start..ebda_start + 1024
         } else {
             // We don't know where the EBDA starts, so just search the largest possible EBDA
-            EBDA_EARLIEST_START..=EBDA_END
+            EBDA_EARLIEST_START..(EBDA_END + 1)
         },
     ]
 }