aarch64_sys_reg/
lib.rs

1// Copyright 2025 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! AArch64 system register names and encoding.
6
7mod consts;
8mod funcs;
9mod gen;
10
11#[cfg(test)]
12mod tests;
13
14pub use consts::*;
15pub use funcs::*;
16pub use gen::*;
17use serde::Deserialize;
18use serde::Serialize;
19use thiserror::Error;
20
21#[derive(Error, Debug)]
22pub enum Error {
23    #[error("invalid CRm {0}")]
24    InvalidCrm(u8),
25    #[error("invalid CRn {0}")]
26    InvalidCrn(u8),
27    #[error("invalid Op0 {0}")]
28    InvalidOp0(u8),
29    #[error("invalid Op1 {0}")]
30    InvalidOp1(u8),
31    #[error("invalid Op2 {0}")]
32    InvalidOp2(u8),
33}
34
35pub type Result<T> = std::result::Result<T, Error>;
36
37/// AArch64 system register as used in MSR/MRS instructions.
38#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize)]
39#[serde(transparent)]
40pub struct AArch64SysRegId(u16);
41
42impl AArch64SysRegId {
43    /// Construct a system register ID from Op0, Op1, CRn, CRm, Op2.
44    ///
45    /// The meanings of the arguments are described in the ARMv8 Architecture Reference Manual
46    /// "System instruction class encoding overview" section.
47    pub fn new(op0: u8, op1: u8, crn: u8, crm: u8, op2: u8) -> Result<Self> {
48        if op0 > 0b11 {
49            return Err(Error::InvalidOp0(op0));
50        }
51        if op1 > 0b111 {
52            return Err(Error::InvalidOp1(op1));
53        }
54        if crn > 0b1111 {
55            return Err(Error::InvalidCrn(crn));
56        }
57        if crm > 0b1111 {
58            return Err(Error::InvalidCrm(crm));
59        }
60        if op2 > 0b111 {
61            return Err(Error::InvalidOp2(op2));
62        }
63
64        Ok(Self::new_unchecked(op0, op1, crn, crm, op2))
65    }
66
67    /// Construct a system register ID from Op0, Op1, CRn, CRm, Op2.
68    ///
69    /// Out-of-range values will be silently truncated.
70    pub const fn new_unchecked(op0: u8, op1: u8, crn: u8, crm: u8, op2: u8) -> Self {
71        let op0 = (op0 as u16 & 0b11) << 14;
72        let op1 = (op1 as u16 & 0b111) << 11;
73        let crn = (crn as u16 & 0b1111) << 7;
74        let crm = (crm as u16 & 0b1111) << 3;
75        let op2 = op2 as u16 & 0b111;
76        Self(op0 | op1 | crn | crm | op2)
77    }
78
79    #[inline]
80    pub fn from_encoded(v: u16) -> Self {
81        Self(v)
82    }
83
84    #[inline]
85    pub const fn op0(&self) -> u8 {
86        ((self.0 >> 14) & 0b11) as u8
87    }
88
89    #[inline]
90    pub const fn op1(&self) -> u8 {
91        ((self.0 >> 11) & 0b111) as u8
92    }
93
94    #[inline]
95    pub const fn crn(&self) -> u8 {
96        ((self.0 >> 7) & 0b1111) as u8
97    }
98
99    #[inline]
100    pub const fn crm(&self) -> u8 {
101        ((self.0 >> 3) & 0b1111) as u8
102    }
103
104    #[inline]
105    pub const fn op2(&self) -> u8 {
106        (self.0 & 0b111) as u8
107    }
108
109    /// Returns the system register as encoded in bits 5-20 of MRS and MSR instructions.
110    #[inline]
111    pub const fn encoded(&self) -> u16 {
112        self.0
113    }
114}
115
116impl std::fmt::Debug for AArch64SysRegId {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        f.debug_struct("AArch64SysRegId")
119            .field("Op0", &self.op0())
120            .field("Op1", &self.op1())
121            .field("CRn", &self.crn())
122            .field("CRm", &self.crm())
123            .field("Op2", &self.op2())
124            .finish()
125    }
126}