Use the HUGE/PAT bit to track the validity of x86 PTEs

This commit is contained in:
Zhang Junyang
2024-10-12 21:53:03 +08:00
committed by Tate, Hongliang Tian
parent b269118c9a
commit dc7e0d7a63
4 changed files with 30 additions and 15 deletions

View File

@ -161,7 +161,9 @@ impl PageTableCreator {
}
let pt = unsafe { &mut *(pde.paddr() as *mut Ia32eTable) };
let pte = pt.index(1, from.addr());
pte.update(to.addr(), flags);
// In level-1 PTE, the HUGE bit is the PAT bit (page attribute table).
// We use it as the "valid" bit for the page table entry.
pte.update(to.addr(), flags | Ia32eFlags::HUGE);
}
pub fn nr_frames_used(&self) -> usize {

View File

@ -53,7 +53,10 @@ bitflags::bitflags! {
const ACCESSED = 1 << 5;
/// Whether the memory area represented by this entry is modified.
const DIRTY = 1 << 6;
/// Only in the non-starting and non-ending levels, indication of huge page.
/// In level 2 or 3 it indicates that it map to a huge page.
/// In level 1, it is the PAT (page attribute table) bit.
/// We use this bit in level 1, 2 and 3 to indicate that this entry is
/// "valid". For levels above 3, `PRESENT` is used for "valid".
const HUGE = 1 << 7;
/// Indicates that the mapping is present in all address spaces, so it isn't flushed from
/// the TLB on an address space switch.
@ -156,14 +159,15 @@ macro_rules! parse_flags {
impl PageTableEntryTrait for PageTableEntry {
fn is_present(&self) -> bool {
self.0 & PageTableFlags::PRESENT.bits() != 0
// For PT child, `PRESENT` should be set; for huge page, `HUGE` should
// be set; for the leaf child page, `PAT`, which is the same bit as
// the `HUGE` bit in upper levels, should be set.
self.0 & PageTableFlags::PRESENT.bits() != 0 || self.0 & PageTableFlags::HUGE.bits() != 0
}
fn new_page(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> Self {
let mut pte = Self(
paddr & Self::PHYS_ADDR_MASK
| ((level != 1) as usize) << PageTableFlags::HUGE.bits().ilog2(),
);
fn new_page(paddr: Paddr, _level: PagingLevel, prop: PageProperty) -> Self {
let flags = PageTableFlags::HUGE.bits();
let mut pte = Self(paddr & Self::PHYS_ADDR_MASK | flags);
pte.set_prop(prop);
pte
}
@ -209,8 +213,12 @@ impl PageTableEntryTrait for PageTableEntry {
}
fn set_prop(&mut self, prop: PageProperty) {
let mut flags = PageTableFlags::PRESENT.bits();
flags |= parse_flags!(prop.flags.bits(), PageFlags::W, PageTableFlags::WRITABLE)
if !self.is_present() {
return;
}
let mut flags = PageTableFlags::empty().bits();
flags |= parse_flags!(prop.flags.bits(), PageFlags::R, PageTableFlags::PRESENT)
| parse_flags!(prop.flags.bits(), PageFlags::W, PageTableFlags::WRITABLE)
| parse_flags!(!prop.flags.bits(), PageFlags::X, PageTableFlags::NO_EXECUTE)
| parse_flags!(
prop.flags.bits(),
@ -249,8 +257,8 @@ impl PageTableEntryTrait for PageTableEntry {
self.0 = self.0 & !Self::PROP_MASK | flags;
}
fn is_last(&self, level: PagingLevel) -> bool {
level == 1 || (self.0 & PageTableFlags::HUGE.bits() != 0)
fn is_last(&self, _level: PagingLevel) -> bool {
self.0 & PageTableFlags::HUGE.bits() != 0
}
}

View File

@ -327,6 +327,10 @@ pub trait PageTableEntryTrait: Clone + Copy + Debug + Default + Pod + Sized + Sy
}
/// If the flags are present with valid mappings.
///
/// For PTEs created by [`Self::new_absent`], this method should return
/// false. And for PTEs created by [`Self::new_page`] or [`Self::new_pt`]
/// and modified with [`Self::set_prop`] this method should return true.
fn is_present(&self) -> bool;
/// Create a new PTE with the given physical address and flags that map to a page.
@ -343,6 +347,10 @@ pub trait PageTableEntryTrait: Clone + Copy + Debug + Default + Pod + Sized + Sy
fn prop(&self) -> PageProperty;
/// Set the page property of the PTE.
///
/// This will be only done if the PTE is present. If not, this method will
/// do nothing.
fn set_prop(&mut self, prop: PageProperty);
/// If the PTE maps a page rather than a child page table.

View File

@ -57,9 +57,6 @@ where
/// Operates on the mapping properties of the entry.
///
/// It only modifies the properties if the entry is present.
// FIXME: in x86_64, you can protect a page with neither of the RWX
// permissions. This would make the page not accessible and leaked. Such a
// behavior is memory-safe but wrong. In RISC-V there's no problem.
pub(in crate::mm) fn protect(&mut self, op: &mut impl FnMut(&mut PageProperty)) {
if !self.pte.is_present() {
return;