1 // Copyright 2022 The aarch64-paging Authors.
2 // This project is dual-licensed under Apache 2.0 and MIT terms.
3 // See LICENSE-APACHE and LICENSE-MIT for details.
4
5 //! Generic aarch64 page table manipulation functionality which doesn't assume anything about how
6 //! addresses are mapped.
7
8 use crate::MapError;
9 #[cfg(feature = "alloc")]
10 use alloc::alloc::{alloc_zeroed, dealloc, handle_alloc_error, Layout};
11 use bitflags::bitflags;
12 use core::fmt::{self, Debug, Display, Formatter};
13 use core::marker::PhantomData;
14 use core::ops::{Add, Range, Sub};
15 use core::ptr::NonNull;
16
17 const PAGE_SHIFT: usize = 12;
18
19 /// The pagetable level at which all entries are page mappings.
20 const LEAF_LEVEL: usize = 3;
21
22 /// The page size in bytes assumed by this library, 4 KiB.
23 pub const PAGE_SIZE: usize = 1 << PAGE_SHIFT;
24
25 /// The number of address bits resolved in one level of page table lookup. This is a function of the
26 /// page size.
27 pub const BITS_PER_LEVEL: usize = PAGE_SHIFT - 3;
28
29 /// Which virtual address range a page table is for, i.e. which TTBR register to use for it.
30 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
31 pub enum VaRange {
32 /// The page table covers the bottom of the virtual address space (starting at address 0), so
33 /// will be used with `TTBR0`.
34 Lower,
35 /// The page table covers the top of the virtual address space (ending at address
36 /// 0xffff_ffff_ffff_ffff), so will be used with `TTBR1`.
37 Upper,
38 }
39
40 /// Which translation regime a page table is for.
41 ///
42 /// This depends on the exception level, among other things.
43 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
44 pub enum TranslationRegime {
45 /// Secure EL3.
46 El3,
47 /// Non-secure EL2.
48 El2,
49 /// Non-secure EL2&0, with VHE.
50 El2And0,
51 /// Non-secure EL1&0, stage 1.
52 El1And0,
53 }
54
55 impl TranslationRegime {
56 /// Returns whether this translation regime supports use of an ASID.
57 ///
58 /// This also implies that it supports two VA ranges.
supports_asid(self) -> bool59 pub(crate) fn supports_asid(self) -> bool {
60 matches!(self, Self::El2And0 | Self::El1And0)
61 }
62 }
63
64 /// An aarch64 virtual address, the input type of a stage 1 page table.
65 #[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
66 pub struct VirtualAddress(pub usize);
67
68 impl Display for VirtualAddress {
fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>69 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
70 write!(f, "{:#018x}", self.0)
71 }
72 }
73
74 impl Debug for VirtualAddress {
fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>75 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
76 write!(f, "VirtualAddress({})", self)
77 }
78 }
79
80 impl Sub for VirtualAddress {
81 type Output = usize;
82
sub(self, other: Self) -> Self::Output83 fn sub(self, other: Self) -> Self::Output {
84 self.0 - other.0
85 }
86 }
87
88 impl Add<usize> for VirtualAddress {
89 type Output = Self;
90
add(self, other: usize) -> Self91 fn add(self, other: usize) -> Self {
92 Self(self.0 + other)
93 }
94 }
95
96 impl Sub<usize> for VirtualAddress {
97 type Output = Self;
98
sub(self, other: usize) -> Self99 fn sub(self, other: usize) -> Self {
100 Self(self.0 - other)
101 }
102 }
103
104 /// A range of virtual addresses which may be mapped in a page table.
105 #[derive(Clone, Eq, PartialEq)]
106 pub struct MemoryRegion(Range<VirtualAddress>);
107
108 /// An aarch64 physical address or intermediate physical address, the output type of a stage 1 page
109 /// table.
110 #[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
111 pub struct PhysicalAddress(pub usize);
112
113 impl Display for PhysicalAddress {
fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>114 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
115 write!(f, "{:#018x}", self.0)
116 }
117 }
118
119 impl Debug for PhysicalAddress {
fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>120 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
121 write!(f, "PhysicalAddress({})", self)
122 }
123 }
124
125 impl Sub for PhysicalAddress {
126 type Output = usize;
127
sub(self, other: Self) -> Self::Output128 fn sub(self, other: Self) -> Self::Output {
129 self.0 - other.0
130 }
131 }
132
133 impl Add<usize> for PhysicalAddress {
134 type Output = Self;
135
add(self, other: usize) -> Self136 fn add(self, other: usize) -> Self {
137 Self(self.0 + other)
138 }
139 }
140
141 impl Sub<usize> for PhysicalAddress {
142 type Output = Self;
143
sub(self, other: usize) -> Self144 fn sub(self, other: usize) -> Self {
145 Self(self.0 - other)
146 }
147 }
148
149 /// Returns the size in bytes of the address space covered by a single entry in the page table at
150 /// the given level.
granularity_at_level(level: usize) -> usize151 pub(crate) fn granularity_at_level(level: usize) -> usize {
152 PAGE_SIZE << ((LEAF_LEVEL - level) * BITS_PER_LEVEL)
153 }
154
155 /// An implementation of this trait needs to be provided to the mapping routines, so that the
156 /// physical addresses used in the page tables can be converted into virtual addresses that can be
157 /// used to access their contents from the code.
158 pub trait Translation {
159 /// Allocates a zeroed page, which is already mapped, to be used for a new subtable of some
160 /// pagetable. Returns both a pointer to the page and its physical address.
allocate_table(&mut self) -> (NonNull<PageTable>, PhysicalAddress)161 fn allocate_table(&mut self) -> (NonNull<PageTable>, PhysicalAddress);
162
163 /// Deallocates the page which was previous allocated by [`allocate_table`](Self::allocate_table).
164 ///
165 /// # Safety
166 ///
167 /// The memory must have been allocated by `allocate_table` on the same `Translation`, and not
168 /// yet deallocated.
deallocate_table(&mut self, page_table: NonNull<PageTable>)169 unsafe fn deallocate_table(&mut self, page_table: NonNull<PageTable>);
170
171 /// Given the physical address of a subtable, returns the virtual address at which it is mapped.
physical_to_virtual(&self, pa: PhysicalAddress) -> NonNull<PageTable>172 fn physical_to_virtual(&self, pa: PhysicalAddress) -> NonNull<PageTable>;
173 }
174
175 impl MemoryRegion {
176 /// Constructs a new `MemoryRegion` for the given range of virtual addresses.
177 ///
178 /// The start is inclusive and the end is exclusive. Both will be aligned to the [`PAGE_SIZE`],
179 /// with the start being rounded down and the end being rounded up.
new(start: usize, end: usize) -> MemoryRegion180 pub const fn new(start: usize, end: usize) -> MemoryRegion {
181 MemoryRegion(
182 VirtualAddress(align_down(start, PAGE_SIZE))..VirtualAddress(align_up(end, PAGE_SIZE)),
183 )
184 }
185
186 /// Returns the first virtual address of the memory range.
start(&self) -> VirtualAddress187 pub const fn start(&self) -> VirtualAddress {
188 self.0.start
189 }
190
191 /// Returns the first virtual address after the memory range.
end(&self) -> VirtualAddress192 pub const fn end(&self) -> VirtualAddress {
193 self.0.end
194 }
195
196 /// Returns the length of the memory region in bytes.
len(&self) -> usize197 pub const fn len(&self) -> usize {
198 self.0.end.0 - self.0.start.0
199 }
200
201 /// Returns whether the memory region contains exactly 0 bytes.
is_empty(&self) -> bool202 pub const fn is_empty(&self) -> bool {
203 self.0.start.0 == self.0.end.0
204 }
205
split(&self, level: usize) -> ChunkedIterator206 fn split(&self, level: usize) -> ChunkedIterator {
207 ChunkedIterator {
208 range: self,
209 granularity: granularity_at_level(level),
210 start: self.0.start.0,
211 }
212 }
213
214 /// Returns whether this region can be mapped at 'level' using block mappings only.
is_block(&self, level: usize) -> bool215 pub(crate) fn is_block(&self, level: usize) -> bool {
216 let gran = granularity_at_level(level);
217 (self.0.start.0 | self.0.end.0) & (gran - 1) == 0
218 }
219 }
220
221 impl From<Range<VirtualAddress>> for MemoryRegion {
from(range: Range<VirtualAddress>) -> Self222 fn from(range: Range<VirtualAddress>) -> Self {
223 Self::new(range.start.0, range.end.0)
224 }
225 }
226
227 impl Display for MemoryRegion {
fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>228 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
229 write!(f, "{}..{}", self.0.start, self.0.end)
230 }
231 }
232
233 impl Debug for MemoryRegion {
fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>234 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
235 Display::fmt(self, f)
236 }
237 }
238
239 bitflags! {
240 /// Constraints on page table mappings
241 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
242 pub struct Constraints: usize {
243 /// Block mappings are not permitted, only page mappings
244 const NO_BLOCK_MAPPINGS = 1 << 0;
245 /// Use of the contiguous hint is not permitted
246 const NO_CONTIGUOUS_HINT = 1 << 1;
247 }
248 }
249
250 /// A complete hierarchy of page tables including all levels.
251 pub struct RootTable<T: Translation> {
252 table: PageTableWithLevel<T>,
253 translation: T,
254 pa: PhysicalAddress,
255 translation_regime: TranslationRegime,
256 va_range: VaRange,
257 }
258
259 impl<T: Translation> RootTable<T> {
260 /// Creates a new page table starting at the given root level.
261 ///
262 /// The level must be between 0 and 3; level -1 (for 52-bit addresses with LPA2) is not
263 /// currently supported by this library. The value of `TCR_EL1.T0SZ` must be set appropriately
264 /// to match.
new( mut translation: T, level: usize, translation_regime: TranslationRegime, va_range: VaRange, ) -> Self265 pub fn new(
266 mut translation: T,
267 level: usize,
268 translation_regime: TranslationRegime,
269 va_range: VaRange,
270 ) -> Self {
271 if level > LEAF_LEVEL {
272 panic!("Invalid root table level {}.", level);
273 }
274 if !translation_regime.supports_asid() && va_range != VaRange::Lower {
275 panic!(
276 "{:?} doesn't have an upper virtual address range.",
277 translation_regime
278 );
279 }
280 let (table, pa) = PageTableWithLevel::new(&mut translation, level);
281 RootTable {
282 table,
283 translation,
284 pa,
285 translation_regime,
286 va_range,
287 }
288 }
289
290 /// Returns the size in bytes of the virtual address space which can be mapped in this page
291 /// table.
292 ///
293 /// This is a function of the chosen root level.
size(&self) -> usize294 pub fn size(&self) -> usize {
295 granularity_at_level(self.table.level) << BITS_PER_LEVEL
296 }
297
298 /// Recursively maps a range into the pagetable hierarchy starting at the root level, mapping
299 /// the pages to the corresponding physical address range starting at `pa`. Block and page
300 /// entries will be written to, but will only be mapped if `flags` contains `Attributes::VALID`.
301 ///
302 /// Returns an error if the virtual address range is out of the range covered by the pagetable,
303 /// or if the `flags` argument has unsupported attributes set.
map_range( &mut self, range: &MemoryRegion, pa: PhysicalAddress, flags: Attributes, constraints: Constraints, ) -> Result<(), MapError>304 pub fn map_range(
305 &mut self,
306 range: &MemoryRegion,
307 pa: PhysicalAddress,
308 flags: Attributes,
309 constraints: Constraints,
310 ) -> Result<(), MapError> {
311 if flags.contains(Attributes::TABLE_OR_PAGE) {
312 return Err(MapError::InvalidFlags(Attributes::TABLE_OR_PAGE));
313 }
314 self.verify_region(range)?;
315 self.table
316 .map_range(&mut self.translation, range, pa, flags, constraints);
317 Ok(())
318 }
319
320 /// Returns the physical address of the root table in memory.
to_physical(&self) -> PhysicalAddress321 pub fn to_physical(&self) -> PhysicalAddress {
322 self.pa
323 }
324
325 /// Returns the virtual address range for which this table is intended.
326 ///
327 /// This affects which TTBR register is used.
va_range(&self) -> VaRange328 pub fn va_range(&self) -> VaRange {
329 self.va_range
330 }
331
332 /// Returns the translation regime for which this table is intended.
translation_regime(&self) -> TranslationRegime333 pub fn translation_regime(&self) -> TranslationRegime {
334 self.translation_regime
335 }
336
337 /// Returns a reference to the translation used for this page table.
translation(&self) -> &T338 pub fn translation(&self) -> &T {
339 &self.translation
340 }
341
342 /// Applies the provided updater function to the page table descriptors covering a given
343 /// memory range.
344 ///
345 /// This may involve splitting block entries if the provided range is not currently mapped
346 /// down to its precise boundaries. For visiting all the descriptors covering a memory range
347 /// without potential splitting (and no descriptor updates), use
348 /// [`walk_range`](Self::walk_range) instead.
349 ///
350 /// The updater function receives the following arguments:
351 ///
352 /// - The virtual address range mapped by each page table descriptor. A new descriptor will
353 /// have been allocated before the invocation of the updater function if a page table split
354 /// was needed.
355 /// - A mutable reference to the page table descriptor that permits modifications.
356 /// - The level of a translation table the descriptor belongs to.
357 ///
358 /// The updater function should return:
359 ///
360 /// - `Ok` to continue updating the remaining entries.
361 /// - `Err` to signal an error and stop updating the remaining entries.
362 ///
363 /// This should generally only be called while the page table is not active. In particular, any
364 /// change that may require break-before-make per the architecture must be made while the page
365 /// table is inactive. Mapping a previously unmapped memory range may be done while the page
366 /// table is active.
367 ///
368 /// # Errors
369 ///
370 /// Returns [`MapError::PteUpdateFault`] if the updater function returns an error.
371 ///
372 /// Returns [`MapError::RegionBackwards`] if the range is backwards.
373 ///
374 /// Returns [`MapError::AddressRange`] if the largest address in the `range` is greater than the
375 /// largest virtual address covered by the page table given its root level.
376 ///
377 /// Returns [`MapError::BreakBeforeMakeViolation'] if the range intersects with live mappings,
378 /// and modifying those would violate architectural break-before-make (BBM) requirements.
modify_range<F>(&mut self, range: &MemoryRegion, f: &F) -> Result<(), MapError> where F: Fn(&MemoryRegion, &mut Descriptor, usize) -> Result<(), ()> + ?Sized,379 pub fn modify_range<F>(&mut self, range: &MemoryRegion, f: &F) -> Result<(), MapError>
380 where
381 F: Fn(&MemoryRegion, &mut Descriptor, usize) -> Result<(), ()> + ?Sized,
382 {
383 self.verify_region(range)?;
384 self.table.modify_range(&mut self.translation, range, f)
385 }
386
387 /// Applies the provided callback function to the page table descriptors covering a given
388 /// memory range.
389 ///
390 /// The callback function receives the following arguments:
391 ///
392 /// - The range covered by the current step in the walk. This is always a subrange of `range`
393 /// even when the descriptor covers a region that exceeds it.
394 /// - The page table descriptor itself.
395 /// - The level of a translation table the descriptor belongs to.
396 ///
397 /// The callback function should return:
398 ///
399 /// - `Ok` to continue visiting the remaining entries.
400 /// - `Err` to signal an error and stop visiting the remaining entries.
401 ///
402 /// # Errors
403 ///
404 /// Returns [`MapError::PteUpdateFault`] if the callback function returns an error.
405 ///
406 /// Returns [`MapError::RegionBackwards`] if the range is backwards.
407 ///
408 /// Returns [`MapError::AddressRange`] if the largest address in the `range` is greater than the
409 /// largest virtual address covered by the page table given its root level.
walk_range<F>(&self, range: &MemoryRegion, f: &mut F) -> Result<(), MapError> where F: FnMut(&MemoryRegion, &Descriptor, usize) -> Result<(), ()>,410 pub fn walk_range<F>(&self, range: &MemoryRegion, f: &mut F) -> Result<(), MapError>
411 where
412 F: FnMut(&MemoryRegion, &Descriptor, usize) -> Result<(), ()>,
413 {
414 self.visit_range(range, &mut |mr, desc, level| {
415 f(mr, desc, level).map_err(|_| MapError::PteUpdateFault(*desc))
416 })
417 }
418
419 // Private version of `walk_range` using a closure that returns MapError on error
visit_range<F>(&self, range: &MemoryRegion, f: &mut F) -> Result<(), MapError> where F: FnMut(&MemoryRegion, &Descriptor, usize) -> Result<(), MapError>,420 pub(crate) fn visit_range<F>(&self, range: &MemoryRegion, f: &mut F) -> Result<(), MapError>
421 where
422 F: FnMut(&MemoryRegion, &Descriptor, usize) -> Result<(), MapError>,
423 {
424 self.verify_region(range)?;
425 self.table.visit_range(&self.translation, range, f)
426 }
427
428 /// Returns the level of mapping used for the given virtual address:
429 /// - `None` if it is unmapped
430 /// - `Some(LEAF_LEVEL)` if it is mapped as a single page
431 /// - `Some(level)` if it is mapped as a block at `level`
432 #[cfg(test)]
mapping_level(&self, va: VirtualAddress) -> Option<usize>433 pub(crate) fn mapping_level(&self, va: VirtualAddress) -> Option<usize> {
434 self.table.mapping_level(&self.translation, va)
435 }
436
437 /// Checks whether the region is within range of the page table.
verify_region(&self, region: &MemoryRegion) -> Result<(), MapError>438 fn verify_region(&self, region: &MemoryRegion) -> Result<(), MapError> {
439 if region.end() < region.start() {
440 return Err(MapError::RegionBackwards(region.clone()));
441 }
442 match self.va_range {
443 VaRange::Lower => {
444 if (region.start().0 as isize) < 0 {
445 return Err(MapError::AddressRange(region.start()));
446 } else if region.end().0 > self.size() {
447 return Err(MapError::AddressRange(region.end()));
448 }
449 }
450 VaRange::Upper => {
451 if region.start().0 as isize >= 0
452 || (region.start().0 as isize).unsigned_abs() > self.size()
453 {
454 return Err(MapError::AddressRange(region.start()));
455 }
456 }
457 }
458 Ok(())
459 }
460 }
461
462 impl<T: Translation> Debug for RootTable<T> {
fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>463 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
464 writeln!(
465 f,
466 "RootTable {{ pa: {}, level: {}, table:",
467 self.pa, self.table.level
468 )?;
469 self.table.fmt_indented(f, &self.translation, 0)?;
470 write!(f, "}}")
471 }
472 }
473
474 impl<T: Translation> Drop for RootTable<T> {
drop(&mut self)475 fn drop(&mut self) {
476 // SAFETY: We created the table in `RootTable::new` by calling `PageTableWithLevel::new`
477 // with `self.translation`. Subtables were similarly created by
478 // `PageTableWithLevel::split_entry` calling `PageTableWithLevel::new` with the same
479 // translation.
480 unsafe { self.table.free(&mut self.translation) }
481 }
482 }
483
484 struct ChunkedIterator<'a> {
485 range: &'a MemoryRegion,
486 granularity: usize,
487 start: usize,
488 }
489
490 impl Iterator for ChunkedIterator<'_> {
491 type Item = MemoryRegion;
492
next(&mut self) -> Option<MemoryRegion>493 fn next(&mut self) -> Option<MemoryRegion> {
494 if !self.range.0.contains(&VirtualAddress(self.start)) {
495 return None;
496 }
497 let end = self
498 .range
499 .0
500 .end
501 .0
502 .min((self.start | (self.granularity - 1)) + 1);
503 let c = MemoryRegion::new(self.start, end);
504 self.start = end;
505 Some(c)
506 }
507 }
508
509 bitflags! {
510 /// Attribute bits for a mapping in a page table.
511 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
512 pub struct Attributes: usize {
513 const VALID = 1 << 0;
514 const TABLE_OR_PAGE = 1 << 1;
515
516 const ATTRIBUTE_INDEX_0 = 0 << 2;
517 const ATTRIBUTE_INDEX_1 = 1 << 2;
518 const ATTRIBUTE_INDEX_2 = 2 << 2;
519 const ATTRIBUTE_INDEX_3 = 3 << 2;
520 const ATTRIBUTE_INDEX_4 = 4 << 2;
521 const ATTRIBUTE_INDEX_5 = 5 << 2;
522 const ATTRIBUTE_INDEX_6 = 6 << 2;
523 const ATTRIBUTE_INDEX_7 = 7 << 2;
524
525 const OUTER_SHAREABLE = 2 << 8;
526 const INNER_SHAREABLE = 3 << 8;
527
528 const NS = 1 << 5;
529 const USER = 1 << 6;
530 const READ_ONLY = 1 << 7;
531 const ACCESSED = 1 << 10;
532 const NON_GLOBAL = 1 << 11;
533 const DBM = 1 << 51;
534 /// Privileged Execute-never, if two privilege levels are supported.
535 const PXN = 1 << 53;
536 /// Unprivileged Execute-never, or just Execute-never if only one privilege level is
537 /// supported.
538 const UXN = 1 << 54;
539
540 // Software flags in block and page descriptor entries.
541 const SWFLAG_0 = 1 << 55;
542 const SWFLAG_1 = 1 << 56;
543 const SWFLAG_2 = 1 << 57;
544 const SWFLAG_3 = 1 << 58;
545 }
546 }
547
548 impl Attributes {
549 /// Mask for the bits determining the shareability of the mapping.
550 pub const SHAREABILITY_MASK: Self = Self::INNER_SHAREABLE;
551
552 /// Mask for the bits determining the attribute index of the mapping.
553 pub const ATTRIBUTE_INDEX_MASK: Self = Self::ATTRIBUTE_INDEX_7;
554 }
555
556 /// Smart pointer which owns a [`PageTable`] and knows what level it is at. This allows it to
557 /// implement `Debug` and `Drop`, as walking the page table hierachy requires knowing the starting
558 /// level.
559 #[derive(Debug)]
560 struct PageTableWithLevel<T: Translation> {
561 table: NonNull<PageTable>,
562 level: usize,
563 _translation: PhantomData<T>,
564 }
565
566 // SAFETY: The underlying PageTable is process-wide and can be safely accessed from any thread
567 // with appropriate synchronization. This type manages ownership for the raw pointer.
568 unsafe impl<T: Translation + Send> Send for PageTableWithLevel<T> {}
569
570 // SAFETY: &Self only allows reading from the page table, which is safe to do from any thread.
571 unsafe impl<T: Translation + Sync> Sync for PageTableWithLevel<T> {}
572
573 impl<T: Translation> PageTableWithLevel<T> {
574 /// Allocates a new, zeroed, appropriately-aligned page table with the given translation,
575 /// returning both a pointer to it and its physical address.
new(translation: &mut T, level: usize) -> (Self, PhysicalAddress)576 fn new(translation: &mut T, level: usize) -> (Self, PhysicalAddress) {
577 assert!(level <= LEAF_LEVEL);
578 let (table, pa) = translation.allocate_table();
579 (
580 // Safe because the pointer has been allocated with the appropriate layout, and the
581 // memory is zeroed which is valid initialisation for a PageTable.
582 Self::from_pointer(table, level),
583 pa,
584 )
585 }
586
from_pointer(table: NonNull<PageTable>, level: usize) -> Self587 fn from_pointer(table: NonNull<PageTable>, level: usize) -> Self {
588 Self {
589 table,
590 level,
591 _translation: PhantomData,
592 }
593 }
594
595 /// Returns a reference to the descriptor corresponding to a given virtual address.
get_entry(&self, va: VirtualAddress) -> &Descriptor596 fn get_entry(&self, va: VirtualAddress) -> &Descriptor {
597 let shift = PAGE_SHIFT + (LEAF_LEVEL - self.level) * BITS_PER_LEVEL;
598 let index = (va.0 >> shift) % (1 << BITS_PER_LEVEL);
599 // SAFETY: Safe because we know that the pointer is properly aligned, dereferenced and
600 // initialised, and nothing else can access the page table while we hold a mutable reference
601 // to the PageTableWithLevel (assuming it is not currently active).
602 let table = unsafe { self.table.as_ref() };
603 &table.entries[index]
604 }
605
606 /// Returns a mutable reference to the descriptor corresponding to a given virtual address.
get_entry_mut(&mut self, va: VirtualAddress) -> &mut Descriptor607 fn get_entry_mut(&mut self, va: VirtualAddress) -> &mut Descriptor {
608 let shift = PAGE_SHIFT + (LEAF_LEVEL - self.level) * BITS_PER_LEVEL;
609 let index = (va.0 >> shift) % (1 << BITS_PER_LEVEL);
610 // SAFETY: Safe because we know that the pointer is properly aligned, dereferenced and
611 // initialised, and nothing else can access the page table while we hold a mutable reference
612 // to the PageTableWithLevel (assuming it is not currently active).
613 let table = unsafe { self.table.as_mut() };
614 &mut table.entries[index]
615 }
616
617 /// Convert the descriptor in `entry` from a block mapping to a table mapping of
618 /// the same range with the same attributes
split_entry( translation: &mut T, chunk: &MemoryRegion, entry: &mut Descriptor, level: usize, ) -> Self619 fn split_entry(
620 translation: &mut T,
621 chunk: &MemoryRegion,
622 entry: &mut Descriptor,
623 level: usize,
624 ) -> Self {
625 let granularity = granularity_at_level(level);
626 let old = *entry;
627 let (mut subtable, subtable_pa) = Self::new(translation, level + 1);
628 if let Some(old_flags) = old.flags() {
629 let old_pa = old.output_address();
630 if !old_flags.contains(Attributes::TABLE_OR_PAGE)
631 && (!old_flags.is_empty() || old_pa.0 != 0)
632 {
633 // `old` was a block entry, so we need to split it.
634 // Recreate the entire block in the newly added table.
635 let a = align_down(chunk.0.start.0, granularity);
636 let b = align_up(chunk.0.end.0, granularity);
637 subtable.map_range(
638 translation,
639 &MemoryRegion::new(a, b),
640 old_pa,
641 old_flags,
642 Constraints::empty(),
643 );
644 }
645 }
646 entry.set(subtable_pa, Attributes::TABLE_OR_PAGE | Attributes::VALID);
647 subtable
648 }
649
650 /// Maps the the given virtual address range in this pagetable to the corresponding physical
651 /// address range starting at the given `pa`, recursing into any subtables as necessary. To map
652 /// block and page entries, `Attributes::VALID` must be set in `flags`.
653 ///
654 /// Assumes that the entire range is within the range covered by this pagetable.
655 ///
656 /// Panics if the `translation` doesn't provide a corresponding physical address for some
657 /// virtual address within the range, as there is no way to roll back to a safe state so this
658 /// should be checked by the caller beforehand.
map_range( &mut self, translation: &mut T, range: &MemoryRegion, mut pa: PhysicalAddress, flags: Attributes, constraints: Constraints, )659 fn map_range(
660 &mut self,
661 translation: &mut T,
662 range: &MemoryRegion,
663 mut pa: PhysicalAddress,
664 flags: Attributes,
665 constraints: Constraints,
666 ) {
667 let level = self.level;
668 let granularity = granularity_at_level(level);
669
670 for chunk in range.split(level) {
671 let entry = self.get_entry_mut(chunk.0.start);
672
673 if level == LEAF_LEVEL {
674 // Put down a page mapping.
675 entry.set(pa, flags | Attributes::TABLE_OR_PAGE);
676 } else if chunk.is_block(level)
677 && !entry.is_table_or_page()
678 && is_aligned(pa.0, granularity)
679 && !constraints.contains(Constraints::NO_BLOCK_MAPPINGS)
680 {
681 // Rather than leak the entire subhierarchy, only put down
682 // a block mapping if the region is not already covered by
683 // a table mapping.
684 entry.set(pa, flags);
685 } else {
686 let mut subtable = entry
687 .subtable(translation, level)
688 .unwrap_or_else(|| Self::split_entry(translation, &chunk, entry, level));
689 subtable.map_range(translation, &chunk, pa, flags, constraints);
690 }
691 pa.0 += chunk.len();
692 }
693 }
694
fmt_indented( &self, f: &mut Formatter, translation: &T, indentation: usize, ) -> Result<(), fmt::Error>695 fn fmt_indented(
696 &self,
697 f: &mut Formatter,
698 translation: &T,
699 indentation: usize,
700 ) -> Result<(), fmt::Error> {
701 const WIDTH: usize = 3;
702 // SAFETY: Safe because we know that the pointer is aligned, initialised and dereferencable,
703 // and the PageTable won't be mutated while we are using it.
704 let table = unsafe { self.table.as_ref() };
705
706 let mut i = 0;
707 while i < table.entries.len() {
708 if table.entries[i].0 == 0 {
709 let first_zero = i;
710 while i < table.entries.len() && table.entries[i].0 == 0 {
711 i += 1;
712 }
713 if i - 1 == first_zero {
714 writeln!(f, "{:indentation$}{: <WIDTH$}: 0", "", first_zero)?;
715 } else {
716 writeln!(f, "{:indentation$}{: <WIDTH$}-{}: 0", "", first_zero, i - 1)?;
717 }
718 } else {
719 writeln!(
720 f,
721 "{:indentation$}{: <WIDTH$}: {:?}",
722 "", i, table.entries[i],
723 )?;
724 if let Some(subtable) = table.entries[i].subtable(translation, self.level) {
725 subtable.fmt_indented(f, translation, indentation + 2)?;
726 }
727 i += 1;
728 }
729 }
730 Ok(())
731 }
732
733 /// Frees the memory used by this pagetable and all subtables. It is not valid to access the
734 /// page table after this.
735 ///
736 /// # Safety
737 ///
738 /// The table and all its subtables must have been created by `PageTableWithLevel::new` with the
739 /// same `translation`.
free(&mut self, translation: &mut T)740 unsafe fn free(&mut self, translation: &mut T) {
741 // SAFETY: Safe because we know that the pointer is aligned, initialised and dereferencable,
742 // and the PageTable won't be mutated while we are freeing it.
743 let table = unsafe { self.table.as_ref() };
744 for entry in table.entries {
745 if let Some(mut subtable) = entry.subtable(translation, self.level) {
746 // Safe because our caller promised that all our subtables were created by
747 // `PageTableWithLevel::new` with the same `translation`.
748 subtable.free(translation);
749 }
750 }
751 // SAFETY: Safe because our caller promised that the table was created by
752 // `PageTableWithLevel::new` with `translation`, which then allocated it by calling
753 // `allocate_table` on `translation`.
754 unsafe {
755 // Actually free the memory used by the `PageTable`.
756 translation.deallocate_table(self.table);
757 }
758 }
759
760 /// Modifies a range of page table entries by applying a function to each page table entry.
761 /// If the range is not aligned to block boundaries, block descriptors will be split up.
modify_range<F>( &mut self, translation: &mut T, range: &MemoryRegion, f: &F, ) -> Result<(), MapError> where F: Fn(&MemoryRegion, &mut Descriptor, usize) -> Result<(), ()> + ?Sized,762 fn modify_range<F>(
763 &mut self,
764 translation: &mut T,
765 range: &MemoryRegion,
766 f: &F,
767 ) -> Result<(), MapError>
768 where
769 F: Fn(&MemoryRegion, &mut Descriptor, usize) -> Result<(), ()> + ?Sized,
770 {
771 let level = self.level;
772 for chunk in range.split(level) {
773 let entry = self.get_entry_mut(chunk.0.start);
774 if let Some(mut subtable) = entry.subtable(translation, level).or_else(|| {
775 if !chunk.is_block(level) {
776 // The current chunk is not aligned to the block size at this level
777 // Split it before recursing to the next level
778 Some(Self::split_entry(translation, &chunk, entry, level))
779 } else {
780 None
781 }
782 }) {
783 subtable.modify_range(translation, &chunk, f)?;
784 } else {
785 f(&chunk, entry, level).map_err(|_| MapError::PteUpdateFault(*entry))?;
786 }
787 }
788 Ok(())
789 }
790
791 /// Walks a range of page table entries and passes each one to a caller provided function.
792 /// If the function returns an error, the walk is terminated and the error value is passed on
visit_range<F, E>(&self, translation: &T, range: &MemoryRegion, f: &mut F) -> Result<(), E> where F: FnMut(&MemoryRegion, &Descriptor, usize) -> Result<(), E>,793 fn visit_range<F, E>(&self, translation: &T, range: &MemoryRegion, f: &mut F) -> Result<(), E>
794 where
795 F: FnMut(&MemoryRegion, &Descriptor, usize) -> Result<(), E>,
796 {
797 let level = self.level;
798 for chunk in range.split(level) {
799 let entry = self.get_entry(chunk.0.start);
800 if let Some(subtable) = entry.subtable(translation, level) {
801 subtable.visit_range(translation, &chunk, f)?;
802 } else {
803 f(&chunk, entry, level)?;
804 }
805 }
806 Ok(())
807 }
808
809 /// Returns the level of mapping used for the given virtual address:
810 /// - `None` if it is unmapped
811 /// - `Some(LEAF_LEVEL)` if it is mapped as a single page
812 /// - `Some(level)` if it is mapped as a block at `level`
813 #[cfg(test)]
mapping_level(&self, translation: &T, va: VirtualAddress) -> Option<usize>814 fn mapping_level(&self, translation: &T, va: VirtualAddress) -> Option<usize> {
815 let entry = self.get_entry(va);
816 if let Some(subtable) = entry.subtable(translation, self.level) {
817 subtable.mapping_level(translation, va)
818 } else {
819 if entry.is_valid() {
820 Some(self.level)
821 } else {
822 None
823 }
824 }
825 }
826 }
827
828 /// A single level of a page table.
829 #[repr(C, align(4096))]
830 pub struct PageTable {
831 entries: [Descriptor; 1 << BITS_PER_LEVEL],
832 }
833
834 impl PageTable {
835 /// Allocates a new zeroed, appropriately-aligned pagetable on the heap using the global
836 /// allocator and returns a pointer to it.
837 #[cfg(feature = "alloc")]
new() -> NonNull<Self>838 pub fn new() -> NonNull<Self> {
839 // SAFETY: Safe because the pointer has been allocated with the appropriate layout by the
840 // global allocator, and the memory is zeroed which is valid initialisation for a PageTable.
841 unsafe { allocate_zeroed() }
842 }
843 }
844
845 /// An entry in a page table.
846 ///
847 /// A descriptor may be:
848 /// - Invalid, i.e. the virtual address range is unmapped
849 /// - A page mapping, if it is in the lowest level page table.
850 /// - A block mapping, if it is not in the lowest level page table.
851 /// - A pointer to a lower level pagetable, if it is not in the lowest level page table.
852 #[derive(Clone, Copy, PartialEq, Eq)]
853 #[repr(C)]
854 pub struct Descriptor(usize);
855
856 impl Descriptor {
857 const PHYSICAL_ADDRESS_BITMASK: usize = !(PAGE_SIZE - 1) & !(0xffff << 48);
858
output_address(self) -> PhysicalAddress859 pub(crate) fn output_address(self) -> PhysicalAddress {
860 PhysicalAddress(self.0 & Self::PHYSICAL_ADDRESS_BITMASK)
861 }
862
863 /// Returns the flags of this page table entry, or `None` if its state does not
864 /// contain a valid set of flags.
flags(self) -> Option<Attributes>865 pub fn flags(self) -> Option<Attributes> {
866 Attributes::from_bits(self.0 & !Self::PHYSICAL_ADDRESS_BITMASK)
867 }
868
869 /// Modifies the page table entry by setting or clearing its flags.
870 /// Panics when attempting to convert a table descriptor into a block/page descriptor or vice
871 /// versa - this is not supported via this API.
modify_flags(&mut self, set: Attributes, clear: Attributes)872 pub fn modify_flags(&mut self, set: Attributes, clear: Attributes) {
873 let flags = (self.0 | set.bits()) & !clear.bits();
874
875 if (self.0 ^ flags) & Attributes::TABLE_OR_PAGE.bits() != 0 {
876 panic!("Cannot convert between table and block/page descriptors\n");
877 }
878 self.0 = flags;
879 }
880
881 /// Returns `true` if [`Attributes::VALID`] is set on this entry, e.g. if the entry is mapped.
is_valid(self) -> bool882 pub fn is_valid(self) -> bool {
883 (self.0 & Attributes::VALID.bits()) != 0
884 }
885
886 /// Returns `true` if this is a valid entry pointing to a next level translation table or a page.
is_table_or_page(self) -> bool887 pub fn is_table_or_page(self) -> bool {
888 if let Some(flags) = self.flags() {
889 flags.contains(Attributes::TABLE_OR_PAGE | Attributes::VALID)
890 } else {
891 false
892 }
893 }
894
set(&mut self, pa: PhysicalAddress, flags: Attributes)895 pub(crate) fn set(&mut self, pa: PhysicalAddress, flags: Attributes) {
896 self.0 = (pa.0 & Self::PHYSICAL_ADDRESS_BITMASK) | flags.bits();
897 }
898
subtable<T: Translation>( self, translation: &T, level: usize, ) -> Option<PageTableWithLevel<T>>899 fn subtable<T: Translation>(
900 self,
901 translation: &T,
902 level: usize,
903 ) -> Option<PageTableWithLevel<T>> {
904 if level < LEAF_LEVEL && self.is_table_or_page() {
905 let output_address = self.output_address();
906 let table = translation.physical_to_virtual(output_address);
907 return Some(PageTableWithLevel::from_pointer(table, level + 1));
908 }
909 None
910 }
911 }
912
913 impl Debug for Descriptor {
fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>914 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
915 write!(f, "{:#016x}", self.0)?;
916 if self.is_valid() {
917 if let Some(flags) = self.flags() {
918 write!(f, " ({}, {:?})", self.output_address(), flags)?;
919 }
920 }
921 Ok(())
922 }
923 }
924
925 /// Allocates appropriately aligned heap space for a `T` and zeroes it.
926 ///
927 /// # Safety
928 ///
929 /// It must be valid to initialise the type `T` by simply zeroing its memory.
930 #[cfg(feature = "alloc")]
allocate_zeroed<T>() -> NonNull<T>931 unsafe fn allocate_zeroed<T>() -> NonNull<T> {
932 let layout = Layout::new::<T>();
933 // Safe because we know the layout has non-zero size.
934 let pointer = alloc_zeroed(layout);
935 if pointer.is_null() {
936 handle_alloc_error(layout);
937 }
938 // Safe because we just checked that the pointer is non-null.
939 NonNull::new_unchecked(pointer as *mut T)
940 }
941
942 /// Deallocates the heap space for a `T` which was previously allocated by `allocate_zeroed`.
943 ///
944 /// # Safety
945 ///
946 /// The memory must have been allocated by the global allocator, with the layout for `T`, and not
947 /// yet deallocated.
948 #[cfg(feature = "alloc")]
deallocate<T>(ptr: NonNull<T>)949 pub(crate) unsafe fn deallocate<T>(ptr: NonNull<T>) {
950 let layout = Layout::new::<T>();
951 dealloc(ptr.as_ptr() as *mut u8, layout);
952 }
953
align_down(value: usize, alignment: usize) -> usize954 const fn align_down(value: usize, alignment: usize) -> usize {
955 value & !(alignment - 1)
956 }
957
align_up(value: usize, alignment: usize) -> usize958 const fn align_up(value: usize, alignment: usize) -> usize {
959 ((value - 1) | (alignment - 1)) + 1
960 }
961
is_aligned(value: usize, alignment: usize) -> bool962 pub(crate) const fn is_aligned(value: usize, alignment: usize) -> bool {
963 value & (alignment - 1) == 0
964 }
965
966 #[cfg(test)]
967 mod tests {
968 use super::*;
969 #[cfg(feature = "alloc")]
970 use crate::idmap::IdTranslation;
971 #[cfg(feature = "alloc")]
972 use alloc::{format, string::ToString, vec, vec::Vec};
973
974 #[cfg(feature = "alloc")]
975 #[test]
display_memory_region()976 fn display_memory_region() {
977 let region = MemoryRegion::new(0x1234, 0x56789);
978 assert_eq!(
979 ®ion.to_string(),
980 "0x0000000000001000..0x0000000000057000"
981 );
982 assert_eq!(
983 &format!("{:?}", region),
984 "0x0000000000001000..0x0000000000057000"
985 );
986 }
987
988 #[test]
subtract_virtual_address()989 fn subtract_virtual_address() {
990 let low = VirtualAddress(0x12);
991 let high = VirtualAddress(0x1234);
992 assert_eq!(high - low, 0x1222);
993 }
994
995 #[cfg(debug_assertions)]
996 #[test]
997 #[should_panic]
subtract_virtual_address_overflow()998 fn subtract_virtual_address_overflow() {
999 let low = VirtualAddress(0x12);
1000 let high = VirtualAddress(0x1234);
1001
1002 // This would overflow, so should panic.
1003 let _ = low - high;
1004 }
1005
1006 #[test]
add_virtual_address()1007 fn add_virtual_address() {
1008 assert_eq!(VirtualAddress(0x1234) + 0x42, VirtualAddress(0x1276));
1009 }
1010
1011 #[test]
subtract_physical_address()1012 fn subtract_physical_address() {
1013 let low = PhysicalAddress(0x12);
1014 let high = PhysicalAddress(0x1234);
1015 assert_eq!(high - low, 0x1222);
1016 }
1017
1018 #[cfg(debug_assertions)]
1019 #[test]
1020 #[should_panic]
subtract_physical_address_overflow()1021 fn subtract_physical_address_overflow() {
1022 let low = PhysicalAddress(0x12);
1023 let high = PhysicalAddress(0x1234);
1024
1025 // This would overflow, so should panic.
1026 let _ = low - high;
1027 }
1028
1029 #[test]
add_physical_address()1030 fn add_physical_address() {
1031 assert_eq!(PhysicalAddress(0x1234) + 0x42, PhysicalAddress(0x1276));
1032 }
1033
1034 #[test]
invalid_descriptor()1035 fn invalid_descriptor() {
1036 let desc = Descriptor(0usize);
1037 assert!(!desc.is_valid());
1038 assert!(!desc.flags().unwrap().contains(Attributes::VALID));
1039 }
1040
1041 #[test]
set_descriptor()1042 fn set_descriptor() {
1043 const PHYSICAL_ADDRESS: usize = 0x12340000;
1044 let mut desc = Descriptor(0usize);
1045 assert!(!desc.is_valid());
1046 desc.set(
1047 PhysicalAddress(PHYSICAL_ADDRESS),
1048 Attributes::TABLE_OR_PAGE | Attributes::USER | Attributes::SWFLAG_1 | Attributes::VALID,
1049 );
1050 assert!(desc.is_valid());
1051 assert_eq!(
1052 desc.flags().unwrap(),
1053 Attributes::TABLE_OR_PAGE | Attributes::USER | Attributes::SWFLAG_1 | Attributes::VALID
1054 );
1055 assert_eq!(desc.output_address(), PhysicalAddress(PHYSICAL_ADDRESS));
1056 }
1057
1058 #[test]
modify_descriptor_flags()1059 fn modify_descriptor_flags() {
1060 let mut desc = Descriptor(0usize);
1061 assert!(!desc.is_valid());
1062 desc.set(
1063 PhysicalAddress(0x12340000),
1064 Attributes::TABLE_OR_PAGE | Attributes::USER | Attributes::SWFLAG_1,
1065 );
1066 desc.modify_flags(
1067 Attributes::DBM | Attributes::SWFLAG_3,
1068 Attributes::VALID | Attributes::SWFLAG_1,
1069 );
1070 assert!(!desc.is_valid());
1071 assert_eq!(
1072 desc.flags().unwrap(),
1073 Attributes::TABLE_OR_PAGE | Attributes::USER | Attributes::SWFLAG_3 | Attributes::DBM
1074 );
1075 }
1076
1077 #[test]
1078 #[should_panic]
modify_descriptor_table_or_page_flag()1079 fn modify_descriptor_table_or_page_flag() {
1080 let mut desc = Descriptor(0usize);
1081 assert!(!desc.is_valid());
1082 desc.set(
1083 PhysicalAddress(0x12340000),
1084 Attributes::TABLE_OR_PAGE | Attributes::USER | Attributes::SWFLAG_1,
1085 );
1086 desc.modify_flags(Attributes::VALID, Attributes::TABLE_OR_PAGE);
1087 }
1088
1089 #[cfg(feature = "alloc")]
1090 #[test]
unaligned_chunks()1091 fn unaligned_chunks() {
1092 let region = MemoryRegion::new(0x0000_2000, 0x0020_5000);
1093 let chunks = region.split(LEAF_LEVEL - 1).collect::<Vec<_>>();
1094 assert_eq!(
1095 chunks,
1096 vec![
1097 MemoryRegion::new(0x0000_2000, 0x0020_0000),
1098 MemoryRegion::new(0x0020_0000, 0x0020_5000),
1099 ]
1100 );
1101 }
1102
1103 #[cfg(feature = "alloc")]
1104 #[test]
1105 #[should_panic]
no_el2_ttbr1()1106 fn no_el2_ttbr1() {
1107 RootTable::<IdTranslation>::new(IdTranslation, 1, TranslationRegime::El2, VaRange::Upper);
1108 }
1109
1110 #[cfg(feature = "alloc")]
1111 #[test]
1112 #[should_panic]
no_el3_ttbr1()1113 fn no_el3_ttbr1() {
1114 RootTable::<IdTranslation>::new(IdTranslation, 1, TranslationRegime::El3, VaRange::Upper);
1115 }
1116 }
1117