use super::{
CortexAState,
instructions::aarch32::{
build_ldc, build_mcr, build_mov, build_mrc, build_mrs, build_msr, build_stc, build_vmov,
build_vmrs,
},
registers::{
aarch32::{
AARCH32_CORE_REGISTERS, AARCH32_WITH_FP_16_CORE_REGISTERS,
AARCH32_WITH_FP_32_CORE_REGISTERS,
},
cortex_m::{FP, PC, RA, SP, XPSR},
},
};
use crate::{
Architecture, CoreInformation, CoreInterface, CoreRegister, CoreStatus, CoreType, Endian,
InstructionSet, MemoryInterface,
architecture::arm::{
ArmError, DapAccess, FullyQualifiedApAddress,
ap::{ApRegister, BD0, BD1, BD2, BD3, TAR, TAR2},
core::armv7a_debug_regs::*,
memory::ArmMemoryInterface,
sequences::ArmDebugSequence,
},
core::{CoreRegisters, MemoryMappedRegister, RegisterId, RegisterValue},
error::Error,
memory::{MemoryNotAlignedError, valid_32bit_address},
};
use std::{
mem::size_of,
sync::Arc,
time::{Duration, Instant},
};
use zerocopy::{FromBytes, IntoBytes};
const OPERATION_TIMEOUT: Duration = Duration::from_millis(250);
struct BankedAccess<'a> {
interface: &'a mut dyn DapAccess,
ap: FullyQualifiedApAddress,
dtrtx: u64,
itr: u64,
dscr: u64,
dtrrx: u64,
}
impl<'a> BankedAccess<'a> {
fn set_dtrtx(&mut self, value: u32) -> Result<(), ArmError> {
self.interface
.write_raw_ap_register(&self.ap, self.dtrtx, value)
}
fn dscr(&mut self) -> Result<Dbgdscr, ArmError> {
self.interface
.read_raw_ap_register(&self.ap, self.dscr)
.map(Dbgdscr::from)
}
fn set_dscr(&mut self, value: Dbgdscr) -> Result<(), ArmError> {
self.interface
.write_raw_ap_register(&self.ap, self.dscr, value.into())
}
fn set_itr(&mut self, value: u32) -> Result<(), ArmError> {
self.interface
.write_raw_ap_register(&self.ap, self.itr, value)
}
fn dtrrx(&mut self) -> Result<u32, ArmError> {
self.interface.read_raw_ap_register(&self.ap, self.dtrrx)
}
fn with_dcc_fast_mode<R>(
&mut self,
f: impl FnOnce(&mut Self) -> Result<R, ArmError>,
) -> Result<R, ArmError> {
let mut dscr = self.dscr()?;
dscr.set_extdccmode(2);
self.set_dscr(dscr)?;
let result = f(self);
let mut dscr = self.dscr()?;
dscr.set_extdccmode(0);
self.set_dscr(dscr)?;
result
}
}
#[derive(thiserror::Error, Debug)]
pub enum Armv7aError {
#[error("Register number {0} is not valid for ARMv7-A")]
InvalidRegisterNumber(u16),
#[error("Core is running but operation requires it to be halted")]
NotHalted,
#[error("A data abort occurred")]
DataAbort,
}
pub struct Armv7a<'probe> {
memory: Box<dyn ArmMemoryInterface + 'probe>,
state: &'probe mut CortexAState,
base_address: u64,
sequence: Arc<dyn ArmDebugSequence>,
num_breakpoints: Option<u32>,
itr_enabled: bool,
endianness: Option<Endian>,
}
impl<'probe> Armv7a<'probe> {
pub(crate) fn new(
mut memory: Box<dyn ArmMemoryInterface + 'probe>,
state: &'probe mut CortexAState,
base_address: u64,
sequence: Arc<dyn ArmDebugSequence>,
) -> Result<Self, Error> {
if !state.initialized() {
let address = Dbgdscr::get_mmio_address_from_base(base_address)?;
let dbgdscr = Dbgdscr(memory.read_word_32(address)?);
tracing::debug!("State when connecting: {:x?}", dbgdscr);
let core_state = if dbgdscr.halted() {
let reason = dbgdscr.halt_reason();
tracing::debug!("Core was halted when connecting, reason: {:?}", reason);
CoreStatus::Halted(reason)
} else {
CoreStatus::Running
};
state.current_state = core_state;
}
let mut core = Self {
memory,
state,
base_address,
sequence,
num_breakpoints: None,
itr_enabled: false,
endianness: None,
};
if !core.state.initialized() {
core.reset_register_cache();
core.read_fp_reg_count()?;
core.state.initialize();
}
Ok(core)
}
fn read_fp_reg_count(&mut self) -> Result<(), Error> {
if self.state.fp_reg_count == 0 && matches!(self.state.current_state, CoreStatus::Halted(_))
{
self.prepare_r0_for_clobber()?;
let instruction = build_mrc(15, 0, 0, 1, 0, 2);
self.execute_instruction(instruction)?;
let instruction = build_mcr(14, 0, 0, 0, 5, 0);
let cpacr = Cpacr(self.execute_instruction_with_result(instruction)?);
if cpacr.cp(10) == 0 || cpacr.cp(11) == 0 {
return Ok(());
}
let instruction = build_vmrs(0, 0b0111);
self.execute_instruction(instruction)?;
let instruction = build_mcr(14, 0, 0, 0, 5, 0);
let vmrs = self.execute_instruction_with_result(instruction)?;
self.state.fp_reg_count = match vmrs & 0b111 {
0b001 => 16,
0b010 => 32,
_ => 0,
};
}
Ok(())
}
fn execute_instruction(&mut self, instruction: u32) -> Result<Dbgdscr, ArmError> {
if !self.state.current_state.is_halted() {
return Err(ArmError::CoreNotHalted);
}
if !self.itr_enabled {
let address = Dbgdscr::get_mmio_address_from_base(self.base_address)?;
let mut dbgdscr = Dbgdscr(self.memory.read_word_32(address)?);
dbgdscr.set_itren(true);
self.memory.write_word_32(address, dbgdscr.into())?;
self.itr_enabled = true;
}
execute_instruction(&mut *self.memory, self.base_address, instruction)
}
fn execute_instruction_with_result(&mut self, instruction: u32) -> Result<u32, Error> {
let mut dbgdscr = self.execute_instruction(instruction)?;
let start = Instant::now();
while !dbgdscr.txfull_l() {
let address = Dbgdscr::get_mmio_address_from_base(self.base_address)?;
dbgdscr = Dbgdscr(self.memory.read_word_32(address)?);
check_and_clear_data_abort(&mut *self.memory, self.base_address, dbgdscr)?;
if start.elapsed() >= OPERATION_TIMEOUT {
return Err(Error::Timeout);
}
}
let address = Dbgdtrtx::get_mmio_address_from_base(self.base_address)?;
let result = self.memory.read_word_32(address)?;
Ok(result)
}
fn execute_instruction_with_input(
&mut self,
instruction: u32,
value: u32,
) -> Result<(), Error> {
let address = Dbgdtrrx::get_mmio_address_from_base(self.base_address)?;
self.memory.write_word_32(address, value)?;
let address = Dbgdscr::get_mmio_address_from_base(self.base_address)?;
let mut dbgdscr = Dbgdscr(self.memory.read_word_32(address)?);
let start = Instant::now();
while !dbgdscr.rxfull_l() {
dbgdscr = Dbgdscr(self.memory.read_word_32(address)?);
check_and_clear_data_abort(&mut *self.memory, self.base_address, dbgdscr)?;
if start.elapsed() >= OPERATION_TIMEOUT {
return Err(Error::Timeout);
}
}
self.execute_instruction(instruction)?;
Ok(())
}
fn reset_register_cache(&mut self) {
self.state.register_cache = vec![None; 51];
}
fn writeback_registers(&mut self) -> Result<(), Error> {
let writeback_iter = (17u16..=48).chain(15u16..=16).chain(0u16..=14);
for i in writeback_iter {
if let Some((val, writeback)) = self.state.register_cache[i as usize]
&& writeback
{
match i {
0..=14 => {
let instruction = build_mrc(14, 0, i, 0, 5, 0);
self.execute_instruction_with_input(instruction, val.try_into()?)?;
}
15 => {
let instruction = build_mrc(14, 0, 0, 0, 5, 0);
self.execute_instruction_with_input(instruction, val.try_into()?)?;
let instruction = build_mov(15, 0);
self.execute_instruction(instruction)?;
}
16 => {
let instruction = build_msr(0);
self.execute_instruction_with_input(instruction, val.try_into()?)?;
}
17..=48 => {
let value: u64 = val.try_into()?;
let low_word = value as u32;
let high_word = (value >> 32) as u32;
let instruction = build_mrc(14, 0, 0, 0, 5, 0);
self.execute_instruction_with_input(instruction, low_word)?;
let instruction = build_mrc(14, 0, 1, 0, 5, 0);
self.execute_instruction_with_input(instruction, high_word)?;
let instruction = build_vmov(0, 0, 1, i - 17);
self.execute_instruction(instruction)?;
}
_ => {
panic!("Logic missing for writeback of register {i}");
}
}
}
}
self.reset_register_cache();
Ok(())
}
fn prepare_r0_for_clobber(&mut self) -> Result<(), Error> {
self.prepare_for_clobber(0)
}
fn prepare_for_clobber(&mut self, reg: usize) -> Result<(), Error> {
if self.state.register_cache[reg].is_none() {
let val: u32 = self.read_core_reg(RegisterId(reg as u16))?.try_into()?;
self.state.register_cache[reg] = Some((val.into(), true));
}
Ok(())
}
fn set_r0(&mut self, value: u32) -> Result<(), Error> {
let instruction = build_mrc(14, 0, 0, 0, 5, 0);
self.execute_instruction_with_input(instruction, value)
}
fn set_core_status(&mut self, new_status: CoreStatus) {
super::update_core_status(&mut self.memory, &mut self.state.current_state, new_status);
}
pub(crate) fn halted_access<R>(
&mut self,
op: impl FnOnce(&mut Self) -> Result<R, Error>,
) -> Result<R, Error> {
let was_running = !(self.state.current_state.is_halted() || self.status()?.is_halted());
if was_running {
self.halt(Duration::from_millis(100))?;
}
let result = op(self);
if was_running {
self.run()?
}
result
}
fn banked_access(&mut self) -> Result<BankedAccess<'_>, Error> {
let address = Dbgdtrtx::get_mmio_address_from_base(self.base_address)?;
let ap = self.memory.fully_qualified_address();
let is_64_bit = self.is_64_bit();
let interface = self.memory.get_arm_debug_interface()?;
if is_64_bit {
interface.write_raw_ap_register(&ap, TAR2::ADDRESS, (address >> 32) as u32)?;
}
interface.write_raw_ap_register(&ap, TAR::ADDRESS, address as u32)?;
Ok(BankedAccess {
interface,
ap,
dtrtx: BD0::ADDRESS,
itr: BD1::ADDRESS,
dscr: BD2::ADDRESS,
dtrrx: BD3::ADDRESS,
})
}
}
pub(crate) fn request_halt(
memory: &mut dyn ArmMemoryInterface,
base_address: u64,
) -> Result<(), ArmError> {
let address = Dbgdrcr::get_mmio_address_from_base(base_address)?;
let mut value = Dbgdrcr(0);
value.set_hrq(true);
memory.write_word_32(address, value.into())?;
Ok(())
}
pub(crate) fn run(memory: &mut dyn ArmMemoryInterface, base_address: u64) -> Result<(), ArmError> {
let address = Dbgdrcr::get_mmio_address_from_base(base_address)?;
let mut value = Dbgdrcr(0);
value.set_rrq(true);
memory.write_word_32(address, value.into())?;
let address = Dbgdscr::get_mmio_address_from_base(base_address)?;
let start = Instant::now();
loop {
let dbgdscr = Dbgdscr(memory.read_word_32(address)?);
if dbgdscr.restarted() {
return Ok(());
}
if start.elapsed() > OPERATION_TIMEOUT {
return Err(ArmError::Timeout);
}
}
}
pub(crate) fn wait_for_core_halted(
memory: &mut dyn ArmMemoryInterface,
base_address: u64,
timeout: Duration,
) -> Result<(), ArmError> {
let start = Instant::now();
while !core_halted(memory, base_address)? {
if start.elapsed() >= timeout {
return Err(ArmError::Timeout);
}
std::thread::sleep(Duration::from_millis(1));
}
Ok(())
}
fn core_halted(memory: &mut dyn ArmMemoryInterface, base_address: u64) -> Result<bool, ArmError> {
let address = Dbgdscr::get_mmio_address_from_base(base_address)?;
let dbgdscr = Dbgdscr(memory.read_word_32(address)?);
Ok(dbgdscr.halted())
}
pub(crate) fn set_hw_breakpoint(
memory: &mut dyn ArmMemoryInterface,
base_address: u64,
bp_unit_index: usize,
addr: u32,
) -> Result<(), ArmError> {
let bp_value_addr = Dbgbvr::get_mmio_address_from_base(base_address)?
+ (bp_unit_index * size_of::<u32>()) as u64;
let bp_control_addr = Dbgbcr::get_mmio_address_from_base(base_address)?
+ (bp_unit_index * size_of::<u32>()) as u64;
let mut bp_control = Dbgbcr(0);
bp_control.set_bt(0b0000);
bp_control.set_hmc(true);
bp_control.set_pmc(0b11);
bp_control.set_bas(0b1111);
bp_control.set_e(true);
memory.write_word_32(bp_value_addr, addr)?;
memory.write_word_32(bp_control_addr, bp_control.into())?;
Ok(())
}
pub(crate) fn clear_hw_breakpoint(
memory: &mut dyn ArmMemoryInterface,
base_address: u64,
bp_unit_index: usize,
) -> Result<(), ArmError> {
let bp_value_addr = Dbgbvr::get_mmio_address_from_base(base_address)?
+ (bp_unit_index * size_of::<u32>()) as u64;
let bp_control_addr = Dbgbcr::get_mmio_address_from_base(base_address)?
+ (bp_unit_index * size_of::<u32>()) as u64;
memory.write_word_32(bp_value_addr, 0)?;
memory.write_word_32(bp_control_addr, 0)?;
Ok(())
}
pub(crate) fn get_hw_breakpoint(
memory: &mut dyn ArmMemoryInterface,
base_address: u64,
bp_unit_index: usize,
) -> Result<Option<u32>, ArmError> {
let bp_value_addr = Dbgbvr::get_mmio_address_from_base(base_address)?
+ (bp_unit_index * size_of::<u32>()) as u64;
let bp_value = memory.read_word_32(bp_value_addr)?;
let bp_control_addr = Dbgbcr::get_mmio_address_from_base(base_address)?
+ (bp_unit_index * size_of::<u32>()) as u64;
let bp_control = Dbgbcr(memory.read_word_32(bp_control_addr)?);
Ok(if bp_control.e() { Some(bp_value) } else { None })
}
fn check_and_clear_data_abort(
memory: &mut dyn ArmMemoryInterface,
base_address: u64,
dbgdscr: Dbgdscr,
) -> Result<(), ArmError> {
if dbgdscr.adabort_l() || dbgdscr.sdabort_l() || dbgdscr.und_l() {
let address = Dbgdrcr::get_mmio_address_from_base(base_address)?;
let mut dbgdrcr = Dbgdrcr(0);
dbgdrcr.set_cse(true);
memory.write_word_32(address, dbgdrcr.into())?;
return Err(ArmError::Armv7a(
crate::architecture::arm::armv7a::Armv7aError::DataAbort,
));
}
Ok(())
}
fn execute_instruction(
memory: &mut dyn ArmMemoryInterface,
base_address: u64,
instruction: u32,
) -> Result<Dbgdscr, ArmError> {
let address = Dbgitr::get_mmio_address_from_base(base_address)?;
memory.write_word_32(address, instruction)?;
let address = Dbgdscr::get_mmio_address_from_base(base_address)?;
let mut dbgdscr = Dbgdscr(memory.read_word_32(address)?);
let start = Instant::now();
while !dbgdscr.instrcoml_l() {
dbgdscr = Dbgdscr(memory.read_word_32(address)?);
check_and_clear_data_abort(memory, base_address, dbgdscr)?;
if start.elapsed() >= OPERATION_TIMEOUT {
return Err(ArmError::Timeout);
}
}
check_and_clear_data_abort(memory, base_address, dbgdscr)?;
Ok(dbgdscr)
}
fn set_instruction_input(
memory: &mut dyn ArmMemoryInterface,
base_address: u64,
value: u32,
) -> Result<(), ArmError> {
let address = Dbgdtrrx::get_mmio_address_from_base(base_address)?;
memory.write_word_32(address, value)?;
let address = Dbgdscr::get_mmio_address_from_base(base_address)?;
let mut dbgdscr = Dbgdscr(memory.read_word_32(address)?);
let start = Instant::now();
while !dbgdscr.rxfull_l() {
dbgdscr = Dbgdscr(memory.read_word_32(address)?);
check_and_clear_data_abort(memory, base_address, dbgdscr)?;
if start.elapsed() >= OPERATION_TIMEOUT {
return Err(ArmError::Timeout);
}
}
Ok(())
}
fn get_instruction_result(
memory: &mut dyn ArmMemoryInterface,
base_address: u64,
) -> Result<u32, ArmError> {
let address = Dbgdscr::get_mmio_address_from_base(base_address)?;
let start = Instant::now();
loop {
let dbgdscr = Dbgdscr(memory.read_word_32(address)?);
if dbgdscr.txfull_l() {
break;
}
if start.elapsed() > OPERATION_TIMEOUT {
return Err(ArmError::Timeout);
}
}
let address = Dbgdtrtx::get_mmio_address_from_base(base_address)?;
memory.read_word_32(address)
}
pub(crate) fn write_word_32(
memory: &mut dyn ArmMemoryInterface,
base_address: u64,
address: u32,
data: u32,
) -> Result<(), ArmError> {
set_instruction_input(memory, base_address, address)?;
execute_instruction(memory, base_address, build_mrc(14, 0, 0, 0, 5, 0))?;
set_instruction_input(memory, base_address, data)?;
execute_instruction(memory, base_address, build_stc(14, 5, 0, 4))?;
Ok(())
}
pub(crate) fn read_word_32(
memory: &mut dyn ArmMemoryInterface,
base_address: u64,
address: u32,
) -> Result<u32, ArmError> {
set_instruction_input(memory, base_address, address)?;
execute_instruction(memory, base_address, build_mrc(14, 0, 0, 0, 5, 0))?;
execute_instruction(memory, base_address, build_ldc(14, 5, 0, 4))?;
get_instruction_result(memory, base_address)
}
impl CoreInterface for Armv7a<'_> {
fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), Error> {
wait_for_core_halted(&mut *self.memory, self.base_address, timeout).map_err(|e| e.into())
}
fn core_halted(&mut self) -> Result<bool, Error> {
core_halted(&mut *self.memory, self.base_address).map_err(|e| e.into())
}
fn status(&mut self) -> Result<crate::core::CoreStatus, Error> {
let address = Dbgdscr::get_mmio_address_from_base(self.base_address)?;
let dbgdscr = Dbgdscr(self.memory.read_word_32(address)?);
if dbgdscr.halted() {
let reason = dbgdscr.halt_reason();
self.set_core_status(CoreStatus::Halted(reason));
self.read_fp_reg_count()?;
return Ok(CoreStatus::Halted(reason));
}
if self.state.current_state.is_halted() {
tracing::warn!("Core is running, but we expected it to be halted");
}
self.set_core_status(CoreStatus::Running);
Ok(CoreStatus::Running)
}
fn halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
if !matches!(self.state.current_state, CoreStatus::Halted(_)) {
request_halt(&mut *self.memory, self.base_address)?;
self.wait_for_core_halted(timeout)?;
self.reset_register_cache();
}
let _ = self.status()?;
let pc_value = self.read_core_reg(self.program_counter().into())?;
Ok(CoreInformation {
pc: pc_value.try_into()?,
})
}
fn run(&mut self) -> Result<(), Error> {
if matches!(self.state.current_state, CoreStatus::Running) {
return Ok(());
}
self.writeback_registers()?;
if self.itr_enabled {
let address = Dbgdscr::get_mmio_address_from_base(self.base_address)?;
let mut dbgdscr = Dbgdscr(self.memory.read_word_32(address)?);
dbgdscr.set_itren(false);
self.memory.write_word_32(address, dbgdscr.into())?;
self.itr_enabled = false;
}
run(&mut *self.memory, self.base_address)?;
self.set_core_status(CoreStatus::Running);
let _ = self.status()?;
Ok(())
}
fn reset(&mut self) -> Result<(), Error> {
self.sequence.reset_system(
&mut *self.memory,
crate::CoreType::Armv7a,
Some(self.base_address),
)?;
self.reset_register_cache();
self.set_core_status(CoreStatus::Running);
let _ = self.status()?;
Ok(())
}
fn reset_and_halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
self.sequence.reset_catch_set(
&mut *self.memory,
crate::CoreType::Armv7a,
Some(self.base_address),
)?;
self.sequence.reset_system(
&mut *self.memory,
crate::CoreType::Armv7a,
Some(self.base_address),
)?;
if !self.core_halted()? {
tracing::warn!("Core not halted after reset, platform-specific setup may be required");
tracing::warn!("Requesting halt anyway, but system may already be initialised");
let address = Dbgdrcr::get_mmio_address_from_base(self.base_address)?;
let mut value = Dbgdrcr(0);
value.set_hrq(true);
self.memory.write_word_32(address, value.into())?;
}
self.sequence.reset_catch_clear(
&mut *self.memory,
crate::CoreType::Armv7a,
Some(self.base_address),
)?;
self.wait_for_core_halted(timeout)?;
let _ = self.status()?;
self.reset_register_cache();
let pc_value = self.read_core_reg(self.program_counter().into())?;
Ok(CoreInformation {
pc: pc_value.try_into()?,
})
}
fn step(&mut self) -> Result<CoreInformation, Error> {
let bp_unit_index = (self.available_breakpoint_units()? - 1) as usize;
let bp_value_addr = Dbgbvr::get_mmio_address_from_base(self.base_address)?
+ (bp_unit_index * size_of::<u32>()) as u64;
let saved_bp_value = self.memory.read_word_32(bp_value_addr)?;
let bp_control_addr = Dbgbcr::get_mmio_address_from_base(self.base_address)?
+ (bp_unit_index * size_of::<u32>()) as u64;
let saved_bp_control = self.memory.read_word_32(bp_control_addr)?;
let current_pc: u32 = self
.read_core_reg(self.program_counter().into())?
.try_into()?;
let mut bp_control = Dbgbcr(0);
bp_control.set_bt(0b0100);
bp_control.set_hmc(true);
bp_control.set_pmc(0b11);
bp_control.set_bas(0b1111);
bp_control.set_e(true);
self.memory.write_word_32(bp_value_addr, current_pc)?;
self.memory
.write_word_32(bp_control_addr, bp_control.into())?;
self.run()?;
self.wait_for_core_halted(Duration::from_millis(100))?;
self.memory.write_word_32(bp_value_addr, saved_bp_value)?;
self.memory
.write_word_32(bp_control_addr, saved_bp_control)?;
let pc_value = self.read_core_reg(self.program_counter().into())?;
Ok(CoreInformation {
pc: pc_value.try_into()?,
})
}
fn read_core_reg(&mut self, address: RegisterId) -> Result<RegisterValue, Error> {
let reg_num = address.0;
if (reg_num as usize) < self.state.register_cache.len()
&& let Some(cached_result) = self.state.register_cache[reg_num as usize]
{
return Ok(cached_result.0);
}
let result: Result<RegisterValue, Error> = match reg_num {
0..=14 => {
let instruction = build_mcr(14, 0, reg_num, 0, 5, 0);
let val = self.execute_instruction_with_result(instruction)?;
Ok(val.into())
}
15 => {
self.prepare_r0_for_clobber()?;
let instruction = build_mov(0, 15);
self.execute_instruction(instruction)?;
let instruction = build_mcr(14, 0, 0, 0, 5, 0);
let pra_plus_offset = self.execute_instruction_with_result(instruction)?;
Ok((pra_plus_offset - 8).into())
}
16 => {
self.prepare_r0_for_clobber()?;
let instruction = build_mrs(0);
self.execute_instruction(instruction)?;
let instruction = build_mcr(14, 0, 0, 0, 5, 0);
let cpsr = self.execute_instruction_with_result(instruction)?;
Ok(cpsr.into())
}
17..=48 => {
self.prepare_for_clobber(0)?;
self.prepare_for_clobber(1)?;
let fpexc: u32 = self.read_core_reg(50.into())?.try_into()?;
if (fpexc & (1 << 30)) == 0 {
return Ok(0u32.into());
}
let instruction = build_vmov(1, 0, 1, reg_num - 17);
self.execute_instruction(instruction)?;
let instruction = build_mcr(14, 0, 0, 0, 5, 0);
let mut value = self.execute_instruction_with_result(instruction)? as u64;
let instruction = build_mcr(14, 0, 1, 0, 5, 0);
value |= (self.execute_instruction_with_result(instruction)? as u64) << 32;
Ok(value.into())
}
49 => {
self.prepare_for_clobber(0)?;
let fpexc: u32 = self.read_core_reg(50.into())?.try_into()?;
if (fpexc & (1 << 30)) == 0 {
return Ok(0u32.into());
}
let instruction = build_vmrs(0, 1);
self.execute_instruction(instruction)?;
let instruction = build_mcr(14, 0, 0, 0, 5, 0);
let value = self.execute_instruction_with_result(instruction)?;
Ok(value.into())
}
50 => {
self.prepare_for_clobber(0)?;
let instruction = build_vmrs(0, 0b1000);
self.execute_instruction(instruction)?;
let instruction = build_mcr(14, 0, 0, 0, 5, 0);
let value = self.execute_instruction_with_result(instruction)?;
Ok(value.into())
}
_ => Err(Error::Arm(
Armv7aError::InvalidRegisterNumber(reg_num).into(),
)),
};
if let Ok(value) = result {
self.state.register_cache[reg_num as usize] = Some((value, false));
Ok(value)
} else {
Err(result.err().unwrap())
}
}
fn write_core_reg(&mut self, address: RegisterId, value: RegisterValue) -> Result<(), Error> {
let reg_num = address.0;
if (reg_num as usize) >= self.state.register_cache.len() {
return Err(Error::Arm(
Armv7aError::InvalidRegisterNumber(reg_num).into(),
));
}
self.state.register_cache[reg_num as usize] = Some((value, true));
Ok(())
}
fn available_breakpoint_units(&mut self) -> Result<u32, Error> {
if self.num_breakpoints.is_none() {
let address = Dbgdidr::get_mmio_address_from_base(self.base_address)?;
let dbgdidr = Dbgdidr(self.memory.read_word_32(address)?);
self.num_breakpoints = Some(dbgdidr.brps() + 1);
}
Ok(self.num_breakpoints.unwrap())
}
fn hw_breakpoints(&mut self) -> Result<Vec<Option<u64>>, Error> {
let mut breakpoints = vec![];
let num_hw_breakpoints = self.available_breakpoint_units()? as usize;
for bp_unit_index in 0..num_hw_breakpoints {
let bp_value_addr = Dbgbvr::get_mmio_address_from_base(self.base_address)?
+ (bp_unit_index * size_of::<u32>()) as u64;
let bp_value = self.memory.read_word_32(bp_value_addr)?;
let bp_control_addr = Dbgbcr::get_mmio_address_from_base(self.base_address)?
+ (bp_unit_index * size_of::<u32>()) as u64;
let bp_control = Dbgbcr(self.memory.read_word_32(bp_control_addr)?);
if bp_control.e() {
breakpoints.push(Some(bp_value as u64));
} else {
breakpoints.push(None);
}
}
Ok(breakpoints)
}
fn enable_breakpoints(&mut self, _state: bool) -> Result<(), Error> {
Ok(())
}
fn set_hw_breakpoint(&mut self, bp_unit_index: usize, addr: u64) -> Result<(), Error> {
let addr = valid_32bit_address(addr)?;
set_hw_breakpoint(&mut *self.memory, self.base_address, bp_unit_index, addr)?;
Ok(())
}
fn clear_hw_breakpoint(&mut self, bp_unit_index: usize) -> Result<(), Error> {
clear_hw_breakpoint(&mut *self.memory, self.base_address, bp_unit_index)?;
Ok(())
}
fn registers(&self) -> &'static CoreRegisters {
match self.state.fp_reg_count {
16 => &AARCH32_WITH_FP_16_CORE_REGISTERS,
32 => &AARCH32_WITH_FP_32_CORE_REGISTERS,
_ => &AARCH32_CORE_REGISTERS,
}
}
fn program_counter(&self) -> &'static CoreRegister {
&PC
}
fn frame_pointer(&self) -> &'static CoreRegister {
&FP
}
fn stack_pointer(&self) -> &'static CoreRegister {
&SP
}
fn return_address(&self) -> &'static CoreRegister {
&RA
}
fn hw_breakpoints_enabled(&self) -> bool {
true
}
fn architecture(&self) -> Architecture {
Architecture::Arm
}
fn core_type(&self) -> CoreType {
CoreType::Armv7a
}
fn instruction_set(&mut self) -> Result<InstructionSet, Error> {
let cpsr: u32 = self.read_core_reg(XPSR.id())?.try_into()?;
match (cpsr >> 5) & 1 {
1 => Ok(InstructionSet::Thumb2),
_ => Ok(InstructionSet::A32),
}
}
fn endianness(&mut self) -> Result<Endian, Error> {
if let Some(endianness) = self.endianness {
return Ok(endianness);
}
self.halted_access(|core| {
let endianness = {
let psr = TryInto::<u32>::try_into(core.read_core_reg(XPSR.id())?).unwrap();
if psr & 1 << 9 == 0 {
Endian::Little
} else {
Endian::Big
}
};
core.endianness = Some(endianness);
Ok(endianness)
})
}
fn fpu_support(&mut self) -> Result<bool, Error> {
Ok(self.state.fp_reg_count != 0)
}
fn floating_point_register_count(&mut self) -> Result<usize, Error> {
Ok(self.state.fp_reg_count)
}
#[tracing::instrument(skip(self))]
fn reset_catch_set(&mut self) -> Result<(), Error> {
self.halted_access(|core| {
core.sequence.reset_catch_set(
&mut *core.memory,
CoreType::Armv7a,
Some(core.base_address),
)?;
Ok(())
})?;
Ok(())
}
#[tracing::instrument(skip(self))]
fn reset_catch_clear(&mut self) -> Result<(), Error> {
self.halted_access(|core| {
core.sequence.reset_catch_clear(
&mut *core.memory,
CoreType::Armv7a,
Some(core.base_address),
)?;
Ok(())
})?;
Ok(())
}
#[tracing::instrument(skip(self))]
fn debug_core_stop(&mut self) -> Result<(), Error> {
if matches!(self.state.current_state, CoreStatus::Halted(_)) {
self.writeback_registers()?;
}
self.sequence
.debug_core_stop(&mut *self.memory, CoreType::Armv7a)?;
Ok(())
}
}
impl MemoryInterface for Armv7a<'_> {
fn supports_native_64bit_access(&mut self) -> bool {
false
}
fn read_word_64(&mut self, address: u64) -> Result<u64, Error> {
self.halted_access(|core| {
#[repr(align(4))]
struct AlignedBytes([u8; 8]);
let mut bytes = AlignedBytes([0u8; 8]);
core.read(address, &mut bytes.0)?;
let ret = match core.endianness()? {
Endian::Little => u64::from_le_bytes(bytes.0),
Endian::Big => u64::from_be_bytes(bytes.0),
};
Ok(ret)
})
}
fn read_word_32(&mut self, address: u64) -> Result<u32, Error> {
self.halted_access(|core| {
let address = valid_32bit_address(address)?;
let instr = build_ldc(14, 5, 0, 4);
core.prepare_r0_for_clobber()?;
core.set_r0(address)?;
core.execute_instruction_with_result(instr)
})
}
fn read_word_16(&mut self, address: u64) -> Result<u16, Error> {
self.halted_access(|core| {
let mut byte_offset = address % 4;
let word_start = address - byte_offset;
let data = core.read_word_32(word_start)?;
if Endian::Big == core.endianness()? {
if address & 1 != 0 {
return Err(Error::MemoryNotAligned(MemoryNotAlignedError {
address,
alignment: 2,
}));
}
byte_offset = 2 - byte_offset;
}
Ok((data >> (byte_offset * 8)) as u16)
})
}
fn read_word_8(&mut self, address: u64) -> Result<u8, Error> {
self.halted_access(|core| {
let mut byte_offset = address % 4;
let word_start = address - byte_offset;
let data = core.read_word_32(word_start)?;
if Endian::Big == core.endianness()? {
byte_offset = 3 - byte_offset;
}
Ok(data.to_le_bytes()[byte_offset as usize])
})
}
fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), Error> {
self.halted_access(|core| {
for (i, word) in data.iter_mut().enumerate() {
*word = core.read_word_64(address + ((i as u64) * 8))?;
}
Ok(())
})
}
fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), Error> {
self.halted_access(|core| {
let count = data.len();
if count > 2 {
core.prepare_r0_for_clobber()?;
core.set_r0(valid_32bit_address(address)?)?;
let mut banked = core.banked_access()?;
if banked
.with_dcc_fast_mode(|banked| {
banked.set_itr(build_ldc(14, 5, 0, 4))?;
let _ = banked.dtrrx()?;
for word in data[0..count - 1].iter_mut() {
*word = banked.dtrrx()?;
}
Ok(())
})
.is_ok()
{
if let Ok(last) = banked.dtrrx()
&& let Some(v) = data.last_mut()
{
*v = last;
}
}
let dscr = banked.dscr()?;
check_and_clear_data_abort(&mut *core.memory, core.base_address, dscr)?;
} else {
for (i, word) in data.iter_mut().enumerate() {
*word = core.read_word_32(address + ((i as u64) * 4))?;
}
}
Ok(())
})
}
fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), Error> {
self.halted_access(|core| {
for (i, word) in data.iter_mut().enumerate() {
*word = core.read_word_16(address + ((i as u64) * 2))?;
}
Ok(())
})
}
fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
self.read(address, data)
}
fn read(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
self.halted_access(|core| {
if address.is_multiple_of(4) && data.len().is_multiple_of(4) {
if let Ok((aligned_buffer, _)) =
<[u32]>::mut_from_prefix_with_elems(data, data.len() / 4)
{
core.read_32(address, aligned_buffer)?;
} else {
let mut temporary_buffer = vec![0u32; data.len() / 4];
core.read_32(address, &mut temporary_buffer)?;
data.copy_from_slice(temporary_buffer.as_bytes());
}
if core.endianness()? == Endian::Big {
for word in data.chunks_exact_mut(4) {
word.reverse();
}
}
} else {
let start_address = address & !3;
let end_address = address + (data.len() as u64);
let end_address = end_address + (4 - (end_address & 3));
let start_extra_count = address as usize % 4;
let mut buffer = vec![0u32; (end_address - start_address) as usize / 4];
core.read_32(start_address, &mut buffer)?;
if core.endianness()? == Endian::Big {
for word in buffer.iter_mut() {
*word = word.swap_bytes();
}
}
data.copy_from_slice(
&buffer.as_bytes()[start_extra_count..start_extra_count + data.len()],
);
}
Ok(())
})
}
fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), Error> {
self.halted_access(|core| {
let (data_low, data_high) = match core.endianness()? {
Endian::Little => (data as u32, (data >> 32) as u32),
Endian::Big => ((data >> 32) as u32, data as u32),
};
core.write_32(address, &[data_low, data_high])
})
}
fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), Error> {
self.halted_access(|core| {
let address = valid_32bit_address(address)?;
let instr = build_stc(14, 5, 0, 4);
core.prepare_r0_for_clobber()?;
core.set_r0(address)?;
core.execute_instruction_with_input(instr, data)
})
}
fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), Error> {
self.halted_access(|core| {
let mut byte_offset = address % 4;
let word_start = address - byte_offset;
if Endian::Big == core.endianness()? {
byte_offset = 3 - byte_offset;
}
let current_word = core.read_word_32(word_start)?;
let mut word_bytes = current_word.to_le_bytes();
word_bytes[byte_offset as usize] = data;
core.write_word_32(word_start, u32::from_le_bytes(word_bytes))
})
}
fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), Error> {
self.halted_access(|core| {
let mut byte_offset = address % 4;
let word_start = address - byte_offset;
if Endian::Big == core.endianness()? {
if address & 1 != 0 {
return Err(Error::MemoryNotAligned(MemoryNotAlignedError {
address,
alignment: 2,
}));
}
byte_offset = 2 - byte_offset;
}
let mut word = core.read_word_32(word_start)?;
word &= !(0xFFFFu32 << (byte_offset * 8));
word |= (data as u32) << (byte_offset * 8);
core.write_word_32(word_start, word)
})
}
fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), Error> {
self.halted_access(|core| {
for (i, word) in data.iter().enumerate() {
core.write_word_64(address + ((i as u64) * 8), *word)?;
}
Ok(())
})
}
fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), Error> {
self.halted_access(|core| {
if data.len() > 2 {
core.prepare_r0_for_clobber()?;
core.set_r0(valid_32bit_address(address)?)?;
let mut banked = core.banked_access()?;
banked
.with_dcc_fast_mode(|banked| {
banked.set_itr(build_stc(14, 5, 0, 4))?;
for word in data.iter() {
banked.set_dtrtx(*word)?;
}
Ok(())
})
.ok();
let dscr = banked.dscr()?;
check_and_clear_data_abort(&mut *core.memory, core.base_address, dscr)?;
} else {
for (i, word) in data.iter().enumerate() {
core.write_word_32(address + ((i as u64) * 4), *word)?;
}
}
Ok(())
})
}
fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), Error> {
self.halted_access(|core| {
for (i, word) in data.iter().enumerate() {
core.write_word_16(address + ((i as u64) * 2), *word)?;
}
Ok(())
})
}
fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
self.write(address, data)
}
fn write(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
self.halted_access(|core| {
let len = data.len();
let start_extra_count = ((4 - (address % 4) as usize) % 4).min(len);
let end_extra_count = (len - start_extra_count) % 4;
assert!(start_extra_count < 4);
assert!(end_extra_count < 4);
if start_extra_count != 0 || end_extra_count != 0 {
for (i, byte) in data.iter().enumerate() {
core.write_word_8(address + (i as u64), *byte)?;
}
return Ok(());
}
let mut buffer = vec![0u32; data.len() / 4];
let endianness = core.endianness()?;
for (bytes, value) in data.chunks_exact(4).zip(buffer.iter_mut()) {
*value = match endianness {
Endian::Little => u32::from_le_bytes(bytes.try_into().unwrap()),
Endian::Big => u32::from_be_bytes(bytes.try_into().unwrap()),
}
}
core.write_32(address, &buffer)?;
Ok(())
})
}
fn supports_8bit_transfers(&self) -> Result<bool, Error> {
Ok(false)
}
fn flush(&mut self) -> Result<(), Error> {
Ok(())
}
}
#[cfg(test)]
mod test {
use crate::{
architecture::arm::{
FullyQualifiedApAddress, communication_interface::SwdSequence,
sequences::DefaultArmSequence,
},
probe::DebugProbeError,
};
use super::*;
const TEST_BASE_ADDRESS: u64 = 0x8000_1000;
fn address_to_reg_num(address: u64) -> u32 {
((address - TEST_BASE_ADDRESS) / 4) as u32
}
#[derive(Debug)]
pub struct ExpectedMemoryOp {
read: bool,
address: u64,
value: u32,
}
pub struct MockProbe {
expected_ops: Vec<ExpectedMemoryOp>,
}
impl MockProbe {
pub fn new() -> Self {
MockProbe {
expected_ops: vec![],
}
}
pub fn expected_read(&mut self, addr: u64, value: u32) {
self.expected_ops.push(ExpectedMemoryOp {
read: true,
address: addr,
value,
});
}
pub fn expected_write(&mut self, addr: u64, value: u32) {
self.expected_ops.push(ExpectedMemoryOp {
read: false,
address: addr,
value,
});
}
}
impl MemoryInterface<ArmError> for MockProbe {
fn read_8(&mut self, _address: u64, _data: &mut [u8]) -> Result<(), ArmError> {
todo!()
}
fn read_16(&mut self, _address: u64, _data: &mut [u16]) -> Result<(), ArmError> {
todo!()
}
fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), ArmError> {
if self.expected_ops.is_empty() {
panic!(
"Received unexpected read_32 op: register {:#}",
address_to_reg_num(address)
);
}
assert_eq!(data.len(), 1);
let expected_op = self.expected_ops.remove(0);
assert!(
expected_op.read,
"R/W mismatch for register: Expected {:#} Actual: {:#}",
address_to_reg_num(expected_op.address),
address_to_reg_num(address)
);
assert_eq!(
expected_op.address,
address,
"Read from unexpected register: Expected {:#} Actual: {:#}",
address_to_reg_num(expected_op.address),
address_to_reg_num(address)
);
data[0] = expected_op.value;
Ok(())
}
fn read(&mut self, address: u64, data: &mut [u8]) -> Result<(), ArmError> {
self.read_8(address, data)
}
fn write_8(&mut self, _address: u64, _data: &[u8]) -> Result<(), ArmError> {
todo!()
}
fn write_16(&mut self, _address: u64, _data: &[u16]) -> Result<(), ArmError> {
todo!()
}
fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), ArmError> {
if self.expected_ops.is_empty() {
panic!(
"Received unexpected write_32 op: register {:#}",
address_to_reg_num(address)
);
}
assert_eq!(data.len(), 1);
let expected_op = self.expected_ops.remove(0);
assert!(
!expected_op.read,
"Read/write mismatch on register: {:#}",
address_to_reg_num(address)
);
assert_eq!(
expected_op.address,
address,
"Write to unexpected register: Expected {:#} Actual: {:#}",
address_to_reg_num(expected_op.address),
address_to_reg_num(address)
);
assert_eq!(
expected_op.value, data[0],
"Write value mismatch Expected {:#X} Actual: {:#X}",
expected_op.value, data[0]
);
Ok(())
}
fn write(&mut self, address: u64, data: &[u8]) -> Result<(), ArmError> {
self.write_8(address, data)
}
fn flush(&mut self) -> Result<(), ArmError> {
todo!()
}
fn read_64(&mut self, _address: u64, _data: &mut [u64]) -> Result<(), ArmError> {
todo!()
}
fn write_64(&mut self, _address: u64, _data: &[u64]) -> Result<(), ArmError> {
todo!()
}
fn supports_8bit_transfers(&self) -> Result<bool, ArmError> {
Ok(false)
}
fn supports_native_64bit_access(&mut self) -> bool {
false
}
}
impl ArmMemoryInterface for MockProbe {
fn fully_qualified_address(&self) -> FullyQualifiedApAddress {
todo!()
}
fn get_arm_debug_interface(
&mut self,
) -> Result<&mut dyn crate::architecture::arm::ArmDebugInterface, DebugProbeError> {
Err(DebugProbeError::NotImplemented {
function_name: "get_arm_debug_interface",
})
}
fn generic_status(&mut self) -> Result<crate::architecture::arm::ap::CSW, ArmError> {
Err(ArmError::Probe(DebugProbeError::NotImplemented {
function_name: "generic_status",
}))
}
fn base_address(&mut self) -> Result<u64, ArmError> {
todo!()
}
}
impl SwdSequence for MockProbe {
fn swj_sequence(&mut self, _bit_len: u8, _bits: u64) -> Result<(), DebugProbeError> {
todo!()
}
fn swj_pins(
&mut self,
_pin_out: u32,
_pin_select: u32,
_pin_wait: u32,
) -> Result<u32, DebugProbeError> {
todo!()
}
}
fn add_status_expectations(probe: &mut MockProbe, halted: bool) {
let mut dbgdscr = Dbgdscr(0);
dbgdscr.set_halted(halted);
dbgdscr.set_restarted(true);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
}
fn add_enable_itr_expectations(probe: &mut MockProbe) {
let mut dbgdscr = Dbgdscr(0);
dbgdscr.set_halted(true);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
dbgdscr.set_itren(true);
probe.expected_write(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
}
fn add_read_reg_expectations(probe: &mut MockProbe, reg: u16, value: u32) {
probe.expected_write(
Dbgitr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
build_mcr(14, 0, reg, 0, 5, 0),
);
let mut dbgdscr = Dbgdscr(0);
dbgdscr.set_instrcoml_l(true);
dbgdscr.set_txfull_l(true);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
probe.expected_read(
Dbgdtrtx::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
value,
);
}
fn add_read_pc_expectations(probe: &mut MockProbe, value: u32) {
let mut dbgdscr = Dbgdscr(0);
dbgdscr.set_instrcoml_l(true);
dbgdscr.set_txfull_l(true);
probe.expected_write(
Dbgitr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
build_mov(0, 15),
);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
add_read_reg_expectations(probe, 0, value + 8);
}
fn add_read_fp_count_expectations(probe: &mut MockProbe) {
let mut dbgdscr = Dbgdscr(0);
dbgdscr.set_instrcoml_l(true);
dbgdscr.set_txfull_l(true);
let mut cpacr = Cpacr(0);
cpacr.set_cp(10, 0b11);
cpacr.set_cp(11, 0b11);
probe.expected_write(
Dbgitr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
build_mrc(15, 0, 0, 1, 0, 2),
);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
add_read_reg_expectations(probe, 0, cpacr.0);
probe.expected_write(
Dbgitr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
build_vmrs(0, 0b0111),
);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
add_read_reg_expectations(probe, 0, 0b010);
}
fn add_read_cpsr_expectations(probe: &mut MockProbe, value: u32) {
let mut dbgdscr = Dbgdscr(0);
dbgdscr.set_instrcoml_l(true);
dbgdscr.set_txfull_l(true);
probe.expected_write(
Dbgitr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
build_mrs(0),
);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
add_read_reg_expectations(probe, 0, value);
}
fn add_idr_expectations(probe: &mut MockProbe, bp_count: u32) {
let mut dbgdidr = Dbgdidr(0);
dbgdidr.set_brps(bp_count - 1);
probe.expected_read(
Dbgdidr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdidr.into(),
);
}
fn add_set_r0_expectation(probe: &mut MockProbe, value: u32) {
let mut dbgdscr = Dbgdscr(0);
dbgdscr.set_instrcoml_l(true);
dbgdscr.set_rxfull_l(true);
probe.expected_write(
Dbgdtrrx::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
value,
);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
probe.expected_write(
Dbgitr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
build_mrc(14, 0, 0, 0, 5, 0),
);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
}
fn add_read_memory_expectations(probe: &mut MockProbe, address: u64, value: u32) {
add_set_r0_expectation(probe, address as u32);
let mut dbgdscr = Dbgdscr(0);
dbgdscr.set_instrcoml_l(true);
dbgdscr.set_txfull_l(true);
probe.expected_write(
Dbgitr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
build_ldc(14, 5, 0, 4),
);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
probe.expected_read(
Dbgdtrtx::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
value,
);
}
impl Drop for MockProbe {
fn drop(&mut self) {
if !self.expected_ops.is_empty() {
panic!("self.expected_ops is not empty: {:?}", self.expected_ops);
}
}
}
#[test]
fn armv7a_new() {
let mut probe = MockProbe::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
let mock_mem = Box::new(probe) as _;
let _ = Armv7a::new(
mock_mem,
&mut CortexAState::new(),
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
}
#[test]
fn armv7a_core_halted() {
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
let mut dbgdscr = Dbgdscr(0);
dbgdscr.set_halted(false);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
dbgdscr.set_halted(true);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
assert!(!armv7a.core_halted().unwrap());
assert!(armv7a.core_halted().unwrap());
}
#[test]
fn armv7a_wait_for_core_halted() {
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
let mut dbgdscr = Dbgdscr(0);
dbgdscr.set_halted(false);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
dbgdscr.set_halted(true);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
armv7a
.wait_for_core_halted(Duration::from_millis(100))
.unwrap();
}
#[test]
fn armv7a_status_running() {
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
let mut dbgdscr = Dbgdscr(0);
dbgdscr.set_halted(false);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
assert_eq!(CoreStatus::Running, armv7a.status().unwrap());
}
#[test]
fn armv7a_status_halted() {
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
let mut dbgdscr = Dbgdscr(0);
dbgdscr.set_halted(true);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
assert_eq!(
CoreStatus::Halted(crate::HaltReason::Request),
armv7a.status().unwrap()
);
}
#[test]
fn armv7a_read_core_reg_common() {
const REG_VALUE: u32 = 0xABCD;
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 2, REG_VALUE);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
assert_eq!(
RegisterValue::from(REG_VALUE),
armv7a.read_core_reg(RegisterId(2)).unwrap()
);
assert_eq!(
RegisterValue::from(REG_VALUE),
armv7a.read_core_reg(RegisterId(2)).unwrap()
);
}
#[test]
fn armv7a_read_core_reg_pc() {
const REG_VALUE: u32 = 0xABCD;
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
add_read_pc_expectations(&mut probe, REG_VALUE);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
assert_eq!(
RegisterValue::from(REG_VALUE),
armv7a.read_core_reg(RegisterId(15)).unwrap()
);
assert_eq!(
RegisterValue::from(REG_VALUE),
armv7a.read_core_reg(RegisterId(15)).unwrap()
);
}
#[test]
fn armv7a_read_core_reg_cpsr() {
const REG_VALUE: u32 = 0xABCD;
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
add_read_cpsr_expectations(&mut probe, REG_VALUE);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
assert_eq!(
RegisterValue::from(REG_VALUE),
armv7a.read_core_reg(XPSR.id()).unwrap()
);
assert_eq!(
RegisterValue::from(REG_VALUE),
armv7a.read_core_reg(XPSR.id()).unwrap()
);
}
#[test]
fn armv7a_halt() {
const REG_VALUE: u32 = 0xABCD;
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, false);
let mut dbgdrcr = Dbgdrcr(0);
dbgdrcr.set_hrq(true);
probe.expected_write(
Dbgdrcr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdrcr.into(),
);
add_status_expectations(&mut probe, true);
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
add_read_pc_expectations(&mut probe, REG_VALUE);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
assert_eq!(
REG_VALUE as u64,
armv7a.halt(Duration::from_millis(100)).unwrap().pc
);
}
#[test]
fn armv7a_run() {
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
add_set_r0_expectation(&mut probe, 0);
let mut dbgdscr = Dbgdscr(0);
dbgdscr.set_itren(true);
probe.expected_read(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
dbgdscr.set_itren(false);
probe.expected_write(
Dbgdscr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdscr.into(),
);
let mut dbgdrcr = Dbgdrcr(0);
dbgdrcr.set_rrq(true);
probe.expected_write(
Dbgdrcr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgdrcr.into(),
);
add_status_expectations(&mut probe, false);
add_status_expectations(&mut probe, false);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
armv7a.run().unwrap();
}
#[test]
fn armv7a_available_breakpoint_units() {
const BP_COUNT: u32 = 4;
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
add_idr_expectations(&mut probe, BP_COUNT);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
assert_eq!(BP_COUNT, armv7a.available_breakpoint_units().unwrap());
}
#[test]
fn armv7a_hw_breakpoints() {
const BP_COUNT: u32 = 4;
const BP1: u64 = 0x2345;
const BP2: u64 = 0x8000_0000;
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
add_idr_expectations(&mut probe, BP_COUNT);
probe.expected_read(
Dbgbvr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
BP1 as u32,
);
probe.expected_read(
Dbgbcr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
1,
);
probe.expected_read(
Dbgbvr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap() + 4,
BP2 as u32,
);
probe.expected_read(
Dbgbcr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap() + 4,
1,
);
probe.expected_read(
Dbgbvr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap() + (2 * 4),
0,
);
probe.expected_read(
Dbgbcr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap() + (2 * 4),
0,
);
probe.expected_read(
Dbgbvr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap() + (3 * 4),
0,
);
probe.expected_read(
Dbgbcr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap() + (3 * 4),
0,
);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
let results = armv7a.hw_breakpoints().unwrap();
assert_eq!(Some(BP1), results[0]);
assert_eq!(Some(BP2), results[1]);
assert_eq!(None, results[2]);
assert_eq!(None, results[3]);
}
#[test]
fn armv7a_set_hw_breakpoint() {
const BP_VALUE: u64 = 0x2345;
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
let mut dbgbcr = Dbgbcr(0);
dbgbcr.set_hmc(true);
dbgbcr.set_pmc(0b11);
dbgbcr.set_bas(0b1111);
dbgbcr.set_e(true);
probe.expected_write(
Dbgbvr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
BP_VALUE as u32,
);
probe.expected_write(
Dbgbcr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
dbgbcr.into(),
);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
armv7a.set_hw_breakpoint(0, BP_VALUE).unwrap();
}
#[test]
fn armv7a_clear_hw_breakpoint() {
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
probe.expected_write(
Dbgbvr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
0,
);
probe.expected_write(
Dbgbcr::get_mmio_address_from_base(TEST_BASE_ADDRESS).unwrap(),
0,
);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
armv7a.clear_hw_breakpoint(0).unwrap();
}
#[test]
fn armv7a_read_word_32() {
const MEMORY_VALUE: u32 = 0xBA5EBA11;
const MEMORY_ADDRESS: u64 = 0x12345678;
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
add_read_memory_expectations(&mut probe, MEMORY_ADDRESS, MEMORY_VALUE);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
assert_eq!(MEMORY_VALUE, armv7a.read_word_32(MEMORY_ADDRESS).unwrap());
}
fn test_read_word(value: u32, address: u64, memory_word_address: u64, endian: Endian) -> u8 {
let mut probe = MockProbe::new();
let mut state = CortexAState::new();
add_status_expectations(&mut probe, true);
add_enable_itr_expectations(&mut probe);
add_read_reg_expectations(&mut probe, 0, 0);
add_read_fp_count_expectations(&mut probe);
add_read_memory_expectations(&mut probe, memory_word_address, value);
let cpsr = if endian == Endian::Big { 1 << 9 } else { 0 };
add_read_cpsr_expectations(&mut probe, cpsr);
let mock_mem = Box::new(probe) as _;
let mut armv7a = Armv7a::new(
mock_mem,
&mut state,
TEST_BASE_ADDRESS,
DefaultArmSequence::create(),
)
.unwrap();
armv7a.read_word_8(address).unwrap()
}
#[test]
fn armv7a_read_word_8() {
const MEMORY_VALUE: u32 = 0xBA5EBB11;
const MEMORY_ADDRESS: u64 = 0x12345679;
const MEMORY_WORD_ADDRESS: u64 = 0x12345678;
assert_eq!(
0xBB,
test_read_word(
MEMORY_VALUE,
MEMORY_ADDRESS,
MEMORY_WORD_ADDRESS,
Endian::Little
)
);
}
#[test]
fn armv7a_read_word_8_be() {
const MEMORY_VALUE: u32 = 0xBA5EBB11;
const MEMORY_ADDRESS: u64 = 0x12345679;
const MEMORY_WORD_ADDRESS: u64 = 0x12345678;
assert_eq!(
0x5E,
test_read_word(
MEMORY_VALUE,
MEMORY_ADDRESS,
MEMORY_WORD_ADDRESS,
Endian::Big
)
);
}
}