Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion aarch64/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod mailbox;
mod pagealloc;
mod param;
mod pre_mmu;
mod reg;
mod registers;
mod swtch;
mod trap;
Expand All @@ -33,6 +34,7 @@ use param::KZERO;
use port::fdt::DeviceTree;
use port::mem::{PhysAddr, PhysRange, VirtRange};
use port::println;
use reg::midr_el1::MidrEl1;
use vm::{Entry, RootPageTableType, VaMapping};

use crate::kmem::{
Expand Down Expand Up @@ -134,7 +136,7 @@ pub extern "C" fn main9(dtb_va: usize) {
println!();
println!("r9 from the Internet");
println!("DTB found at: {:#x}", dtb_va);
println!("midr_el1: {:?}", registers::MidrEl1::read());
println!("midr_el1: {:?}", MidrEl1::read());

print_stacks();

Expand Down
161 changes: 161 additions & 0 deletions aarch64/src/reg/esr_el1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use core::fmt;

use bitstruct::bitstruct;
use num_enum::TryFromPrimitive;

bitstruct! {
#[derive(Copy, Clone)]
pub struct EsrEl1(pub u64) {
pub iss: u32 = 0..25;
pub il: bool = 25;
pub ec: u8 = 26..32;
pub iss2: u8 = 32..37;
}
}

impl EsrEl1 {
/// Try to convert the error into an ExceptionClass enum, or return the original number
/// as the error.
pub fn exception_class_enum(&self) -> Result<ExceptionClass, u8> {
ExceptionClass::try_from(self.ec()).map_err(|e| e.number)
}
}

impl fmt::Debug for EsrEl1 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EsrEl1")
.field("iss", &format_args!("{:#010x}", self.iss()))
.field("il", &format_args!("{}", self.il()))
.field("ec", &format_args!("{:?}", self.exception_class_enum()))
.field("iss2", &format_args!("{:#04x}", self.iss2()))
.finish()
}
}

/// Exception class maps to ESR_EL1 EC bits[31:26]. We skip aarch32 exceptions.
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[repr(u8)]
pub enum ExceptionClass {
Unknown = 0,
WaitFor = 1,
FloatSimd = 7,
Ls64 = 10,
BranchTargetException = 13,
IllegalExecutionState = 14,
MsrMrsSystem = 24,
Sve = 25,
Tstart = 27,
PointerAuthFailure = 28,
Sme = 29,
GranuleProtectionCheck = 30,
InstructionAbortLowerEl = 32,
InstructionAbortSameEl = 33,
PcAlignmentFault = 34,
DataAbortLowerEl = 36,
DataAbortSameEl = 37,
SpAlignmentFault = 38,
MemoryOperationException = 39,
TrappedFloatingPointException = 44,
SError = 47,
BreakpointLowerEl = 48,
BreakpointSameEl = 49,
SoftwareStepLowerEl = 50,
SoftwareStepSameEl = 51,
WatchpointLowerEl = 52,
WatchpointSameEl = 53,
Brk = 60,
}

bitstruct! {
#[derive(Copy, Clone)]
pub struct EsrEl1IssInstructionAbort(pub u32) {
ifsc: u8 = 0..6;
s1ptw: bool = 7;
ea: bool = 9;
fnv: bool = 10;
set: u8 = 11..13;
}
}

#[allow(dead_code)]
impl EsrEl1IssInstructionAbort {
pub fn from_esr_el1(r: EsrEl1) -> Option<EsrEl1IssInstructionAbort> {
r.exception_class_enum()
.ok()
.filter(|ec| *ec == ExceptionClass::InstructionAbortSameEl)
.map(|_| EsrEl1IssInstructionAbort(r.iss()))
}

pub fn instruction_fault(&self) -> Result<InstructionFaultStatusCode, u8> {
InstructionFaultStatusCode::try_from(self.ifsc()).map_err(|e| e.number)
}
}

