use super::gdb::GdbController;
use wishbone_bridge::{Bridge, BridgeError};
use log::{debug, info};
use std::cell::RefCell;
use std::collections::HashMap;
use std::io;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
pub mod exception;
use exception::RiscvException;
bitflags! {
struct VexRiscvFlags: u32 {
const RESET = 1;
const HALT = 1 << 1;
const PIP_BUSY = 1 << 2;
const HALTED_BY_BREAK = 1 << 3;
const STEP = 1 << 4;
const RESET_SET = 1 << 16;
const HALT_SET = 1 << 17;
const RESET_CLEAR = 1 << 24;
const HALT_CLEAR = 1 << 25;
}
}
#[derive(Debug, PartialEq)]
pub enum RiscvCpuState {
Unknown,
Halted,
Running,
}
#[derive(Debug)]
pub enum RiscvCpuError {
UnrecognizedFile(String ),
InvalidRegister(u32),
BreakpointExhausted,
BreakpointNotFound(u32 ),
BridgeError(BridgeError),
IoError(io::Error),
InstructionTimeout,
}
impl ::std::fmt::Display for RiscvCpuError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
use RiscvCpuError::*;
match self {
UnrecognizedFile(s) => write!(f, "unrecognized file: {}", s),
InvalidRegister(r) => write!(f, "invalid register {}", r),
BreakpointExhausted => write!(f, "ran out of hardware breakpoints"),
BreakpointNotFound(b) => write!(f, "breakpoint {} not found", b),
BridgeError(e) => write!(f, "bridge error: {}", e),
IoError(e) => write!(f, "io error: {}", e),
InstructionTimeout => write!(f, "cpu instruction timed out"),
}
}
}
impl std::convert::From<BridgeError> for RiscvCpuError {
fn from(e: BridgeError) -> RiscvCpuError {
RiscvCpuError::BridgeError(e)
}
}
impl std::convert::From<io::Error> for RiscvCpuError {
fn from(e: io::Error) -> Self {
RiscvCpuError::IoError(e)
}
}
const THREADS_XML: &str = r#"<?xml version="1.0"?>
<threads>
</threads>"#;
#[derive(Debug, PartialEq, Hash, Eq, Clone)]
enum RiscvRegisterType {
General,
CSR,
}
impl RiscvRegisterType {
fn feature_name(&self) -> &str {
match *self {
RiscvRegisterType::General => "org.gnu.gdb.riscv.cpu",
RiscvRegisterType::CSR => "org.gnu.gdb.riscv.csr",
}
}
fn group(&self) -> &str {
match *self {
RiscvRegisterType::General => "general",
RiscvRegisterType::CSR => "csr",
}
}
}
#[derive(Debug, PartialEq, Hash, Eq, Clone)]
enum RegisterContentsType {
Int,
DataPtr,
CodePtr,
}
#[derive(Debug, PartialEq, Hash, Eq, Clone)]
struct RiscvRegister {
register_type: RiscvRegisterType,
index: u32,
gdb_index: u32,
name: String,
present: bool,
save_restore: bool,
contents: RegisterContentsType,
}
impl RiscvRegister {
pub fn general(
index: u32,
name: &str,
save_restore: bool,
contents: RegisterContentsType,
) -> RiscvRegister {
RiscvRegister {
register_type: RiscvRegisterType::General,
index,
gdb_index: index,
name: name.to_string(),
present: true,
save_restore,
contents,
}
}
pub fn csr(index: u32, name: &str, present: bool) -> RiscvRegister {
RiscvRegister {
register_type: RiscvRegisterType::CSR,
index,
gdb_index: index + Self::csr_offset(),
name: name.to_string(),
present,
save_restore: true,
contents: RegisterContentsType::Int,
}
}
fn csr_offset() -> u32 {
65
}
pub fn x0() -> RiscvRegister {
RiscvRegister::general(0, "x0", false, RegisterContentsType::Int)
}
pub fn x1() -> RiscvRegister {
RiscvRegister::general(1, "x1", false, RegisterContentsType::Int)
}
pub fn x2() -> RiscvRegister {
RiscvRegister::general(2, "x2", false, RegisterContentsType::DataPtr)
}
pub fn pc() -> RiscvRegister {
RiscvRegister::general(32, "pc", false, RegisterContentsType::CodePtr)
}
pub fn satp() -> RiscvRegister {
RiscvRegister::csr(0x180, "satp", true)
}
pub fn mstatus() -> RiscvRegister {
RiscvRegister::csr(0x300, "mstatus", true)
}
pub fn mtvec() -> RiscvRegister {
RiscvRegister::csr(0x305, "mtvec", true)
}
pub fn mepc() -> RiscvRegister {
RiscvRegister::csr(0x341, "mepc", true)
}
pub fn mcause() -> RiscvRegister {
RiscvRegister::csr(0x342, "mcause", true)
}
pub fn mtval() -> RiscvRegister {
RiscvRegister::csr(0x343, "mtval", true)
}
}
struct RiscvBreakpoint {
address: u32,
enabled: bool,
allocated: bool,
}
pub struct RiscvCpu {
gdb_register_map: HashMap<u32, RiscvRegister>,
target_xml: String,
debug_offset: u32,
cached_values: Arc<Mutex<HashMap<RiscvRegister, u32>>>,
breakpoints: RefCell<[RiscvBreakpoint; 2]>,
cpu_state: Arc<Mutex<RiscvCpuState>>,
controller: RiscvCpuController,
has_mmu: bool,
mmu_enabled: Arc<AtomicBool>,
last_exception: Arc<Mutex<Option<RiscvException>>>,
}
pub struct RiscvCpuController {
debug_offset: u32,
cpu_state: Arc<Mutex<RiscvCpuState>>,
cached_values: Arc<Mutex<HashMap<RiscvRegister, u32>>>,
has_mmu: bool,
mmu_enabled: Arc<AtomicBool>,
last_exception: Arc<Mutex<Option<RiscvException>>>,
}
impl RiscvCpu {
pub fn new(bridge: &Bridge, offset: u32) -> Result<RiscvCpu, RiscvCpuError> {
let mut gdb_register_map = Self::make_registers();
let cpu_state = Arc::new(Mutex::new(RiscvCpuState::Unknown));
let debug_offset = offset;
let cached_values = Arc::new(Mutex::new(HashMap::new()));
let last_exception = Arc::new(Mutex::new(None));
let mmu_enabled = Arc::new(AtomicBool::new(false));
let mut controller = RiscvCpuController {
cpu_state: cpu_state.clone(),
cached_values: cached_values.clone(),
debug_offset,
has_mmu: false,
mmu_enabled: mmu_enabled.clone(),
last_exception: last_exception.clone(),
};
let was_running =
(controller.read_status(bridge)? & VexRiscvFlags::HALT) != VexRiscvFlags::HALT;
if was_running {
controller.perform_halt(bridge)?;
}
let satp_register = RiscvRegister::satp();
let old_satp = controller.read_register(bridge, &satp_register)?;
controller.write_register(bridge, &satp_register, !old_satp)?;
let new_satp = controller.read_register(bridge, &satp_register)?;
if new_satp != old_satp {
controller.write_register(bridge, &satp_register, old_satp)?;
controller.has_mmu = true;
Self::insert_register(&mut gdb_register_map, satp_register);
mmu_enabled.store((old_satp & 0x8000_0000) == 0x8000_0000, Ordering::Relaxed);
}
if was_running {
controller.perform_resume(bridge, false)?;
}
let target_xml = Self::make_target_xml(&gdb_register_map);
let has_mmu = controller.has_mmu;
let cpu = RiscvCpu {
gdb_register_map,
target_xml,
debug_offset,
cached_values,
breakpoints: RefCell::new([
RiscvBreakpoint {
address: 0,
enabled: false,
allocated: false,
},
RiscvBreakpoint {
address: 0,
enabled: false,
allocated: false,
},
]),
controller,
cpu_state,
has_mmu,
mmu_enabled,
last_exception,
};
Ok(cpu)
}
fn insert_register(target: &mut HashMap<u32, RiscvRegister>, reg: RiscvRegister) {
target.insert(reg.gdb_index, reg);
}
fn make_registers() -> HashMap<u32, RiscvRegister> {
let mut registers = HashMap::new();
registers.insert(0, RiscvRegister::x0());
registers.insert(1, RiscvRegister::x1());
registers.insert(2, RiscvRegister::x2());
for reg_num in 3..32 {
registers.insert(
reg_num,
RiscvRegister::general(
reg_num,
&format!("x{}", reg_num),
true,
RegisterContentsType::Int,
),
);
}
registers.insert(32, RiscvRegister::pc());
Self::insert_register(&mut registers, RiscvRegister::csr(0x000, "ustatus", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x004, "uie", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x005, "utvec", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x040, "uscratch", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x041, "uepc", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x042, "ucause", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x043, "utval", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x044, "uip", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0xc00, "cycle", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0xc01, "time", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0xc02, "instret", false));
for hpmcounter_n in 3..32 {
Self::insert_register(
&mut registers,
RiscvRegister::csr(
0xc00 + hpmcounter_n,
&format!("hpmcounter{}", hpmcounter_n),
false,
),
);
}
Self::insert_register(&mut registers, RiscvRegister::csr(0xc80, "cycleh", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0xc81, "timeh", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0xc82, "instreth", false));
for hpmcounter_n in 3..32 {
Self::insert_register(
&mut registers,
RiscvRegister::csr(
0xc80 + hpmcounter_n,
&format!("hpmcounter{}h", hpmcounter_n),
false,
),
);
}
Self::insert_register(&mut registers, RiscvRegister::csr(0x100, "sstatus", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x102, "sedeleg", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x103, "sideleg", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x104, "sie", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x105, "stvec", false));
Self::insert_register(
&mut registers,
RiscvRegister::csr(0x106, "scounteren", false),
);
Self::insert_register(&mut registers, RiscvRegister::csr(0x140, "sscratch", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x141, "sepc", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x142, "scause", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x143, "stval", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x144, "sip", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x180, "satp", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0xf11, "mvendorid", true));
Self::insert_register(&mut registers, RiscvRegister::csr(0xf12, "marchid", true));
Self::insert_register(&mut registers, RiscvRegister::csr(0xf13, "mimpid", true));
Self::insert_register(&mut registers, RiscvRegister::csr(0xf14, "mhartid", true));
Self::insert_register(&mut registers, RiscvRegister::mstatus());
Self::insert_register(&mut registers, RiscvRegister::csr(0x301, "misa", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x302, "medeleg", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x303, "mideleg", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x304, "mie", true));
Self::insert_register(&mut registers, RiscvRegister::mtvec());
Self::insert_register(
&mut registers,
RiscvRegister::csr(0x306, "mcounteren", false),
);
Self::insert_register(&mut registers, RiscvRegister::csr(0x340, "mscratch", true));
Self::insert_register(&mut registers, RiscvRegister::mepc());
Self::insert_register(&mut registers, RiscvRegister::mcause());
Self::insert_register(&mut registers, RiscvRegister::mtval());
Self::insert_register(&mut registers, RiscvRegister::csr(0x344, "mip", true));
Self::insert_register(&mut registers, RiscvRegister::csr(0x3a0, "mpmcfg0", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x3a1, "mpmcfg1", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x3a2, "mpmcfg2", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x3a3, "mpmcfg3", false));
for pmpaddr_n in 0..16 {
Self::insert_register(
&mut registers,
RiscvRegister::csr(0x3b0 + pmpaddr_n, &format!("pmpaddr{}", pmpaddr_n), false),
);
}
Self::insert_register(&mut registers, RiscvRegister::csr(0xb00, "mcycle", true));
Self::insert_register(&mut registers, RiscvRegister::csr(0xb02, "minstret", true));
for mhpmcounter_n in 3..32 {
Self::insert_register(
&mut registers,
RiscvRegister::csr(
0xb00 + mhpmcounter_n,
&format!("mhpmcounter{}", mhpmcounter_n),
false,
),
);
}
Self::insert_register(&mut registers, RiscvRegister::csr(0xb80, "mcycleh", true));
Self::insert_register(&mut registers, RiscvRegister::csr(0xb82, "minstreth", true));
for mhpmcounter_n in 3..32 {
Self::insert_register(
&mut registers,
RiscvRegister::csr(
0xb80 + mhpmcounter_n,
&format!("mhpmcounter{}h", mhpmcounter_n),
false,
),
);
}
for mhpmevent_n in 3..32 {
Self::insert_register(
&mut registers,
RiscvRegister::csr(
0x320 + mhpmevent_n,
&format!("mhpmevent{}", mhpmevent_n),
false,
),
);
}
Self::insert_register(&mut registers, RiscvRegister::csr(0x7a0, "tselect", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x7a1, "tdata1", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x7a2, "tdata2", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x7a3, "tdata3", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x7b0, "dcsr", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x7b1, "dpc", false));
Self::insert_register(&mut registers, RiscvRegister::csr(0x7b2, "dscratch", false));
registers
}
fn make_target_xml(registers: &HashMap<u32, RiscvRegister>) -> String {
let mut reg_indexes: Vec<u32> = registers.keys().copied().collect();
reg_indexes.sort();
let mut target_xml = "<?xml version=\"1.0\"?>\n<!DOCTYPE target SYSTEM \"gdb-target.dtd\">\n<target version=\"1.0\">\n".to_string();
let mut last_register_type = None;
for reg_index in reg_indexes {
let reg = registers.get(®_index).unwrap();
if Some(®.register_type) != last_register_type {
if last_register_type != None {
target_xml.push_str("</feature>\n");
}
target_xml.push_str(&format!(
"<feature name=\"{}\">\n",
reg.register_type.feature_name()
));
last_register_type = Some(®.register_type);
}
if !reg.present {
continue;
}
let reg_type = match reg.contents {
RegisterContentsType::Int => "int",
RegisterContentsType::CodePtr => "code_ptr",
RegisterContentsType::DataPtr => "data_ptr",
};
target_xml.push_str(&format!(
"<reg name=\"{}\" bitsize=\"32\" regnum=\"{}\" type=\"{}\" group=\"{}\"",
reg.name,
reg.gdb_index,
reg_type,
reg.register_type.group()
));
if !reg.save_restore {
target_xml.push_str(" save-restore=\"no\"");
}
target_xml.push_str("/>\n");
}
if last_register_type != None {
target_xml.push_str("</feature>\n");
}
target_xml.push_str("</target>\n");
target_xml
}
pub fn get_feature(&self, name: &str) -> Result<Vec<u8>, RiscvCpuError> {
if name == "target.xml" {
let xml = self.target_xml.to_string().into_bytes();
Ok(xml)
} else {
Err(RiscvCpuError::UnrecognizedFile(name.to_string()))
}
}
pub fn get_threads(&self) -> Result<Vec<u8>, RiscvCpuError> {
Ok(THREADS_XML.to_string().into_bytes())
}
pub fn explain(&self, bridge: &Bridge) -> Result<String, RiscvCpuError> {
let exception = self.controller.get_current_trap(bridge)?;
if self.controller.interrupts_enabled(bridge)? {
Ok(format!("Last trap was: {}\n", exception))
} else {
Ok(format!("Current trap is: {}\n", exception))
}
}
pub fn add_breakpoint(&self, bridge: &Bridge, addr: u32) -> Result<(), RiscvCpuError> {
let mut bp_index = None;
let mut bps = self.breakpoints.borrow_mut();
for (bpidx, bp) in bps.iter().enumerate() {
if !bp.allocated {
bp_index = Some(bpidx);
}
}
if bp_index.is_none() {
return Err(RiscvCpuError::BreakpointExhausted);
}
let bp_index = bp_index.unwrap();
bps[bp_index].address = addr;
bps[bp_index].allocated = true;
bps[bp_index].enabled = true;
bridge.poke(self.debug_offset + 0x40 + (bp_index as u32 * 4), addr | 1)?;
Ok(())
}
pub fn remove_breakpoint(&self, bridge: &Bridge, addr: u32) -> Result<(), RiscvCpuError> {
let mut bp_index = None;
let mut bps = self.breakpoints.borrow_mut();
for (bpidx, bp) in bps.iter().enumerate() {
if bp.allocated && bp.address == addr {
bp_index = Some(bpidx);
}
}
if bp_index.is_none() {
return Err(RiscvCpuError::BreakpointNotFound(addr));
}
let bp_index = bp_index.unwrap();
bps[bp_index].allocated = false;
bps[bp_index].enabled = false;
bridge.poke(self.debug_offset + 0x40 + (bp_index as u32 * 4), 0)?;
Ok(())
}
pub fn halt(&self, bridge: &Bridge) -> Result<(), RiscvCpuError> {
let mut current_status = self.cpu_state.lock().unwrap();
*current_status = RiscvCpuState::Halted;
self.controller.perform_halt(bridge)?;
debug!("HALT: CPU is now halted");
Ok(())
}
fn update_breakpoints(&self, bridge: &Bridge) -> Result<(), RiscvCpuError> {
for (bpidx, bp) in self.breakpoints.borrow().iter().enumerate() {
if bp.allocated && bp.enabled {
debug!(
"Re-enabling breakpoint {} at address {:08x}",
bpidx, bp.address
);
bridge.poke(
self.debug_offset + 0x40 + (bpidx as u32 * 4),
bp.address | 1,
)?;
} else {
debug!("Breakpoint {} is unallocated", bpidx);
bridge.poke(self.debug_offset + 0x40 + (bpidx as u32 * 4), 0)?;
}
}
Ok(())
}
pub fn reset(&self, bridge: &Bridge) -> Result<(), RiscvCpuError> {
self.cached_values.lock().unwrap().drain();
self.flush_cache(bridge)?;
self.mmu_enabled.store(false, Ordering::Relaxed);
*self.last_exception.lock().unwrap() = None;
self.controller
.write_status(bridge, VexRiscvFlags::HALT_SET)?;
self.controller
.write_status(bridge, VexRiscvFlags::HALT_SET | VexRiscvFlags::RESET_SET)?;
self.controller
.write_status(bridge, VexRiscvFlags::RESET_CLEAR)?;
*self.cpu_state.lock().unwrap() = RiscvCpuState::Halted;
debug!("RESET: CPU is now halted and reset");
Ok(())
}
pub fn resume(&self, bridge: &Bridge) -> Result<Option<String>, RiscvCpuError> {
*self.cpu_state.lock().unwrap() = RiscvCpuState::Running;
self.update_breakpoints(bridge)?;
self.controller.perform_resume(bridge, false)?;
if let Some(exception) = self.last_exception.lock().unwrap().take() {
if exception != RiscvException::NoException {
return Ok(Some(format!("{}", exception)));
}
}
Ok(None)
}
pub fn step(&self, bridge: &Bridge) -> Result<Option<String>, RiscvCpuError> {
self.controller.perform_resume(bridge, true)?;
if let Some(exception) = self.last_exception.lock().unwrap().take() {
if exception != RiscvException::NoException {
return Ok(Some(format!("{}", exception)));
}
}
Ok(None)
}
fn gdb_to_register(&self, regnum: u32) -> Result<&RiscvRegister, RiscvCpuError> {
match self.gdb_register_map.get(®num) {
Some(s) => Ok(s),
None => Err(RiscvCpuError::InvalidRegister(regnum)),
}
}
pub fn read_register(&self, bridge: &Bridge, gdb_idx: u32) -> Result<u32, RiscvCpuError> {
let reg = self.gdb_to_register(gdb_idx)?;
if let Some(val) = self.get_cached_reg(reg) {
return Ok(val);
}
self.controller.read_register(bridge, reg)
}
pub fn all_cpu_registers(&self) -> Vec<u32> {
let mut v = vec![];
for (idx, reg) in &self.gdb_register_map {
if reg.register_type == RiscvRegisterType::General {
v.push(*idx);
}
}
v.sort();
v
}
pub fn write_register(
&self,
bridge: &Bridge,
gdb_idx: u32,
value: u32,
) -> Result<(), RiscvCpuError> {
let reg = self.gdb_to_register(gdb_idx)?;
if reg.register_type == RiscvRegisterType::General {
self.set_cached_reg(reg, value);
Ok(())
} else if reg.gdb_index == RiscvRegister::satp().gdb_index {
self.mmu_enabled
.store(value & 0x8000_0000 == 0x8000_0000, Ordering::Relaxed);
self.set_cached_reg(reg, value);
Ok(())
} else {
self.controller.write_register(bridge, reg, value)
}
}
pub fn read_memory(&self, bridge: &Bridge, addr: u32, sz: u32) -> Result<u32, RiscvCpuError> {
self.controller.read_memory(bridge, addr, sz)
}
pub fn write_memory(
&self,
bridge: &Bridge,
addr: u32,
sz: u32,
value: u32,
) -> Result<(), RiscvCpuError> {
self.controller.write_memory(bridge, addr, sz, value)
}
pub fn get_controller(&self) -> RiscvCpuController {
RiscvCpuController {
cpu_state: self.cpu_state.clone(),
debug_offset: self.debug_offset,
cached_values: self.cached_values.clone(),
has_mmu: self.has_mmu,
mmu_enabled: self.mmu_enabled.clone(),
last_exception: self.last_exception.clone(),
}
}
fn get_cached_reg(&self, reg: &RiscvRegister) -> Option<u32> {
match self.cached_values.lock().unwrap().get(reg) {
Some(x) => Some(*x),
None => None,
}
}
fn set_cached_reg(&self, reg: &RiscvRegister, value: u32) {
self.cached_values
.lock()
.unwrap()
.insert(reg.clone(), value);
}
pub fn flush_cache(&self, bridge: &Bridge) -> Result<(), RiscvCpuError> {
self.controller.flush_cache(bridge)
}
}
fn is_running(flags: VexRiscvFlags) -> bool {
((flags & VexRiscvFlags::PIP_BUSY) == VexRiscvFlags::PIP_BUSY)
|| ((flags & VexRiscvFlags::HALT) != VexRiscvFlags::HALT)
}
impl RiscvCpuController {
pub fn poll(
&self,
bridge: &Bridge,
gdb_controller: &mut GdbController,
) -> Result<bool, RiscvCpuError> {
let flags = self.read_status(bridge)?;
let mut current_status = self.cpu_state.lock().unwrap();
if !is_running(flags) {
if *current_status == RiscvCpuState::Running {
*current_status = RiscvCpuState::Halted;
let halt_msg =
if flags & VexRiscvFlags::HALTED_BY_BREAK == VexRiscvFlags::HALTED_BY_BREAK {
let pc = self.read_result(bridge)?;
self.cached_values
.lock()
.unwrap()
.insert(RiscvRegister::pc(), pc);
"05"
} else {
"02"
};
self.perform_halt(bridge)?;
debug!("POLL: CPU is now halted");
gdb_controller.gdb_send(format!("T{}", halt_msg).as_bytes())?;
}
} else {
if *current_status == RiscvCpuState::Halted {
info!("POLL: The debugger thinks the CPU is halted, but CPU is now running! Halting it and flushing the caches.");
self.cached_values.lock().unwrap().drain();
self.perform_halt(bridge)?;
}
}
Ok(*current_status == RiscvCpuState::Running)
}
fn perform_halt(&self, bridge: &Bridge) -> Result<(), RiscvCpuError> {
self.write_status(bridge, VexRiscvFlags::HALT_SET)?;
self.flush_cache(bridge)?;
let mut last_exception = self.last_exception.lock().unwrap();
let exception = Some(self.get_current_trap(bridge)?);
if !self.interrupts_enabled(bridge)? && exception != *last_exception {
*last_exception = exception;
}
if self.has_mmu {
let satp = RiscvRegister::satp();
let satp_value = self.read_register(bridge, &satp)?;
self.mmu_enabled
.store(satp_value & 0x8000_0000 == 0x8000_0000, Ordering::Relaxed);
if satp_value & 0x8000_0000 == 0x8000_0000 {
debug!("cpu has an mmu that is enabled -= disabling it while in debug mode");
self.set_cached_reg(&satp, satp_value);
self.write_register(bridge, &satp, satp_value & !0x8000_0000)?;
}
}
Ok(())
}
fn perform_resume(&self, bridge: &Bridge, step_only: bool) -> Result<(), RiscvCpuError> {
let coll: HashMap<RiscvRegister, u32> = {
let mut cached_registers = self.cached_values.lock().unwrap();
let drain = cached_registers.drain();
drain.collect()
};
for (reg, value) in &coll {
if reg.gdb_index > 2 {
debug!("restoring value of {} to {:08x}", reg.name, value);
self.write_register(bridge, reg, *value)?;
}
}
for (reg, value) in coll {
if reg.gdb_index <= 2 {
debug!("restoring value of {} to {:08x}", reg.name, value);
self.write_register(bridge, ®, value)?;
}
}
self.flush_cache(bridge)?;
if step_only {
self.write_status(bridge, VexRiscvFlags::HALT_CLEAR | VexRiscvFlags::STEP)?;
} else {
self.write_status(bridge, VexRiscvFlags::HALT_CLEAR)?;
debug!("RESUME: CPU is now running");
}
Ok(())
}
pub fn interrupts_enabled(&self, bridge: &Bridge) -> Result<bool, RiscvCpuError> {
let mstatus_reg = RiscvRegister::mstatus();
let mstatus = self.read_register(bridge, &mstatus_reg)?;
if mstatus & (1 << 3) != 0 {
Ok(true)
} else {
Ok(false)
}
}
pub fn get_current_trap(&self, bridge: &Bridge) -> Result<RiscvException, RiscvCpuError> {
let mcause_reg = RiscvRegister::mcause();
let mepc_reg = RiscvRegister::mepc();
let mtval_reg = RiscvRegister::mtval();
let mcause = self.read_register(bridge, &mcause_reg)?;
let mepc = self.read_register(bridge, &mepc_reg)?;
let mtval = self.read_register(bridge, &mtval_reg)?;
Ok(RiscvException::from_regs(mcause, mepc, mtval))
}
fn read_status(&self, bridge: &Bridge) -> Result<VexRiscvFlags, RiscvCpuError> {
match bridge.peek(self.debug_offset) {
Err(e) => Err(RiscvCpuError::BridgeError(e)),
Ok(bits) => Ok(VexRiscvFlags { bits }),
}
}
fn read_memory(&self, bridge: &Bridge, addr: u32, sz: u32) -> Result<u32, RiscvCpuError> {
if sz == 4 {
return Ok(bridge.peek(addr)?);
} else if sz == 2 {
return Ok((bridge.peek(addr & !0x3)? >> (8 * (addr & 2))) & 0xffff);
} else if sz == 1 {
return Ok((bridge.peek(addr & !0x3)? >> (8 * (addr & 3))) & 0xff);
}
if self.get_cached_reg(&RiscvRegister::x1()).is_none() {
self.set_cached_reg(
&RiscvRegister::x1(),
self.read_register(bridge, &RiscvRegister::x1())?,
);
}
self.write_register(bridge, &RiscvRegister::x1(), addr)?;
let inst = match sz {
4 => (1 << 15) | (0x2 << 12) | (1 << 7) | 0x3,
2 => (1 << 15) | (0x5 << 12) | (1 << 7) | 0x3,
1 => (1 << 15) | (0x4 << 12) | (1 << 7) | 0x3,
x => panic!("Unrecognized memory size: {}", x),
};
self.write_instruction(bridge, inst)?;
Ok(self.read_result(bridge)?)
}
fn write_memory(
&self,
bridge: &Bridge,
addr: u32,
sz: u32,
value: u32,
) -> Result<(), RiscvCpuError> {
if sz == 4 {
return Ok(bridge.poke(addr, value)?);
}
for reg in &[RiscvRegister::x1(), RiscvRegister::x2()] {
if self.get_cached_reg(®).is_none() {
self.set_cached_reg(®, self.read_register(bridge, reg)?);
}
}
self.write_register(bridge, &RiscvRegister::x1(), value)?;
self.write_register(bridge, &RiscvRegister::x2(), addr)?;
let inst = match sz {
4 => (1 << 20) | (2 << 15) | (0x2 << 12) | 0x23,
2 => (1 << 20) | (2 << 15) | (0x1 << 12) | 0x23,
#[allow(clippy::identity_op)]
1 => (1 << 20) | (2 << 15) | (0x0 << 12) | 0x23,
x => panic!("Unrecognized memory size: {}", x),
};
self.write_instruction(bridge, inst)?;
Ok(())
}
fn read_register(&self, bridge: &Bridge, reg: &RiscvRegister) -> Result<u32, RiscvCpuError> {
match reg.register_type {
RiscvRegisterType::General => {
if reg.index == 32 {
self.write_instruction(bridge, 0x17) } else {
self.write_instruction(bridge, (reg.index << 15) | 0x13) }
}
RiscvRegisterType::CSR => {
if self.get_cached_reg(&RiscvRegister::x1()).is_none() {
self.set_cached_reg(
&RiscvRegister::x1(),
self.read_register(bridge, &RiscvRegister::x1())?,
);
}
#[allow(clippy::identity_op)]
self.write_instruction(
bridge,
0
| ((reg.index & 0x1fff) << 20)
| (0 << 15) | (2 << 12) | (1 << 7) | (0x73 << 0), )
}
}?;
let result = self.read_result(bridge)?;
debug!("Register x{} value: 0x{:08x}", reg.index, result);
Ok(result)
}
fn write_register(
&self,
bridge: &Bridge,
reg: &RiscvRegister,
value: u32,
) -> Result<(), RiscvCpuError> {
debug!("Setting register {:?} -> {:08x}", reg, value);
match reg.register_type {
RiscvRegisterType::General => {
if reg.index == 32 {
self.write_register(bridge, &RiscvRegister::x1(), value)?;
self.write_instruction(bridge, 0x67 | (1 << 15))
} else if (value & 0xffff_f800) != 0 {
let low = value & 0x0000_0fff;
let high = if (low & 0x800) != 0 {
(value & 0xffff_f000).wrapping_add(0x1000)
} else {
value & 0xffff_f000
};
self.write_instruction(bridge, (reg.index << 7) | high | 0x37)?;
if low != 0 {
self.write_instruction(
bridge,
(reg.index << 7) | (reg.index << 15) | (low << 20) | 0x13,
)?;
}
Ok(())
} else {
self.write_instruction(
bridge,
(reg.index << 7) | (6 << 12) | (value << 20) | 0x13,
)
}
}
RiscvRegisterType::CSR => {
if self.get_cached_reg(&RiscvRegister::x1()).is_none() {
self.set_cached_reg(
&RiscvRegister::x1(),
self.read_register(bridge, &RiscvRegister::x1())?,
);
}
self.write_register(bridge, &RiscvRegister::x1(), value)?;
#[allow(clippy::identity_op)]
self.write_instruction(
bridge,
0
| ((reg.index & 0x1fff) << 20)
| (1 << 15) | (1 << 12) | (0 << 7) | (0x73 << 0), )
}
}
}
fn flush_cache(&self, bridge: &Bridge) -> Result<(), RiscvCpuError> {
for opcode in &[4111, 19, 19, 19] {
self.write_instruction(bridge, *opcode)?;
}
Ok(())
}
fn get_cached_reg(&self, reg: &RiscvRegister) -> Option<u32> {
match self.cached_values.lock().unwrap().get(reg) {
Some(x) => Some(*x),
None => None,
}
}
fn set_cached_reg(&self, reg: &RiscvRegister, value: u32) {
self.cached_values
.lock()
.unwrap()
.insert(reg.clone(), value);
}
fn write_instruction(&self, bridge: &Bridge, opcode: u32) -> Result<(), RiscvCpuError> {
bridge.poke(self.debug_offset + 4, opcode)?;
for _ in 0..100 {
if (self.read_status(bridge)? & VexRiscvFlags::PIP_BUSY) != VexRiscvFlags::PIP_BUSY {
return Ok(());
}
}
Err(RiscvCpuError::InstructionTimeout)
}
fn read_result(&self, bridge: &Bridge) -> Result<u32, RiscvCpuError> {
Ok(bridge.peek(self.debug_offset + 4)?)
}
fn write_status(&self, bridge: &Bridge, value: VexRiscvFlags) -> Result<(), RiscvCpuError> {
debug!("SETTING BRIDGE STATUS: {:08x}", value.bits);
bridge.poke(self.debug_offset, value.bits)?;
Ok(())
}
}