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
414/// This type represents a change of a page table requiring a complete TLB flush
415///
416/// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs
417/// to be flushed from the TLB before it's accessed. This type is returned from a function that
418/// made the change to ensure that the TLB flush is not forgotten.
419#[derive(Debug, Default)]
420#[must_use = "Page Table changes must be flushed or ignored."]
421pub struct MapperFlushAll(());
422
423impl MapperFlushAll {
424    /// Create a new flush promise
425    ///
426    /// Note that this method is intended for implementing the [`Mapper`] trait and no other uses
427    /// are expected.
428    #[inline]
429    pub fn new() -> Self {
430        MapperFlushAll(())
431    }
432
433    /// Flush all pages from the TLB to ensure that the newest mapping is used.
434    #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
435    #[inline]
436    pub fn flush_all(self) {
437        crate::instructions::tlb::flush_all()
438    }
439
440    /// Don't flush the TLB and silence the “must be used” warning.
441    #[inline]
442    pub fn ignore(self) {}
443}
444
445/// This error is returned from `map_to` and similar methods.
446#[derive(Debug)]
447pub enum MapToError<S: PageSize> {
448    /// An additional frame was needed for the mapping process, but the frame allocator
449    /// returned `None`.
450    FrameAllocationFailed,
451    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
452    /// given page is part of an already mapped huge page.
453    ParentEntryHugePage,
454    /// The given page is already mapped to a physical frame.
455    PageAlreadyMapped(PhysFrame<S>),
456}
457
458/// An error indicating that an `unmap` call failed.
459#[derive(Debug)]
460pub enum UnmapError {
461    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
462    /// given page is part of a huge page and can't be freed individually.
463    ParentEntryHugePage,
464    /// The given page is not mapped to a physical frame.
465    PageNotMapped,
466    /// The page table entry for the given page points to an invalid physical address.
467    InvalidFrameAddress(PhysAddr),
468}
469
470/// An error indicating that an `update_flags` call failed.
471#[derive(Debug)]
472pub enum FlagUpdateError {
473    /// The given page is not mapped to a physical frame.
474    PageNotMapped,
475    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
476    /// given page is part of a huge page and can't be freed individually.
477    ParentEntryHugePage,
478}
479
480/// An error indicating that an `translate` call failed.
481#[derive(Debug)]
482pub enum TranslateError {
483    /// The given page is not mapped to a physical frame.
484    PageNotMapped,
485    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
486    /// given page is part of a huge page and can't be freed individually.
487    ParentEntryHugePage,
488    /// The page table entry for the given page points to an invalid physical address.
489    InvalidFrameAddress(PhysAddr),
490}
491
492static _ASSERT_OBJECT_SAFE: Option<&(dyn Translate + Sync)> = None;
493
494/// Provides methods for cleaning up unused entries.
495pub trait CleanUp {
496    /// Remove all empty P1-P3 tables
497    ///
498    /// ## Safety
499    ///
500    /// The caller has to guarantee that it's safe to free page table frames:
501    /// All page table frames must only be used once and only in this page table
502    /// (e.g. no reference counted page tables or reusing the same page tables for different virtual addresses ranges in the same page table).
503    unsafe fn clean_up<D>(&mut self, frame_deallocator: &mut D)
504    where
505        D: FrameDeallocator<Size4KiB>;
506
507    /// Remove all empty P1-P3 tables in a certain range
508    /// ```
509    /// # use core::ops::RangeInclusive;
510    /// # use x86_64::{VirtAddr, structures::paging::{
511    /// #    FrameDeallocator, Size4KiB, mapper::CleanUp, page::Page,
512    /// # }};
513    /// # unsafe fn test(page_table: &mut impl CleanUp, frame_deallocator: &mut impl FrameDeallocator<Size4KiB>) {
514    /// // clean up all page tables in the lower half of the address space
515    /// let lower_half = Page::range_inclusive(
516    ///     Page::containing_address(VirtAddr::new(0)),
517    ///     Page::containing_address(VirtAddr::new(0x0000_7fff_ffff_ffff)),
518    /// );
519    /// page_table.clean_up_addr_range(lower_half, frame_deallocator);
520    /// # }
521    /// ```
522    ///
523    /// ## Safety
524    ///
525    /// The caller has to guarantee that it's safe to free page table frames:
526    /// All page table frames must only be used once and only in this page table
527    /// (e.g. no reference counted page tables or reusing the same page tables for different virtual addresses ranges in the same page table).
528    unsafe fn clean_up_addr_range<D>(
529        &mut self,
530        range: PageRangeInclusive,
531        frame_deallocator: &mut D,
532    ) where
533        D: FrameDeallocator<Size4KiB>;
534}