use crate::{
architecture::arm::sequences::ArmDebugSequence, config::DebugSequence, debug::DebugRegisters,
error::Error, CoreType, InstructionSet, MemoryInterface, Target,
};
use anyhow::anyhow;
pub use probe_rs_target::{Architecture, CoreAccessOptions};
use probe_rs_target::{
ArmCoreAccessOptions, MemoryRegion, RiscvCoreAccessOptions, XtensaCoreAccessOptions,
};
use std::{collections::HashMap, ops::Range, sync::Arc, time::Duration};
pub mod core_state;
pub mod core_status;
pub(crate) mod dump;
pub mod memory_mapped_registers;
pub mod registers;
pub use core_state::*;
pub use core_status::*;
pub use memory_mapped_registers::MemoryMappedRegister;
pub use registers::*;
use self::dump::CoreDump;
#[derive(Debug, Clone)]
pub struct CoreInformation {
pub pc: u64,
}
pub trait CoreInterface: MemoryInterface {
fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), Error>;
fn core_halted(&mut self) -> Result<bool, Error>;
fn status(&mut self) -> Result<CoreStatus, Error>;
fn halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error>;
fn run(&mut self) -> Result<(), Error>;
fn reset(&mut self) -> Result<(), Error>;
fn reset_and_halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error>;
fn step(&mut self) -> Result<CoreInformation, Error>;
fn read_core_reg(
&mut self,
address: registers::RegisterId,
) -> Result<registers::RegisterValue, Error>;
fn write_core_reg(
&mut self,
address: registers::RegisterId,
value: registers::RegisterValue,
) -> Result<(), Error>;
fn available_breakpoint_units(&mut self) -> Result<u32, Error>;
fn hw_breakpoints(&mut self) -> Result<Vec<Option<u64>>, Error>;
fn enable_breakpoints(&mut self, state: bool) -> Result<(), Error>;
fn set_hw_breakpoint(&mut self, unit_index: usize, addr: u64) -> Result<(), Error>;
fn clear_hw_breakpoint(&mut self, unit_index: usize) -> Result<(), Error>;
fn registers(&self) -> &'static registers::CoreRegisters;
fn program_counter(&self) -> &'static CoreRegister;
fn frame_pointer(&self) -> &'static CoreRegister;
fn stack_pointer(&self) -> &'static CoreRegister;
fn return_address(&self) -> &'static CoreRegister;
fn hw_breakpoints_enabled(&self) -> bool;
fn debug_on_sw_breakpoint(&mut self, _enabled: bool) -> Result<(), Error> {
Ok(())
}
fn architecture(&self) -> Architecture;
fn core_type(&self) -> CoreType;
fn instruction_set(&mut self) -> Result<InstructionSet, Error>;
fn fpu_support(&mut self) -> Result<bool, Error>;
fn floating_point_register_count(&mut self) -> Result<usize, Error>;
fn reset_catch_set(&mut self) -> Result<(), Error>;
fn reset_catch_clear(&mut self) -> Result<(), Error>;
fn debug_core_stop(&mut self) -> Result<(), Error>;
fn on_session_stop(&mut self) -> Result<(), Error> {
Ok(())
}
fn enable_vector_catch(&mut self, _condition: VectorCatchCondition) -> Result<(), Error> {
Err(Error::NotImplemented("vector catch"))
}
fn disable_vector_catch(&mut self, _condition: VectorCatchCondition) -> Result<(), Error> {
Err(Error::NotImplemented("vector catch"))
}
}
impl<'probe> MemoryInterface for Core<'probe> {
fn supports_native_64bit_access(&mut self) -> bool {
self.inner.supports_native_64bit_access()
}
fn read_word_64(&mut self, address: u64) -> Result<u64, Error> {
self.inner.read_word_64(address)
}
fn read_word_32(&mut self, address: u64) -> Result<u32, Error> {
self.inner.read_word_32(address)
}
fn read_word_16(&mut self, address: u64) -> Result<u16, Error> {
self.inner.read_word_16(address)
}
fn read_word_8(&mut self, address: u64) -> Result<u8, Error> {
self.inner.read_word_8(address)
}
fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), Error> {
self.inner.read_64(address, data)
}
fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), Error> {
self.inner.read_32(address, data)
}
fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), Error> {
self.inner.read_16(address, data)
}
fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
self.inner.read_8(address, data)
}
fn read(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
self.inner.read(address, data)
}
fn write_word_64(&mut self, addr: u64, data: u64) -> Result<(), Error> {
self.inner.write_word_64(addr, data)
}
fn write_word_32(&mut self, addr: u64, data: u32) -> Result<(), Error> {
self.inner.write_word_32(addr, data)
}
fn write_word_16(&mut self, addr: u64, data: u16) -> Result<(), Error> {
self.inner.write_word_16(addr, data)
}
fn write_word_8(&mut self, addr: u64, data: u8) -> Result<(), Error> {
self.inner.write_word_8(addr, data)
}
fn write_64(&mut self, addr: u64, data: &[u64]) -> Result<(), Error> {
self.inner.write_64(addr, data)
}
fn write_32(&mut self, addr: u64, data: &[u32]) -> Result<(), Error> {
self.inner.write_32(addr, data)
}
fn write_16(&mut self, addr: u64, data: &[u16]) -> Result<(), Error> {
self.inner.write_16(addr, data)
}
fn write_8(&mut self, addr: u64, data: &[u8]) -> Result<(), Error> {
self.inner.write_8(addr, data)
}
fn write(&mut self, addr: u64, data: &[u8]) -> Result<(), Error> {
self.inner.write(addr, data)
}
fn supports_8bit_transfers(&self) -> Result<bool, Error> {
self.inner.supports_8bit_transfers()
}
fn flush(&mut self) -> Result<(), Error> {
self.inner.flush()
}
}
#[derive(Debug, PartialEq)]
pub struct ExceptionInfo {
pub description: String,
pub calling_frame_registers: DebugRegisters,
}
pub trait ExceptionInterface {
fn exception_details(
&self,
memory: &mut dyn MemoryInterface,
stackframe_registers: &DebugRegisters,
) -> Result<Option<ExceptionInfo>, Error>;
fn calling_frame_registers(
&self,
memory: &mut dyn MemoryInterface,
stackframe_registers: &crate::debug::DebugRegisters,
) -> Result<crate::debug::DebugRegisters, crate::Error>;
fn exception_description(
&self,
memory: &mut dyn MemoryInterface,
stackframe_registers: &crate::debug::DebugRegisters,
) -> Result<String, crate::Error>;
}
pub struct UnimplementedExceptionHandler;
impl ExceptionInterface for UnimplementedExceptionHandler {
fn exception_details(
&self,
_memory: &mut dyn MemoryInterface,
_stackframe_registers: &DebugRegisters,
) -> Result<Option<ExceptionInfo>, Error> {
Err(Error::NotImplemented(
"Unwinding of exception frames has not yet been implemented for this architecture.",
))
}
fn calling_frame_registers(
&self,
_memory: &mut dyn MemoryInterface,
_stackframe_registers: &crate::debug::DebugRegisters,
) -> Result<crate::debug::DebugRegisters, crate::Error> {
Err(Error::NotImplemented(
"Not implemented for this architecture.",
))
}
fn exception_description(
&self,
_memory: &mut dyn MemoryInterface,
_stackframe_registers: &crate::debug::DebugRegisters,
) -> Result<String, crate::Error> {
Err(Error::NotImplemented(
"Not implemented for this architecture.",
))
}
}
pub fn exception_handler_for_core(core_type: CoreType) -> Box<dyn ExceptionInterface> {
match core_type {
CoreType::Armv6m => {
Box::new(crate::architecture::arm::core::exception_handling::ArmV6MExceptionHandler {})
}
CoreType::Armv7m | CoreType::Armv7em => {
Box::new(crate::architecture::arm::core::exception_handling::ArmV7MExceptionHandler {})
}
CoreType::Armv8m => Box::new(
crate::architecture::arm::core::exception_handling::armv8m::ArmV8MExceptionHandler,
),
CoreType::Armv7a | CoreType::Armv8a | CoreType::Riscv | CoreType::Xtensa => {
Box::new(UnimplementedExceptionHandler)
}
}
}
pub struct Core<'probe> {
id: usize,
name: &'probe str,
memory_regions: &'probe [MemoryRegion],
inner: Box<dyn CoreInterface + 'probe>,
}
impl<'probe> Core<'probe> {
pub fn inner_mut(&mut self) -> &mut Box<dyn CoreInterface + 'probe> {
&mut self.inner
}
pub(crate) fn new(
id: usize,
name: &'probe str,
memory_regions: &'probe [MemoryRegion],
core: impl CoreInterface + 'probe,
) -> Core<'probe> {
Self {
id,
name,
memory_regions,
inner: Box::new(core),
}
}
pub fn memory_regions(&self) -> impl Iterator<Item = &MemoryRegion> {
self.memory_regions
.iter()
.filter(|r| r.cores().iter().any(|m| m == self.name))
}
pub(crate) fn create_state(
id: usize,
options: CoreAccessOptions,
target: &Target,
core_type: CoreType,
) -> CombinedCoreState {
let specific_state = SpecificCoreState::from_core_type(core_type);
match options {
CoreAccessOptions::Arm(options) => {
let DebugSequence::Arm(sequence) = target.debug_sequence.clone() else {
panic!(
"Mismatch between sequence and core kind. This is a bug, please report it."
);
};
let core_state = CoreState::new(ResolvedCoreOptions::Arm { sequence, options });
CombinedCoreState {
id,
core_state,
specific_state,
}
}
CoreAccessOptions::Riscv(options) => {
let core_state = CoreState::new(ResolvedCoreOptions::Riscv { options });
CombinedCoreState {
id,
core_state,
specific_state,
}
}
CoreAccessOptions::Xtensa(options) => {
let core_state = CoreState::new(ResolvedCoreOptions::Xtensa { options });
CombinedCoreState {
id,
core_state,
specific_state,
}
}
}
}
pub fn id(&self) -> usize {
self.id
}
#[tracing::instrument(skip(self))]
pub fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), Error> {
self.inner.wait_for_core_halted(timeout)
}
pub fn core_halted(&mut self) -> Result<bool, Error> {
self.inner.core_halted()
}
#[tracing::instrument(skip(self))]
pub fn halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
self.inner.halt(timeout)
}
#[tracing::instrument(skip(self))]
pub fn run(&mut self) -> Result<(), Error> {
self.inner.run()
}
#[tracing::instrument(skip(self))]
pub fn reset(&mut self) -> Result<(), Error> {
self.inner.reset()
}
#[tracing::instrument(skip(self))]
pub fn reset_and_halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
self.inner.reset_and_halt(timeout)
}
#[tracing::instrument(skip(self))]
pub fn step(&mut self) -> Result<CoreInformation, Error> {
self.inner.step()
}
#[tracing::instrument(skip(self))]
pub fn status(&mut self) -> Result<CoreStatus, Error> {
self.inner.status()
}
#[tracing::instrument(skip(self, address), fields(address))]
pub fn read_core_reg<T>(
&mut self,
address: impl Into<registers::RegisterId>,
) -> Result<T, Error>
where
registers::RegisterValue: TryInto<T>,
Result<T, <registers::RegisterValue as TryInto<T>>::Error>: RegisterValueResultExt<T>,
{
let address = address.into();
tracing::Span::current().record("address", format!("{address:?}"));
let value = self.inner.read_core_reg(address)?;
value.try_into().into_crate_error()
}
#[tracing::instrument(skip(self, address, value))]
pub fn write_core_reg<T>(
&mut self,
address: impl Into<registers::RegisterId>,
value: T,
) -> Result<(), Error>
where
T: Into<registers::RegisterValue>,
{
let address = address.into();
self.inner.write_core_reg(address, value.into())
}
pub fn available_breakpoint_units(&mut self) -> Result<u32, Error> {
self.inner.available_breakpoint_units()
}
fn enable_breakpoints(&mut self, state: bool) -> Result<(), Error> {
self.inner.enable_breakpoints(state)
}
#[tracing::instrument(skip(self))]
pub fn debug_on_sw_breakpoint(&mut self, enabled: bool) -> Result<(), Error> {
self.inner.debug_on_sw_breakpoint(enabled)
}
pub fn registers(&self) -> &'static registers::CoreRegisters {
self.inner.registers()
}
pub fn program_counter(&self) -> &'static CoreRegister {
self.inner.program_counter()
}
pub fn frame_pointer(&self) -> &'static CoreRegister {
self.inner.frame_pointer()
}
pub fn stack_pointer(&self) -> &'static CoreRegister {
self.inner.stack_pointer()
}
pub fn return_address(&self) -> &'static CoreRegister {
self.inner.return_address()
}
fn find_free_breakpoint_comparator_index(&mut self) -> Result<usize, Error> {
let mut next_available_hw_breakpoint = 0;
for breakpoint in self.inner.hw_breakpoints()? {
if breakpoint.is_none() {
return Ok(next_available_hw_breakpoint);
} else {
next_available_hw_breakpoint += 1;
}
}
Err(Error::Other(anyhow!("No available hardware breakpoints")))
}
#[tracing::instrument(skip(self))]
pub fn set_hw_breakpoint(&mut self, address: u64) -> Result<(), Error> {
if !self.inner.hw_breakpoints_enabled() {
self.enable_breakpoints(true)?;
}
let breakpoint_comparator_index = match self
.inner
.hw_breakpoints()?
.iter()
.position(|&bp| bp == Some(address))
{
Some(breakpoint_comparator_index) => breakpoint_comparator_index,
None => self.find_free_breakpoint_comparator_index()?,
};
tracing::debug!(
"Trying to set HW breakpoint #{} with comparator address {:#08x}",
breakpoint_comparator_index,
address
);
self.inner
.set_hw_breakpoint(breakpoint_comparator_index, address)?;
Ok(())
}
#[tracing::instrument(skip(self))]
pub fn clear_hw_breakpoint(&mut self, address: u64) -> Result<(), Error> {
let bp_position = self
.inner
.hw_breakpoints()?
.iter()
.position(|bp| bp.is_some() && bp.unwrap() == address);
tracing::debug!(
"Will clear HW breakpoint #{} with comparator address {:#08x}",
bp_position.unwrap_or(usize::MAX),
address
);
match bp_position {
Some(bp_position) => {
self.inner.clear_hw_breakpoint(bp_position)?;
Ok(())
}
None => Err(Error::Other(anyhow!(
"No breakpoint found at address {:#010x}",
address
))),
}
}
#[tracing::instrument(skip(self))]
pub fn clear_all_hw_breakpoints(&mut self) -> Result<(), Error> {
for breakpoint in (self.inner.hw_breakpoints()?).into_iter().flatten() {
self.clear_hw_breakpoint(breakpoint)?
}
Ok(())
}
pub fn architecture(&self) -> Architecture {
self.inner.architecture()
}
pub fn core_type(&self) -> CoreType {
self.inner.core_type()
}
pub fn instruction_set(&mut self) -> Result<InstructionSet, Error> {
self.inner.instruction_set()
}
pub fn fpu_support(&mut self) -> Result<bool, Error> {
self.inner.fpu_support()
}
pub fn floating_point_register_count(&mut self) -> Result<usize, Error> {
self.inner.floating_point_register_count()
}
pub(crate) fn reset_catch_clear(&mut self) -> Result<(), Error> {
self.inner.reset_catch_clear()
}
pub(crate) fn debug_core_stop(&mut self) -> Result<(), Error> {
self.inner.debug_core_stop()
}
pub fn enable_vector_catch(&mut self, condition: VectorCatchCondition) -> Result<(), Error> {
self.inner.enable_vector_catch(condition)
}
pub fn disable_vector_catch(&mut self, condition: VectorCatchCondition) -> Result<(), Error> {
self.inner.disable_vector_catch(condition)
}
pub fn dump(&mut self, ranges: Vec<Range<u64>>) -> Result<CoreDump, Error> {
let instruction_set = self.instruction_set()?;
let core_type = self.core_type();
let supports_native_64bit_access = self.supports_native_64bit_access();
let fpu_support = self.fpu_support()?;
let floating_point_register_count = self.floating_point_register_count()?;
let mut registers = HashMap::new();
for register in self.registers().all_registers() {
let value = self.read_core_reg(register.id())?;
registers.insert(register.id(), value);
}
let mut data = Vec::new();
for range in ranges {
let mut values = vec![0; (range.end - range.start) as usize];
self.read(range.start, &mut values)?;
data.push((range, values));
}
Ok(CoreDump {
registers,
data,
instruction_set,
supports_native_64bit_access,
core_type,
fpu_support,
floating_point_register_count: Some(floating_point_register_count),
})
}
}
impl<'probe> CoreInterface for Core<'probe> {
fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), Error> {
self.wait_for_core_halted(timeout)
}
fn core_halted(&mut self) -> Result<bool, Error> {
self.core_halted()
}
fn status(&mut self) -> Result<CoreStatus, Error> {
self.status()
}
fn halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
self.halt(timeout)
}
fn run(&mut self) -> Result<(), Error> {
self.run()
}
fn reset(&mut self) -> Result<(), Error> {
self.reset()
}
fn reset_and_halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
self.reset_and_halt(timeout)
}
fn step(&mut self) -> Result<CoreInformation, Error> {
self.step()
}
fn read_core_reg(
&mut self,
address: registers::RegisterId,
) -> Result<registers::RegisterValue, Error> {
self.read_core_reg(address)
}
fn write_core_reg(
&mut self,
address: registers::RegisterId,
value: registers::RegisterValue,
) -> Result<(), Error> {
self.write_core_reg(address, value)
}
fn available_breakpoint_units(&mut self) -> Result<u32, Error> {
self.available_breakpoint_units()
}
fn hw_breakpoints(&mut self) -> Result<Vec<Option<u64>>, Error> {
todo!()
}
fn enable_breakpoints(&mut self, state: bool) -> Result<(), Error> {
self.enable_breakpoints(state)
}
fn set_hw_breakpoint(&mut self, _unit_index: usize, addr: u64) -> Result<(), Error> {
self.set_hw_breakpoint(addr)
}
fn clear_hw_breakpoint(&mut self, _unit_index: usize) -> Result<(), Error> {
self.clear_all_hw_breakpoints()
}
fn registers(&self) -> &'static registers::CoreRegisters {
self.registers()
}
fn program_counter(&self) -> &'static CoreRegister {
self.program_counter()
}
fn frame_pointer(&self) -> &'static CoreRegister {
self.frame_pointer()
}
fn stack_pointer(&self) -> &'static CoreRegister {
self.stack_pointer()
}
fn return_address(&self) -> &'static CoreRegister {
self.return_address()
}
fn hw_breakpoints_enabled(&self) -> bool {
todo!()
}
fn architecture(&self) -> Architecture {
self.architecture()
}
fn core_type(&self) -> CoreType {
self.core_type()
}
fn instruction_set(&mut self) -> Result<InstructionSet, Error> {
self.instruction_set()
}
fn fpu_support(&mut self) -> Result<bool, Error> {
self.fpu_support()
}
fn floating_point_register_count(&mut self) -> Result<usize, crate::error::Error> {
self.floating_point_register_count()
}
fn reset_catch_set(&mut self) -> Result<(), Error> {
todo!()
}
fn reset_catch_clear(&mut self) -> Result<(), Error> {
self.reset_catch_clear()
}
fn debug_core_stop(&mut self) -> Result<(), Error> {
self.debug_core_stop()
}
}
pub enum ResolvedCoreOptions {
Arm {
sequence: Arc<dyn ArmDebugSequence>,
options: ArmCoreAccessOptions,
},
Riscv {
options: RiscvCoreAccessOptions,
},
Xtensa {
options: XtensaCoreAccessOptions,
},
}
impl std::fmt::Debug for ResolvedCoreOptions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Arm { options, .. } => f
.debug_struct("Arm")
.field("sequence", &"<ArmDebugSequence>")
.field("options", options)
.finish(),
Self::Riscv { options } => f.debug_struct("Riscv").field("options", options).finish(),
Self::Xtensa { options } => f.debug_struct("Xtensa").field("options", options).finish(),
}
}
}