use heterob::{bit_numbering::Lsb, endianness::Le, Seq, P10, P11, P2, P21, P4, P7, P9};
use snafu::Snafu;
#[derive(Snafu, Debug, Clone, PartialEq, Eq)]
pub enum AdvancedErrorReportingError {
#[snafu(display("can't read common registres"))]
Common,
#[snafu(display("can't read TLP Prefix Log"))]
TlpPrefixLog,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AdvancedErrorReporting {
pub uncorrectable_error_status: UncorrectableError,
pub uncorrectable_error_mask: UncorrectableError,
pub uncorrectable_error_severity: UncorrectableError,
pub correctable_error_status: CorrectableError,
pub correctable_error_mask: CorrectableError,
pub advanced_error_capabilities_and_control: AdvancedErrorCapabilitiesAndControl,
pub header_log: HeaderLog,
pub root_error_command: Option<RootErrorCommand>,
pub root_error_status: Option<RootErrorStatus>,
pub error_source_identification: Option<ErrorSourceIdentification>,
pub tlp_prefix_log: Option<TlpPrefixLog>,
}
impl AdvancedErrorReporting {
pub const COMMON_SIZE: usize = 0x2c - 0x04;
pub const RP_AND_RCEC_SIZE: usize = 0x38 - 0x04;
pub const FULL_SIZE: usize = 0x48 - 0x04;
pub const HEADER_LOG_SIZE: usize = 4 * 4;
pub const TLP_PREFIX_LOG_SIZE: usize = 4 * 4;
}
impl TryFrom<&[u8]> for AdvancedErrorReporting {
type Error = AdvancedErrorReportingError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
let Seq {
head:
Le((
uncorrectable_error_status,
uncorrectable_error_mask,
uncorrectable_error_severity,
correctable_error_status,
correctable_error_mask,
advanced_error_capabilities_and_control,
header_log,
)),
tail: slice,
} = P7(slice)
.try_into()
.map_err(|_| AdvancedErrorReportingError::Common)?;
let (root_error_command, root_error_status, error_source_identification) =
if let Ok(Seq {
head:
Le((
root_error_command,
root_error_status,
err_cor_source_identification,
err_fatal_or_nonfatal_source_identification,
)),
..
}) = P4(slice).try_into()
{
(
Some(From::<u32>::from(root_error_command)),
Some(From::<u32>::from(root_error_status)),
Some(ErrorSourceIdentification {
err_cor_source_identification,
err_fatal_or_nonfatal_source_identification,
}),
)
} else {
(None, None, None)
};
let advanced_error_capabilities_and_control: AdvancedErrorCapabilitiesAndControl =
From::<u32>::from(advanced_error_capabilities_and_control);
let Le(header_log) = From::<[u8; Self::HEADER_LOG_SIZE]>::from(header_log);
let tlp_prefix_log = if advanced_error_capabilities_and_control.tlp_prefix_log_present {
let Seq {
head: Le((_rp_and_rcec, tlp_prefix_log)),
..
} = P2(slice)
.try_into()
.map_err(|_| AdvancedErrorReportingError::TlpPrefixLog)?;
let _: [u8; Self::RP_AND_RCEC_SIZE] = _rp_and_rcec;
let Le(tlp_prefix_log) = From::<[u8; Self::TLP_PREFIX_LOG_SIZE]>::from(tlp_prefix_log);
Some(TlpPrefixLog(tlp_prefix_log))
} else {
None
};
Ok(Self {
uncorrectable_error_status: From::<u32>::from(uncorrectable_error_status),
uncorrectable_error_mask: From::<u32>::from(uncorrectable_error_mask),
uncorrectable_error_severity: From::<u32>::from(uncorrectable_error_severity),
correctable_error_status: From::<u32>::from(correctable_error_status),
correctable_error_mask: From::<u32>::from(correctable_error_mask),
advanced_error_capabilities_and_control,
header_log: HeaderLog(header_log),
root_error_command,
root_error_status,
error_source_identification,
tlp_prefix_log,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UncorrectableError {
pub link_training_error: bool,
pub data_link_protocol_error_status: bool,
pub surprise_down_error_status: bool,
pub poisoned_tlp_received_status: bool,
pub flow_control_protocol_error_status: bool,
pub completion_timeout_status: bool,
pub completer_abort_status: bool,
pub unexpected_completion_status: bool,
pub receiver_overflow_status: bool,
pub malformed_tlp_status: bool,
pub ecrc_error_status: bool,
pub unsupported_request_error_status: bool,
pub acs_violation_status: bool,
pub uncorrectable_internal_error_status: bool,
pub mc_blocked_tlp_status: bool,
pub atomicop_egress_blocked_status: bool,
pub tlp_prefix_blocked_error_status: bool,
pub poisoned_tlp_egress_blocked_status: bool,
}
impl From<u32> for UncorrectableError {
fn from(dword: u32) -> Self {
let Lsb((
link_training_error,
(),
data_link_protocol_error_status,
surprise_down_error_status,
(),
poisoned_tlp_received_status,
flow_control_protocol_error_status,
completion_timeout_status,
completer_abort_status,
unexpected_completion_status,
receiver_overflow_status,
malformed_tlp_status,
ecrc_error_status,
unsupported_request_error_status,
acs_violation_status,
uncorrectable_internal_error_status,
mc_blocked_tlp_status,
atomicop_egress_blocked_status,
tlp_prefix_blocked_error_status,
poisoned_tlp_egress_blocked_status,
(),
)) = P21::<_, 1, 3, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5>(dword).into();
Self {
link_training_error,
data_link_protocol_error_status,
surprise_down_error_status,
poisoned_tlp_received_status,
flow_control_protocol_error_status,
completion_timeout_status,
completer_abort_status,
unexpected_completion_status,
receiver_overflow_status,
malformed_tlp_status,
ecrc_error_status,
unsupported_request_error_status,
acs_violation_status,
uncorrectable_internal_error_status,
mc_blocked_tlp_status,
atomicop_egress_blocked_status,
tlp_prefix_blocked_error_status,
poisoned_tlp_egress_blocked_status,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CorrectableError {
pub receiver_error_status: bool,
pub bad_tlp_status: bool,
pub bad_dllp_status: bool,
pub replay_num_rollover_status: bool,
pub replay_timer_timeout_status: bool,
pub advisory_non_fatal_error_status: bool,
pub corrected_internal_error_status: bool,
pub header_log_overflow_status: bool,
}
impl From<u32> for CorrectableError {
fn from(dword: u32) -> Self {
let Lsb((
receiver_error_status,
(),
bad_tlp_status,
bad_dllp_status,
replay_num_rollover_status,
(),
replay_timer_timeout_status,
advisory_non_fatal_error_status,
corrected_internal_error_status,
header_log_overflow_status,
(),
)) = P11::<_, 1, 5, 1, 1, 1, 3, 1, 1, 1, 1, 16>(dword).into();
Self {
receiver_error_status,
bad_tlp_status,
bad_dllp_status,
replay_num_rollover_status,
replay_timer_timeout_status,
advisory_non_fatal_error_status,
corrected_internal_error_status,
header_log_overflow_status,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AdvancedErrorCapabilitiesAndControl {
pub first_error_pointer: u8,
pub ecrc_generation_capable: bool,
pub ecrc_generation_enable: bool,
pub ecrc_check_capable: bool,
pub ecrc_check_enable: bool,
pub multiple_header_recording_capable: bool,
pub multiple_header_recording_enable: bool,
pub tlp_prefix_log_present: bool,
pub completion_timeout_prefix_or_header_log_capable: bool,
}
impl From<u32> for AdvancedErrorCapabilitiesAndControl {
fn from(dword: u32) -> Self {
let Lsb((
first_error_pointer,
ecrc_generation_capable,
ecrc_generation_enable,
ecrc_check_capable,
ecrc_check_enable,
multiple_header_recording_capable,
multiple_header_recording_enable,
tlp_prefix_log_present,
completion_timeout_prefix_or_header_log_capable,
(),
)) = P10::<_, 5, 1, 1, 1, 1, 1, 1, 1, 1, 10>(dword).into();
Self {
first_error_pointer,
ecrc_generation_capable,
ecrc_generation_enable,
ecrc_check_capable,
ecrc_check_enable,
multiple_header_recording_capable,
multiple_header_recording_enable,
tlp_prefix_log_present,
completion_timeout_prefix_or_header_log_capable,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HeaderLog(pub [u32; 4]);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RootErrorCommand {
pub correctable_error_reporting_enable: bool,
pub non_fatal_error_reporting_enable: bool,
pub fatal_error_reporting_enable: bool,
}
impl From<u32> for RootErrorCommand {
fn from(dword: u32) -> Self {
let Lsb((
correctable_error_reporting_enable,
non_fatal_error_reporting_enable,
fatal_error_reporting_enable,
(),
)) = P4::<_, 1, 1, 1, 29>(dword).into();
Self {
correctable_error_reporting_enable,
non_fatal_error_reporting_enable,
fatal_error_reporting_enable,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RootErrorStatus {
pub err_cor_received: bool,
pub multiple_err_cor_received: bool,
pub err_fatal_or_nonfatal_received: bool,
pub multiple_err_fatal_or_nonfatal_received: bool,
pub first_uncorrectable_fatal: bool,
pub non_fatal_error_messages_received: bool,
pub fatal_error_messages_received: bool,
pub advanced_error_interrupt_message_number: u8,
}
impl From<u32> for RootErrorStatus {
fn from(dword: u32) -> Self {
let Lsb((
err_cor_received,
multiple_err_cor_received,
err_fatal_or_nonfatal_received,
multiple_err_fatal_or_nonfatal_received,
first_uncorrectable_fatal,
non_fatal_error_messages_received,
fatal_error_messages_received,
(),
advanced_error_interrupt_message_number,
)) = P9::<_, 1, 1, 1, 1, 1, 1, 1, 20, 5>(dword).into();
Self {
err_cor_received,
multiple_err_cor_received,
err_fatal_or_nonfatal_received,
multiple_err_fatal_or_nonfatal_received,
first_uncorrectable_fatal,
non_fatal_error_messages_received,
fatal_error_messages_received,
advanced_error_interrupt_message_number,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ErrorSourceIdentification {
pub err_cor_source_identification: u16,
pub err_fatal_or_nonfatal_source_identification: u16,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TlpPrefixLog(pub [u32; 4]);
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn uncorrectable_error_status() {
let data = [0x11, 0x00, 0x06, 0x00];
let sample = UncorrectableError {
link_training_error: true,
data_link_protocol_error_status: true,
surprise_down_error_status: false,
poisoned_tlp_received_status: false,
flow_control_protocol_error_status: false,
completion_timeout_status: false,
completer_abort_status: false,
unexpected_completion_status: false,
receiver_overflow_status: true,
malformed_tlp_status: true,
ecrc_error_status: false,
unsupported_request_error_status: false,
acs_violation_status: false,
uncorrectable_internal_error_status: false,
mc_blocked_tlp_status: false,
atomicop_egress_blocked_status: false,
tlp_prefix_blocked_error_status: false,
poisoned_tlp_egress_blocked_status: false,
};
assert_eq!(sample, u32::from_le_bytes(data).into());
}
#[test]
fn correctable_error_status() {
let data = [0x00, 0x20, 0x00, 0x00];
let sample = CorrectableError {
receiver_error_status: false,
bad_tlp_status: false,
bad_dllp_status: false,
replay_num_rollover_status: false,
replay_timer_timeout_status: false,
advisory_non_fatal_error_status: true,
corrected_internal_error_status: false,
header_log_overflow_status: false,
};
assert_eq!(sample, u32::from_le_bytes(data).into());
}
#[test]
fn advanced_error_capabilities_and_control() {
let data = [0x00, 0x00, 0x00, 0x00];
let sample = AdvancedErrorCapabilitiesAndControl {
first_error_pointer: 0,
ecrc_generation_capable: false,
ecrc_generation_enable: false,
ecrc_check_capable: false,
ecrc_check_enable: false,
multiple_header_recording_capable: false,
multiple_header_recording_enable: false,
tlp_prefix_log_present: false,
completion_timeout_prefix_or_header_log_capable: false,
};
assert_eq!(sample, u32::from_le_bytes(data).into());
}
#[test]
fn root_error_command() {
let data = [0x07, 0x00, 0x00, 0x00];
let sample = RootErrorCommand {
correctable_error_reporting_enable: true,
non_fatal_error_reporting_enable: true,
fatal_error_reporting_enable: true,
};
assert_eq!(sample, u32::from_le_bytes(data).into());
}
#[test]
fn root_error_status() {
let data = [0x00, 0x00, 0x00, 0x00];
let sample = RootErrorStatus {
err_cor_received: false,
multiple_err_cor_received: false,
err_fatal_or_nonfatal_received: false,
multiple_err_fatal_or_nonfatal_received: false,
first_uncorrectable_fatal: false,
non_fatal_error_messages_received: false,
fatal_error_messages_received: false,
advanced_error_interrupt_message_number: 0,
};
assert_eq!(sample, u32::from_le_bytes(data).into());
}
}