#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[repr(u8)]
pub enum InstructionFaultStatusCode {
AddressSizeFaultLevel0 = 0,
AddressSizeFaultLevel1 = 1,
AddressSizeFaultLevel2 = 2,
AddressSizeFaultLevel3 = 3,
TranslationFaultLevel0 = 4,
TranslationFaultLevel1 = 5,
TranslationFaultLevel2 = 6,
TranslationFaultLevel3 = 7,
AccessFlagFaultLevel0 = 8,
AccessFlagFaultLevel1 = 9,
AccessFlagFaultLevel2 = 10,
AccessFlagFaultLevel3 = 11,
PermissionFaultLevel0 = 12,
PermissionFaultLevel1 = 13,
PermissionFaultLevel2 = 14,
PermissionFaultLevel3 = 15,
SyncExtAbortNotOnWalkOrUpdate = 16,
SyncExtAbortOnWalkOrUpdateLevelNeg1 = 19,
SyncExtAbortOnWalkOrUpdateLevel0 = 20,
SyncExtAbortOnWalkOrUpdateLevel1 = 21,
SyncExtAbortOnWalkOrUpdateLevel2 = 22,
SyncExtAbortOnWalkOrUpdateLevel3 = 23,
SyncParityOrEccErrOnMemAccessNotOnWalk = 24,
SyncParityOrEccErrOnMemAccessOnWalkOrUpdateLevelNeg1 = 27,
SyncParityOrEccErrOnMemAccessOnWalkOrUpdateLevel0 = 28,
SyncParityOrEccErrOnMemAccessOnWalkOrUpdateLevel1 = 29,
SyncParityOrEccErrOnMemAccessOnWalkOrUpdateLevel2 = 30,
SyncParityOrEccErrOnMemAccessOnWalkOrUpdateLevel3 = 31,
GranuleProtectFaultOnWalkOrUpdateLevelNeg1 = 35,
GranuleProtectFaultOnWalkOrUpdateLevel0 = 36,
GranuleProtectFaultOnWalkOrUpdateLevel1 = 37,
GranuleProtectFaultOnWalkOrUpdateLevel2 = 38,
GranuleProtectFaultOnWalkOrUpdateLevel3 = 39,
GranuleProtectFaultNotOnWalkOrUpdateLevel = 40,
AddressSizeFaultLevelNeg1 = 41,
TranslationFaultLevelNeg1 = 43,
TlbConflictAbort = 48,
UnsupportedAtomicHardwareUpdateFault = 49,
}

#[cfg(test)]
mod tests {
use super::*;

// This test is useful for making sense of early-stage exceptions. Qemu
// will report an exception of the form below. Copy the ESR value into
// this test to break it down.
//
// Exception return from AArch64 EL2 to AArch64 EL1 PC 0x8006c
// Taking exception 3 [Prefetch Abort] on CPU 0
// ...from EL1 to EL1
// ...with ESR 0x21/0x86000004
// ...with FAR 0x80090
// ...with ELR 0x80090
// ...to EL1 PC 0x200 PSTATE 0x3c5
#[test]
fn test_parse_esr_el1() {
let r = EsrEl1(0x86000004);
assert_eq!(r.exception_class_enum().unwrap(), ExceptionClass::InstructionAbortSameEl);
assert_eq!(
EsrEl1IssInstructionAbort::from_esr_el1(r).unwrap().instruction_fault().unwrap(),
InstructionFaultStatusCode::TranslationFaultLevel0
);
}
}
52 changes: 52 additions & 0 deletions aarch64/src/reg/midr_el1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use core::fmt;

use aarch64_cpu::registers::{MIDR_EL1, Readable};
use bitstruct::bitstruct;
use num_enum::TryFromPrimitive;

bitstruct! {
#[derive(Copy, Clone)]
pub struct MidrEl1(pub u64) {
revision: u8 = 0..4;
partnum: u16 = 4..16;
architecture: u8 = 16..20;
variant: u8 = 20..24;
implementer: u16 = 24..32;
}
}

impl MidrEl1 {
pub fn read() -> Self {
Self(if cfg!(test) { 0 } else { MIDR_EL1.extract().into() })
}

pub fn partnum_enum(&self) -> Result<PartNum, u16> {
PartNum::try_from(self.partnum()).map_err(|e| e.number)
}
}

impl fmt::Debug for MidrEl1 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MidrEl1")
.field("revision", &format_args!("{:#x}", self.revision()))
.field(
"partnum",
&format_args!("{:?}", self.partnum_enum().unwrap_or(PartNum::Unknown)),
)
.field("architecture", &format_args!("{:#x}", self.architecture()))
.field("variant", &format_args!("{:#x}", self.variant()))
.field("implementer", &format_args!("{:#x}", self.implementer()))
.finish()
}
}

/// Known IDs for midr_el1's partnum
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[repr(u16)]
pub enum PartNum {
Unknown = 0,
RaspberryPi1 = 0xb76,
RaspberryPi2 = 0xc07,
RaspberryPi3 = 0xd03,
RaspberryPi4 = 0xd08,
}
2 changes: 2 additions & 0 deletions aarch64/src/reg/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod esr_el1;
pub mod midr_el1;
Loading