diff --git a/Cargo.toml b/Cargo.toml index 1d14a8e4..d51c67f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ log = "0.4.20" spinning_top = "0.3.0" pci_types = { version = "0.10.0", public = true } byteorder = { version = "1.5.0", default-features = false } +smallvec = { version = "1.15.2", default-features = false } [dev-dependencies] aml_test_tools = { path = "tools/aml_test_tools" } diff --git a/src/aml/pci_routing.rs b/src/aml/pci_routing.rs index 5b3d037e..47df1b13 100644 --- a/src/aml/pci_routing.rs +++ b/src/aml/pci_routing.rs @@ -5,7 +5,7 @@ use crate::aml::{ Operation, namespace::AmlName, object::Object, - resource::{self, InterruptPolarity, InterruptTrigger, Resource}, + resource::{self, InterruptPolarity, InterruptTrigger, Irqs, Resource}, }; use alloc::{vec, vec::Vec}; use bit_field::BitField; @@ -191,7 +191,7 @@ impl PciRoutingTable { polarity: InterruptPolarity::ActiveLow, is_shared: true, is_wake_capable: false, - irq: gsi, + irqs: Irqs::from_buf([gsi]), }), PciRouteType::LinkObject(ref name) => { let path = AmlName::from_str("_CRS").unwrap().resolve(name)?; diff --git a/src/aml/resource.rs b/src/aml/resource.rs index 9af13af2..e7700125 100644 --- a/src/aml/resource.rs +++ b/src/aml/resource.rs @@ -4,6 +4,7 @@ use alloc::vec::Vec; use bit_field::BitField; use byteorder::{ByteOrder, LittleEndian}; use core::mem; +pub use smallvec; #[derive(Debug, PartialEq, Eq)] pub enum Resource { @@ -339,6 +340,8 @@ fn address_space_descriptor(bytes: &[u8]) -> Result { })) } +pub type Irqs = smallvec::SmallVec<[u32; 1]>; + #[derive(Debug, PartialEq, Eq, Clone)] pub struct IrqDescriptor { pub is_consumer: bool, @@ -346,11 +349,7 @@ pub struct IrqDescriptor { 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, + pub irqs: Irqs, } fn irq_format_descriptor(bytes: &[u8]) -> Result { @@ -386,6 +385,15 @@ fn irq_format_descriptor(bytes: &[u8]) -> Result { * 0 Level-Triggered – Interrupt is triggered in response to signal in a low state. * 1 Edge-Triggered – Interrupt is triggered in response to a change in signal state from low to high. */ + fn irqs_from_bit_mask(bit_mask: u16) -> Irqs { + let mut irqs = Irqs::default(); + for bit in 0..16 { + if bit_mask.get_bit(bit) { + irqs.push(bit as u32); + } + } + irqs + } match bytes.len() { 0..=2 => Err(AmlError::InvalidResourceDescriptor), @@ -394,7 +402,7 @@ fn irq_format_descriptor(bytes: &[u8]) -> Result { let irq = LittleEndian::read_u16(&bytes[1..=2]); Ok(Resource::Irq(IrqDescriptor { - irq: irq as u32, + irqs: irqs_from_bit_mask(irq), is_wake_capable: false, is_shared: false, polarity: InterruptPolarity::ActiveHigh, @@ -420,7 +428,7 @@ fn irq_format_descriptor(bytes: &[u8]) -> Result { }; Ok(Resource::Irq(IrqDescriptor { - irq: irq as u32, + irqs: irqs_from_bit_mask(irq), is_wake_capable, is_shared, polarity, @@ -568,8 +576,25 @@ fn extended_interrupt_descriptor(bytes: &[u8]) -> Result { } 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]]); + + let irqs = if number_of_interrupts == 1 { + let irq = LittleEndian::read_u32(&bytes[5..9]); + + Irqs::from_buf([irq]) + } else { + let mut irqs = Irqs::with_capacity(number_of_interrupts); + + for i in 0..number_of_interrupts { + let start = 5 + i * size_of::(); + let end = start + 4; + + let irq = LittleEndian::read_u32(&bytes[start..end]); + + irqs.push(irq); + } + + irqs + }; Ok(Resource::Irq(IrqDescriptor { is_consumer: bytes[3].get_bit(0), @@ -577,7 +602,7 @@ fn extended_interrupt_descriptor(bytes: &[u8]) -> Result { 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, + irqs, })) } @@ -641,7 +666,7 @@ mod tests { polarity: InterruptPolarity::ActiveHigh, is_shared: false, is_wake_capable: false, - irq: (1 << 1) + irqs: Irqs::from_buf([1 << 1]) }) ]) ); @@ -898,7 +923,7 @@ mod tests { polarity: InterruptPolarity::ActiveHigh, is_shared: false, is_wake_capable: false, - irq: (1 << 6) + irqs: Irqs::from_buf([1 << 6]) }), Resource::Dma(DMADescriptor { channel_mask: 1 << 2, diff --git a/tests/package_name_references.rs b/tests/package_name_references.rs index ee90e85e..2d65b51a 100644 --- a/tests/package_name_references.rs +++ b/tests/package_name_references.rs @@ -8,6 +8,7 @@ use acpi::aml::{ namespace::AmlName, object::Object, pci_routing::{PciRoutingTable, Pin}, + resource::Irqs, }; use aml_test_tools::handlers::null_handler::NullHandler; use std::str::FromStr; @@ -154,9 +155,9 @@ DefinitionBlock("", "DSDT", 2, "RSACPI", "PRTTST", 1) { // An absolute name, a relative name found by the namespace search rules, and a GSI. // The IRQ of a link object is decoded from its `_CRS` as a mask. - assert_eq!(table.route(1, 0, Pin::IntA, &interpreter).unwrap().irq, 1 << 11); - assert_eq!(table.route(2, 0, Pin::IntB, &interpreter).unwrap().irq, 1 << 10); - assert_eq!(table.route(3, 0, Pin::IntC, &interpreter).unwrap().irq, 19); + assert_eq!(table.route(1, 0, Pin::IntA, &interpreter).unwrap().irqs, Irqs::from_buf([1 << 11])); + assert_eq!(table.route(2, 0, Pin::IntB, &interpreter).unwrap().irqs, Irqs::from_buf([1 << 10])); + assert_eq!(table.route(3, 0, Pin::IntC, &interpreter).unwrap().irqs, Irqs::from_buf([19])); } /// `DerefOf` of a string is a namespace lookup, and must not recurse into the object it finds -