Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Mapper::clear to clear any page table entry regardless of present flag #484

Merged
merged 3 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Unreleased

## Breaking changes

- [add `Mapper::clear` to clear any page table entry regardless of the present flag](https://github.com/rust-osdev/x86_64/pull/484)
- [`Mapper::unmap` now also returns the flags of the page ](https://github.com/rust-osdev/x86_64/pull/484)

# 0.15.1 – 2024-03-19

## New Features
Expand Down
111 changes: 105 additions & 6 deletions src/structures/paging/mapper/mapped_page_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl<'a, P: PageTableFrameMapping> Mapper<Size1GiB> for MappedPageTable<'a, P> {
fn unmap(
&mut self,
page: Page<Size1GiB>,
) -> Result<(PhysFrame<Size1GiB>, MapperFlush<Size1GiB>), UnmapError> {
) -> Result<(PhysFrame<Size1GiB>, PageTableFlags, MapperFlush<Size1GiB>), UnmapError> {
let p4 = &mut self.level_4_table;
let p3 = self
.page_table_walker
Expand All @@ -189,7 +189,39 @@ impl<'a, P: PageTableFrameMapping> Mapper<Size1GiB> for MappedPageTable<'a, P> {
.map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p3_entry.addr()))?;

p3_entry.set_unused();
Ok((frame, MapperFlush::new(page)))
Ok((frame, flags, MapperFlush::new(page)))
}

fn clear(&mut self, page: Page<Size1GiB>) -> Result<UnmappedFrame<Size1GiB>, UnmapError> {
let p4 = &mut self.level_4_table;
let p3 = self
.page_table_walker
.next_table_mut(&mut p4[page.p4_index()])?;

let p3_entry = &mut p3[page.p3_index()];
let flags = p3_entry.flags();

if !flags.contains(PageTableFlags::HUGE_PAGE) {
return Err(UnmapError::ParentEntryHugePage);
}

if !flags.contains(PageTableFlags::PRESENT) {
let cloned = p3_entry.clone();
p3_entry.set_unused();
return Ok(UnmappedFrame::NotPresent { entry: cloned });
}

let frame = PhysFrame::from_start_address(p3_entry.addr())
.map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p3_entry.addr()))?;
let flags = p3_entry.flags();

p3_entry.set_unused();

Ok(UnmappedFrame::Present {
frame,
flags,
flush: MapperFlush::new(page),
})
}

unsafe fn update_flags(
Expand Down Expand Up @@ -277,7 +309,7 @@ impl<'a, P: PageTableFrameMapping> Mapper<Size2MiB> for MappedPageTable<'a, P> {
fn unmap(
&mut self,
page: Page<Size2MiB>,
) -> Result<(PhysFrame<Size2MiB>, MapperFlush<Size2MiB>), UnmapError> {
) -> Result<(PhysFrame<Size2MiB>, PageTableFlags, MapperFlush<Size2MiB>), UnmapError> {
let p4 = &mut self.level_4_table;
let p3 = self
.page_table_walker
Expand All @@ -300,7 +332,40 @@ impl<'a, P: PageTableFrameMapping> Mapper<Size2MiB> for MappedPageTable<'a, P> {
.map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p2_entry.addr()))?;

p2_entry.set_unused();
Ok((frame, MapperFlush::new(page)))
Ok((frame, flags, MapperFlush::new(page)))
}

fn clear(&mut self, page: Page<Size2MiB>) -> Result<UnmappedFrame<Size2MiB>, UnmapError> {
let p4 = &mut self.level_4_table;
let p3 = self
.page_table_walker
.next_table_mut(&mut p4[page.p4_index()])?;
let p2 = self
.page_table_walker
.next_table_mut(&mut p3[page.p3_index()])?;

let p2_entry = &mut p2[page.p2_index()];
let flags = p2_entry.flags();

if !flags.contains(PageTableFlags::HUGE_PAGE) {
return Err(UnmapError::ParentEntryHugePage);
}

if !flags.contains(PageTableFlags::PRESENT) {
let cloned = p2_entry.clone();
p2_entry.set_unused();
return Ok(UnmappedFrame::NotPresent { entry: cloned });
}
let frame = PhysFrame::from_start_address(p2_entry.addr())
.map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p2_entry.addr()))?;
let flags = p2_entry.flags();

p2_entry.set_unused();
Ok(UnmappedFrame::Present {
frame,
flags,
flush: MapperFlush::new(page),
})
}

