use std::ops::ControlFlow;
use crate::{
DebugError, DebugRegisters, StackFrame, exception_handling::ExceptionInterface,
unwind_pc_without_debuginfo,
};
use probe_rs::{InstructionSet, MemoryInterface, RegisterRole, RegisterValue};
pub struct RiscvExceptionHandler;
impl RiscvExceptionHandler {
fn unwind_registers(
&self,
memory: &mut dyn MemoryInterface,
unwind_registers: &mut DebugRegisters,
) -> Result<(), DebugError> {
let ra = unwind_registers.get_register_value_by_role(&RegisterRole::ReturnAddress)?;
if ra == 0 {
return Ok(());
}
let sp = unwind_registers.get_register_value_by_role(&RegisterRole::StackPointer)?;
if sp < 8 {
return Err(DebugError::Other(
"Stack pointer is too low to unwind".to_string(),
));
}
let mut stack_frame = [0; 2];
memory.read_32(sp - 8, &mut stack_frame)?;
let [caller_sp, return_addr] = stack_frame;
if (caller_sp as u64).saturating_sub(sp) > 0x1000_0000 {
return Err(DebugError::Other(
"Stack pointer is too far away to unwind".to_string(),
));
}
let regs_from_current_frame = [
(RegisterRole::ReturnAddress, return_addr),
(RegisterRole::StackPointer, caller_sp),
];
for (role, value) in regs_from_current_frame {
let reg = unwind_registers.get_register_mut_by_role(&role).unwrap();
reg.value = Some(RegisterValue::from(value));
}
Ok(())
}
}
impl ExceptionInterface for RiscvExceptionHandler {
fn unwind_without_debuginfo(
&self,
unwind_registers: &mut DebugRegisters,
frame_pc: u64,
_stack_frames: &[StackFrame],
instruction_set: Option<InstructionSet>,
memory: &mut dyn MemoryInterface,
) -> ControlFlow<Option<DebugError>> {
unwind_pc_without_debuginfo(unwind_registers, frame_pc, instruction_set)?;
match self.unwind_registers(memory, unwind_registers) {
Ok(_) => ControlFlow::Continue(()),
Err(error) => ControlFlow::Break(Some(error)),
}
}
}