use crate::types::{StreamID, ValidationError, IOVA, PASID};
use core::fmt;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FaultSeverity {
Warning,
Error,
Critical,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TranslationStep {
Stage1,
Stage2,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AddressType {
IOVA,
IPA,
PA,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FaultType {
TranslationFault = 0x01,
AddressSizeFault = 0x02,
AccessFlagFault = 0x03,
PermissionFault = 0x04,
ExternalAbort = 0x05,
TLBConflictAbort = 0x06,
UnsupportedAtomicUpdate = 0x07,
AlignmentFault = 0x08,
OutputAddressRangeFault = 0x09,
BadStreamID = 0x0A,
CDFetchFault = 0x0B,
BadCD = 0x0C,
WalkEABT = 0x0D,
BadSTE = 0x0E,
STEFetchFault = 0x0F,
StreamDisabled = 0x10,
BadSubstreamId = 0x11,
SecurityFault = 0x12,
}
impl FaultType {
#[inline]
#[must_use]
pub const fn code(self) -> u8 {
self as u8
}
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Self::TranslationFault => "Translation Fault",
Self::AddressSizeFault => "Address Size Fault",
Self::AccessFlagFault => "Access Flag Fault",
Self::PermissionFault => "Permission Fault",
Self::ExternalAbort => "External Abort",
Self::TLBConflictAbort => "TLB Conflict Abort",
Self::UnsupportedAtomicUpdate => "Unsupported Atomic Update",
Self::AlignmentFault => "Alignment Fault",
Self::OutputAddressRangeFault => "Output Address Range Fault",
Self::BadStreamID => "Bad StreamID",
Self::CDFetchFault => "Context Descriptor Fetch Fault",
Self::BadCD => "Bad Context Descriptor",
Self::WalkEABT => "Page Table Walk External Abort",
Self::BadSTE => "Bad Stream Table Entry",
Self::STEFetchFault => "Stream Table Entry Fetch Fault",
Self::StreamDisabled => "Stream Disabled",
Self::BadSubstreamId => "Bad SubstreamID",
Self::SecurityFault => "Security Fault",
}
}
#[must_use]
pub const fn description(self) -> &'static str {
match self {
Self::TranslationFault => "No valid translation found for the input address",
Self::AddressSizeFault => "Input address exceeds the supported address size",
Self::AccessFlagFault => "Access flag not set in translation descriptor",
Self::PermissionFault => "Requested access type not permitted by page permissions",
Self::ExternalAbort => "External abort during descriptor fetch",
Self::TLBConflictAbort => "Conflicting TLB entries detected",
Self::UnsupportedAtomicUpdate => "Atomic update to translation table not supported",
Self::AlignmentFault => "Address is not properly aligned",
Self::OutputAddressRangeFault => "Output address exceeds addressable range",
Self::BadStreamID => "Invalid or misconfigured StreamID",
Self::CDFetchFault => "Error fetching Context Descriptor",
Self::BadCD => "Invalid or misconfigured Context Descriptor",
Self::WalkEABT => "External abort during page table walk",
Self::BadSTE => "Invalid or misconfigured Stream Table Entry",
Self::STEFetchFault => "Error fetching Stream Table Entry",
Self::StreamDisabled => "Transaction on a disabled/abort stream (STE.Config=disabled)",
Self::BadSubstreamId => "Non-zero PASID (SubstreamID) supplied to a stage-2-only or bypass stream",
Self::SecurityFault => "Transaction violates security policy (crosses security boundary)",
}
}
#[inline]
#[must_use]
pub const fn is_translation_fault(self) -> bool {
matches!(self, Self::TranslationFault)
}
#[inline]
#[must_use]
pub const fn is_permission_fault(self) -> bool {
matches!(self, Self::PermissionFault | Self::AccessFlagFault)
}
#[inline]
#[must_use]
pub const fn is_configuration_fault(self) -> bool {
matches!(
self,
Self::BadStreamID
| Self::CDFetchFault
| Self::BadCD
| Self::BadSTE
| Self::STEFetchFault
| Self::StreamDisabled
| Self::BadSubstreamId
)
}
#[inline]
#[must_use]
pub const fn is_address_fault(self) -> bool {
matches!(
self,
Self::AddressSizeFault | Self::AlignmentFault | Self::OutputAddressRangeFault
)
}
#[inline]
#[must_use]
pub const fn is_external_fault(self) -> bool {
matches!(self, Self::ExternalAbort | Self::WalkEABT)
}
#[must_use]
pub const fn severity(self) -> FaultSeverity {
match self {
Self::BadSTE | Self::BadCD | Self::BadStreamID | Self::StreamDisabled | Self::BadSubstreamId => FaultSeverity::Critical,
Self::TranslationFault
| Self::PermissionFault
| Self::SecurityFault
| Self::ExternalAbort
| Self::AddressSizeFault
| Self::AlignmentFault
| Self::OutputAddressRangeFault
| Self::CDFetchFault
| Self::STEFetchFault
| Self::WalkEABT
| Self::UnsupportedAtomicUpdate => FaultSeverity::Error,
Self::AccessFlagFault | Self::TLBConflictAbort => FaultSeverity::Warning,
}
}
#[must_use]
pub const fn is_recoverable(self) -> bool {
matches!(self, Self::AccessFlagFault | Self::TLBConflictAbort)
}
#[must_use]
pub const fn can_occur_in_stage1(self) -> bool {
!matches!(self, Self::BadSTE | Self::STEFetchFault | Self::BadSubstreamId)
}
#[must_use]
pub const fn can_occur_in_stage2(self) -> bool {
!matches!(
self,
Self::BadStreamID
| Self::BadSTE
| Self::STEFetchFault
| Self::BadCD
| Self::CDFetchFault
| Self::BadSubstreamId
)
}
#[must_use]
pub const fn is_stage_agnostic(self) -> bool {
matches!(
self,
Self::BadStreamID | Self::BadSTE | Self::ExternalAbort | Self::STEFetchFault | Self::BadSubstreamId
)
}
pub const fn from_code(code: u8) -> Result<Self, ValidationError> {
match code {
0x01 => Ok(Self::TranslationFault),
0x02 => Ok(Self::AddressSizeFault),
0x03 => Ok(Self::AccessFlagFault),
0x04 => Ok(Self::PermissionFault),
0x05 => Ok(Self::ExternalAbort),
0x06 => Ok(Self::TLBConflictAbort),
0x07 => Ok(Self::UnsupportedAtomicUpdate),
0x08 => Ok(Self::AlignmentFault),
0x09 => Ok(Self::OutputAddressRangeFault),
0x0A => Ok(Self::BadStreamID),
0x0B => Ok(Self::CDFetchFault),
0x0C => Ok(Self::BadCD),
0x0D => Ok(Self::WalkEABT),
0x0E => Ok(Self::BadSTE),
0x0F => Ok(Self::STEFetchFault),
0x10 => Ok(Self::StreamDisabled),
0x11 => Ok(Self::BadSubstreamId),
0x12 => Ok(Self::SecurityFault),
_ => Err(ValidationError::InvalidFaultType { code }),
}
}
}
impl fmt::Display for FaultType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone)]
pub struct FaultContext {
fault_type: FaultType,
stream_id: StreamID,
pasid: PASID,
address: IOVA,
}
impl FaultContext {
#[must_use]
pub const fn new(fault_type: FaultType, stream_id: StreamID, pasid: PASID, address: IOVA) -> Self {
Self { fault_type, stream_id, pasid, address }
}
#[must_use]
pub const fn fault_type(&self) -> FaultType {
self.fault_type
}
#[must_use]
pub const fn stream_id(&self) -> StreamID {
self.stream_id
}
#[must_use]
pub const fn pasid(&self) -> PASID {
self.pasid
}
#[must_use]
pub const fn address(&self) -> IOVA {
self.address
}
}