Browse Source

Parse Extended Interrupt Descriptor _CRS objects

Isaac Woods 5 years ago
parent
commit
f2f55ae8ab
3 changed files with 166 additions and 0 deletions
  1. 1 0
      aml/Cargo.toml
  2. 7 0
      aml/src/lib.rs
  3. 158 0
      aml/src/resource.rs

+ 1 - 0
aml/Cargo.toml

@@ -12,6 +12,7 @@ edition = "2018"
 [dependencies]
 log = "0.4"
 bit_field = "0.10"
+byteorder = { version = "1", default-features = false }
 
 [features]
 debug_parser = []

+ 7 - 0
aml/src/lib.rs

@@ -49,6 +49,7 @@ pub(crate) mod opcode;
 pub(crate) mod parser;
 pub mod pci_routing;
 pub(crate) mod pkg_length;
+pub mod resource;
 pub(crate) mod term_object;
 pub(crate) mod type1;
 pub(crate) mod type2;
@@ -257,4 +258,10 @@ pub enum AmlError {
     PrtInvalidPin,
     PrtInvalidSource,
     PrtInvalidGsi,
+
+    /*
+     * Errors produced parsing Resource Descriptors.
+     */
+    ReservedResourceType,
+    ResourceDescriptorTooShort,
 }

+ 158 - 0
aml/src/resource.rs

