use snafu::prelude::*;
use super::DDR_OFFSET;
use crate::header::{Header, HeaderType};
pub mod power_management_interface;
pub use power_management_interface::PowerManagementInterface;
pub mod accelerated_graphics_port;
pub use accelerated_graphics_port::AcceleratedGraphicsPort;
pub mod vital_product_data;
pub use vital_product_data::VitalProductData;
pub mod slot_identification;
pub use slot_identification::SlotIdentification;
pub mod message_signaled_interrups;
pub use message_signaled_interrups::MessageSignaledInterrups;
pub mod compact_pci_hot_swap {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompactPciHotSwap;
}
pub use compact_pci_hot_swap::CompactPciHotSwap;
pub mod pci_x;
pub use pci_x::{PciX, PciXBridge};
pub mod hypertransport;
pub use hypertransport::Hypertransport;
pub mod vendor_specific;
pub use vendor_specific::VendorSpecific;
pub mod debug_port;
pub use debug_port::DebugPort;
pub mod compact_pci_resource_control {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompactPciResourceControl;
}
pub use compact_pci_resource_control::CompactPciResourceControl;
pub mod pci_hot_plug {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PciHotPlug;
}
pub use pci_hot_plug::PciHotPlug;
pub mod bridge_subsystem_vendor_id;
pub use bridge_subsystem_vendor_id::BridgeSubsystemVendorId;
pub mod agp_8x {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Agp8x;
}
pub use agp_8x::Agp8x;
pub mod secure_device {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SecureDevice;
}
pub use secure_device::SecureDevice;
pub mod pci_express;
pub use pci_express::PciExpress;
pub mod msi_x;
pub use msi_x::MsiX;
pub mod sata;
pub use sata::Sata;
pub mod advanced_features;
pub use advanced_features::AdvancedFeatures;
pub mod enhanced_allocation;
pub use enhanced_allocation::EnhancedAllocation;
pub mod flattening_portal_bridge;
pub use flattening_portal_bridge::FlatteningPortalBridge;
#[derive(Debug, Clone, PartialEq, Eq, Snafu)]
pub enum CapabilityError {
#[snafu(display("capabilities pointer should be greater than 0x40"))]
Pointer,
#[snafu(display("[{ptr:02x}] capability header is not available"))]
Header { ptr: u8 },
#[snafu(display("[{ptr:02x}] {source} data read error"))]
Data {
ptr: u8,
source: CapabilityDataError,
},
#[snafu(display("[{ptr:02x}] PCI Express error: {source}"))]
PciExpress {
ptr: u8,
source: pci_express::PciExpressError,
},
#[snafu(display("[{ptr:02x}] HyperTransport: {source}"))]
Hypertransport {
ptr: u8,
source: hypertransport::HypertransportError,
},
#[snafu(display("[{ptr:02x}] Vendor Specific error: {source}"))]
VendorSpecific {
ptr: u8,
source: vendor_specific::VendorSpecificError,
},
#[snafu(display("[{ptr:02x}] MSI error: {source}"))]
MessageSignaledInterrups {
ptr: u8,
source: message_signaled_interrups::MessageSignaledInterrupsError,
},
#[snafu(display("[{ptr:02x}] PCI-X error: {source}"))]
PciX {
ptr: u8,
source: pci_x::PciXError,
},
#[snafu(display("[{ptr:02x}] PCI-X Bridge error: {source}"))]
PciXBridge {
ptr: u8,
source: pci_x::PciXBridgeError,
},
#[snafu(display("[{ptr:02x}] Enhanced Allocation error: {source}"))]
EnhancedAllocation {
ptr: u8,
source: enhanced_allocation::EnhancedAllocationError,
},
}
#[derive(Snafu, Debug, Clone, Copy, PartialEq, Eq)]
#[snafu(display("{name} ({size} bytes)"))]
pub struct CapabilityDataError {
name: &'static str,
size: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Capabilities<'a> {
data: &'a [u8],
header: &'a Header,
pointer: u8,
}
impl<'a> Capabilities<'a> {
pub fn new(data: &'a [u8], header: &'a Header) -> Self {
Self { data, header, pointer: header.capabilities_pointer }
}
}
impl<'a> Iterator for Capabilities<'a> {
type Item = CapabilityResult<'a>;
fn next(&mut self) -> Option<Self::Item> {
(self.pointer != 0).then(|| parse_cap(self.data, &mut self.pointer, self.header))
}
}
type CapabilityResult<'a> = Result<Capability<'a>, CapabilityError>;
fn parse_cap<'a>(bytes: &'a [u8], pointer: &mut u8, header: &'a Header) -> CapabilityResult<'a> {
let ptr = *pointer;
let offset = (*pointer as usize).checked_sub(DDR_OFFSET).ok_or_else(|| {
*pointer = 0;
CapabilityError::Pointer
})?;
let (id, cap_data) = if let Some([id, next, rest @ ..]) = bytes.get(offset..) {
*pointer = *next;
(*id, rest)
} else {
return Err(CapabilityError::Header { ptr });
};
use CapabilityKind as Kind;
let kind = match id {
0x00 => Kind::NullCapability,
0x01 => cap_data
.try_into()
.map(Kind::PowerManagementInterface)
.context(DataSnafu { ptr })?,
0x02 => cap_data
.try_into()
.map(Kind::AcceleratedGraphicsPort)
.context(DataSnafu { ptr })?,
0x03 => cap_data
.try_into()
.map(Kind::VitalProductData)
.context(DataSnafu { ptr })?,
0x04 => cap_data
.try_into()
.map(Kind::SlotIdentification)
.context(DataSnafu { ptr })?,
0x05 => cap_data
.try_into()
.map(Kind::MessageSignaledInterrups)
.context(MessageSignaledInterrupsSnafu { ptr })?,
0x06 => Kind::CompactPciHotSwap(CompactPciHotSwap),
0x07 => {
if matches!(header.header_type, HeaderType::Bridge(_)) {
cap_data
.try_into()
.map(Kind::PciXBridge)
.context(PciXBridgeSnafu { ptr })?
} else {
cap_data
.try_into()
.map(Kind::PciX)
.context(PciXSnafu { ptr })?
}
}
0x08 => cap_data
.try_into()
.map(Kind::Hypertransport)
.context(HypertransportSnafu { ptr })?,
0x09 => VendorSpecific::try_new(cap_data, header)
.map(Kind::VendorSpecific)
.context(VendorSpecificSnafu { ptr })?,
0x0a => cap_data
.try_into()
.map(Kind::DebugPort)
.context(DataSnafu { ptr })?,
0x0b => Kind::CompactPciResourceControl(CompactPciResourceControl),
0x0c => Kind::PciHotPlug(PciHotPlug),
0x0d => cap_data
.try_into()
.map(Kind::BridgeSubsystemVendorId)
.context(DataSnafu { ptr })?,
0x0e => Kind::Agp8x(Agp8x),
0x0f => Kind::SecureDevice(SecureDevice),
0x10 => cap_data
.try_into()
.map(Kind::PciExpress)
.context(PciExpressSnafu { ptr })?,
0x11 => cap_data
.try_into()
.map(Kind::MsiX)
.context(DataSnafu { ptr })?,
0x12 => cap_data
.try_into()
.map(Kind::Sata)
.context(DataSnafu { ptr })?,
0x13 => cap_data
.try_into()
.map(Kind::AdvancedFeatures)
.context(DataSnafu { ptr })?,
0x14 => EnhancedAllocation::try_new(cap_data, header)
.map(Kind::EnhancedAllocation)
.context(EnhancedAllocationSnafu { ptr })?,
0x15 => cap_data
.try_into()
.map(Kind::FlatteningPortalBridge)
.context(DataSnafu { ptr })?,
v => Kind::Reserved(v),
};
Ok(Capability { pointer: ptr, kind })
}
#[derive(Debug, PartialEq, Eq)]
pub struct Capability<'a> {
pub pointer: u8,
pub kind: CapabilityKind<'a>,
}
impl<'a> Capability<'a> {
pub const HEADER_SIZE: usize = 2;
}
#[derive(Debug, PartialEq, Eq)]
pub enum CapabilityKind<'a> {
NullCapability,
PowerManagementInterface(PowerManagementInterface),
AcceleratedGraphicsPort(AcceleratedGraphicsPort),
VitalProductData(VitalProductData),
SlotIdentification(SlotIdentification),
MessageSignaledInterrups(MessageSignaledInterrups),
CompactPciHotSwap(CompactPciHotSwap),
PciX(PciX),
PciXBridge(PciXBridge),
Hypertransport(Hypertransport),
VendorSpecific(VendorSpecific<'a>),
DebugPort(DebugPort),
CompactPciResourceControl(CompactPciResourceControl),
PciHotPlug(PciHotPlug),
BridgeSubsystemVendorId(BridgeSubsystemVendorId),
Agp8x(Agp8x),
SecureDevice(SecureDevice),
PciExpress(PciExpress),
MsiX(MsiX),
Sata(Sata),
AdvancedFeatures(AdvancedFeatures),
EnhancedAllocation(EnhancedAllocation<'a>),
FlatteningPortalBridge(FlatteningPortalBridge),
Reserved(u8),
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ECS_OFFSET;
use pretty_assertions::assert_eq;
use std::prelude::v1::*;
#[test]
fn capabilities() {
let data = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/device/8086_9dc8/config"
));
let header = data.as_slice().try_into().unwrap();
let ddr = &data[DDR_OFFSET..ECS_OFFSET];
let result = Capabilities::new(ddr, &header).collect::<Vec<_>>();
let sample = vec![
Ok(Capability {
pointer: 0x50,
kind: CapabilityKind::PowerManagementInterface(
data[0x50 + 2 .. ].try_into().unwrap(),
),
}),
Ok(Capability {
pointer: 0x80,
kind: VendorSpecific::try_new(&data[(0x80 + 2)..], &header)
.map(CapabilityKind::VendorSpecific)
.unwrap(),
}),
Ok(Capability {
pointer: 0x60,
kind: CapabilityKind::MessageSignaledInterrups(
data[(0x60 + 2)..].try_into().unwrap(),
),
}),
];
assert_eq!(sample, result);
}
}