blob: caa8b97870593894bca0a4bb116a791571592755 [file] [log] [blame]
Imre Kis725ef5e2024-11-20 14:20:19 +01001// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
Imre Kis5f960442024-11-29 16:49:43 +01004#[derive(Clone, Copy, Debug)]
Imre Kis725ef5e2024-11-20 14:20:19 +01005pub enum TranslationGranule<const VA_BITS: usize> {
6 Granule4k = 4 * 1024,
7 Granule16k = 16 * 1024,
8 Granule64k = 64 * 1024,
9}
10
11impl<const VA_BITS: usize> TranslationGranule<VA_BITS> {
12 pub const fn initial_lookup_level(&self) -> isize {
13 match self {
14 TranslationGranule::Granule4k => match VA_BITS {
15 #[cfg(feature = "feat_ttst")]
16 16..=21 => 3,
17 #[cfg(feature = "feat_ttst")]
18 22..=24 => 2,
19 25..=30 => 2,
20 31..=39 => 1,
21 40..=48 => 0,
22 #[cfg(feature = "feat_lpa2")]
23 49..=52 => -1,
24 _ => panic!("Invalid VA_BITS"),
25 },
26 TranslationGranule::Granule16k => match VA_BITS {
27 #[cfg(feature = "feat_ttst")]
28 16..=24 => 3,
29 25 => 3,
30 26..=36 => 2,
31 37..=47 => 1,
32 48 => 0,
33 #[cfg(feature = "feat_lpa2")]
34 49..=52 => 0,
35 _ => panic!("Invalid VA_BITS"),
36 },
37 TranslationGranule::Granule64k => match VA_BITS {
38 #[cfg(feature = "feat_ttst")]
39 17..=24 => 3,
40 25..=29 => 3,
41 30..=42 => 2,
42 43..=48 => 1,
43 #[cfg(feature = "feat_lva")]
44 49..=52 => 1,
45 _ => panic!("Invalid VA_BITS"),
46 },
47 }
48 }
49
50 pub const fn entry_count_at_level(&self, level: isize) -> usize {
51 let total_bits_at_level = self.total_bits_at_level(level);
52 let bits_per_level = match self {
53 TranslationGranule::Granule4k => 9,
54 TranslationGranule::Granule16k => 11,
55 TranslationGranule::Granule64k => 13,
56 };
57
58 let bits = if total_bits_at_level + bits_per_level < VA_BITS {
59 total_bits_at_level + bits_per_level
60 } else {
61 VA_BITS
62 } - total_bits_at_level;
63
64 1 << bits
65 }
66
67 pub const fn table_size<D>(&self, level: isize) -> usize {
68 self.entry_count_at_level(level) * core::mem::size_of::<D>()
69 }
70
71 pub const fn table_alignment<D>(&self, level: isize) -> usize {
72 // D8.2.5.2
73 // For the VMSAv8-64 translation system, if the translation table has fewer than eight
74 // entries and an OA size greater than 48 bits is used, then the table is aligned to 64
75 // bytes. Otherwise, the translation table is aligned to the size of that translation table.
76
77 let alignment = self.table_size::<D>(level);
78
79 if VA_BITS > 48 && alignment < 64 {
80 64
81 } else {
82 alignment
83 }
84 }
85
86 pub const fn block_size_at_level(&self, level: isize) -> usize {
87 1 << self.total_bits_at_level(level)
88 }
89
90 pub const fn total_bits_at_level(&self, level: isize) -> usize {
91 assert!(self.initial_lookup_level() <= level && level <= 3);
92
93 match self {
94 TranslationGranule::Granule4k => match level {
95 -1 => 48,
96 0 => 39,
97 1 => 30,
98 2 => 21,
99 3 => 12,
100 _ => panic!("Invalid translation level"),
101 },
102 TranslationGranule::Granule16k => match level {
103 0 => 47,
104 1 => 36,
105 2 => 25,
106 3 => 14,
107 _ => panic!("Invalid translation level"),
108 },
109 TranslationGranule::Granule64k => match level {
110 1 => 42,
111 2 => 29,
112 3 => 16,
113 _ => panic!("Invalid translation level"),
114 },
115 }
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 macro_rules! validate_initial_lookup_level {
124 ( $granule:expr, $tnsz_min:literal, $tnsz_max:literal, $level:literal ) => {
125 assert_eq!(
126 $level,
127 TranslationGranule::<{ 64 - $tnsz_min }>::initial_lookup_level(&$granule)
128 );
129 assert_eq!(
130 $level,
131 TranslationGranule::<{ 64 - $tnsz_max }>::initial_lookup_level(&$granule)
132 );
133 };
134 }
135
136 #[test]
137 fn test_initial_lookup_level() {
138 // Table D8-16 4KB granule, determining stage 1 initial lookup level
139 #[cfg(feature = "feat_lpa2")]
140 validate_initial_lookup_level!(TranslationGranule::Granule4k, 12, 15, -1);
141 validate_initial_lookup_level!(TranslationGranule::Granule4k, 16, 24, 0);
142 validate_initial_lookup_level!(TranslationGranule::Granule4k, 25, 33, 1);
143 validate_initial_lookup_level!(TranslationGranule::Granule4k, 34, 39, 2);
144 #[cfg(feature = "feat_ttst")]
145 validate_initial_lookup_level!(TranslationGranule::Granule4k, 40, 42, 2);
146 #[cfg(feature = "feat_ttst")]
147 validate_initial_lookup_level!(TranslationGranule::Granule4k, 43, 48, 3);
148
149 //// Table D8-26 16KB granule, determining stage 1 initial lookup level
150 #[cfg(feature = "feat_lpa2")]
151 validate_initial_lookup_level!(TranslationGranule::Granule16k, 12, 15, 0);
152 validate_initial_lookup_level!(TranslationGranule::Granule16k, 16, 16, 0);
153 validate_initial_lookup_level!(TranslationGranule::Granule16k, 17, 27, 1);
154 validate_initial_lookup_level!(TranslationGranule::Granule16k, 28, 38, 2);
155 validate_initial_lookup_level!(TranslationGranule::Granule16k, 39, 39, 3);
156 #[cfg(feature = "feat_ttst")]
157 validate_initial_lookup_level!(TranslationGranule::Granule16k, 40, 48, 3);
158 //
159 //// D8.2.10.1 VMSAv8-64 Stage 1 address translation using the 64KB translation granule
160 #[cfg(feature = "feat_lva")]
161 validate_initial_lookup_level!(TranslationGranule::Granule64k, 12, 15, 1);
162 validate_initial_lookup_level!(TranslationGranule::Granule64k, 16, 21, 1);
163 validate_initial_lookup_level!(TranslationGranule::Granule64k, 22, 34, 2);
164 validate_initial_lookup_level!(TranslationGranule::Granule64k, 35, 39, 3);
165 #[cfg(feature = "feat_ttst")]
166 validate_initial_lookup_level!(TranslationGranule::Granule64k, 40, 47, 3);
167 }
168
169 #[test]
170 fn test_entry_count_at_level() {
171 // Table D8-14 4KB granule translation table properties at each lookup level
172 #[cfg(feature = "feat_lpa2")]
173 {
174 let granule = TranslationGranule::<52>::Granule4k;
175 assert_eq!(16, granule.entry_count_at_level(-1));
176 assert_eq!(512, granule.entry_count_at_level(0));
177 assert_eq!(512, granule.entry_count_at_level(1));
178 assert_eq!(512, granule.entry_count_at_level(2));
179 assert_eq!(512, granule.entry_count_at_level(3));
180 }
181 #[cfg(not(feature = "feat_lpa2"))]
182 {
183 let granule = TranslationGranule::<48>::Granule4k;
184 assert_eq!(512, granule.entry_count_at_level(0));
185 assert_eq!(512, granule.entry_count_at_level(1));
186 assert_eq!(512, granule.entry_count_at_level(2));
187 assert_eq!(512, granule.entry_count_at_level(3));
188 }
189
190 let granule = TranslationGranule::<42>::Granule4k;
191 assert_eq!(8, granule.entry_count_at_level(0));
192 assert_eq!(512, granule.entry_count_at_level(1));
193 assert_eq!(512, granule.entry_count_at_level(2));
194 assert_eq!(512, granule.entry_count_at_level(3));
195
196 let granule = TranslationGranule::<32>::Granule4k;
197 assert_eq!(4, granule.entry_count_at_level(1));
198 assert_eq!(512, granule.entry_count_at_level(2));
199 assert_eq!(512, granule.entry_count_at_level(3));
200
201 let granule = TranslationGranule::<26>::Granule4k;
202 assert_eq!(32, granule.entry_count_at_level(2));
203 assert_eq!(512, granule.entry_count_at_level(3));
204
205 // Table D8-24 16KB granule translation table properties at each lookup level
206 #[cfg(feature = "feat_lpa2")]
207 {
208 let granule = TranslationGranule::<52>::Granule16k;
209 assert_eq!(32, granule.entry_count_at_level(0));
210 assert_eq!(2048, granule.entry_count_at_level(1));
211 assert_eq!(2048, granule.entry_count_at_level(2));
212 assert_eq!(2048, granule.entry_count_at_level(3));
213 }
214 #[cfg(not(feature = "feat_lpa2"))]
215 {
216 let granule = TranslationGranule::<48>::Granule16k;
217 assert_eq!(2, granule.entry_count_at_level(0));
218 assert_eq!(2048, granule.entry_count_at_level(1));
219 assert_eq!(2048, granule.entry_count_at_level(2));
220 assert_eq!(2048, granule.entry_count_at_level(3));
221 }
222
223 let granule = TranslationGranule::<40>::Granule16k;
224 assert_eq!(16, granule.entry_count_at_level(1));
225 assert_eq!(2048, granule.entry_count_at_level(2));
226 assert_eq!(2048, granule.entry_count_at_level(3));
227
228 let granule = TranslationGranule::<30>::Granule16k;
229 assert_eq!(32, granule.entry_count_at_level(2));
230 assert_eq!(2048, granule.entry_count_at_level(3));
231
232 // Table D8-33 64KB granule translation table properties at each lookup level
233 #[cfg(feature = "feat_lva")]
234 {
235 let granule = TranslationGranule::<52>::Granule64k;
236 assert_eq!(1024, granule.entry_count_at_level(1));
237 }
238
239 let granule = TranslationGranule::<48>::Granule64k;
240 #[cfg(not(feature = "feat_lva"))]
241 {
242 assert_eq!(64, granule.entry_count_at_level(1));
243 }
244
245 assert_eq!(8192, granule.entry_count_at_level(2));
246 assert_eq!(8192, granule.entry_count_at_level(3));
247
248 let granule = TranslationGranule::<40>::Granule64k;
249 assert_eq!(2048, granule.entry_count_at_level(2));
250 assert_eq!(8192, granule.entry_count_at_level(3));
251 }
252
253 #[test]
254 fn test_granule_size_at_level() {
255 // Table D8-15 4KB granule block descriptor properties at each lookup level
256 #[cfg(feature = "feat_lpa2")]
257 {
258 let granule = TranslationGranule::<52>::Granule4k;
259 assert_eq!(256 << 40, granule.block_size_at_level(-1));
260 }
261
262 let granule = TranslationGranule::<48>::Granule4k;
263 assert_eq!(512 << 30, granule.block_size_at_level(0));
264 assert_eq!(1 << 30, granule.block_size_at_level(1));
265 assert_eq!(2 << 20, granule.block_size_at_level(2));
266 assert_eq!(4 << 10, granule.block_size_at_level(3));
267
268 // Table D8-25 16KB granule block descriptor properties at each lookup level
269 let granule = TranslationGranule::<48>::Granule16k;
270 assert_eq!(128 << 40, granule.block_size_at_level(0));
271 assert_eq!(64 << 30, granule.block_size_at_level(1));
272 assert_eq!(32 << 20, granule.block_size_at_level(2));
273 assert_eq!(16 << 10, granule.block_size_at_level(3));
274
275 // Table D8-34 64KB granule block descriptor properties at each lookup level
276 let granule = TranslationGranule::<48>::Granule64k;
277 assert_eq!(4 << 40, granule.block_size_at_level(1));
278 assert_eq!(512 << 20, granule.block_size_at_level(2));
279 assert_eq!(64 << 10, granule.block_size_at_level(3));
280 }
281}