diff --git a/os/src/main.rs b/os/src/main.rs index e4bcce04..d0568ac8 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -8,6 +8,7 @@ mod console; mod sbi; mod config; +mod mm; use core::arch::global_asm; use core::panic::PanicInfo; diff --git a/os/src/mm/address/.gitkeep b/os/src/mm/address/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/os/src/mm/address/address.rs b/os/src/mm/address/address.rs new file mode 100644 index 00000000..f39cab15 --- /dev/null +++ b/os/src/mm/address/address.rs @@ -0,0 +1,329 @@ +use crate::mm::address::operations::{AlignOps, CalcOps, UsizeConvert}; +use core::mem::size_of; +use core::ops::Range; + +/// trait to represent an address +pub trait Address: + CalcOps + AlignOps + UsizeConvert + Copy + Clone + PartialEq + PartialOrd + Eq + Ord +{ + /// check if the address is null (zero) + fn is_null(self) -> bool { + self.as_usize() == 0 + } + + /// return a null address (zero) + fn null() -> Self { + Self::from_usize(0) + } + + /// get the offset within a page + fn page_offset(self) -> usize { + self.as_usize() & (crate::config::PAGE_SIZE - 1) + } + + /// calculate the difference between two addresses + fn addr_diff(self, other: Self) -> isize { + self.as_usize() as isize - other.as_usize() as isize + } + + /// add the size of type T to the address + fn add(self) -> Self { + self.add_by(size_of::()) + } + + /// add the size of n elements of type T to the address + fn add_n(self, n: usize) -> Self { + self.add_by(size_of::() * n) + } + + /// add an offset to the address + fn add_by(self, offset: usize) -> Self { + Self::from_usize(self.as_usize() + offset) + } + + /// subtract the size of Self from the address + fn sub(self) -> Self { + self.sub_by(size_of::()) + } + + /// subtract the size of n elements of Self from the address + fn sub_n(self, n: usize) -> Self { + self.sub_by(size_of::() * n) + } + + /// subtract an offset from the address + fn sub_by(self, offset: usize) -> Self { + Self::from_usize(self.as_usize() - offset) + } + + /// increment the address by the size of Self + fn step(&mut self) { + self.step_by(size_of::()) + } + + /// increment the address by the size of n elements of Self + fn step_n(&mut self, n: usize) { + self.step_by(size_of::() * n) + } + + /// decrement the address by the size of Self + fn step_back(&mut self) { + self.step_back_by(size_of::()) + } + + /// decrement the address by the size of n elements of Self + fn step_back_n(&mut self, n: usize) { + self.step_back_by(size_of::() * n) + } + + /// increment the address by the given offset + fn step_by(&mut self, offset: usize) { + *self = self.add_by(offset); + } + + /// decrement the address by the given offset + fn step_back_by(&mut self, offset: usize) { + *self = self.sub_by(offset); + } +} + +/// macro to implement the Address trait for a type +#[macro_export] +macro_rules! impl_address { + ($type:ty) => { + impl $crate::mm::address::operations::UsizeConvert for $type { + fn as_usize(&self) -> usize { + unsafe { core::mem::transmute::(*self) } + } + fn from_usize(value: usize) -> Self { + unsafe { core::mem::transmute::(value) } + } + } + + $crate::impl_calc_ops!($type); + impl $crate::mm::address::operations::AlignOps for $type {} + + impl $crate::mm::address::address::Address for $type {} + + unsafe impl Sync for $type {} + unsafe impl Send for $type {} + }; +} + +/// physical address +#[repr(transparent)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Paddr(*const ()); +impl_address!(Paddr); + +/// virtual address +#[repr(transparent)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Vaddr(*const ()); +impl_address!(Vaddr); + +impl Vaddr { + /// create a virtual address from a reference + pub fn from_ref(r: &T) -> Self { + Self::from_ptr(r as *const T) + } + + /// create a virtual address from a pointer + pub fn from_ptr(p: *const T) -> Self { + Self::from_usize(p as usize) + } + + /// convert the address to a reference + /// # Safety + /// the caller must ensure that the address is valid for type T + pub unsafe fn as_ref(&self) -> &T { + &*(self.as_usize() as *const T) + } + + /// convert the address to a mutable reference + /// # Safety + /// the caller must ensure that the address is valid for type T + pub unsafe fn as_mut(&mut self) -> &mut T { + &mut *(self.as_usize() as *mut T) + } + + /// convert the address to a const pointer + pub fn as_ptr(&self) -> *const T { + self.as_usize() as *const T + } + + /// convert the address to a mutable pointer + /// # Safety + /// the caller must ensure that the address is valid for type T + pub unsafe fn as_mut_ptr(&mut self) -> *mut T { + self.as_usize() as *mut T + } +} + +/// address range +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct AddressRange +where + T: Address, +{ + start: T, + end: T, +} + +impl AddressRange +where + T: Address, +{ + /// create a new address range + pub fn new(start: T, end: T) -> Self { + Self { + start, + end, + } + } + + /// create an address range from a Range + pub fn from_range(range: Range) -> Self { + Self { + start: range.start, + end: range.end, + } + } + + /// create an address range from start address and length + pub fn from_start_len(start: T, len: usize) -> Self { + Self { + start, + end: T::from_usize(start.as_usize() + len), + } + } + + /// create an address range from a slice + pub fn from_slices(slices: &[T]) -> Option { + if slices.len() < 2 { + return None; + } + Some(Self { + start: slices[0], + end: slices[slices.len() - 1], + }) + } + + /// get the start address + pub fn start(&self) -> T { + self.start + } + + /// get the end address + pub fn end(&self) -> T { + self.end + } + + /// get the length of the range + pub fn len(&self) -> usize { + debug_assert!(self.end.as_usize() >= self.start.as_usize()); + self.end.as_usize() - self.start.as_usize() + } + + /// check if the range is empty + pub fn empty(&self) -> bool { + self.start == self.end + } + + /// check if the range contains an address + pub fn contains(&self, addr: T) -> bool { + addr >= self.start && addr < self.end + } + + /// check if the range contains another range + pub fn contains_range(&self, other: &Self) -> bool { + other.start >= self.start && other.end <= self.end + } + + /// check if the range is contained in another range + pub fn contains_in(&self, other: &Self) -> bool { + self.start >= other.start && self.end <= other.end + } + + /// check if the range intersects with another range + pub fn intersects(&self, other: &Self) -> bool { + self.start < other.end && other.start < self.end + } + + /// check if the range is adjacent to another range + pub fn adjacent(&self, other: &Self) -> bool { + self.end == other.start || other.end == self.start + } + + /// get the intersection of two ranges + pub fn intersection(&self, other: &Self) -> Option { + if !self.intersects(other) { + return None; + } + let start = core::cmp::max(self.start, other.start); + let end = core::cmp::min(self.end, other.end); + Some(Self { start, end }) + } + + /// get the union of two ranges + pub fn union(&self, other: &Self) -> Option { + if !self.intersects(other) && !self.adjacent(other) { + return None; + } + let start = core::cmp::min(self.start, other.start); + let end = core::cmp::max(self.end, other.end); + Some(Self { start, end }) + } + + /// get an iterator over the range + pub fn iter(&self) -> AddressRangeIterator { + AddressRangeIterator { + range: *self, + current: self.start, + } + } +} + +impl IntoIterator for AddressRange +where + T: Address, +{ + type Item = T; + type IntoIter = AddressRangeIterator; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +/// iterator for address range +pub struct AddressRangeIterator +where + T: Address, +{ + range: AddressRange, + current: T, +} + +impl Iterator for AddressRangeIterator +where + T: Address, +{ + type Item = T; + + fn next(&mut self) -> Option { + if self.current >= self.range.end { + return None; + } + let addr = self.current; + self.current.step(); + Some(addr) + } +} + +/// physical address range +pub type PaddrRange = AddressRange; + +/// virtual address range +pub type VaddrRange = AddressRange; diff --git a/os/src/mm/address/mod.rs b/os/src/mm/address/mod.rs new file mode 100644 index 00000000..a22d77a9 --- /dev/null +++ b/os/src/mm/address/mod.rs @@ -0,0 +1,31 @@ +//! Address module +//! +//! This module provides abstractions for working with physical and virtual addresses, +//! as well as page numbers in a memory management system. +//! +//! # Components +//! +//! - [`Address`]: Trait for representing memory addresses (physical or virtual) +//! - [`Paddr`]: Physical address type +//! - [`Vaddr`]: Virtual address type +//! - [`AddressRange`]: Generic range of addresses +//! - [`PageNum`]: Trait for representing page numbers +//! - [`Ppn`]: Physical page number +//! - [`Vpn`]: Virtual page number +//! - [`PageNumRange`]: Generic range of page numbers +//! +//! # Operations +//! +//! The module provides three key trait categories: +//! +//! - [`UsizeConvert`]: Convert between types and usize +//! - [`CalcOps`]: Arithmetic and bitwise operations +//! - [`AlignOps`]: Address alignment operations + +mod address; +mod operations; +mod page_num; + +pub use address::{Address, AddressRange, Paddr, PaddrRange, Vaddr, VaddrRange}; +pub use operations::{AlignOps, CalcOps, UsizeConvert}; +pub use page_num::{PageNum, Ppn, Vpn}; diff --git a/os/src/mm/address/operations.rs b/os/src/mm/address/operations.rs new file mode 100644 index 00000000..d1366623 --- /dev/null +++ b/os/src/mm/address/operations.rs @@ -0,0 +1,255 @@ +use crate::config::PAGE_SIZE; +use core::ops::{ + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Shl, ShlAssign, + Shr, ShrAssign, Sub, SubAssign, +}; + +/// convert between usize and the type +pub trait UsizeConvert: Copy + Clone + PartialEq + PartialOrd + Eq + Ord { + /// convert the type to usize + fn as_usize(&self) -> usize; + /// convert usize to the type + fn from_usize(value: usize) -> Self; +} + +/// arithmetic and bitwise operations +pub trait CalcOps: + UsizeConvert + + Add + + Add + + AddAssign + + AddAssign + + Sub + + Sub + + SubAssign + + SubAssign + + BitAnd + + BitAnd + + BitAndAssign + + BitAndAssign + + BitOr + + BitOr + + BitOrAssign + + BitOrAssign + + BitXor + + BitXor + + BitXorAssign + + BitXorAssign + + Shl + + ShlAssign + + Shr + + ShrAssign +{ +} + +/// macro to implement arithmetic and bitwise operations +#[macro_export] +macro_rules! impl_calc_ops { + ($type:ty) => { + impl core::ops::Add for $type { + type Output = Self; + fn add(self, rhs: usize) -> Self::Output { + $crate::mm::address::operations::UsizeConvert::from_usize(self.as_usize() + rhs) + } + } + impl core::ops::Add for $type { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { + $crate::mm::address::operations::UsizeConvert::from_usize( + self.as_usize() + rhs.as_usize(), + ) + } + } + impl core::ops::AddAssign for $type { + fn add_assign(&mut self, rhs: usize) { + *self = + $crate::mm::address::operations::UsizeConvert::from_usize(self.as_usize() + rhs) + } + } + impl core::ops::AddAssign for $type { + fn add_assign(&mut self, rhs: Self) { + *self = $crate::mm::address::operations::UsizeConvert::from_usize( + self.as_usize() + rhs.as_usize(), + ) + } + } + impl core::ops::Sub for $type { + type Output = Self; + fn sub(self, rhs: usize) -> Self::Output { + $crate::mm::address::operations::UsizeConvert::from_usize(self.as_usize() - rhs) + } + } + impl core::ops::Sub for $type { + type Output = Self; + fn sub(self, rhs: Self) -> Self::Output { + $crate::mm::address::operations::UsizeConvert::from_usize( + self.as_usize() - rhs.as_usize(), + ) + } + } + impl core::ops::SubAssign for $type { + fn sub_assign(&mut self, rhs: usize) { + *self = + $crate::mm::address::operations::UsizeConvert::from_usize(self.as_usize() - rhs) + } + } + impl core::ops::SubAssign for $type { + fn sub_assign(&mut self, rhs: Self) { + *self = $crate::mm::address::operations::UsizeConvert::from_usize( + self.as_usize() - rhs.as_usize(), + ) + } + } + impl core::ops::BitAnd for $type { + type Output = Self; + fn bitand(self, rhs: usize) -> Self::Output { + $crate::mm::address::operations::UsizeConvert::from_usize(self.as_usize() & rhs) + } + } + impl core::ops::BitAnd for $type { + type Output = Self; + fn bitand(self, rhs: Self) -> Self::Output { + $crate::mm::address::operations::UsizeConvert::from_usize( + self.as_usize() & rhs.as_usize(), + ) + } + } + impl core::ops::BitAndAssign for $type { + fn bitand_assign(&mut self, rhs: usize) { + *self = + $crate::mm::address::operations::UsizeConvert::from_usize(self.as_usize() & rhs) + } + } + impl core::ops::BitAndAssign for $type { + fn bitand_assign(&mut self, rhs: Self) { + *self = $crate::mm::address::operations::UsizeConvert::from_usize( + self.as_usize() & rhs.as_usize(), + ) + } + } + impl core::ops::BitOr for $type { + type Output = Self; + fn bitor(self, rhs: usize) -> Self::Output { + $crate::mm::address::operations::UsizeConvert::from_usize(self.as_usize() | rhs) + } + } + impl core::ops::BitOr for $type { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + $crate::mm::address::operations::UsizeConvert::from_usize( + self.as_usize() | rhs.as_usize(), + ) + } + } + impl core::ops::BitOrAssign for $type { + fn bitor_assign(&mut self, rhs: usize) { + *self = + $crate::mm::address::operations::UsizeConvert::from_usize(self.as_usize() | rhs) + } + } + impl core::ops::BitOrAssign for $type { + fn bitor_assign(&mut self, rhs: Self) { + *self = $crate::mm::address::operations::UsizeConvert::from_usize( + self.as_usize() | rhs.as_usize(), + ) + } + } + impl core::ops::BitXor for $type { + type Output = Self; + fn bitxor(self, rhs: usize) -> Self::Output { + $crate::mm::address::operations::UsizeConvert::from_usize(self.as_usize() ^ rhs) + } + } + impl core::ops::BitXor for $type { + type Output = Self; + fn bitxor(self, rhs: Self) -> Self::Output { + $crate::mm::address::operations::UsizeConvert::from_usize( + self.as_usize() ^ rhs.as_usize(), + ) + } + } + impl core::ops::BitXorAssign for $type { + fn bitxor_assign(&mut self, rhs: usize) { + *self = + $crate::mm::address::operations::UsizeConvert::from_usize(self.as_usize() ^ rhs) + } + } + impl core::ops::BitXorAssign for $type { + fn bitxor_assign(&mut self, rhs: Self) { + *self = $crate::mm::address::operations::UsizeConvert::from_usize( + self.as_usize() ^ rhs.as_usize(), + ) + } + } + impl core::ops::Shl for $type { + type Output = Self; + fn shl(self, rhs: usize) -> Self::Output { + $crate::mm::address::operations::UsizeConvert::from_usize(self.as_usize() << rhs) + } + } + impl core::ops::ShlAssign for $type { + fn shl_assign(&mut self, rhs: usize) { + *self = $crate::mm::address::operations::UsizeConvert::from_usize( + self.as_usize() << rhs, + ) + } + } + impl core::ops::Shr for $type { + type Output = Self; + fn shr(self, rhs: usize) -> Self::Output { + $crate::mm::address::operations::UsizeConvert::from_usize(self.as_usize() >> rhs) + } + } + impl core::ops::ShrAssign for $type { + fn shr_assign(&mut self, rhs: usize) { + *self = $crate::mm::address::operations::UsizeConvert::from_usize( + self.as_usize() >> rhs, + ) + } + } + impl $crate::mm::address::operations::CalcOps for $type {} + }; +} + +/// alignment operations +pub trait AlignOps: UsizeConvert { + /// check if the address is aligned to the given alignment + fn is_aligned(self, alignment: usize) -> bool { + debug_assert!( + alignment.is_power_of_two(), + "alignment must be a power of two" + ); + let mask = alignment - 1; + self.as_usize() & mask == 0 + } + /// check if the address is page aligned + fn is_page_aligned(self) -> bool { + self.is_aligned(PAGE_SIZE) + } + /// align the address up to the given alignment + fn align_up(self, alignment: usize) -> Self { + debug_assert!( + alignment.is_power_of_two(), + "alignment must be a power of two" + ); + let mask = alignment - 1; + Self::from_usize((self.as_usize() + mask) & !mask) + } + /// align the address down to the given alignment + fn align_down(self, alignment: usize) -> Self { + debug_assert!( + alignment.is_power_of_two(), + "alignment must be a power of two" + ); + let mask = alignment - 1; + Self::from_usize(self.as_usize() & !mask) + } + /// align the address up to the page size + fn align_up_to_page(self) -> Self { + self.align_up(PAGE_SIZE) + } + /// align the address down to the page size + fn align_down_to_page(self) -> Self { + self.align_down(PAGE_SIZE) + } +} diff --git a/os/src/mm/address/page_num.rs b/os/src/mm/address/page_num.rs new file mode 100644 index 00000000..ca30cc67 --- /dev/null +++ b/os/src/mm/address/page_num.rs @@ -0,0 +1,198 @@ +use crate::config::PAGE_SIZE; +use crate::mm::address::address::{Address, Paddr, Vaddr}; +use crate::mm::address::operations::{AlignOps, CalcOps, UsizeConvert}; +use core::ops::Range; + +/// trait to represent a page number +pub trait PageNum: + CalcOps + UsizeConvert + Copy + Clone + PartialEq + PartialOrd + Eq + Ord +{ + type TAddress: Address; + + /// increment the page number by 1 + fn step(&mut self) { + self.step_by(1); + } + + /// increment the page number by the given offset + fn step_by(&mut self, offset: usize) { + *self = Self::from_usize(self.as_usize() + offset); + } + + /// decrement the page number by 1 + fn step_back(&mut self) { + self.step_back_by(1); + } + + /// decrement the page number by the given offset + fn step_back_by(&mut self, offset: usize) { + *self = Self::from_usize(self.as_usize() - offset); + } + + /// convert an address to a page number (floor) + fn from_addr_floor(addr: Self::TAddress) -> Self { + Self::from_usize(addr.align_down_to_page().as_usize() / PAGE_SIZE) + } + + /// convert an address to a page number (ceil) + fn from_addr_ceil(addr: Self::TAddress) -> Self { + Self::from_usize(addr.align_up_to_page().as_usize() / PAGE_SIZE) + } + + /// get the start address of the page + fn start_addr(self) -> Self::TAddress { + Self::TAddress::from_usize(self.as_usize() * PAGE_SIZE) + } + + /// get the end address of the page + fn end_addr(self) -> Self::TAddress { + Self::TAddress::from_usize((self.as_usize() + 1) * PAGE_SIZE) + } + + /// calculate the difference between two page numbers + fn diff(self, other: Self) -> isize { + self.as_usize() as isize - other.as_usize() as isize + } +} + +/// macro to implement the PageNum trait for a type +#[macro_export] +macro_rules! impl_page_num { + ($type:ty, $addr_type:ty) => { + impl $crate::mm::address::operations::UsizeConvert for $type { + fn as_usize(&self) -> usize { + self.0 + } + + fn from_usize(value: usize) -> Self { + Self(value) + } + } + + $crate::impl_calc_ops!($type); + + impl $crate::mm::address::page_num::PageNum for $type { + type TAddress = $addr_type; + } + }; +} + +/// physical page number +#[repr(transparent)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Ppn(pub usize); +impl_page_num!(Ppn, Paddr); + +/// virtual page number +#[repr(transparent)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Vpn(pub usize); +impl_page_num!(Vpn, Vaddr); + +/// page number range +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct PageNumRange +where + T: PageNum, +{ + pub start: T, + pub end: T, +} + +impl PageNumRange +where + T: PageNum, +{ + /// create a new page number range + pub fn new(start: T, end: T) -> Self { + Self { start, end } + } + + /// create a page number range from a Range + pub fn from_range(range: Range) -> Self { + Self { + start: range.start, + end: range.end, + } + } + + /// create a page number range from start and length + pub fn from_start_len(start: T, len: usize) -> Self { + Self { + start, + end: T::from_usize(start.as_usize() + len), + } + } + + /// get the length of the range + pub fn len(&self) -> usize { + debug_assert!(self.end.as_usize() >= self.start.as_usize()); + self.end.as_usize() - self.start.as_usize() + } + + /// check if the range is empty + pub fn empty(&self) -> bool { + self.start == self.end + } + + /// check if the range contains a page number + pub fn contains(&self, addr: T) -> bool { + addr >= self.start && addr < self.end + } + + /// check if the range contains another range + pub fn contains_range(&self, other: &Self) -> bool { + other.start >= self.start && other.end <= self.end + } + + /// check if the range is contained in another range + pub fn contains_in(&self, other: &Self) -> bool { + self.start >= other.start && self.end <= other.end + } + + /// get an iterator over the range + pub fn iter(&self) -> PageNumRangeIterator { + PageNumRangeIterator { + range: *self, + current: self.start, + } + } +} + +impl IntoIterator for PageNumRange +where + T: PageNum, +{ + type Item = T; + type IntoIter = PageNumRangeIterator; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +/// iterator for page number range +pub struct PageNumRangeIterator +where + T: PageNum, +{ + range: PageNumRange, + current: T, +} + +impl Iterator for PageNumRangeIterator +where + T: PageNum, +{ + type Item = T; + + fn next(&mut self) -> Option { + if self.current >= self.range.end { + return None; + } + let result = self.current; + self.current.step(); + Some(result) + } +} diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs new file mode 100644 index 00000000..0fda4d55 --- /dev/null +++ b/os/src/mm/mod.rs @@ -0,0 +1 @@ +mod address; \ No newline at end of file