1use crate::architecture::riscv::registers::RISCV_CORE_REGISTERS;
2use crate::architecture::xtensa::arch::{Register as XtensaRegister, SpecialRegister};
3use crate::architecture::xtensa::registers::XTENSA_CORE_REGISTERS;
4use crate::{Core, CoreType, Error, InstructionSet, MemoryInterface};
5use crate::{RegisterId, RegisterValue};
6use object::elf::PT_NOTE;
7use object::read::elf::ProgramHeader;
8use object::{Object, ObjectSegment};
9use probe_rs_target::MemoryRange;
10use scroll::Cread;
11use serde::{Deserialize, Serialize};
12use std::array;
13use std::sync::LazyLock;
14use std::{
15 collections::HashMap,
16 fs::OpenOptions,
17 ops::Range,
18 path::{Path, PathBuf},
19};
20
21trait Processor {
22 fn instruction_set(&self) -> InstructionSet;
24
25 fn core_type(&self) -> CoreType;
27
28 fn supports_native_64bit_access(&self) -> bool {
30 false
31 }
32
33 fn register_data_len(&self) -> usize;
35
36 fn register_map(&self) -> &[(usize, RegisterId)];
40
41 fn read_registers(
43 &self,
44 note_data: &[u8],
45 registers: &mut HashMap<RegisterId, RegisterValue>,
46 ) -> Result<(), CoreDumpError> {
47 for (offset, reg_id) in self.register_map().iter().copied() {
48 let value = self.read_register(note_data, offset)?;
49 registers.insert(reg_id, value);
50 }
51
52 Ok(())
53 }
54
55 fn read_register(&self, note_data: &[u8], idx: usize) -> Result<RegisterValue, CoreDumpError> {
58 let value = u32::from_le_bytes(note_data[idx * 4..][..4].try_into().unwrap());
59 Ok(RegisterValue::U32(value))
60 }
61}
62
63struct XtensaProcessor;
64impl Processor for XtensaProcessor {
65 fn instruction_set(&self) -> InstructionSet {
66 InstructionSet::Xtensa
67 }
68 fn core_type(&self) -> CoreType {
69 CoreType::Xtensa
70 }
71 fn register_data_len(&self) -> usize {
72 128 * 4
73 }
74 fn register_map(&self) -> &[(usize, RegisterId)] {
75 static REGS: LazyLock<[(usize, RegisterId); 24]> = LazyLock::new(|| {
76 let core_regs = &XTENSA_CORE_REGISTERS;
77
78 array::from_fn(|idx| {
79 match idx {
80 0 => (idx, RegisterId::from(XtensaRegister::CurrentPc)),
82 1 => (idx, RegisterId::from(SpecialRegister::Ps)),
83 2 => (idx, RegisterId::from(SpecialRegister::Lbeg)),
84 3 => (idx, RegisterId::from(SpecialRegister::Lend)),
85 4 => (idx, RegisterId::from(SpecialRegister::Lcount)),
86 5 => (idx, RegisterId::from(SpecialRegister::Sar)),
87 6 => (idx, RegisterId::from(SpecialRegister::Windowstart)),
88 7 => (idx, RegisterId::from(SpecialRegister::Windowbase)),
89 8..24 => {
91 let ar_idx = idx - 8;
94 (ar_idx + 64, core_regs.core_register(ar_idx).id())
95 }
96 _ => unreachable!(),
97 }
98 })
99 });
100
101 &*REGS
102 }
103}
104
105struct RiscvProcessor;
106impl Processor for RiscvProcessor {
107 fn instruction_set(&self) -> InstructionSet {
108 InstructionSet::RV32
109 }
110 fn core_type(&self) -> CoreType {
111 CoreType::Riscv
112 }
113 fn register_data_len(&self) -> usize {
114 32 * 4
115 }
116 fn register_map(&self) -> &[(usize, RegisterId)] {
117 static REGS: LazyLock<[(usize, RegisterId); 32]> = LazyLock::new(|| {
118 let core_regs = &RISCV_CORE_REGISTERS;
119
120 array::from_fn(|idx| {
121 let regid = if idx == 0 {
123 core_regs.pc().unwrap().id()
124 } else {
125 core_regs.core_register(idx).id()
126 };
127 (idx, regid)
128 })
129 });
130 &*REGS
131 }
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct CoreDump {
137 pub registers: HashMap<RegisterId, RegisterValue>,
139 pub data: Vec<(Range<u64>, Vec<u8>)>,
141 pub instruction_set: InstructionSet,
143 pub supports_native_64bit_access: bool,
145 pub core_type: CoreType,
147 pub fpu_support: bool,
149 pub floating_point_register_count: Option<usize>,
151}
152
153impl CoreDump {
154 pub fn dump_core(core: &mut Core, ranges: Vec<Range<u64>>) -> Result<Self, Error> {
160 let mut registers = HashMap::new();
161 for register in core.registers().all_registers() {
162 let value = core.read_core_reg(register.id())?;
163 registers.insert(register.id(), value);
164 }
165
166 let mut data = Vec::new();
167 for range in ranges {
168 let mut values = vec![0; (range.end - range.start) as usize];
169 core.read(range.start, &mut values)?;
170 data.push((range, values));
171 }
172
173 Ok(CoreDump {
174 registers,
175 data,
176 instruction_set: core.instruction_set()?,
177 supports_native_64bit_access: core.supports_native_64bit_access(),
178 core_type: core.core_type(),
179 fpu_support: core.fpu_support()?,
180 floating_point_register_count: Some(core.floating_point_register_count()?),
181 })
182 }
183
184 pub fn store(&self, path: &Path) -> Result<(), CoreDumpError> {
186 let mut file = OpenOptions::new()
187 .create(true)
188 .write(true)
189 .truncate(true)
190 .open(path)
191 .map_err(|e| {
192 CoreDumpError::CoreDumpFileWrite(e, dunce::canonicalize(path).unwrap_or_default())
193 })?;
194 rmp_serde::encode::write_named(&mut file, self).map_err(CoreDumpError::EncodingCoreDump)?;
195 Ok(())
196 }
197
198 pub fn load(path: &Path) -> Result<Self, CoreDumpError> {
200 let file_contents = std::fs::read(path).map_err(|e| {
201 CoreDumpError::CoreDumpFileRead(e, dunce::canonicalize(path).unwrap_or_default())
202 })?;
203 Self::load_raw(&file_contents)
204 }
205
206 pub fn load_raw(data: &[u8]) -> Result<Self, CoreDumpError> {
208 if let Ok(elf) = object::read::elf::ElfFile32::parse(data) {
209 Self::load_elf(elf)
210 } else if let Ok(elf) = object::read::elf::ElfFile64::parse(data) {
211 Self::load_elf(elf)
212 } else {
213 rmp_serde::from_slice(data).map_err(CoreDumpError::DecodingCoreDump)
214 }
215 }
216
217 fn load_elf<Elf: object::read::elf::FileHeader<Endian = object::Endianness>>(
218 elf: object::read::elf::ElfFile<'_, Elf>,
219 ) -> Result<Self, CoreDumpError> {
220 let endianness = elf.endianness();
221 let elf_data = elf.data();
222
223 let processor: Box<dyn Processor> = match elf.architecture() {
224 object::Architecture::Riscv32 => Box::new(RiscvProcessor),
225 object::Architecture::Xtensa => Box::new(XtensaProcessor),
226 other => {
227 return Err(CoreDumpError::DecodingElfCoreDump(format!(
228 "Unsupported architecture: {other:?}",
229 )));
230 }
231 };
232
233 let mut data = Vec::new();
235 for segment in elf.segments() {
237 let address: u64 = segment.elf_program_header().p_vaddr(endianness).into();
238 let size: u64 = segment.elf_program_header().p_memsz(endianness).into();
239 let memory = segment.data()?;
240 tracing::debug!(
241 "Adding memory segment: {:#x} - {:#x}",
242 address,
243 address + size
244 );
245 data.push((address..address + size, memory.to_vec()));
246 }
247
248 let Some(register_note) = elf
250 .elf_program_headers()
251 .iter()
252 .find(|s| s.p_type(endianness) == PT_NOTE)
253 else {
254 return Err(CoreDumpError::DecodingElfCoreDump(
255 "No note segment found".to_string(),
256 ));
257 };
258
259 let mut registers = HashMap::new();
260 for note in register_note
261 .notes(endianness, elf_data)?
262 .expect("Failed to read notes from a PT_NOTE segment. This is a bug, please report it.")
263 {
264 let note = note?;
265 if note.name() != b"CORE" {
266 continue;
267 }
268
269 const CORE_NOTE_HEADER_SIZE: usize = 72;
274 let note_length = processor.register_data_len();
275
276 if note.desc().len() < CORE_NOTE_HEADER_SIZE + note_length {
277 return Err(CoreDumpError::DecodingElfCoreDump(format!(
278 "Note segment is too small: {} bytes instead of at least {}",
279 note.desc().len(),
280 CORE_NOTE_HEADER_SIZE + note_length
281 )));
282 }
283
284 let note_data = ¬e.desc()[CORE_NOTE_HEADER_SIZE..][..note_length];
285 processor.read_registers(note_data, &mut registers)?;
286 }
287
288 Ok(Self {
289 registers,
290 data,
291 instruction_set: processor.instruction_set(),
292 supports_native_64bit_access: processor.supports_native_64bit_access(),
293 core_type: processor.core_type(),
294 fpu_support: false,
295 floating_point_register_count: None,
296 })
297 }
298
299 pub fn core_type(&self) -> CoreType {
301 self.core_type
302 }
303
304 pub fn instruction_set(&self) -> InstructionSet {
306 self.instruction_set
307 }
308
309 fn get_memory_from_coredump(
311 &self,
312 address: u64,
313 size_in_bytes: u64,
314 ) -> Result<&[u8], crate::Error> {
315 for (range, memory) in &self.data {
316 if range.contains_range(&(address..(address + size_in_bytes))) {
317 let offset = (address - range.start) as usize;
318
319 return Ok(&memory[offset..][..size_in_bytes as usize]);
320 }
321 }
322 Err(crate::Error::Other(format!(
324 "The coredump does not include the memory for address {address:#x} of size {size_in_bytes:#x}"
325 )))
326 }
327
328 fn read_memory_range<T>(&self, address: u64, data: &mut [T]) -> Result<(), crate::Error>
331 where
332 T: scroll::ctx::FromCtx<scroll::Endian>,
333 {
334 let memory =
335 self.get_memory_from_coredump(address, (std::mem::size_of_val(data)) as u64)?;
336
337 let value_size = std::mem::size_of::<T>();
338
339 for (n, data) in data.iter_mut().enumerate() {
340 *data = memory.cread_with::<T>(n * value_size, scroll::LE);
341 }
342 Ok(())
343 }
344}
345
346impl MemoryInterface for CoreDump {
347 fn supports_native_64bit_access(&mut self) -> bool {
348 self.supports_native_64bit_access
349 }
350
351 fn read_word_64(&mut self, address: u64) -> Result<u64, crate::Error> {
352 let mut data = [0u64; 1];
353 self.read_memory_range(address, &mut data)?;
354 Ok(data[0])
355 }
356
357 fn read_word_32(&mut self, address: u64) -> Result<u32, crate::Error> {
358 let mut data = [0u32; 1];
359 self.read_memory_range(address, &mut data)?;
360 Ok(data[0])
361 }
362
363 fn read_word_16(&mut self, address: u64) -> Result<u16, crate::Error> {
364 let mut data = [0u16; 1];
365 self.read_memory_range(address, &mut data)?;
366 Ok(data[0])
367 }
368
369 fn read_word_8(&mut self, address: u64) -> Result<u8, crate::Error> {
370 let mut data = [0u8; 1];
371 self.read_memory_range(address, &mut data)?;
372 Ok(data[0])
373 }
374
375 fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), crate::Error> {
376 self.read_memory_range(address, data)?;
377 Ok(())
378 }
379
380 fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), crate::Error> {
381 self.read_memory_range(address, data)?;
382 Ok(())
383 }
384
385 fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), crate::Error> {
386 self.read_memory_range(address, data)?;
387 Ok(())
388 }
389
390 fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), crate::Error> {
391 self.read_memory_range(address, data)?;
392 Ok(())
393 }
394
395 fn write_word_64(&mut self, _address: u64, _data: u64) -> Result<(), crate::Error> {
396 todo!()
397 }
398
399 fn write_word_32(&mut self, _address: u64, _data: u32) -> Result<(), crate::Error> {
400 todo!()
401 }
402
403 fn write_word_16(&mut self, _address: u64, _data: u16) -> Result<(), crate::Error> {
404 todo!()
405 }
406
407 fn write_word_8(&mut self, _address: u64, _data: u8) -> Result<(), crate::Error> {
408 todo!()
409 }
410
411 fn write_64(&mut self, _address: u64, _data: &[u64]) -> Result<(), crate::Error> {
412 todo!()
413 }
414
415 fn write_32(&mut self, _address: u64, _data: &[u32]) -> Result<(), crate::Error> {
416 todo!()
417 }
418
419 fn write_16(&mut self, _address: u64, _data: &[u16]) -> Result<(), crate::Error> {
420 todo!()
421 }
422
423 fn write_8(&mut self, _address: u64, _data: &[u8]) -> Result<(), crate::Error> {
424 todo!()
425 }
426
427 fn supports_8bit_transfers(&self) -> Result<bool, crate::Error> {
428 todo!()
429 }
430
431 fn flush(&mut self) -> Result<(), crate::Error> {
432 todo!()
433 }
434}
435
436#[derive(thiserror::Error, Debug)]
438pub enum CoreDumpError {
439 #[error("Opening {1} for writing the core dump failed.")]
441 CoreDumpFileWrite(std::io::Error, PathBuf),
442 #[error("Opening {1} for reading the core dump failed.")]
444 CoreDumpFileRead(std::io::Error, PathBuf),
445 #[error("Encoding the coredump MessagePack failed.")]
447 EncodingCoreDump(rmp_serde::encode::Error),
448 #[error("Decoding the coredump MessagePack failed.")]
450 DecodingCoreDump(rmp_serde::decode::Error),
451 #[error("Decoding the coredump .elf failed.")]
453 DecodingElfCoreDump(String),
454 #[error("Invalid ELF file.")]
456 ElfCoreDumpFormat(#[from] object::read::Error),
457}