use crate::{
architecture::riscv::sequences::RiscvDebugSequence,
core::{
Architecture, BreakpointCause, CoreInformation, CoreRegisters, RegisterId, RegisterValue,
},
memory::{valid_32bit_address, CoreMemoryInterface},
memory_mapped_bitfield_register,
probe::DebugProbeError,
semihosting::decode_semihosting_syscall,
semihosting::SemihostingCommand,
CoreInterface, CoreRegister, CoreStatus, CoreType, Error, HaltReason, InstructionSet,
MemoryInterface, MemoryMappedRegister,
};
use bitfield::bitfield;
use communication_interface::{AbstractCommandErrorKind, RiscvCommunicationInterface, RiscvError};
use registers::{FP, RA, RISCV_CORE_REGSISTERS, SP};
use std::{
sync::Arc,
time::{Duration, Instant},
};
#[macro_use]
pub(crate) mod registers;
pub use registers::PC;
pub(crate) mod assembly;
pub mod communication_interface;
pub(crate) mod dtm;
pub mod sequences;
pub struct Riscv32<'state> {
interface: RiscvCommunicationInterface<'state>,
state: &'state mut RiscvCoreState,
sequence: Arc<dyn RiscvDebugSequence>,
}
impl<'state> Riscv32<'state> {
pub fn new(
interface: RiscvCommunicationInterface<'state>,
state: &'state mut RiscvCoreState,
sequence: Arc<dyn RiscvDebugSequence>,
) -> Result<Self, RiscvError> {
Ok(Self {
interface,
state,
sequence,
})
}
fn read_csr(&mut self, address: u16) -> Result<u32, RiscvError> {
self.interface.read_csr(address)
}
fn write_csr(&mut self, address: u16, value: u32) -> Result<(), RiscvError> {
tracing::debug!("Writing CSR {:#x}", address);
match self.interface.abstract_cmd_register_write(address, value) {
Err(RiscvError::AbstractCommand(AbstractCommandErrorKind::NotSupported)) => {
tracing::debug!("Could not write core register {:#x} with abstract command, falling back to program buffer", address);
self.interface.write_csr_progbuf(address, value)
}
other => other,
}
}
fn resume_core(&mut self) -> Result<(), crate::Error> {
self.state.semihosting_command = None;
self.interface.resume_core()?;
Ok(())
}
fn check_for_semihosting(&mut self) -> Result<Option<SemihostingCommand>, Error> {
const TRAP_INSTRUCTIONS: [u32; 3] = [
0x01f01013, 0x00100073, 0x40705013, ];
if let Some(command) = self.state.semihosting_command {
return Ok(Some(command));
}
let pc: u32 = self.read_core_reg(self.program_counter().id)?.try_into()?;
let mut actual_instructions = [0u32; 3];
self.read_32((pc - 4) as u64, &mut actual_instructions)?;
let actual_instructions = actual_instructions.as_slice();
tracing::debug!(
"Semihosting check pc={pc:#x} instructions={0:#08x} {1:#08x} {2:#08x}",
actual_instructions[0],
actual_instructions[1],
actual_instructions[2]
);
let command = if TRAP_INSTRUCTIONS == actual_instructions {
Some(decode_semihosting_syscall(self)?)
} else {
None
};
self.state.semihosting_command = command;
Ok(command)
}
fn determine_number_of_hardware_breakpoints(&mut self) -> Result<u32, RiscvError> {
tracing::debug!("Determining number of HW breakpoints supported");
let tselect = 0x7a0;
let tdata1 = 0x7a1;
let tinfo = 0x7a4;
let mut tselect_index = 0;
loop {
tracing::debug!("Trying tselect={}", tselect_index);
if let Err(e) = self.write_csr(tselect, tselect_index) {
match e {
RiscvError::AbstractCommand(AbstractCommandErrorKind::Exception) => break,
other_error => return Err(other_error),
}
}
let readback = self.read_csr(tselect)?;
if readback != tselect_index {
break;
}
match self.read_csr(tinfo) {
Ok(tinfo_val) => {
if tinfo_val & 0xffff == 1 {
break;
} else {
tracing::info!(
"Discovered trigger with index {} and type {}",
tselect_index,
tinfo_val & 0xffff
);
}
}
Err(RiscvError::AbstractCommand(AbstractCommandErrorKind::Exception)) => {
let tdata_val = self.read_csr(tdata1)?;
let misa_value = Misa(self.read_csr(0x301)?);
let xlen = u32::pow(2, misa_value.mxl() + 4);
let trigger_type = tdata_val >> (xlen - 4);
if trigger_type == 0 {
break;
}
tracing::info!(
"Discovered trigger with index {} and type {}",
tselect_index,
trigger_type,
);
}
Err(other) => return Err(other),
}
tselect_index += 1;
}
tracing::debug!("Target supports {} breakpoints.", tselect_index);
Ok(tselect_index)
}
}
impl CoreInterface for Riscv32<'_> {
fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), crate::Error> {
self.interface.wait_for_core_halted(timeout)?;
self.state.pc_written = false;
Ok(())
}
fn core_halted(&mut self) -> Result<bool, crate::Error> {
Ok(self.interface.core_halted()?)
}
fn status(&mut self) -> Result<crate::core::CoreStatus, crate::Error> {
let status: Dmstatus = self.interface.read_dm_register()?;
if status.allhalted() {
let dcsr = Dcsr(self.read_core_reg(RegisterId::from(0x7b0))?.try_into()?);
let reason = match dcsr.cause() {
1 => {
self.state.pc_written = false;
if let Some(cmd) = self.check_for_semihosting()? {
HaltReason::Breakpoint(BreakpointCause::Semihosting(cmd))
} else {
HaltReason::Breakpoint(BreakpointCause::Software)
}
}
2 => HaltReason::Breakpoint(BreakpointCause::Hardware),
3 => HaltReason::Request,
4 => HaltReason::Step,
5 => HaltReason::Exception,
_ => HaltReason::Unknown,
};
Ok(CoreStatus::Halted(reason))
} else if status.allrunning() {
Ok(CoreStatus::Running)
} else {
Err(Error::Other(
"Some cores are running while some are halted, this should not happen.".to_string(),
))
}
}
fn halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
self.interface.halt(timeout)?;
Ok(self.interface.core_info()?)
}
fn run(&mut self) -> Result<(), Error> {
if !self.state.pc_written {
self.step()?;
}
self.resume_core()?;
Ok(())
}
fn reset(&mut self) -> Result<(), Error> {
self.reset_and_halt(Duration::from_secs(1))?;
self.resume_core()?;
Ok(())
}
fn reset_and_halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
self.sequence
.reset_system_and_halt(&mut self.interface, timeout)?;
let pc = self.read_core_reg(RegisterId(0x7b1))?;
Ok(CoreInformation { pc: pc.try_into()? })
}
fn step(&mut self) -> Result<CoreInformation, crate::Error> {
let halt_reason = self.status()?;
if matches!(
halt_reason,
CoreStatus::Halted(HaltReason::Breakpoint(
BreakpointCause::Software | BreakpointCause::Semihosting(_)
))
) {
let mut debug_pc = self.read_core_reg(RegisterId(0x7b1))?;
if matches!(self.instruction_set()?, InstructionSet::RV32C) {
let instruction = self.read_word_32(debug_pc.try_into().unwrap())?;
if instruction & 0x3 != 0x3 {
debug_pc.increment_address(2)?;
} else {
debug_pc.increment_address(4)?;
}
} else {
debug_pc.increment_address(4)?;
}
self.write_core_reg(RegisterId(0x7b1), debug_pc)?;
return Ok(CoreInformation {
pc: debug_pc.try_into()?,
});
} else if matches!(
halt_reason,
CoreStatus::Halted(HaltReason::Breakpoint(BreakpointCause::Hardware))
) {
self.enable_breakpoints(false)?;
}
let mut dcsr = Dcsr(self.read_core_reg(RegisterId(0x7b0))?.try_into()?);
dcsr.set_step(true);
dcsr.set_stepie(false);
dcsr.set_stopcount(true);
self.write_csr(0x7b0, dcsr.0)?;
self.resume_core()?;
self.wait_for_core_halted(Duration::from_millis(100))?;
let pc = self.read_core_reg(RegisterId(0x7b1))?;
let mut dcsr = Dcsr(self.read_core_reg(RegisterId(0x7b0))?.try_into()?);
dcsr.set_step(false);
dcsr.set_stepie(true);
dcsr.set_stopcount(false);
self.write_csr(0x7b0, dcsr.0)?;
if matches!(
halt_reason,
CoreStatus::Halted(HaltReason::Breakpoint(BreakpointCause::Hardware))
) {
self.enable_breakpoints(true)?;
}
self.state.pc_written = false;
Ok(CoreInformation { pc: pc.try_into()? })
}
fn read_core_reg(&mut self, address: RegisterId) -> Result<RegisterValue, crate::Error> {
self.read_csr(address.0)
.map(|v| v.into())
.map_err(|e| e.into())
}
fn write_core_reg(
&mut self,
address: RegisterId,
value: RegisterValue,
) -> Result<(), crate::Error> {
let value: u32 = value.try_into()?;
if address == self.program_counter().id {
self.state.pc_written = true;
}
self.write_csr(address.0, value).map_err(|e| e.into())
}
fn available_breakpoint_units(&mut self) -> Result<u32, crate::Error> {
match self.state.hw_breakpoints {
Some(bp) => Ok(bp),
None => {
let bp = self.determine_number_of_hardware_breakpoints()?;
self.state.hw_breakpoints = Some(bp);
Ok(bp)
}
}
}
fn hw_breakpoints(&mut self) -> Result<Vec<Option<u64>>, Error> {
let was_running = !self.core_halted()?;
if was_running {
self.halt(Duration::from_millis(100))?;
}
let tselect = 0x7a0;
let tdata1 = 0x7a1;
let tdata2 = 0x7a2;
let mut breakpoints = vec![];
let num_hw_breakpoints = self.available_breakpoint_units()? as usize;
for bp_unit_index in 0..num_hw_breakpoints {
self.write_csr(tselect, bp_unit_index as u32)?;
let tdata_value = Mcontrol(self.read_csr(tdata1)?);
tracing::debug!("Breakpoint {}: {:?}", bp_unit_index, tdata_value);
let trigger_any_mode_active = tdata_value.m() || tdata_value.s() || tdata_value.u();
let trigger_any_action_enabled =
tdata_value.execute() || tdata_value.store() || tdata_value.load();
if tdata_value.type_() == 0b10
&& tdata_value.action() == 1
&& tdata_value.match_() == 0
&& trigger_any_mode_active
&& trigger_any_action_enabled
{
let breakpoint = self.read_csr(tdata2)?;
breakpoints.push(Some(breakpoint as u64));
} else {
breakpoints.push(None);
}
}
if was_running {
self.resume_core()?;
}
Ok(breakpoints)
}
fn enable_breakpoints(&mut self, state: bool) -> Result<(), crate::Error> {
let tselect = 0x7a0;
let tdata1 = 0x7a1;
for bp_unit_index in 0..self.available_breakpoint_units()? as usize {
self.write_csr(tselect, bp_unit_index as u32)?;
let mut tdata_value = Mcontrol(self.read_csr(tdata1)?);
if tdata_value.type_() == 2
&& tdata_value.action() == 1
&& tdata_value.match_() == 0
&& tdata_value.execute()
&& ((tdata_value.m() && tdata_value.u()) || (!tdata_value.m() && !tdata_value.u()))
{
tracing::debug!(
"Will modify breakpoint enabled={} for {}: {:?}",
state,
bp_unit_index,
tdata_value
);
tdata_value.set_m(state);
tdata_value.set_u(state);
self.write_csr(tdata1, tdata_value.0)?;
}
}
self.state.hw_breakpoints_enabled = state;
Ok(())
}
fn set_hw_breakpoint(&mut self, bp_unit_index: usize, addr: u64) -> Result<(), crate::Error> {
let addr = valid_32bit_address(addr)?;
let tselect = 0x7a0;
let tdata1 = 0x7a1;
let tdata2 = 0x7a2;
tracing::info!("Setting breakpoint {}", bp_unit_index);
self.write_csr(tselect, bp_unit_index as u32)?;
let tdata_value = Mcontrol(self.read_csr(tdata1)?);
let trigger_type = tdata_value.type_();
if trigger_type != 0b10 {
return Err(RiscvError::UnexpectedTriggerType(trigger_type).into());
}
let mut instruction_breakpoint = Mcontrol(0);
instruction_breakpoint.set_action(1);
instruction_breakpoint.set_type(2);
instruction_breakpoint.set_match(0);
instruction_breakpoint.set_m(true);
instruction_breakpoint.set_u(true);
instruction_breakpoint.set_execute(true);
instruction_breakpoint.set_dmode(true);
instruction_breakpoint.set_select(false);
self.write_csr(tdata1, 0)?;
self.write_csr(tdata2, addr)?;
self.write_csr(tdata1, instruction_breakpoint.0)?;
Ok(())
}
fn clear_hw_breakpoint(&mut self, unit_index: usize) -> Result<(), crate::Error> {
tracing::info!("Clearing breakpoint {}", unit_index);
let was_running = !self.core_halted()?;
if was_running {
self.halt(Duration::from_millis(100))?;
}
let tselect = 0x7a0;
let tdata1 = 0x7a1;
let tdata2 = 0x7a2;
self.write_csr(tselect, unit_index as u32)?;
self.write_csr(tdata1, 0)?;
self.write_csr(tdata2, 0)?;
if was_running {
self.resume_core()?;
}
Ok(())
}
fn registers(&self) -> &'static CoreRegisters {
&RISCV_CORE_REGSISTERS
}
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 {
self.state.hw_breakpoints_enabled
}
fn debug_on_sw_breakpoint(&mut self, enabled: bool) -> Result<(), Error> {
self.interface.debug_on_sw_breakpoint(enabled)?;
Ok(())
}
fn architecture(&self) -> Architecture {
Architecture::Riscv
}
fn core_type(&self) -> CoreType {
CoreType::Riscv
}
fn instruction_set(&mut self) -> Result<InstructionSet, Error> {
let misa_value = Misa(self.read_csr(0x301)?);
if misa_value.extensions() & (1 << 2) != 0 {
Ok(InstructionSet::RV32C)
} else {
Ok(InstructionSet::RV32)
}
}
fn floating_point_register_count(&mut self) -> Result<usize, Error> {
Ok(self
.registers()
.all_registers()
.filter(|r| r.register_has_role(crate::RegisterRole::FloatingPoint))
.count())
}
fn fpu_support(&mut self) -> Result<bool, crate::error::Error> {
let isa_extensions =
Misa::from(self.read_csr(Misa::get_mmio_address() as u16)?).extensions();
let mask = (1 << 3) | (1 << 5) | (1 << 16);
Ok(isa_extensions & mask != 0)
}
fn reset_catch_set(&mut self) -> Result<(), Error> {
self.sequence.reset_catch_set(&mut self.interface)?;
Ok(())
}
fn reset_catch_clear(&mut self) -> Result<(), Error> {
self.sequence.reset_catch_clear(&mut self.interface)?;
Ok(())
}
fn debug_core_stop(&mut self) -> Result<(), Error> {
self.interface.disable_debug_module()?;
Ok(())
}
}
impl CoreMemoryInterface for Riscv32<'_> {
type ErrorType = Error;
fn memory(&self) -> &dyn MemoryInterface<Self::ErrorType> {
&self.interface
}
fn memory_mut(&mut self) -> &mut dyn MemoryInterface<Self::ErrorType> {
&mut self.interface
}
}
#[derive(Debug)]
pub struct RiscvCoreState {
hw_breakpoints_enabled: bool,
hw_breakpoints: Option<u32>,
pc_written: bool,
semihosting_command: Option<SemihostingCommand>,
}
impl RiscvCoreState {
pub(crate) fn new() -> Self {
Self {
hw_breakpoints_enabled: false,
hw_breakpoints: None,
pc_written: false,
semihosting_command: None,
}
}
}
memory_mapped_bitfield_register! {
pub struct Dmcontrol(u32);
0x10, "dmcontrol",
impl From;
pub _, set_haltreq: 31;
pub _, set_resumereq: 30;
pub hartreset, set_hartreset: 29;
pub _, set_ackhavereset: 28;
pub hasel, set_hasel: 26;
pub hartsello, set_hartsello: 25, 16;
pub hartselhi, set_hartselhi: 15, 6;
pub _, set_resethaltreq: 3;
pub _, set_clrresethaltreq: 2;
pub ndmreset, set_ndmreset: 1;
pub dmactive, set_dmactive: 0;
}
impl Dmcontrol {
pub fn hartsel(&self) -> u32 {
self.hartselhi() << 10 | self.hartsello()
}
pub fn set_hartsel(&mut self, value: u32) {
self.set_hartsello(value & 0x3ff);
self.set_hartselhi((value >> 10) & 0x3ff);
}
}
memory_mapped_bitfield_register! {
pub struct Dmstatus(u32);
0x11, "dmstatus",
impl From;
pub impebreak, _: 22;
pub allhavereset, _: 19;
pub anyhavereset, _: 18;
pub allresumeack, _: 17;
pub anyresumeack, _: 16;
pub allnonexistent, _: 15;
pub anynonexistent, _: 14;
pub allunavail, _: 13;
pub anyunavail, _: 12;
pub allrunning, _: 11;
pub anyrunning, _: 10;
pub allhalted, _: 9;
pub anyhalted, _: 8;
pub authenticated, _: 7;
pub authbusy, _: 6;
pub hasresethaltreq, _: 5;
pub confstrptrvalid, _: 4;
pub version, _: 3, 0;
}
bitfield! {
struct Dcsr(u32);
impl Debug;
xdebugver, _: 31, 28;
ebreakm, set_ebreakm: 15;
ebreaks, set_ebreaks: 13;
ebreaku, set_ebreaku: 12;
stepie, set_stepie: 11;
stopcount, set_stopcount: 10;
stoptime, set_stoptime: 9;
cause, set_cause: 8, 6;
mprven, set_mprven: 4;
nmip, _: 3;
step, set_step: 2;
prv, set_prv: 1,0;
}
memory_mapped_bitfield_register! {
pub struct Abstractcs(u32);
0x16, "abstractcs",
impl From;
progbufsize, _: 28, 24;
busy, _: 12;
cmderr, set_cmderr: 10, 8;
datacount, _: 3, 0;
}
memory_mapped_bitfield_register! {
pub struct Hartinfo(u32);
0x12, "hartinfo",
impl From;
nscratch, _: 23, 20;
dataaccess, _: 16;
datasize, _: 15, 12;
dataaddr, _: 11, 0;
}
memory_mapped_bitfield_register! { pub struct Data0(u32); 0x04, "data0", impl From; }
memory_mapped_bitfield_register! { pub struct Data1(u32); 0x05, "data1", impl From; }
memory_mapped_bitfield_register! { pub struct Data2(u32); 0x06, "data2", impl From; }
memory_mapped_bitfield_register! { pub struct Data3(u32); 0x07, "data3", impl From; }
memory_mapped_bitfield_register! { pub struct Data4(u32); 0x08, "data4", impl From; }
memory_mapped_bitfield_register! { pub struct Data5(u32); 0x09, "data5", impl From; }
memory_mapped_bitfield_register! { pub struct Data6(u32); 0x0A, "data6", impl From; }
memory_mapped_bitfield_register! { pub struct Data7(u32); 0x0B, "data7", impl From; }
memory_mapped_bitfield_register! { pub struct Data8(u32); 0x0C, "data8", impl From; }
memory_mapped_bitfield_register! { pub struct Data9(u32); 0x0D, "data9", impl From; }
memory_mapped_bitfield_register! { pub struct Data10(u32); 0x0E, "data10", impl From; }
memory_mapped_bitfield_register! { pub struct Data11(u32); 0x0f, "data11", impl From; }
memory_mapped_bitfield_register! { struct Command(u32); 0x17, "command", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf0(u32); 0x20, "progbuf0", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf1(u32); 0x21, "progbuf1", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf2(u32); 0x22, "progbuf2", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf3(u32); 0x23, "progbuf3", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf4(u32); 0x24, "progbuf4", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf5(u32); 0x25, "progbuf5", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf6(u32); 0x26, "progbuf6", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf7(u32); 0x27, "progbuf7", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf8(u32); 0x28, "progbuf8", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf9(u32); 0x29, "progbuf9", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf10(u32); 0x2A, "progbuf10", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf11(u32); 0x2B, "progbuf11", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf12(u32); 0x2C, "progbuf12", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf13(u32); 0x2D, "progbuf13", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf14(u32); 0x2E, "progbuf14", impl From; }
memory_mapped_bitfield_register! { pub struct Progbuf15(u32); 0x2F, "progbuf15", impl From; }
bitfield! {
struct Mcontrol(u32);
impl Debug;
type_, set_type: 31, 28;
dmode, set_dmode: 27;
maskmax, _: 26, 21;
hit, set_hit: 20;
select, set_select: 19;
timing, set_timing: 18;
sizelo, set_sizelo: 17, 16;
action, set_action: 15, 12;
chain, set_chain: 11;
match_, set_match: 10, 7;
m, set_m: 6;
s, set_s: 4;
u, set_u: 3;
execute, set_execute: 2;
store, set_store: 1;
load, set_load: 0;
}
memory_mapped_bitfield_register! {
pub struct Misa(u32);
0x301, "misa",
impl From;
mxl, _: 31, 30;
extensions, _: 25, 0;
}