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}