diff --git a/Cargo.lock b/Cargo.lock index e5f7c368d..4a05828f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -451,12 +451,6 @@ dependencies = [ "toml", ] -[[package]] -name = "const-assert" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8298db53081b3a951cadb6e0f4ebbe36def7bcb591a34676708d0d7ac87dd86" - [[package]] name = "controlled" version = "0.1.0" @@ -1282,7 +1276,6 @@ dependencies = [ "bitflags 1.3.2", "buddy_system_allocator", "cfg-if", - "const-assert", "fdt", "gimli 0.28.1", "iced-x86", diff --git a/kernel/comps/virtio/src/device/socket/config.rs b/kernel/comps/virtio/src/device/socket/config.rs index 08f2c169f..59b1d5c3a 100644 --- a/kernel/comps/virtio/src/device/socket/config.rs +++ b/kernel/comps/virtio/src/device/socket/config.rs @@ -2,7 +2,7 @@ use aster_util::safe_ptr::SafePtr; use bitflags::bitflags; -use ostd::{io_mem::IoMem, Pod}; +use ostd::{io_mem::IoMem, mm::PodOnce, Pod}; use crate::transport::VirtioTransport; @@ -32,6 +32,8 @@ pub struct VirtioVsockConfig { pub guest_cid_high: u32, } +impl PodOnce for VirtioVsockConfig {} + impl VirtioVsockConfig { pub(crate) fn new(transport: &dyn VirtioTransport) -> SafePtr { let memory = transport.device_config_mem().unwrap(); diff --git a/kernel/comps/virtio/src/queue.rs b/kernel/comps/virtio/src/queue.rs index 41401b75e..2957ad531 100644 --- a/kernel/comps/virtio/src/queue.rs +++ b/kernel/comps/virtio/src/queue.rs @@ -13,7 +13,7 @@ use aster_util::{field_ptr, safe_ptr::SafePtr}; use bitflags::bitflags; use log::debug; use ostd::{ - mm::{DmaCoherent, FrameAllocOptions}, + mm::{DmaCoherent, FrameAllocOptions, PodOnce}, offset_of, Pod, }; @@ -445,6 +445,8 @@ bitflags! { } } +impl PodOnce for DescFlags {} + /// The driver uses the available ring to offer buffers to the device: /// each ring entry refers to the head of a descriptor chain. /// It is only written by the driver and read by the device. @@ -487,3 +489,5 @@ bitflags! { const VIRTQ_AVAIL_F_NO_INTERRUPT = 1; } } + +impl PodOnce for AvailFlags {} diff --git a/ostd/Cargo.toml b/ostd/Cargo.toml index dc9febb0e..d4a199203 100644 --- a/ostd/Cargo.toml +++ b/ostd/Cargo.toml @@ -20,7 +20,6 @@ bit_field = "0.10.1" buddy_system_allocator = { version = "0.10", default-features = false, features = ["alloc"] } bitflags = "1.3" cfg-if = "1.0" -const-assert = "1.0" gimli = { version = "0.28", default-features = false, features = ["read-core"] } id-alloc = { path = "libs/id-alloc", version = "0.1.0" } inherit-methods-macro = { git = "https://github.com/asterinas/inherit-methods-macro", rev = "98f7e3e", version = "0.1.0" } diff --git a/ostd/src/arch/x86/iommu/dma_remapping/second_stage.rs b/ostd/src/arch/x86/iommu/dma_remapping/second_stage.rs index b5cb82be6..b842401bc 100644 --- a/ostd/src/arch/x86/iommu/dma_remapping/second_stage.rs +++ b/ostd/src/arch/x86/iommu/dma_remapping/second_stage.rs @@ -8,7 +8,7 @@ use crate::{ mm::{ page_prop::{CachePolicy, PageFlags, PrivilegedPageFlags as PrivFlags}, page_table::{PageTableEntryTrait, PageTableMode}, - Paddr, PageProperty, PagingConstsTrait, PagingLevel, Vaddr, + Paddr, PageProperty, PagingConstsTrait, PagingLevel, PodOnce, Vaddr, }, util::SameSizeAs, Pod, @@ -78,6 +78,8 @@ impl PageTableEntry { // SAFETY: `PageTableEntry` has the same size as `usize` in our supported x86 architecture. unsafe impl SameSizeAs for PageTableEntry {} +impl PodOnce for PageTableEntry {} + impl PageTableEntryTrait for PageTableEntry { fn new_page(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> Self { let mut pte = Self(paddr as u64 & Self::PHYS_MASK | PageTableFlags::LAST_PAGE.bits()); diff --git a/ostd/src/arch/x86/mm/mod.rs b/ostd/src/arch/x86/mm/mod.rs index 5afe22e3f..548760996 100644 --- a/ostd/src/arch/x86/mm/mod.rs +++ b/ostd/src/arch/x86/mm/mod.rs @@ -13,7 +13,7 @@ use crate::{ mm::{ page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags as PrivFlags}, page_table::PageTableEntryTrait, - Paddr, PagingConstsTrait, PagingLevel, Vaddr, PAGE_SIZE, + Paddr, PagingConstsTrait, PagingLevel, PodOnce, Vaddr, PAGE_SIZE, }, util::SameSizeAs, Pod, @@ -167,6 +167,8 @@ macro_rules! parse_flags { // SAFETY: `PageTableEntry` has the same size as `usize` unsafe impl SameSizeAs for PageTableEntry {} +impl PodOnce for PageTableEntry {} + impl PageTableEntryTrait for PageTableEntry { fn is_present(&self) -> bool { // For PT child, `PRESENT` should be set; for huge page, `HUGE` should diff --git a/ostd/src/lib.rs b/ostd/src/lib.rs index d0d372115..0aaa70735 100644 --- a/ostd/src/lib.rs +++ b/ostd/src/lib.rs @@ -9,7 +9,6 @@ #![feature(core_intrinsics)] #![feature(coroutines)] #![feature(fn_traits)] -#![feature(generic_const_exprs)] #![feature(iter_from_coroutine)] #![feature(let_chains)] #![feature(linkage)] @@ -20,9 +19,6 @@ #![feature(sync_unsafe_cell)] #![feature(trait_upcasting)] #![feature(iter_advance_by)] -// The `generic_const_exprs` feature is incomplete however required for the page table -// const generic implementation. We are using this feature in a conservative manner. -#![expect(incomplete_features)] #![expect(internal_features)] #![no_std] #![warn(missing_docs)] diff --git a/ostd/src/mm/io.rs b/ostd/src/mm/io.rs index 62b729fb4..a819d3c21 100644 --- a/ostd/src/mm/io.rs +++ b/ostd/src/mm/io.rs @@ -44,7 +44,6 @@ use alloc::vec; use core::marker::PhantomData; use align_ext::AlignExt; -use const_assert::{Assert, IsTrue}; use inherit_methods_macro::inherit_methods; use crate::{ @@ -525,6 +524,8 @@ impl<'a> VmReader<'a, Infallible> { let cursor = self.cursor.cast::(); assert!(cursor.is_aligned()); + const { assert!(pod_once_impls::is_non_tearing::()) }; + // SAFETY: We have checked that the number of bytes remaining is at least the size of `T` // and that the cursor is properly aligned with respect to the type `T`. All other safety // requirements are the same as for `Self::read`. @@ -746,6 +747,8 @@ impl<'a> VmWriter<'a, Infallible> { let cursor = self.cursor.cast::(); assert!(cursor.is_aligned()); + const { assert!(pod_once_impls::is_non_tearing::()) }; + // SAFETY: We have checked that the number of bytes remaining is at least the size of `T` // and that the cursor is properly aligned with respect to the type `T`. All other safety // requirements are the same as for `Self::writer`. @@ -926,27 +929,35 @@ impl<'a> From<&'a mut [u8]> for VmWriter<'a, Infallible> { /// A marker trait for POD types that can be read or written with one instruction. /// -/// We currently rely on this trait to ensure that the memory operation created by -/// `ptr::read_volatile` and `ptr::write_volatile` doesn't tear. However, the Rust documentation -/// makes no such guarantee, and even the wording in the LLVM LangRef is ambiguous. -/// -/// At this point, we can only _hope_ that this doesn't break in future versions of the Rust or -/// LLVM compilers. However, this is unlikely to happen in practice, since the Linux kernel also -/// uses "volatile" semantics to implement `READ_ONCE`/`WRITE_ONCE`. +/// This trait is mostly a hint, since it's safe and can be implemented for _any_ POD type. If it +/// is implemented for a type that cannot be read or written with a single instruction, calling +/// `read_once`/`write_once` will lead to a failed compile-time assertion. pub trait PodOnce: Pod {} -impl PodOnce for T where Assert<{ is_pod_once::() }>: IsTrue {} +#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] +mod pod_once_impls { + use super::PodOnce; -#[cfg(target_arch = "x86_64")] -const fn is_pod_once() -> bool { - let size = size_of::(); + impl PodOnce for u8 {} + impl PodOnce for u16 {} + impl PodOnce for u32 {} + impl PodOnce for u64 {} + impl PodOnce for usize {} + impl PodOnce for i8 {} + impl PodOnce for i16 {} + impl PodOnce for i32 {} + impl PodOnce for i64 {} + impl PodOnce for isize {} - size == 1 || size == 2 || size == 4 || size == 8 -} - -#[cfg(target_arch = "riscv64")] -const fn is_pod_once() -> bool { - let size = size_of::(); - - size == 1 || size == 2 || size == 4 || size == 8 + /// Checks whether the memory operation created by `ptr::read_volatile` and + /// `ptr::write_volatile` doesn't tear. + /// + /// Note that the Rust documentation makes no such guarantee, and even the wording in the LLVM + /// LangRef is ambiguous. But this is unlikely to break in practice because the Linux kernel + /// also uses "volatile" semantics to implement `READ_ONCE`/`WRITE_ONCE`. + pub(super) const fn is_non_tearing() -> bool { + let size = core::mem::size_of::(); + + size == 1 || size == 2 || size == 4 || size == 8 + } } diff --git a/ostd/src/mm/page_table/cursor.rs b/ostd/src/mm/page_table/cursor.rs index 6ba26242a..757b1a89c 100644 --- a/ostd/src/mm/page_table/cursor.rs +++ b/ostd/src/mm/page_table/cursor.rs @@ -114,10 +114,7 @@ pub enum PageTableItem { /// simulate the recursion, and adpot a page table locking protocol to /// provide concurrency. #[derive(Debug)] -pub struct Cursor<'a, M: PageTableMode, E: PageTableEntryTrait, C: PagingConstsTrait> -where - [(); C::NR_LEVELS as usize]:, -{ +pub struct Cursor<'a, M: PageTableMode, E: PageTableEntryTrait, C: PagingConstsTrait> { /// The lock guards of the cursor. The level 1 page table lock guard is at /// index 0, and the level N page table lock guard is at index N - 1. /// @@ -125,7 +122,7 @@ where /// from low to high, exactly the reverse order of the acquisition. /// This behavior is ensured by the default drop implementation of Rust: /// . - guards: [Option>; C::NR_LEVELS as usize], + guards: [Option>; MAX_NR_LEVELS], /// The level of the page table that the cursor points to. level: PagingLevel, /// From `guard_level` to `level`, the locks are held in `guards`. @@ -139,10 +136,10 @@ where _phantom: PhantomData<&'a PageTable>, } -impl<'a, M: PageTableMode, E: PageTableEntryTrait, C: PagingConstsTrait> Cursor<'a, M, E, C> -where - [(); C::NR_LEVELS as usize]:, -{ +/// The maximum value of `PagingConstsTrait::NR_LEVELS`. +const MAX_NR_LEVELS: usize = 4; + +impl<'a, M: PageTableMode, E: PageTableEntryTrait, C: PagingConstsTrait> Cursor<'a, M, E, C> { /// Creates a cursor claiming the read access for the given range. /// /// The cursor created will only be able to query or jump within the given @@ -159,6 +156,8 @@ where return Err(PageTableError::UnalignedVaddr); } + const { assert!(C::NR_LEVELS as usize <= MAX_NR_LEVELS) }; + let mut cursor = Self { guards: core::array::from_fn(|_| None), level: C::NR_LEVELS, @@ -340,8 +339,6 @@ where impl Iterator for Cursor<'_, M, E, C> -where - [(); C::NR_LEVELS as usize]:, { type Item = PageTableItem; @@ -361,14 +358,9 @@ where #[derive(Debug)] pub struct CursorMut<'a, M: PageTableMode, E: PageTableEntryTrait, C: PagingConstsTrait>( Cursor<'a, M, E, C>, -) -where - [(); C::NR_LEVELS as usize]:; +); -impl<'a, M: PageTableMode, E: PageTableEntryTrait, C: PagingConstsTrait> CursorMut<'a, M, E, C> -where - [(); C::NR_LEVELS as usize]:, -{ +impl<'a, M: PageTableMode, E: PageTableEntryTrait, C: PagingConstsTrait> CursorMut<'a, M, E, C> { /// Creates a cursor claiming the write access for the given range. /// /// The cursor created will only be able to map, query or jump within the given diff --git a/ostd/src/mm/page_table/mod.rs b/ostd/src/mm/page_table/mod.rs index f8093721d..9a1b6f6a2 100644 --- a/ostd/src/mm/page_table/mod.rs +++ b/ostd/src/mm/page_table/mod.rs @@ -9,8 +9,8 @@ use core::{ }; use super::{ - io::PodOnce, nr_subpage_per_huge, page_prop::PageProperty, page_size, Paddr, PagingConstsTrait, - PagingLevel, Vaddr, + nr_subpage_per_huge, page_prop::PageProperty, page_size, Paddr, PagingConstsTrait, PagingLevel, + PodOnce, Vaddr, }; use crate::{ arch::mm::{PageTableEntry, PagingConsts}, @@ -84,9 +84,7 @@ pub struct PageTable< M: PageTableMode, E: PageTableEntryTrait = PageTableEntry, C: PagingConstsTrait = PagingConsts, -> where - [(); C::NR_LEVELS as usize]:, -{ +> { root: RawPageTableNode, _phantom: PhantomData, } @@ -201,10 +199,7 @@ impl PageTable { } } -impl<'a, M: PageTableMode, E: PageTableEntryTrait, C: PagingConstsTrait> PageTable -where - [(); C::NR_LEVELS as usize]:, -{ +impl<'a, M: PageTableMode, E: PageTableEntryTrait, C: PagingConstsTrait> PageTable { /// Create a new empty page table. Useful for the kernel page table and IOMMU page tables only. pub fn empty() -> Self { PageTable { diff --git a/ostd/src/mm/page_table/node/child.rs b/ostd/src/mm/page_table/node/child.rs index 54a536019..1ad5a3bcf 100644 --- a/ostd/src/mm/page_table/node/child.rs +++ b/ostd/src/mm/page_table/node/child.rs @@ -23,9 +23,7 @@ use crate::{ pub(in crate::mm) enum Child< E: PageTableEntryTrait = PageTableEntry, C: PagingConstsTrait = PagingConsts, -> where - [(); C::NR_LEVELS as usize]:, -{ +> { PageTable(RawPageTableNode), Frame(Frame, PageProperty), /// Pages not tracked by handles. @@ -33,10 +31,7 @@ pub(in crate::mm) enum Child< None, } -impl Child -where - [(); C::NR_LEVELS as usize]:, -{ +impl Child { /// Returns whether the child does not map to anything. pub(in crate::mm) fn is_none(&self) -> bool { matches!(self, Child::None) diff --git a/ostd/src/mm/page_table/node/entry.rs b/ostd/src/mm/page_table/node/entry.rs index e497aeedd..ec48d1c23 100644 --- a/ostd/src/mm/page_table/node/entry.rs +++ b/ostd/src/mm/page_table/node/entry.rs @@ -12,10 +12,7 @@ use crate::mm::{nr_subpage_per_huge, page_prop::PageProperty, page_size, PagingC /// This is a static reference to an entry in a node that does not account for /// a dynamic reference count to the child. It can be used to create a owned /// handle, which is a [`Child`]. -pub(in crate::mm) struct Entry<'a, E: PageTableEntryTrait, C: PagingConstsTrait> -where - [(); C::NR_LEVELS as usize]:, -{ +pub(in crate::mm) struct Entry<'a, E: PageTableEntryTrait, C: PagingConstsTrait> { /// The page table entry. /// /// We store the page table entry here to optimize the number of reads from @@ -30,10 +27,7 @@ where node: &'a mut PageTableNode, } -impl<'a, E: PageTableEntryTrait, C: PagingConstsTrait> Entry<'a, E, C> -where - [(); C::NR_LEVELS as usize]:, -{ +impl<'a, E: PageTableEntryTrait, C: PagingConstsTrait> Entry<'a, E, C> { /// Returns if the entry does not map to anything. pub(in crate::mm) fn is_none(&self) -> bool { !self.pte.is_present() diff --git a/ostd/src/mm/page_table/node/mod.rs b/ostd/src/mm/page_table/node/mod.rs index 51d4d6cf0..ead126e4c 100644 --- a/ostd/src/mm/page_table/node/mod.rs +++ b/ostd/src/mm/page_table/node/mod.rs @@ -56,19 +56,13 @@ use crate::{ /// Only the CPU or a PTE can access a page table node using a raw handle. To access the page /// table node from the kernel code, use the handle [`PageTableNode`]. #[derive(Debug)] -pub(super) struct RawPageTableNode -where - [(); C::NR_LEVELS as usize]:, -{ +pub(super) struct RawPageTableNode { raw: Paddr, level: PagingLevel, _phantom: PhantomData<(E, C)>, } -impl RawPageTableNode -where - [(); C::NR_LEVELS as usize]:, -{ +impl RawPageTableNode { pub(super) fn paddr(&self) -> Paddr { self.raw } @@ -189,8 +183,6 @@ where impl From> for Frame> -where - [(); C::NR_LEVELS as usize]:, { fn from(raw: RawPageTableNode) -> Self { let raw = ManuallyDrop::new(raw); @@ -201,10 +193,7 @@ where } } -impl Drop for RawPageTableNode -where - [(); C::NR_LEVELS as usize]:, -{ +impl Drop for RawPageTableNode { fn drop(&mut self) { // SAFETY: The physical address in the raw handle is valid. The restored // handle is dropped to decrement the reference count. @@ -223,16 +212,11 @@ where pub(super) struct PageTableNode< E: PageTableEntryTrait = PageTableEntry, C: PagingConstsTrait = PagingConsts, -> where - [(); C::NR_LEVELS as usize]:, -{ +> { page: Frame>, } -impl PageTableNode -where - [(); C::NR_LEVELS as usize]:, -{ +impl PageTableNode { /// Borrows an entry in the node at a given index. /// /// # Panics @@ -346,10 +330,7 @@ where } } -impl Drop for PageTableNode -where - [(); C::NR_LEVELS as usize]:, -{ +impl Drop for PageTableNode { fn drop(&mut self) { // Release the lock. self.page.meta().lock.store(0, Ordering::Release); @@ -362,9 +343,7 @@ where pub(in crate::mm) struct PageTablePageMeta< E: PageTableEntryTrait = PageTableEntry, C: PagingConstsTrait = PagingConsts, -> where - [(); C::NR_LEVELS as usize]:, -{ +> { /// The number of valid PTEs. It is mutable if the lock is held. pub nr_children: SyncUnsafeCell, /// The level of the page table page. A page table page cannot be @@ -393,10 +372,7 @@ pub(in crate::mm) enum MapTrackingStatus { Tracked, } -impl PageTablePageMeta -where - [(); C::NR_LEVELS as usize]:, -{ +impl PageTablePageMeta { pub fn new_locked(level: PagingLevel, is_tracked: MapTrackingStatus) -> Self { Self { nr_children: SyncUnsafeCell::new(0), @@ -410,10 +386,7 @@ where // SAFETY: The layout of the `PageTablePageMeta` is ensured to be the same for // all possible generic parameters. And the layout fits the requirements. -unsafe impl AnyFrameMeta for PageTablePageMeta -where - [(); C::NR_LEVELS as usize]:, -{ +unsafe impl AnyFrameMeta for PageTablePageMeta { fn on_drop(&mut self, reader: &mut VmReader) { let nr_children = self.nr_children.get_mut(); diff --git a/ostd/src/mm/page_table/test.rs b/ostd/src/mm/page_table/test.rs index 06283e6b5..b7f3edc5b 100644 --- a/ostd/src/mm/page_table/test.rs +++ b/ostd/src/mm/page_table/test.rs @@ -146,10 +146,7 @@ fn test_user_copy_on_write() { assert!(child_pt.query(from.start + 10).is_none()); } -impl PageTable -where - [(); C::NR_LEVELS as usize]:, -{ +impl PageTable { fn protect(&self, range: &Range, mut op: impl FnMut(&mut PageProperty)) { let mut cursor = self.cursor_mut(range).unwrap(); loop {