Skip to main content

ch32rv_debug/
arch.rs

1//! en: A minimal RISC-V RV32 architecture for gdbstub: 32 integer GPRs plus the PC, all u32,
2//! in the order GDB's `riscv:rv32` core.xml expects (x0..x31, then pc). FPU/CSR registers are
3//! not exposed yet (docs/architecture.ja.md §1.3 notes V4F FPU needs a custom Arch later).
4//! ja: gdbstub 用の最小 RISC-V RV32 定義。GPR 32 本 + PC(すべて u32)を GDB の core.xml 順
5//! (x0..x31, pc)で並べる。FPU/CSR は未対応(V4F FPU は将来の課題)。
6
7use core::num::NonZeroUsize;
8
9use gdbstub::arch::{Arch, RegId, Registers};
10
11/// RV32 core register file: x0..x31 and pc.
12#[derive(Debug, Default, Clone, PartialEq, Eq)]
13pub struct Rv32CoreRegs {
14    pub x: [u32; 32],
15    pub pc: u32,
16    /// en: An RV32E hart (x0..x15 only): GDB then works with 16 GPRs and pc (68 bytes, as it does
17    /// for an RV32E ELF and as [`RV32E_TARGET_XML`] says), not 33 registers.
18    /// ja: RV32E の hart(x0..x15 のみ)。GDB は GPR 16 本と pc(68 byte)で扱う。
19    pub rv32e: bool,
20}
21
22/// en: The target description of an RV32E hart: x0..x15 and pc (remote number 32, as GDB numbers
23/// the RISC-V pc), so GDB expects 17 registers whether or not it has the ELF.
24/// ja: RV32E の hart の target description。x0..x15 と pc(GDB の RISC-V の pc の番号 32)。ELF の
25/// 有無にかかわらず GDB は 17 本と見る。
26pub const RV32E_TARGET_XML: &str = concat!(
27    r#"<?xml version="1.0"?><!DOCTYPE target SYSTEM "gdb-target.dtd">"#,
28    r#"<target version="1.0"><architecture>riscv:rv32</architecture>"#,
29    r#"<feature name="org.gnu.gdb.riscv.cpu">"#,
30    r#"<reg name="zero" bitsize="32" type="int" regnum="0"/>"#,
31    r#"<reg name="ra" bitsize="32" type="code_ptr"/>"#,
32    r#"<reg name="sp" bitsize="32" type="data_ptr"/>"#,
33    r#"<reg name="gp" bitsize="32" type="data_ptr"/>"#,
34    r#"<reg name="tp" bitsize="32" type="data_ptr"/>"#,
35    r#"<reg name="t0" bitsize="32" type="int"/>"#,
36    r#"<reg name="t1" bitsize="32" type="int"/>"#,
37    r#"<reg name="t2" bitsize="32" type="int"/>"#,
38    r#"<reg name="fp" bitsize="32" type="data_ptr"/>"#,
39    r#"<reg name="s1" bitsize="32" type="int"/>"#,
40    r#"<reg name="a0" bitsize="32" type="int"/>"#,
41    r#"<reg name="a1" bitsize="32" type="int"/>"#,
42    r#"<reg name="a2" bitsize="32" type="int"/>"#,
43    r#"<reg name="a3" bitsize="32" type="int"/>"#,
44    r#"<reg name="a4" bitsize="32" type="int"/>"#,
45    r#"<reg name="a5" bitsize="32" type="int"/>"#,
46    r#"<reg name="pc" bitsize="32" type="code_ptr" regnum="32"/>"#,
47    r#"</feature></target>"#,
48);
49
50impl Registers for Rv32CoreRegs {
51    type ProgramCounter = u32;
52
53    fn pc(&self) -> u32 {
54        self.pc
55    }
56
57    fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
58        let gprs = if self.rv32e { 16 } else { 32 };
59        for r in self.x[..gprs].iter().chain(core::iter::once(&self.pc)) {
60            for b in r.to_le_bytes() {
61                write_byte(Some(b));
62            }
63        }
64    }
65
66    fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
67        // 33 registers x 4 bytes = 132 bytes, or on an RV32E hart 17 x 4 = 68.
68        let gprs = if bytes.len() >= 33 * 4 {
69            32
70        } else if bytes.len() >= 17 * 4 {
71            16
72        } else {
73            return Err(());
74        };
75        self.rv32e = gprs == 16;
76        for (i, chunk) in bytes.as_chunks::<4>().0.iter().enumerate().take(gprs + 1) {
77            let v = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
78            if i < gprs {
79                self.x[i] = v;
80            } else {
81                self.pc = v;
82            }
83        }
84        Ok(())
85    }
86}
87
88/// Register identifier: a GPR index 0..31, or the PC (id 32).
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum Rv32RegId {
91    Gpr(u8),
92    Pc,
93}
94
95impl RegId for Rv32RegId {
96    fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
97        let size = NonZeroUsize::new(4);
98        match id {
99            0..=31 => Some((Rv32RegId::Gpr(id as u8), size)),
100            32 => Some((Rv32RegId::Pc, size)),
101            _ => None,
102        }
103    }
104
105    fn to_raw_id(&self) -> Option<usize> {
106        Some(match self {
107            Rv32RegId::Gpr(n) => *n as usize,
108            Rv32RegId::Pc => 32,
109        })
110    }
111}
112
113/// The RV32 architecture marker for gdbstub (zero-variant, used at the type level only).
114pub enum Rv32 {}
115
116impl Arch for Rv32 {
117    type Usize = u32;
118    type Registers = Rv32CoreRegs;
119    type BreakpointKind = usize;
120    type RegId = Rv32RegId;
121
122    fn target_description_xml() -> Option<&'static str> {
123        // Lets GDB auto-detect the architecture without `set architecture`.
124        Some(r#"<target version="1.0"><architecture>riscv:rv32</architecture></target>"#)
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    #![allow(clippy::unwrap_used)]
131    use super::*;
132
133    #[test]
134    fn core_regs_serialize_roundtrip() {
135        let mut regs = Rv32CoreRegs::default();
136        for (i, x) in regs.x.iter_mut().enumerate() {
137            *x = 0x1000_0000 + i as u32;
138        }
139        regs.pc = 0x0800_0000;
140
141        // Serialize: 33 registers x 4 bytes, little-endian, x0..x31 then pc.
142        let mut bytes = Vec::new();
143        regs.gdb_serialize(|b| {
144            if let Some(b) = b {
145                bytes.push(b);
146            }
147        });
148        assert_eq!(bytes.len(), 33 * 4);
149        assert_eq!(&bytes[0..4], &0x1000_0000u32.to_le_bytes()); // x0
150        assert_eq!(&bytes[128..132], &0x0800_0000u32.to_le_bytes()); // pc last
151
152        // Round-trip back.
153        let mut back = Rv32CoreRegs::default();
154        back.gdb_deserialize(&bytes).unwrap();
155        assert_eq!(back, regs);
156    }
157
158    #[test]
159    fn deserialize_rejects_short_input() {
160        let mut regs = Rv32CoreRegs::default();
161        assert!(regs.gdb_deserialize(&[0u8; 67]).is_err());
162    }
163
164    #[test]
165    fn rv32e_has_16_gprs_and_pc() {
166        let mut regs = Rv32CoreRegs {
167            rv32e: true,
168            pc: 0x3cc,
169            ..Default::default()
170        };
171        regs.x[15] = 0xf;
172        let mut bytes = Vec::new();
173        regs.gdb_serialize(|b| bytes.extend(b));
174        assert_eq!(bytes.len(), 17 * 4);
175        assert_eq!(&bytes[60..64], &0xfu32.to_le_bytes()); // x15
176        assert_eq!(&bytes[64..68], &0x3ccu32.to_le_bytes()); // pc
177        let mut back = Rv32CoreRegs::default();
178        back.gdb_deserialize(&bytes).unwrap();
179        assert_eq!(back, regs);
180    }
181
182    #[test]
183    fn reg_id_mapping() {
184        assert_eq!(
185            Rv32RegId::from_raw_id(0).map(|(r, _)| r),
186            Some(Rv32RegId::Gpr(0))
187        );
188        assert_eq!(
189            Rv32RegId::from_raw_id(31).map(|(r, _)| r),
190            Some(Rv32RegId::Gpr(31))
191        );
192        assert_eq!(
193            Rv32RegId::from_raw_id(32).map(|(r, _)| r),
194            Some(Rv32RegId::Pc)
195        );
196        assert!(Rv32RegId::from_raw_id(33).is_none());
197    }
198}