arm_sysregs/
manual.rs

1// SPDX-FileCopyrightText: Copyright The arm-sysregs Contributors.
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Manually implemented methods for system register types.
5
6#[cfg(feature = "el1")]
7use crate::{
8    ClidrEl1, CsselrEl1, EsrEl1, IdAa64dfr0El1, IdAa64dfr1El1, IdAa64mmfr0El1, IdAa64mmfr1El1,
9    IdAa64mmfr2El1, IdAa64mmfr3El1, IdAa64pfr0El1, IdAa64pfr1El1, MpidrEl1, SpsrEl1,
10    read_mpidr_el1,
11};
12#[cfg(feature = "el2")]
13use crate::{EsrEl2, SpsrEl2};
14#[cfg(feature = "el3")]
15use crate::{EsrEl3, MdcrEl3, SmcrEl3, SpsrEl3};
16#[cfg(feature = "el1")]
17use core::fmt::{self, Debug, Formatter};
18use num_enum::TryFromPrimitive;
19
20#[cfg(feature = "el1")]
21impl ClidrEl1 {
22    /// Returns the inner cache boundary level.
23    pub fn icb_level(self) -> Option<CacheLevel> {
24        let icb = self.icb();
25        if icb != 0 {
26            Some(CacheLevel(icb as u8))
27        } else {
28            None
29        }
30    }
31
32    /// Returns Cache Type [1-7] fields.
33    pub fn cache_type(self, level: CacheLevel) -> CacheType {
34        self.ctype(level.level().into()).try_into().unwrap()
35    }
36}
37
38#[cfg(feature = "el1")]
39impl CsselrEl1 {
40    /// Creates new instance. TnD is only valid if FEAT_MTE2 is implemented.
41    pub fn new(tnd: bool, level: CacheLevel, ind: bool) -> Self {
42        let mut instance = Self::from_bits_retain(u64::from(level) << 1);
43
44        if ind {
45            instance |= Self::IND;
46        } else if tnd {
47            // TnD is only valid if InD is not set.
48            instance |= Self::TND;
49        }
50
51        instance
52    }
53
54    /// Returns the cache level of requested cache.
55    pub fn cache_level(self) -> CacheLevel {
56        CacheLevel(self.level() + 1)
57    }
58}
59
60#[cfg(feature = "el1")]
61impl EsrEl1 {
62    /// Mask for the parts of an ESR value containing the opcode.
63    pub const ISS_SYSREG_OPCODE_MASK: Self = Self::from_bits_retain(0x003f_fc1e);
64}
65
66#[cfg(feature = "el1")]
67impl Debug for EsrEl1 {
68    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
69        write!(f, "EsrEl1({:#x})", self.0)
70    }
71}
72
73#[cfg(feature = "el2")]
74impl EsrEl2 {
75    /// Mask for the parts of an ESR value containing the opcode.
76    pub const ISS_SYSREG_OPCODE_MASK: Self = Self::from_bits_retain(0x003f_fc1e);
77}
78
79#[cfg(feature = "el2")]
80impl Debug for EsrEl2 {
81    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
82        write!(f, "EsrEl2({:#x})", self.0)
83    }
84}
85
86#[cfg(feature = "el3")]
87impl EsrEl3 {
88    /// Mask for the parts of an ESR value containing the opcode.
89    pub const ISS_SYSREG_OPCODE_MASK: Self = Self::from_bits_retain(0x003f_fc1e);
90}
91
92#[cfg(feature = "el3")]
93impl Debug for EsrEl3 {
94    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
95        write!(f, "EsrEl3({:#x})", self.0)
96    }
97}
98
99#[cfg(feature = "el1")]
100impl IdAa64dfr0El1 {
101    const SYS_REG_TRACE_SUPPORTED: u8 = 1;
102    const SPE_SUPPORTED: u8 = 1;
103    const TRF_SUPPORTED: u8 = 1;
104    const TRBE_NOT_SUPPORTED: u8 = 0;
105    const MTPMU_SUPPORTED: u8 = 1;
106
107    /// Trace support. Indicates whether System register interface to a PE trace unit is
108    /// implemented.
109    pub fn is_feat_sys_reg_trace_present(self) -> bool {
110        self.tracever() == Self::SYS_REG_TRACE_SUPPORTED
111    }
112
113    /// Indicates whether Armv8.1 Statistical Profiling Extension is implemented.
114    pub fn is_feat_spe_present(self) -> bool {
115        self.pmsver() >= Self::SPE_SUPPORTED
116    }
117
118    /// Indicates whether Armv8.4 Self-hosted Trace Extension is implemented.
119    pub fn is_feat_trf_present(self) -> bool {
120        self.tracefilt() == Self::TRF_SUPPORTED
121    }
122
123    /// Indicates whether Trace Buffer Extension is implemented.
124    pub fn is_feat_trbe_present(self) -> bool {
125        self.tracebuffer() != Self::TRBE_NOT_SUPPORTED
126    }
127
128    /// Indicates whether Multi Threaded PMU Extension is implemented.
129    pub fn is_feat_mtpmu_present(self) -> bool {
130        self.mtpmu() == Self::MTPMU_SUPPORTED
131    }
132}
133
134#[cfg(feature = "el1")]
135impl IdAa64dfr1El1 {
136    const EBEP_IMPLEMENTED: u8 = 0b1;
137
138    /// Indicates whether FEAT_EBEP is implemented.
139    pub fn is_feat_ebep_present(self) -> bool {
140        self.ebep() == Self::EBEP_IMPLEMENTED
141    }
142}
143
144#[cfg(feature = "el1")]
145impl IdAa64mmfr0El1 {
146    const FGT_SUPPORTED: u8 = 0b0001;
147    const FGT2_SUPPORTED: u8 = 0b0001;
148
149    /// Indicates whether Fine Grain Traps Extension is implemented.
150    pub fn is_feat_fgt_present(self) -> bool {
151        let val = self.fgt();
152        val == Self::FGT_SUPPORTED || val == Self::FGT2_SUPPORTED
153    }
154
155    /// Indicates whether Fine Grain Traps 2 Extension is implemented.
156    pub fn is_feat_fgt2_present(self) -> bool {
157        self.fgt() == Self::FGT2_SUPPORTED
158    }
159}
160
161#[cfg(feature = "el1")]
162impl IdAa64mmfr1El1 {
163    const VH_SUPPORTED: u8 = 0b0001;
164    const HCX_SUPPORTED: u8 = 0b0001;
165
166    /// Indicates presence of FEAT_VHE.
167    pub fn is_feat_vhe_present(self) -> bool {
168        self.vh() >= Self::VH_SUPPORTED
169    }
170
171    /// Indicates presence of FEAT_HCX.
172    pub fn is_feat_hcx_present(self) -> bool {
173        self.hcx() >= Self::HCX_SUPPORTED
174    }
175}
176
177#[cfg(feature = "el1")]
178impl IdAa64mmfr2El1 {
179    const CCIDX_64_BIT: u8 = 0b0001;
180
181    /// Checks whether 64-bit format is implemented for all levels of the CCSIDR_EL1.
182    pub fn has_64_bit_ccsidr_el1(self) -> bool {
183        self.ccidx() == Self::CCIDX_64_BIT
184    }
185}
186
187#[cfg(feature = "el1")]
188impl IdAa64mmfr3El1 {
189    const TCRX_SUPPORTED: u8 = 1;
190
191    /// Indicates presence of FEAT_TCR2.
192    pub fn is_feat_tcr2_present(self) -> bool {
193        self.tcrx() >= Self::TCRX_SUPPORTED
194    }
195}
196
197#[cfg(feature = "el1")]
198impl IdAa64pfr0El1 {
199    const SVE_SUPPORTED: u8 = 1;
200    const MPAM_SUPPORTED: u8 = 1;
201
202    /// Indicates whether SVE is implemented.
203    pub fn is_feat_sve_present(self) -> bool {
204        self.sve() == Self::SVE_SUPPORTED
205    }
206
207    /// Indicates whether MPAM Extension is implemented.
208    pub fn is_feat_mpam_present(self) -> bool {
209        self.mpam() == Self::MPAM_SUPPORTED
210    }
211}
212
213#[cfg(feature = "el1")]
214impl IdAa64pfr1El1 {
215    const SSBS_IMPLEMENTED: u8 = 0b1;
216    const MTE_IMPLEMENTED: u8 = 0b0001;
217    const MTE2_IMPLEMENTED: u8 = 0b0010;
218    const SME_IMPLEMENTED: u8 = 0b0001;
219    const SME2_IMPLEMENTED: u8 = 0b0010;
220    const NMI_IMPLEMENTED: u8 = 0b1;
221    const GCS_IMPLEMENTED: u8 = 0b1;
222
223    /// Indicates whether FEAT_SSBS is implemented.
224    pub fn is_feat_ssbs_present(self) -> bool {
225        self.ssbs() >= Self::SSBS_IMPLEMENTED
226    }
227
228    /// Indicates whether FEAT_MTE is implemented.
229    pub fn is_feat_mte_present(self) -> bool {
230        self.mte() >= Self::MTE_IMPLEMENTED
231    }
232
233    /// Indicates whether FEAT_MTE2 is implemented.
234    pub fn is_feat_mte2_present(self) -> bool {
235        self.mte() >= Self::MTE2_IMPLEMENTED
236    }
237
238    /// Indicates whether FEAT_SME is implemented.
239    pub fn is_feat_sme_present(self) -> bool {
240        self.sme() >= Self::SME_IMPLEMENTED
241    }
242
243    /// Indicates whether FEAT_SME2 is implemented.
244    pub fn is_feat_sme2_present(self) -> bool {
245        self.sme() >= Self::SME2_IMPLEMENTED
246    }
247
248    /// Indicates whether FEAT_NMI is implemented.
249    pub fn is_feat_nmi_present(self) -> bool {
250        self.nmi() == Self::NMI_IMPLEMENTED
251    }
252
253    /// Indicates whether FEAT_GCS is implemented.
254    pub fn is_feat_gcs_present(self) -> bool {
255        self.gcs() == Self::GCS_IMPLEMENTED
256    }
257}
258
259#[cfg(feature = "el3")]
260impl MdcrEl3 {
261    /// Set to 0b10 to disable AArch32 Secure self-hosted privileged debug from S-EL1.
262    pub const SPD32: Self = Self::from_bits_retain(0b10 << 14);
263    /// Non-secure state owns the Profiling Buffer. Profiling is disabled in Secure and Realm
264    /// states.
265    pub const NSPB_NS: Self = Self::from_bits_retain(0b11 << 12);
266    /// Enable TRBE register access for the security state that owns the buffer.
267    pub const NSTB_EN: Self = Self::from_bits_retain(1 << 24);
268    /// Together with MDCR_EL3.NSTBE determines which security state owns the trace buffer
269    pub const NSTB_SS: Self = Self::from_bits_retain(1 << 25);
270}
271
272#[cfg(feature = "el1")]
273impl MpidrEl1 {
274    /// Size in bits of the affinity fields.
275    pub const AFFINITY_BITS: usize = 8;
276
277    /// Converts a PSCI MPIDR value into the equivalent `MpidrEL1` value.
278    ///
279    /// This reads the MT and U bits from the current CPU's MPIDR_EL1 value and combines them with
280    /// the affinity values from the given `psci_mpidr`.
281    ///
282    /// This assumes that the MPIDR_EL1 values of all CPUs in a system have the same values for the
283    /// MT and U bits.
284    pub fn from_psci_mpidr(psci_mpidr: u64) -> Self {
285        let mpidr_el1 = read_mpidr_el1();
286        Self::from_bits_retain(psci_mpidr) | (mpidr_el1 & (Self::MT | Self::U))
287    }
288}
289
290#[cfg(feature = "el3")]
291impl SmcrEl3 {
292    /// Build SMCR_EL3 register value from given SSVE vector length.
293    pub fn from_ssve_vector_len(vector_length: u64) -> Self {
294        Self::from_bits_retain(((vector_length - 1) / 128) & Self::LEN_MASK)
295    }
296}
297
298#[cfg(feature = "el1")]
299impl SpsrEl1 {
300    /// All of the N, Z, C and V bits.
301    pub const NZCV: Self = Self::V.union(Self::C).union(Self::Z).union(Self::N);
302}
303
304#[cfg(feature = "el2")]
305impl SpsrEl2 {
306    /// All of the N, Z, C and V bits.
307    pub const NZCV: Self = Self::V.union(Self::C).union(Self::Z).union(Self::N);
308}
309
310#[cfg(feature = "el3")]
311impl SpsrEl3 {
312    /// AArch64 execution state, EL0.
313    pub const M_AARCH64_EL0: Self = Self::from_bits_retain(0b00000);
314    /// AArch64 execution state, EL1 with SP_EL0.
315    pub const M_AARCH64_EL1T: Self = Self::from_bits_retain(0b00100);
316    /// AArch64 execution state, EL1 with SP_EL1.
317    pub const M_AARCH64_EL1H: Self = Self::from_bits_retain(0b00101);
318    /// AArch64 execution state, EL2 with SP_EL0.
319    pub const M_AARCH64_EL2T: Self = Self::from_bits_retain(0b01000);
320    /// AArch64 execution state, EL2 with SP_EL2.
321    pub const M_AARCH64_EL2H: Self = Self::from_bits_retain(0b01001);
322    /// AArch64 execution state, EL3 with SP_EL0.
323    pub const M_AARCH64_EL3T: Self = Self::from_bits_retain(0b01100);
324    /// AArch64 execution state, EL3 with SP_EL3.
325    pub const M_AARCH64_EL3H: Self = Self::from_bits_retain(0b01101);
326
327    /// Exception was taken with PSTATE.SP set to SP_EL0.
328    pub const SP_EL0: Self = Self::from_bits_retain(0);
329    /// Exception was taken with PSTATE.SP set to SP_ELx.
330    pub const SP_ELX: Self = Self::from_bits_retain(1);
331
332    /// All of the N, Z, C and V bits.
333    pub const NZCV: Self = Self::V.union(Self::C).union(Self::Z).union(Self::N);
334
335    /// Speculative Store Bypass Safe.
336    pub const SSBS: Self = Self::from_bits_retain(1 << 12);
337
338    const EL_MASK: u64 = 0x3;
339    const EL_SHIFT: usize = 2;
340    const SP_MASK: u64 = 0x1;
341
342    /// Returns the value of the EL field.
343    pub const fn exception_level(self) -> ExceptionLevel {
344        match (self.bits() >> Self::EL_SHIFT) & Self::EL_MASK {
345            0 => ExceptionLevel::El0,
346            1 => ExceptionLevel::El1,
347            2 => ExceptionLevel::El2,
348            3 => ExceptionLevel::El3,
349            _ => unreachable!(),
350        }
351    }
352
353    /// Returns the value of the SP field.
354    pub const fn stack_pointer(self) -> StackPointer {
355        match self.bits() & Self::SP_MASK {
356            0 => StackPointer::El0,
357            1 => StackPointer::ElX,
358            _ => unreachable!(),
359        }
360    }
361}
362
363/// Cache type enum.
364#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
365#[repr(u8)]
366pub enum CacheType {
367    /// No cache.
368    NoCache = 0b000,
369    /// Instruction cache only.
370    InstructionOnly = 0b001,
371    /// Data cache only.
372    DataOnly = 0b010,
373    /// Separate instruction and data caches.
374    SeparateInstructionAndData = 0b011,
375    /// Unified cache.
376    Unified = 0b100,
377}
378
379/// Wrapper type for describing cache level in a human readable format, i.e. L3 cache = `CacheLevel(3)`
380#[derive(Clone, Copy, Debug, Eq, PartialEq)]
381pub struct CacheLevel(pub(crate) u8);
382
383impl CacheLevel {
384    /// Creates new instance.
385    pub fn new(level: u8) -> Self {
386        assert!((1..8).contains(&level));
387        Self(level)
388    }
389
390    /// Returns the level value.
391    pub fn level(&self) -> u8 {
392        self.0
393    }
394}
395
396impl From<CacheLevel> for u32 {
397    fn from(value: CacheLevel) -> Self {
398        (value.0 - 1).into()
399    }
400}
401
402impl From<CacheLevel> for u64 {
403    fn from(value: CacheLevel) -> Self {
404        u32::from(value).into()
405    }
406}
407
408/// An AArch64 exception level.
409#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, TryFromPrimitive)]
410#[repr(u8)]
411pub enum ExceptionLevel {
412    /// Exception level 0.
413    El0 = 0,
414    /// Exception level 1.
415    El1 = 1,
416    /// Exception level 2.
417    El2 = 2,
418    /// Exception level 3.
419    El3 = 3,
420}
421
422/// Values for SPSEL.
423#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, TryFromPrimitive)]
424#[repr(u8)]
425pub enum StackPointer {
426    /// Use SP_EL0.
427    El0 = 0,
428    /// Use SP_EL1, SP_EL2 or SP_EL3 according to the current exception level.
429    ElX = 1,
430}
431
432#[cfg(test)]
433mod tests {
434    #[cfg(feature = "el1")]
435    use super::*;
436
437    #[test]
438    #[cfg(feature = "el1")]
439    fn debug_mpidr_el1() {
440        assert_eq!(format!("{:?}", MpidrEl1::empty()), "MpidrEl1(0x0)");
441        assert_eq!(
442            format!("{:?}", MpidrEl1::MT | MpidrEl1::U),
443            "MpidrEl1(MT | U)"
444        );
445        assert_eq!(
446            format!("{:?}", MpidrEl1::from_bits_retain(0x12_4134_5678)),
447            "MpidrEl1(MT | U | 0x1200345678)"
448        );
449    }
450
451    #[cfg(feature = "el1")]
452    #[test]
453    fn debug_spsr_el1() {
454        assert_eq!(format!("{:?}", SpsrEl1::empty()), "SpsrEl1(0x0)");
455        assert_eq!(format!("{:?}", SpsrEl1::NZCV), "SpsrEl1(V | C | Z | N)");
456    }
457
458    #[cfg(feature = "el2")]
459    #[test]
460    fn debug_spsr_el2() {
461        assert_eq!(format!("{:?}", SpsrEl2::empty()), "SpsrEl2(0x0)");
462        assert_eq!(format!("{:?}", SpsrEl2::NZCV), "SpsrEl2(V | C | Z | N)");
463    }
464
465    #[cfg(feature = "el3")]
466    #[test]
467    fn debug_spsr_el3() {
468        assert_eq!(format!("{:?}", SpsrEl3::empty()), "SpsrEl3(0x0)");
469        assert_eq!(format!("{:?}", SpsrEl3::NZCV), "SpsrEl3(V | C | Z | N)");
470        assert_eq!(format!("{:?}", SpsrEl3::M_AARCH64_EL3H), "SpsrEl3(0xd)");
471    }
472
473    #[cfg(feature = "el1")]
474    #[test]
475    fn debug_esr_el1() {
476        assert_eq!(format!("{:?}", EsrEl1::empty()), "EsrEl1(0x0)");
477        assert_eq!(format!("{:?}", EsrEl1::IL), "EsrEl1(0x2000000)");
478        assert_eq!(
479            format!("{:?}", EsrEl1::ISS_SYSREG_OPCODE_MASK),
480            "EsrEl1(0x3ffc1e)"
481        );
482    }
483
484    #[cfg(feature = "el2")]
485    #[test]
486    fn debug_esr_el2() {
487        assert_eq!(format!("{:?}", EsrEl2::empty()), "EsrEl2(0x0)");
488        assert_eq!(format!("{:?}", EsrEl2::IL), "EsrEl2(0x2000000)");
489        assert_eq!(
490            format!("{:?}", EsrEl2::ISS_SYSREG_OPCODE_MASK),
491            "EsrEl2(0x3ffc1e)"
492        );
493    }
494
495    #[cfg(feature = "el3")]
496    #[test]
497    fn debug_esr_el3() {
498        assert_eq!(format!("{:?}", EsrEl3::empty()), "EsrEl3(0x0)");
499        assert_eq!(format!("{:?}", EsrEl3::IL), "EsrEl3(0x2000000)");
500        assert_eq!(
501            format!("{:?}", EsrEl3::ISS_SYSREG_OPCODE_MASK),
502            "EsrEl3(0x3ffc1e)"
503        );
504    }
505}