Skip to main content

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