panda/api/
regs.rs

1use crate::prelude::*;
2use crate::{cpu_arch_state, CPUArchPtr};
3
4use strum::IntoEnumIterator;
5use strum_macros::{EnumIter, EnumString, ToString};
6
7/// Type-safe API to allow APIs to accept only program counters coming from
8/// syscall callbacks. To convert to integer of the width of your target, use the
9/// `.pc()` method.
10#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[repr(transparent)]
12pub struct SyscallPc(target_ulong);
13
14impl SyscallPc {
15    pub fn pc(self) -> target_ulong {
16        self.0
17    }
18}
19
20impl Reg {
21    /// Iterate over the available registers
22    pub fn iter() -> RegIter {
23        <Self as IntoEnumIterator>::iter()
24    }
25}
26
27// Arch-specific mappings ----------------------------------------------------------------------------------------------
28
29// TODO: handle AX/AH/AL, etc via shifts? Tricky b/c enum val used to index QEMU array
30/// x86 named guest registers
31#[cfg(feature = "i386")]
32#[derive(Debug, Copy, Clone, PartialEq, Eq, EnumString, EnumIter, ToString)]
33pub enum Reg {
34    EAX = 0,
35    ECX = 1,
36    EDX = 2,
37    EBX = 3,
38    ESP = 4,
39    EBP = 5,
40    ESI = 6,
41    EDI = 7,
42}
43
44/// x86 return registers
45#[cfg(feature = "i386")]
46static RET_REGS: &'static [Reg] = &[Reg::EAX];
47
48// TODO: handle EAX/AX/AH/AL, etc via shifts? Tricky b/c enum val used to index QEMU array
49/// x64 named guest registers
50#[cfg(feature = "x86_64")]
51#[derive(Debug, Copy, Clone, PartialEq, Eq, EnumString, EnumIter, ToString)]
52pub enum Reg {
53    RAX = 0,
54    RCX = 1,
55    RDX = 2,
56    RBX = 3,
57    RSP = 4,
58    RBP = 5,
59    RSI = 6,
60    RDI = 7,
61    R8 = 8,
62    R9 = 9,
63    R10 = 10,
64    R11 = 11,
65    R12 = 12,
66    R13 = 13,
67    R14 = 14,
68    R15 = 15,
69}
70
71/// x64 return registers
72#[cfg(feature = "x86_64")]
73static RET_REGS: &'static [Reg] = &[Reg::RAX];
74
75/// ARM named guest registers
76#[cfg(feature = "arm")]
77#[derive(Debug, Copy, Clone, PartialEq, Eq, EnumString, EnumIter, ToString)]
78pub enum Reg {
79    R0 = 0,
80    R1 = 1,
81    R2 = 2,
82    R3 = 3,
83    R4 = 4,
84    R5 = 5,
85    R6 = 6,
86    R7 = 7,
87    R8 = 8,
88    R9 = 9,
89    R10 = 10,
90    R11 = 11,
91    R12 = 12,
92    LR = 14,
93    SP = 13,
94    IP = 15,
95}
96
97/// ARM return registers
98#[cfg(feature = "arm")]
99static RET_REGS: &'static [Reg] = &[Reg::R0, Reg::R1, Reg::R2, Reg::R3];
100
101/// AArch64 named guest registers
102#[cfg(feature = "aarch64")]
103#[derive(Debug, Copy, Clone, PartialEq, Eq, EnumString, EnumIter, ToString)]
104pub enum Reg {
105    X0 = 0,
106    X1 = 1,
107    X2 = 2,
108    X3 = 3,
109    X4 = 4,
110    X5 = 5,
111    X6 = 6,
112    X7 = 7,
113    X8 = 8,
114    X9 = 9,
115    X10 = 10,
116    X11 = 11,
117    X12 = 12,
118    LR = 13,
119    SP = 14,
120    IP = 15,
121}
122
123/// AArch64 return registers
124#[cfg(feature = "aarch64")]
125static RET_REGS: &'static [Reg] = &[Reg::X0, Reg::X1, Reg::X2, Reg::X3];
126
127/// MIPS named guest registers
128#[cfg(any(
129    feature = "mips",
130    feature = "mipsel",
131    feature = "mips64",
132    feature = "mips64el"
133))]
134#[derive(Debug, Copy, Clone, PartialEq, Eq, EnumString, EnumIter, ToString)]
135pub enum Reg {
136    ZERO = 0,
137    AT = 1,
138    V0 = 2,
139    V1 = 3,
140    A0 = 4,
141    A1 = 5,
142    A2 = 6,
143    A3 = 7,
144    T0 = 8,
145    T1 = 9,
146    T2 = 10,
147    T3 = 11,
148    T4 = 12,
149    T5 = 13,
150    T6 = 14,
151    T7 = 15,
152    S0 = 16,
153    S1 = 17,
154    S2 = 18,
155    S3 = 19,
156    S4 = 20,
157    S5 = 21,
158    S6 = 22,
159    S7 = 23,
160    T8 = 24,
161    T9 = 25,
162    K0 = 26,
163    K1 = 27,
164    GP = 28,
165    SP = 29,
166    FP = 30,
167    RA = 31,
168}
169
170/// MIPS return registers
171#[cfg(any(
172    feature = "mips",
173    feature = "mipsel",
174    feature = "mips64",
175    feature = "mips64el"
176))]
177static RET_REGS: &'static [Reg] = &[Reg::V0, Reg::V1];
178
179// TODO: support floating point set as well? Separate QEMU bank.
180/// PPC named guest registers
181#[cfg(feature = "ppc")]
182#[derive(Debug, Copy, Clone, PartialEq, Eq, EnumString, EnumIter, ToString)]
183pub enum Reg {
184    R0 = 0,
185    R1 = 1,
186    R2 = 2,
187    R3 = 3,
188    R4 = 4,
189    R5 = 5,
190    R6 = 6,
191    R7 = 7,
192    R8 = 8,
193    R9 = 9,
194    R10 = 10,
195    R11 = 11,
196    R12 = 12,
197    R13 = 13,
198    R14 = 14,
199    R15 = 15,
200    R16 = 16,
201    R17 = 17,
202    R18 = 18,
203    R19 = 19,
204    R20 = 20,
205    R21 = 21,
206    R22 = 22,
207    R23 = 23,
208    R24 = 24,
209    R25 = 25,
210    R26 = 26,
211    R27 = 27,
212    R28 = 28,
213    R29 = 29,
214    R30 = 30,
215    R31 = 31,
216    LR = 100, // Special case - separate bank in QEMU
217}
218
219/// PPC return registers
220#[cfg(feature = "ppc")]
221static RET_REGS: &'static [Reg] = &[Reg::R3, Reg::R4];
222
223// Getters/setters -----------------------------------------------------------------------------------------------------
224
225/// Get stack pointer register
226pub fn reg_sp() -> Reg {
227    #[cfg(feature = "i386")]
228    return Reg::ESP;
229
230    #[cfg(feature = "x86_64")]
231    return Reg::RSP;
232
233    #[cfg(any(
234        feature = "arm",
235        feature = "aarch64",
236        feature = "mips",
237        feature = "mipsel",
238        feature = "mips64",
239        feature = "mips64el"
240    ))]
241    return Reg::SP;
242
243    #[cfg(any(feature = "ppc"))]
244    return Reg::R1;
245}
246
247/// Get return value registers
248/// MIPS/ARM/PPC: Note that most C code will only use the first register, e.g. index 0 in returned `Vec`
249pub fn reg_ret_val() -> &'static [Reg] {
250    return &RET_REGS;
251}
252
253/// Get return address register
254pub fn reg_ret_addr() -> Option<Reg> {
255    #[cfg(feature = "i386")]
256    return None;
257
258    #[cfg(feature = "x86_64")]
259    return None;
260
261    #[cfg(any(feature = "arm", feature = "aarch64", feature = "ppc"))]
262    return Some(Reg::LR);
263
264    #[cfg(any(
265        feature = "mips",
266        feature = "mipsel",
267        feature = "mips64",
268        feature = "mips64el"
269    ))]
270    return Some(Reg::RA);
271}
272
273/// Read the current value of a register
274pub fn get_reg<T: Into<Reg>>(cpu: &CPUState, reg: T) -> target_ulong {
275    let cpu_arch = cpu_arch_state!(cpu);
276    let val;
277
278    #[cfg(any(feature = "i386", feature = "x86_64", feature = "arm"))]
279    unsafe {
280        val = (*cpu_arch).regs[reg.into() as usize];
281    }
282
283    #[cfg(feature = "aarch64")]
284    unsafe {
285        val = (*cpu_arch).xregs[reg.into() as usize];
286    }
287
288    #[cfg(any(
289        feature = "mips",
290        feature = "mipsel",
291        feature = "mips64",
292        feature = "mips64el"
293    ))]
294    unsafe {
295        val = (*cpu_arch).active_tc.gpr[reg.into() as usize];
296    }
297
298    #[cfg(any(feature = "ppc"))]
299    unsafe {
300        let reg_enum = reg.into();
301        if reg_enum == Reg::LR {
302            val = (*cpu_arch).lr;
303        } else {
304            val = (*cpu_arch).gpr[reg_enum as usize];
305        }
306    }
307
308    val
309}
310
311/// Set the value for a register
312pub fn set_reg<T: Into<Reg>>(cpu: &CPUState, reg: T, val: target_ulong) {
313    let cpu_arch = cpu_arch_state!(cpu);
314
315    #[cfg(any(feature = "i386", feature = "x86_64", feature = "arm"))]
316    unsafe {
317        (*cpu_arch).regs[reg.into() as usize] = val;
318    }
319
320    #[cfg(any(
321        feature = "mips",
322        feature = "mipsel",
323        feature = "mips64",
324        feature = "mips64el"
325    ))]
326    unsafe {
327        (*cpu_arch).active_tc.gpr[reg.into() as usize] = val;
328    }
329
330    #[cfg(any(feature = "ppc"))]
331    unsafe {
332        let reg_enum = reg.into();
333        if reg_enum == Reg::LR {
334            (*cpu_arch).lr = val;
335        } else {
336            (*cpu_arch).gpr[reg_enum as usize] = val;
337        }
338    }
339
340    #[cfg(feature = "aarch64")]
341    unsafe {
342        (*cpu_arch).xregs[reg.into() as usize] = val;
343    }
344}
345
346pub fn get_pc(cpu: &CPUState) -> target_ulong {
347    let cpu_arch = cpu_arch_state!(cpu);
348    let val;
349
350    #[cfg(any(feature = "x86_64", feature = "i386"))]
351    unsafe {
352        val = (*cpu_arch).eip;
353    }
354
355    #[cfg(feature = "arm")]
356    unsafe {
357        val = (*cpu_arch).regs[15];
358    }
359
360    #[cfg(feature = "aarch64")]
361    unsafe {
362        val = (*cpu_arch).pc;
363    }
364
365    #[cfg(feature = "ppc")]
366    unsafe {
367        val = (*cpu_arch).nip;
368    }
369
370    #[cfg(any(
371        feature = "mips",
372        feature = "mipsel",
373        feature = "mips64",
374        feature = "mips64el"
375    ))]
376    unsafe {
377        val = (*cpu_arch).active_tc.PC;
378    }
379
380    val
381}
382
383pub fn set_pc(cpu: &mut CPUState, pc: target_ulong) {
384    let cpu_arch = cpu_arch_state!(cpu);
385
386    #[cfg(any(feature = "x86_64", feature = "i386"))]
387    unsafe {
388        (*cpu_arch).eip = pc;
389    }
390
391    #[cfg(feature = "arm")]
392    unsafe {
393        (*cpu_arch).regs[15] = pc;
394    }
395
396    #[cfg(feature = "aarch64")]
397    unsafe {
398        (*cpu_arch).pc = pc;
399    }
400
401    #[cfg(feature = "ppc")]
402    unsafe {
403        (*cpu_arch).nip = pc;
404    }
405
406    #[cfg(any(
407        feature = "mips",
408        feature = "mipsel",
409        feature = "mips64",
410        feature = "mips64el"
411    ))]
412    unsafe {
413        (*cpu_arch).active_tc.PC = pc;
414    }
415}
416
417// Printing ------------------------------------------------------------------------------------------------------------
418
419/// Print the contents of all registers
420pub fn dump_regs(cpu: &CPUState) {
421    for reg in Reg::iter() {
422        println!("{:?}:\t0x{:016x}", reg, get_reg(cpu, reg));
423    }
424}