unsafe fn update_flags(
Expand Down Expand Up @@ -405,7 +470,7 @@ impl<'a, P: PageTableFrameMapping> Mapper<Size4KiB> for MappedPageTable<'a, P> {
fn unmap(
&mut self,
page: Page<Size4KiB>,
) -> Result<(PhysFrame<Size4KiB>, MapperFlush<Size4KiB>), UnmapError> {
) -> Result<(PhysFrame<Size4KiB>, PageTableFlags, MapperFlush<Size4KiB>), UnmapError> {
let p4 = &mut self.level_4_table;
let p3 = self
.page_table_walker
Expand All @@ -423,9 +488,43 @@ impl<'a, P: PageTableFrameMapping> Mapper<Size4KiB> for MappedPageTable<'a, P> {
FrameError::FrameNotPresent => UnmapError::PageNotMapped,
FrameError::HugeFrame => UnmapError::ParentEntryHugePage,
})?;
let flags = p1_entry.flags();

p1_entry.set_unused();
Ok((frame, flags, MapperFlush::new(page)))
}

fn clear(&mut self, page: Page<Size4KiB>) -> Result<UnmappedFrame<Size4KiB>, UnmapError> {
let p4 = &mut self.level_4_table;
let p3 = self
.page_table_walker
.next_table_mut(&mut p4[page.p4_index()])?;
let p2 = self
.page_table_walker
.next_table_mut(&mut p3[page.p3_index()])?;
let p1 = self
.page_table_walker
.next_table_mut(&mut p2[page.p2_index()])?;

let p1_entry = &mut p1[page.p1_index()];

let frame = match p1_entry.frame() {
Ok(frame) => frame,
Err(FrameError::HugeFrame) => return Err(UnmapError::ParentEntryHugePage),
Err(FrameError::FrameNotPresent) => {
let cloned = p1_entry.clone();
p1_entry.set_unused();
return Ok(UnmappedFrame::NotPresent { entry: cloned });
}
};
let flags = p1_entry.flags();

p1_entry.set_unused();
Ok((frame, MapperFlush::new(page)))
Ok(UnmappedFrame::Present {
frame,
flags,
flush: MapperFlush::new(page),
})
}

unsafe fn update_flags(
Expand Down
36 changes: 34 additions & 2 deletions src/structures/paging/mapper/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub use self::recursive_page_table::{InvalidPageTable, RecursivePageTable};
use crate::structures::paging::{
frame_alloc::{FrameAllocator, FrameDeallocator},
page::PageRangeInclusive,
page_table::PageTableFlags,
page_table::{PageTableEntry, PageTableFlags},
Page, PageSize, PhysFrame, Size1GiB, Size2MiB, Size4KiB,
};
use crate::{PhysAddr, VirtAddr};
Expand Down Expand Up @@ -282,7 +282,18 @@ pub trait Mapper<S: PageSize> {
/// Removes a mapping from the page table and returns the frame that used to be mapped.
///
/// Note that no page tables or pages are deallocated.
fn unmap(&mut self, page: Page<S>) -> Result<(PhysFrame<S>, MapperFlush<S>), UnmapError>;
fn unmap(
&mut self,
page: Page<S>,
) -> Result<(PhysFrame<S>, PageTableFlags, MapperFlush<S>), UnmapError>;

/// Clears a mapping from the page table and returns the frame that used to be mapped.
///
/// Unlike [`Mapper::unmap`] this will ignore the present flag of the page and will successfully
/// clear the table entry for any valid page.
///
/// Note that no page tables or pages are deallocated.
fn clear(&mut self, page: Page<S>) -> Result<UnmappedFrame<S>, UnmapError>;

/// Updates the flags of an existing mapping.
///
Expand Down Expand Up @@ -376,6 +387,27 @@ pub trait Mapper<S: PageSize> {
}
}

/// The result of [`Mapper::clear`], representing either
/// the unmapped frame or the entry data if the frame is not marked as present.
#[derive(Debug)]
#[must_use = "Page table changes must be flushed or ignored if the page is present."]
pub enum UnmappedFrame<S: PageSize> {
/// The frame was present before the [`Mapper::clear`] call
Present {
/// The physical frame that was unmapped
frame: PhysFrame<S>,
/// The flags of the frame that was unmapped
flags: PageTableFlags,
/// The changed page, to flush the TLB
flush: MapperFlush<S>,
},
/// The frame was not present before the [`Mapper::clear`] call
NotPresent {
/// The page table entry
entry: PageTableEntry,
},
}

/// This type represents a page whose mapping has changed in the page table.
///
/// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs
Expand Down
21 changes: 18 additions & 3 deletions src/structures/paging/mapper/offset_page_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,15 @@ impl<'a> Mapper<Size1GiB> for OffsetPageTable<'a> {
fn unmap(
&mut self,
page: Page<Size1GiB>,
) -> Result<(PhysFrame<Size1GiB>, MapperFlush<Size1GiB>), UnmapError> {
) -> Result<(PhysFrame<Size1GiB>, PageTableFlags, MapperFlush<Size1GiB>), UnmapError> {
self.inner.unmap(page)
}

#[inline]
fn clear(&mut self, page: Page<Size1GiB>) -> Result<UnmappedFrame<Size1GiB>, UnmapError> {
self.inner.clear(page)
}

#[inline]
unsafe fn update_flags(
&mut self,
Expand Down Expand Up @@ -157,10 +162,15 @@ impl<'a> Mapper<Size2MiB> for OffsetPageTable<'a> {
fn unmap(
&mut self,
page: Page<Size2MiB>,
) -> Result<(PhysFrame<Size2MiB>, MapperFlush<Size2MiB>), UnmapError> {
) -> Result<(PhysFrame<Size2MiB>, PageTableFlags, MapperFlush<Size2MiB>), UnmapError> {
self.inner.unmap(page)
}

#[inline]
fn clear(&mut self, page: Page<Size2MiB>) -> Result<UnmappedFrame<Size2MiB>, UnmapError> {
self.inner.clear(page)
}

#[inline]
unsafe fn update_flags(
&mut self,
Expand Down Expand Up @@ -226,10 +236,15 @@ impl<'a> Mapper<Size4KiB> for OffsetPageTable<'a> {
fn unmap(
&mut self,
page: Page<Size4KiB>,
) -> Result<(PhysFrame<Size4KiB>, MapperFlush<Size4KiB>), UnmapError> {
) -> Result<(PhysFrame<Size4KiB>, PageTableFlags, MapperFlush<Size4KiB>), UnmapError> {
self.inner.unmap(page)
}

#[inline]
fn clear(&mut self, page: Page<Size4KiB>) -> Result<UnmappedFrame<Size4KiB>, UnmapError> {
self.inner.clear(page)
}

#[inline]
unsafe fn update_flags(
&mut self,
Expand Down
Loading
Loading