probe_rs/core/
registers.rs

1//! Core registers are represented by the `CoreRegister` struct, and collected in a `RegisterFile` for each of the supported architectures.
2
3use crate::Error;
4use serde::{Deserialize, Serialize};
5use std::{
6    cmp::Ordering,
7    convert::Infallible,
8    fmt::{Display, Formatter},
9};
10
11/// The type of data stored in a register, with size in bits encapsulated in the enum.
12#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
13pub enum RegisterDataType {
14    /// Unsigned integer data, with size in bits encapsulated.
15    UnsignedInteger(usize),
16    /// Floating point data, with size in bits encapsulated.
17    FloatingPoint(usize),
18}
19
20/// This is used to label the register with a specific role that it plays during program execution and exception handling.
21/// This denotes the purpose of a register (e.g. `return address`),
22/// while the [`CoreRegister::name`] will contain the architecture specific label of the register.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
24pub enum RegisterRole {
25    /// The default role for a register, with the name as defined by the architecture.
26    Core(&'static str),
27    /// Function Argument registers like "A0", "a1", etc. (uses architecture specific names)
28    Argument(&'static str),
29    /// Function Return value registers like "R0", "r1", etc. (uses architecture specific names)
30    Return(&'static str),
31    /// Program Counter register
32    ProgramCounter,
33    /// Frame Pointer register
34    FramePointer,
35    /// Stack Pointer register
36    StackPointer,
37    /// Main Stack Pointer register
38    MainStackPointer,
39    /// Process Stack Pointer register
40    ProcessStackPointer,
41    /// Processor Status register
42    ProcessorStatus,
43    /// Return Address register
44    ReturnAddress,
45    /// Floating Point Unit register
46    FloatingPoint,
47    /// Floating Point Status register
48    FloatingPointStatus,
49    /// Other architecture specific roles, e.g. "saved", "temporary", "variable", etc.
50    Other(&'static str),
51}
52
53impl Display for RegisterRole {
54    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
55        match self {
56            RegisterRole::Core(name) => write!(f, "{name}"),
57            RegisterRole::Argument(name) => write!(f, "{name}"),
58            RegisterRole::Return(name) => write!(f, "{name}"),
59            RegisterRole::ProgramCounter => write!(f, "PC"),
60            RegisterRole::FramePointer => write!(f, "FP"),
61            RegisterRole::StackPointer => write!(f, "SP"),
62            RegisterRole::MainStackPointer => write!(f, "MSP"),
63            RegisterRole::ProcessStackPointer => write!(f, "PSP"),
64            RegisterRole::ProcessorStatus => write!(f, "PSR"),
65            RegisterRole::ReturnAddress => write!(f, "LR"),
66            RegisterRole::FloatingPoint => write!(f, "FPU"),
67            RegisterRole::FloatingPointStatus => write!(f, "FPSR"),
68            RegisterRole::Other(name) => write!(f, "{name}"),
69        }
70    }
71}
72
73/// The rule used to preserve the value of a register between function calls during unwinding,
74/// when DWARF unwind information is not available.
75///
76/// The rules for these are based on the 'Procedure Calling Standard' for each of the supported architectures:
77/// - Implemented: [AAPCS32](https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#core-registers)
78/// - To be Implemented: [AAPCS64](https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#core-registers)
79/// - To be Implemented: [RISC-V PCS](https://github.com/riscv-non-isa/riscv-elf-psabi-doc/releases/download/v1.0/riscv-abi.pdf)
80///
81/// Please note that the `Procedure Calling Standard` define register rules for the act of calling and/or returning from functions,
82/// while the timing of a stack unwinding is different (the `callee` has not yet completed / executed the epilogue),
83/// and the rules about preserving register values have to take this into account.
84#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
85pub enum UnwindRule {
86    /// Callee-saved, a.k.a non-volatile registers, or call-preserved.
87    /// If there is DWARF unwind `RegisterRule` we will apply it during unwind,
88    /// otherwise we assume it was untouched and preserve the current value.
89    Preserve,
90    /// Caller-saved, a.k.a. volatile registers, or call-clobbered.
91    /// If there is DWARF unwind `RegisterRule` we will apply it during unwind,
92    /// otherwise we assume it was corrupted by the callee, and clear the value.
93    /// Note: This is the default value, and is used for all situations where DWARF unwind
94    /// information is not available, and the register is not explicitly marked in the definition.
95    #[default]
96    Clear,
97    /// Additional rules are required to determine the value of the register.
98    /// These are typically found in either the DWARF unwind information,
99    /// or requires additional platform specific registers to be read.
100    SpecialRule,
101}
102
103/// Describes a core (or CPU / hardware) register with its properties.
104/// Each architecture will have a set of general purpose registers, and potentially some special purpose registers. It also happens that some general purpose registers can be used for special purposes. For instance, some ARM variants allows the `LR` (link register / return address) to be used as general purpose register `R14`."
105#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
106pub struct CoreRegister {
107    /// Some architectures have multiple names for the same register, depending on the context and the role of the register.
108    pub id: RegisterId,
109    /// If the register plays a special role (one or more) during program execution and exception handling, this array will contain the appropriate [`RegisterRole`] entry/entries.
110    pub roles: &'static [RegisterRole],
111    /// The data type of the register
112    pub data_type: RegisterDataType,
113    /// For unwind purposes (debug and/or exception handling), we need to know how values are preserved between function calls. (Applies to ARM and RISC-V)
114    #[serde(skip_serializing)]
115    pub unwind_rule: UnwindRule,
116}
117
118impl PartialOrd for CoreRegister {
119    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
120        Some(self.cmp(other))
121    }
122}
123
124impl Ord for CoreRegister {
125    fn cmp(&self, other: &Self) -> Ordering {
126        self.id.cmp(&other.id)
127    }
128}
129
130impl Display for CoreRegister {
131    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
132        let primary_name = self.name();
133        write!(f, "{primary_name}")?;
134        if !self.roles.is_empty() {
135            for role in self.roles {
136                if primary_name != role.to_string() {
137                    write!(f, "/{role}")?;
138                }
139            }
140        }
141        Ok(())
142    }
143}
144
145impl CoreRegister {
146    /// Get the primary display name (As defined by `RegisterRole::Core()` of this register
147    pub fn name(&self) -> &'static str {
148        self.roles
149            .iter()
150            .find_map(|role| match role {
151                RegisterRole::Core(name) => Some(*name),
152                _ => None,
153            })
154            .unwrap_or("Unknown")
155    }
156
157    /// Get the id of this register
158    pub fn id(&self) -> RegisterId {
159        self.id
160    }
161
162    /// Get the type of data stored in this register
163    pub fn data_type(&self) -> RegisterDataType {
164        self.data_type.clone()
165    }
166
167    /// Get the size, in bits, of this register
168    pub fn size_in_bits(&self) -> usize {
169        match self.data_type() {
170            RegisterDataType::UnsignedInteger(size_in_bits) => size_in_bits,
171            RegisterDataType::FloatingPoint(size_in_bits) => size_in_bits,
172        }
173    }
174
175    /// Get the size, in bytes, of this register
176    pub fn size_in_bytes(&self) -> usize {
177        // Always round up
178        self.size_in_bits().div_ceil(8)
179    }
180
181    /// Get the width to format this register as a hex string
182    /// Assumes a format string like `{:#0<width>x}`
183    pub fn format_hex_width(&self) -> usize {
184        (self.size_in_bytes() * 2) + 2
185    }
186
187    /// Helper method to identify registers that have a specific role in its definition.
188    pub fn register_has_role(&self, role: RegisterRole) -> bool {
189        for r in self.roles {
190            if r == &role {
191                return true;
192            }
193        }
194        false
195    }
196}
197
198impl From<CoreRegister> for RegisterId {
199    fn from(description: CoreRegister) -> RegisterId {
200        description.id
201    }
202}
203
204impl From<&CoreRegister> for RegisterId {
205    fn from(description: &CoreRegister) -> RegisterId {
206        description.id
207    }
208}
209
210/// The location of a CPU \register. This is not an actual memory address, but a core specific location that represents a specific core register.
211#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize, Deserialize)]
212#[serde(transparent)]
213pub struct RegisterId(pub u16);
214
215impl From<RegisterId> for u32 {
216    fn from(value: RegisterId) -> Self {
217        u32::from(value.0)
218    }
219}
220
221impl From<u16> for RegisterId {
222    fn from(value: u16) -> Self {
223        RegisterId(value)
224    }
225}
226
227/// A value of a core register
228///
229/// Creating a new `RegisterValue` should be done using From or Into.
230/// Converting a value back to a primitive type can be done with either
231/// a match arm or TryInto
232#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
233pub enum RegisterValue {
234    /// 32-bit unsigned integer
235    U32(u32),
236    /// 64-bit unsigned integer
237    U64(u64),
238    /// 128-bit unsigned integer, often used with SIMD / FP
239    U128(u128),
240}
241
242impl RegisterValue {
243    /// Safely increment an address by a fixed number of bytes.
244    pub fn increment_address(&mut self, bytes: usize) -> Result<(), Error> {
245        match self {
246            RegisterValue::U32(value) => {
247                if let Some(reg_val) = value.checked_add(bytes as u32) {
248                    *value = reg_val;
249                    Ok(())
250                } else {
251                    Err(Error::Other(format!(
252                        "Overflow error: Attempting to add {bytes} bytes to Register value {self}"
253                    )))
254                }
255            }
256            RegisterValue::U64(value) => {
257                if let Some(reg_val) = value.checked_add(bytes as u64) {
258                    *value = reg_val;
259                    Ok(())
260                } else {
261                    Err(Error::Other(format!(
262                        "Overflow error: Attempting to add {bytes} bytes to Register value {self}"
263                    )))
264                }
265            }
266            RegisterValue::U128(value) => {
267                if let Some(reg_val) = value.checked_add(bytes as u128) {
268                    *value = reg_val;
269                    Ok(())
270                } else {
271                    Err(Error::Other(format!(
272                        "Overflow error: Attempting to add {bytes} bytes to Register value {self}"
273                    )))
274                }
275            }
276        }
277    }
278
279    /// Safely decrement an address by a fixed number of bytes.
280    pub fn decrement_address(&mut self, bytes: usize) -> Result<(), Error> {
281        match self {
282            RegisterValue::U32(value) => {
283                if let Some(reg_val) = value.checked_sub(bytes as u32) {
284                    *value = reg_val;
285                    Ok(())
286                } else {
287                    Err(Error::Other(format!(
288                        "Overflow error: Attempting to subtract {bytes} bytes to Register value {self}"
289                    )))
290                }
291            }
292            RegisterValue::U64(value) => {
293                if let Some(reg_val) = value.checked_sub(bytes as u64) {
294                    *value = reg_val;
295                    Ok(())
296                } else {
297                    Err(Error::Other(format!(
298                        "Overflow error: Attempting to subtract {bytes} bytes to Register value {self}"
299                    )))
300                }
301            }
302            RegisterValue::U128(value) => {
303                if let Some(reg_val) = value.checked_sub(bytes as u128) {
304                    *value = reg_val;
305                    Ok(())
306                } else {
307                    Err(Error::Other(format!(
308                        "Overflow error: Attempting to subtract {bytes} bytes to Register value {self}"
309                    )))
310                }
311            }
312        }
313    }
314
315    /// Determine if the contained register value is equal to the maximum value that can be stored in that datatype.
316    pub fn is_max_value(&self) -> bool {
317        match self {
318            RegisterValue::U32(register_value) => *register_value == u32::MAX,
319            RegisterValue::U64(register_value) => *register_value == u64::MAX,
320            RegisterValue::U128(register_value) => *register_value == u128::MAX,
321        }
322    }
323
324    /// Determine if the contained register value is zero.
325    pub fn is_zero(&self) -> bool {
326        matches!(
327            self,
328            RegisterValue::U32(0) | RegisterValue::U64(0) | RegisterValue::U128(0)
329        )
330    }
331}
332
333impl Default for RegisterValue {
334    fn default() -> Self {
335        // Smallest data storage as default.
336        RegisterValue::U32(0_u32)
337    }
338}
339
340impl PartialOrd for RegisterValue {
341    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
342        let self_value = match self {
343            RegisterValue::U32(self_value) => *self_value as u128,
344            RegisterValue::U64(self_value) => *self_value as u128,
345            RegisterValue::U128(self_value) => *self_value,
346        };
347        let other_value = match other {
348            RegisterValue::U32(other_value) => *other_value as u128,
349            RegisterValue::U64(other_value) => *other_value as u128,
350            RegisterValue::U128(other_value) => *other_value,
351        };
352        self_value.partial_cmp(&other_value)
353    }
354}
355
356impl PartialEq for RegisterValue {
357    fn eq(&self, other: &Self) -> bool {
358        let self_value = match self {
359            RegisterValue::U32(self_value) => *self_value as u128,
360            RegisterValue::U64(self_value) => *self_value as u128,
361            RegisterValue::U128(self_value) => *self_value,
362        };
363        let other_value = match other {
364            RegisterValue::U32(other_value) => *other_value as u128,
365            RegisterValue::U64(other_value) => *other_value as u128,
366            RegisterValue::U128(other_value) => *other_value,
367        };
368        self_value == other_value
369    }
370}
371
372impl core::fmt::Display for RegisterValue {
373    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
374        match self {
375            RegisterValue::U32(register_value) => write!(f, "{register_value:#010x}"),
376            RegisterValue::U64(register_value) => write!(f, "{register_value:#018x}"),
377            RegisterValue::U128(register_value) => write!(f, "{register_value:#034x}"),
378        }
379    }
380}
381
382impl From<u32> for RegisterValue {
383    fn from(val: u32) -> Self {
384        Self::U32(val)
385    }
386}
387
388impl From<u64> for RegisterValue {
389    fn from(val: u64) -> Self {
390        Self::U64(val)
391    }
392}
393
394impl From<u128> for RegisterValue {
395    fn from(val: u128) -> Self {
396        Self::U128(val)
397    }
398}
399
400impl TryInto<u32> for RegisterValue {
401    type Error = crate::Error;
402
403    fn try_into(self) -> Result<u32, Self::Error> {
404        match self {
405            Self::U32(v) => Ok(v),
406            Self::U64(v) => v
407                .try_into()
408                .map_err(|_| crate::Error::Other(format!("Value '{v}' too large for u32"))),
409            Self::U128(v) => v
410                .try_into()
411                .map_err(|_| crate::Error::Other(format!("Value '{v}' too large for u32"))),
412        }
413    }
414}
415
416impl TryInto<u64> for RegisterValue {
417    type Error = crate::Error;
418
419    fn try_into(self) -> Result<u64, Self::Error> {
420        match self {
421            Self::U32(v) => Ok(v.into()),
422            Self::U64(v) => Ok(v),
423            Self::U128(v) => v
424                .try_into()
425                .map_err(|_| crate::Error::Other(format!("Value '{v}' too large for u64"))),
426        }
427    }
428}
429
430impl TryInto<u128> for RegisterValue {
431    type Error = crate::Error;
432
433    fn try_into(self) -> Result<u128, Self::Error> {
434        match self {
435            Self::U32(v) => Ok(v.into()),
436            Self::U64(v) => Ok(v.into()),
437            Self::U128(v) => Ok(v),
438        }
439    }
440}
441
442/// Extension trait to support converting errors
443/// from TryInto calls into [Error]
444pub trait RegisterValueResultExt<T> {
445    /// Convert [Result<T,E>] into `Result<T, probe_rs::Error>`
446    fn into_crate_error(self) -> Result<T, Error>;
447}
448
449/// No translation conversion case
450impl<T> RegisterValueResultExt<T> for Result<T, Error> {
451    fn into_crate_error(self) -> Result<T, Error> {
452        self
453    }
454}
455
456/// Convert from Error = Infallible to Error = probe_rs::Error
457impl<T> RegisterValueResultExt<T> for Result<T, Infallible> {
458    fn into_crate_error(self) -> Result<T, Error> {
459        Ok(self.unwrap())
460    }
461}
462
463/// A static array of all the registers ([`CoreRegister`]) that apply to a specific architecture.
464#[derive(Debug, PartialEq)]
465pub struct CoreRegisters(Vec<&'static CoreRegister>);
466
467impl CoreRegisters {
468    /// Construct a new register file from a vector of &[`CoreRegister`]s.
469    /// The register file must contain at least the essential entries for program counter, stack pointer, frame pointer and return address registers.
470    pub fn new(core_registers: Vec<&'static CoreRegister>) -> CoreRegisters {
471        CoreRegisters(core_registers)
472    }
473
474    /// Returns an iterator over the descriptions of all the non-FPU registers of this core.
475    pub fn core_registers(&self) -> impl Iterator<Item = &CoreRegister> {
476        self.0
477            .iter()
478            .filter(|r| {
479                !r.roles.iter().any(|role| {
480                    matches!(
481                        role,
482                        RegisterRole::FloatingPoint | RegisterRole::FloatingPointStatus
483                    )
484                })
485            })
486            .cloned()
487    }
488
489    /// Returns an iterator over the descriptions of all the registers of this core.
490    pub fn all_registers(&self) -> impl Iterator<Item = &CoreRegister> {
491        self.0.iter().cloned()
492    }
493
494    /// Returns the nth platform register.
495    ///
496    /// # Panics
497    ///
498    /// Panics if the register at given index does not exist.
499    pub fn core_register(&self, index: usize) -> &CoreRegister {
500        self.core_registers().nth(index).unwrap()
501    }
502
503    /// Returns the nth platform register if it is exists, `None` otherwise.
504    pub fn get_core_register(&self, index: usize) -> Option<&CoreRegister> {
505        self.core_registers().nth(index)
506    }
507
508    /// Returns the nth argument register.
509    ///
510    /// # Panics
511    ///
512    /// Panics if the register at given index does not exist.
513    pub fn argument_register(&self, index: usize) -> &CoreRegister {
514        self.get_argument_register(index).unwrap()
515    }
516
517    /// Returns the nth argument register if it is exists, `None` otherwise.
518    pub fn get_argument_register(&self, index: usize) -> Option<&CoreRegister> {
519        self.0
520            .iter()
521            .filter(|r| {
522                r.roles
523                    .iter()
524                    .any(|role| matches!(role, RegisterRole::Argument(_)))
525            })
526            .cloned()
527            .nth(index)
528    }
529
530    /// Returns the nth result register.
531    ///
532    /// # Panics
533    ///
534    /// Panics if the register at given index does not exist.
535    pub fn result_register(&self, index: usize) -> &CoreRegister {
536        self.get_result_register(index).unwrap()
537    }
538
539    /// Returns the nth result register if it is exists, `None` otherwise.
540    pub fn get_result_register(&self, index: usize) -> Option<&CoreRegister> {
541        self.0
542            .iter()
543            .filter(|r| {
544                r.roles
545                    .iter()
546                    .any(|role| matches!(role, RegisterRole::Return(_)))
547            })
548            .cloned()
549            .nth(index)
550    }
551
552    /// The program counter.
553    pub fn pc(&self) -> Option<&CoreRegister> {
554        self.0
555            .iter()
556            .find(|r| r.register_has_role(RegisterRole::ProgramCounter))
557            .cloned()
558    }
559
560    /// The main stack pointer.
561    pub fn msp(&self) -> Option<&CoreRegister> {
562        self.0
563            .iter()
564            .find(|r| r.register_has_role(RegisterRole::MainStackPointer))
565            .cloned()
566    }
567
568    /// The process stack pointer.
569    pub fn psp(&self) -> Option<&CoreRegister> {
570        self.0
571            .iter()
572            .find(|r| r.register_has_role(RegisterRole::ProcessStackPointer))
573            .cloned()
574    }
575
576    /// The processor status register.
577    pub fn psr(&self) -> Option<&CoreRegister> {
578        self.0
579            .iter()
580            .find(|r| r.register_has_role(RegisterRole::ProcessorStatus))
581            .cloned()
582    }
583
584    /// Find any register that have a `RegisterRole::Other` and the specified name.
585    pub fn other_by_name(&self, name: &str) -> Option<&CoreRegister> {
586        self.0
587            .iter()
588            .find(|r| {
589                r.roles
590                    .iter()
591                    .any(|role| matches!(role, RegisterRole::Other(n) if *n == name))
592            })
593            .cloned()
594    }
595
596    /// The fpu status register.
597    pub fn fpsr(&self) -> Option<&CoreRegister> {
598        self.0
599            .iter()
600            .find(|r| r.register_has_role(RegisterRole::FloatingPointStatus))
601            .cloned()
602    }
603
604    /// Returns an iterator over the descriptions of all the registers of this core.
605    pub fn fpu_status_registers(&self) -> Option<impl Iterator<Item = &CoreRegister>> {
606        let mut fpu_registers = self
607            .0
608            .iter()
609            .filter(|r| r.register_has_role(RegisterRole::FloatingPointStatus))
610            .peekable();
611        if fpu_registers.peek().is_some() {
612            Some(fpu_registers.cloned())
613        } else {
614            None
615        }
616    }
617
618    /// Returns an iterator over the descriptions of all the registers of this core.
619    pub fn fpu_registers(&self) -> Option<impl Iterator<Item = &CoreRegister>> {
620        let mut fpu_registers = self
621            .0
622            .iter()
623            .filter(|r| r.register_has_role(RegisterRole::FloatingPoint))
624            .peekable();
625        if fpu_registers.peek().is_some() {
626            Some(fpu_registers.cloned())
627        } else {
628            None
629        }
630    }
631
632    /// Returns the nth fpu register.
633    ///
634    /// # Panics
635    ///
636    /// Panics if the register at given index does not exist.
637    pub fn fpu_register(&self, index: usize) -> &CoreRegister {
638        self.get_fpu_register(index).unwrap()
639    }
640
641    /// Returns the nth fpu register if it is exists, `None` otherwise.
642    pub fn get_fpu_register(&self, index: usize) -> Option<&CoreRegister> {
643        self.0
644            .iter()
645            .filter(|r| r.register_has_role(RegisterRole::FloatingPoint))
646            .cloned()
647            .nth(index)
648    }
649}