use heterob::{
bit_numbering::Lsb,
endianness::{Le, LeBytesTryInto},
Seq, P3, P8,
};
use crate::{header::HeaderType, Header};
use snafu::Snafu;
#[derive(Debug, Clone, PartialEq, Eq, Snafu)]
pub enum EnhancedAllocationError {
#[snafu(display("number of entries is unreadable"))]
NumEntries,
#[snafu(display("second DW for Type 1 is unreadable"))]
Type1SecondDw,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnhancedAllocation<'a> {
pub num_entries: u8,
pub type_1_second_dw: Option<Type1SecondDw>,
pub entries: EnhancedAllocationEntries<'a>,
}
impl<'a> EnhancedAllocation<'a> {
pub fn try_new(slice: &'a [u8], header: &'a Header) -> Result<Self, EnhancedAllocationError> {
if let [num_entries, _, slice @ ..] = slice {
let num_entries = *num_entries & 0x3f;
if matches!(header.header_type, HeaderType::Bridge(_)) {
if let [sec, sub, _, _, slice @ ..] = slice {
Ok(Self {
num_entries,
type_1_second_dw: Some(Type1SecondDw {
fixed_secondary_bus_number: *sec,
fixed_subordinate_bus_number: *sub,
}),
entries: EnhancedAllocationEntries::new(slice, num_entries),
})
} else {
Err(EnhancedAllocationError::NumEntries)
}
} else {
Ok(Self {
num_entries,
type_1_second_dw: None,
entries: EnhancedAllocationEntries::new(slice, num_entries),
})
}
} else {
Err(EnhancedAllocationError::NumEntries)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnhancedAllocationEntries<'a> {
data: &'a [u8],
count: u8,
}
impl<'a> EnhancedAllocationEntries<'a> {
pub fn new(data: &'a [u8], count: u8) -> Self {
Self { data, count }
}
}
impl<'a> Iterator for EnhancedAllocationEntries<'a> {
type Item = EnhancedAllocationEntry;
fn next(&mut self) -> Option<Self::Item> {
if self.count == 0 {
return None;
}
let Seq {
head: Le((first_dw, base_lo, max_offset_lo)),
tail: mut _slice,
} = P3(self.data).try_into().ok()?;
let _: (u32, u32) = (base_lo, max_offset_lo);
self.count -= 1;
let Lsb((
entry_size,
(),
bar_equivalent_indicator,
primary_properties,
secondary_properties,
(),
writable,
enable,
)) = P8::<u32, 3, 1, 4, 8, 8, 6, 1, 1>(first_dw).into();
let _: u8 = entry_size;
let is_base_64 = base_lo & 0b10 != 0;
let base_lo = base_lo & !0b11;
let base = if is_base_64 {
let Seq { head, tail } = _slice.le_bytes_try_into().ok()?;
let _: u32 = head;
_slice = tail;
let base_hi = (head as u64) << 32;
ResourceRangeAddress::U64(base_hi | base_lo as u64)
} else {
ResourceRangeAddress::U32(base_lo)
};
let is_max_offset_64 = max_offset_lo & 0b10 != 0;
let max_offset_lo = max_offset_lo | 0b11;
let max_offset = if is_max_offset_64 {
let Seq { head, tail } = _slice.le_bytes_try_into().ok()?;
let _: u32 = head;
_slice = tail;
let max_offset_hi = (head as u64) << 32;
ResourceRangeAddress::U64(max_offset_hi | max_offset_lo as u64)
} else {
ResourceRangeAddress::U32(max_offset_lo)
};
if cfg!(feature = "caps_ea_real_entry_size") {
self.data = _slice;
} else {
let next_entry_start = (entry_size as usize + 1) * 4;
self.data = self.data.get(next_entry_start..)?;
}
Some(EnhancedAllocationEntry {
entry_size,
bar_equivalent_indicator: From::<u8>::from(bar_equivalent_indicator),
primary_properties: From::<u8>::from(primary_properties),
secondary_properties: From::<u8>::from(secondary_properties),
writable,
enable,
base,
max_offset,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Type1SecondDw {
pub fixed_secondary_bus_number: u8,
pub fixed_subordinate_bus_number: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnhancedAllocationEntry {
pub entry_size: u8,
pub bar_equivalent_indicator: BarEquivalentIndicator,
pub primary_properties: ResourceDefinition,
pub secondary_properties: ResourceDefinition,
pub writable: bool,
pub enable: bool,
pub base: ResourceRangeAddress,
pub max_offset: ResourceRangeAddress,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BarEquivalentIndicator {
Location10h,
Location14h,
Location18h,
Location1Ch,
Location20h,
Location24h,
BehindType1Function,
EquivalentNotIndicated,
ExpansionRomBaseAddress,
VfBar0,
VfBar1,
VfBar2,
VfBar3,
VfBar4,
VfBar5,
Reserved,
}
impl From<u8> for BarEquivalentIndicator {
fn from(byte: u8) -> Self {
match byte {
0x0 => Self::Location10h,
0x1 => Self::Location14h,
0x2 => Self::Location18h,
0x3 => Self::Location1Ch,
0x4 => Self::Location20h,
0x5 => Self::Location24h,
0x6 => Self::BehindType1Function,
0x7 => Self::EquivalentNotIndicated,
0x8 => Self::ExpansionRomBaseAddress,
0x9 => Self::VfBar0,
0xA => Self::VfBar1,
0xB => Self::VfBar2,
0xC => Self::VfBar3,
0xD => Self::VfBar4,
0xE => Self::VfBar5,
0xF => Self::Reserved,
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResourceDefinition {
MemorySpaceNonPrefetchable,
MemorySpacePrefetchable,
IoSpace,
PfForVfMemorySpacePrefetchable,
PfForVfMemorySpaceNonPrefetchable,
Type1ForAbbMemoryNonPrefetchable,
Type1ForAbbMemoryPrefetchable,
Type1ForAbbIoSpace,
Reserved(u8),
UnavailableMemorySpace,
UnavailableIoSpace,
Unavailable,
}
impl From<u8> for ResourceDefinition {
fn from(byte: u8) -> Self {
match byte {
0x00 => ResourceDefinition::MemorySpaceNonPrefetchable,
0x01 => ResourceDefinition::MemorySpacePrefetchable,
0x02 => ResourceDefinition::IoSpace,
0x03 => ResourceDefinition::PfForVfMemorySpacePrefetchable,
0x04 => ResourceDefinition::PfForVfMemorySpaceNonPrefetchable,
0x05 => ResourceDefinition::Type1ForAbbMemoryNonPrefetchable,
0x06 => ResourceDefinition::Type1ForAbbMemoryPrefetchable,
0x07 => ResourceDefinition::Type1ForAbbIoSpace,
v @ 0x08..=0xFC => ResourceDefinition::Reserved(v),
0xFD => ResourceDefinition::UnavailableMemorySpace,
0xFE => ResourceDefinition::UnavailableIoSpace,
0xFF => ResourceDefinition::Unavailable,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResourceRangeAddress {
U32(u32),
U64(u64),
}