Imre Kis | 725ef5e | 2024-11-20 14:20:19 +0100 | [diff] [blame] | 1 | // 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 Kis | 5f96044 | 2024-11-29 16:49:43 +0100 | [diff] [blame] | 4 | #[derive(Clone, Copy, Debug)] |
Imre Kis | 725ef5e | 2024-11-20 14:20:19 +0100 | [diff] [blame] | 5 | pub enum TranslationGranule<const VA_BITS: usize> { |
| 6 | Granule4k = 4 * 1024, |
| 7 | Granule16k = 16 * 1024, |
| 8 | Granule64k = 64 * 1024, |
| 9 | } |
| 10 | |
| 11 | impl<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)] |
| 120 | mod 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 | } |