@@ -0,0 +1,158 @@
+use crate::{value::AmlValue, AmlError};
+use bit_field::BitField;
+use byteorder::{ByteOrder, LittleEndian};
+
+#[derive(Debug)]
+pub enum Resource {
+    Irq(IrqDescriptor),
+}
+
+/// Parse a `ResourceDescriptor`. Returns `AmlError::IncompatibleValueConversion` if the passed value is not a
+/// `Buffer`.
+pub(crate) fn resource_descriptor(descriptor: &AmlValue) -> Result<Resource, AmlError> {
+    if let AmlValue::Buffer { bytes, size } = descriptor {
+        /*
+         * If bit 7 of Byte 0 is set, it's a large descriptor. If not, it's a small descriptor.
+         */
+        if bytes[0].get_bit(7) {
+            /*
+             * We're parsing a large item. The descriptor type is encoded in Bits 0-6 of Byte 0. Valid types:
+             *      0x00: Reserved
+             *      0x01: 24-bit Memory Range Descriptor
+             *      0x02: Generic Register Descriptor
+             *      0x03: Reserved
+             *      0x04: Vendor-defined Descriptor
+             *      0x05: 32-bit Memory Range Descriptor
+             *      0x06: 32-bit Fixed Memory Range Descriptor
+             *      0x07: Address Space Resource Descriptor
+             *      0x08: Word Address Space Descriptor
+             *      0x09: Extended Interrupt Descriptor
+             *      0x0a: QWord Address Space Descriptor
+             *      0x0b: Extended Address Space Descriptor
+             *      0x0c: GPIO Connection Descriptor
+             *      0x0d: Pin Function Descriptor
+             *      0x0e: GenericSerialBus Connection Descriptor
+             *      0x0f: Pin Configuration Descriptor
+             *      0x10: Pin Group Descriptor
+             *      0x11: Pin Group Function Descriptor
+             *      0x12: Pin Group Configuration Descriptor
+             *      0x13-0x7f: Reserved
+             *
+             * Byte 1 contains bits 0-7 of the length, and Byte 2 contains bits 8-15 of the length. Subsequent
+             * bytes contain the actual data items.
+             */
+            let descriptor_type = bytes[0].get_bits(0..7);
+            let length = LittleEndian::read_u16(&[bytes[1], bytes[2]]);
+
+            match descriptor_type {
+                0x01 => unimplemented!(),
+                0x02 => unimplemented!(),
+                0x03 => unimplemented!(),
+                0x04 => unimplemented!(),
+                0x05 => unimplemented!(),
+                0x06 => unimplemented!(),
+                0x07 => unimplemented!(),
+                0x08 => unimplemented!(),
+                0x09 => extended_interrupt_descriptor(bytes),
+                0x0a => unimplemented!(),
+                0x0b => unimplemented!(),
+                0x0c => unimplemented!(),
+                0x0d => unimplemented!(),
+                0x0e => unimplemented!(),
+                0x0f => unimplemented!(),
+                0x10 => unimplemented!(),
+                0x11 => unimplemented!(),
+                0x12 => unimplemented!(),
+
+                0x00 | 0x13..=0x7f => Err(AmlError::ReservedResourceType),
+                0x80..=0xff => unreachable!(),
+            }
+        } else {
+            /*
+             * We're parsing a small descriptor. Byte 0 has the format:
+             *    | Bits        | Field             |
+             *    |-------------|-------------------|
+             *    | 0-2         | Length - n bytes  |
+             *    | 3-6         | Small item type   |
+             *    | 7           | 0 = small item    |
+             *
+             * The valid types are:
+             *      0x00-0x03: Reserved
+             *      0x04: IRQ Format Descriptor
+             *      0x05: DMA Format Descriptor
+             *      0x06: Start Dependent Functions Descriptor
+             *      0x07: End Dependent Functions Descriptor
+             *      0x08: IO Port Descriptor
+             *      0x09: Fixed Location IO Port Descriptor
+             *      0x0A: Fixed DMA Descriptor
+             *      0x0B-0x0D: Reserved
+             *      0x0E: Vendor Defined Descriptor
+             *      0x0F: End Tag Descriptor
+             */
+            unimplemented!()
+        }
+    } else {
+        Err(AmlError::IncompatibleValueConversion)
+    }
+}
+
+#[derive(Debug)]
+pub enum InterruptTrigger {
+    Edge,
+    Level,
+}
+
+#[derive(Debug)]
+pub enum InterruptPolarity {
+    ActiveHigh,
+    ActiveLow,
+}
+
+#[derive(Debug)]
+pub struct IrqDescriptor {
+    pub is_consumer: bool,
+    pub trigger: InterruptTrigger,
+    pub polarity: InterruptPolarity,
+    pub is_shared: bool,
+    pub is_wake_capable: bool,
+    /*
+     * NOTE: We currently only support the cases where a descriptor only contains a single interrupt
+     * number.
+     */
+    pub irq: u32,
+}
+
+fn extended_interrupt_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {
+    /*
+     * --- Extended Interrupt Descriptor ---
+     * Byte 3 contains the Interrupt Vector Flags:
+     *      Bit 0: 1 if device consumes the resource, 0 if it produces it
+     *      Bit 1: 1 if edge-triggered, 0 if level-triggered
+     *      Bit 2: 1 = active-high, 0 = active-low
+     *      Bit 3: 1 if interrupt is shared with other devices
+     *      Bit 4: 1 if this interrupt is capable of waking the system, 0 if it is not
+     * Byte 4 contains the number of interrupt numbers that follow. When this descriptor is
+     * returned from `_CRS` or send to `_SRS`, this field must be 1.
+     *
+     * From Byte 5 onwards, there are `n` interrupt numbers, each of which is encoded as a
+     * 4-byte little-endian number.
+     *
+     * NOTE: We only support the case where there is a single interrupt number.
+     */
+    if bytes.len() < 9 {
+        return Err(AmlError::ResourceDescriptorTooShort);
+    }
+
+    let number_of_interrupts = bytes[4] as usize;
+    assert_eq!(number_of_interrupts, 1);
+    let irq = LittleEndian::read_u32(&[bytes[5], bytes[6], bytes[7], bytes[8]]);
+
+    Ok(Resource::Irq(IrqDescriptor {
+        is_consumer: bytes[3].get_bit(0),
+        trigger: if bytes[3].get_bit(1) { InterruptTrigger::Edge } else { InterruptTrigger::Level },
+        polarity: if bytes[3].get_bit(2) { InterruptPolarity::ActiveLow } else { InterruptPolarity::ActiveHigh },
+        is_shared: bytes[3].get_bit(3),
+        is_wake_capable: bytes[3].get_bit(4),
+        irq,
+    }))
+}