1use core::num::NonZeroUsize;
8
9use gdbstub::arch::{Arch, RegId, Registers};
10
11#[derive(Debug, Default, Clone, PartialEq, Eq)]
13pub struct Rv32CoreRegs {
14 pub x: [u32; 32],
15 pub pc: u32,
16}
17
18impl Registers for Rv32CoreRegs {
19 type ProgramCounter = u32;
20
21 fn pc(&self) -> u32 {
22 self.pc
23 }
24
25 fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
26 for r in self.x.iter().chain(core::iter::once(&self.pc)) {
27 for b in r.to_le_bytes() {
28 write_byte(Some(b));
29 }
30 }
31 }
32
33 fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
34 if bytes.len() < 33 * 4 {
36 return Err(());
37 }
38 for (i, chunk) in bytes.chunks(4).enumerate().take(33) {
39 if chunk.len() < 4 {
40 break;
41 }
42 let v = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
43 if i < 32 {
44 self.x[i] = v;
45 } else {
46 self.pc = v;
47 }
48 }
49 Ok(())
50 }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum Rv32RegId {
56 Gpr(u8),
57 Pc,
58}
59
60impl RegId for Rv32RegId {
61 fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
62 let size = NonZeroUsize::new(4);
63 match id {
64 0..=31 => Some((Rv32RegId::Gpr(id as u8), size)),
65 32 => Some((Rv32RegId::Pc, size)),
66 _ => None,
67 }
68 }
69
70 fn to_raw_id(&self) -> Option<usize> {
71 Some(match self {
72 Rv32RegId::Gpr(n) => *n as usize,
73 Rv32RegId::Pc => 32,
74 })
75 }
76}
77
78pub enum Rv32 {}
80
81impl Arch for Rv32 {
82 type Usize = u32;
83 type Registers = Rv32CoreRegs;
84 type BreakpointKind = usize;
85 type RegId = Rv32RegId;
86
87 fn target_description_xml() -> Option<&'static str> {
88 Some(r#"<target version="1.0"><architecture>riscv:rv32</architecture></target>"#)
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 #![allow(clippy::unwrap_used)]
96 use super::*;
97
98 #[test]
99 fn core_regs_serialize_roundtrip() {
100 let mut regs = Rv32CoreRegs::default();
101 for (i, x) in regs.x.iter_mut().enumerate() {
102 *x = 0x1000_0000 + i as u32;
103 }
104 regs.pc = 0x0800_0000;
105
106 let mut bytes = Vec::new();
108 regs.gdb_serialize(|b| {
109 if let Some(b) = b {
110 bytes.push(b);
111 }
112 });
113 assert_eq!(bytes.len(), 33 * 4);
114 assert_eq!(&bytes[0..4], &0x1000_0000u32.to_le_bytes()); assert_eq!(&bytes[128..132], &0x0800_0000u32.to_le_bytes()); let mut back = Rv32CoreRegs::default();
119 back.gdb_deserialize(&bytes).unwrap();
120 assert_eq!(back, regs);
121 }
122
123 #[test]
124 fn deserialize_rejects_short_input() {
125 let mut regs = Rv32CoreRegs::default();
126 assert!(regs.gdb_deserialize(&[0u8; 131]).is_err());
127 }
128
129 #[test]
130 fn reg_id_mapping() {
131 assert_eq!(
132 Rv32RegId::from_raw_id(0).map(|(r, _)| r),
133 Some(Rv32RegId::Gpr(0))
134 );
135 assert_eq!(
136 Rv32RegId::from_raw_id(31).map(|(r, _)| r),
137 Some(Rv32RegId::Gpr(31))
138 );
139 assert_eq!(
140 Rv32RegId::from_raw_id(32).map(|(r, _)| r),
141 Some(Rv32RegId::Pc)
142 );
143 assert!(Rv32RegId::from_raw_id(33).is_none());
144 }
145}