1 use core::num::NonZeroUsize;
2 use gdbstub::arch::RegId;
3 
4 /// FPU register identifier.
5 #[derive(Debug, Clone, Copy)]
6 pub enum X87FpuInternalRegId {
7     /// Floating-point control register
8     Fctrl,
9     /// Floating-point status register
10     Fstat,
11     /// Tag word
12     Ftag,
13     /// FPU instruction pointer segment
14     Fiseg,
15     /// FPU instruction pointer offset
16     Fioff,
17     /// FPU operand segment
18     Foseg,
19     /// FPU operand offset
20     Fooff,
21     /// Floating-point opcode
22     Fop,
23 }
24 
25 impl X87FpuInternalRegId {
from_u8(val: u8) -> Option<Self>26     fn from_u8(val: u8) -> Option<Self> {
27         use self::X87FpuInternalRegId::*;
28 
29         let r = match val {
30             0 => Fctrl,
31             1 => Fstat,
32             2 => Ftag,
33             3 => Fiseg,
34             4 => Fioff,
35             5 => Foseg,
36             6 => Fooff,
37             7 => Fop,
38             _ => return None,
39         };
40         Some(r)
41     }
42 }
43 
44 /// Segment register identifier.
45 #[derive(Debug, Clone, Copy)]
46 #[allow(clippy::upper_case_acronyms)]
47 pub enum X86SegmentRegId {
48     /// Code Segment
49     CS,
50     /// Stack Segment
51     SS,
52     /// Data Segment
53     DS,
54     /// Extra Segment
55     ES,
56     /// General Purpose Segment
57     FS,
58     /// General Purpose Segment
59     GS,
60 }
61 
62 impl X86SegmentRegId {
from_u8(val: u8) -> Option<Self>63     fn from_u8(val: u8) -> Option<Self> {
64         use self::X86SegmentRegId::*;
65 
66         let r = match val {
67             0 => CS,
68             1 => SS,
69             2 => DS,
70             3 => ES,
71             4 => FS,
72             5 => GS,
73             _ => return None,
74         };
75         Some(r)
76     }
77 }
78 
79 /// 32-bit x86 core + SSE register identifier.
80 ///
81 /// Source: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-core.xml>
82 /// Additionally: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-sse.xml>
83 #[derive(Debug, Clone, Copy)]
84 #[non_exhaustive]
85 pub enum X86CoreRegId {
86     /// Accumulator
87     Eax,
88     /// Count register
89     Ecx,
90     /// Data register
91     Edx,
92     /// Base register
93     Ebx,
94     /// Stack pointer
95     Esp,
96     /// Base pointer
97     Ebp,
98     /// Source index
99     Esi,
100     /// Destination index
101     Edi,
102     /// Instruction pointer
103     Eip,
104     /// Status register
105     Eflags,
106     /// Segment registers
107     Segment(X86SegmentRegId),
108     /// FPU registers: ST0 through ST7
109     St(u8),
110     /// FPU internal registers
111     Fpu(X87FpuInternalRegId),
112     /// SIMD Registers: XMM0 through XMM7
113     Xmm(u8),
114     /// SSE Status/Control Register
115     Mxcsr,
116 }
117 
118 impl RegId for X86CoreRegId {
from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)>119     fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
120         use self::X86CoreRegId::*;
121 
122         let (r, sz): (X86CoreRegId, usize) = match id {
123             0 => (Eax, 4),
124             1 => (Ecx, 4),
125             2 => (Edx, 4),
126             3 => (Ebx, 4),
127             4 => (Esp, 4),
128             5 => (Ebp, 4),
129             6 => (Esi, 4),
130             7 => (Edi, 4),
131             8 => (Eip, 4),
132             9 => (Eflags, 4),
133             10..=15 => (Segment(X86SegmentRegId::from_u8(id as u8 - 10)?), 4),
134             16..=23 => (St(id as u8 - 16), 10),
135             24..=31 => (Fpu(X87FpuInternalRegId::from_u8(id as u8 - 24)?), 4),
136             32..=39 => (Xmm(id as u8 - 32), 16),
137             40 => (Mxcsr, 4),
138             _ => return None,
139         };
140 
141         Some((r, Some(NonZeroUsize::new(sz)?)))
142     }
143 }
144 
145 /// 64-bit x86 core + SSE register identifier.
146 ///
147 /// Source: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-core.xml>
148 /// Additionally: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-sse.xml>
149 #[derive(Debug, Clone, Copy)]
150 #[non_exhaustive]
151 pub enum X86_64CoreRegId {
152     /// General purpose registers:
153     /// RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, r8-r15
154     Gpr(u8),
155     /// Instruction pointer
156     Rip,
157     /// Status register
158     Eflags,
159     /// Segment registers
160     Segment(X86SegmentRegId),
161     /// FPU registers: ST0 through ST7
162     St(u8),
163     /// FPU internal registers
164     Fpu(X87FpuInternalRegId),
165     /// SIMD Registers: XMM0 through XMM15
166     Xmm(u8),
167     /// SSE Status/Control Register
168     Mxcsr,
169 }
170 
171 impl RegId for X86_64CoreRegId {
from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)>172     fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
173         use self::X86_64CoreRegId::*;
174 
175         let (r, sz): (X86_64CoreRegId, usize) = match id {
176             0..=15 => (Gpr(id as u8), 8),
177             16 => (Rip, 8),
178             17 => (Eflags, 4),
179             18..=23 => (Segment(X86SegmentRegId::from_u8(id as u8 - 18)?), 4),
180             24..=31 => (St(id as u8 - 24), 10),
181             32..=39 => (Fpu(X87FpuInternalRegId::from_u8(id as u8 - 32)?), 4),
182             40..=55 => (Xmm(id as u8 - 40), 16),
183             56 => (Mxcsr, 4),
184             _ => return None,
185         };
186 
187         Some((r, Some(NonZeroUsize::new(sz)?)))
188     }
189 }
190 
191 #[cfg(test)]
192 mod tests {
193     use gdbstub::arch::RegId;
194     use gdbstub::arch::Registers;
195 
196     /// Compare the following two values which are expected to be the same:
197     /// * length of data written by `Registers::gdb_serialize()` in byte
198     /// * sum of sizes of all registers obtained by `RegId::from_raw_id()`
test<Rs: Registers, RId: RegId>()199     fn test<Rs: Registers, RId: RegId>() {
200         // Obtain the data length written by `gdb_serialize` by passing a custom
201         // closure.
202         let mut serialized_data_len = 0;
203         let counter = |b: Option<u8>| {
204             if b.is_some() {
205                 serialized_data_len += 1;
206             }
207         };
208         Rs::default().gdb_serialize(counter);
209 
210         // Accumulate register sizes returned by `from_raw_id`.
211         let mut i = 0;
212         let mut sum_reg_sizes = 0;
213         while let Some((_, size)) = RId::from_raw_id(i) {
214             sum_reg_sizes += size.unwrap().get();
215             i += 1;
216         }
217 
218         assert_eq!(serialized_data_len, sum_reg_sizes);
219     }
220 
221     #[test]
test_x86()222     fn test_x86() {
223         test::<crate::x86::reg::X86CoreRegs, crate::x86::reg::id::X86CoreRegId>()
224     }
225 
226     #[test]
test_x86_64()227     fn test_x86_64() {
228         test::<crate::x86::reg::X86_64CoreRegs, crate::x86::reg::id::X86_64CoreRegId>()
229     }
230 }
231