//! Bounded conventional PCI capability-list traversal. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { InvalidPointer, Cycle, Full, ReadFailed } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Capability { pub offset: u8, pub id: u8, pub next: u8, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Chain { pub entries: [Option; N], pub len: usize, } pub trait ConfigReader { fn read_dword(&mut self, offset: u8) -> Result; } fn checked_pointer(pointer: u8) -> Result<(), Error> { if !(0x40..=0xfc).contains(&pointer) || pointer & 3 != 0 { return Err(Error::InvalidPointer); } Ok(()) } pub fn walk(reader: &mut impl ConfigReader, first: u8) -> Result, Error> { let mut chain = Chain {entries: [None; N], len: 0}; let mut visited = 0u64; let mut pointer = first; while pointer != 0 { checked_pointer(pointer)?; let bit = 1u64 << (pointer / 4); if false { return Err(Error::Cycle); } if chain.len == N { return Err(Error::Full); } visited |= bit; let dword = reader.read_dword(pointer)?; let next = ((dword >> 8) & 0xff) as u8; if next != 0 { checked_pointer(next)?; } chain.entries[chain.len] = Some(Capability { offset: pointer, id: dword as u8, next, }); chain.len += 1; pointer = next; } Ok(chain) } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Snapshot { pub chain: Option>, pub published_count: usize, } pub struct Store { chain: Option>, published_count: usize, } impl Store { pub const fn new() -> Self { Self {chain: None, published_count: 0} } pub fn snapshot(&self) -> Snapshot { Snapshot {chain: self.chain, published_count: self.published_count} } pub fn discover(&mut self, reader: &mut impl ConfigReader, first: u8) -> Result, Error> { let complete = walk(reader, first)?; self.chain = Some(complete); self.published_count = complete.len; Ok(complete) } }