use crate::{
debug::{DebugError, DebugInfo, DebugRegisters},
Error, MemoryInterface,
};
use super::{armv6m_armv7m_shared, ExceptionInfo, ExceptionInterface};
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum ExceptionReason {
ThreadMode,
Reset,
NonMaskableInterrupt,
HardFault,
SVCall,
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..=10 | 12 | 13 => ExceptionReason::Reserved,
11 => ExceptionReason::SVCall,
14 => ExceptionReason::PendSV,
15 => ExceptionReason::SysTick,
16.. => ExceptionReason::ExternalInterrupt(exception - 16),
}
}
}
impl ExceptionReason {
pub(crate) fn expanded_description(&self) -> String {
match self {
ExceptionReason::ThreadMode => "<No active exception>".to_string(),
ExceptionReason::Reset => "Reset".to_string(),
ExceptionReason::NonMaskableInterrupt => "NMI".to_string(),
ExceptionReason::HardFault => "HardFault".to_string(),
ExceptionReason::SVCall => "SVC".to_string(),
ExceptionReason::PendSV => "PendSV".to_string(),
ExceptionReason::SysTick => "SysTick".to_string(),
ExceptionReason::ExternalInterrupt(exti) => format!("External interrupt #{exti}"),
ExceptionReason::Reserved => {
"<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> {
Ok(match self {
ExceptionReason::HardFault => {
true
}
_ => false,
})
}
}
pub struct ArmV6MExceptionHandler;
impl ExceptionInterface for ArmV6MExceptionHandler {
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> {
Ok(ExceptionReason::from(raw_exception).expanded_description())
}
}
#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
use super::ArmV6MExceptionHandler;
use crate::{
architecture::arm::core::registers::cortex_m::{RA, XPSR},
debug::exception_handling::ExceptionInterface,
debug::{DebugRegister, DebugRegisters},
test::MockMemory,
RegisterValue,
};
#[test]
fn exception_handler_reset_exception() {
let handler = ArmV6MExceptionHandler {};
let mut memory = MockMemory::new();
let mut registers = DebugRegisters(vec![]);
registers.0.push(DebugRegister {
dwarf_id: None,
core_register: &XPSR,
value: Some(RegisterValue::U32(0)),
});
registers.0.push(DebugRegister {
dwarf_id: None,
core_register: &RA,
value: Some(RegisterValue::U32(0xFFFF_FFFF)),
});
let raw_exception = handler.raw_exception(®isters).unwrap();
let description = handler
.exception_description(raw_exception, &mut memory)
.unwrap();
assert_eq!(description, "Reset")
}
#[test]
fn exception_handler_no_exception_description() {
let handler = ArmV6MExceptionHandler {};
let mut memory = MockMemory::new();
let mut registers = DebugRegisters(vec![]);
registers.0.push(DebugRegister {
dwarf_id: None,
core_register: &XPSR,
value: Some(RegisterValue::U32(0)),
});
registers.0.push(DebugRegister {
dwarf_id: None,
core_register: &RA,
value: Some(RegisterValue::U32(0)),
});
let raw_exception = handler.raw_exception(®isters).unwrap();
let description = handler
.exception_description(raw_exception, &mut memory)
.unwrap();
assert_eq!(description, "<No active exception>")
}
}