[wip] Improve virt_page impl and add tests

This commit is contained in:
Berkus Decker 2021-02-26 00:20:03 +02:00
parent f26fa39265
commit 083711b61e
1 changed files with 30 additions and 9 deletions

View File

@ -19,12 +19,15 @@ use {
/// A virtual memory page. /// A virtual memory page.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(C)]
pub struct Page<S: PageSize = Size4KiB> { pub struct Page<S: PageSize = Size4KiB> {
start_address: VirtAddr, start_address: VirtAddr,
size: PhantomData<S>, size: PhantomData<S>,
} }
enum Error {
NotAligned,
}
impl<S: PageSize> Page<S> { impl<S: PageSize> Page<S> {
/// The page size in bytes. /// The page size in bytes.
pub const SIZE: usize = S::SIZE; pub const SIZE: usize = S::SIZE;
@ -32,12 +35,13 @@ impl<S: PageSize> Page<S> {
/// Returns the page that starts at the given virtual address. /// Returns the page that starts at the given virtual address.
/// ///
/// Returns an error if the address is not correctly aligned (i.e. is not a valid page start). /// Returns an error if the address is not correctly aligned (i.e. is not a valid page start).
pub fn from_start_address(address: VirtAddr) -> Result<Self, ()> { pub fn from_start_address(address: VirtAddr) -> Result<Self, Error> {
if !address.is_aligned(S::SIZE) { if !address.is_aligned(S::SIZE) {
return Err(()); Err(Error::NotAligned)
} } else {
Ok(Page::containing_address(address)) Ok(Page::containing_address(address))
} }
}
/// Returns the page that contains the given virtual address. /// Returns the page that contains the given virtual address.
pub fn containing_address(address: VirtAddr) -> Self { pub fn containing_address(address: VirtAddr) -> Self {
@ -141,6 +145,7 @@ impl<S: PageSize> fmt::Debug for Page<S> {
impl<S: PageSize> Add<u64> for Page<S> { impl<S: PageSize> Add<u64> for Page<S> {
type Output = Self; type Output = Self;
// @todo should I add pages or just bytes here?
fn add(self, rhs: u64) -> Self::Output { fn add(self, rhs: u64) -> Self::Output {
Page::containing_address(self.start_address() + rhs * u64::from(S::SIZE)) Page::containing_address(self.start_address() + rhs * u64::from(S::SIZE))
} }
@ -154,6 +159,7 @@ impl<S: PageSize> AddAssign<u64> for Page<S> {
impl<S: PageSize> Sub<u64> for Page<S> { impl<S: PageSize> Sub<u64> for Page<S> {
type Output = Self; type Output = Self;
// @todo should I sub pages or just bytes here?
fn sub(self, rhs: u64) -> Self::Output { fn sub(self, rhs: u64) -> Self::Output {
Page::containing_address(self.start_address() - rhs * u64::from(S::SIZE)) Page::containing_address(self.start_address() - rhs * u64::from(S::SIZE))
} }
@ -174,7 +180,6 @@ impl<S: PageSize> Sub<Self> for Page<S> {
/// A range of pages with exclusive upper bound. /// A range of pages with exclusive upper bound.
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct PageRange<S: PageSize = Size4KiB> { pub struct PageRange<S: PageSize = Size4KiB> {
/// The start of the range, inclusive. /// The start of the range, inclusive.
pub start: Page<S>, pub start: Page<S>,
@ -197,7 +202,7 @@ impl<S: PageSize> Iterator for PageRange<S> {
type Item = Page<S>; type Item = Page<S>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.start < self.end { if !self.is_empty() {
let page = self.start.clone(); let page = self.start.clone();
self.start += 1; self.start += 1;
Some(page) Some(page)
@ -209,9 +214,11 @@ impl<S: PageSize> Iterator for PageRange<S> {
impl PageRange<Size2MiB> { impl PageRange<Size2MiB> {
/// Converts the range of 2MiB pages to a range of 4KiB pages. /// Converts the range of 2MiB pages to a range of 4KiB pages.
pub fn as_4kib_page_range(self) -> PageRange<Size4KiB> { // @todo what about range of 1GiB pages?
pub fn as_4kib_page_range(&self) -> PageRange<Size4KiB> {
PageRange { PageRange {
start: Page::containing_address(self.start.start_address()), start: Page::containing_address(self.start.start_address()),
// @fixme end is calculated incorrectly, add test
end: Page::containing_address(self.end.start_address()), end: Page::containing_address(self.end.start_address()),
} }
} }
@ -228,7 +235,6 @@ impl<S: PageSize> fmt::Debug for PageRange<S> {
/// A range of pages with inclusive upper bound. /// A range of pages with inclusive upper bound.
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct PageRangeInclusive<S: PageSize = Size4KiB> { pub struct PageRangeInclusive<S: PageSize = Size4KiB> {
/// The start of the range, inclusive. /// The start of the range, inclusive.
pub start: Page<S>, pub start: Page<S>,
@ -247,7 +253,7 @@ impl<S: PageSize> Iterator for PageRangeInclusive<S> {
type Item = Page<S>; type Item = Page<S>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.start <= self.end { if !self.is_empty() {
let page = self.start.clone(); let page = self.start.clone();
self.start += 1; self.start += 1;
Some(page) Some(page)
@ -297,4 +303,19 @@ mod tests {
} }
assert_eq!(range_inclusive.next(), None); assert_eq!(range_inclusive.next(), None);
} }
#[test_case]
fn test_page_range_conversion() {
let page_size = Size2MiB::SIZE;
let number = 10;
let start_addr = VirtAddr::new(0xdeadbeaf);
let start: Page = Page::containing_address(start_addr);
let end = start.clone() + number;
let range = Page::range(start.clone(), end.clone()).as_4kib_page_range();
// 10 2MiB pages is 512 4KiB pages
aseert_eq!(range.num_pages(), 512);
}
} }