use core::fmt;
use core::hash::{Hash, Hasher};
use core::slice::Chunks;
use crate::{
bitfield::{BitField, FlagType, Layout},
InfoType,
MalformedStructureError::{self, InvalidFormattedSectionLength},
RawStructure,
};
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct SystemSlots<'a> {
pub handle: u16,
pub slot_designation: &'a str,
pub slot_type: SlotType,
pub slot_data_bus_width: SlotWidth,
pub current_usage: CurrentUsage,
pub slot_length: SlotLength,
pub slot_id: u16,
pub slot_characteristics_1: SlotCharacteristics1,
pub slot_characteristics_2: Option<SlotCharacteristics2>,
pub segment_group_number: Option<u16>,
pub bus_number: Option<u8>,
pub device_and_function_number: Option<DeviceAndFunctionNumber>,
pub data_bus_width: Option<u8>,
pub peer_devices: Option<PeerDevices<'a>>,
pub slot_information: Option<u8>,
pub slot_physical_width: Option<SlotWidth>,
pub slot_pitch: Option<SlotPitch>,
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum SlotType {
Other,
Unknown,
Isa,
Mca,
Eisa,
Pci,
PcCard,
VlVesa,
Proprietary,
ProcessorCardSlot,
ProprietaryMemoryCardSlot,
IoRiserCardSlot,
Nubus,
Pci66Mhz,
Agp,
Agp2x,
Agp4x,
PciX,
Agp8x,
M2Socket1DP,
M2Socket1SD,
M2Socket2,
M2Socket3,
MxmType1,
MxmType2,
MxmType3,
MxmType3He,
MxmType4,
Mxm3TypeA,
Mxm3TypeB,
U2PciExpressGen2,
U2PciExpressGen3,
PciExpressMini52pin1,
PciExpressMini52pin2,
PciExpressMini76pin,
U2PciExpressGen4,
U2PciExpressGen5,
OcpNic3Small,
OcpNic3Large,
OcpNicPriorTo3,
CxlFlexbus1,
Pc98C20,
Pc98C24,
Pc98E,
Pc98LocalBus,
Pc98Card,
PciExpress,
PciExpressX1,
PciExpressX2,
PciExpressX4,
PciExpressX8,
PciExpressX16,
PciExpressGen2,
PciExpressGen2x1,
PciExpressGen2x2,
PciExpressGen2x4,
PciExpressGen2x8,
PciExpressGen2x16,
PciExpressGen3,
PciExpressGen3x1,
PciExpressGen3x2,
PciExpressGen3x4,
PciExpressGen3x8,
PciExpressGen3x16,
PciExpressGen4,
PciExpressGen4x1,
PciExpressGen4x2,
PciExpressGen4x4,
PciExpressGen4x8,
PciExpressGen4x16,
PciExpressGen5,
PciExpressGen5x1,
PciExpressGen5x2,
PciExpressGen5x4,
PciExpressGen5x8,
PciExpressGen5x16,
PciExpressGen6,
E1FormFactorSlot,
E3FormFactorSlot,
Undefined(u8),
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum SlotWidth {
Other,
Unknown,
Byte,
Word,
Dword,
Qword,
Dqword,
X1,
X2,
X4,
X8,
X12,
X16,
X32,
Undefined(u8),
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum CurrentUsage {
Other,
Unknown,
Available,
InUse,
Unavailable,
Undefined(u8),
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum SlotLength {
Other,
Unknown,
ShortLength,
LongLength,
DriveFormFactor2_5,
DriveFormFactor3_5,
Undefined(u8),
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct SlotCharacteristics1(u8);
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct SlotCharacteristics2(u8);
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct Device {
pub segment_group_number: u16,
pub bus_number: u8,
pub device_and_function_number: DeviceAndFunctionNumber,
pub data_bus_width: u8,
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct DeviceAndFunctionNumber(u8, u8);
#[repr(C)]
#[repr(packed)]
struct DevicePacked {
segment_group_number: u16,
bus_number: u8,
dev_and_fun_number: u8,
data_bus_width: u8,
}
#[derive(Clone, Debug)]
pub struct PeerDevices<'a>(Chunks<'a, u8>);
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct SlotPitch(u16);
impl<'a> SystemSlots<'a> {
pub(crate) fn try_from(structure: RawStructure<'a>) -> Result<SystemSlots<'a>, MalformedStructureError> {
let data_len = structure.data.len() + 4;
let handle = structure.handle;
match ((structure.version.major, structure.version.minor), data_len) {
(v, l) if ((2, 0)..(2, 1)).contains(&v) && l != 0x0C => {
Err(InvalidFormattedSectionLength(InfoType::SystemSlots, handle, "", 0x0C))
}
(v, l) if ((2, 1)..(2, 6)).contains(&v) && l != 0x0D => {
Err(InvalidFormattedSectionLength(InfoType::SystemSlots, handle, "", 0x0D))
}
(v, l) if ((2, 6)..(3, 2)).contains(&v) && l != 0x11 => {
Err(InvalidFormattedSectionLength(InfoType::SystemSlots, handle, "", 0x11))
}
(v, l) if v >= (3, 2) && l < 0x11 => Err(InvalidFormattedSectionLength(
InfoType::SystemSlots,
handle,
"minimum of ",
0x11,
)),
_ => {
let peer_grouping_count: u8 = structure.get::<u8>(0x12).unwrap_or(0);
let n = peer_grouping_count as usize;
Ok(SystemSlots {
handle,
slot_designation: structure.get_string(0x04)?,
slot_type: structure.get::<u8>(0x05)?.into(),
slot_data_bus_width: structure.get::<u8>(0x06)?.into(),
current_usage: structure.get::<u8>(0x07)?.into(),
slot_length: structure.get::<u8>(0x08)?.into(),
slot_id: structure.get::<u16>(0x09)?,
slot_characteristics_1: structure.get::<u8>(0x0B)?.into(),
slot_characteristics_2: structure.get::<u8>(0x0C).ok().map(Into::into),
segment_group_number: structure
.get::<u16>(0x0D)
.ok()
.filter(|v| v != &0xFFFF),
bus_number: structure
.get::<u8>(0x0F)
.ok()
.filter(|v| v != &0xFF),
device_and_function_number: structure
.get::<u8>(0x10)
.ok()
.filter(|v| v != &0xFF)
.map(Into::into),
data_bus_width: structure.get::<u8>(0x11).ok(),
peer_devices: structure.get_slice(0x13, 5 * n).map(Into::into),
slot_information: structure.get::<u8>(0x14 + 5 * n).ok(),
slot_physical_width: structure.get::<u8>(0x15 + 5 * n).ok().map(Into::into),
slot_pitch: structure.get::<u16>(0x16 + 5 * n).ok().map(Into::into),
})
}
}
}
}
impl From<u8> for SlotType {
fn from(byte: u8) -> SlotType {
match byte {
0x01 => SlotType::Other,
0x02 => SlotType::Unknown,
0x03 => SlotType::Isa,
0x04 => SlotType::Mca,
0x05 => SlotType::Eisa,
0x06 => SlotType::Pci,
0x07 => SlotType::PcCard,
0x08 => SlotType::VlVesa,
0x09 => SlotType::Proprietary,
0x0A => SlotType::ProcessorCardSlot,
0x0B => SlotType::ProprietaryMemoryCardSlot,
0x0C => SlotType::IoRiserCardSlot,
0x0D => SlotType::Nubus,
0x0E => SlotType::Pci66Mhz,
0x0F => SlotType::Agp,
0x10 => SlotType::Agp2x,
0x11 => SlotType::Agp4x,
0x12 => SlotType::PciX,
0x13 => SlotType::Agp8x,
0x14 => SlotType::M2Socket1DP,
0x15 => SlotType::M2Socket1SD,
0x16 => SlotType::M2Socket2,
0x17 => SlotType::M2Socket3,
0x18 => SlotType::MxmType1,
0x19 => SlotType::MxmType2,
0x1A => SlotType::MxmType3,
0x1B => SlotType::MxmType3He,
0x1C => SlotType::MxmType4,
0x1D => SlotType::Mxm3TypeA,
0x1E => SlotType::Mxm3TypeB,
0x1F => SlotType::PciExpressGen2,
0x20 => SlotType::PciExpressGen3,
0x21 => SlotType::PciExpressMini52pin1,
0x22 => SlotType::PciExpressMini52pin2,
0x23 => SlotType::PciExpressMini76pin,
0x24 => SlotType::PciExpressGen4,
0x25 => SlotType::PciExpressGen5,
0x26 => SlotType::OcpNic3Small,
0x27 => SlotType::OcpNic3Large,
0x28 => SlotType::OcpNicPriorTo3,
0x30 => SlotType::CxlFlexbus1,
0xA0 => SlotType::Pc98C20,
0xA1 => SlotType::Pc98C24,
0xA2 => SlotType::Pc98E,
0xA3 => SlotType::Pc98LocalBus,
0xA4 => SlotType::Pc98Card,
0xA5 => SlotType::PciExpress,
0xA6 => SlotType::PciExpressX1,
0xA7 => SlotType::PciExpressX2,
0xA8 => SlotType::PciExpressX4,
0xA9 => SlotType::PciExpressX8,
0xAA => SlotType::PciExpressX16,
0xAB => SlotType::PciExpressGen2,
0xAC => SlotType::PciExpressGen2x1,
0xAD => SlotType::PciExpressGen2x2,
0xAE => SlotType::PciExpressGen2x4,
0xAF => SlotType::PciExpressGen2x8,
0xB0 => SlotType::PciExpressGen2x16,
0xB1 => SlotType::PciExpressGen3,
0xB2 => SlotType::PciExpressGen3x1,
0xB3 => SlotType::PciExpressGen3x2,
0xB4 => SlotType::PciExpressGen3x4,
0xB5 => SlotType::PciExpressGen3x8,
0xB6 => SlotType::PciExpressGen3x16,
0xB8 => SlotType::PciExpressGen4,
0xB9 => SlotType::PciExpressGen4x1,
0xBA => SlotType::PciExpressGen4x2,
0xBB => SlotType::PciExpressGen4x4,
0xBC => SlotType::PciExpressGen4x8,
0xBD => SlotType::PciExpressGen4x16,
0xBE => SlotType::PciExpressGen5,
0xBF => SlotType::PciExpressGen5x1,
0xC0 => SlotType::PciExpressGen5x2,
0xC1 => SlotType::PciExpressGen5x4,
0xC2 => SlotType::PciExpressGen5x8,
0xC3 => SlotType::PciExpressGen5x16,
0xC4 => SlotType::PciExpressGen6,
0xC5 => SlotType::E1FormFactorSlot,
0xC6 => SlotType::E3FormFactorSlot,
v => Self::Undefined(v),
}
}
}
impl fmt::Display for SlotType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let is_alt = f.alternate();
match self {
Self::Other => write!(f, "Other"),
Self::Unknown => write!(f, "Unknown"),
Self::Isa => write!(f, "ISA"),
Self::Mca => write!(f, "MCA"),
Self::Eisa => write!(f, "EISA"),
Self::Pci => write!(f, "PCI"),
Self::PcCard => write!(f, "PC Card (PCMCIA)"),
Self::VlVesa => write!(f, "VL-VESA"),
Self::Proprietary => write!(f, "Proprietary"),
Self::ProcessorCardSlot => write!(f, "Processor Card Slot"),
Self::ProprietaryMemoryCardSlot => write!(f, "Proprietary Memory Card Slot"),
Self::IoRiserCardSlot => write!(f, "I/O Riser Card Slot"),
Self::Nubus => write!(f, "NuBus"),
Self::Pci66Mhz => write!(f, "PCI – 66MHz Capable"),
Self::Agp => write!(f, "AGP"),
Self::Agp2x => write!(f, "AGP 2X"),
Self::Agp4x => write!(f, "AGP 4X"),
Self::PciX => write!(f, "PCI-X"),
Self::Agp8x => write!(f, "AGP 8X"),
Self::M2Socket1DP => write!(f, "M.2 Socket 1-DP (Mechanical Key A)"),
Self::M2Socket1SD => write!(f, "M.2 Socket 1-SD (Mechanical Key E)"),
Self::M2Socket2 => write!(f, "M.2 Socket 2 (Mechanical Key B)"),
Self::M2Socket3 => write!(f, "M.2 Socket 3 (Mechanical Key M)"),
Self::MxmType1 => write!(f, "MXM Type I"),
Self::MxmType2 => write!(f, "MXM Type II"),
Self::MxmType3 => write!(f, "MXM Type III (standard connector)"),
Self::MxmType3He => write!(f, "MXM Type III (HE connector)"),
Self::MxmType4 => write!(f, "MXM Type IV"),
Self::Mxm3TypeA => write!(f, "MXM 3.0 Type A"),
Self::Mxm3TypeB => write!(f, "MXM 3.0 Type B"),
Self::U2PciExpressGen2 => write!(f, "PCI Express Gen 2 SFF-8639 (U.2)"),
Self::U2PciExpressGen3 => write!(f, "PCI Express Gen 3 SFF-8639 (U.2)"),
Self::PciExpressMini52pin1 => {
if is_alt {
write!(
f,
"PCI Express Mini 52-pin (CEM spec. 2.0) with bottom-side keep-outs. \
Use Slot Length field value 03h (short length) for \"half-Mini card\" -only \
support, 04h (long length) for \"full-Mini card\" or dual support."
)
} else {
write!(f, "PCI Express Mini 52-pin with bottom-side keep-outs")
}
}
Self::PciExpressMini52pin2 => {
if is_alt {
write!(
f,
"PCI Express Mini 52-pin (CEM spec. 2.0) without bottom-side keep-outs. \
Use Slot Length field value 03h (short length) for \"half-Mini card\" -only \
support, 04h (long length) for \"full-Mini card\" or dual support."
)
} else {
write!(f, "PCI Express Mini 52-pin without bottom-side keep-outs")
}
}
Self::PciExpressMini76pin => {
if is_alt {
write!(
f,
"PCI Express Mini 76-pin (CEM spec. 2.0) Corresponds to Display-Mini card."
)
} else {
write!(f, "PCI Express Mini 76-pin")
}
}
Self::U2PciExpressGen4 => write!(f, "PCI Express Gen 4 SFF-8639 (U.2)"),
Self::U2PciExpressGen5 => write!(f, "PCI Express Gen 5 SFF-8639 (U.2)"),
Self::OcpNic3Small => write!(f, "OCP NIC 3.0 Small Form Factor (SFF)"),
Self::OcpNic3Large => write!(f, "OCP NIC 3.0 Large Form Factor (LFF)"),
Self::OcpNicPriorTo3 => write!(f, "OCP NIC Prior to 3.0"),
Self::CxlFlexbus1 => write!(f, "CXL Flexbus 1.0 (deprecated)"),
Self::Pc98C20 => write!(f, "PC-98/C20"),
Self::Pc98C24 => write!(f, "PC-98/C24"),
Self::Pc98E => write!(f, "PC-98/E"),
Self::Pc98LocalBus => write!(f, "PC-98/Local Bus"),
Self::Pc98Card => write!(f, "PC-98/Card"),
Self::PciExpress => write!(f, "PCI Express"),
Self::PciExpressX1 => write!(f, "PCI Express x1"),
Self::PciExpressX2 => write!(f, "PCI Express x2"),
Self::PciExpressX4 => write!(f, "PCI Express x4"),
Self::PciExpressX8 => write!(f, "PCI Express x8"),
Self::PciExpressX16 => write!(f, "PCI Express x16"),
Self::PciExpressGen2 => write!(f, "PCI Express Gen 2"),
Self::PciExpressGen2x1 => write!(f, "PCI Express Gen 2 x1"),
Self::PciExpressGen2x2 => write!(f, "PCI Express Gen 2 x2"),
Self::PciExpressGen2x4 => write!(f, "PCI Express Gen 2 x4"),
Self::PciExpressGen2x8 => write!(f, "PCI Express Gen 2 x8"),
Self::PciExpressGen2x16 => write!(f, "PCI Express Gen 2 x16"),
Self::PciExpressGen3 => write!(f, "PCI Express Gen 3"),
Self::PciExpressGen3x1 => write!(f, "PCI Express Gen 3 x1"),
Self::PciExpressGen3x2 => write!(f, "PCI Express Gen 3 x2"),
Self::PciExpressGen3x4 => write!(f, "PCI Express Gen 3 x4"),
Self::PciExpressGen3x8 => write!(f, "PCI Express Gen 3 x8"),
Self::PciExpressGen3x16 => write!(f, "PCI Express Gen 3 x16"),
Self::PciExpressGen4 => write!(f, "PCI Express Gen 4"),
Self::PciExpressGen4x1 => write!(f, "PCI Express Gen 4 x1"),
Self::PciExpressGen4x2 => write!(f, "PCI Express Gen 4 x2"),
Self::PciExpressGen4x4 => write!(f, "PCI Express Gen 4 x4"),
Self::PciExpressGen4x8 => write!(f, "PCI Express Gen 4 x8"),
Self::PciExpressGen4x16 => write!(f, "PCI Express Gen 4 x16"),
Self::PciExpressGen5 => write!(f, "PCI Express Gen 5"),
Self::PciExpressGen5x1 => write!(f, "PCI Express Gen 5 x1"),
Self::PciExpressGen5x2 => write!(f, "PCI Express Gen 5 x2"),
Self::PciExpressGen5x4 => write!(f, "PCI Express Gen 5 x4"),
Self::PciExpressGen5x8 => write!(f, "PCI Express Gen 5 x8"),
Self::PciExpressGen5x16 => write!(f, "PCI Express Gen 5 x16"),
Self::PciExpressGen6 => write!(f, "PCI Express Gen 6 and Beyond"),
Self::E1FormFactorSlot => {
if is_alt {
write!(
f,
"Enterprise and Datacenter 1U E1 Form Factor Slot (EDSFF E1.S, E1.L). \
See specifications SFF-TA-1006 and SFF-TA-1007 for more details on values \
for slot length and pitch."
)
} else {
write!(f, "EDSFF E1")
}
}
Self::E3FormFactorSlot => {
if is_alt {
write!(
f,
"Enterprise and Datacenter 3\" E3 Form Factor Slot (EDSFF E3.S, \
E3.L). See specification SFF-TA-1008 for details on values for slot length \
and pitch."
)
} else {
write!(f, "EDSFF E3")
}
}
Self::Undefined(v) => write!(f, "Undefined: {v}"),
}
}
}
impl From<u8> for SlotWidth {
fn from(byte: u8) -> SlotWidth {
match byte {
0x01 => Self::Other,
0x02 => Self::Unknown,
0x03 => Self::Byte,
0x04 => Self::Word,
0x05 => Self::Dword,
0x06 => Self::Qword,
0x07 => Self::Dqword,
0x08 => Self::X1,
0x09 => Self::X2,
0x0A => Self::X4,
0x0B => Self::X8,
0x0C => Self::X12,
0x0D => Self::X16,
0x0E => Self::X32,
v => Self::Undefined(v),
}
}
}
impl fmt::Display for SlotWidth {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Other => write!(f, "Other"),
Self::Unknown => write!(f, "Unknown"),
Self::Byte => write!(f, "8 bit"),
Self::Word => write!(f, "16 bit"),
Self::Dword => write!(f, "32 bit"),
Self::Qword => write!(f, "64 bit"),
Self::Dqword => write!(f, "128 bit"),
Self::X1 => write!(f, "1x or x1"),
Self::X2 => write!(f, "2x or x2"),
Self::X4 => write!(f, "4x or x4"),
Self::X8 => write!(f, "8x or x8"),
Self::X12 => write!(f, "12x or x12"),
Self::X16 => write!(f, "16x or x16"),
Self::X32 => write!(f, "32x or x32"),
Self::Undefined(v) => write!(f, "Undefined: {v}"),
}
}
}
impl From<u8> for CurrentUsage {
fn from(byte: u8) -> CurrentUsage {
match byte {
0x01 => Self::Other,
0x02 => Self::Unknown,
0x03 => Self::Available,
0x04 => Self::InUse,
0x05 => Self::Unavailable,
v => Self::Undefined(v),
}
}
}
impl fmt::Display for CurrentUsage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let is_alt = f.alternate();
match self {
Self::Other => write!(f, "Other"),
Self::Unknown => write!(f, "Unknown"),
Self::Available => write!(f, "Available"),
Self::InUse => write!(f, "In use"),
Self::Unavailable => {
if is_alt {
write!(f, "Unavailable (e.g., connected to a processor that is not installed)")
} else {
write!(f, "Unavailable")
}
}
Self::Undefined(v) => write!(f, "Undefined: {v}"),
}
}
}
impl From<u8> for SlotLength {
fn from(byte: u8) -> SlotLength {
match byte {
0x01 => Self::Other,
0x02 => Self::Unknown,
0x03 => Self::ShortLength,
0x04 => Self::LongLength,
0x05 => Self::DriveFormFactor2_5,
0x06 => Self::DriveFormFactor3_5,
v => Self::Undefined(v),
}
}
}
impl fmt::Display for SlotLength {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Other => write!(f, "Other"),
Self::Unknown => write!(f, "Unknown"),
Self::ShortLength => write!(f, "Short Length"),
Self::LongLength => write!(f, "Long Length"),
Self::DriveFormFactor2_5 => write!(f, "2.5\" drive form factor"),
Self::DriveFormFactor3_5 => write!(f, "3.5\" drive form factor"),
Self::Undefined(v) => write!(f, "Undefined: {v}"),
}
}
}
impl BitField<'_> for SlotCharacteristics1 {
type Size = u8;
fn value(&self) -> Self::Size {
self.0
}
layout!(
length = 8;
"Characteristics unknown",
"5.0 V is provided"
"Provides 5.0 volts",
"3.3 V is provided"
"Provides 3.3 volts",
"Opening is shared"
"Slot’s opening is shared with another slot (for example, PCI/EISA shared slot)",
"PC Card-16 is supported"
"PC Card slot supports PC Card-16",
"Cardbus is supported"
"PC Card slot supports CardBus",
"Zoom Video is supported"
"PC Card slot supports Zoom Video",
"Modem ring resume is supported"
"PC Card slot supports Modem Ring Resume",
);
}
impl From<u8> for SlotCharacteristics1 {
fn from(byte: u8) -> Self {
Self(byte)
}
}
impl BitField<'_> for SlotCharacteristics2 {
type Size = u8;
fn value(&self) -> Self::Size {
self.0
}
layout!(
length = 8;
"PME signal is supported"
"PCI slot supports Power Management Event (PME#) signal",
"Hot-plug devices are supported"
"Slot supports hot-plug devices",
"SMBus signal is supported"
"PCI slot supports SMBus signal",
"PCIe slot bifurcation is supported"
"PCIe slot supports bifurcation. This slot can partition its lanes into two or more \
PCIe devices plugged into the slot. Note: This field does not indicate complete details \
on what levels of bifurcation are supported by the slot, but only that the slot \
supports some level of bifurcation",
"Async/surprise removal is supported"
"Slot supports async/surprise removal (i.e., removal without prior notification to the \
operating system, device driver, or applications)",
"Flexbus slot, CXL 1.0 capable",
"Flexbus slot, CXL 2.0 capable",
"Reserved": 1,
);
}
impl From<u8> for SlotCharacteristics2 {
fn from(byte: u8) -> Self {
Self(byte)
}
}
impl<'a> From<&'a [u8]> for Device {
fn from(data: &'a [u8]) -> Device {
let_as_struct!(packed, DevicePacked, data);
Device {
segment_group_number: u16::from_le(packed.segment_group_number),
bus_number: packed.bus_number,
device_and_function_number: packed.dev_and_fun_number.into(),
data_bus_width: packed.data_bus_width,
}
}
}
impl<'a> From<&'a Device> for [u8; 5] {
fn from(d: &'a Device) -> [u8; 5] {
let segment = d.segment_group_number.to_le_bytes();
[
segment[0],
segment[1],
d.bus_number,
d.device_and_function_number.into(),
d.data_bus_width,
]
}
}
impl fmt::Display for Device {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{:04x}:{:02x}:{:02x}.{:x} (Width {})",
self.segment_group_number,
self.bus_number,
self.device_and_function_number.0,
self.device_and_function_number.1,
self.data_bus_width,
)
}
}
impl From<u8> for DeviceAndFunctionNumber {
fn from(byte: u8) -> Self {
Self(byte >> 3, byte & 0b0111)
}
}
impl From<DeviceAndFunctionNumber> for u8 {
fn from(df: DeviceAndFunctionNumber) -> Self {
(df.0 << 3) | df.1
}
}
impl<'a> From<&'a [u8]> for PeerDevices<'a> {
fn from(data: &'a [u8]) -> Self {
Self(data.chunks(5))
}
}
impl PartialEq for PeerDevices<'_> {
fn eq(&self, other: &Self) -> bool {
self.0.clone().eq(other.0.clone())
}
}
impl Eq for PeerDevices<'_> {}
impl Hash for PeerDevices<'_> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.clone().for_each(|c| c.hash(state));
}
}
impl Iterator for PeerDevices<'_> {
type Item = Device;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(Into::into)
}
}
impl From<u16> for SlotPitch {
fn from(word: u16) -> Self {
Self(word)
}
}
impl fmt::Display for SlotPitch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0 == 0 {
write!(f, "Not given or is unknown")
} else {
write!(f, "{:.1} mm", (self.0 as f32) / 10.0)
}
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq as pretty_assert_eq;
use std::prelude::v1::*;
const PRIMES: &[usize] = &[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61];
#[test]
fn slot_type() {
use super::SlotType;
let samples = &[
(0x03, SlotType::Isa, "ISA", "ISA"),
(
0x21,
SlotType::PciExpressMini52pin1,
"PCI Express Mini 52-pin with bottom-side keep-outs",
"PCI Express Mini 52-pin (CEM spec. 2.0) with bottom-side keep-outs. Use Slot \
Length field value 03h (short length) for \"half-Mini card\" -only support, 04h \
(long length) for \"full-Mini card\" or dual support.",
),
(0xA0, SlotType::Pc98C20, "PC-98/C20", "PC-98/C20"),
(
0xC4,
SlotType::PciExpressGen6,
"PCI Express Gen 6 and Beyond",
"PCI Express Gen 6 and Beyond",
),
(0xFE, SlotType::Undefined(254), "Undefined: 254", "Undefined: 254"),
];
let result = samples.iter().map(|v| Into::into(v.0)).collect::<Vec<_>>();
pretty_assert_eq!(
samples
.iter()
.map(|(_, v, s, m)| (v, (*s).into(), (*m).into()))
.collect::<Vec<_>>(),
result
.iter()
.map(|r| (r, format!("{r}"), format!("{r:#}")))
.collect::<Vec<_>>(),
);
}
#[test]
fn slot_width() {
use super::SlotWidth;
let samples = &[
(0x01, SlotWidth::Other, "Other"),
(0x03, SlotWidth::Byte, "8 bit"),
(0x06, SlotWidth::Qword, "64 bit"),
(0x07, SlotWidth::Dqword, "128 bit"),
(0x0E, SlotWidth::X32, "32x or x32"),
(0xFE, SlotWidth::Undefined(254), "Undefined: 254"),
];
let result = samples.iter().map(|v| Into::into(v.0)).collect::<Vec<_>>();
pretty_assert_eq!(
samples.iter().map(|(_, v, s)| (v, (*s).into())).collect::<Vec<_>>(),
result.iter().map(|r| (r, format!("{r}"))).collect::<Vec<_>>(),
);
}
#[test]
fn current_usage() {
use super::CurrentUsage;
let samples = &[
(0x01, CurrentUsage::Other, "Other"),
(0x02, CurrentUsage::Unknown, "Unknown"),
(0x04, CurrentUsage::InUse, "In use"),
(
0x05,
CurrentUsage::Unavailable,
"Unavailable (e.g., connected to a processor that is not installed)",
),
(0xFE, CurrentUsage::Undefined(254), "Undefined: 254"),
];
let result = samples.iter().map(|v| Into::into(v.0)).collect::<Vec<_>>();
pretty_assert_eq!(
samples.iter().map(|(_, v, s)| (v, (*s).into())).collect::<Vec<_>>(),
result.iter().map(|r| (r, format!("{r:#}"))).collect::<Vec<_>>(),
);
}
#[test]
fn slot_length() {
use super::SlotLength;
let samples = &[
(0x01, SlotLength::Other, "Other"),
(0x02, SlotLength::Unknown, "Unknown"),
(0x03, SlotLength::ShortLength, "Short Length"),
(0x05, SlotLength::DriveFormFactor2_5, "2.5\" drive form factor"),
(0xFE, SlotLength::Undefined(254), "Undefined: 254"),
];
let result = samples.iter().map(|v| Into::into(v.0)).collect::<Vec<_>>();
pretty_assert_eq!(
samples.iter().map(|(_, v, s)| (v, (*s).into())).collect::<Vec<_>>(),
result.iter().map(|r| (r, format!("{r:}"))).collect::<Vec<_>>(),
);
}
#[test]
fn slot_caharacteristics_1() {
use super::SlotCharacteristics1;
use crate::bitfield::{BitField, Position};
let sample = PRIMES.iter().cloned().take_while(|&x| x < 8).collect::<Vec<_>>();
let byte = sample.iter().map(|&p| Position(p)).collect();
let result = SlotCharacteristics1(byte)
.iter()
.filter_map(|f| if f.is_set { Some(*f.position) } else { None })
.collect::<Vec<_>>();
pretty_assert_eq!(sample, result, "Positions");
let dflt_sample = vec!["5.0 V is provided", "Modem ring resume is supported"];
let alt_sample = vec!["Provides 5.0 volts", "PC Card slot supports Modem Ring Resume"];
let byte = 0b1000_0010;
let iter = SlotCharacteristics1(byte).significants();
let dflt_result = iter.map(|f| format!("{f}")).collect::<Vec<_>>();
pretty_assert_eq!(
dflt_sample,
dflt_result,
"Significant values, default formatting ({:08b})",
byte
);
let alt_result = iter.map(|f| format!("{f:#}")).collect::<Vec<_>>();
pretty_assert_eq!(
alt_sample,
alt_result,
"Significant values, alternative formatting ({:08b})",
byte
);
let result = SlotCharacteristics1(0).reserved().count();
pretty_assert_eq!(0, result, "Reserved fields");
}
#[test]
fn slot_caharacteristics_2() {
use super::SlotCharacteristics2;
use crate::bitfield::{BitField, Position};
let sample = PRIMES.iter().cloned().take_while(|&x| x < 8).collect::<Vec<_>>();
let byte = sample.iter().map(|&p| Position(p)).collect();
let result = SlotCharacteristics2(byte)
.iter()
.filter_map(|f| if f.is_set { Some(*f.position) } else { None })
.collect::<Vec<_>>();
pretty_assert_eq!(sample, result, "Positions");
let dflt_sample = vec!["PME signal is supported", "Async/surprise removal is supported"];
let alt_sample = vec!["PCI slot supports Power Management Event (PME#) signal","Slot supports async/surprise removal (i.e., removal without prior notification to the operating system, device driver, or applications)"];
let byte = 0b0001_0001;
let iter = SlotCharacteristics2(byte).significants();
let dflt_result = iter.map(|f| format!("{f}")).collect::<Vec<_>>();
pretty_assert_eq!(
dflt_sample,
dflt_result,
"Significant values, default formatting ({:08b})",
byte
);
let alt_result = iter.map(|f| format!("{f:#}")).collect::<Vec<_>>();
pretty_assert_eq!(
alt_sample,
alt_result,
"Significant values, alternative formatting ({:08b})",
byte
);
let result = SlotCharacteristics2(0).reserved().count();
pretty_assert_eq!(1, result, "Reserved fields");
}
#[test]
fn device() {
use super::Device;
let sample_data = &[
0x09, 0x11, 0x03, 0x09, 0x01, 0xB1, 0x0D, 0x04, 0x04, 0x04, 0x00, 0x04, 0x01, 0xE9, 0x05, 0xB5, 0xDF, 0x10,
];
let result: Device = sample_data[0x0D..=0x11].into();
pretty_assert_eq!("05e9:b5:1b.7 (Width 16)", format!("{}", result), "Display trait");
let as_array: [u8; 5] = (&result).into();
pretty_assert_eq!([0xE9, 0x05, 0xB5, 0xDF, 0x10], as_array, "Display into [u8; 5]");
}
#[test]
fn peer_devices() {
use super::PeerDevices;
let sample_data = &[
0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x01, 0x00, 0x00, 0x00, 0xE4, 0x04,
];
let result = PeerDevices(sample_data.chunks(5));
let display_sample: Vec<String> = [
"0000:00:01.0 (Width 0)",
"0000:00:1c.3 (Width 1)",
"0000:00:1c.4 (Width 4)",
]
.iter()
.map(|v| v.to_string())
.collect();
pretty_assert_eq!(display_sample, result.map(|v| format!("{v}")).collect::<Vec<_>>());
}
#[test]
fn system_slots() {
use super::*;
use crate::{InfoType, RawStructure, SmbiosVersion};
for major in u8::MIN..=u8::MAX {
for minor in u8::MIN..=u8::MAX {
let structure = RawStructure {
version: SmbiosVersion { major, minor },
info: InfoType::SystemSlots,
length: 0,
handle: 666,
data: &[],
strings: &[],
};
let result = SystemSlots::try_from(structure);
match ((major, minor), result) {
(v, Err(e)) if ((2, 0)..(2, 1)).contains(&v) => {
pretty_assert_eq!(
"Formatted section length of structure SystemSlots with handle 666 \
should be 12 bytes",
format!("{}", e)
);
}
(v, Err(e)) if ((2, 1)..(2, 6)).contains(&v) => {
pretty_assert_eq!(
"Formatted section length of structure SystemSlots with handle 666 \
should be 13 bytes",
format!("{}", e)
);
}
(v, Err(e)) if ((2, 6)..(3, 2)).contains(&v) => {
pretty_assert_eq!(
"Formatted section length of structure SystemSlots with handle 666 \
should be 17 bytes",
format!("{}", e)
);
}
(v, Err(e)) if ((3, 2)..).contains(&v) => {
pretty_assert_eq!(
"Formatted section length of structure SystemSlots with handle 666 \
should be minimum of 17 bytes",
format!("{}", e)
);
}
(_, Err(e)) => {
pretty_assert_eq!("could not convert slice to array", format!("{}", e));
}
(_, Ok(ss)) => {
pretty_assert_eq!(666, ss.handle);
}
}
}
}
let peer_devices = [
Device {
segment_group_number: 1,
bus_number: 1,
device_and_function_number: DeviceAndFunctionNumber(1, 1),
data_bus_width: 1,
},
Device {
segment_group_number: 2,
bus_number: 2,
device_and_function_number: DeviceAndFunctionNumber(2, 2),
data_bus_width: 2,
},
Device {
segment_group_number: 3,
bus_number: 3,
device_and_function_number: DeviceAndFunctionNumber(3, 3),
data_bus_width: 3,
},
]
.iter()
.fold(Vec::new(), |mut acc, v| {
let arr: [u8; 5] = v.into();
acc.extend_from_slice(&arr);
acc
});
let sample = SystemSlots {
handle: 0x0023,
slot_designation: "SSD1",
slot_type: SlotType::PciExpress,
slot_data_bus_width: SlotWidth::X4,
current_usage: CurrentUsage::InUse,
slot_length: SlotLength::ShortLength,
slot_id: 2,
slot_characteristics_1: SlotCharacteristics1(0b0000_1100),
slot_characteristics_2: Some(SlotCharacteristics2(0b0000_0001)),
segment_group_number: Some(0),
bus_number: Some(0),
device_and_function_number: Some(DeviceAndFunctionNumber(0x1C, 4)),
data_bus_width: Some(0),
peer_devices: Some(peer_devices.as_slice().into()),
slot_information: Some(0x06),
slot_physical_width: Some(SlotWidth::X16),
slot_pitch: Some(SlotPitch(0x04E2)),
};
let structure = RawStructure {
version: (3, 4).into(),
info: InfoType::SystemSlots,
length: 0,
handle: 0x0023,
data: &[
0x01, 0xA5, 0x0A, 0x04, 0x03, 0x02,
0x00, 0x0C, 0x01, 0x00,
0x00, 0x00, 0xE4, 0x00, 0x03, 0x01,
0x00,
0x01,
0x01 << 3 | 0x01,
0x01, 0x02,
0x00,
0x02,
0x02 << 3 | 0x02,
0x02, 0x03,
0x00,
0x03,
0x03 << 3 | 0x03,
0x03, 0x00, 0x06, 0x0D, 0xE2,
0x04, ],
strings: &[
0x53, 0x53, 0x44, 0x31, 0x00,
],
};
let result = SystemSlots::try_from(structure).unwrap();
pretty_assert_eq!(sample, result, "Sample:\n{:X?}\nResult:\n{:X?}", sample, result);
}
#[test]
fn dmi_bin() {
use super::*;
use crate::{EntryPoint, Structure};
const DMIDECODE_BIN: &[u8] = include_bytes!("../../tests/data/dmi.0.bin");
let entry_point = EntryPoint::search(DMIDECODE_BIN).unwrap();
let slots = entry_point
.structures(&DMIDECODE_BIN[(entry_point.smbios_address() as usize)..])
.filter_map(|s| {
if let Err(ref s) = s {
println!("{s}");
}
s.ok().filter(|s| matches!(s, Structure::SystemSlots(_)))
})
.collect::<Vec<_>>();
pretty_assert_eq!(4, slots.len(), "Slots count: {}. Should be 4", slots.len());
let slot1_sample = SystemSlots {
handle: 0x0900,
slot_designation: "PCIe Slot 1",
slot_type: SlotType::PciExpressGen3x16,
slot_data_bus_width: SlotWidth::X8,
current_usage: CurrentUsage::Available,
slot_length: SlotLength::LongLength,
slot_id: 1,
slot_characteristics_1: SlotCharacteristics1(0b0000_0100),
slot_characteristics_2: Some(SlotCharacteristics2(0b0000_0001)),
segment_group_number: None,
bus_number: None,
device_and_function_number: None,
data_bus_width: None,
peer_devices: None,
slot_information: None,
slot_physical_width: None,
slot_pitch: None,
};
let slot1_result = slots
.iter()
.find_map(|s| match s {
Structure::SystemSlots(ss) if ss.handle == 0x0900 => Some(ss),
_ => None,
})
.unwrap();
pretty_assert_eq!(&slot1_sample, slot1_result, "Entire SystemSlots struct: Slot 1");
let slot4_sample = SystemSlots {
handle: 0x0903,
slot_designation: "PCIe Slot 4",
slot_type: SlotType::PciExpressGen3,
slot_data_bus_width: SlotWidth::X16,
current_usage: CurrentUsage::InUse,
slot_length: SlotLength::LongLength,
slot_id: 4,
slot_characteristics_1: SlotCharacteristics1(0b0000_0100),
slot_characteristics_2: Some(SlotCharacteristics2(0b0000_0001)),
segment_group_number: Some(0),
bus_number: Some(0xAF),
device_and_function_number: Some(0x00.into()),
data_bus_width: None,
peer_devices: None,
slot_information: None,
slot_physical_width: None,
slot_pitch: None,
};
let slot4_result = slots
.iter()
.find_map(|s| match s {
Structure::SystemSlots(ss) if ss.handle == 0x0903 => Some(ss),
_ => None,
})
.unwrap();
pretty_assert_eq!(&slot4_sample, slot4_result, "Entire SystemSlots struct: Slot 4");
}
}