use crate::{
architecture::{
arm::core::registers::{
aarch32::{
AARCH32_CORE_REGSISTERS, AARCH32_WITH_FP_16_CORE_REGSISTERS,
AARCH32_WITH_FP_32_CORE_REGSISTERS,
},
aarch64::AARCH64_CORE_REGSISTERS,
cortex_m::{CORTEX_M_CORE_REGISTERS, CORTEX_M_WITH_FP_CORE_REGISTERS},
},
riscv::registers::RISCV_CORE_REGSISTERS,
xtensa::registers::XTENSA_CORE_REGSISTERS,
},
debug::{DebugRegister, DebugRegisters},
CoreType, InstructionSet, MemoryInterface,
};
use crate::{RegisterId, RegisterValue};
use anyhow::anyhow;
use probe_rs_target::MemoryRange;
use scroll::Pread;
use std::{
collections::HashMap,
fs::OpenOptions,
mem::size_of_val,
ops::Range,
path::{Path, PathBuf},
};
use super::RegisterDataType;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoreDump {
pub registers: HashMap<RegisterId, RegisterValue>,
pub data: Vec<(Range<u64>, Vec<u8>)>,
pub instruction_set: InstructionSet,
pub supports_native_64bit_access: bool,
pub core_type: CoreType,
pub fpu_support: bool,
pub floating_point_register_count: Option<usize>,
}
impl CoreDump {
pub fn store(&self, path: &Path) -> Result<(), CoreDumpError> {
let mut file = OpenOptions::new()
.create(true)
.write(true)
.open(path)
.map_err(|e| {
CoreDumpError::CoreDumpFileWrite(e, dunce::canonicalize(path).unwrap_or_default())
})?;
rmp_serde::encode::write_named(&mut file, self).map_err(CoreDumpError::EncodingCoreDump)?;
Ok(())
}
pub fn load(path: &Path) -> Result<Self, CoreDumpError> {
let file = OpenOptions::new().read(true).open(path).map_err(|e| {
CoreDumpError::CoreDumpFileRead(e, dunce::canonicalize(path).unwrap_or_default())
})?;
rmp_serde::from_read(&file).map_err(CoreDumpError::DecodingCoreDump)
}
pub fn load_raw(data: &[u8]) -> Result<Self, CoreDumpError> {
rmp_serde::from_slice(data).map_err(CoreDumpError::DecodingCoreDump)
}
pub fn debug_registers(&self) -> DebugRegisters {
let reg_list = match self.core_type {
CoreType::Armv6m => &CORTEX_M_CORE_REGISTERS,
CoreType::Armv7a => match self.floating_point_register_count {
Some(16) => &AARCH32_WITH_FP_16_CORE_REGSISTERS,
Some(32) => &AARCH32_WITH_FP_32_CORE_REGSISTERS,
_ => &AARCH32_CORE_REGSISTERS,
},
CoreType::Armv7m => {
if self.fpu_support {
&CORTEX_M_WITH_FP_CORE_REGISTERS
} else {
&CORTEX_M_CORE_REGISTERS
}
}
CoreType::Armv7em => {
if self.fpu_support {
&CORTEX_M_WITH_FP_CORE_REGISTERS
} else {
&CORTEX_M_CORE_REGISTERS
}
}
CoreType::Armv8a => &AARCH64_CORE_REGSISTERS,
CoreType::Armv8m => {
if self.fpu_support {
&CORTEX_M_WITH_FP_CORE_REGISTERS
} else {
&CORTEX_M_CORE_REGISTERS
}
}
CoreType::Riscv => &RISCV_CORE_REGSISTERS,
CoreType::Xtensa => &XTENSA_CORE_REGSISTERS,
};
let mut debug_registers = Vec::<DebugRegister>::new();
for (dwarf_id, core_register) in reg_list.core_registers().enumerate() {
if matches!(core_register.data_type(), RegisterDataType::UnsignedInteger(size_in_bits) if size_in_bits <= 64)
{
debug_registers.push(DebugRegister {
core_register,
dwarf_id: if dwarf_id < 32 {
Some(dwarf_id as u16)
} else {
None
},
value: match self.registers.get(&core_register.id()) {
Some(register_value) => Some(*register_value),
None => {
tracing::warn!("Failed to read value for register {:?}", core_register);
None
}
},
});
} else {
tracing::trace!(
"Unwind will use the default rule for this register : {:?}",
core_register
);
}
}
DebugRegisters(debug_registers)
}
pub fn core_type(&self) -> CoreType {
self.core_type
}
pub fn instruction_set(&self) -> InstructionSet {
self.instruction_set
}
fn get_memory_from_coredump(
&self,
address: u64,
size_in_bytes: u64,
) -> Result<(u64, &Vec<u8>), crate::Error> {
for (range, memory) in &self.data {
if range.contains_range(&(address..(address + size_in_bytes))) {
return Ok((range.start, memory));
}
}
Err(crate::Error::Other(anyhow!("The coredump does not include the memory for address {address:#x} of size {size_in_bytes:#x}")))
}
fn read_memory_range<'a, T>(
&'a self,
address: u64,
data: &'a mut [T],
) -> Result<(), crate::Error>
where
<T as scroll::ctx::TryFromCtx<'a, scroll::Endian>>::Error:
std::convert::From<scroll::Error>,
<T as scroll::ctx::TryFromCtx<'a, scroll::Endian>>::Error: std::fmt::Display,
T: scroll::ctx::TryFromCtx<'a, scroll::Endian>,
{
let (memory_offset, memory) =
self.get_memory_from_coredump(address, (size_of_val(data)) as u64)?;
for (n, data) in data.iter_mut().enumerate() {
*data = memory
.pread_with::<T>((address - memory_offset) as usize + n * 4, scroll::LE)
.map_err(|e| anyhow!("{e}"))?;
}
Ok(())
}
}
impl MemoryInterface for CoreDump {
fn supports_native_64bit_access(&mut self) -> bool {
self.supports_native_64bit_access
}
fn read_word_64(&mut self, address: u64) -> Result<u64, crate::Error> {
let mut data = [0u64; 1];
self.read_memory_range(address, &mut data)?;
Ok(data[0])
}
fn read_word_32(&mut self, address: u64) -> Result<u32, crate::Error> {
let mut data = [0u32; 1];
self.read_memory_range(address, &mut data)?;
Ok(data[0])
}
fn read_word_16(&mut self, address: u64) -> Result<u16, crate::Error> {
let mut data = [0u16; 1];
self.read_memory_range(address, &mut data)?;
Ok(data[0])
}
fn read_word_8(&mut self, address: u64) -> Result<u8, crate::Error> {
let mut data = [0u8; 1];
self.read_memory_range(address, &mut data)?;
Ok(data[0])
}
fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), crate::Error> {
self.read_memory_range(address, data)?;
Ok(())
}
fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), crate::Error> {
self.read_memory_range(address, data)?;
Ok(())
}
fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), crate::Error> {
self.read_memory_range(address, data)?;
Ok(())
}
fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), crate::Error> {
self.read_memory_range(address, data)?;
Ok(())
}
fn write_word_64(&mut self, _address: u64, _data: u64) -> Result<(), crate::Error> {
todo!()
}
fn write_word_32(&mut self, _address: u64, _data: u32) -> Result<(), crate::Error> {
todo!()
}
fn write_word_16(&mut self, _address: u64, _data: u16) -> Result<(), crate::Error> {
todo!()
}
fn write_word_8(&mut self, _address: u64, _data: u8) -> Result<(), crate::Error> {
todo!()
}
fn write_64(&mut self, _address: u64, _data: &[u64]) -> Result<(), crate::Error> {
todo!()
}
fn write_32(&mut self, _address: u64, _data: &[u32]) -> Result<(), crate::Error> {
todo!()
}
fn write_16(&mut self, _address: u64, _data: &[u16]) -> Result<(), crate::Error> {
todo!()
}
fn write_8(&mut self, _address: u64, _data: &[u8]) -> Result<(), crate::Error> {
todo!()
}
fn supports_8bit_transfers(&self) -> Result<bool, crate::Error> {
todo!()
}
fn flush(&mut self) -> Result<(), crate::Error> {
todo!()
}
}
#[derive(thiserror::Error, Debug)]
pub enum CoreDumpError {
#[error("Opening {1} for writing the core dump failed.")]
CoreDumpFileWrite(std::io::Error, PathBuf),
#[error("Opening {1} for reading the core dump failed.")]
CoreDumpFileRead(std::io::Error, PathBuf),
#[error("Encoding the coredump MessagePack failed.")]
EncodingCoreDump(rmp_serde::encode::Error),
#[error("Decoding the coredump MessagePack failed.")]
DecodingCoreDump(rmp_serde::decode::Error),
}