1use core::marker::PhantomData;
2
3use tock_registers::{LocalRegisterCopy, register_bitfields};
5
6pub trait Granule: Clone + Copy {
7 const M: u32;
8 const SIZE: usize = 2usize.pow(Self::M);
9 const MASK: u64 = (1u64 << Self::M) - 1; }
11
12#[derive(Clone, Copy)]
13pub struct Granule4KB {}
14
15impl Granule for Granule4KB {
16 const M: u32 = 12; }
18
19#[derive(Clone, Copy)]
20pub struct Granule16KB {}
21
22impl Granule for Granule16KB {
23 const M: u32 = 14; }
25
26#[derive(Clone, Copy)]
27pub struct Granule64KB {}
28
29impl Granule for Granule64KB {
30 const M: u32 = 16; }
32
33pub trait OA: Clone + Copy {
34 const BITS: usize;
35}
36
37#[derive(Clone, Copy)]
38pub struct OA48 {}
39
40impl OA for OA48 {
41 const BITS: usize = 48; }
43
44#[derive(Clone, Copy)]
45pub struct OA52 {}
46
47impl OA for OA52 {
48 const BITS: usize = 52; }
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
54pub enum AccessPermission {
55 PrivilegedReadWrite = 0b00,
59
60 ReadWrite = 0b01,
63
64 PrivilegedReadOnly = 0b10,
68
69 ReadOnly = 0b11,
72}
73
74impl AccessPermission {
75 pub const fn as_bits(self) -> u8 {
77 self as u8
78 }
79
80 pub const fn from_bits(bits: u8) -> Option<Self> {
82 match bits & 0b11 {
83 0b00 => Some(Self::PrivilegedReadWrite),
84 0b01 => Some(Self::ReadWrite),
85 0b10 => Some(Self::PrivilegedReadOnly),
86 0b11 => Some(Self::ReadOnly),
87 _ => None,
88 }
89 }
90
91 pub const fn allows_unprivileged(self) -> bool {
93 matches!(self, Self::ReadWrite | Self::ReadOnly)
94 }
95
96 pub const fn allows_privileged_write(self) -> bool {
98 matches!(self, Self::PrivilegedReadWrite | Self::ReadWrite)
99 }
100
101 pub const fn allows_unprivileged_write(self) -> bool {
103 matches!(self, Self::ReadWrite)
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
109pub enum Shareability {
110 NonShareable,
111 OuterShareable,
112 InnerShareable,
113}
114
115register_bitfields![u64,
116 TTE64_REG [
119 VALID OFFSET(0) NUMBITS(1) [
121 Invalid = 0,
122 Valid = 1
123 ],
124
125 TYPE OFFSET(1) NUMBITS(1) [
128 Block = 0, Table = 1 ],
131
132 ATTR_INDX OFFSET(2) NUMBITS(3) [],
134
135 NS OFFSET(5) NUMBITS(1) [
137 Secure = 0,
138 NonSecure = 1
139 ],
140
141 AP OFFSET(6) NUMBITS(2) [
145 PrivilegedReadWrite = 0b00, ReadWrite = 0b01, PrivilegedReadOnly = 0b10, ReadOnly = 0b11 ],
150
151 SH OFFSET(8) NUMBITS(2) [
153 NonShareable = 0b00,
154 OuterShareable = 0b10,
155 InnerShareable = 0b11
156 ],
157
158 AF OFFSET(10) NUMBITS(1) [
160 NotAccessed = 0,
161 Accessed = 1
162 ],
163
164 NG OFFSET(11) NUMBITS(1) [
166 Global = 0,
167 NotGlobal = 1
168 ],
169
170 ADDR OFFSET(12) NUMBITS(38) [],
171
172 DBM OFFSET(51) NUMBITS(1) [
174 ReadOnly = 0,
175 Writable = 1
176 ],
177
178 CONTIG OFFSET(52) NUMBITS(1) [
180 NotContiguous = 0,
181 Contiguous = 1
182 ],
183
184 PXN OFFSET(53) NUMBITS(1) [
186 ExecuteAllowed = 0,
187 ExecuteNever = 1
188 ],
189
190 XN_UXN OFFSET(54) NUMBITS(1) [
192 ExecuteAllowed = 0,
193 ExecuteNever = 1
194 ],
195
196 SW_RESERVED OFFSET(55) NUMBITS(4) []
198 ]
199];
200
201#[derive(Clone, Copy)]
202pub struct TTE64<G: Granule, O: OA> {
203 reg: LocalRegisterCopy<u64, TTE64_REG::Register>,
204 _marker: PhantomData<(G, O)>,
205}
206
207impl<G: Granule, O: OA> TTE64<G, O> {
208 pub const fn new(value: u64) -> Self {
210 Self {
211 reg: LocalRegisterCopy::new(value),
212 _marker: PhantomData,
213 }
214 }
215
216 pub const fn invalid() -> Self {
218 Self::new(0)
219 }
220
221 pub fn new_table(table_addr: u64) -> Self {
223 let mut tte = Self::new(0);
224
225 tte.reg
226 .modify(TTE64_REG::VALID::Valid + TTE64_REG::TYPE::Table + TTE64_REG::AF::Accessed);
227 tte.set_address(table_addr);
228 tte
229 }
230
231 pub fn new_block(block_addr: u64) -> Self {
233 let mut tte = Self::new(0);
234
235 tte.reg
236 .modify(TTE64_REG::VALID::Valid + TTE64_REG::TYPE::Block + TTE64_REG::AF::Accessed);
237 tte.set_address(block_addr);
238 tte
239 }
240
241 pub fn get(&self) -> u64 {
243 self.reg.get()
244 }
245
246 pub fn is_valid(&self) -> bool {
248 self.reg.is_set(TTE64_REG::VALID)
249 }
250
251 pub fn set_is_valid(&mut self, val: bool) {
252 if val {
253 self.reg.modify(TTE64_REG::VALID::Valid);
254 } else {
255 self.reg.modify(TTE64_REG::VALID::Invalid);
256 }
257 }
258
259 pub fn is_table(&self) -> bool {
261 self.is_valid() && self.reg.is_set(TTE64_REG::TYPE)
262 }
263
264 pub fn is_block(&self) -> bool {
266 self.is_valid() && !self.reg.is_set(TTE64_REG::TYPE)
267 }
268
269 pub fn set_address(&mut self, addr: u64) {
270 assert!(
271 addr & G::MASK == 0,
272 "Address must be aligned to granule size"
273 );
274 assert!(
275 addr < (1u64 << O::BITS),
276 "Address exceeds output address width"
277 );
278 let val = addr >> TTE64_REG::ADDR.shift; self.reg.modify(TTE64_REG::ADDR.val(val));
280 }
281
282 pub fn address(&self) -> u64 {
285 if !self.is_valid() {
286 return 0;
287 }
288
289 let raw_value = self.reg.get();
290 let m = G::M; let bit_start = m;
293 let bit_end =
294
295 if O::BITS == 52 && (G::M == 12 || G::M == 14) {
297 50
298 } else {
299 48
300 };
301 let mask = ((1u64 << (bit_end - bit_start + 1)) - 1) << bit_start;
302 raw_value & mask
303 }
304
305 pub fn address_with_page_level(&self, level: usize) -> u64 {
306 if self.is_table() {
307 return self.address();
308 }
309 let raw_addr = self.reg.get();
310 let n = match (G::M, level) {
311 (12, 0) => 39,
312 (12, 1) => 30,
313 (12, 2) => 21,
314 (14, 1) => 36,
315 (14, 2) => 25,
316 (16, 1) => 42,
317 (16, 2) => 29,
318 _ => panic!("Invalid granule size or level combination"),
319 };
320
321 let bit_start = n;
322 let bit_end = if O::BITS == 52 && (G::M == 12 || G::M == 14) {
324 50
325 } else {
326 48
327 };
328 let mask = ((1u64 << (bit_end - bit_start + 1)) - 1) << bit_start;
329 raw_addr & mask
330 }
331
332 pub fn is_accessed(&self) -> bool {
334 self.reg.is_set(TTE64_REG::AF)
335 }
336
337 pub fn attr_index(&self) -> u64 {
339 self.reg.read(TTE64_REG::ATTR_INDX)
340 }
341
342 pub fn is_executable(&self) -> bool {
344 !self.reg.is_set(TTE64_REG::XN_UXN)
345 }
346
347 pub fn is_privileged_executable(&self) -> bool {
349 !self.reg.is_set(TTE64_REG::PXN)
350 }
351
352 pub fn access_permission(&self) -> AccessPermission {
354 AccessPermission::from_bits(self.reg.read(TTE64_REG::AP) as _).unwrap()
355 }
356
357 pub fn shareability(&self) -> Shareability {
359 match self.reg.read_as_enum(TTE64_REG::SH) {
360 Some(TTE64_REG::SH::Value::NonShareable) => Shareability::NonShareable,
361 Some(TTE64_REG::SH::Value::OuterShareable) => Shareability::OuterShareable,
362 Some(TTE64_REG::SH::Value::InnerShareable) => Shareability::InnerShareable,
363 None => unreachable!("invalid value"),
364 }
365 }
366
367 pub fn set_shareability(&mut self, shareability: Shareability) {
368 self.reg.modify(match shareability {
369 Shareability::NonShareable => TTE64_REG::SH::NonShareable,
370 Shareability::OuterShareable => TTE64_REG::SH::OuterShareable,
371 Shareability::InnerShareable => TTE64_REG::SH::InnerShareable,
372 });
373 }
374
375 pub fn set_access(&mut self) {
377 self.reg.modify(TTE64_REG::AF::Accessed);
378 }
379
380 pub fn clear_access(&mut self) {
382 self.reg.modify(TTE64_REG::AF::NotAccessed);
383 }
384
385 pub fn is_contiguous(&self) -> bool {
387 self.reg.is_set(TTE64_REG::CONTIG)
388 }
389
390 pub fn set_contiguous(&mut self) {
392 self.reg.modify(TTE64_REG::CONTIG::Contiguous);
393 }
394
395 pub fn is_global(&self) -> bool {
397 !self.reg.is_set(TTE64_REG::NG)
398 }
399
400 pub fn set_not_global(&mut self) {
402 self.reg.modify(TTE64_REG::NG::NotGlobal);
403 }
404
405 pub fn is_dirty_writable(&self) -> bool {
407 self.reg.is_set(TTE64_REG::DBM)
408 }
409
410 pub fn sw_reserved(&self) -> u64 {
412 self.reg.read(TTE64_REG::SW_RESERVED)
413 }
414
415 pub fn set_sw_reserved(&mut self, value: u64) {
417 self.reg.modify(TTE64_REG::SW_RESERVED.val(value & 0xF));
418 }
419}
420
421pub type TTE4K48 = TTE64<Granule4KB, OA48>;
424
425pub type TTE4K52 = TTE64<Granule4KB, OA52>;
427
428pub type TTE16K48 = TTE64<Granule16KB, OA48>;
430
431pub type TTE16K52 = TTE64<Granule16KB, OA52>;
433
434pub type TTE64K48 = TTE64<Granule64KB, OA48>;
436
437pub type TTE64K52 = TTE64<Granule64KB, OA52>;
439
440pub mod block_sizes {
442 pub mod granule_4k {
444 pub const LEVEL1_BLOCK_SIZE: usize = 1024 * 1024 * 1024; pub const LEVEL2_BLOCK_SIZE: usize = 2 * 1024 * 1024; pub const LEVEL3_PAGE_SIZE: usize = 4 * 1024; }
448
449 pub mod granule_16k {
451 pub const LEVEL1_BLOCK_SIZE: usize = 64 * 1024 * 1024 * 1024; pub const LEVEL2_BLOCK_SIZE: usize = 32 * 1024 * 1024; pub const LEVEL3_PAGE_SIZE: usize = 16 * 1024; }
455
456 pub mod granule_64k {
458 pub const LEVEL1_BLOCK_SIZE: usize = 4 * 1024 * 1024 * 1024; pub const LEVEL2_BLOCK_SIZE: usize = 512 * 1024 * 1024; pub const LEVEL3_PAGE_SIZE: usize = 64 * 1024; }
462}
463
464impl<G: Granule, O: OA> TTE64<G, O> {
466 pub fn calculate_index(va: u64, level: usize) -> usize {
468 match (G::M, level) {
469 (12, 0) => ((va >> 39) & 0x1FF) as usize, (12, 1) => ((va >> 30) & 0x1FF) as usize, (12, 2) => ((va >> 21) & 0x1FF) as usize, (12, 3) => ((va >> 12) & 0x1FF) as usize, (14, 0) => ((va >> 47) & 0x1) as usize, (14, 1) => ((va >> 36) & 0x7FF) as usize, (14, 2) => ((va >> 25) & 0x7FF) as usize, (14, 3) => ((va >> 14) & 0x7FF) as usize, (16, 1) => ((va >> 42) & 0x3F) as usize, (16, 2) => ((va >> 29) & 0x1FFF) as usize, (16, 3) => ((va >> 16) & 0x1FFF) as usize, _ => panic!("Invalid granule size or level combination"),
484 }
485 }
486
487 pub fn is_aligned(addr: u64) -> bool {
489 (addr & G::MASK) == 0
490 }
491
492 pub fn align_down(addr: u64) -> u64 {
494 addr & !G::MASK
495 }
496
497 pub fn align_up(addr: u64) -> u64 {
499 (addr + G::MASK) & !G::MASK
500 }
501}
502
503#[cfg(test)]
504mod tests {
505 use super::*;
506
507 #[test]
508 fn test_address_extraction_4k_48bit() {
509 type TTE = TTE64<Granule4KB, OA48>;
511
512 let table_addr = 0x1000_0000_1000; let tte_table = TTE::new_table(table_addr);
515 assert_eq!(tte_table.address(), table_addr);
516
517 let block = 2 * 1024 * 1024; let block_addr = 0x2000_0000_1000 + block; let tte_block = TTE::new_block(block_addr);
521 assert_eq!(
522 tte_block.address_with_page_level(2),
523 0x2000_0000_0000 + block
524 );
525 }
526
527 #[test]
528 fn test_address_extraction_4k_52bit() {
529 type TTE = TTE64<Granule4KB, OA52>;
531
532 let table_addr = (1 << 50) - 0x1000; let tte_table = TTE::new_table(table_addr);
534 let read_addr = tte_table.address();
535 assert_eq!(
536 read_addr, table_addr,
537 "want {:#x} != read {:#x} address mismatch",
538 table_addr, read_addr
539 );
540 }
541
542 #[test]
543 fn test_address_extraction_16k_48bit() {
544 type TTE = TTE64<Granule16KB, OA48>;
546
547 let table_addr = (1 << 47) + 16 * 1024; let tte_table = TTE::new_table(table_addr);
550 let read = tte_table.address();
551 assert_eq!(
552 table_addr, read,
553 "want {:#x} != read {:#x} address mismatch",
554 table_addr, read
555 );
556
557 let block_addr = 0x2000_0000_0000; let tte_block = TTE::new_block(block_addr);
560 assert_eq!(tte_block.address(), block_addr);
561 }
562
563 #[test]
564 fn test_address_extraction_16k_52bit() {
565 type TTE = TTE64<Granule16KB, OA52>;
567
568 let table_addr = (1 << 50) - 0x4000; let tte_table = TTE::new_table(table_addr); assert_eq!(tte_table.address(), table_addr);
573 }
574
575 #[test]
576 fn test_address_extraction_64k_48bit() {
577 type TTE = TTE64<Granule64KB, OA48>;
579
580 let table_addr = 0x1000_0001_0000; let tte_table = TTE::new_table(table_addr);
583 assert_eq!(tte_table.address(), table_addr);
584
585 let block_addr = 0x2000_0002_0000; let tte_block = TTE::new_block(block_addr);
588 assert_eq!(tte_block.address(), block_addr);
589 }
590
591 #[test]
592 fn test_address_extraction_64k_52bit() {
593 type TTE = TTE64<Granule64KB, OA52>;
595
596 let table_addr = 0xf00_1001_0000u64; let tte_table = TTE::new_table(table_addr); assert_eq!(
600 table_addr,
601 tte_table.address(),
602 "want {:#x} != read {:#x} address mismatch",
603 table_addr,
604 tte_table.address()
605 );
606 }
607
608 #[test]
609 fn test_invalid_tte_address() {
610 type TTE = TTE64<Granule4KB, OA48>;
612
613 let tte_invalid = TTE::invalid();
614 assert_eq!(tte_invalid.address(), 0);
615 assert!(!tte_invalid.is_valid());
616 }
617
618 #[test]
619 fn test_granule_constants() {
620 assert_eq!(Granule4KB::M, 12); assert_eq!(Granule16KB::M, 14); assert_eq!(Granule64KB::M, 16); assert_eq!(Granule4KB::SIZE, 4096);
627 assert_eq!(Granule16KB::SIZE, 16384);
628 assert_eq!(Granule64KB::SIZE, 65536);
629
630 assert_eq!(Granule4KB::MASK, 0xFFF);
632 assert_eq!(Granule16KB::MASK, 0x3FFF);
633 assert_eq!(Granule64KB::MASK, 0xFFFF);
634 }
635}