Red Bear OS base baseline from 0.1.0 pre-patched archive
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "acpi-resource"
|
||||
description = "Shared ACPI resource template decoder"
|
||||
version = "0.0.1"
|
||||
authors = ["Red Bear OS"]
|
||||
repository = "https://gitlab.redox-os.org/redox-os/drivers"
|
||||
categories = ["hardware-support"]
|
||||
license = "MIT/Apache-2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serde.workspace = true
|
||||
thiserror.workspace = true
|
||||
@@ -0,0 +1,688 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
const SMALL_IRQ: u8 = 0x20;
|
||||
const SMALL_END_TAG: u8 = 0x78;
|
||||
|
||||
const LARGE_MEMORY32: u8 = 0x85;
|
||||
const LARGE_FIXED_MEMORY32: u8 = 0x86;
|
||||
const LARGE_ADDRESS32: u8 = 0x87;
|
||||
const LARGE_EXTENDED_IRQ: u8 = 0x89;
|
||||
const LARGE_ADDRESS64: u8 = 0x8A;
|
||||
const LARGE_GPIO: u8 = 0x8C;
|
||||
const LARGE_SERIAL_BUS: u8 = 0x8E;
|
||||
|
||||
const SERIAL_BUS_I2C: u8 = 1;
|
||||
const I2C_TYPE_DATA_LEN: usize = 6;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum InterruptTrigger {
|
||||
Edge,
|
||||
Level,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum InterruptPolarity {
|
||||
ActiveHigh,
|
||||
ActiveLow,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum AddressResourceType {
|
||||
MemoryRange,
|
||||
IoRange,
|
||||
BusNumberRange,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ResourceSource {
|
||||
pub index: u8,
|
||||
pub source: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct IrqDescriptor {
|
||||
pub interrupts: Vec<u8>,
|
||||
pub triggering: InterruptTrigger,
|
||||
pub polarity: InterruptPolarity,
|
||||
pub shareable: bool,
|
||||
pub wake_capable: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ExtendedIrqDescriptor {
|
||||
pub producer_consumer: bool,
|
||||
pub interrupts: Vec<u32>,
|
||||
pub triggering: InterruptTrigger,
|
||||
pub polarity: InterruptPolarity,
|
||||
pub shareable: bool,
|
||||
pub wake_capable: bool,
|
||||
pub resource_source: Option<ResourceSource>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct GpioDescriptor {
|
||||
pub revision_id: u8,
|
||||
pub producer_consumer: bool,
|
||||
pub pin_config: u8,
|
||||
pub shareable: bool,
|
||||
pub wake_capable: bool,
|
||||
pub io_restriction: u8,
|
||||
pub triggering: InterruptTrigger,
|
||||
pub polarity: InterruptPolarity,
|
||||
pub drive_strength: u16,
|
||||
pub debounce_timeout: u16,
|
||||
pub pins: Vec<u16>,
|
||||
pub resource_source: Option<ResourceSource>,
|
||||
pub vendor_data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct I2cSerialBusDescriptor {
|
||||
pub revision_id: u8,
|
||||
pub producer_consumer: bool,
|
||||
pub slave_mode: bool,
|
||||
pub connection_sharing: bool,
|
||||
pub type_revision_id: u8,
|
||||
pub access_mode_10bit: bool,
|
||||
pub slave_address: u16,
|
||||
pub connection_speed: u32,
|
||||
pub resource_source: Option<ResourceSource>,
|
||||
pub vendor_data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Memory32RangeDescriptor {
|
||||
pub write_protect: bool,
|
||||
pub minimum: u32,
|
||||
pub maximum: u32,
|
||||
pub alignment: u32,
|
||||
pub address_length: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct FixedMemory32Descriptor {
|
||||
pub write_protect: bool,
|
||||
pub address: u32,
|
||||
pub address_length: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Address32Descriptor {
|
||||
pub resource_type: AddressResourceType,
|
||||
pub producer_consumer: bool,
|
||||
pub decode: bool,
|
||||
pub min_address_fixed: bool,
|
||||
pub max_address_fixed: bool,
|
||||
pub specific_flags: u8,
|
||||
pub granularity: u32,
|
||||
pub minimum: u32,
|
||||
pub maximum: u32,
|
||||
pub translation_offset: u32,
|
||||
pub address_length: u32,
|
||||
pub resource_source: Option<ResourceSource>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Address64Descriptor {
|
||||
pub resource_type: AddressResourceType,
|
||||
pub producer_consumer: bool,
|
||||
pub decode: bool,
|
||||
pub min_address_fixed: bool,
|
||||
pub max_address_fixed: bool,
|
||||
pub specific_flags: u8,
|
||||
pub granularity: u64,
|
||||
pub minimum: u64,
|
||||
pub maximum: u64,
|
||||
pub translation_offset: u64,
|
||||
pub address_length: u64,
|
||||
pub resource_source: Option<ResourceSource>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ResourceDescriptor {
|
||||
Irq(IrqDescriptor),
|
||||
ExtendedIrq(ExtendedIrqDescriptor),
|
||||
GpioInt(GpioDescriptor),
|
||||
GpioIo(GpioDescriptor),
|
||||
I2cSerialBus(I2cSerialBusDescriptor),
|
||||
Memory32Range(Memory32RangeDescriptor),
|
||||
FixedMemory32(FixedMemory32Descriptor),
|
||||
Address32(Address32Descriptor),
|
||||
Address64(Address64Descriptor),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
pub enum ResourceDecodeError {
|
||||
#[error("descriptor at offset {offset} overruns the resource template")]
|
||||
TruncatedDescriptor { offset: usize },
|
||||
|
||||
#[error("unsupported small descriptor length {length} for tag {tag:#04x} at offset {offset}")]
|
||||
InvalidSmallLength {
|
||||
offset: usize,
|
||||
tag: u8,
|
||||
length: usize,
|
||||
},
|
||||
|
||||
#[error("descriptor {descriptor} at offset {offset} is shorter than {minimum} bytes")]
|
||||
InvalidLargeLength {
|
||||
offset: usize,
|
||||
descriptor: &'static str,
|
||||
minimum: usize,
|
||||
},
|
||||
|
||||
#[error("descriptor {descriptor} at offset {offset} has an invalid internal offset")]
|
||||
InvalidInternalOffset {
|
||||
offset: usize,
|
||||
descriptor: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
pub fn decode_resource_template(
|
||||
bytes: &[u8],
|
||||
) -> Result<Vec<ResourceDescriptor>, ResourceDecodeError> {
|
||||
let mut resources = Vec::new();
|
||||
let mut offset = 0usize;
|
||||
|
||||
while offset < bytes.len() {
|
||||
let descriptor = *bytes
|
||||
.get(offset)
|
||||
.ok_or(ResourceDecodeError::TruncatedDescriptor { offset })?;
|
||||
|
||||
if descriptor & 0x80 == 0 {
|
||||
let length = usize::from(descriptor & 0x07);
|
||||
let end = offset + 1 + length;
|
||||
let desc = bytes
|
||||
.get(offset..end)
|
||||
.ok_or(ResourceDecodeError::TruncatedDescriptor { offset })?;
|
||||
let body = &desc[1..];
|
||||
|
||||
match descriptor & 0x78 {
|
||||
SMALL_IRQ => resources.push(ResourceDescriptor::Irq(parse_irq(body, offset)?)),
|
||||
SMALL_END_TAG => break,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
offset = end;
|
||||
continue;
|
||||
}
|
||||
|
||||
let length = usize::from(read_u16(bytes, offset + 1)?);
|
||||
let end = offset + 3 + length;
|
||||
let desc = bytes
|
||||
.get(offset..end)
|
||||
.ok_or(ResourceDecodeError::TruncatedDescriptor { offset })?;
|
||||
let body = &desc[3..];
|
||||
|
||||
match descriptor {
|
||||
LARGE_MEMORY32 => resources.push(ResourceDescriptor::Memory32Range(parse_memory32(
|
||||
body, offset,
|
||||
)?)),
|
||||
LARGE_FIXED_MEMORY32 => resources.push(ResourceDescriptor::FixedMemory32(
|
||||
parse_fixed_memory32(body, offset)?,
|
||||
)),
|
||||
LARGE_ADDRESS32 => {
|
||||
resources.push(ResourceDescriptor::Address32(parse_address32(
|
||||
desc, body, offset,
|
||||
)?));
|
||||
}
|
||||
LARGE_ADDRESS64 => {
|
||||
resources.push(ResourceDescriptor::Address64(parse_address64(
|
||||
desc, body, offset,
|
||||
)?));
|
||||
}
|
||||
LARGE_EXTENDED_IRQ => resources.push(ResourceDescriptor::ExtendedIrq(
|
||||
parse_extended_irq(desc, body, offset)?,
|
||||
)),
|
||||
LARGE_GPIO => {
|
||||
let (is_interrupt, descriptor) = parse_gpio(desc, body, offset)?;
|
||||
resources.push(if is_interrupt {
|
||||
ResourceDescriptor::GpioInt(descriptor)
|
||||
} else {
|
||||
ResourceDescriptor::GpioIo(descriptor)
|
||||
});
|
||||
}
|
||||
LARGE_SERIAL_BUS => {
|
||||
if let Some(descriptor) = parse_i2c_serial_bus(desc, body, offset)? {
|
||||
resources.push(ResourceDescriptor::I2cSerialBus(descriptor));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
offset = end;
|
||||
}
|
||||
|
||||
Ok(resources)
|
||||
}
|
||||
|
||||
fn parse_irq(body: &[u8], offset: usize) -> Result<IrqDescriptor, ResourceDecodeError> {
|
||||
if body.len() != 2 && body.len() != 3 {
|
||||
return Err(ResourceDecodeError::InvalidSmallLength {
|
||||
offset,
|
||||
tag: SMALL_IRQ,
|
||||
length: body.len(),
|
||||
});
|
||||
}
|
||||
|
||||
let mask = u16::from_le_bytes([body[0], body[1]]);
|
||||
let flags = body.get(2).copied().unwrap_or(0);
|
||||
let interrupts = (0..16)
|
||||
.filter(|irq| mask & (1 << irq) != 0)
|
||||
.map(|irq| irq as u8)
|
||||
.collect();
|
||||
|
||||
Ok(IrqDescriptor {
|
||||
interrupts,
|
||||
triggering: if flags & 0x01 != 0 {
|
||||
InterruptTrigger::Level
|
||||
} else {
|
||||
InterruptTrigger::Edge
|
||||
},
|
||||
polarity: if flags & 0x08 != 0 {
|
||||
InterruptPolarity::ActiveLow
|
||||
} else {
|
||||
InterruptPolarity::ActiveHigh
|
||||
},
|
||||
shareable: flags & 0x10 != 0,
|
||||
wake_capable: flags & 0x20 != 0,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_extended_irq(
|
||||
desc: &[u8],
|
||||
body: &[u8],
|
||||
offset: usize,
|
||||
) -> Result<ExtendedIrqDescriptor, ResourceDecodeError> {
|
||||
ensure_length(body, 2, offset, "ExtendedIrq")?;
|
||||
|
||||
let flags = body[0];
|
||||
let count = usize::from(body[1]);
|
||||
let ints_len = count * 4;
|
||||
ensure_length(body, 2 + ints_len, offset, "ExtendedIrq")?;
|
||||
|
||||
let interrupts = (0..count)
|
||||
.map(|index| read_u32(body, 2 + index * 4))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let resource_source = if body.len() > 2 + ints_len {
|
||||
Some(parse_source_inline(&body[2 + ints_len..]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let _ = desc;
|
||||
|
||||
Ok(ExtendedIrqDescriptor {
|
||||
producer_consumer: flags & 0x01 != 0,
|
||||
triggering: if flags & 0x02 != 0 {
|
||||
InterruptTrigger::Level
|
||||
} else {
|
||||
InterruptTrigger::Edge
|
||||
},
|
||||
polarity: if flags & 0x04 != 0 {
|
||||
InterruptPolarity::ActiveLow
|
||||
} else {
|
||||
InterruptPolarity::ActiveHigh
|
||||
},
|
||||
shareable: flags & 0x08 != 0,
|
||||
wake_capable: flags & 0x10 != 0,
|
||||
interrupts,
|
||||
resource_source,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_gpio(
|
||||
desc: &[u8],
|
||||
body: &[u8],
|
||||
offset: usize,
|
||||
) -> Result<(bool, GpioDescriptor), ResourceDecodeError> {
|
||||
ensure_length(body, 20, offset, "Gpio")?;
|
||||
|
||||
let connection_type = body[1];
|
||||
let flags = read_u16(body, 2)?;
|
||||
let int_flags = read_u16(body, 4)?;
|
||||
let pin_table_offset = usize::from(read_u16(body, 11)?);
|
||||
let resource_source_index = body[13];
|
||||
let resource_source_offset = usize::from(read_u16(body, 14)?);
|
||||
let vendor_offset = usize::from(read_u16(body, 16)?);
|
||||
let vendor_length = usize::from(read_u16(body, 18)?);
|
||||
|
||||
let pins_end = min_nonzero([resource_source_offset, vendor_offset, desc.len()]);
|
||||
let pins = parse_u16_list(desc, pin_table_offset, pins_end, offset, "Gpio")?;
|
||||
let resource_source = parse_source_absolute(
|
||||
desc,
|
||||
resource_source_offset,
|
||||
min_nonzero([vendor_offset, desc.len()]),
|
||||
resource_source_index,
|
||||
offset,
|
||||
"Gpio",
|
||||
)?;
|
||||
let vendor_data = parse_blob_absolute(desc, vendor_offset, vendor_length, offset, "Gpio")?;
|
||||
|
||||
Ok((
|
||||
connection_type == 0,
|
||||
GpioDescriptor {
|
||||
revision_id: body[0],
|
||||
producer_consumer: flags & 0x0001 != 0,
|
||||
pin_config: body[6],
|
||||
shareable: int_flags & 0x0008 != 0,
|
||||
wake_capable: int_flags & 0x0010 != 0,
|
||||
io_restriction: (int_flags & 0x0003) as u8,
|
||||
triggering: if int_flags & 0x0001 != 0 {
|
||||
InterruptTrigger::Level
|
||||
} else {
|
||||
InterruptTrigger::Edge
|
||||
},
|
||||
polarity: if int_flags & 0x0002 != 0 {
|
||||
InterruptPolarity::ActiveLow
|
||||
} else {
|
||||
InterruptPolarity::ActiveHigh
|
||||
},
|
||||
drive_strength: read_u16(body, 7)?,
|
||||
debounce_timeout: read_u16(body, 9)?,
|
||||
pins,
|
||||
resource_source,
|
||||
vendor_data,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_i2c_serial_bus(
|
||||
desc: &[u8],
|
||||
body: &[u8],
|
||||
offset: usize,
|
||||
) -> Result<Option<I2cSerialBusDescriptor>, ResourceDecodeError> {
|
||||
ensure_length(body, 15, offset, "SerialBus")?;
|
||||
if body[2] != SERIAL_BUS_I2C {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let type_data_length = usize::from(read_u16(body, 7)?);
|
||||
if type_data_length < I2C_TYPE_DATA_LEN {
|
||||
return Err(ResourceDecodeError::InvalidLargeLength {
|
||||
offset,
|
||||
descriptor: "I2cSerialBus",
|
||||
minimum: 15,
|
||||
});
|
||||
}
|
||||
|
||||
let vendor_length = type_data_length - I2C_TYPE_DATA_LEN;
|
||||
let vendor_data = parse_blob_absolute(desc, 18, vendor_length, offset, "I2cSerialBus")?;
|
||||
let resource_source = parse_source_absolute(
|
||||
desc,
|
||||
12 + type_data_length,
|
||||
desc.len(),
|
||||
body[1],
|
||||
offset,
|
||||
"I2cSerialBus",
|
||||
)?;
|
||||
|
||||
Ok(Some(I2cSerialBusDescriptor {
|
||||
revision_id: body[0],
|
||||
producer_consumer: body[3] & 0x02 != 0,
|
||||
slave_mode: body[3] & 0x01 != 0,
|
||||
connection_sharing: body[3] & 0x04 != 0,
|
||||
type_revision_id: body[6],
|
||||
access_mode_10bit: read_u16(body, 4)? & 0x0001 != 0,
|
||||
connection_speed: read_u32(body, 9)?,
|
||||
slave_address: read_u16(body, 13)?,
|
||||
resource_source,
|
||||
vendor_data,
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_memory32(
|
||||
body: &[u8],
|
||||
offset: usize,
|
||||
) -> Result<Memory32RangeDescriptor, ResourceDecodeError> {
|
||||
ensure_length(body, 17, offset, "Memory32Range")?;
|
||||
Ok(Memory32RangeDescriptor {
|
||||
write_protect: body[0] & 0x01 != 0,
|
||||
minimum: read_u32(body, 1)?,
|
||||
maximum: read_u32(body, 5)?,
|
||||
alignment: read_u32(body, 9)?,
|
||||
address_length: read_u32(body, 13)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_fixed_memory32(
|
||||
body: &[u8],
|
||||
offset: usize,
|
||||
) -> Result<FixedMemory32Descriptor, ResourceDecodeError> {
|
||||
ensure_length(body, 9, offset, "FixedMemory32")?;
|
||||
Ok(FixedMemory32Descriptor {
|
||||
write_protect: body[0] & 0x01 != 0,
|
||||
address: read_u32(body, 1)?,
|
||||
address_length: read_u32(body, 5)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_address32(
|
||||
desc: &[u8],
|
||||
body: &[u8],
|
||||
offset: usize,
|
||||
) -> Result<Address32Descriptor, ResourceDecodeError> {
|
||||
ensure_length(body, 23, offset, "Address32")?;
|
||||
Ok(Address32Descriptor {
|
||||
resource_type: parse_address_type(body[0]),
|
||||
producer_consumer: body[1] & 0x01 != 0,
|
||||
decode: body[1] & 0x02 != 0,
|
||||
min_address_fixed: body[1] & 0x04 != 0,
|
||||
max_address_fixed: body[1] & 0x08 != 0,
|
||||
specific_flags: body[2],
|
||||
granularity: read_u32(body, 3)?,
|
||||
minimum: read_u32(body, 7)?,
|
||||
maximum: read_u32(body, 11)?,
|
||||
translation_offset: read_u32(body, 15)?,
|
||||
address_length: read_u32(body, 19)?,
|
||||
resource_source: if desc.len() > 26 {
|
||||
parse_source_absolute(desc, 26, desc.len(), desc[26], offset, "Address32")?
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_address64(
|
||||
desc: &[u8],
|
||||
body: &[u8],
|
||||
offset: usize,
|
||||
) -> Result<Address64Descriptor, ResourceDecodeError> {
|
||||
ensure_length(body, 43, offset, "Address64")?;
|
||||
Ok(Address64Descriptor {
|
||||
resource_type: parse_address_type(body[0]),
|
||||
producer_consumer: body[1] & 0x01 != 0,
|
||||
decode: body[1] & 0x02 != 0,
|
||||
min_address_fixed: body[1] & 0x04 != 0,
|
||||
max_address_fixed: body[1] & 0x08 != 0,
|
||||
specific_flags: body[2],
|
||||
granularity: read_u64(body, 3)?,
|
||||
minimum: read_u64(body, 11)?,
|
||||
maximum: read_u64(body, 19)?,
|
||||
translation_offset: read_u64(body, 27)?,
|
||||
address_length: read_u64(body, 35)?,
|
||||
resource_source: if desc.len() > 46 {
|
||||
parse_source_absolute(desc, 46, desc.len(), desc[46], offset, "Address64")?
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn ensure_length(
|
||||
body: &[u8],
|
||||
minimum: usize,
|
||||
offset: usize,
|
||||
descriptor: &'static str,
|
||||
) -> Result<(), ResourceDecodeError> {
|
||||
if body.len() < minimum {
|
||||
return Err(ResourceDecodeError::InvalidLargeLength {
|
||||
offset,
|
||||
descriptor,
|
||||
minimum,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_source_inline(bytes: &[u8]) -> ResourceSource {
|
||||
let index = bytes.first().copied().unwrap_or(0);
|
||||
let source = bytes.get(1..).map(parse_nul_string).unwrap_or_default();
|
||||
ResourceSource { index, source }
|
||||
}
|
||||
|
||||
fn parse_source_absolute(
|
||||
desc: &[u8],
|
||||
start: usize,
|
||||
end: usize,
|
||||
index: u8,
|
||||
offset: usize,
|
||||
descriptor: &'static str,
|
||||
) -> Result<Option<ResourceSource>, ResourceDecodeError> {
|
||||
if start == 0 || start >= end || start > desc.len() {
|
||||
return Ok(None);
|
||||
}
|
||||
let slice = desc
|
||||
.get(start..end)
|
||||
.ok_or(ResourceDecodeError::InvalidInternalOffset { offset, descriptor })?;
|
||||
Ok(Some(ResourceSource {
|
||||
index,
|
||||
source: parse_nul_string(slice),
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_blob_absolute(
|
||||
desc: &[u8],
|
||||
start: usize,
|
||||
length: usize,
|
||||
offset: usize,
|
||||
descriptor: &'static str,
|
||||
) -> Result<Vec<u8>, ResourceDecodeError> {
|
||||
if start == 0 || length == 0 {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let end = start + length;
|
||||
Ok(desc
|
||||
.get(start..end)
|
||||
.ok_or(ResourceDecodeError::InvalidInternalOffset { offset, descriptor })?
|
||||
.to_vec())
|
||||
}
|
||||
|
||||
fn parse_u16_list(
|
||||
desc: &[u8],
|
||||
start: usize,
|
||||
end: usize,
|
||||
offset: usize,
|
||||
descriptor: &'static str,
|
||||
) -> Result<Vec<u16>, ResourceDecodeError> {
|
||||
if start == 0 || start >= end || start > desc.len() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let slice = desc
|
||||
.get(start..end)
|
||||
.ok_or(ResourceDecodeError::InvalidInternalOffset { offset, descriptor })?;
|
||||
if slice.len() % 2 != 0 {
|
||||
return Err(ResourceDecodeError::InvalidInternalOffset { offset, descriptor });
|
||||
}
|
||||
slice
|
||||
.chunks_exact(2)
|
||||
.map(|chunk| Ok(u16::from_le_bytes([chunk[0], chunk[1]])))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn parse_nul_string(bytes: &[u8]) -> String {
|
||||
let end = bytes
|
||||
.iter()
|
||||
.position(|byte| *byte == 0)
|
||||
.unwrap_or(bytes.len());
|
||||
String::from_utf8_lossy(&bytes[..end]).to_string()
|
||||
}
|
||||
|
||||
fn parse_address_type(value: u8) -> AddressResourceType {
|
||||
match value {
|
||||
0 => AddressResourceType::MemoryRange,
|
||||
1 => AddressResourceType::IoRange,
|
||||
2 => AddressResourceType::BusNumberRange,
|
||||
other => AddressResourceType::Unknown(other),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_u16(bytes: &[u8], offset: usize) -> Result<u16, ResourceDecodeError> {
|
||||
let slice = bytes
|
||||
.get(offset..offset + 2)
|
||||
.ok_or(ResourceDecodeError::TruncatedDescriptor { offset })?;
|
||||
Ok(u16::from_le_bytes([slice[0], slice[1]]))
|
||||
}
|
||||
|
||||
fn read_u32(bytes: &[u8], offset: usize) -> Result<u32, ResourceDecodeError> {
|
||||
let slice = bytes
|
||||
.get(offset..offset + 4)
|
||||
.ok_or(ResourceDecodeError::TruncatedDescriptor { offset })?;
|
||||
Ok(u32::from_le_bytes([slice[0], slice[1], slice[2], slice[3]]))
|
||||
}
|
||||
|
||||
fn read_u64(bytes: &[u8], offset: usize) -> Result<u64, ResourceDecodeError> {
|
||||
let slice = bytes
|
||||
.get(offset..offset + 8)
|
||||
.ok_or(ResourceDecodeError::TruncatedDescriptor { offset })?;
|
||||
Ok(u64::from_le_bytes([
|
||||
slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7],
|
||||
]))
|
||||
}
|
||||
|
||||
fn min_nonzero<const N: usize>(values: [usize; N]) -> usize {
|
||||
values
|
||||
.into_iter()
|
||||
.filter(|value| *value != 0)
|
||||
.min()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{decode_resource_template, ResourceDescriptor};
|
||||
|
||||
#[test]
|
||||
fn decodes_small_irq_descriptor() {
|
||||
let resources = decode_resource_template(&[0x23, 0x0A, 0x00, 0x19, 0x79, 0x00]).unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
&resources[0],
|
||||
ResourceDescriptor::Irq(descriptor)
|
||||
if descriptor.interrupts == vec![1, 3]
|
||||
&& descriptor.shareable
|
||||
&& descriptor.wake_capable == false
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_i2c_serial_bus_descriptor() {
|
||||
let template = [
|
||||
0x8E, 0x14, 0x00, 0x01, 0x02, 0x01, 0x02, 0x00, 0x00, 0x01, 0x06, 0x00, 0x80, 0x1A,
|
||||
0x06, 0x00, 0x15, 0x00, b'I', b'2', b'C', b'0', 0x00, 0x79, 0x00,
|
||||
];
|
||||
let resources = decode_resource_template(&template).unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
&resources[0],
|
||||
ResourceDescriptor::I2cSerialBus(descriptor)
|
||||
if descriptor.connection_speed == 400_000
|
||||
&& descriptor.slave_address == 0x15
|
||||
&& descriptor.resource_source.as_ref().map(|source| source.source.as_str())
|
||||
== Some("I2C0")
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_gpio_interrupt_descriptor() {
|
||||
let template = [
|
||||
0x8C, 0x1B, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17,
|
||||
0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, b'\\', b'_', b'S', b'B',
|
||||
0x00, 0x79, 0x00,
|
||||
];
|
||||
let resources = decode_resource_template(&template).unwrap();
|
||||
|
||||
assert!(matches!(&resources[0], ResourceDescriptor::GpioInt(_)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user