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::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
203    /// Indicates whether SVE is implemented.
204    pub fn is_feat_sve_present(self) -> bool {
205        self.sve() == Self::SVE_SUPPORTED
206    }
207
208    /// Indicates whether MPAM Extension is implemented.
209    pub fn is_feat_mpam_present(self) -> bool {
210        self.mpam() == Self::MPAM_SUPPORTED
211    }
212}
213
214#[cfg(feature = "el1")]
215impl IdAa64pfr1El1 {
216    const SSBS_IMPLEMENTED: u8 = 0b1;
217    const MTE_IMPLEMENTED: u8 = 0b0001;
218    const MTE2_IMPLEMENTED: u8 = 0b0010;
219    const SME_IMPLEMENTED: u8 = 0b0001;
220    const SME2_IMPLEMENTED: u8 = 0b0010;
221    const NMI_IMPLEMENTED: u8 = 0b1;
222    const GCS_IMPLEMENTED: u8 = 0b1;
223
224    /// Indicates whether FEAT_SSBS is implemented.
225    pub fn is_feat_ssbs_present(self) -> bool {
226        self.ssbs() >= Self::SSBS_IMPLEMENTED
227    }
228
229    /// Indicates whether FEAT_MTE is implemented.
230    pub fn is_feat_mte_present(self) -> bool {
231        self.mte() >= Self::MTE_IMPLEMENTED
232    }
233
234    /// Indicates whether FEAT_MTE2 is implemented.
235    pub fn is_feat_mte2_present(self) -> bool {
236        self.mte() >= Self::MTE2_IMPLEMENTED
237    }
238
239    /// Indicates whether FEAT_SME is implemented.
240    pub fn is_feat_sme_present(self) -> bool {
241        self.sme() >= Self::SME_IMPLEMENTED
242    }
243
244    /// Indicates whether FEAT_SME2 is implemented.
245    pub fn is_feat_sme2_present(self) -> bool {
246        self.sme() >= Self::SME2_IMPLEMENTED
247    }
248
249    /// Indicates whether FEAT_NMI is implemented.
250    pub fn is_feat_nmi_present(self) -> bool {
251        self.nmi() == Self::NMI_IMPLEMENTED
252    }
253
254    /// Indicates whether FEAT_GCS is implemented.
255    pub fn is_feat_gcs_present(self) -> bool {
256        self.gcs() == Self::GCS_IMPLEMENTED
257    }
258}
259
260#[cfg(feature = "el3")]
261impl MdcrEl3 {
262    /// Set to 0b10 to disable AArch32 Secure self-hosted privileged debug from S-EL1.
263    pub const SPD32: Self = Self::from_bits_retain(0b10 << 14);
264    /// Non-secure state owns the Profiling Buffer. Profiling is disabled in Secure and Realm
265    /// states.
266    pub const NSPB_NS: Self = Self::from_bits_retain(0b11 << 12);
267    /// Enable TRBE register access for the security state that owns the buffer.
268    pub const NSTB_EN: Self = Self::from_bits_retain(1 << 24);
269    /// Together with MDCR_EL3.NSTBE determines which security state owns the trace buffer
270    pub const NSTB_SS: Self = Self::from_bits_retain(1 << 25);
271}
272
273#[cfg(feature = "el1")]
274impl MpidrEl1 {
275    /// Size in bits of the affinity fields.
276    pub const AFFINITY_BITS: usize = 8;
277
278    /// Converts a PSCI MPIDR value into the equivalent `MpidrEL1` value.
279    ///
280    /// This reads the MT and U bits from the current CPU's MPIDR_EL1 value and combines them with
281    /// the affinity values from the given `psci_mpidr`.
282    ///
283    /// This assumes that the MPIDR_EL1 values of all CPUs in a system have the same values for the
284    /// MT and U bits.
285    #[cfg(any(test, feature = "fakes", target_arch = "aarch64"))]
286    pub fn from_psci_mpidr(psci_mpidr: u64) -> Self {
287        let mpidr_el1 = read_mpidr_el1();
288        Self::from_bits_retain(psci_mpidr) | (mpidr_el1 & (Self::MT | Self::U))
289    }
290}
291
292#[cfg(feature = "el3")]
293impl SmcrEl3 {
294    /// Build SMCR_EL3 register value from given SSVE vector length.
295    pub fn from_ssve_vector_len(vector_length: u64) -> Self {
296        Self::from_bits_retain(((vector_length - 1) / 128) & Self::LEN_MASK)
297    }
298}
299
300#[cfg(feature = "el1")]
301impl SpsrEl1 {
302    /// All of the N, Z, C and V bits.
303    pub const NZCV: Self = Self::V.union(Self::C).union(Self::Z).union(Self::N);
304}
305
306#[cfg(feature = "el2")]
307impl SpsrEl2 {
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 = "el3")]
313impl SpsrEl3 {
314    /// AArch64 execution state, EL0.
315    pub const M_AARCH64_EL0: Self = Self::from_bits_retain(0b00000);
316    /// AArch64 execution state, EL1 with SP_EL0.
317    pub const M_AARCH64_EL1T: Self = Self::from_bits_retain(0b00100);
318    /// AArch64 execution state, EL1 with SP_EL1.
319    pub const M_AARCH64_EL1H: Self = Self::from_bits_retain(0b00101);
320    /// AArch64 execution state, EL2 with SP_EL0.
321    pub const M_AARCH64_EL2T: Self = Self::from_bits_retain(0b01000);
322    /// AArch64 execution state, EL2 with SP_EL2.
323    pub const M_AARCH64_EL2H: Self = Self::from_bits_retain(0b01001);
324    /// AArch64 execution state, EL3 with SP_EL0.
325    pub const M_AARCH64_EL3T: Self = Self::from_bits_retain(0b01100);
326    /// AArch64 execution state, EL3 with SP_EL3.
327    pub const M_AARCH64_EL3H: Self = Self::from_bits_retain(0b01101);
328
329    /// Exception was taken with PSTATE.SP set to SP_EL0.
330    pub const SP_EL0: Self = Self::from_bits_retain(0);
331    /// Exception was taken with PSTATE.SP set to SP_ELx.
332    pub const SP_ELX: Self = Self::from_bits_retain(1);
333
334    /// All of the N, Z, C and V bits.
335    pub const NZCV: Self = Self::V.union(Self::C).union(Self::Z).union(Self::N);
336
337    /// Speculative Store Bypass Safe.
338    pub const SSBS: Self = Self::from_bits_retain(1 << 12);
339
340    const EL_MASK: u64 = 0x3;
341    const EL_SHIFT: usize = 2;
342    const SP_MASK: u64 = 0x1;
343
344    /// Returns the value of the EL field.
345    pub const fn exception_level(self) -> ExceptionLevel {
346        match (self.bits() >> Self::EL_SHIFT) & Self::EL_MASK {
347            0 => ExceptionLevel::El0,
348            1 => ExceptionLevel::El1,
349            2 => ExceptionLevel::El2,
350            3 => ExceptionLevel::El3,
351            _ => unreachable!(),
352        }
353    }
354
355    /// Returns the value of the SP field.
356    pub const fn stack_pointer(self) -> StackPointer {
357        match self.bits() & Self::SP_MASK {
358            0 => StackPointer::El0,
359            1 => StackPointer::ElX,
360            _ => unreachable!(),
361        }
362    }
363}
364
365/// Cache type enum.
366#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
367#[repr(u8)]
368pub enum CacheType {
369    /// No cache.
370    NoCache = 0b000,
371    /// Instruction cache only.
372    InstructionOnly = 0b001,
373    /// Data cache only.
374    DataOnly = 0b010,
375    /// Separate instruction and data caches.
376    SeparateInstructionAndData = 0b011,
377    /// Unified cache.
378    Unified = 0b100,
379}
380
381/// Wrapper type for describing cache level in a human readable format, e.g. L3 cache = `CacheLevel(3)`
382#[derive(Clone, Copy, Debug, Eq, PartialEq)]
383pub struct CacheLevel(pub(crate) u8);
384
385impl CacheLevel {
386    /// Creates new instance.
387    pub fn new(level: u8) -> Self {
388        assert!((1..8).contains(&level));
389        Self(level)
390    }
391
392    /// Returns the level value.
393    pub fn level(&self) -> u8 {
394        self.0
395    }
396}
397
398impl From<CacheLevel> for u32 {
399    fn from(value: CacheLevel) -> Self {
400        (value.0 - 1).into()
401    }
402}
403
404impl From<CacheLevel> for u64 {
405    fn from(value: CacheLevel) -> Self {
406        u32::from(value).into()
407    }
408}
409
410/// An AArch64 exception level.
411#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, TryFromPrimitive)]
412#[repr(u8)]
413pub enum ExceptionLevel {
414    /// Exception level 0.
415    El0 = 0,
416    /// Exception level 1.
417    El1 = 1,
418    /// Exception level 2.
419    El2 = 2,
420    /// Exception level 3.
421    El3 = 3,
422}
423
424/// Values for SPSEL.
425#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, TryFromPrimitive)]
426#[repr(u8)]
427pub enum StackPointer {
428    /// Use SP_EL0.
429    El0 = 0,
430    /// Use SP_EL1, SP_EL2 or SP_EL3 according to the current exception level.
431    ElX = 1,
432}
433
434/// Allowed Shareability attributes.
435#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, TryFromPrimitive)]
436#[repr(u8)]
437pub enum Shareability {
438    /// Non-shareable.
439    Non = 0b00,
440    /// Outer-shareable.
441    Outer = 0b10,
442    /// Inner-shareable.
443    Inner = 0b11,
444}
445
446/// Allowed Cacheability attributes.
447#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, TryFromPrimitive)]
448#[repr(u8)]
449pub enum Cacheability {
450    /// Normal memory, Non-cacheable.
451    Non = 0b00,
452    /// Normal memory, Write-Back Read-Allocate Write-Allocate Cacheable.
453    WriteBackAllocate = 0b01,
454    /// Normal memory, Write-Through Read-Allocate No Write-Allocate Cacheable.
455    WriteThrough = 0b10,
456    /// Normal memory, Write-Back Read-Allocate No Write-Allocate Cacheable.
457    WriteBackNoAllocate = 0b11,
458}
459
460#[cfg(test)]
461mod tests {
462    #[cfg(feature = "el1")]
463    use super::*;
464
465    #[test]
466    #[cfg(feature = "el1")]
467    fn debug_mpidr_el1() {
468        assert_eq!(format!("{:?}", MpidrEl1::empty()), "MpidrEl1(0x0)");
469        assert_eq!(
470            format!("{:?}", MpidrEl1::MT | MpidrEl1::U),
471            "MpidrEl1(MT | U)"
472        );
473        assert_eq!(
474            format!("{:?}", MpidrEl1::from_bits_retain(0x12_4134_5678)),
475            "MpidrEl1(MT | U | 0x1200345678)"
476        );
477    }
478
479    #[cfg(feature = "el1")]
480    #[test]
481    fn debug_spsr_el1() {
482        assert_eq!(format!("{:?}", SpsrEl1::empty()), "SpsrEl1(0x0)");
483        assert_eq!(format!("{:?}", SpsrEl1::NZCV), "SpsrEl1(V | C | Z | N)");
484    }
485
486    #[cfg(feature = "el2")]
487    #[test]
488    fn debug_spsr_el2() {
489        assert_eq!(format!("{:?}", SpsrEl2::empty()), "SpsrEl2(0x0)");
490        assert_eq!(format!("{:?}", SpsrEl2::NZCV), "SpsrEl2(V | C | Z | N)");
491    }
492
493    #[cfg(feature = "el3")]
494    #[test]
495    fn debug_spsr_el3() {
496        assert_eq!(format!("{:?}", SpsrEl3::empty()), "SpsrEl3(0x0)");
497        assert_eq!(format!("{:?}", SpsrEl3::NZCV), "SpsrEl3(V | C | Z | N)");
498        assert_eq!(format!("{:?}", SpsrEl3::M_AARCH64_EL3H), "SpsrEl3(0xd)");
499    }
500
501    #[cfg(feature = "el1")]
502    #[test]
503    fn debug_esr_el1() {
504        assert_eq!(format!("{:?}", EsrEl1::empty()), "EsrEl1(0x0)");
505        assert_eq!(format!("{:?}", EsrEl1::IL), "EsrEl1(0x2000000)");
506        assert_eq!(
507            format!("{:?}", EsrEl1::ISS_SYSREG_OPCODE_MASK),
508            "EsrEl1(0x3ffc1e)"
509        );
510    }
511
512    #[cfg(feature = "el2")]
513    #[test]
514    fn debug_esr_el2() {
515        assert_eq!(format!("{:?}", EsrEl2::empty()), "EsrEl2(0x0)");
516        assert_eq!(format!("{:?}", EsrEl2::IL), "EsrEl2(0x2000000)");
517        assert_eq!(
518            format!("{:?}", EsrEl2::ISS_SYSREG_OPCODE_MASK),
519            "EsrEl2(0x3ffc1e)"
520        );
521    }
522
523    #[cfg(feature = "el3")]
524    #[test]
525    fn debug_esr_el3() {
526        assert_eq!(format!("{:?}", EsrEl3::empty()), "EsrEl3(0x0)");
527        assert_eq!(format!("{:?}", EsrEl3::IL), "EsrEl3(0x2000000)");
528        assert_eq!(
529            format!("{:?}", EsrEl3::ISS_SYSREG_OPCODE_MASK),
530            "EsrEl3(0x3ffc1e)"
531        );
532    }
533}