Переглянути джерело

change(pte): new definitions for PTE attributes

add universal `PageAttribute` and `TableAttribute` to describe
common behavior of page table entries.

change the definition of trait `RawAttribute` to flatten the architecture
difference in some aspects (e.g. different defs of whether an entry is for
a page table or a page under x86 and riscv)
greatbridf 8 місяців тому
батько
коміт
c488664697

+ 3 - 0
Cargo.lock

@@ -106,6 +106,9 @@ dependencies = [
 [[package]]
 name = "eonix_mm"
 version = "0.1.0"
+dependencies = [
+ "bitflags",
+]
 
 [[package]]
 name = "eonix_preempt"

+ 85 - 84
arch/src/x86_64/mm.rs

@@ -1,7 +1,9 @@
 use core::{marker::PhantomData, ptr::NonNull};
 use eonix_mm::{
     address::{Addr as _, PAddr},
-    page_table::{PageAttribute, PageTableLevel, PagingMode, RawPageTable, PTE},
+    page_table::{
+        PageAttribute, PageTableLevel, PagingMode, RawAttribute, RawPageTable, TableAttribute, PTE,
+    },
     paging::{PageBlock, PFN},
 };
 
@@ -18,7 +20,6 @@ const PA_PWT: u64 = 0x008;
 const PA_PCD: u64 = 0x010;
 const PA_A: u64 = 0x020;
 const PA_D: u64 = 0x040;
-#[allow(dead_code)]
 const PA_PS: u64 = 0x080;
 const PA_G: u64 = 0x100;
 const PA_COW: u64 = 0x200;
@@ -84,117 +85,117 @@ impl<'a> RawPageTable<'a> for RawPageTable4Levels<'a> {
     }
 }
 
