Skip to content

Commit ca63fa9

Browse files
committed
add Mapper::unmap_ignore_present
Add a way to clear page table entries that don't have the present flag set. fixes: #481
1 parent f53cbec commit ca63fa9

File tree

5 files changed

+267
-1
lines changed

5 files changed

+267
-1
lines changed

Changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Unreleased
22

3+
## Breaking changes
4+
5+
- [add `Mapper::clear` to clear any page table entry regardless of the present flag](https://github.com/rust-osdev/x86_64/pull/484)
6+
37
# 0.15.1 – 2024-03-19
48

59
## New Features

src/structures/paging/mapper/mapped_page_table.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,38 @@ impl<'a, P: PageTableFrameMapping> Mapper<Size1GiB> for MappedPageTable<'a, P> {
192192
Ok((frame, MapperFlush::new(page)))
193193
}
194194

195+
fn clear(&mut self, page: Page<Size1GiB>) -> Result<UnmappedFrame<Size1GiB>, UnmapError> {
196+
let p4 = &mut self.level_4_table;
197+
let p3 = self
198+
.page_table_walker
199+
.next_table_mut(&mut p4[page.p4_index()])?;
200+
201+
let p3_entry = &mut p3[page.p3_index()];
202+
let flags = p3_entry.flags();
203+
204+
if !flags.contains(PageTableFlags::HUGE_PAGE) {
205+
return Err(UnmapError::ParentEntryHugePage);
206+
}
207+
208+
if !flags.contains(PageTableFlags::PRESENT) {
209+
let cloned = p3_entry.clone();
210+
p3_entry.set_unused();
211+
return Ok(UnmappedFrame::NotPresent { entry: cloned });
212+
}
213+
214+
let frame = PhysFrame::from_start_address(p3_entry.addr())
215+
.map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p3_entry.addr()))?;
216+
let flags = p3_entry.flags();
217+
218+
p3_entry.set_unused();
219+
220+
Ok(UnmappedFrame::Present {
221+
frame,
222+
flags,
223+
flush: MapperFlush::new(page),
224+
})
225+
}
226+
195227
unsafe fn update_flags(
196228
&mut self,
197229
page: Page<Size1GiB>,
@@ -303,6 +335,39 @@ impl<'a, P: PageTableFrameMapping> Mapper<Size2MiB> for MappedPageTable<'a, P> {
303335
Ok((frame, MapperFlush::new(page)))
304336
}
305337

338+
fn clear(&mut self, page: Page<Size2MiB>) -> Result<UnmappedFrame<Size2MiB>, UnmapError> {
339+
let p4 = &mut self.level_4_table;
340+
let p3 = self
341+
.page_table_walker
342+
.next_table_mut(&mut p4[page.p4_index()])?;
343+
let p2 = self
344+
.page_table_walker
345+
.next_table_mut(&mut p3[page.p3_index()])?;
346+
347+
let p2_entry = &mut p2[page.p2_index()];
348+
let flags = p2_entry.flags();
349+
350+
if !flags.contains(PageTableFlags::HUGE_PAGE) {
351+
return Err(UnmapError::ParentEntryHugePage);
352+
}
353+
354+
if !flags.contains(PageTableFlags::PRESENT) {
355+
let cloned = p2_entry.clone();
356+
p2_entry.set_unused();
357+
return Ok(UnmappedFrame::NotPresent { entry: cloned });
358+
}
359+
let frame = PhysFrame::from_start_address(p2_entry.addr())
360+
.map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p2_entry.addr()))?;
361+
let flags = p2_entry.flags();
362+
363+
p2_entry.set_unused();
364+
Ok(UnmappedFrame::Present {
365+
frame,
366+
flags,
367+
flush: MapperFlush::new(page),
368+
})
369+
}
370+
306371
unsafe fn update_flags(
307372
&mut self,
308373
page: Page<Size2MiB>,
@@ -428,6 +493,39 @@ impl<'a, P: PageTableFrameMapping> Mapper<Size4KiB> for MappedPageTable<'a, P> {
428493
Ok((frame, MapperFlush::new(page)))
429494
}
430495

496+
fn clear(&mut self, page: Page<Size4KiB>) -> Result<UnmappedFrame<Size4KiB>, UnmapError> {
497+
let p4 = &mut self.level_4_table;
498+
let p3 = self
499+
.page_table_walker
500+
.next_table_mut(&mut p4[page.p4_index()])?;
501+
let p2 = self
502+
.page_table_walker
503+
.next_table_mut(&mut p3[page.p3_index()])?;
504+
let p1 = self
505+
.page_table_walker
506+
.next_table_mut(&mut p2[page.p2_index()])?;
507+
508+
let p1_entry = &mut p1[page.p1_index()];
509+
510+
let frame = match p1_entry.frame() {
511+
Ok(frame) => frame,
512+
Err(FrameError::HugeFrame) => return Err(UnmapError::ParentEntryHugePage),
513+
Err(FrameError::FrameNotPresent) => {
514+
let cloned = p1_entry.clone();
515+
p1_entry.set_unused();
516+
return Ok(UnmappedFrame::NotPresent { entry: cloned });
517+
}
518+
};
519+
let flags = p1_entry.flags();
520+
521+
p1_entry.set_unused();
522+
Ok(UnmappedFrame::Present {
523+
frame,
524+
flags,
525+
flush: MapperFlush::new(page),
526+
})
527+
}
528+
431529
unsafe fn update_flags(
432530
&mut self,
433531
page: Page<Size4KiB>,

src/structures/paging/mapper/mod.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub use self::recursive_page_table::{InvalidPageTable, RecursivePageTable};
99
use crate::structures::paging::{
1010
frame_alloc::{FrameAllocator, FrameDeallocator},
1111
page::PageRangeInclusive,
12-
page_table::PageTableFlags,
12+
page_table::{PageTableEntry, PageTableFlags},
1313
Page, PageSize, PhysFrame, Size1GiB, Size2MiB, Size4KiB,
1414
};
1515
use crate::{PhysAddr, VirtAddr};
@@ -284,6 +284,14 @@ pub trait Mapper<S: PageSize> {
284284
/// Note that no page tables or pages are deallocated.
285285
fn unmap(&mut self, page: Page<S>) -> Result<(PhysFrame<S>, MapperFlush<S>), UnmapError>;
286286

287+
/// Clears a mapping from the page table and returns the frame that used to be mapped.
288+
///
289+
/// Unlike [`Mapper::unmap`] this will ignore the present flag of the page and will successfully
290+
/// clear the table entry for any valid page.
291+
///
292+
/// Note that no page tables or pages are deallocated.
293+
fn clear(&mut self, page: Page<S>) -> Result<UnmappedFrame<S>, UnmapError>;
294+
287295
/// Updates the flags of an existing mapping.
288296
///
289297
/// To read the current flags of a mapped page, use the [`Translate::translate`] method.
@@ -376,6 +384,27 @@ pub trait Mapper<S: PageSize> {
376384
}
377385
}
378386

387+
/// The result of [`Mapper::clear`], representing either
388+
/// the unmapped frame or the entry data if the frame is not marked as present.
389+
#[derive(Debug)]
390+
#[must_use = "Page table changes must be flushed or ignored if the page is present."]
391+
pub enum UnmappedFrame<S: PageSize> {
392+
/// The frame was present before the [`Mapper::clear`] call
393+
Present {
394+
/// The physical frame that was unmapped
395+
frame: PhysFrame<S>,
396+
/// The flags of the frame that was unmapped
397+
flags: PageTableFlags,
398+
/// The changed page, to flush the TLB
399+
flush: MapperFlush<S>,
400+
},
401+
/// The frame was not present before the [`Mapper::clear`] call
402+
NotPresent {
403+
/// The page table entry
404+
entry: PageTableEntry,
405+
},
406+
}
407+
379408
/// This type represents a page whose mapping has changed in the page table.
380409
///
381410
/// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs

src/structures/paging/mapper/offset_page_table.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ impl<'a> Mapper<Size1GiB> for OffsetPageTable<'a> {
9292
self.inner.unmap(page)
9393
}
9494

95+
#[inline]
96+
fn clear(&mut self, page: Page<Size1GiB>) -> Result<UnmappedFrame<Size1GiB>, UnmapError> {
97+
self.inner.clear(page)
98+
}
99+
95100
#[inline]
96101
unsafe fn update_flags(
97102
&mut self,
@@ -161,6 +166,11 @@ impl<'a> Mapper<Size2MiB> for OffsetPageTable<'a> {
161166
self.inner.unmap(page)
162167
}
163168

169+
#[inline]
170+
fn clear(&mut self, page: Page<Size2MiB>) -> Result<UnmappedFrame<Size2MiB>, UnmapError> {
171+
self.inner.clear(page)
172+
}
173+
164174
#[inline]
165175
unsafe fn update_flags(
166176
&mut self,
@@ -230,6 +240,11 @@ impl<'a> Mapper<Size4KiB> for OffsetPageTable<'a> {
230240
self.inner.unmap(page)
231241
}
232242

243+
#[inline]
244+
fn clear(&mut self, page: Page<Size4KiB>) -> Result<UnmappedFrame<Size4KiB>, UnmapError> {
245+
self.inner.clear(page)
246+
}
247+
233248
#[inline]
234249
unsafe fn update_flags(
235250
&mut self,

src/structures/paging/mapper/recursive_page_table.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,41 @@ impl<'a> Mapper<Size1GiB> for RecursivePageTable<'a> {
345345
Ok((frame, MapperFlush::new(page)))
346346
}
347347

348+
fn clear(&mut self, page: Page<Size1GiB>) -> Result<UnmappedFrame<Size1GiB>, UnmapError> {
349+
let p4 = &mut self.p4;
350+
let p4_entry = &p4[page.p4_index()];
351+
352+
p4_entry.frame().map_err(|err| match err {
353+
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
354+
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
355+
})?;
356+
357+
let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) };
358+
let p3_entry = &mut p3[page.p3_index()];
359+
let flags = p3_entry.flags();
360+
361+
if !flags.contains(PageTableFlags::HUGE_PAGE) {
362+
return Err(UnmapError::ParentEntryHugePage);
363+
}
364+
365+
if !flags.contains(PageTableFlags::PRESENT) {
366+
let cloned = p3_entry.clone();
367+
p3_entry.set_unused();
368+
return Ok(UnmappedFrame::NotPresent { entry: cloned });
369+
}
370+
371+
let frame = PhysFrame::from_start_address(p3_entry.addr())
372+
.map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p3_entry.addr()))?;
373+
let flags = p3_entry.flags();
374+
375+
p3_entry.set_unused();
376+
Ok(UnmappedFrame::Present {
377+
frame,
378+
flags,
379+
flush: MapperFlush::new(page),
380+
})
381+
}
382+
348383
unsafe fn update_flags(
349384
&mut self,
350385
page: Page<Size1GiB>,
@@ -471,6 +506,47 @@ impl<'a> Mapper<Size2MiB> for RecursivePageTable<'a> {
471506
Ok((frame, MapperFlush::new(page)))
472507
}
473508

509+
fn clear(&mut self, page: Page<Size2MiB>) -> Result<UnmappedFrame<Size2MiB>, UnmapError> {
510+
let p4 = &mut self.p4;
511+
let p4_entry = &p4[page.p4_index()];
512+
p4_entry.frame().map_err(|err| match err {
513+
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
514+
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
515+
})?;
516+
517+
let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) };
518+
let p3_entry = &p3[page.p3_index()];
519+
p3_entry.frame().map_err(|err| match err {
520+
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
521+
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
522+
})?;
523+
524+
let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) };
525+
let p2_entry = &mut p2[page.p2_index()];
526+
let flags = p2_entry.flags();
527+
528+
if !flags.contains(PageTableFlags::HUGE_PAGE) {
529+
return Err(UnmapError::ParentEntryHugePage);
530+
}
531+
532+
if !flags.contains(PageTableFlags::PRESENT) {
533+
let cloned = p2_entry.clone();
534+
p2_entry.set_unused();
535+
return Ok(UnmappedFrame::NotPresent { entry: cloned });
536+
}
537+
538+
let frame = PhysFrame::from_start_address(p2_entry.addr())
539+
.map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p2_entry.addr()))?;
540+
let flags = p2_entry.flags();
541+
542+
p2_entry.set_unused();
543+
Ok(UnmappedFrame::Present {
544+
frame,
545+
flags,
546+
flush: MapperFlush::new(page),
547+
})
548+
}
549+
474550
unsafe fn update_flags(
475551
&mut self,
476552
page: Page<Size2MiB>,
@@ -627,6 +703,50 @@ impl<'a> Mapper<Size4KiB> for RecursivePageTable<'a> {
627703
Ok((frame, MapperFlush::new(page)))
628704
}
629705

706+
fn clear(&mut self, page: Page<Size4KiB>) -> Result<UnmappedFrame<Size4KiB>, UnmapError> {
707+
let p4 = &mut self.p4;
708+
let p4_entry = &p4[page.p4_index()];
709+
p4_entry.frame().map_err(|err| match err {
710+
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
711+
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
712+
})?;
713+
714+
let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) };
715+
let p3_entry = &p3[page.p3_index()];
716+
p3_entry.frame().map_err(|err| match err {
717+
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
718+
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
719+
})?;
720+
721+
let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) };
722+
let p2_entry = &p2[page.p2_index()];
723+
p2_entry.frame().map_err(|err| match err {
724+
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
725+
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
726+
})?;
727+
728+
let p1 = unsafe { &mut *(p1_ptr(page, self.recursive_index)) };
729+
let p1_entry = &mut p1[page.p1_index()];
730+
731+
let frame = match p1_entry.frame() {
732+
Ok(frame) => frame,
733+
Err(FrameError::FrameNotPresent) => {
734+
let cloned = p1_entry.clone();
735+
p1_entry.set_unused();
736+
return Ok(UnmappedFrame::NotPresent { entry: cloned });
737+
}
738+
Err(FrameError::HugeFrame) => return Err(UnmapError::ParentEntryHugePage),
739+
};
740+
let flags = p1_entry.flags();
741+
742+
p1_entry.set_unused();
743+
Ok(UnmappedFrame::Present {
744+
frame,
745+
flags,
746+
flush: MapperFlush::new(page),
747+
})
748+
}
749+
630750
unsafe fn update_flags(
631751
&mut self,
632752
page: Page<Size4KiB>,

0 commit comments

Comments
 (0)