use super::{armv6m_armv7m_shared, ExceptionInfo, ExceptionInterface};
use crate::{
debug::{DebugError, DebugInfo, DebugRegisters},
memory_mapped_bitfield_register, Error, MemoryInterface, MemoryMappedRegister,
};
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;
usage_fault, _: 25,16;
uf_div_by_zero, _: 25;
uf_unaligned_access, _: 24;
uf_coprocessor, _: 19;
uf_integrity_check, _: 18;
uf_invalid_state, _: 17;
uf_undefined_instruction, _: 16;
bus_fault, _: 15,8;
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;
mem_manage_fault, _: 7,0;
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 <Cause: {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 <Cause: {source} at location: {:#010x}>",
memory.read_word_32(Bfar::get_mmio_address())?
)
} else {
format!("BusFault <Cause: {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 <Cause: {source} at location: {:#010x}>",
memory.read_word_32(Mmfar::get_mmio_address())?
)
} else {
format!("MemManage Fault <Cause: {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;
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum ExceptionReason {
ThreadMode,
Reset,
NonMaskableInterrupt,
HardFault,
MemoryManagementFault,
BusFault,
UsageFault,
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..=10 | 13 => ExceptionReason::Reserved,
11 => ExceptionReason::SVCall,
12 => ExceptionReason::DebugMonitor,
14 => ExceptionReason::PendSV,
15 => ExceptionReason::SysTick,
16.. => ExceptionReason::ExternalInterrupt(exception - 16),
}
}
}
impl ExceptionReason {
pub(crate) fn expanded_description(
&self,
memory: &mut dyn MemoryInterface,
) -> Result<String, Error> {
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() {
"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::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(crate) fn is_precise_fault(&self, memory: &mut dyn MemoryInterface) -> Result<bool, Error> {
let is_precise = match self {
ExceptionReason::HardFault
| ExceptionReason::BusFault
| ExceptionReason::MemoryManagementFault
| ExceptionReason::UsageFault => {
let cfsr = Cfsr(memory.read_word_32(Cfsr::get_mmio_address())?);
cfsr.bf_precise_data_access_error()
|| cfsr.bf_instruction_prefetch()
|| cfsr.mem_manage_fault() > 0
|| cfsr.usage_fault() > 0
}
ExceptionReason::DebugMonitor => {
true
}
_ => false,
};
Ok(is_precise)
}
}
pub struct ArmV7MExceptionHandler;
impl ExceptionInterface for ArmV7MExceptionHandler {
fn exception_details(
&self,
memory_interface: &mut dyn MemoryInterface,
stackframe_registers: &DebugRegisters,
debug_info: &DebugInfo,
) -> Result<Option<ExceptionInfo>, DebugError> {
armv6m_armv7m_shared::exception_details(
self,
memory_interface,
stackframe_registers,
debug_info,
)
}
fn calling_frame_registers(
&self,
memory_interface: &mut dyn MemoryInterface,
stackframe_registers: &crate::debug::DebugRegisters,
raw_exception: u32,
) -> Result<crate::debug::DebugRegisters, DebugError> {
let mut updated_registers = stackframe_registers.clone();
let exception_reason = ExceptionReason::from(raw_exception);
if exception_reason.is_precise_fault(memory_interface)? {
let exception_context_address =
updated_registers.get_register_mut_by_role(&crate::RegisterRole::StackPointer)?;
if let Some(sp_value) = exception_context_address.value.as_mut() {
sp_value.increment_address(0x8)?;
}
}
updated_registers = armv6m_armv7m_shared::calling_frame_registers(
memory_interface,
&updated_registers,
raw_exception,
)?;
Ok(updated_registers)
}
fn raw_exception(
&self,
stackframe_registers: &crate::debug::DebugRegisters,
) -> Result<u32, DebugError> {
let value = armv6m_armv7m_shared::raw_exception(stackframe_registers)?;
Ok(value)
}
fn exception_description(
&self,
raw_exception: u32,
memory_interface: &mut dyn MemoryInterface,
) -> Result<String, DebugError> {
let description =
ExceptionReason::from(raw_exception).expanded_description(memory_interface)?;
Ok(description)
}
}