-impl PageAttribute for PageAttribute64 {
-    fn new() -> Self {
-        Self(PA_NXE)
+impl RawAttribute for PageAttribute64 {
+    fn null() -> Self {
+        Self(0)
     }
 
-    fn present(self, present: bool) -> Self {
-        if present {
-            Self(self.0 | PA_P)
-        } else {
-            Self(self.0 & !PA_P)
-        }
-    }
+    fn as_table_attr(self) -> Option<TableAttribute> {
+        let mut table_attr = TableAttribute::empty();
 
-    fn write(self, write: bool) -> Self {
-        if write {
-            Self(self.0 | PA_RW)
-        } else {
-            Self(self.0 & !PA_RW)
+        if self.0 & PA_PS != 0 {
+            panic!("Encountered a huge page while parsing table attributes");
         }
-    }
 
-    fn execute(self, execute: bool) -> Self {
-        if execute {
-            Self(self.0 & !PA_NXE)
-        } else {
-            Self(self.0 | PA_NXE)
+        if self.0 & PA_P != 0 {
+            table_attr |= TableAttribute::PRESENT;
+        }
+        if self.0 & PA_G != 0 {
+            table_attr |= TableAttribute::GLOBAL;
+        }
+        if self.0 & PA_US != 0 {
+            table_attr |= TableAttribute::USER;
+        }
+        if self.0 & PA_A != 0 {
+            table_attr |= TableAttribute::ACCESSED;
         }
+
+        Some(table_attr)
     }
 
-    fn user(self, user: bool) -> Self {
-        if user {
-            Self(self.0 | PA_US)
-        } else {
-            Self(self.0 & !PA_US)
+    fn as_page_attr(self) -> Option<PageAttribute> {
+        let mut page_attr = PageAttribute::READ;
+
+        if self.0 & PA_P != 0 {
+            page_attr |= PageAttribute::PRESENT;
         }
-    }
 
-    fn accessed(self, accessed: bool) -> Self {
-        if accessed {
-            Self(self.0 | PA_A)
-        } else {
-            Self(self.0 & !PA_A)
+        if self.0 & PA_RW != 0 {
+            page_attr |= PageAttribute::WRITE;
         }
-    }
 
-    fn dirty(self, dirty: bool) -> Self {
-        if dirty {
-            Self(self.0 | PA_D)
-        } else {
-            Self(self.0 & !PA_D)
+        if self.0 & PA_NXE == 0 {
+            page_attr |= PageAttribute::EXECUTE;
         }
-    }
 
-    fn global(self, global: bool) -> Self {
-        if global {
-            Self(self.0 | PA_G)
-        } else {
-            Self(self.0 & !PA_G)
+        if self.0 & PA_US != 0 {
+            page_attr |= PageAttribute::USER;
         }
-    }
 
-    fn copy_on_write(self, cow: bool) -> Self {
-        if cow {
-            Self(self.0 | PA_COW)
-        } else {
-            Self(self.0 & !PA_COW)
+        if self.0 & PA_A != 0 {
+            page_attr |= PageAttribute::ACCESSED;
         }
-    }
 
-    fn mapped(self, mmap: bool) -> Self {
-        if mmap {
-            Self(self.0 | PA_MMAP)
-        } else {
-            Self(self.0 & !PA_MMAP)
+        if self.0 & PA_D != 0 {
+            page_attr |= PageAttribute::DIRTY;
         }
-    }
 
-    fn is_present(&self) -> bool {
-        self.0 & PA_P != 0
-    }
+        if self.0 & PA_G != 0 {
+            page_attr |= PageAttribute::GLOBAL;
+        }
 
-    fn is_write(&self) -> bool {
-        self.0 & PA_RW != 0
-    }
+        if self.0 & PA_COW != 0 {
+            page_attr |= PageAttribute::COPY_ON_WRITE;
+        }
 
-    fn is_execute(&self) -> bool {
-        self.0 & PA_NXE == 0
-    }
+        if self.0 & PA_MMAP != 0 {
+            page_attr |= PageAttribute::MAPPED;
+        }
 
-    fn is_user(&self) -> bool {
-        self.0 & PA_US != 0
-    }
+        if self.0 & PA_ANON != 0 {
+            page_attr |= PageAttribute::ANONYMOUS;
+        }
 
-    fn is_accessed(&self) -> bool {
-        self.0 & PA_A != 0
+        Some(page_attr)
     }
 
-    fn is_dirty(&self) -> bool {
-        self.0 & PA_D != 0
-    }
+    fn from_table_attr(table_attr: TableAttribute) -> Self {
+        let mut raw_attr = PA_RW;
 
-    fn is_global(&self) -> bool {
-        self.0 & PA_G != 0
-    }
+        for attr in table_attr.iter() {
+            match attr {
+                TableAttribute::PRESENT => raw_attr |= PA_P,
+                TableAttribute::GLOBAL => raw_attr |= PA_G,
+                TableAttribute::USER => raw_attr |= PA_US,
+                TableAttribute::ACCESSED => raw_attr |= PA_A,
+                _ => unreachable!("Invalid table attribute"),
+            }
+        }
 
-    fn is_copy_on_write(&self) -> bool {
-        self.0 & PA_COW != 0
-    }
+        Self(raw_attr)
+    }
+
+    fn from_page_attr(page_attr: PageAttribute) -> Self {
+        let mut raw_attr = PA_NXE;
+
+        for attr in page_attr.iter() {
+            match attr {
+                PageAttribute::PRESENT => raw_attr |= PA_P,
+                PageAttribute::READ => {}
+                PageAttribute::WRITE => raw_attr |= PA_RW,
+                PageAttribute::EXECUTE => raw_attr &= !PA_NXE,
+                PageAttribute::USER => raw_attr |= PA_US,
+                PageAttribute::ACCESSED => raw_attr |= PA_A,
+                PageAttribute::DIRTY => raw_attr |= PA_D,
+                PageAttribute::GLOBAL => raw_attr |= PA_G,
+                PageAttribute::COPY_ON_WRITE => raw_attr |= PA_COW,
+                PageAttribute::MAPPED => raw_attr |= PA_MMAP,
+                PageAttribute::ANONYMOUS => raw_attr |= PA_ANON,
+                _ => unreachable!("Invalid page attribute"),
+            }
+        }
 
-    fn is_mapped(&self) -> bool {
-        self.0 & PA_MMAP != 0
+        Self(raw_attr)
     }
 }
 

+ 1 - 0
crates/eonix_mm/Cargo.toml

@@ -4,3 +4,4 @@ version = "0.1.0"
 edition = "2024"
 
 [dependencies]
+bitflags = "2.6.0"

+ 1 - 1
crates/eonix_mm/src/page_table.rs

@@ -5,5 +5,5 @@ mod pte_iterator;
 
 pub use page_table::{PageTable, RawPageTable};
 pub use paging_mode::{PageTableLevel, PagingMode};
-pub use pte::{PageAttribute, PTE};
+pub use pte::{PageAttribute, RawAttribute, TableAttribute, PTE};
 pub use pte_iterator::PageTableIterator;

+ 8 - 3
crates/eonix_mm/src/page_table/page_table.rs

@@ -1,7 +1,8 @@
 use super::{
     paging_mode::PageTableLevel,
+    pte::{RawAttribute, TableAttribute},
     pte_iterator::{KernelIterator, UserIterator},
-    PageAttribute, PagingMode, PTE,
+    PagingMode, PTE,
 };
 use crate::{
     address::{PAddr, VRange},
@@ -58,7 +59,7 @@ where
         };
 
         let level0 = M::LEVELS[0];
-        for idx in 0..level0.max_index() / 2 {
+        for idx in 0..=level0.max_index() / 2 {
             // We consider the first half of the page table as user space.
             // Clear all (potential) user space mappings.
             root_page_table.index_mut(idx).take();
@@ -109,7 +110,11 @@ where
 
         for pte in (0..=level.max_index()).map(|i| page_table.index_mut(i)) {
             let (pfn, attr) = pte.take();
-            if !attr.is_present() || !attr.is_user() {
+            let Some(attr) = attr.as_table_attr() else {
+                continue;
+            };
+
+            if !attr.contains(TableAttribute::PRESENT | TableAttribute::USER) {
                 continue;
             }
 

+ 57 - 25
crates/eonix_mm/src/page_table/pte.rs

@@ -1,39 +1,71 @@
 use crate::paging::PFN;
+use bitflags::bitflags;
 
-pub trait PageAttribute: Copy {
-    /// Create a new instance of the attribute with all attributes set to false.
-    fn new() -> Self;
-
-    fn present(self, present: bool) -> Self;
-    fn write(self, write: bool) -> Self;
-    fn execute(self, execute: bool) -> Self;
-    fn user(self, user: bool) -> Self;
-    fn accessed(self, accessed: bool) -> Self;
-    fn dirty(self, dirty: bool) -> Self;
-    fn global(self, global: bool) -> Self;
-    fn copy_on_write(self, cow: bool) -> Self;
-    fn mapped(self, mmap: bool) -> Self;
-
-    fn is_present(&self) -> bool;
-    fn is_write(&self) -> bool;
-    fn is_execute(&self) -> bool;
-    fn is_user(&self) -> bool;
-    fn is_accessed(&self) -> bool;
-    fn is_dirty(&self) -> bool;
-    fn is_global(&self) -> bool;
-    fn is_copy_on_write(&self) -> bool;
-    fn is_mapped(&self) -> bool;
+bitflags! {
+    #[derive(Clone, Copy, PartialEq)]
+    pub struct TableAttribute: usize {
+        const PRESENT = 1;
+        const USER = 2;
+        const ACCESSED = 4;
+        const GLOBAL = 8;
+    }
+
+    #[derive(Clone, Copy, PartialEq)]
+    pub struct PageAttribute: usize {
+        const PRESENT = 1;
+        const READ = 2;
+        const WRITE = 4;
+        const EXECUTE = 8;
+        const USER = 16;
+        const ACCESSED = 32;
+        const DIRTY = 64;
+        const GLOBAL = 128;
+        const COPY_ON_WRITE = 256;
+        const MAPPED = 512;
+        const ANONYMOUS = 1024;
+    }
+}
+
+pub trait RawAttribute: Copy {
+    /// Create a new attribute representing a non-present page.
+    fn null() -> Self;
+
+    /// Interpret the attribute as a page table attribute. Return `None` if it is
+    /// not an attribute for a page table.
+    ///
+    /// # Panic
+    /// The implementor should panic if invalid combinations of flags are present.
+    fn as_table_attr(self) -> Option<TableAttribute>;
+
+    /// Interpret the attribute as a page attribute. Return `None` if it is not
+    /// an attribute for a page.
+    ///
+    /// # Panic
+    /// The implementor should panic if invalid combinations of flags are present.
+    fn as_page_attr(self) -> Option<PageAttribute>;
+
+    /// Convert the attribute to a raw value.
+    ///
+    /// # Panic
+    /// The implementor should panic if invalid combinations of flags are present.
+    fn from_table_attr(table_attr: TableAttribute) -> Self;
+
+    /// Convert the attribute to a raw value.
+    ///
+    /// # Panic
+    /// The implementor should panic if invalid combinations of flags are present.
+    fn from_page_attr(page_attr: PageAttribute) -> Self;
 }
 
 pub trait PTE: Sized {
-    type Attr: PageAttribute;
+    type Attr: RawAttribute;
 
     fn set(&mut self, pfn: PFN, attr: Self::Attr);
     fn get(&self) -> (PFN, Self::Attr);
 
     fn take(&mut self) -> (PFN, Self::Attr) {
         let pfn_attr = self.get();
-        self.set(PFN::from_val(0), Self::Attr::new());
+        self.set(PFN::from_val(0), Self::Attr::null());
         pfn_attr
     }
 

+ 15 - 17
crates/eonix_mm/src/page_table/pte_iterator.rs

@@ -1,4 +1,7 @@
-use super::{PageAttribute as _, PagingMode, RawPageTable as _, PTE};
+use super::{
+    pte::{RawAttribute, TableAttribute},
+    PagingMode, RawPageTable as _, PTE,
+};
 use crate::{
     address::{AddrOps as _, VRange},
     paging::{Page, PageAccess, PageAlloc, PAGE_SIZE},
@@ -9,16 +12,16 @@ pub struct KernelIterator;
 pub struct UserIterator;
 
 pub trait IteratorType<M: PagingMode> {
-    fn page_table_attributes() -> <M::Entry as PTE>::Attr;
+    fn page_table_attributes() -> TableAttribute;
 
     fn get_page_table<'a, A, X>(pte: &mut M::Entry, alloc: &A) -> M::RawTable<'a>
     where
         A: PageAlloc,
         X: PageAccess,
     {
-        let attr = pte.get_attr();
+        let attr = pte.get_attr().as_table_attr().expect("Not a page table");
 
-        if attr.is_present() {
+        if attr.contains(TableAttribute::PRESENT) {
             let pfn = pte.get_pfn();
             unsafe {
                 // SAFETY: We are creating a pointer to a page referenced to in
@@ -36,7 +39,10 @@ pub trait IteratorType<M: PagingMode> {
                 page_table_ptr.write_bytes(0, 1);
             }
 
-            pte.set(page.into_raw(), Self::page_table_attributes());
+            pte.set(
+                page.into_raw(),
+                <M::Entry as PTE>::Attr::from_table_attr(Self::page_table_attributes()),
+            );
 
             unsafe {
                 // SAFETY: `page_table_ptr` is a valid pointer to a page table.
@@ -159,21 +165,13 @@ where
 }
 
 impl<M: PagingMode> IteratorType<M> for KernelIterator {
-    fn page_table_attributes() -> <M::Entry as PTE>::Attr {
-        <M::Entry as PTE>::Attr::new()
-            .present(true)
-            .write(true)
-            .execute(true)
-            .global(true)
+    fn page_table_attributes() -> TableAttribute {
+        TableAttribute::PRESENT | TableAttribute::GLOBAL
     }
 }
 
 impl<M: PagingMode> IteratorType<M> for UserIterator {
-    fn page_table_attributes() -> <M::Entry as PTE>::Attr {
-        <M::Entry as PTE>::Attr::new()
-            .present(true)
-            .write(true)
-            .execute(true)
-            .user(true)
+    fn page_table_attributes() -> TableAttribute {
+        TableAttribute::PRESENT | TableAttribute::USER
     }
 }

+ 26 - 18
src/kernel/mem/mm_area.rs

@@ -1,12 +1,12 @@
+use super::mm_list::EMPTY_PAGE;
 use super::paging::AllocZeroed as _;
 use super::{AsMemoryBlock, Mapping, Page, Permission};
 use crate::io::ByteBuffer;
 use crate::KResult;
 use core::{borrow::Borrow, cell::UnsafeCell, cmp::Ordering};
 use eonix_mm::address::{AddrOps as _, VAddr, VRange};
-use eonix_mm::page_table::{PageAttribute, PTE};
+use eonix_mm::page_table::{PageAttribute, RawAttribute, PTE};
 use eonix_mm::paging::PFN;
-use super::mm_list::EMPTY_PAGE;
 
 #[derive(Debug)]
 pub struct MMArea {
@@ -81,25 +81,28 @@ impl MMArea {
 
     /// # Return
     /// Whether the whole handling process is done.
-    pub fn handle_cow(&self, pte: &mut impl PTE) -> bool {
-        let mut page_attr = pte.get_attr();
+    pub fn handle_cow<E>(&self, pte: &mut E) -> bool
+    where
+        E: PTE,
+    {
+        let mut page_attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
         let pfn = pte.get_pfn();
 
-        page_attr = page_attr.copy_on_write(false);
-        page_attr = page_attr.write(self.permission.write);
+        page_attr.remove(PageAttribute::COPY_ON_WRITE);
+        page_attr.set(PageAttribute::WRITE, self.permission.write);
 
         let page = unsafe { Page::from_raw(pfn) };
         if page.is_exclusive() {
             // SAFETY: This is actually safe. If we read `1` here and we have `MMList` lock
             // held, there couldn't be neither other processes sharing the page, nor other
             // threads making the page COW at the same time.
-            pte.set_attr(page_attr);
+            pte.set_attr(E::Attr::from_page_attr(page_attr));
             core::mem::forget(page);
             return true;
         }
 
         let new_page;
-        if is_anonymous(pfn) {
+        if is_empty_page(pfn) {
             new_page = Page::zeroed();
         } else {
             new_page = Page::alloc();
@@ -115,18 +118,21 @@ impl MMArea {
             };
         }
 
-        page_attr = page_attr.accessed(false);
+        page_attr.remove(PageAttribute::ACCESSED);
 
-        pte.set(new_page.into_raw(), page_attr);
+        pte.set(new_page.into_raw(), E::Attr::from_page_attr(page_attr));
 
         false
     }
 
     /// # Arguments
     /// * `offset`: The offset from the start of the mapping, aligned to 4KB boundary.
-    pub fn handle_mmap(&self, pte: &mut impl PTE, offset: usize) -> KResult<()> {
+    pub fn handle_mmap<E>(&self, pte: &mut E, offset: usize) -> KResult<()>
+    where
+        E: PTE,
+    {
         // TODO: Implement shared mapping
-        let mut page_attr = pte.get_attr();
+        let mut page_attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
         let pfn = pte.get_pfn();
 
         match &self.mapping {
@@ -155,19 +161,21 @@ impl MMArea {
             _ => panic!("Anonymous mapping should not be PA_MMAP"),
         }
 
-        page_attr = page_attr.present(true).mapped(false);
-        pte.set_attr(page_attr);
+        page_attr.insert(PageAttribute::PRESENT);
+        page_attr.remove(PageAttribute::MAPPED);
+
+        pte.set_attr(E::Attr::from_page_attr(page_attr));
         Ok(())
     }
 
     pub fn handle(&self, pte: &mut impl PTE, offset: usize) -> KResult<()> {
-        let page_attr = pte.get_attr();
+        let page_attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
 
-        if page_attr.is_copy_on_write() {
+        if page_attr.contains(PageAttribute::COPY_ON_WRITE) {
             self.handle_cow(pte);
         }
 
-        if page_attr.is_mapped() {
+        if page_attr.contains(PageAttribute::MAPPED) {
             self.handle_mmap(pte, offset)?;
         }
 
@@ -176,7 +184,7 @@ impl MMArea {
 }
 
 /// check pfn with EMPTY_PAGE's pfn
-fn is_anonymous(pfn: PFN) -> bool {
+fn is_empty_page(pfn: PFN) -> bool {
     let empty_pfn = EMPTY_PAGE.pfn();
     pfn == empty_pfn
 }

+ 22 - 19
src/kernel/mem/mm_list.rs

@@ -13,11 +13,11 @@ use bindings::{EEXIST, EFAULT, EINVAL, ENOMEM};
 use core::fmt;
 use core::sync::atomic::{AtomicUsize, Ordering};
 use eonix_mm::address::{Addr as _, PAddr};
-use eonix_mm::page_table::PagingMode;
+use eonix_mm::page_table::{PageAttribute, PagingMode};
 use eonix_mm::paging::PFN;
 use eonix_mm::{
     address::{AddrOps as _, VAddr, VRange},
-    page_table::{PageAttribute, PageTable, PTE},
+    page_table::{PageTable, RawAttribute, PTE},
     paging::PAGE_SIZE,
 };
 use eonix_runtime::task::Task;
@@ -587,41 +587,44 @@ where
     fn set_anonymous(&mut self, execute: bool) {
         // Writable flag is set during page fault handling while executable flag is
         // preserved across page faults, so we set executable flag now.
-        let attr = <Self as PTE>::Attr::new()
-            .present(true)
-            .user(true)
-            .copy_on_write(true)
-            .execute(execute);
+        let mut attr = PageAttribute::PRESENT | PageAttribute::USER | PageAttribute::COPY_ON_WRITE;
+        attr.set(PageAttribute::EXECUTE, execute);
 
-        self.set(EMPTY_PAGE.clone().into_raw(), attr);
+        self.set(EMPTY_PAGE.clone().into_raw(), T::Attr::from_page_attr(attr));
     }
 
     fn set_mapped(&mut self, execute: bool) {
         // Writable flag is set during page fault handling while executable flag is
         // preserved across page faults, so we set executable flag now.
-        let attr = <Self as PTE>::Attr::new()
-            .user(true)
-            .copy_on_write(true)
-            .mapped(true)
-            .execute(execute);
+        let mut attr = PageAttribute::MAPPED | PageAttribute::USER | PageAttribute::COPY_ON_WRITE;
+        attr.set(PageAttribute::EXECUTE, execute);
 
-        self.set(EMPTY_PAGE.clone().into_raw(), attr);
+        self.set(EMPTY_PAGE.clone().into_raw(), T::Attr::from_page_attr(attr));
     }
 
     fn set_copy_on_write(&mut self, from: &mut Self) {
-        let mut from_attr = from.get_attr();
-        if !from_attr.is_present() {
+        let mut from_attr = from
+            .get_attr()
+            .as_page_attr()
+            .expect("Not a page attribute");
+
+        if !from_attr.contains(PageAttribute::PRESENT) {
             return;
         }
 
-        from_attr = from_attr.write(false).copy_on_write(true);
+        from_attr.remove(PageAttribute::WRITE);
+        from_attr.insert(PageAttribute::COPY_ON_WRITE);
 
         let pfn = unsafe {
             // SAFETY: We get the pfn from a valid page table entry, so it should be valid as well.
             Page::with_raw(from.get_pfn(), |page| page.clone().into_raw())
         };
 
-        self.set(pfn, from_attr.accessed(false));
-        from.set_attr(from_attr);
+        self.set(
+            pfn,
+            T::Attr::from_page_attr(from_attr & !PageAttribute::ACCESSED),
+        );
+
+        from.set_attr(T::Attr::from_page_attr(from_attr));
     }
 }