diff --git a/src/aml/resource.rs b/src/aml/resource.rs index e7700125..71c5d93f 100644 --- a/src/aml/resource.rs +++ b/src/aml/resource.rs @@ -13,6 +13,8 @@ pub enum Resource { MemoryRange(MemoryRangeDescriptor), IOPort(IOPortDescriptor), Dma(DMADescriptor), + SerialBus(SerialBusDescriptor), + GPIO(GPIOConnectionDescriptor), } const UNSUPPORTED_LARGE_RESOURCE_DESCRIPTORS: &[(u8, &str)] = &[ @@ -22,9 +24,7 @@ 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"), - (0x0e, "GenericSerialBus Connection Descriptor"), (0x0f, "Pin Configuration Descriptor"), (0x10, "Pin Group Descriptor"), (0x11, "Pin Group Function Descriptor"), @@ -118,6 +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), + 0x0c => gpio_connection_descriptor(descriptor_bytes), + 0x0e => serial_bus_descriptor(descriptor_bytes), 0x00 | 0x13..=0x7f => Err(AmlError::InvalidResourceDescriptor), 0x80..=0xff => unreachable!(), @@ -606,6 +608,220 @@ 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: alloc::string::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] > 1 { + 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() < 12 { + 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, + })) +} + +#[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 { + if bytes.len() < 23 { + return Err(AmlError::InvalidResourceDescriptor); + } + // 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::*; @@ -821,7 +1037,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.