Skip to main content

libarch/arm64/
cache.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use bitrs::{bitfield_repr, layout, multilayout};
6use regio::arm64::{SysReg, spec};
7
8/// [arm/v8]: D13.2.33  CTR_EL0, Cache Type Register.
9pub const CTR_EL0: SysReg<spec::CTR_EL0, CacheTypeRegister> = SysReg::new();
10
11/// L1 instruction cache policy.
12#[bitfield_repr(u8)]
13#[derive(Clone, Copy)]
14pub enum L1ICachePolicy {
15    Vpipt = 0b00,
16    Aivivt = 0b01,
17    Vipt = 0b10,
18    Pipt = 0b11,
19}
20
21layout!({
22    /// The layout of [`CTR_EL0`].
23    pub struct CacheTypeRegister(u64);
24    {
25        let __ @ 63..38;
26        let tmin_line @ 37..32;
27        let __ @ 31 = 1;
28        let __ @ 30;
29        let dic @ 29;
30        let idc @ 28;
31        let cwg @ 27..24;
32        let erg @ 23..20;
33
34        /// log2 of the number of words in the smallest data cache line.
35        let dmin_line @ 19..16;
36
37        let l1_ip @ 15..14: L1ICachePolicy;
38        let __ @ 13..4;
39
40        /// log2 of the number of words in the smallest instruction cache line.
41        let imin_line @ 3..0;
42    }
43});
44
45impl CacheTypeRegister {
46    /// Returns the smallest data cache line size in bytes.
47    pub const fn dcache_line_size(&self) -> usize {
48        (1 << self.dmin_line()) * size_of::<u32>()
49    }
50
51    /// Returns the smallest instruction cache line size in bytes.
52    pub const fn icache_line_size(&self) -> usize {
53        (1 << self.imin_line()) * size_of::<u32>()
54    }
55}
56
57/// [arm/v8]: D13.2.36  DCZID_EL0, Data Cache Zero ID register
58pub const DCZID_EL0: SysReg<spec::DCZID_EL0, DataCacheZeroIdRegister> = SysReg::new();
59
60layout!({
61    /// The layout of [`DCZID_EL0`].
62    pub struct DataCacheZeroIdRegister(u64);
63    {
64        let __ @ 63..5;
65        let dzp @ 4;
66        let bz @ 3..0;
67    }
68});
69
70impl DataCacheZeroIdRegister {
71    /// Returns the block size for DC ZVA in bytes.
72    pub fn zva_line_size(&self) -> usize {
73        (1 << self.bz()) * size_of::<u32>()
74    }
75}
76
77/// [arm/sysreg]/clidr_el1: CLIDR_EL1, Cache Level ID Register
78pub const CLIDR_EL1: SysReg<spec::CLIDR_EL1, CacheLevelIdRegister> = SysReg::new();
79
80/// The type of cache implemented at a given level, per the `ctype<n>` fields
81/// of [`CacheLevelIdRegister`].
82#[bitfield_repr(u8)]
83#[derive(Clone, Copy)]
84pub enum CacheType {
85    /// No cache at this level; no caches exist at higher levels either.
86    None = 0b000,
87    Instruction = 0b001,
88    Data = 0b010,
89    /// Separate instruction and data caches.
90    Separate = 0b011,
91    Unified = 0b100,
92}
93
94impl CacheType {
95    pub const fn has_instruction_cache(self) -> bool {
96        matches!(self, Self::Instruction | Self::Separate)
97    }
98
99    pub const fn has_data_cache(self) -> bool {
100        matches!(self, Self::Data | Self::Separate)
101    }
102}
103
104/// The type of allocation tag cache implemented at a given level, per the
105/// `ttype<n>` fields of [`CacheLevelIdRegister`] (FEAT_MTE2).
106#[bitfield_repr(u8)]
107#[derive(Clone, Copy)]
108pub enum TagCacheType {
109    None = 0b00,
110    /// Separate allocation tag cache.
111    Separate = 0b01,
112    /// Unified allocation tag and data cache, with tags and data in unified
113    /// lines.
114    UnifiedLines = 0b10,
115    /// Unified allocation tag and data cache, with tags and data in separate
116    /// lines.
117    SeparateLines = 0b11,
118}
119
120layout!({
121    /// The layout of [`CLIDR_EL1`].
122    pub struct CacheLevelIdRegister(u64);
123    {
124        let __ @ 63..47;
125        let ttype7 @ 46..45: TagCacheType; // Tag cache type at L7
126        let ttype6 @ 44..43: TagCacheType;
127        let ttype5 @ 42..41: TagCacheType;
128        let ttype4 @ 40..39: TagCacheType;
129        let ttype3 @ 38..37: TagCacheType;
130        let ttype2 @ 36..35: TagCacheType;
131        let ttype1 @ 34..33: TagCacheType;
132        let icb @ 32..30; // Inner cache boundary
133        let lou_u @ 29..27; // Level of Unification Uniprocessor
134        let loc @ 26..24; // Level of Coherence
135        let lou_is @ 23..21; // Level of Unification Inner Shareable
136        let ctype7 @ 20..18: CacheType; // Cache type at L7
137        let ctype6 @ 17..15: CacheType;
138        let ctype5 @ 14..12: CacheType;
139        let ctype4 @ 11..9: CacheType;
140        let ctype3 @ 8..6: CacheType;
141        let ctype2 @ 5..3: CacheType;
142        let ctype1 @ 2..0: CacheType;
143    }
144});
145
146impl CacheLevelIdRegister {
147    /// The number of cache levels described by the register.
148    pub const MAX_LEVELS: usize = 7;
149
150    /// Returns the type of cache at `level`, which is zero-based (L1 is level
151    /// 0) to match the `level` field of [`CacheSizeSelectionRegister`].
152    ///
153    /// Panics if `level` is not below [`Self::MAX_LEVELS`] or the field holds
154    /// a reserved value.
155    pub fn cache_type(&self, level: usize) -> CacheType {
156        match level {
157            0 => self.ctype1(),
158            1 => self.ctype2(),
159            2 => self.ctype3(),
160            3 => self.ctype4(),
161            4 => self.ctype5(),
162            5 => self.ctype6(),
163            6 => self.ctype7(),
164            _ => panic!("cache level {level} out of range"),
165        }
166    }
167
168    /// Returns the type of allocation tag cache at `level`, which is
169    /// zero-based (L1 is level 0).
170    ///
171    /// Panics if `level` is not below [`Self::MAX_LEVELS`].
172    pub fn tag_cache_type(&self, level: usize) -> TagCacheType {
173        match level {
174            0 => self.ttype1(),
175            1 => self.ttype2(),
176            2 => self.ttype3(),
177            3 => self.ttype4(),
178            4 => self.ttype5(),
179            5 => self.ttype6(),
180            6 => self.ttype7(),
181            _ => panic!("cache level {level} out of range"),
182        }
183    }
184}
185
186/// [arm/sysreg]/ccsidr_el1: CCSIDR_EL1, Current Cache Size ID Register
187///
188/// Layout without FEAT_CCIDX; use [`CCSIDR_EL1_CCIDX`] when
189/// `ID_AA64MMFR2_EL1.ccidx` reports the revised format.
190pub const CCSIDR_EL1: SysReg<spec::CCSIDR_EL1, CacheSizeIdRegister> = SysReg::new();
191
192/// [arm/sysreg]/ccsidr_el1: CCSIDR_EL1, Current Cache Size ID Register
193///
194/// Layout with FEAT_CCIDX.
195pub const CCSIDR_EL1_CCIDX: SysReg<spec::CCSIDR_EL1, CacheSizeIdRegisterCcidx> = SysReg::new();
196
197multilayout!({
198    /// The layout of [`CCSIDR_EL1`].
199    #[bitrs(legacy)]
200    pub struct CacheSizeIdRegister(u64);
201
202    /// The layout of [`CCSIDR_EL1_CCIDX`].
203    #[bitrs(ccidx)]
204    pub struct CacheSizeIdRegisterCcidx(u64);
205
206    #[legacy]
207    {
208        let __ @ 63..28;
209        let num_sets @ 27..13; // Number of sets, minus 1
210        let associativity @ 12..3; // Associativity, minus 1
211    }
212
213    #[ccidx]
214    {
215        let __ @ 63..56;
216        let num_sets @ 55..32; // Number of sets, minus 1
217        let __ @ 31..24;
218        let associativity @ 23..3; // Associativity, minus 1
219    }
220
221    {
222        /// log2 of the number of words in a cache line, minus 2.
223        let line_size @ 2..0;
224    }
225});
226
227macro_rules! impl_cache_size_id {
228    ($($layout:ty),*) => {
229        $(
230            impl $layout {
231                /// Returns the number of sets.
232                pub const fn sets(&self) -> u32 {
233                    self.num_sets() as u32 + 1
234                }
235
236                /// Returns the associativity (number of ways).
237                pub const fn ways(&self) -> u32 {
238                    self.associativity() as u32 + 1
239                }
240
241                /// Returns the cache line size in bytes.
242                pub const fn line_size_bytes(&self) -> usize {
243                    1 << (self.line_size() as usize + 4)
244                }
245            }
246        )*
247    };
248}
249
250impl_cache_size_id!(CacheSizeIdRegister, CacheSizeIdRegisterCcidx);
251
252/// [arm/sysreg]/csselr_el1: CSSELR_EL1, Cache Size Selection Register
253pub const CSSELR_EL1: SysReg<spec::CSSELR_EL1, CacheSizeSelectionRegister> = SysReg::new();
254
255layout!({
256    /// The layout of [`CSSELR_EL1`].
257    pub struct CacheSizeSelectionRegister(u64);
258    {
259        let __ @ 63..5;
260        let tnd @ 4; // Allocation tag not data (FEAT_MTE2)
261        let level @ 3..1; // Cache level, zero-based (L1 is 0)
262        let ind @ 0; // Instruction not data
263    }
264});
265
266impl CacheSizeSelectionRegister {
267    /// Returns a value selecting the instruction or data cache at `level`,
268    /// which is zero-based (L1 is level 0).
269    pub fn select(level: u8, instruction: bool) -> Self {
270        *Self::new().set_level(level).set_ind(instruction)
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use super::*;
277
278    #[test]
279    fn cache_type_el0() {
280        let ctr = *CacheTypeRegister::new().set_dmin_line(4).set_imin_line(4);
281        assert_eq!(ctr.dcache_line_size(), 64);
282        assert_eq!(ctr.icache_line_size(), 64);
283    }
284
285    #[test]
286    fn data_cache_zero_id_el0() {
287        let dczid = *DataCacheZeroIdRegister::new().set_bz(4);
288        assert_eq!(dczid.zva_line_size(), 64);
289    }
290
291    #[test]
292    fn cache_level_id_el1() {
293        let clidr = *CacheLevelIdRegister::new()
294            .set_ctype1(CacheType::Separate)
295            .set_ctype2(CacheType::Unified)
296            .set_ttype2(TagCacheType::UnifiedLines)
297            .set_loc(2)
298            .set_lou_is(1);
299        assert_eq!(clidr.cache_type(0), CacheType::Separate);
300        assert_eq!(clidr.cache_type(1), CacheType::Unified);
301        assert_eq!(clidr.cache_type(2), CacheType::None);
302        assert_eq!(clidr.tag_cache_type(0), TagCacheType::None);
303        assert_eq!(clidr.tag_cache_type(1), TagCacheType::UnifiedLines);
304        assert_eq!(clidr.loc(), 2);
305        assert_eq!(clidr.lou_is(), 1);
306        assert!(CacheType::Separate.has_instruction_cache());
307        assert!(CacheType::Separate.has_data_cache());
308        assert!(!CacheType::Instruction.has_data_cache());
309        assert!(!CacheType::Unified.has_data_cache());
310    }
311
312    #[test]
313    fn cache_size_id_el1() {
314        let ccsidr =
315            *CacheSizeIdRegister::new().set_num_sets(63).set_associativity(3).set_line_size(2);
316        assert_eq!(ccsidr.sets(), 64);
317        assert_eq!(ccsidr.ways(), 4);
318        assert_eq!(ccsidr.line_size_bytes(), 64);
319
320        let ccsidr = *CacheSizeIdRegisterCcidx::new()
321            .set_num_sets(2047)
322            .set_associativity(15)
323            .set_line_size(3);
324        assert_eq!(ccsidr.sets(), 2048);
325        assert_eq!(ccsidr.ways(), 16);
326        assert_eq!(ccsidr.line_size_bytes(), 128);
327    }
328
329    #[test]
330    fn cache_size_selection_el1() {
331        let csselr = CacheSizeSelectionRegister::select(2, true);
332        assert_eq!(csselr.bits(), 0b101);
333        assert_eq!(csselr.level(), 2);
334        assert!(csselr.ind());
335    }
336}