use crate::architecture::arm::memory::adi_v5_memory_interface::ArmProbe;
use crate::architecture::riscv::RiscVState;
use crate::{CoreType, InstructionSet};
use num_traits::Zero;
pub use probe_rs_target::{Architecture, CoreAccessOptions};
use crate::architecture::{
arm::core::CortexAState, arm::core::CortexMState,
riscv::communication_interface::RiscvCommunicationInterface,
};
use crate::error;
use crate::Target;
use crate::{Error, MemoryInterface};
use anyhow::{anyhow, Result};
use std::cmp::Ordering;
use std::convert::Infallible;
use std::time::Duration;
pub trait MemoryMappedRegister: Clone + From<u32> + Into<u32> + Sized + std::fmt::Debug {
const ADDRESS: u64;
const NAME: &'static str;
}
#[derive(Debug, Clone)]
pub struct CoreInformation {
pub pc: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RegisterDataType {
UnsignedInteger,
FloatingPoint,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RegisterDescription {
pub(crate) name: &'static str,
pub(crate) _kind: RegisterKind,
pub(crate) id: RegisterId,
pub(crate) _type: RegisterDataType,
pub(crate) size_in_bits: usize,
}
impl RegisterDescription {
pub fn name(&self) -> &'static str {
self.name
}
pub fn data_type(&self) -> RegisterDataType {
self._type.clone()
}
pub fn size_in_bits(&self) -> usize {
self.size_in_bits
}
pub fn size_in_bytes(&self) -> usize {
(self.size_in_bits + 7) / 8
}
pub fn format_hex_width(&self) -> usize {
(self.size_in_bytes() * 2) + 2
}
}
impl From<RegisterDescription> for RegisterId {
fn from(description: RegisterDescription) -> RegisterId {
description.id
}
}
impl From<&RegisterDescription> for RegisterId {
fn from(description: &RegisterDescription) -> RegisterId {
description.id
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub struct RegisterId(pub u16);
impl From<RegisterId> for u32 {
fn from(value: RegisterId) -> Self {
u32::from(value.0)
}
}
impl From<u16> for RegisterId {
fn from(value: u16) -> Self {
RegisterId(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum RegisterKind {
General,
PC,
Fp,
}
#[derive(Debug, Clone, Copy)]
pub enum RegisterValue {
U32(u32),
U64(u64),
U128(u128),
}
impl RegisterValue {
pub fn incremenet_address(&mut self, bytes: usize) -> Result<(), Error> {
match self {
RegisterValue::U32(value) => {
if let Some(reg_val) = value.checked_add(bytes as u32) {
*value = reg_val;
Ok(())
} else {
Err(Error::Other(anyhow!(
"Overflow error: Attempting to add {} bytes to Register value {}",
bytes,
self
)))
}
}
RegisterValue::U64(value) => {
if let Some(reg_val) = value.checked_add(bytes as u64) {
*value = reg_val;
Ok(())
} else {
Err(Error::Other(anyhow!(
"Overflow error: Attempting to add {} bytes to Register value {}",
bytes,
self
)))
}
}
RegisterValue::U128(value) => {
if let Some(reg_val) = value.checked_add(bytes as u128) {
*value = reg_val;
Ok(())
} else {
Err(Error::Other(anyhow!(
"Overflow error: Attempting to add {} bytes to Register value {}",
bytes,
self
)))
}
}
}
}
pub fn is_max_value(&self) -> bool {
match self {
RegisterValue::U32(register_value) => *register_value == u32::MAX,
RegisterValue::U64(register_value) => *register_value == u64::MAX,
RegisterValue::U128(register_value) => *register_value == u128::MAX,
}
}
pub fn is_zero(&self) -> bool {
match self {
RegisterValue::U32(register_value) => register_value.is_zero(),
RegisterValue::U64(register_value) => register_value.is_zero(),
RegisterValue::U128(register_value) => register_value.is_zero(),
}
}
}
impl Default for RegisterValue {
fn default() -> Self {
RegisterValue::U32(0_u32)
}
}
impl PartialOrd for RegisterValue {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let self_value = match self {
RegisterValue::U32(self_value) => *self_value as u128,
RegisterValue::U64(self_value) => *self_value as u128,
RegisterValue::U128(self_value) => *self_value,
};
let other_value = match other {
RegisterValue::U32(other_value) => *other_value as u128,
RegisterValue::U64(other_value) => *other_value as u128,
RegisterValue::U128(other_value) => *other_value,
};
self_value.partial_cmp(&other_value)
}
}
impl PartialEq for RegisterValue {
fn eq(&self, other: &Self) -> bool {
let self_value = match self {
RegisterValue::U32(self_value) => *self_value as u128,
RegisterValue::U64(self_value) => *self_value as u128,
RegisterValue::U128(self_value) => *self_value,
};
let other_value = match other {
RegisterValue::U32(other_value) => *other_value as u128,
RegisterValue::U64(other_value) => *other_value as u128,
RegisterValue::U128(other_value) => *other_value,
};
self_value == other_value
}
}
impl core::fmt::Display for RegisterValue {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
RegisterValue::U32(register_value) => write!(f, "{register_value:#010x}"),
RegisterValue::U64(register_value) => write!(f, "{register_value:#018x}"),
RegisterValue::U128(register_value) => write!(f, "{register_value:#034x}"),
}
}
}
impl From<u32> for RegisterValue {
fn from(val: u32) -> Self {
Self::U32(val)
}
}
impl From<u64> for RegisterValue {
fn from(val: u64) -> Self {
Self::U64(val)
}
}
impl From<u128> for RegisterValue {
fn from(val: u128) -> Self {
Self::U128(val)
}
}
impl TryInto<u32> for RegisterValue {
type Error = crate::Error;
fn try_into(self) -> Result<u32, Self::Error> {
match self {
Self::U32(v) => Ok(v),
Self::U64(v) => v
.try_into()
.map_err(|_| crate::Error::Other(anyhow!("Value '{}' too large for u32", v))),
Self::U128(v) => v
.try_into()
.map_err(|_| crate::Error::Other(anyhow!("Value '{}' too large for u32", v))),
}
}
}
impl TryInto<u64> for RegisterValue {
type Error = crate::Error;
fn try_into(self) -> Result<u64, Self::Error> {
match self {
Self::U32(v) => Ok(v.into()),
Self::U64(v) => Ok(v),
Self::U128(v) => v
.try_into()
.map_err(|_| crate::Error::Other(anyhow!("Value '{}' too large for u64", v))),
}
}
}
impl TryInto<u128> for RegisterValue {
type Error = crate::Error;
fn try_into(self) -> Result<u128, Self::Error> {
match self {
Self::U32(v) => Ok(v.into()),
Self::U64(v) => Ok(v.into()),
Self::U128(v) => Ok(v),
}
}
}
pub trait RegisterValueResultExt<T> {
fn into_crate_error(self) -> Result<T, Error>;
}
impl<T> RegisterValueResultExt<T> for Result<T, Error> {
fn into_crate_error(self) -> Result<T, Error> {
self
}
}
impl<T> RegisterValueResultExt<T> for Result<T, Infallible> {
fn into_crate_error(self) -> Result<T, Error> {
Ok(self.unwrap())
}
}
#[derive(Debug, PartialEq)]
pub struct RegisterFile {
pub(crate) platform_registers: &'static [RegisterDescription],
pub(crate) program_counter: &'static RegisterDescription,
pub(crate) stack_pointer: &'static RegisterDescription,
pub(crate) return_address: &'static RegisterDescription,
pub(crate) frame_pointer: &'static RegisterDescription,
pub(crate) argument_registers: &'static [RegisterDescription],
pub(crate) result_registers: &'static [RegisterDescription],
pub(crate) msp: Option<&'static RegisterDescription>,
pub(crate) psp: Option<&'static RegisterDescription>,
pub(crate) psr: Option<&'static RegisterDescription>,
pub(crate) fp_status: Option<&'static RegisterDescription>,
pub(crate) fp_registers: Option<&'static [RegisterDescription]>,
pub(crate) other: &'static [RegisterDescription],
}
impl RegisterFile {
pub fn platform_registers(&self) -> impl Iterator<Item = &RegisterDescription> {
self.platform_registers.iter()
}
pub fn frame_pointer(&self) -> &RegisterDescription {
self.frame_pointer
}
pub fn program_counter(&self) -> &RegisterDescription {
self.program_counter
}
pub fn stack_pointer(&self) -> &RegisterDescription {
self.stack_pointer
}
pub fn return_address(&self) -> &RegisterDescription {
self.return_address
}
pub fn argument_register(&self, index: usize) -> &RegisterDescription {
&self.argument_registers[index]
}
pub fn get_argument_register(&self, index: usize) -> Option<&RegisterDescription> {
self.argument_registers.get(index)
}
pub fn result_register(&self, index: usize) -> &RegisterDescription {
&self.result_registers[index]
}
pub fn get_result_register(&self, index: usize) -> Option<&RegisterDescription> {
self.result_registers.get(index)
}
pub fn platform_register(&self, index: usize) -> &RegisterDescription {
&self.platform_registers[index]
}
pub fn get_platform_register(&self, index: usize) -> Option<&RegisterDescription> {
self.platform_registers.get(index)
}
pub fn msp(&self) -> Option<&RegisterDescription> {
self.msp
}
pub fn psp(&self) -> Option<&RegisterDescription> {
self.psp
}
pub fn psr(&self) -> Option<&RegisterDescription> {
self.psr
}
pub fn other(&self) -> impl Iterator<Item = &RegisterDescription> {
self.other.iter()
}
pub fn other_by_name(&self, name: &str) -> Option<&RegisterDescription> {
self.other.iter().find(|r| r.name == name)
}
pub fn fpscr(&self) -> Option<&RegisterDescription> {
self.fp_status
}
pub fn fpu_registers(&self) -> Option<impl Iterator<Item = &RegisterDescription>> {
self.fp_registers.map(|r| r.iter())
}
pub fn fpu_register(&self, index: usize) -> Option<&RegisterDescription> {
self.fp_registers.map(|r| &r[index])
}
pub fn get_fpu_register(&self, index: usize) -> Option<&RegisterDescription> {
self.fp_registers.and_then(|r| r.get(index))
}
}
pub trait CoreInterface: MemoryInterface {
fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), error::Error>;
fn core_halted(&mut self) -> Result<bool, error::Error>;
fn status(&mut self) -> Result<CoreStatus, error::Error>;
fn halt(&mut self, timeout: Duration) -> Result<CoreInformation, error::Error>;
fn run(&mut self) -> Result<(), error::Error>;
fn reset(&mut self) -> Result<(), error::Error>;
fn reset_and_halt(&mut self, timeout: Duration) -> Result<CoreInformation, error::Error>;
fn step(&mut self) -> Result<CoreInformation, error::Error>;
fn read_core_reg(&mut self, address: RegisterId) -> Result<RegisterValue, error::Error>;
fn write_core_reg(
&mut self,
address: RegisterId,
value: RegisterValue,
) -> Result<(), error::Error>;
fn available_breakpoint_units(&mut self) -> Result<u32, error::Error>;
fn hw_breakpoints(&mut self) -> Result<Vec<Option<u64>>, error::Error>;
fn enable_breakpoints(&mut self, state: bool) -> Result<(), error::Error>;
fn set_hw_breakpoint(&mut self, unit_index: usize, addr: u64) -> Result<(), error::Error>;
fn clear_hw_breakpoint(&mut self, unit_index: usize) -> Result<(), error::Error>;
fn registers(&self) -> &'static RegisterFile;
fn hw_breakpoints_enabled(&self) -> bool;
fn debug_on_sw_breakpoint(&mut self, _enabled: bool) -> Result<(), error::Error> {
Ok(())
}
fn architecture(&self) -> Architecture;
fn core_type(&self) -> CoreType;
fn instruction_set(&mut self) -> Result<InstructionSet, error::Error>;
fn fpu_support(&mut self) -> Result<bool, error::Error>;
fn on_session_stop(&mut self) -> Result<(), Error> {
Ok(())
}
}
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_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_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_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_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::Error> {
self.inner.supports_8bit_transfers()
}
fn flush(&mut self) -> Result<(), Error> {
self.inner.flush()
}
}
#[derive(Debug)]
pub struct CoreState {
id: usize,
core_access_options: CoreAccessOptions,
}
impl CoreState {
pub fn new(id: usize, core_access_options: CoreAccessOptions) -> Self {
Self {
id,
core_access_options,
}
}
pub fn id(&self) -> usize {
self.id
}
}
#[derive(Debug)]
pub enum SpecificCoreState {
Armv6m(CortexMState),
Armv7a(CortexAState),
Armv7m(CortexMState),
Armv7em(CortexMState),
Armv8a(CortexAState),
Armv8m(CortexMState),
Riscv(RiscVState),
}
impl SpecificCoreState {
pub(crate) fn from_core_type(typ: CoreType) -> Self {
match typ {
CoreType::Armv6m => SpecificCoreState::Armv6m(CortexMState::new()),
CoreType::Armv7a => SpecificCoreState::Armv7a(CortexAState::new()),
CoreType::Armv7m => SpecificCoreState::Armv7m(CortexMState::new()),
CoreType::Armv7em => SpecificCoreState::Armv7m(CortexMState::new()),
CoreType::Armv8a => SpecificCoreState::Armv8a(CortexAState::new()),
CoreType::Armv8m => SpecificCoreState::Armv8m(CortexMState::new()),
CoreType::Riscv => SpecificCoreState::Riscv(RiscVState::new()),
}
}
pub(crate) fn core_type(&self) -> CoreType {
match self {
SpecificCoreState::Armv6m(_) => CoreType::Armv6m,
SpecificCoreState::Armv7a(_) => CoreType::Armv7a,
SpecificCoreState::Armv7m(_) => CoreType::Armv7m,
SpecificCoreState::Armv7em(_) => CoreType::Armv7em,
SpecificCoreState::Armv8a(_) => CoreType::Armv8a,
SpecificCoreState::Armv8m(_) => CoreType::Armv8m,
SpecificCoreState::Riscv(_) => CoreType::Riscv,
}
}
pub(crate) fn attach_arm<'probe, 'target: 'probe>(
&'probe mut self,
state: &'probe mut CoreState,
memory: Box<dyn ArmProbe + 'probe>,
target: &'target Target,
) -> Result<Core<'probe>, Error> {
let debug_sequence = match &target.debug_sequence {
crate::config::DebugSequence::Arm(sequence) => sequence.clone(),
crate::config::DebugSequence::Riscv(_) => {
return Err(Error::UnableToOpenProbe(
"Core architecture and Probe mismatch.",
))
}
};
let options = match &state.core_access_options {
CoreAccessOptions::Arm(options) => options,
CoreAccessOptions::Riscv(_) => {
return Err(Error::UnableToOpenProbe(
"Core architecture and Probe mismatch.",
))
}
};
Ok(match self {
SpecificCoreState::Armv6m(s) => Core::new(
crate::architecture::arm::armv6m::Armv6m::new(memory, s, debug_sequence)?,
state,
),
SpecificCoreState::Armv7a(s) => Core::new(
crate::architecture::arm::armv7a::Armv7a::new(
memory,
s,
options.debug_base.expect("base_address not specified"),
debug_sequence,
)?,
state,
),
SpecificCoreState::Armv7m(s) | SpecificCoreState::Armv7em(s) => Core::new(
crate::architecture::arm::armv7m::Armv7m::new(memory, s, debug_sequence)?,
state,
),
SpecificCoreState::Armv8a(s) => Core::new(
crate::architecture::arm::armv8a::Armv8a::new(
memory,
s,
options.debug_base.expect("base_address not specified"),
options.cti_base.expect("cti_address not specified"),
debug_sequence,
)?,
state,
),
SpecificCoreState::Armv8m(s) => Core::new(
crate::architecture::arm::armv8m::Armv8m::new(memory, s, debug_sequence)?,
state,
),
_ => {
return Err(Error::UnableToOpenProbe(
"Core architecture and Probe mismatch.",
))
}
})
}
pub(crate) fn attach_riscv<'probe>(
&'probe mut self,
state: &'probe mut CoreState,
interface: &'probe mut RiscvCommunicationInterface,
) -> Result<Core<'probe>, Error> {
Ok(match self {
SpecificCoreState::Riscv(s) => Core::new(
crate::architecture::riscv::Riscv32::new(interface, s),
state,
),
_ => {
return Err(Error::UnableToOpenProbe(
"Core architecture and Probe mismatch.",
))
}
})
}
}
pub struct Core<'probe> {
inner: Box<dyn CoreInterface + 'probe>,
state: &'probe mut CoreState,
}
impl<'probe> Core<'probe> {
pub fn new(core: impl CoreInterface + 'probe, state: &'probe mut CoreState) -> Core<'probe> {
Self {
inner: Box::new(core),
state,
}
}
pub fn create_state(id: usize, options: CoreAccessOptions) -> CoreState {
CoreState::new(id, options)
}
pub fn id(&self) -> usize {
self.state.id
}
#[tracing::instrument(skip(self))]
pub fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), error::Error> {
self.inner.wait_for_core_halted(timeout)
}
pub fn core_halted(&mut self) -> Result<bool, error::Error> {
self.inner.core_halted()
}
#[tracing::instrument(skip(self))]
pub fn halt(&mut self, timeout: Duration) -> Result<CoreInformation, error::Error> {
self.inner.halt(timeout)
}
#[tracing::instrument(skip(self))]
pub fn run(&mut self) -> Result<(), error::Error> {
self.inner.run()
}
#[tracing::instrument(skip(self))]
pub fn reset(&mut self) -> Result<(), error::Error> {
self.inner.reset()
}
#[tracing::instrument(skip(self))]
pub fn reset_and_halt(&mut self, timeout: Duration) -> Result<CoreInformation, error::Error> {
self.inner.reset_and_halt(timeout)
}
#[tracing::instrument(skip(self))]
pub fn step(&mut self) -> Result<CoreInformation, error::Error> {
self.inner.step()
}
#[tracing::instrument(skip(self))]
pub fn status(&mut self) -> Result<CoreStatus, error::Error> {
self.inner.status()
}
#[tracing::instrument(skip(self, address), fields(address))]
pub fn read_core_reg<T>(&mut self, address: impl Into<RegisterId>) -> Result<T, error::Error>
where
RegisterValue: TryInto<T>,
Result<T, <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, value))]
pub fn write_core_reg<T>(&mut self, address: RegisterId, value: T) -> Result<(), error::Error>
where
T: Into<RegisterValue>,
{
self.inner.write_core_reg(address, value.into())
}
pub fn available_breakpoint_units(&mut self) -> Result<u32, error::Error> {
self.inner.available_breakpoint_units()
}
fn enable_breakpoints(&mut self, state: bool) -> Result<(), error::Error> {
self.inner.enable_breakpoints(state)
}
#[tracing::instrument(skip(self))]
pub fn debug_on_sw_breakpoint(&mut self, enabled: bool) -> Result<(), error::Error> {
self.inner.debug_on_sw_breakpoint(enabled)
}
pub fn registers(&self) -> &'static RegisterFile {
self.inner.registers()
}
fn find_free_breakpoint_comparator_index(&mut self) -> Result<usize, error::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::Error::Other(anyhow!(
"No available hardware breakpoints"
)))
}
#[tracing::instrument(skip(self))]
pub fn set_hw_breakpoint(&mut self, address: u64) -> Result<(), error::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::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::Error::Other(anyhow!(
"No breakpoint found at address {:#010x}",
address
))),
}
}
#[tracing::instrument(skip(self))]
pub fn clear_all_hw_breakpoints(&mut self) -> Result<(), error::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::Error> {
self.inner.instruction_set()
}
pub fn fpu_support(&mut self) -> Result<bool, error::Error> {
self.inner.fpu_support()
}
#[tracing::instrument(skip(self))]
pub(crate) fn on_session_stop(&mut self) -> Result<(), Error> {
self.inner.on_session_stop()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BreakpointId(usize);
impl BreakpointId {
pub fn new(id: usize) -> Self {
BreakpointId(id)
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum CoreStatus {
Running,
Halted(HaltReason),
LockedUp,
Sleeping,
Unknown,
}
impl CoreStatus {
pub fn is_halted(&self) -> bool {
matches!(self, CoreStatus::Halted(_))
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum BreakpointCause {
Hardware,
Software,
Unknown,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum HaltReason {
Multiple,
Breakpoint(BreakpointCause),
Exception,
Watchpoint,
Step,
Request,
External,
Unknown,
}