probe_rs_debug/exception_handling.rs
1//! This module (and its children) contains the implementation of the [`ExceptionInterface`] for the various ARM core
2//! variants.
3
4use std::ops::ControlFlow;
5
6use probe_rs_target::CoreType;
7
8use crate::unwind_pc_without_debuginfo;
9use probe_rs::{InstructionSet, MemoryInterface};
10
11use super::{DebugError, DebugInfo, DebugRegisters, StackFrame};
12
13pub(crate) mod armv6m;
14/// Where applicable, this defines shared logic for implementing exception handling across the various ARMv6-m and
15/// ARMv7-m [`crate::CoreType`]'s.
16pub(crate) mod armv6m_armv7m_shared;
17// NOTE: There is also a [`CoreType::Armv7em`] variant, but it is not currently used/implemented in probe-rs.
18pub(crate) mod armv7m;
19pub(crate) mod armv8m;
20pub(crate) mod riscv;
21pub(crate) mod xtensa;
22
23/// Creates a new exception interface for the [`CoreType`] at hand.
24pub fn exception_handler_for_core(core_type: CoreType) -> Box<dyn ExceptionInterface> {
25 use self::{armv6m, armv7m, armv8m};
26 match core_type {
27 CoreType::Armv6m => Box::new(armv6m::ArmV6MExceptionHandler),
28 CoreType::Armv7m | CoreType::Armv7em => Box::new(armv7m::ArmV7MExceptionHandler),
29 CoreType::Armv8m => Box::new(armv8m::ArmV8MExceptionHandler),
30 CoreType::Xtensa => Box::new(xtensa::XtensaExceptionHandler),
31 CoreType::Riscv => Box::new(riscv::RiscvExceptionHandler),
32 CoreType::Armv7a | CoreType::Armv8a => Box::new(UnimplementedExceptionHandler),
33 }
34}
35
36/// Placeholder for exception handling for cores where handling exceptions is not yet supported.
37pub struct UnimplementedExceptionHandler;
38
39impl ExceptionInterface for UnimplementedExceptionHandler {}
40
41/// A struct containing key information about an exception.
42/// The exception details are architecture specific, and the abstraction is handled in the
43/// architecture specific implementations of [`ExceptionInterface`].
44#[derive(PartialEq)]
45pub struct ExceptionInfo {
46 /// The exception number.
47 /// This is architecture specific and can be used to decode the architecture specific exception reason.
48 pub raw_exception: u32,
49 /// A human readable explanation for the exception.
50 pub description: String,
51 /// A populated [`StackFrame`] to represent the stack data in the exception handler.
52 pub handler_frame: StackFrame,
53}
54
55/// A generic interface to identify and decode exceptions during unwind processing.
56pub trait ExceptionInterface {
57 /// Using the `stackframe_registers` for a "called frame",
58 /// determine if the given frame was called from an exception handler,
59 /// and resolve the relevant details about the exception, including the reason for the exception,
60 /// and the stackframe registers for the frame that triggered the exception.
61 /// A return value of `Ok(None)` indicates that the given frame was called from within the current thread,
62 /// and the unwind should continue normally.
63 fn exception_details(
64 &self,
65 _memory: &mut dyn MemoryInterface,
66 _stackframe_registers: &DebugRegisters,
67 _debug_info: &DebugInfo,
68 ) -> Result<Option<ExceptionInfo>, DebugError> {
69 // For architectures where the exception handling has not been implemented in probe-rs,
70 // this will result in maintaining the current `unwind` behavior, i.e. unwinding will include up
71 // to the first frame that was called from an exception handler.
72 Ok(None)
73 }
74
75 /// Using the `stackframe_registers` for a "called frame", retrieve updated register values for the "calling frame".
76 fn calling_frame_registers(
77 &self,
78 _memory: &mut dyn MemoryInterface,
79 _stackframe_registers: &crate::DebugRegisters,
80 _raw_exception: u32,
81 ) -> Result<crate::DebugRegisters, DebugError> {
82 Err(DebugError::NotImplemented("calling frame registers"))
83 }
84
85 /// Retrieve the architecture specific exception number.
86 fn raw_exception(
87 &self,
88 _stackframe_registers: &crate::DebugRegisters,
89 ) -> Result<u32, DebugError> {
90 Err(DebugError::NotImplemented("raw exception"))
91 }
92
93 /// Convert the architecture specific exception number into a human readable description.
94 /// Where possible, the implementation may read additional registers from the core, to provide additional context.
95 fn exception_description(
96 &self,
97 _raw_exception: u32,
98 _memory: &mut dyn MemoryInterface,
99 ) -> Result<String, DebugError> {
100 Err(DebugError::NotImplemented("exception description"))
101 }
102
103 /// Unwind the stack without debug info.
104 ///
105 /// This method can be implemented to provide a stack trace using frame pointers, for example.
106 fn unwind_without_debuginfo(
107 &self,
108 unwind_registers: &mut DebugRegisters,
109 frame_pc: u64,
110 _stack_frames: &[StackFrame],
111 instruction_set: Option<InstructionSet>,
112 _memory: &mut dyn MemoryInterface,
113 ) -> ControlFlow<Option<DebugError>> {
114 unwind_pc_without_debuginfo(unwind_registers, frame_pc, instruction_set)
115 }
116}