From 6bcbb31f9c35a2d950358626bc3806ffea1d9feb Mon Sep 17 00:00:00 2001 From: Daniel Wyatt Date: Tue, 1 Sep 2026 00:14:24 -0400 Subject: [PATCH 1/7] Add SerialBus+I2C resource descriptors. Only I2C for now, but trivial to expand support. --- src/aml/resource.rs | 100 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/src/aml/resource.rs b/src/aml/resource.rs index e7700125..90bc5657 100644 --- a/src/aml/resource.rs +++ b/src/aml/resource.rs @@ -13,6 +13,7 @@ pub enum Resource { MemoryRange(MemoryRangeDescriptor), IOPort(IOPortDescriptor), Dma(DMADescriptor), + SerialBus(SerialBusDescriptor), } const UNSUPPORTED_LARGE_RESOURCE_DESCRIPTORS: &[(u8, &str)] = &[ @@ -24,7 +25,6 @@ const UNSUPPORTED_LARGE_RESOURCE_DESCRIPTORS: &[(u8, &str)] = &[ (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"), @@ -118,6 +118,7 @@ fn resource_descriptor(bytes: &[u8]) -> Result<(Option, &[u8]), AmlErr 0x08 => address_space_descriptor::(descriptor_bytes), 0x09 => extended_interrupt_descriptor(descriptor_bytes), 0x0a => address_space_descriptor::(descriptor_bytes), + 0x0e => serial_bus_descriptor(descriptor_bytes), 0x00 | 0x13..=0x7f => Err(AmlError::InvalidResourceDescriptor), 0x80..=0xff => unreachable!(), @@ -606,6 +607,101 @@ fn extended_interrupt_descriptor(bytes: &[u8]) -> Result { })) } +#[derive(Debug, PartialEq, Eq)] +pub enum ShareType { + Shared, + Exclusive, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum ResourceUsage { + ResourceConsumer, + ResourceProducer, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum SlaveMode { + ControllerInitiated, + DeviceInitiated, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum AddressingMode { + AddressingMode7Bit, + AddressingMode10Bit, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct SerialBusDescriptor { + pub sharing: ShareType, + pub usage: ResourceUsage, + pub slave_mode: SlaveMode, + pub source: crate::aml::String, + pub source_index: u8, + pub vendor_data: Vec, + pub bus: SerialBus, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum SerialBus { + I2C { addressing_mode: AddressingMode, speed: u32, address: u16 }, +} + +fn i2c_bus_descriptor(bytes: &[u8]) -> Result<(SerialBus, (usize, usize)), AmlError> { + if bytes.len() < 18 { + return Err(AmlError::InvalidResourceDescriptor); + } + // revision + if bytes[9] > 2 { + return Err(AmlError::InvalidResourceDescriptor); + } + let type_length = LittleEndian::read_u16(&bytes[10..=11]) as usize; + if type_length < 6 { + return Err(AmlError::InvalidResourceDescriptor); + } + let speed = LittleEndian::read_u32(&bytes[12..=15]); + let address = LittleEndian::read_u16(&bytes[16..=17]); + Ok(( + SerialBus::I2C { + addressing_mode: if bytes[7].get_bit(0) { + AddressingMode::AddressingMode10Bit + } else { + AddressingMode::AddressingMode7Bit + }, + speed, + address, + }, + (18, type_length - 6), + )) +} + +fn serial_bus_descriptor(bytes: &[u8]) -> Result { + if bytes.len() < 7 { + return Err(AmlError::InvalidResourceDescriptor); + } + // revision + if bytes[3] > 2 { + return Err(AmlError::InvalidResourceDescriptor); + } + let (descriptor, (vendor_data_idx, vendor_data_length)) = match bytes[5] { + 1 => i2c_bus_descriptor(bytes)?, + _ => return Err(AmlError::LibUnimplemented), + }; + let vendor_data = &bytes[vendor_data_idx..vendor_data_idx + vendor_data_length]; + let source = str::from_utf8(&bytes[vendor_data_idx + vendor_data_length..bytes.len() - 1]) + .map_err(|_| AmlError::InvalidResourceDescriptor)?; + + Ok(Resource::SerialBus(SerialBusDescriptor { + sharing: if bytes[6].get_bit(2) { ShareType::Shared } else { ShareType::Exclusive }, + usage: if bytes[6].get_bit(1) { ResourceUsage::ResourceConsumer } else { ResourceUsage::ResourceProducer }, + slave_mode: if bytes[6].get_bit(0) { SlaveMode::DeviceInitiated } else { SlaveMode::ControllerInitiated }, + source_index: bytes[4], + source: alloc::string::String::from(source), + vendor_data: Vec::from(vendor_data), + bus: descriptor, + })) +} + #[cfg(test)] mod tests { use super::*; @@ -821,7 +917,7 @@ mod tests { #[test] fn skips_unsupported_large_resource_descriptors() { let bytes: Vec = [ - 0x8e, 0x01, 0x00, 0x00, // GenericSerialBus descriptor with one byte of payload. + 0x82, 0x01, 0x00, 0x00, // Generic Register descriptor with one byte of payload. 0x86, 0x09, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, // Memory32Fixed(ReadWrite, 0x1000, 0x1000) 0x79, 0x00, // End tag. From c3adfb10de7889f546f242e5bb14ba762f3ed1e7 Mon Sep 17 00:00:00 2001 From: Daniel Wyatt Date: Tue, 1 Sep 2026 00:20:53 -0400 Subject: [PATCH 2/7] Add GPIO connection resource descriptors. --- src/aml/resource.rs | 119 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 1 deletion(-) diff --git a/src/aml/resource.rs b/src/aml/resource.rs index 90bc5657..a70e5bbc 100644 --- a/src/aml/resource.rs +++ b/src/aml/resource.rs @@ -14,6 +14,7 @@ pub enum Resource { IOPort(IOPortDescriptor), Dma(DMADescriptor), SerialBus(SerialBusDescriptor), + GPIO(GPIOConnectionDescriptor), } const UNSUPPORTED_LARGE_RESOURCE_DESCRIPTORS: &[(u8, &str)] = &[ @@ -23,7 +24,6 @@ const UNSUPPORTED_LARGE_RESOURCE_DESCRIPTORS: &[(u8, &str)] = &[ (0x04, "Vendor-defined Descriptor"), (0x05, "32-bit Memory Range Descriptor"), (0x0b, "Extended Address Space Descriptor"), - (0x0c, "GPIO Connection Descriptor"), (0x0d, "Pin Function Descriptor"), (0x0f, "Pin Configuration Descriptor"), (0x10, "Pin Group Descriptor"), @@ -119,6 +119,7 @@ fn resource_descriptor(bytes: &[u8]) -> Result<(Option, &[u8]), AmlErr 0x09 => extended_interrupt_descriptor(descriptor_bytes), 0x0a => address_space_descriptor::(descriptor_bytes), 0x0e => serial_bus_descriptor(descriptor_bytes), + 0x0c => gpio_connection_descriptor(descriptor_bytes), 0x00 | 0x13..=0x7f => Err(AmlError::InvalidResourceDescriptor), 0x80..=0xff => unreachable!(), @@ -702,6 +703,122 @@ fn serial_bus_descriptor(bytes: &[u8]) -> Result { })) } +#[derive(Debug, PartialEq, Eq)] +pub enum GPIOInterruptPolarity { + ActiveHigh, + ActiveLow, + ActiveBoth, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum WakeCapability { + NotWakeCapable, + WakeCapable, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum IORestriction { + None, + InputOnly, + OutputOnly, + NoneAndPreserve, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum PinConfiguration { + Default, + PullUp, + PullDown, + NoPull, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct GPIOConnectionDescriptor { + pub usage: ResourceUsage, + pub sharing: ShareType, + pub pin_configuration: PinConfiguration, + pub output_drive_strength: u16, + pub debounce_timeout: u16, + pub pins: Vec, + pub source: crate::aml::String, + pub vendor_data: Vec, + pub connection: GPIOConnection, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum GPIOConnection { + Interrupt { trigger: InterruptTrigger, wake_capability: WakeCapability, polarity: GPIOInterruptPolarity }, + IO { io_restriction: IORestriction }, +} + +fn gpio_connection_descriptor(bytes: &[u8]) -> Result { + // revision + if bytes[3] != 1 { + return Err(AmlError::InvalidResourceDescriptor); + } + let pin_table_offset = LittleEndian::read_u16(&bytes[14..=15]) as usize; + let source_name_offset = LittleEndian::read_u16(&bytes[17..=18]) as usize; + let vendor_data_offset = LittleEndian::read_u16(&bytes[19..=20]) as usize; + let vendor_data_length = LittleEndian::read_u16(&bytes[21..=22]) as usize; + + let pin_count = ((source_name_offset - pin_table_offset) / 2) as usize; + let vendor_data = &bytes[vendor_data_offset..vendor_data_offset + vendor_data_length]; + let source = str::from_utf8(&bytes[source_name_offset..bytes.len() - vendor_data_length - 1]) + .map_err(|_| AmlError::InvalidResourceDescriptor)?; + + Ok(Resource::GPIO(GPIOConnectionDescriptor { + usage: if bytes[5].get_bit(0) { ResourceUsage::ResourceConsumer } else { ResourceUsage::ResourceProducer }, + sharing: if bytes[7].get_bit(3) { ShareType::Shared } else { ShareType::Exclusive }, + pin_configuration: match bytes[9] { + 0x00 => PinConfiguration::Default, + 0x01 => PinConfiguration::PullUp, + 0x02 => PinConfiguration::PullDown, + 0x03 => PinConfiguration::NoPull, + _ => { + return Err(AmlError::InvalidResourceDescriptor); + } + }, + output_drive_strength: LittleEndian::read_u16(&bytes[10..=11]), + debounce_timeout: LittleEndian::read_u16(&bytes[12..=13]), + pins: bytes[pin_table_offset..pin_table_offset + 2 * (pin_count)] + .chunks(2) + .map(|b| LittleEndian::read_u16(&[b[0], b[1]])) + .collect::>(), + source: alloc::string::String::from(source), + vendor_data: Vec::from(vendor_data), + connection: match bytes[4] { + 0 => GPIOConnection::Interrupt { + trigger: match bytes[7].get_bit(0) { + true => InterruptTrigger::Edge, + false => InterruptTrigger::Level, + }, + wake_capability: match bytes[7].get_bit(4) { + true => WakeCapability::WakeCapable, + false => WakeCapability::NotWakeCapable, + }, + polarity: match bytes[7].get_bits(1..=2) { + 0x0 => GPIOInterruptPolarity::ActiveHigh, + 0x1 => GPIOInterruptPolarity::ActiveLow, + 0x2 => GPIOInterruptPolarity::ActiveBoth, + _ => return Err(AmlError::InvalidResourceDescriptor), + }, + }, + 1 => GPIOConnection::IO { + io_restriction: match bytes[7].get_bits(0..=1) { + 0b00 => IORestriction::None, + 0b01 => IORestriction::InputOnly, + 0b10 => IORestriction::OutputOnly, + 0b11 => IORestriction::NoneAndPreserve, + _ => unreachable!(), + }, + }, + _ => { + return Err(AmlError::InvalidResourceDescriptor); + } + }, + })) +} + #[cfg(test)] mod tests { use super::*; From 1b90f9605616c409239d8394827497a2be61b9ee Mon Sep 17 00:00:00 2001 From: Daniel Wyatt Date: Tue, 1 Sep 2026 14:35:10 -0400 Subject: [PATCH 3/7] Fix numerical ordering on large descriptor tags --- src/aml/resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aml/resource.rs b/src/aml/resource.rs index a70e5bbc..14b424bd 100644 --- a/src/aml/resource.rs +++ b/src/aml/resource.rs @@ -118,8 +118,8 @@ fn resource_descriptor(bytes: &[u8]) -> Result<(Option, &[u8]), AmlErr 0x08 => address_space_descriptor::(descriptor_bytes), 0x09 => extended_interrupt_descriptor(descriptor_bytes), 0x0a => address_space_descriptor::(descriptor_bytes), - 0x0e => serial_bus_descriptor(descriptor_bytes), 0x0c => gpio_connection_descriptor(descriptor_bytes), + 0x0e => serial_bus_descriptor(descriptor_bytes), 0x00 | 0x13..=0x7f => Err(AmlError::InvalidResourceDescriptor), 0x80..=0xff => unreachable!(), From 2cc69908937804bf79a912280978a093e229829f Mon Sep 17 00:00:00 2001 From: Daniel Wyatt Date: Tue, 1 Sep 2026 14:35:25 -0400 Subject: [PATCH 4/7] Use alloc::string::String --- src/aml/resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aml/resource.rs b/src/aml/resource.rs index 14b424bd..0b1b16c0 100644 --- a/src/aml/resource.rs +++ b/src/aml/resource.rs @@ -637,7 +637,7 @@ pub struct SerialBusDescriptor { pub sharing: ShareType, pub usage: ResourceUsage, pub slave_mode: SlaveMode, - pub source: crate::aml::String, + pub source: alloc::string::String, pub source_index: u8, pub vendor_data: Vec, pub bus: SerialBus, From 05615e29437fccd64d72155ef125b536574aed6c Mon Sep 17 00:00:00 2001 From: Daniel Wyatt Date: Tue, 1 Sep 2026 15:24:56 -0400 Subject: [PATCH 5/7] Check GPIO descriptor length --- src/aml/resource.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/aml/resource.rs b/src/aml/resource.rs index 0b1b16c0..4ee5629b 100644 --- a/src/aml/resource.rs +++ b/src/aml/resource.rs @@ -752,6 +752,9 @@ pub enum GPIOConnection { } fn gpio_connection_descriptor(bytes: &[u8]) -> Result { + if bytes.len() < 23 { + return Err(AmlError::InvalidResourceDescriptor); + } // revision if bytes[3] != 1 { return Err(AmlError::InvalidResourceDescriptor); From 9a4efd046d68e901fd801d6787d404badb965fc8 Mon Sep 17 00:00:00 2001 From: Daniel Wyatt Date: Tue, 1 Sep 2026 15:26:10 -0400 Subject: [PATCH 6/7] Stricter length check on serial bus descriptor --- src/aml/resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aml/resource.rs b/src/aml/resource.rs index 4ee5629b..9e6211e4 100644 --- a/src/aml/resource.rs +++ b/src/aml/resource.rs @@ -677,7 +677,7 @@ fn i2c_bus_descriptor(bytes: &[u8]) -> Result<(SerialBus, (usize, usize)), AmlEr } fn serial_bus_descriptor(bytes: &[u8]) -> Result { - if bytes.len() < 7 { + if bytes.len() < 12 { return Err(AmlError::InvalidResourceDescriptor); } // revision From 338c6961e6c22472c882cbc354003ec5f18ed3b1 Mon Sep 17 00:00:00 2001 From: Daniel Wyatt Date: Tue, 1 Sep 2026 15:28:49 -0400 Subject: [PATCH 7/7] Fix revision check on I2C resource descriptor --- src/aml/resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aml/resource.rs b/src/aml/resource.rs index 9e6211e4..71c5d93f 100644 --- a/src/aml/resource.rs +++ b/src/aml/resource.rs @@ -653,7 +653,7 @@ fn i2c_bus_descriptor(bytes: &[u8]) -> Result<(SerialBus, (usize, usize)), AmlEr return Err(AmlError::InvalidResourceDescriptor); } // revision - if bytes[9] > 2 { + if bytes[9] > 1 { return Err(AmlError::InvalidResourceDescriptor); } let type_length = LittleEndian::read_u16(&bytes[10..=11]) as usize;