aarch64_paging/
descriptor.rs

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//! Abstractions for page table descriptor and the physical addresses and attributes they may
6//! describe
7
8use crate::Translation;
9use crate::paging::LEAF_LEVEL;
10use crate::paging::PAGE_SIZE;
11use crate::paging::PageTableWithLevel;
12
13use bitflags::bitflags;
14use core::fmt::{self, Debug, Display, Formatter};
15use core::ops::{Add, Sub};
16use core::sync::atomic::{AtomicUsize, Ordering};
17
18/// An aarch64 virtual address, the input type of a stage 1 page table.
19#[derive(Copy, Clone, Default, Eq, Ord, PartialEq, PartialOrd)]
20#[repr(transparent)]
21pub struct VirtualAddress(pub usize);
22
23impl Display for VirtualAddress {
24    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
25        write!(f, "{:#018x}", self.0)
26    }
27}
28
29impl Debug for VirtualAddress {
30    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
31        write!(f, "VirtualAddress({})", self)
32    }
33}
34
35impl Sub for VirtualAddress {
36    type Output = usize;
37
38    fn sub(self, other: Self) -> Self::Output {
39        self.0 - other.0
40    }
41}
42
43impl Add<usize> for VirtualAddress {
44    type Output = Self;
45
46    fn add(self, other: usize) -> Self {
47        Self(self.0 + other)
48    }
49}
50
51impl Sub<usize> for VirtualAddress {
52    type Output = Self;
53
54    fn sub(self, other: usize) -> Self {
55        Self(self.0 - other)
56    }
57}
58
59/// An aarch64 physical address or intermediate physical address, the output type of a stage 1 page
60/// table.
61#[derive(Copy, Clone, Default, Eq, Ord, PartialEq, PartialOrd)]
62#[repr(transparent)]
63pub struct PhysicalAddress(pub usize);
64
65impl Display for PhysicalAddress {
66    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
67        write!(f, "{:#018x}", self.0)
68    }
69}
70
71impl Debug for PhysicalAddress {
72    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
73        write!(f, "PhysicalAddress({})", self)
74    }
75}
76
77impl Sub for PhysicalAddress {
78    type Output = usize;
79
80    fn sub(self, other: Self) -> Self::Output {
81        self.0 - other.0
82    }
83}
84
85impl Add<usize> for PhysicalAddress {
86    type Output = Self;
87
88    fn add(self, other: usize) -> Self {
89        Self(self.0 + other)
90    }
91}
92
93impl Sub<usize> for PhysicalAddress {
94    type Output = Self;
95
96    fn sub(self, other: usize) -> Self {
97        Self(self.0 - other)
98    }
99}
100
101bitflags! {
102    /// Attribute bits for a mapping in a page table.
103    #[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
104    pub struct Attributes: usize {
105        const VALID         = 1 << 0;
106        const TABLE_OR_PAGE = 1 << 1;
107
108        const ATTRIBUTE_INDEX_0 = 0 << 2;
109        const ATTRIBUTE_INDEX_1 = 1 << 2;
110        const ATTRIBUTE_INDEX_2 = 2 << 2;
111        const ATTRIBUTE_INDEX_3 = 3 << 2;
112        const ATTRIBUTE_INDEX_4 = 4 << 2;
113        const ATTRIBUTE_INDEX_5 = 5 << 2;
114        const ATTRIBUTE_INDEX_6 = 6 << 2;
115        const ATTRIBUTE_INDEX_7 = 7 << 2;
116
117        const OUTER_SHAREABLE = 2 << 8;
118        const INNER_SHAREABLE = 3 << 8;
119
120        const NS            = 1 << 5;
121        const USER          = 1 << 6;
122        const READ_ONLY     = 1 << 7;
123        const ACCESSED      = 1 << 10;
124        const NON_GLOBAL    = 1 << 11;
125        /// Guarded Page - indirect forward edge jumps expect an appropriate BTI landing pad.
126        const GP            = 1 << 50;
127        const DBM           = 1 << 51;
128        /// Privileged Execute-never, if two privilege levels are supported.
129        const PXN           = 1 << 53;
130        /// Unprivileged Execute-never, or just Execute-never if only one privilege level is
131        /// supported.
132        const UXN           = 1 << 54;
133
134        // Software flags in block and page descriptor entries.
135        const SWFLAG_0 = 1 << 55;
136        const SWFLAG_1 = 1 << 56;
137        const SWFLAG_2 = 1 << 57;
138        const SWFLAG_3 = 1 << 58;
139
140        const PXN_TABLE = 1 << 59;
141        const XN_TABLE = 1 << 60;
142        const AP_TABLE_NO_EL0 = 1 << 61;
143        const AP_TABLE_NO_WRITE = 1 << 62;
144        const NS_TABLE = 1 << 63;
145    }
146}
147
148impl Attributes {
149    /// Mask for the bits determining the shareability of the mapping.
150    pub const SHAREABILITY_MASK: Self = Self::INNER_SHAREABLE;
151
152    /// Mask for the bits determining the attribute index of the mapping.
153    pub const ATTRIBUTE_INDEX_MASK: Self = Self::ATTRIBUTE_INDEX_7;
154}
155
156pub(crate) type DescriptorBits = usize;
157
158/// An entry in a page table.
159///
160/// A descriptor may be:
161///   - Invalid, i.e. the virtual address range is unmapped
162///   - A page mapping, if it is in the lowest level page table.
163///   - A block mapping, if it is not in the lowest level page table.
164///   - A pointer to a lower level pagetable, if it is not in the lowest level page table.
165#[repr(C)]
166pub struct Descriptor(pub(crate) AtomicUsize);
167
168impl Descriptor {
169    /// An empty (i.e. 0) descriptor.
170    pub const EMPTY: Self = Self(AtomicUsize::new(0));
171
172    const PHYSICAL_ADDRESS_BITMASK: usize = !(PAGE_SIZE - 1) & !(0xffff << 48);
173
174    /// Returns the contents of a descriptor which may be potentially live
175    /// Use acquire semantics so that the load is not reordered with subsequent loads
176    pub(crate) fn bits(&self) -> DescriptorBits {
177        self.0.load(Ordering::Acquire)
178    }
179
180    /// Returns the physical address that this descriptor refers to if it is valid.
181    ///
182    /// Depending on the flags this could be the address of a subtable, a mapping, or (if it is not
183    /// a valid mapping) entirely arbitrary.
184    pub fn output_address(&self) -> PhysicalAddress {
185        Self::output_address_from_bits(self.bits())
186    }
187
188    fn output_address_from_bits(bits: DescriptorBits) -> PhysicalAddress {
189        PhysicalAddress(bits & Self::PHYSICAL_ADDRESS_BITMASK)
190    }
191
192    fn flags_from_bits(bits: DescriptorBits) -> Attributes {
193        Attributes::from_bits_retain(bits & !Self::PHYSICAL_ADDRESS_BITMASK)
194    }
195
196    /// Returns the flags of this page table entry, or `None` if its state does not
197    /// contain a valid set of flags.
198    pub fn flags(&self) -> Attributes {
199        Self::flags_from_bits(self.bits())
200    }
201
202    /// Returns `true` if [`Attributes::VALID`] is set on this entry, e.g. if the entry is mapped.
203    pub fn is_valid(&self) -> bool {
204        (self.bits() & Attributes::VALID.bits()) != 0
205    }
206
207    /// Returns `true` if this is a valid entry pointing to a next level translation table or a page.
208    pub fn is_table_or_page(&self) -> bool {
209        self.flags()
210            .contains(Attributes::TABLE_OR_PAGE | Attributes::VALID)
211    }
212
213    pub(crate) fn set(&mut self, pa: PhysicalAddress, flags: Attributes) {
214        self.0.store(
215            (pa.0 & Self::PHYSICAL_ADDRESS_BITMASK) | flags.bits(),
216            Ordering::Release,
217        );
218    }
219
220    pub(crate) fn subtable<T: Translation>(
221        &self,
222        translation: &T,
223        level: usize,
224    ) -> Option<PageTableWithLevel<T>> {
225        if level < LEAF_LEVEL && self.is_table_or_page() {
226            let output_address = self.output_address();
227            let table = translation.physical_to_virtual(output_address);
228            return Some(PageTableWithLevel::from_pointer(table, level + 1));
229        }
230        None
231    }
232}
233
234impl Debug for Descriptor {
235    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
236        write!(f, "{:#016x}", self.bits())?;
237        if self.is_valid() {
238            write!(f, " ({}, {:?})", self.output_address(), self.flags())?;
239        }
240        Ok(())
241    }
242}
243
244enum DescriptorEnum<'a> {
245    /// A descriptor that is part of a set of page tables that are currently in use by one of the
246    /// CPUs
247    Active(&'a mut Descriptor),
248
249    /// A descriptor that is part of a set of page tables that are currently inactive. This means
250    /// TLB maintenance may be elided until the next time the page tables are made active.
251    Inactive(&'a mut Descriptor),
252
253    /// A descriptor that does not actually represent an entry in a page table. It permits updaters
254    /// taking an UpdatableDescriptor to be called for a dry run to observe their effect without
255    /// the need to pass an actual descriptor.
256    ActiveClone(DescriptorBits),
257}
258
259pub struct UpdatableDescriptor<'a> {
260    descriptor: DescriptorEnum<'a>,
261    level: usize,
262    updated: bool,
263}
264
265impl<'a> UpdatableDescriptor<'a> {
266    /// Creates a new wrapper around a real descriptor that may or may not be live
267    pub(crate) fn new(desc: &'a mut Descriptor, level: usize, live: bool) -> Self {
268        Self {
269            descriptor: if live {
270                DescriptorEnum::Active(desc)
271            } else {
272                DescriptorEnum::Inactive(desc)
273            },
274            level: level,
275            updated: false,
276        }
277    }
278
279    /// Creates a new wrapper around an ActiveClone descriptor, which is used to observe the
280    /// effect of user provided updater functions without applying them to actual descriptors
281    pub(crate) fn clone_from(d: &Descriptor, level: usize) -> Self {
282        Self {
283            descriptor: DescriptorEnum::ActiveClone(d.bits()),
284            level: level,
285            updated: false,
286        }
287    }
288
289    /// Returns the level in the page table hierarchy at which this descriptor appears
290    pub fn level(&self) -> usize {
291        self.level
292    }
293
294    /// Whether the descriptor was updated as a result of a call to set() or modify_flags()
295    pub fn updated(&self) -> bool {
296        self.updated
297    }
298
299    /// Returns whether this descriptor represents a table mapping. In this case, the output address
300    /// refers to a next level table.
301    pub fn is_table(&self) -> bool {
302        self.level < 3 && self.flags().contains(Attributes::TABLE_OR_PAGE)
303    }
304
305    /// Returns the bit representation of the underlying descriptor
306    pub fn bits(&self) -> DescriptorBits {
307        match &self.descriptor {
308            DescriptorEnum::Active(d) | DescriptorEnum::Inactive(d) => d.bits(),
309            DescriptorEnum::ActiveClone(d) => *d,
310        }
311    }
312
313    /// Assigns the underlying descriptor according to `pa` and `flags, provided that doing so is
314    /// permitted under BBM rules
315    pub fn set(&mut self, pa: PhysicalAddress, flags: Attributes) -> Result<(), ()> {
316        if !self.bbm_permits_update(pa, flags) {
317            return Err(());
318        }
319        let val = (pa.0 & Descriptor::PHYSICAL_ADDRESS_BITMASK) | flags.bits();
320        match &mut self.descriptor {
321            DescriptorEnum::Active(d) | DescriptorEnum::Inactive(d) => {
322                self.updated |= val != d.0.swap(val, Ordering::Release)
323            }
324            DescriptorEnum::ActiveClone(d) => {
325                self.updated |= *d != val;
326                *d = val
327            }
328        };
329        Ok(())
330    }
331
332    /// Returns the physical address to which this descriptor refers
333    ///
334    /// Depending on the flags this could be the address of a subtable, a mapping, or (if it is not
335    /// a valid mapping) entirely arbitrary.
336    pub fn output_address(&self) -> PhysicalAddress {
337        Descriptor::output_address_from_bits(self.bits())
338    }
339
340    /// Returns the flags of this descriptor
341    pub fn flags(&self) -> Attributes {
342        Descriptor::flags_from_bits(self.bits())
343    }
344
345    /// Returns whether this descriptor should be considered live and valid, in which case BBM
346    /// rules need to be checked. ActiveClone() variants are explicitly intended for checking BBM
347    /// rules, so they are considered live by this API
348    fn is_live_and_valid(&self) -> bool {
349        match &self.descriptor {
350            DescriptorEnum::Inactive(_) => false,
351            _ => self.flags().contains(Attributes::VALID),
352        }
353    }
354
355    /// Returns whether BBM permits setting the flags on this descriptor to `flags`
356    fn bbm_permits_update(&self, pa: PhysicalAddress, flags: Attributes) -> bool {
357        if !self.is_live_and_valid() || !flags.contains(Attributes::VALID) {
358            return true;
359        }
360
361        // Modifying the output address on a live valid descriptor is not allowed
362        if pa != self.output_address() {
363            return false;
364        }
365
366        // Masks of bits that may be set resp. cleared on a live, valid mapping without BBM
367        let clear_allowed_mask = Attributes::VALID
368            | Attributes::READ_ONLY
369            | Attributes::ACCESSED
370            | Attributes::DBM
371            | Attributes::PXN
372            | Attributes::UXN
373            | Attributes::SWFLAG_0
374            | Attributes::SWFLAG_1
375            | Attributes::SWFLAG_2
376            | Attributes::SWFLAG_3;
377        let set_allowed_mask = clear_allowed_mask | Attributes::NON_GLOBAL;
378
379        (!self.flags() & flags & !set_allowed_mask).is_empty()
380            && (self.flags() & !flags & !clear_allowed_mask).is_empty()
381    }
382
383    /// Modifies the descriptor by setting or clearing its flags.
384    pub fn modify_flags(&mut self, set: Attributes, clear: Attributes) -> Result<(), ()> {
385        let oldval = self.flags();
386        let flags = (oldval | set) & !clear;
387
388        if (oldval ^ flags).contains(Attributes::TABLE_OR_PAGE) {
389            // Cannot convert between table and block/page descriptors, regardless of whether or
390            // not BBM permits this and whether the entry is live, given that doing so would
391            // corrupt our data strucutures.
392            return Err(());
393        }
394        let oa = self.output_address();
395        self.set(oa, flags)
396    }
397}