use heterob::{bit_numbering::Lsb, endianness::Le, Seq, P11, P13, P14, P2, P4, P5, P6, P8};
use snafu::Snafu;
#[derive(Debug, Clone, PartialEq, Eq, Snafu)]
pub enum PciXError {
#[snafu(display("command and status registers are unreadable"))]
CommandAndStatus,
#[snafu(display("Mode 2 ECC registres unreadable"))]
EccMode2Only,
#[snafu(display("Mode 1/2 ECC registres unreadable"))]
EccMode1OrMode2,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PciX {
pub command: Command,
pub status: Status,
pub ecc: Ecc,
}
impl TryFrom<&[u8]> for PciX {
type Error = PciXError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
let Seq {
head: Le((command, status)),
tail: slice,
} = P2(slice)
.try_into()
.map_err(|_| PciXError::CommandAndStatus)?;
let Lsb(((), pci_x_capabilities_list_item_version)) = P2::<u16, 12, 2>(command).into();
let ecc = match pci_x_capabilities_list_item_version {
0b00u8 => Ecc::None,
0b01 => {
let Seq {
head: Le((control_and_status, first_address, second_address, attribute)),
..
} = P4(slice).try_into().map_err(|_| PciXError::EccMode2Only)?;
Ecc::Mode2Only {
control_and_status: From::<u32>::from(control_and_status),
first_address,
second_address,
attribute,
}
}
0b10 => {
let Seq {
head: Le((control_and_status, first_address, second_address, attribute)),
..
} = P4(slice)
.try_into()
.map_err(|_| PciXError::EccMode1OrMode2)?;
Ecc::Mode1OrMode2 {
control_and_status: From::<u32>::from(control_and_status),
first_address,
second_address,
attribute,
}
}
0b11 => Ecc::Reserved,
_ => unreachable!(),
};
Ok(Self {
ecc,
command: From::<u16>::from(command),
status: From::<u32>::from(status),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Command {
pub uncorrectable_data_error_recovery_enable: bool,
pub enable_relaxed_ordering: bool,
pub maximum_memory_read_byte_count: MaximumByteCount,
pub maximum_outstanding_split_transactions: MaximumOutstandingSplitTransactions,
}
impl From<u16> for Command {
fn from(word: u16) -> Self {
let Lsb((
uncorrectable_data_error_recovery_enable,
enable_relaxed_ordering,
maximum_memory_read_byte_count,
maximum_outstanding_split_transactions,
(),
)) = P5::<u16, 1, 1, 2, 3, 9>(word).into();
Self {
uncorrectable_data_error_recovery_enable,
enable_relaxed_ordering,
maximum_memory_read_byte_count: MaximumByteCount(maximum_memory_read_byte_count),
maximum_outstanding_split_transactions: MaximumOutstandingSplitTransactions(
maximum_outstanding_split_transactions,
),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MaximumByteCount(pub u8);
impl MaximumByteCount {
pub fn value(&self) -> usize {
1 << (self.0 + 9)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MaximumOutstandingSplitTransactions(pub u8);
impl MaximumOutstandingSplitTransactions {
pub fn value(&self) -> u8 {
match self.0 {
0 => 1,
1 => 2,
2 => 3,
3 => 4,
4 => 8,
5 => 12,
6 => 16,
7 => 32,
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Status {
pub function_number: u8,
pub device_number: u8,
pub bus_number: u8,
pub device_64_bit: bool,
pub pci_x_133_capable: bool,
pub slit_completion_discarded: bool,
pub unexpected_split_completion: bool,
pub device_complexity: DeviceComplexity,
pub designed_maximum_memory_read_byte_count: MaximumByteCount,
pub designed_maximum_outstanding_split_transactions: MaximumOutstandingSplitTransactions,
pub designed_maximum_cumulative_read_size: MaximumCumulativeReadSize,
pub received_split_completion_error_message: bool,
pub pci_x_266_capable: bool,
pub pci_x_533_capable: bool,
}
impl From<u32> for Status {
fn from(dword: u32) -> Self {
let Lsb((
function_number,
device_number,
bus_number,
device_64_bit,
pci_x_133_capable,
slit_completion_discarded,
unexpected_split_completion,
device_complexity,
designed_maximum_memory_read_byte_count,
designed_maximum_outstanding_split_transactions,
designed_maximum_cumulative_read_size,
received_split_completion_error_message,
pci_x_266_capable,
pci_x_533_capable,
)) = P14::<u32, 3, 5, 8, 1, 1, 1, 1, 1, 2, 3, 3, 1, 1, 1>(dword).into();
Self {
function_number,
device_number,
bus_number,
device_64_bit,
pci_x_133_capable,
slit_completion_discarded,
unexpected_split_completion,
device_complexity: From::<bool>::from(device_complexity),
designed_maximum_memory_read_byte_count: MaximumByteCount(
designed_maximum_memory_read_byte_count,
),
designed_maximum_outstanding_split_transactions: MaximumOutstandingSplitTransactions(
designed_maximum_outstanding_split_transactions,
),
designed_maximum_cumulative_read_size: MaximumCumulativeReadSize(
designed_maximum_cumulative_read_size,
),
received_split_completion_error_message,
pci_x_266_capable,
pci_x_533_capable,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeviceComplexity {
Simple,
Bridge,
}
impl From<bool> for DeviceComplexity {
fn from(b: bool) -> Self {
if b {
Self::Bridge
} else {
Self::Simple
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MaximumCumulativeReadSize(pub u8);
impl MaximumCumulativeReadSize {
pub fn adqs(&self) -> usize {
1 << (self.0 + 3)
}
pub fn bytes(&self) -> usize {
1 << self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Ecc {
None,
Mode2Only {
control_and_status: EccControlAndStatus,
first_address: u32,
second_address: u32,
attribute: u32,
},
Mode1OrMode2 {
control_and_status: EccControlAndStatus,
first_address: u32,
second_address: u32,
attribute: u32,
},
Reserved,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EccControlAndStatus {
pub select_secondary_ecc_registers: bool,
pub error_present_in_other_ecc_register_bank: bool,
pub additional_correctable_ecc_error: bool,
pub additional_uncorrectable_ecc_error: bool,
pub ecc_error_phase: EccErrorPhase,
pub ecc_error_corrected: bool,
pub syndrome: Syndrome,
pub error_first_command: u8,
pub error_second_command: u8,
pub error_upper_attributes: u8,
pub ecc_control_update_enable: bool,
pub disable_single_bit_error_correction: bool,
pub ecc_mode: bool,
}
impl From<u32> for EccControlAndStatus {
fn from(dword: u32) -> Self {
let Lsb((
select_secondary_ecc_registers,
error_present_in_other_ecc_register_bank,
additional_correctable_ecc_error,
additional_uncorrectable_ecc_error,
ecc_error_phase,
ecc_error_corrected,
syndrome,
error_first_command,
error_second_command,
error_upper_attributes,
ecc_control_update_enable,
(),
disable_single_bit_error_correction,
ecc_mode,
)) = P14::<_, 1, 1, 1, 1, 3, 1, 8, 4, 4, 4, 1, 1, 1, 1>(dword).into();
Self {
select_secondary_ecc_registers,
error_present_in_other_ecc_register_bank,
additional_correctable_ecc_error,
additional_uncorrectable_ecc_error,
ecc_error_phase: From::<u8>::from(ecc_error_phase),
ecc_error_corrected,
syndrome: From::<u8>::from(syndrome),
error_first_command,
error_second_command,
error_upper_attributes,
ecc_control_update_enable,
disable_single_bit_error_correction,
ecc_mode,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EccErrorPhase {
NoError,
First32bits,
Second32bits,
AttributePhase,
Phase32or16bit,
Phase64bit,
Reserved,
}
impl From<u8> for EccErrorPhase {
fn from(byte: u8) -> Self {
match byte {
0 => Self::NoError,
1 => Self::First32bits,
2 => Self::Second32bits,
3 => Self::AttributePhase,
4 => Self::Phase32or16bit,
5 => Self::Phase64bit,
6 | 7 => Self::Reserved,
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Syndrome {
pub e0: bool,
pub e1: bool,
pub e2: bool,
pub e3: bool,
pub e4: bool,
pub e5: bool,
pub e6: bool,
pub e7: bool,
}
impl From<u8> for Syndrome {
fn from(byte: u8) -> Self {
let Lsb((e0, e1, e2, e3, e4, e5, e6, e7)) = P8::<_, 1, 1, 1, 1, 1, 1, 1, 1>(byte).into();
Self {
e0,
e1,
e2,
e3,
e4,
e5,
e6,
e7,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Snafu)]
pub enum PciXBridgeError {
#[snafu(display("statuses and split trx control registers are unreadable"))]
Mandatory,
#[snafu(display("Mode 2 ECC registres unreadable"))]
BridgeEccMode2Only,
#[snafu(display("Mode 1/2 ECC registres unreadable"))]
BridgeEccMode1OrMode2,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PciXBridge {
pub secondary_status: SecondaryStatus,
pub bridge_status: BridgeStatus,
pub upstream_split_transaction_control: SplitTransactionControl,
pub downstream_split_transaction_control: SplitTransactionControl,
pub ecc: Ecc,
}
impl TryFrom<&[u8]> for PciXBridge {
type Error = PciXBridgeError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
let Seq {
head:
Le((
secondary_status,
bridge_status,
up_split_trx_capacity,
up_split_trx_commitment_limit,
down_split_trx_capacity,
down_split_trx_commitment_limit,
)),
tail: slice,
} = P6(slice)
.try_into()
.map_err(|_| PciXBridgeError::Mandatory)?;
let Lsb(((), pci_x_capabilities_list_item_version)) =
P2::<u16, 12, 2>(secondary_status).into();
let ecc = match pci_x_capabilities_list_item_version {
0b00u8 => Ecc::None,
0b01 => {
let Seq {
head: Le((control_and_status, first_address, second_address, attribute)),
..
} = P4(slice)
.try_into()
.map_err(|_| PciXBridgeError::BridgeEccMode2Only)?;
Ecc::Mode2Only {
control_and_status: From::<u32>::from(control_and_status),
first_address,
second_address,
attribute,
}
}
0b10 => {
let Seq {
head: Le((control_and_status, first_address, second_address, attribute)),
..
} = P4(slice)
.try_into()
.map_err(|_| PciXBridgeError::BridgeEccMode1OrMode2)?;
Ecc::Mode1OrMode2 {
control_and_status: From::<u32>::from(control_and_status),
first_address,
second_address,
attribute,
}
}
0b11 => Ecc::Reserved,
_ => unreachable!(),
};
Ok(Self {
secondary_status: From::<u16>::from(secondary_status),
bridge_status: From::<u32>::from(bridge_status),
upstream_split_transaction_control: SplitTransactionControl {
split_transaction_capacity: up_split_trx_capacity,
split_transaction_commitment_limit: up_split_trx_commitment_limit,
},
downstream_split_transaction_control: SplitTransactionControl {
split_transaction_capacity: down_split_trx_capacity,
split_transaction_commitment_limit: down_split_trx_commitment_limit,
},
ecc,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SecondaryStatus {
pub device_64_bit: bool,
pub pci_x_133_capable: bool,
pub slit_completion_discarded: bool,
pub unexpected_split_completion: bool,
pub split_completion_overrun: bool,
pub split_request_delayed: bool,
pub secondary_bus_mode: SecondaryBusMode,
pub error_protection: ErrorProtection,
pub secondary_bus_frequency: SecondaryBusFrequency,
pub pci_x_266_capable: bool,
pub pci_x_533_capable: bool,
}
impl SecondaryStatus {
pub fn secondary_bus_mode_and_frequency(&self) -> u8 {
use ErrorProtection as Ep;
use SecondaryBusFrequency as Sbf;
use SecondaryBusMode as Sbm;
match (
self.secondary_bus_mode,
self.error_protection,
self.secondary_bus_frequency,
) {
(Sbm::Conventional, Ep::Parity, Sbf::NotAvailable) => 0x0,
(Sbm::Mode1, Ep::Parity, Sbf::Freq66MHz) => 0x1,
(Sbm::Mode1, Ep::Parity, Sbf::Freq100MHz) => 0x2,
(Sbm::Mode1, Ep::Parity, Sbf::Freq133MHz) => 0x3,
(Sbm::Mode1, Ep::Ecc, Sbf::Reserved) => 0x4,
(Sbm::Mode1, Ep::Ecc, Sbf::Freq66MHz) => 0x5,
(Sbm::Mode1, Ep::Ecc, Sbf::Freq100MHz) => 0x6,
(Sbm::Mode1, Ep::Ecc, Sbf::Freq133MHz) => 0x7,
(Sbm::PciX266, Ep::Ecc, Sbf::Reserved) => 0x8,
(Sbm::PciX266, Ep::Ecc, Sbf::Freq66MHz) => 0x9,
(Sbm::PciX266, Ep::Ecc, Sbf::Freq100MHz) => 0xA,
(Sbm::PciX266, Ep::Ecc, Sbf::Freq133MHz) => 0xB,
(Sbm::PciX533, Ep::Ecc, Sbf::Reserved) => 0xC,
(Sbm::PciX533, Ep::Ecc, Sbf::Freq66MHz) => 0xD,
(Sbm::PciX533, Ep::Ecc, Sbf::Freq100MHz) => 0xE,
(Sbm::PciX533, Ep::Ecc, Sbf::Freq133MHz) => 0xF,
_ => unreachable!(),
}
}
}
impl From<u16> for SecondaryStatus {
fn from(word: u16) -> Self {
let Lsb((
device_64_bit,
pci_x_133_capable,
slit_completion_discarded,
unexpected_split_completion,
split_completion_overrun,
split_request_delayed,
secondary_bus_mode_and_frequency,
(),
(),
pci_x_266_capable,
pci_x_533_capable,
)) = P11::<u16, 1, 1, 1, 1, 1, 1, 4, 2, 2, 1, 1>(word).into();
Self {
device_64_bit,
pci_x_133_capable,
slit_completion_discarded,
unexpected_split_completion,
split_completion_overrun,
split_request_delayed,
secondary_bus_mode: From::<u8>::from(secondary_bus_mode_and_frequency),
error_protection: From::<u8>::from(secondary_bus_mode_and_frequency),
secondary_bus_frequency: From::<u8>::from(secondary_bus_mode_and_frequency),
pci_x_266_capable,
pci_x_533_capable,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SecondaryBusMode {
Conventional,
Mode1,
PciX266,
PciX533,
}
impl From<u8> for SecondaryBusMode {
fn from(byte: u8) -> Self {
match byte {
0x0 => Self::Conventional,
0x1..=0x7 => Self::Mode1,
0x8..=0xB => Self::PciX266,
0xC..=0xF => Self::PciX533,
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorProtection {
Parity,
Ecc,
}
impl From<u8> for ErrorProtection {
fn from(byte: u8) -> Self {
match byte {
0x0..=0x3 => Self::Parity,
0x4..=0xF => Self::Ecc,
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SecondaryBusFrequency {
NotAvailable,
Freq66MHz,
Freq100MHz,
Freq133MHz,
Reserved,
}
impl From<u8> for SecondaryBusFrequency {
fn from(byte: u8) -> Self {
match byte {
0x0 => Self::NotAvailable,
0x1 | 0x5 | 0x9 | 0xD => Self::Freq66MHz,
0x2 | 0x6 | 0xA | 0xE => Self::Freq100MHz,
0x3 | 0x7 | 0xB | 0xF => Self::Freq133MHz,
0x4 | 0x8 | 0xC => Self::Reserved,
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BridgeStatus {
pub function_number: u8,
pub device_number: u8,
pub bus_number: u8,
pub device_64_bit: bool,
pub pci_x_133_capable: bool,
pub slit_completion_discarded: bool,
pub unexpected_split_completion: bool,
pub split_completion_overrun: bool,
pub split_request_delayed: bool,
pub device_id_messaging_capable: bool,
pub pci_x_266_capable: bool,
pub pci_x_533_capable: bool,
}
impl From<u32> for BridgeStatus {
fn from(dword: u32) -> Self {
let Lsb((
function_number,
device_number,
bus_number,
device_64_bit,
pci_x_133_capable,
slit_completion_discarded,
unexpected_split_completion,
split_completion_overrun,
split_request_delayed,
(),
device_id_messaging_capable,
pci_x_266_capable,
pci_x_533_capable,
)) = P13::<u32, 3, 5, 8, 1, 1, 1, 1, 1, 1, 7, 1, 1, 1>(dword).into();
Self {
function_number,
device_number,
bus_number,
device_64_bit,
pci_x_133_capable,
slit_completion_discarded,
unexpected_split_completion,
split_completion_overrun,
split_request_delayed,
device_id_messaging_capable,
pci_x_266_capable,
pci_x_533_capable,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SplitTransactionControl {
pub split_transaction_capacity: u16,
pub split_transaction_commitment_limit: u16,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
pub fn secondary_bus_mode_and_frequency() {
for word in 0..256 {
let ss: SecondaryStatus = (word << 6).into();
assert_eq!((word & 0b1111) as u8, ss.secondary_bus_mode_and_frequency());
}
}
}