Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 219 additions & 3 deletions src/aml/resource.rs
Comment thread
dewyatt marked this conversation as resolved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@IsaacWoods should we be comparing the length of the descriptor buffer slices against the value of the length field contained within that slice?

I know there's no memory safety risk of skipping this check, it'd mostly be to avoid an out-of-bounds panic.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think the ideal would be for parsing resource descriptors to be entirely panic free (I can't recall if that's the position we're in atm)

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub enum Resource {
MemoryRange(MemoryRangeDescriptor),
IOPort(IOPortDescriptor),
Dma(DMADescriptor),
SerialBus(SerialBusDescriptor),
GPIO(GPIOConnectionDescriptor),
}

const UNSUPPORTED_LARGE_RESOURCE_DESCRIPTORS: &[(u8, &str)] = &[
Expand All @@ -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"),
Expand Down Expand Up @@ -118,6 +118,8 @@ fn resource_descriptor(bytes: &[u8]) -> Result<(Option<Resource>, &[u8]), AmlErr
0x08 => address_space_descriptor::<u16>(descriptor_bytes),
0x09 => extended_interrupt_descriptor(descriptor_bytes),
0x0a => address_space_descriptor::<u64>(descriptor_bytes),
0x0c => gpio_connection_descriptor(descriptor_bytes),
0x0e => serial_bus_descriptor(descriptor_bytes),

0x00 | 0x13..=0x7f => Err(AmlError::InvalidResourceDescriptor),
0x80..=0xff => unreachable!(),
Expand Down Expand Up @@ -606,6 +608,220 @@ fn extended_interrupt_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {
}))
}

#[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 {
Comment thread
dewyatt marked this conversation as resolved.
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<u8>,
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<Resource, AmlError> {
if bytes.len() < 12 {
return Err(AmlError::InvalidResourceDescriptor);
}
// revision
if bytes[3] > 2 {
Comment thread
dewyatt marked this conversation as resolved.
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<u16>,
pub source: crate::aml::String,
pub vendor_data: Vec<u8>,
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<Resource, AmlError> {
Comment thread
dewyatt marked this conversation as resolved.
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::<Vec<u16>>(),
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::*;
Expand Down Expand Up @@ -821,7 +1037,7 @@ mod tests {
#[test]
fn skips_unsupported_large_resource_descriptors() {
let bytes: Vec<u8> = [
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.
Expand Down
Loading