#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ResourceRangeType {
Memory = 0,
Io = 1,
Bus = 2,
Unknown(u8),
}
impl From<u8> for ResourceRangeType {
fn from(value: u8) -> Self {
match value {
0 => Self::Memory,
1 => Self::Io,
2 => Self::Bus,
other => Self::Unknown(other),
}
}
}
#[derive(Clone, Debug)]
pub struct QwordAddressSpaceDescriptor {
pub resource_range_type: ResourceRangeType,
pub general_flags: u8,
pub type_specific_flags: u8,
pub granularity: u64,
pub address_min: u64,
pub address_max: u64,
pub translation_offset: u64,
pub address_length: u64,
}
#[cfg(feature = "alloc")]
pub(crate) fn parse(
base: *const core::ffi::c_void,
) -> alloc::vec::Vec<QwordAddressSpaceDescriptor> {
use alloc::slice;
use alloc::vec::Vec;
const PCI_RESTBL_QWORDADDRSPEC_TAG: u8 = 0x8a;
const PCI_RESTBL_END_TAG: u8 = 0x79;
let base: *const u8 = base.cast();
let mut offset = 0;
loop {
let tag = unsafe { core::ptr::read(base.add(offset)) };
offset += match tag {
PCI_RESTBL_QWORDADDRSPEC_TAG => 3 + 0x2B,
PCI_RESTBL_END_TAG => break,
_ => panic!("{tag}"), };
}
let mut bfr: &[u8] = unsafe { slice::from_raw_parts(base, offset) };
let mut descriptors = Vec::new();
while !bfr.is_empty() {
match bfr[0] {
PCI_RESTBL_QWORDADDRSPEC_TAG => {
let descriptor = QwordAddressSpaceDescriptor {
resource_range_type: ResourceRangeType::from(bfr[0x03]),
general_flags: bfr[0x04],
type_specific_flags: bfr[0x05],
granularity: u64::from_le_bytes(bfr[0x06..0x06 + 8].try_into().unwrap()),
address_min: u64::from_le_bytes(bfr[0x0E..0x0E + 8].try_into().unwrap()),
address_max: u64::from_le_bytes(bfr[0x16..0x16 + 8].try_into().unwrap()),
translation_offset: u64::from_le_bytes(bfr[0x1E..0x1E + 8].try_into().unwrap()),
address_length: u64::from_le_bytes(bfr[0x26..0x26 + 8].try_into().unwrap()),
};
descriptors.push(descriptor);
bfr = &bfr[3 + 0x2B..];
}
_ => break,
}
}
descriptors
}
#[cfg(test)]
mod tests {
use crate::proto::pci::configuration::ResourceRangeType;
#[test]
fn parse() {
const BFR: &[u8] = &[
138, 43, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 255, 111, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 138, 43, 0, 0, 0, 0, 32,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 255, 255, 15, 129, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 16, 1, 0, 0, 0, 0, 138, 43, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 192, 0, 0, 0, 255, 255, 15, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16, 0, 0, 0, 0, 0, 138, 43, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 121, 0,
];
let configuration = super::parse(BFR.as_ptr().cast());
assert_eq!(configuration.len(), 4);
let (mut cnt_mem, mut cnt_io, mut cnt_bus) = (0, 0, 0);
for entry in &configuration {
match entry.resource_range_type {
ResourceRangeType::Memory => cnt_mem += 1,
ResourceRangeType::Io => cnt_io += 1,
ResourceRangeType::Bus => cnt_bus += 1,
_ => unreachable!(),
}
}
assert_eq!(cnt_mem, 2);
assert_eq!(cnt_io, 1);
assert_eq!(cnt_bus, 1);
}
}