use crate::{
debug::{get_object_reference, DebugError, DebugInfo, DebugRegisters, StackFrame},
memory::MemoryInterface,
memory_mapped_bitfield_register, Error, MemoryMappedRegister, RegisterRole, RegisterValue,
};
use bitfield::bitfield;
use super::{
armv6m_armv7m_shared::{Xpsr, EXCEPTION_STACK_REGISTERS},
ExceptionInfo, ExceptionInterface,
};
bitfield! {
struct ExcReturn(u32);
is_exception_flag, _: 31, 24;
use_secure_stack, _:6;
use_default_register_stacking, _:5;
use_standard_stackframe, _: 4;
mode, _: 3;
stack_pointer_selection, _: 2;
exception_secure, _: 0;
}
memory_mapped_bitfield_register! {
pub struct Hfsr(u32);
0xE000ED2C, "HFSR",
impl From;
debug_event, _: 31;
escalation_forced, _: 30;
vector_table_read_fault, _: 1;
}
memory_mapped_bitfield_register! {
pub struct Cfsr(u32);
0xE000ED28, "CFSR",
impl From;
uf_div_by_zero, _: 25;
uf_unaligned_access, _: 24;
uf_coprocessor, _: 19;
uf_integrity_check, _: 18;
uf_invalid_state, _: 17;
uf_undefined_instruction, _: 16;
bf_address_register_valid, _: 15;
bf_fp_lazy_state_preservation, _: 13;
bf_exception_entry, _: 12;
bf_exception_return, _: 11;
bf_imprecise_data_access_error, _: 10;
bf_precise_data_access_error, _: 9;
bf_instruction_prefetch, _: 8;
mm_address_register_valid, _: 7;
mm_fp_lazy_state_preservation, _: 5;
mm_exception_entry, _: 4;
mm_exception_return, _: 3;
mm_data_access_violation, _: 1;
mm_instruction_fetch_violation, _: 0;
}
impl Cfsr {
fn usage_fault_description(&self) -> Result<Option<String>, Error> {
let source = if self.uf_coprocessor() {
"Coprocessor access error"
} else if self.uf_div_by_zero() {
"Division by zero"
} else if self.uf_integrity_check() {
"Integrity check error"
} else if self.uf_invalid_state() {
"Instruction executed with invalid EPSR.T or EPSR.IT field"
} else if self.uf_unaligned_access() {
"Unaligned access"
} else if self.uf_undefined_instruction() {
"Undefined instruction"
} else {
return Ok(None);
};
Ok(Some(format!("UsageFault ({source})")))
}
fn bus_fault_description(
&self,
memory: &mut dyn MemoryInterface,
) -> Result<Option<String>, Error> {
let source = if self.bf_exception_entry() {
"Derived fault on exception entry"
} else if self.bf_exception_return() {
"Derived fault on exception return"
} else if self.bf_fp_lazy_state_preservation() {
"Fault occurred during FP lazy state preservation"
} else if self.bf_imprecise_data_access_error() {
"Imprecise data access error"
} else if self.bf_instruction_prefetch() {
"Instruction prefetch"
} else if self.bf_precise_data_access_error() {
"Precise data access error"
} else {
return Ok(None);
};
Ok(Some(if self.bf_address_register_valid() {
format!(
"BusFault ({source}) at location: {:#010x}",
memory.read_word_32(Bfar::get_mmio_address())?
)
} else {
format!("BusFault ({source})")
}))
}
fn memory_management_fault_description(
&self,
memory: &mut dyn MemoryInterface,
) -> Result<Option<String>, Error> {
let source = if self.mm_data_access_violation() {
"Data access violation"
} else if self.mm_exception_entry() {
"Derived fault on exception entry"
} else if self.mm_exception_return() {
"Derived fault on exception return"
} else if self.mm_fp_lazy_state_preservation() {
"Fault occurred during FP lazy state preservation"
} else if self.mm_instruction_fetch_violation() {
"MPU or Execute Never (XN) default memory map access violation on an instruction fetch"
} else {
return Ok(None);
};
Ok(Some(if self.mm_address_register_valid() {
format!(
"MemManage Fault({source}) at location: {:#010x}",
memory.read_word_32(Mmfar::get_mmio_address())?
)
} else {
format!("MemManage Fault({source})")
}))
}
}
memory_mapped_bitfield_register! {
pub struct Sfsr(u32);
0xE000EDE4, "SFSR",
impl From;
lazy_state_error, _: 7;
secure_fault_address_valid, _: 6;
lazy_state_preservation_error, _: 5;
invalid_transition, _: 4;
attribution_unit_violation, _: 3;
invalid_exception_return, _: 2;
invalid_integrity_signature, _: 1;
invalid_entry_point, _: 0;
}
impl Sfsr {
fn secure_fault_description(
&self,
memory: &mut dyn MemoryInterface,
) -> Result<Option<String>, Error> {
let source = if self.lazy_state_error() {
"Fault occurred during lazy state activation or deactivation"
} else if self.lazy_state_preservation_error() {
"Fault occurred during FP lazy state preservation"
} else if self.invalid_transition() {
"Invalid transition error"
} else if self.attribution_unit_violation() {
"Attribution unit violation error"
} else if self.invalid_exception_return() {
"Invalid exception return error"
} else if self.invalid_integrity_signature() {
"Invalid integrity signature error"
} else if self.invalid_entry_point() {
"Invalid entry point error"
} else {
return Ok(None);
};
Ok(Some(if self.secure_fault_address_valid() {
format!(
"SecureFault ({source}) at location: {:#010x}",
memory.read_word_32(Sfar::get_mmio_address())?
)
} else {
format!("SecureFault ({source})")
}))
}
}
memory_mapped_bitfield_register! {
pub struct Mmfar(u32);
0xE000ED34, "MMFAR",
impl From;
}
memory_mapped_bitfield_register! {
pub struct Bfar(u32);
0xE000ED38, "BFAR",
impl From;
}
memory_mapped_bitfield_register! {
pub struct Sfar(u32);
0xE000EDE8, "SFAR",
impl From;
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum ExceptionReason {
ThreadMode,
Reset,
NonMaskableInterrupt,
HardFault,
MemoryManagementFault,
BusFault,
UsageFault,
SecureFault,
SVCall,
DebugMonitor,
PendSV,
SysTick,
ExternalInterrupt(u32),
Reserved,
}
impl From<u32> for ExceptionReason {
fn from(exception: u32) -> Self {
match exception {
0 => ExceptionReason::ThreadMode,
1 => ExceptionReason::Reset,
2 => ExceptionReason::NonMaskableInterrupt,
3 => ExceptionReason::HardFault,
4 => ExceptionReason::MemoryManagementFault,
5 => ExceptionReason::BusFault,
6 => ExceptionReason::UsageFault,
7 => ExceptionReason::SecureFault,
8..=10 | 13 => ExceptionReason::Reserved,
11 => ExceptionReason::SVCall,
12 => ExceptionReason::DebugMonitor,
14 => ExceptionReason::PendSV,
15 => ExceptionReason::SysTick,
16.. => ExceptionReason::ExternalInterrupt(exception - 16),
}
}
}
impl ExceptionReason {
fn expanded_description(&self, memory: &mut dyn MemoryInterface) -> Result<String, DebugError> {
match self {
ExceptionReason::ThreadMode => Ok("<No active exception>".to_string()),
ExceptionReason::Reset => Ok("Reset".to_string()),
ExceptionReason::NonMaskableInterrupt => Ok("NMI".to_string()),
ExceptionReason::HardFault => {
let hfsr = Hfsr(memory.read_word_32(Hfsr::get_mmio_address())?);
let description = if hfsr.debug_event() {
"Synchronous debug fault.".to_string()
} else if hfsr.escalation_forced() {
let description = "Escalated ";
let cfsr = Cfsr(memory.read_word_32(Cfsr::get_mmio_address())?);
if let Some(source) = cfsr.usage_fault_description()? {
format!("{description}{source}")
} else if let Some(source) = cfsr.bus_fault_description(memory)? {
format!("{description}{source}")
} else if let Some(source) = cfsr.memory_management_fault_description(memory)? {
format!("{description}{source}")
} else {
format!("{description}from an unknown source")
}
} else if hfsr.vector_table_read_fault() {
"Vector table read fault".to_string()
} else {
"Undeterminable".to_string()
};
Ok(format!("HardFault <Cause: {description}>"))
}
ExceptionReason::MemoryManagementFault => {
if let Some(source) = Cfsr(memory.read_word_32(Cfsr::get_mmio_address())?)
.usage_fault_description()?
{
Ok(source)
} else {
Ok("MemManage Fault <Cause: Unknown>".to_string())
}
}
ExceptionReason::BusFault => {
if let Some(source) = Cfsr(memory.read_word_32(Cfsr::get_mmio_address())?)
.bus_fault_description(memory)?
{
Ok(source)
} else {
Ok("BusFault <Cause: Unknown>".to_string())
}
}
ExceptionReason::UsageFault => {
if let Some(source) = Cfsr(memory.read_word_32(Cfsr::get_mmio_address())?)
.usage_fault_description()?
{
Ok(source)
} else {
Ok("UsageFault <Cause: Unknown>".to_string())
}
}
ExceptionReason::SecureFault => {
if let Some(source) = Sfsr(memory.read_word_32(Sfsr::get_mmio_address())?)
.secure_fault_description(memory)?
{
Ok(source)
} else {
Ok("SecureFault <Cause: Unknown>".to_string())
}
}
ExceptionReason::SVCall => Ok("SVC".to_string()),
ExceptionReason::DebugMonitor => Ok("DebugMonitor".to_string()),
ExceptionReason::PendSV => Ok("PendSV".to_string()),
ExceptionReason::SysTick => Ok("SysTick".to_string()),
ExceptionReason::ExternalInterrupt(exti) => Ok(format!("External interrupt #{exti}")),
ExceptionReason::Reserved => {
Ok("<Reserved by the ISA, and not usable by software>".to_string())
}
}
}
}
pub struct ArmV8MExceptionHandler;
impl ExceptionInterface for ArmV8MExceptionHandler {
fn calling_frame_registers(
&self,
memory_interface: &mut dyn MemoryInterface,
stackframe_registers: &DebugRegisters,
_raw_exception: u32,
) -> Result<DebugRegisters, DebugError> {
let mut calling_stack_registers = vec![0u32; EXCEPTION_STACK_REGISTERS.len()];
let stack_frame_return_address: u32 = get_stack_frame_return_address(stackframe_registers)?;
let exc_return = ExcReturn(stack_frame_return_address);
let sp_value = if exc_return.is_exception_flag() == 0xFF {
let stack_info = (
exc_return.use_secure_stack(),
exc_return.stack_pointer_selection(),
);
let sp_reg_id = match stack_info {
(false, false) => 0b00011000, (false, true) => 0b00011001, (true, false) => 0b00011010, (true, true) => 0b00011011, };
stackframe_registers
.get_register(sp_reg_id.into())
.ok_or_else(|| {
Error::Register(
"No Stack Pointer register. Please report this as a bug.".to_string(),
)
})?
.value
.ok_or_else(|| {
Error::Register(
"No value for Stack Pointer register. Please report this as a bug."
.to_string(),
)
})?
.try_into()?
} else {
stackframe_registers
.get_register_value_by_role(&crate::core::RegisterRole::StackPointer)?
};
memory_interface.read_32(sp_value, &mut calling_stack_registers)?;
let mut calling_frame_registers = stackframe_registers.clone();
for (i, register_role) in EXCEPTION_STACK_REGISTERS.iter().enumerate() {
calling_frame_registers
.get_register_mut_by_role(register_role)?
.value = Some(RegisterValue::U32(calling_stack_registers[i]));
}
Ok(calling_frame_registers)
}
fn raw_exception(&self, stackframe_registers: &DebugRegisters) -> Result<u32, DebugError> {
let exception_number = Xpsr(
stackframe_registers
.get_register_value_by_role(&crate::core::RegisterRole::ProcessorStatus)?
as u32,
)
.exception_number();
Ok(exception_number)
}
fn exception_description(
&self,
raw_exception: u32,
memory_interface: &mut dyn MemoryInterface,
) -> Result<String, DebugError> {
ExceptionReason::from(raw_exception).expanded_description(memory_interface)
}
fn exception_details(
&self,
memory_interface: &mut dyn MemoryInterface,
stackframe_registers: &DebugRegisters,
_debug_info: &DebugInfo,
) -> Result<Option<ExceptionInfo>, DebugError> {
let stack_frame_return_address: u32 = get_stack_frame_return_address(stackframe_registers)?;
if ExcReturn(stack_frame_return_address).is_exception_flag() == 0xFF {
let raw_exception = self.raw_exception(stackframe_registers)?;
let description = self.exception_description(raw_exception, memory_interface)?;
let registers = self.calling_frame_registers(
memory_interface,
stackframe_registers,
raw_exception,
)?;
let exception_frame_pc =
registers.get_register_value_by_role(&RegisterRole::ProgramCounter)?;
let handler_frame = StackFrame {
id: get_object_reference(),
function_name: description.clone(),
source_location: None,
registers,
pc: RegisterValue::U32(exception_frame_pc as u32),
frame_base: None,
is_inlined: false,
local_variables: None,
canonical_frame_address: None,
};
Ok(Some(ExceptionInfo {
raw_exception,
description,
handler_frame,
}))
} else {
Ok(None)
}
}
}
fn get_stack_frame_return_address(stackframe_registers: &DebugRegisters) -> Result<u32, Error> {
let return_address: u32 = stackframe_registers
.get_return_address()
.ok_or_else(|| {
Error::Register("No Return Address register. Please report this as a bug.".to_string())
})?
.value
.ok_or_else(|| {
Error::Register(
"No value for Return Address register. Please report this as a bug.".to_string(),
)
})?
.try_into()?;
Ok(return_address)
}