x86_64/structures/paging/mapper/
mod.rs

1//! Abstractions for reading and modifying the mapping of pages.
2
3pub use self::mapped_page_table::{MappedPageTable, PageTableFrameMapping};
4#[cfg(target_pointer_width = "64")]
5pub use self::offset_page_table::OffsetPageTable;
6#[cfg(all(feature = "instructions", target_arch = "x86_64"))]
7pub use self::recursive_page_table::{InvalidPageTable, RecursivePageTable};
8
9use crate::structures::paging::{
10    frame_alloc::{FrameAllocator, FrameDeallocator},
11    page::PageRangeInclusive,
12    page_table::PageTableFlags,
13    Page, PageSize, PhysFrame, Size1GiB, Size2MiB, Size4KiB,
14};
15use crate::{PhysAddr, VirtAddr};
16
17mod mapped_page_table;
18mod offset_page_table;
19#[cfg(all(feature = "instructions", target_arch = "x86_64"))]
20mod recursive_page_table;
21
22/// An empty convencience trait that requires the `Mapper` trait for all page sizes.
23pub trait MapperAllSizes: Mapper<Size4KiB> + Mapper<Size2MiB> + Mapper<Size1GiB> {}
24
25impl<T> MapperAllSizes for T where T: Mapper<Size4KiB> + Mapper<Size2MiB> + Mapper<Size1GiB> {}
26
27/// Provides methods for translating virtual addresses.
28pub trait Translate {
29    /// Return the frame that the given virtual address is mapped to and the offset within that
30    /// frame.
31    ///
32    /// If the given address has a valid mapping, the mapped frame and the offset within that
33    /// frame is returned. Otherwise an error value is returned.
34    ///
35    /// This function works with huge pages of all sizes.
36    fn translate(&self, addr: VirtAddr) -> TranslateResult;
37
38    /// Translates the given virtual address to the physical address that it maps to.
39    ///
40    /// Returns `None` if there is no valid mapping for the given address.
41    ///
42    /// This is a convenience method. For more information about a mapping see the
43    /// [`translate`](Translate::translate) method.
44    #[inline]
45    fn translate_addr(&self, addr: VirtAddr) -> Option<PhysAddr> {
46        match self.translate(addr) {
47            TranslateResult::NotMapped | TranslateResult::InvalidFrameAddress(_) => None,
48            TranslateResult::Mapped { frame, offset, .. } => Some(frame.start_address() + offset),
49        }
50    }
51}
52
53/// The return value of the [`Translate::translate`] function.
54///
55/// If the given address has a valid mapping, a `Frame4KiB`, `Frame2MiB`, or `Frame1GiB` variant
56/// is returned, depending on the size of the mapped page. The remaining variants indicate errors.
57#[derive(Debug)]
58pub enum TranslateResult {
59    /// The virtual address is mapped to a physical frame.
60    Mapped {
61        /// The mapped frame.
62        frame: MappedFrame,
63        /// The offset within the mapped frame.
64        offset: u64,
65        /// The entry flags in the lowest-level page table.
66        ///
67        /// Flags of higher-level page table entries are not included here, but they can still
68        /// affect the effective flags for an address, for example when the WRITABLE flag is not
69        /// set for a level 3 entry.
70        flags: PageTableFlags,
71    },
72    /// The given virtual address is not mapped to a physical frame.
73    NotMapped,
74    /// The page table entry for the given virtual address points to an invalid physical address.
75    InvalidFrameAddress(PhysAddr),
76}
77
78/// Represents a physical frame mapped in a page table.
79#[derive(Debug)]
80pub enum MappedFrame {
81    /// The virtual address is mapped to a 4KiB frame.
82    Size4KiB(PhysFrame<Size4KiB>),
83    /// The virtual address is mapped to a "large" 2MiB frame.
84    Size2MiB(PhysFrame<Size2MiB>),
85    /// The virtual address is mapped to a "huge" 1GiB frame.
86    Size1GiB(PhysFrame<Size1GiB>),
87}
88
89impl MappedFrame {
90    /// Returns the start address of the frame.
91    pub const fn start_address(&self) -> PhysAddr {
92        match self {
93            MappedFrame::Size4KiB(frame) => frame.start_address,
94            MappedFrame::Size2MiB(frame) => frame.start_address,
95            MappedFrame::Size1GiB(frame) => frame.start_address,
96        }
97    }
98
99    /// Returns the size the frame (4KB, 2MB or 1GB).
100    pub const fn size(&self) -> u64 {
101        match self {
102            MappedFrame::Size4KiB(_) => Size4KiB::SIZE,
103            MappedFrame::Size2MiB(_) => Size2MiB::SIZE,
104            MappedFrame::Size1GiB(_) => Size1GiB::SIZE,
105        }
106    }
107}
108
109/// A trait for common page table operations on pages of size `S`.
110pub trait Mapper<S: PageSize> {
111    /// Creates a new mapping in the page table.
112    ///
113    /// This function might need additional physical frames to create new page tables. These
114    /// frames are allocated from the `allocator` argument. At most three frames are required.
115    ///
116    /// Parent page table entries are automatically updated with `PRESENT | WRITABLE | USER_ACCESSIBLE`
117    /// if present in the `PageTableFlags`. Depending on the used mapper implementation
118    /// the `PRESENT` and `WRITABLE` flags might be set for parent tables,
119    /// even if they are not set in `PageTableFlags`.
120    ///
121    /// The `map_to_with_table_flags` method gives explicit control over the parent page table flags.
122    ///
123    /// ## Safety
124    ///
125    /// Creating page table mappings is a fundamentally unsafe operation because
126    /// there are various ways to break memory safety through it. For example,
127    /// re-mapping an in-use page to a different frame changes and invalidates
128    /// all values stored in that page, resulting in undefined behavior on the
129    /// next use.
130    ///
131    /// The caller must ensure that no undefined behavior or memory safety
132    /// violations can occur through the new mapping. Among other things, the
133    /// caller must prevent the following:
134    ///
135    /// - Aliasing of `&mut` references, i.e. two `&mut` references that point to
136    ///   the same physical address. This is undefined behavior in Rust.
137    ///     - This can be ensured by mapping each page to an individual physical
138    ///       frame that is not mapped anywhere else.
139    /// - Creating uninitialized or invalid values: Rust requires that all values
140    ///   have a correct memory layout. For example, a `bool` must be either a 0
141    ///   or a 1 in memory, but not a 3 or 4. An exception is the `MaybeUninit`
142    ///   wrapper type, which abstracts over possibly uninitialized memory.
143    ///     - This is only a problem when re-mapping pages to different physical
144    ///       frames. Mapping a page that is not in use yet is fine.
145    ///
146    /// Special care must be taken when sharing pages with other address spaces,
147    /// e.g. by setting the `GLOBAL` flag. For example, a global mapping must be
148    /// the same in all address spaces, otherwise undefined behavior can occur
149    /// because of TLB races. It's worth noting that all the above requirements
150    /// also apply to shared mappings, including the aliasing requirements.
151    ///
152    /// # Examples
153    ///
154    /// Create a USER_ACCESSIBLE mapping:
155    ///
156    /// ```
157    /// # #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
158    /// # use x86_64::structures::paging::{
159    /// #    Mapper, Page, PhysFrame, FrameAllocator,
160    /// #    Size4KiB, OffsetPageTable, page_table::PageTableFlags
161    /// # };
162    /// # #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
163    /// # unsafe fn test(mapper: &mut OffsetPageTable, frame_allocator: &mut impl FrameAllocator<Size4KiB>,
164    /// #         page: Page<Size4KiB>, frame: PhysFrame) {
165    ///         mapper
166    ///           .map_to(
167    ///               page,
168    ///               frame,
169    ///              PageTableFlags::PRESENT
170    ///                   | PageTableFlags::WRITABLE
171    ///                   | PageTableFlags::USER_ACCESSIBLE,
172    ///               frame_allocator,
173    ///           )
174    ///           .unwrap()
175    ///           .flush();
176    /// # }
177    /// ```
178    #[inline]
179    unsafe fn map_to<A>(
180        &mut self,
181        page: Page<S>,
182        frame: PhysFrame<S>,
183        flags: PageTableFlags,
184        frame_allocator: &mut A,
185    ) -> Result<MapperFlush<S>, MapToError<S>>
186    where
187        Self: Sized,
188        A: FrameAllocator<Size4KiB> + ?Sized,
189    {
190        let parent_table_flags = flags
191            & (PageTableFlags::PRESENT
192                | PageTableFlags::WRITABLE
193                | PageTableFlags::USER_ACCESSIBLE);
194
195        unsafe {
196            self.map_to_with_table_flags(page, frame, flags, parent_table_flags, frame_allocator)
197        }
198    }
199
200    /// Creates a new mapping in the page table.
201    ///
202    /// This function might need additional physical frames to create new page tables. These
203    /// frames are allocated from the `allocator` argument. At most three frames are required.
204    ///
205    /// The flags of the parent table(s) can be explicitly specified. Those flags are used for
206    /// newly created table entries, and for existing entries the flags are added.
207    ///
208    /// Depending on the used mapper implementation, the `PRESENT` and `WRITABLE` flags might
209    /// be set for parent tables, even if they are not specified in `parent_table_flags`.
210    ///
211    /// ## Safety
212    ///
213    /// Creating page table mappings is a fundamentally unsafe operation because
214    /// there are various ways to break memory safety through it. For example,
215    /// re-mapping an in-use page to a different frame changes and invalidates
216    /// all values stored in that page, resulting in undefined behavior on the
217    /// next use.
218    ///
219    /// The caller must ensure that no undefined behavior or memory safety
220    /// violations can occur through the new mapping. Among other things, the
221    /// caller must prevent the following:
222    ///
223    /// - Aliasing of `&mut` references, i.e. two `&mut` references that point to
224    ///   the same physical address. This is undefined behavior in Rust.
225    ///     - This can be ensured by mapping each page to an individual physical
226    ///       frame that is not mapped anywhere else.
227    /// - Creating uninitialized or invalid values: Rust requires that all values
228    ///   have a correct memory layout. For example, a `bool` must be either a 0
229    ///   or a 1 in memory, but not a 3 or 4. An exception is the `MaybeUninit`
230    ///   wrapper type, which abstracts over possibly uninitialized memory.
231    ///     - This is only a problem when re-mapping pages to different physical
232    ///       frames. Mapping a page that is not in use yet is fine.
233    ///
234    /// Special care must be taken when sharing pages with other address spaces,
235    /// e.g. by setting the `GLOBAL` flag. For example, a global mapping must be
236    /// the same in all address spaces, otherwise undefined behavior can occur
237    /// because of TLB races. It's worth noting that all the above requirements
238    /// also apply to shared mappings, including the aliasing requirements.
239    ///
240    /// # Examples
241    ///
242    /// Create USER_ACCESSIBLE | NO_EXECUTE | NO_CACHE mapping and update
243    /// the top hierarchy only with USER_ACCESSIBLE:
244    ///
245    /// ```
246    /// # #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
247    /// # use x86_64::structures::paging::{
248    /// #    Mapper, PhysFrame, Page, FrameAllocator,
249    /// #    Size4KiB, OffsetPageTable, page_table::PageTableFlags
250    /// # };
251    /// # #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
252    /// # unsafe fn test(mapper: &mut OffsetPageTable, frame_allocator: &mut impl FrameAllocator<Size4KiB>,
253    /// #         page: Page<Size4KiB>, frame: PhysFrame) {
254    ///         mapper
255    ///           .map_to_with_table_flags(
256    ///               page,
257    ///               frame,
258    ///              PageTableFlags::PRESENT
259    ///                   | PageTableFlags::WRITABLE
260    ///                   | PageTableFlags::USER_ACCESSIBLE
261    ///                   | PageTableFlags::NO_EXECUTE
262    ///                   | PageTableFlags::NO_CACHE,
263    ///              PageTableFlags::USER_ACCESSIBLE,
264    ///               frame_allocator,
265    ///           )
266    ///           .unwrap()
267    ///           .flush();
268    /// # }
269    /// ```
270    unsafe fn map_to_with_table_flags<A>(
271        &mut self,
272        page: Page<S>,
273        frame: PhysFrame<S>,
274        flags: PageTableFlags,
275        parent_table_flags: PageTableFlags,
276        frame_allocator: &mut A,
277    ) -> Result<MapperFlush<S>, MapToError<S>>
278    where
279        Self: Sized,
280        A: FrameAllocator<Size4KiB> + ?Sized;
281
282    /// Removes a mapping from the page table and returns the frame that used to be mapped.
283    ///
284    /// Note that no page tables or pages are deallocated.
285    fn unmap(&mut self, page: Page<S>) -> Result<(PhysFrame<S>, MapperFlush<S>), UnmapError>;
286
287    /// Updates the flags of an existing mapping.
288    ///
289    /// To read the current flags of a mapped page, use the [`Translate::translate`] method.
290    ///
291    /// ## Safety
292    ///
293    /// This method is unsafe because changing the flags of a mapping
294    /// might result in undefined behavior. For example, setting the
295    /// `GLOBAL` and `WRITABLE` flags for a page might result in the corruption
296    /// of values stored in that page from processes running in other address
297    /// spaces.
298    unsafe fn update_flags(
299        &mut self,
300        page: Page<S>,
301        flags: PageTableFlags,
302    ) -> Result<MapperFlush<S>, FlagUpdateError>;
303
304    /// Set the flags of an existing page level 4 table entry
305    ///
306    /// ## Safety
307    ///
308    /// This method is unsafe because changing the flags of a mapping
309    /// might result in undefined behavior. For example, setting the
310    /// `GLOBAL` and `WRITABLE` flags for a page might result in the corruption
311    /// of values stored in that page from processes running in other address
312    /// spaces.
313    unsafe fn set_flags_p4_entry(
314        &mut self,
315        page: Page<S>,
316        flags: PageTableFlags,
317    ) -> Result<MapperFlushAll, FlagUpdateError>;
318
319    /// Set the flags of an existing page table level 3 entry
320    ///
321    /// ## Safety
322    ///
323    /// This method is unsafe because changing the flags of a mapping
324    /// might result in undefined behavior. For example, setting the
325    /// `GLOBAL` and `WRITABLE` flags for a page might result in the corruption
326    /// of values stored in that page from processes running in other address
327    /// spaces.
328    unsafe fn set_flags_p3_entry(
329        &mut self,
330        page: Page<S>,
331        flags: PageTableFlags,
332    ) -> Result<MapperFlushAll, FlagUpdateError>;
333
334    /// Set the flags of an existing page table level 2 entry
335    ///
336    /// ## Safety
337    ///
338    /// This method is unsafe because changing the flags of a mapping
339    /// might result in undefined behavior. For example, setting the
340    /// `GLOBAL` and `WRITABLE` flags for a page might result in the corruption
341    /// of values stored in that page from processes running in other address
342    /// spaces.
343    unsafe fn set_flags_p2_entry(
344        &mut self,
345        page: Page<S>,
346        flags: PageTableFlags,
347    ) -> Result<MapperFlushAll, FlagUpdateError>;
348
349    /// Return the frame that the specified page is mapped to.
350    ///
351    /// This function assumes that the page is mapped to a frame of size `S` and returns an
352    /// error otherwise.
353    fn translate_page(&self, page: Page<S>) -> Result<PhysFrame<S>, TranslateError>;
354
355    /// Maps the given frame to the virtual page with the same address.
356    ///
357    /// ## Safety
358    ///
359    /// This is a convencience function that invokes [`Mapper::map_to`] internally, so
360    /// all safety requirements of it also apply for this function.
361    #[inline]
362    unsafe fn identity_map<A>(
363        &mut self,
364        frame: PhysFrame<S>,
365        flags: PageTableFlags,
366        frame_allocator: &mut A,
367    ) -> Result<MapperFlush<S>, MapToError<S>>
368    where
369        Self: Sized,
370        A: FrameAllocator<Size4KiB> + ?Sized,
371        S: PageSize,
372        Self: Mapper<S>,
373    {
374        let page = Page::containing_address(VirtAddr::new(frame.start_address().as_u64()));
375        unsafe { self.map_to(page, frame, flags, frame_allocator) }
376    }
377}
378
379/// This type represents a page whose mapping has changed in the page table.
380///
381/// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs
382/// to be flushed from the TLB before it's accessed. This type is returned from a function that
383/// changed the mapping of a page to ensure that the TLB flush is not forgotten.
384#[derive(Debug)]
385#[must_use = "Page Table changes must be flushed or ignored."]
386#[cfg_attr(
387    not(all(feature = "instructions", target_arch = "x86_64")),
388    allow(dead_code)
389)] // FIXME
390pub struct MapperFlush<S: PageSize>(Page<S>);
391
392impl<S: PageSize> MapperFlush<S> {
393    /// Create a new flush promise
394    ///
395    /// Note that this method is intended for implementing the [`Mapper`] trait and no other uses
396    /// are expected.
397    #[inline]
398    pub fn new(page: Page<S>) -> Self {
399        MapperFlush(page)
400    }
401
402    /// Flush the page from the TLB to ensure that the newest mapping is used.
403    #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
404    #[inline]
405    pub fn flush(self) {
406        crate::instructions::tlb::flush(self.0.start_address());
407    }
408
409    /// Don't flush the TLB and silence the “must be used” warning.
410    #[inline]
411    pub fn ignore(self) {}
412
413    /// Returns the page to be flushed.
414    #[inline]
415    pub fn page(&self) -> Page<S> {
416        self.0
417    }
418}
419
420/// This type represents a change of a page table requiring a complete TLB flush
421///
422/// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs
423/// to be flushed from the TLB before it's accessed. This type is returned from a function that
424/// made the change to ensure that the TLB flush is not forgotten.
425#[derive(Debug, Default)]
426#[must_use = "Page Table changes must be flushed or ignored."]
427pub struct MapperFlushAll(());
428
429impl MapperFlushAll {
430    /// Create a new flush promise
431    ///
432    /// Note that this method is intended for implementing the [`Mapper`] trait and no other uses
433    /// are expected.
434    #[inline]
435    pub fn new() -> Self {
436        MapperFlushAll(())
437    }
438
439    /// Flush all pages from the TLB to ensure that the newest mapping is used.
440    #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
441    #[inline]
442    pub fn flush_all(self) {
443        crate::instructions::tlb::flush_all()
444    }
445
446    /// Don't flush the TLB and silence the “must be used” warning.
447    #[inline]
448    pub fn ignore(self) {}
449}
450
451/// This error is returned from `map_to` and similar methods.
452#[derive(Debug)]
453pub enum MapToError<S: PageSize> {
454    /// An additional frame was needed for the mapping process, but the frame allocator
455    /// returned `None`.
456    FrameAllocationFailed,
457    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
458    /// given page is part of an already mapped huge page.
459    ParentEntryHugePage,
460    /// The given page is already mapped to a physical frame.
461    PageAlreadyMapped(PhysFrame<S>),
462}
463
464/// An error indicating that an `unmap` call failed.
465#[derive(Debug)]
466pub enum UnmapError {
467    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
468    /// given page is part of a huge page and can't be freed individually.
469    ParentEntryHugePage,
470    /// The given page is not mapped to a physical frame.
471    PageNotMapped,
472    /// The page table entry for the given page points to an invalid physical address.
473    InvalidFrameAddress(PhysAddr),
474}
475
476/// An error indicating that an `update_flags` call failed.
477#[derive(Debug)]
478pub enum FlagUpdateError {
479    /// The given page is not mapped to a physical frame.
480    PageNotMapped,
481    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
482    /// given page is part of a huge page and can't be freed individually.
483    ParentEntryHugePage,
484}
485
486/// An error indicating that an `translate` call failed.
487#[derive(Debug)]
488pub enum TranslateError {
489    /// The given page is not mapped to a physical frame.
490    PageNotMapped,
491    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
492    /// given page is part of a huge page and can't be freed individually.
493    ParentEntryHugePage,
494    /// The page table entry for the given page points to an invalid physical address.
495    InvalidFrameAddress(PhysAddr),
496}
497
498static _ASSERT_OBJECT_SAFE: Option<&(dyn Translate + Sync)> = None;
499
500/// Provides methods for cleaning up unused entries.
501pub trait CleanUp {
502    /// Remove all empty P1-P3 tables
503    ///
504    /// ## Safety
505    ///
506    /// The caller has to guarantee that it's safe to free page table frames:
507    /// All page table frames must only be used once and only in this page table
508    /// (e.g. no reference counted page tables or reusing the same page tables for different virtual addresses ranges in the same page table).
509    unsafe fn clean_up<D>(&mut self, frame_deallocator: &mut D)
510    where
511        D: FrameDeallocator<Size4KiB>;
512
513    /// Remove all empty P1-P3 tables in a certain range
514    /// ```
515    /// # use core::ops::RangeInclusive;
516    /// # use x86_64::{VirtAddr, structures::paging::{
517    /// #    FrameDeallocator, Size4KiB, mapper::CleanUp, page::Page,
518    /// # }};
519    /// # unsafe fn test(page_table: &mut impl CleanUp, frame_deallocator: &mut impl FrameDeallocator<Size4KiB>) {
520    /// // clean up all page tables in the lower half of the address space
521    /// let lower_half = Page::range_inclusive(
522    ///     Page::containing_address(VirtAddr::new(0)),
523    ///     Page::containing_address(VirtAddr::new(0x0000_7fff_ffff_ffff)),
524    /// );
525    /// page_table.clean_up_addr_range(lower_half, frame_deallocator);
526    /// # }
527    /// ```
528    ///
529    /// ## Safety
530    ///
531    /// The caller has to guarantee that it's safe to free page table frames:
532    /// All page table frames must only be used once and only in this page table
533    /// (e.g. no reference counted page tables or reusing the same page tables for different virtual addresses ranges in the same page table).
534    unsafe fn clean_up_addr_range<D>(
535        &mut self,
536        range: PageRangeInclusive,
537        frame_deallocator: &mut D,
538    ) where
539        D: FrameDeallocator<Size4KiB>;
540}