diff --git a/kernel/memory_structs/src/lib.rs b/kernel/memory_structs/src/lib.rs index 3563eae66a..20181c16eb 100644 --- a/kernel/memory_structs/src/lib.rs +++ b/kernel/memory_structs/src/lib.rs @@ -11,19 +11,74 @@ #![allow(incomplete_features)] #![feature(adt_const_params)] +#[cfg(test)] +mod test; + use core::{ cmp::{min, max}, fmt, iter::Step, - marker::ConstParamTy, + marker::{ConstParamTy, PhantomData}, ops::{Add, AddAssign, Deref, DerefMut, Sub, SubAssign}, }; -use kernel_config::memory::{MAX_PAGE_NUMBER, PAGE_SIZE}; +use kernel_config::memory::{MAX_PAGE_NUMBER, PAGE_SIZE, ENTRIES_PER_PAGE_TABLE}; use zerocopy::FromBytes; use paste::paste; use derive_more::*; use range_inclusive::{RangeInclusive, RangeInclusiveIterator}; +/// Enum used to indicate the size of a page or frame. +#[derive(Debug)] +pub enum MemChunkSize { + Normal4K, + Huge2M, + Huge1G, +} + +/// Trait that represents the size of a page or frame, i.e., for normal or huge pages. +/// +/// This is used to parameterize `Page`- and `Frame`-related types with a page size, +/// in order to define normal and huge pages in a generic manner. +pub trait PageSize: Ord + PartialOrd + Clone + Copy + private::Sealed { + const SIZE: MemChunkSize; + const NUM_4K_PAGES: usize; + const SIZE_IN_BYTES: usize; +} + +mod private { + pub trait Sealed { } +} + +/// Marker struct used to indicate the default page size of 4KiB. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Page4K; +impl private::Sealed for Page4K { } +impl PageSize for Page4K { + const SIZE: MemChunkSize = MemChunkSize::Normal4K; + const NUM_4K_PAGES: usize = 1; + const SIZE_IN_BYTES: usize = PAGE_SIZE; +} + +/// Marker struct used to indicate a page size of 2MiB. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Page2M; +impl private::Sealed for Page2M { } +impl PageSize for Page2M { + const SIZE: MemChunkSize = MemChunkSize::Huge2M; + const NUM_4K_PAGES: usize = Page4K::NUM_4K_PAGES * ENTRIES_PER_PAGE_TABLE; + const SIZE_IN_BYTES: usize = Self::NUM_4K_PAGES * Page4K::SIZE_IN_BYTES; +} + +/// Marker struct used to indicate a page size of 1GiB. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Page1G; +impl private::Sealed for Page1G { } +impl PageSize for Page1G { + const SIZE: MemChunkSize = MemChunkSize::Huge1G; + const NUM_4K_PAGES: usize = Page2M::NUM_4K_PAGES * ENTRIES_PER_PAGE_TABLE; + const SIZE_IN_BYTES: usize = Self::NUM_4K_PAGES * Page4K::SIZE_IN_BYTES; +} + /// The possible states that a range of exclusively-owned pages or frames can be in. #[derive(PartialEq, Eq, ConstParamTy)] pub enum MemoryState { @@ -80,12 +135,12 @@ macro_rules! implement_address { self.0 } - #[doc = "Returns the offset from the " $chunk " boundary specified by this `" - $TypeName ".\n\n \ - For example, if the [`PAGE_SIZE`] is 4096 (4KiB), then this will return + #[doc = "Returns the offset from the 4K " $chunk " boundary specified by this `" + $TypeName ".\n\n \ + For example, for the address `0xFFFF_1578`, this will return `0x578`, the least significant 12 bits `(12:0]` of this `" $TypeName "`."] pub const fn [<$chunk _offset>](&self) -> usize { - self.0 & (PAGE_SIZE - 1) + self.0 & (Page4K::SIZE_IN_BYTES - 1) } } impl fmt::Debug for $TypeName { @@ -263,29 +318,21 @@ implement_address!( macro_rules! implement_page_frame { ($TypeName:ident, $desc:literal, $prefix:literal, $address:ident) => { paste! { // using the paste crate's macro for easy concatenation - - #[doc = "A `" $TypeName "` is a chunk of **" $desc "** memory aligned to a [`PAGE_SIZE`] boundary."] + #[doc = "A `" $TypeName "` is a chunk of **" $desc "** memory aligned to \ + a page boundary (default 4KiB) given by the `P` parameter."] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] - pub struct $TypeName { + pub struct $TypeName { + /// A Page or Frame number is *always* given in terms of 4KiB pages/frames, + /// even for huge pages/frames. number: usize, + size: PhantomData::

, } - - impl $TypeName { - #[doc = "Returns the `" $address "` at the start of this `" $TypeName "`."] - pub const fn start_address(&self) -> $address { - $address::new_canonical(self.number * PAGE_SIZE) - } - - #[doc = "Returns the number of this `" $TypeName "`."] - #[inline(always)] - pub const fn number(&self) -> usize { - self.number - } - - #[doc = "Returns the `" $TypeName "` containing the given `" $address "`."] + impl $TypeName { + #[doc = "Returns the 4KiB `" $TypeName "` containing the given `" $address "`."] pub const fn containing_address(addr: $address) -> $TypeName { $TypeName { - number: addr.value() / PAGE_SIZE, + number: addr.value() / Page4K::SIZE_IN_BYTES, + size: PhantomData, } } @@ -294,59 +341,149 @@ macro_rules! implement_page_frame { #[doc(alias = "next_multiple_of")] pub const fn align_up(&self, alignment_4k_pages: usize) -> $TypeName { $TypeName { - number: self.number.next_multiple_of(alignment_4k_pages) + number: self.number.next_multiple_of(alignment_4k_pages), + size: PhantomData } } } - impl fmt::Debug for $TypeName { + impl $TypeName { + #[doc = "Returns the 2MiB huge `" $TypeName "` containing the given `" $address "`."] + pub const fn containing_address_2mb(addr: $address) -> $TypeName { + $TypeName { + number: (addr.value() / Page2M::SIZE_IN_BYTES) * Page2M::NUM_4K_PAGES, + size: PhantomData, + } + } + } + impl $TypeName { + #[doc = "Returns the 1GiB huge `" $TypeName "` containing the given `" $address "`."] + pub const fn containing_address_1gb(addr: $address) -> $TypeName { + $TypeName { + number: (addr.value() / Page1G::SIZE_IN_BYTES) * Page1G::NUM_4K_PAGES, + size: PhantomData, + } + } + } + impl $TypeName

{ + #[doc = "Returns the 4K-sized number of this `" $TypeName "`."] + #[inline(always)] + pub const fn number(&self) -> usize { + self.number + } + + #[doc = "Returns the `" $address "` at the start of this `" $TypeName "`."] + pub const fn start_address(&self) -> $address { + $address::new_canonical(self.number * Page4K::SIZE_IN_BYTES) + } + + #[doc = "Returns the size of this `" $TypeName "`."] + pub const fn page_size(&self) -> MemChunkSize { + P::SIZE + } + } + impl fmt::Debug for $TypeName

{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, concat!(stringify!($TypeName), "(", $prefix, "{:#X})"), self.start_address()) } } - impl Add for $TypeName { - type Output = $TypeName; - fn add(self, rhs: usize) -> $TypeName { + impl Add for $TypeName

{ + type Output = $TypeName

; + fn add(self, rhs: usize) -> $TypeName

{ // cannot exceed max page number (which is also max frame number) $TypeName { - number: core::cmp::min(MAX_PAGE_NUMBER, self.number.saturating_add(rhs)), + number: core::cmp::min( + MAX_PAGE_NUMBER, + self.number.saturating_add(rhs.saturating_mul(P::NUM_4K_PAGES)) + ), + size: self.size, } } } - impl AddAssign for $TypeName { + impl AddAssign for $TypeName

{ fn add_assign(&mut self, rhs: usize) { *self = $TypeName { - number: core::cmp::min(MAX_PAGE_NUMBER, self.number.saturating_add(rhs)), - }; + number: core::cmp::min( + MAX_PAGE_NUMBER, + self.number.saturating_add(rhs.saturating_mul(P::NUM_4K_PAGES)) + ), + size: self.size, + } } } - impl Sub for $TypeName { - type Output = $TypeName; - fn sub(self, rhs: usize) -> $TypeName { + impl Sub for $TypeName

{ + type Output = $TypeName

; + fn sub(self, rhs: usize) -> $TypeName

{ $TypeName { - number: self.number.saturating_sub(rhs), + number: self.number.saturating_sub(rhs.saturating_mul(P::NUM_4K_PAGES)), + size: self.size } } } - impl SubAssign for $TypeName { + impl SubAssign for $TypeName

{ fn sub_assign(&mut self, rhs: usize) { *self = $TypeName { - number: self.number.saturating_sub(rhs), - }; + number: self.number.saturating_sub(rhs.saturating_mul(P::NUM_4K_PAGES)), + size: self.size + } } } - #[doc = "Implementing `Step` allows `" $TypeName "` to be used in an [`Iterator`]."] - impl Step for $TypeName { + impl Step for $TypeName

{ #[inline] - fn steps_between(start: &$TypeName, end: &$TypeName) -> Option { + fn steps_between(start: &$TypeName

, end: &$TypeName

) -> Option { Step::steps_between(&start.number, &end.number) + .map(|n| n / P::NUM_4K_PAGES) } #[inline] - fn forward_checked(start: $TypeName, count: usize) -> Option<$TypeName> { - Step::forward_checked(start.number, count).map(|n| $TypeName { number: n }) + fn forward_checked(start: $TypeName

, count: usize) -> Option<$TypeName

> { + Step::forward_checked(start.number, count * P::NUM_4K_PAGES) + .map(|number| $TypeName { number, size: PhantomData }) } #[inline] - fn backward_checked(start: $TypeName, count: usize) -> Option<$TypeName> { - Step::backward_checked(start.number, count).map(|n| $TypeName { number: n }) + fn backward_checked(start: $TypeName

, count: usize) -> Option<$TypeName

> { + Step::backward_checked(start.number, count * P::NUM_4K_PAGES) + .map(|number| $TypeName { number, size: PhantomData }) + } + } + impl TryFrom<$TypeName> for $TypeName { + type Error = &'static str; + fn try_from(p: $TypeName) -> Result { + if p.number % Page2M::NUM_4K_PAGES == 0 { + Ok(Self { + number: p.number, + size: PhantomData, + }) + } else { + Err("Could not convert 4KiB to 2MiB page.") + } + } + } + impl TryFrom<$TypeName> for $TypeName { + type Error = &'static str; + fn try_from(p: $TypeName) -> Result { + if p.number % Page1G::NUM_4K_PAGES == 0 { + Ok(Self { + number: p.number, + size: PhantomData, + }) + } else { + Err("Could not convert 4KiB to 1GiB page.") + } + } + } + impl From<$TypeName> for $TypeName { + fn from(p: $TypeName) -> Self { + Self { + number: p.number, + size: PhantomData + } + } + } + impl From<$TypeName> for $TypeName { + fn from(p: $TypeName) -> Self { + Self { + number: p.number, + size: PhantomData + } } } } @@ -357,7 +494,7 @@ implement_page_frame!(Page, "virtual", "v", VirtualAddress); implement_page_frame!(Frame, "physical", "p", PhysicalAddress); // Implement other functions for the `Page` type that aren't relevant for `Frame. -impl Page { +impl Page

{ /// Returns the 9-bit part of this `Page`'s [`VirtualAddress`] that is the index into the P4 page table entries list. pub const fn p4_index(&self) -> usize { (self.number >> 27) & 0x1FF @@ -392,17 +529,12 @@ macro_rules! implement_page_frame_range { #[doc = "A range of [`" $chunk "`]s that are contiguous in " $desc " memory."] #[derive(Clone, PartialEq, Eq)] - pub struct $TypeName(RangeInclusive<$chunk>); - - impl $TypeName { - #[doc = "Creates a new range of [`" $chunk "`]s that spans from `start` to `end`, both inclusive bounds."] - pub const fn new(start: $chunk, end: $chunk) -> $TypeName { - $TypeName(RangeInclusive::new(start, end)) - } + pub struct $TypeName(RangeInclusive<$chunk::

>); + impl $TypeName { #[doc = "Creates a `" $TypeName "` that will always yield `None` when iterated."] - pub const fn empty() -> $TypeName { - $TypeName::new($chunk { number: 1 }, $chunk { number: 0 }) + pub const fn empty() -> Self { + Self::new($chunk { number: 1, size: PhantomData }, $chunk { number: 0, size: PhantomData }) } #[doc = "A convenience method for creating a new `" $TypeName "` that spans \ @@ -419,6 +551,12 @@ macro_rules! implement_page_frame_range { $TypeName::new(start, end) } } + } + impl $TypeName

{ + #[doc = "Creates a new range of [`" $chunk "`]s that spans from `start` to `end`, both inclusive bounds."] + pub const fn new(start: $chunk

, end: $chunk

) -> $TypeName

{ + $TypeName(RangeInclusive::new(start, end)) + } #[doc = "Returns the [`" $address "`] of the starting [`" $chunk "`] in this `" $TypeName "`."] pub const fn start_address(&self) -> $address { @@ -430,17 +568,19 @@ macro_rules! implement_page_frame_range { This is instant, because it doesn't need to iterate over each entry, unlike normal iterators."] pub const fn [](&self) -> usize { // add 1 because it's an inclusive range - (self.0.end().number + 1).saturating_sub(self.0.start().number) + (self.0.end().number + (1 * P::NUM_4K_PAGES)) + .saturating_sub(self.0.start().number) + / P::NUM_4K_PAGES } - /// Returns the size of this range in number of bytes. + #[doc = "Returns the size of this range in bytes."] pub const fn size_in_bytes(&self) -> usize { - self.[]() * PAGE_SIZE + self.[]() * P::SIZE_IN_BYTES } #[doc = "Returns `true` if this `" $TypeName "` contains the given [`" $address "`]."] pub const fn contains_address(&self, addr: $address) -> bool { - let c = $chunk::containing_address(addr); + let c = $chunk::::containing_address(addr); self.0.start().number <= c.number && c.number <= self.0.end().number } @@ -474,7 +614,7 @@ macro_rules! implement_page_frame_range { } #[doc = "Returns a new separate `" $TypeName "` that is extended to include the given [`" $chunk "`]."] - pub fn to_extended(&self, to_include: $chunk) -> $TypeName { + pub fn to_extended(&self, to_include: $chunk

) -> $TypeName

{ // if the current range was empty, return a new range containing only the given page/frame if self.is_empty() { return $TypeName::new(to_include.clone(), to_include); @@ -484,10 +624,18 @@ macro_rules! implement_page_frame_range { $TypeName::new(start.clone(), end.clone()) } + #[doc = "Returns `true` if the `other` `" $TypeName "` is fully contained within this `" $TypeName "`."] + pub fn contains_range(&self, other: &$TypeName

) -> bool { + !other.is_empty() + && (other.start() >= self.start()) + && (other.end() <= self.end()) + } + } + impl $TypeName

{ #[doc = "Returns an inclusive `" $TypeName "` representing the [`" $chunk "`]s that overlap \ across this `" $TypeName "` and the given other `" $TypeName "`.\n\n \ If there is no overlap between the two ranges, `None` is returned."] - pub fn overlap(&self, other: &$TypeName) -> Option<$TypeName> { + pub fn overlap(&self, other: &$TypeName

) -> Option<$TypeName

> { let starts = max(*self.start(), *other.start()); let ends = min(*self.end(), *other.end()); if starts <= ends { @@ -496,22 +644,15 @@ macro_rules! implement_page_frame_range { None } } - - #[doc = "Returns `true` if the `other` `" $TypeName "` is fully contained within this `" $TypeName "`."] - pub fn contains_range(&self, other: &$TypeName) -> bool { - !other.is_empty() - && (other.start() >= self.start()) - && (other.end() <= self.end()) - } } - impl fmt::Debug for $TypeName { + impl fmt::Debug for $TypeName

{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.0) } } - impl Deref for $TypeName { - type Target = RangeInclusive<$chunk>; - fn deref(&self) -> &RangeInclusive<$chunk> { + impl Deref for $TypeName

{ + type Target = RangeInclusive<$chunk

>; + fn deref(&self) -> &RangeInclusive<$chunk

> { &self.0 } } @@ -520,31 +661,67 @@ macro_rules! implement_page_frame_range { &mut self.0 } } - impl IntoIterator for $TypeName { - type Item = $chunk; - type IntoIter = RangeInclusiveIterator<$chunk>; + impl IntoIterator for $TypeName

{ + type Item = $chunk

; + type IntoIter = RangeInclusiveIterator<$chunk

>; fn into_iter(self) -> Self::IntoIter { self.0.iter() } } - #[doc = "A `" $TypeName "` that implements `Copy`"] + #[doc = "A `" $TypeName "` that implements `Copy`."] #[derive(Clone, Copy)] - pub struct [] { - start: $chunk, - end: $chunk, + pub struct [] { + start: $chunk

, + end: $chunk

, } - impl From<$TypeName> for [] { - fn from(r: $TypeName) -> Self { + impl From<$TypeName

> for []

{ + fn from(r: $TypeName

) -> Self { Self { start: *r.start(), end: *r.end() } } } - impl From<[]> for $TypeName { - fn from(cr: []) -> Self { + impl From<[]

> for $TypeName

{ + fn from(cr: []

) -> Self { Self::new(cr.start, cr.end) } } + impl From<$TypeName> for $TypeName { + fn from(r: $TypeName) -> Self { + Self::new($chunk::from(*r.start()), $chunk::from(*r.end())) + } + } + impl From<$TypeName> for $TypeName { + fn from(r: $TypeName) -> Self { + Self::new($chunk::from(*r.start()), $chunk::from(*r.end())) + } + } + impl TryFrom<$TypeName> for $TypeName { + type Error = &'static str; + fn try_from(p: $TypeName) -> Result { + if let Ok(aligned_upper_bound) = $chunk::::try_from(*p.end() + 1) { + return Ok(Self::new( + $chunk::::try_from(*p.start())?, + aligned_upper_bound - 1, + )); + } else { + return Err("Could not convert 4KiB page range into 2MiB page range."); + } + } + } + impl TryFrom<$TypeName> for $TypeName { + type Error = &'static str; + fn try_from(p: $TypeName) -> Result { + if let Ok(aligned_upper_bound) = $chunk::::try_from(*p.end() + 1) { + return Ok(Self::new( + $chunk::::try_from(*p.start())?, + aligned_upper_bound - 1, + )); + } else { + return Err("Could not convert 4KiB page range into 1GiB page range."); + } + } + } } }; } diff --git a/kernel/memory_structs/src/test.rs b/kernel/memory_structs/src/test.rs new file mode 100644 index 0000000000..134db5ddc0 --- /dev/null +++ b/kernel/memory_structs/src/test.rs @@ -0,0 +1,219 @@ +//! Tests sized variations of core paging types + +extern crate std; + +use super::*; + +#[test] +fn huge_2mb_range_size() { + let r = PageRange::::new( + Page::::containing_address_2mb(VirtualAddress::new(0x200000).unwrap()), + Page::::containing_address_2mb(VirtualAddress::new(0x400000 - 1).unwrap())); + + assert_eq!(r.end().number(), 512); + assert_eq!(r.start().number(), 512); + assert_eq!(r.size_in_pages(), 1); + assert_eq!(r.size_in_bytes(), 2097152); +} + +#[test] +fn huge_2mb_range_size2() { + let r: PageRange = PageRange::::new( + Page::::containing_address_2mb(VirtualAddress::new(0x200000).unwrap()), + Page::::containing_address_2mb(VirtualAddress::new(0x800000 - 1).unwrap())); + + assert_eq!(r.end().number(), 1536); + assert_eq!(r.start().number(), 512); + assert_eq!(r.size_in_pages(), 3); + assert_eq!(r.size_in_bytes(), 6291456); +} + +#[test] +fn huge_1gb_range_size() { + let r = PageRange::::new( + Page::::containing_address_1gb(VirtualAddress::new(0x40000000).unwrap()), + Page::::containing_address_1gb(VirtualAddress::new(0x80000000 - 1).unwrap())); + + assert_eq!(r.end().number(), 262144); + assert_eq!(r.start().number(), 262144); + assert_eq!(r.size_in_pages(), 1); + assert_eq!(r.size_in_bytes(), 1073741824); +} + +#[test] +fn huge_1gb_range_size2() { + let r = PageRange::::new( + Page::::containing_address_1gb(VirtualAddress::new(0x40000000).unwrap()), + Page::::containing_address_1gb(VirtualAddress::new(0x100000000 - 1).unwrap())); + + assert_eq!(r.end().number(), 786432); // 0xc0000000 + assert_eq!(r.start().number(), 262144); + assert_eq!(r.size_in_pages(), 3); + assert_eq!(r.size_in_bytes(), 3221225472); +} + +#[test] +fn huge_2mb_range_iteration1() { + let r = PageRange::::new( + Page::::containing_address_2mb(VirtualAddress::new(0x200000).unwrap()), + Page::::containing_address_2mb(VirtualAddress::new(0x400000 - 1).unwrap())); + let mut num_iters = 0; + for _ in r { + num_iters += 1; + } + assert_eq!(num_iters, 1); +} + +#[test] +fn huge_2mb_range_iteration2() { + let r = PageRange::::new( + Page::::containing_address_2mb(VirtualAddress::new(0x200000).unwrap()), + Page::::containing_address_2mb(VirtualAddress::new(0x800000 - 1).unwrap())); + let mut num_iters = 0; + // assert_eq!(r.start().number, 0x200000); + for _ in r { + num_iters += 1; + } + assert_eq!(num_iters, 3); +} + +#[test] +fn huge_2mb_range_iteration3() { + let r = PageRange::::new( + Page::::containing_address_2mb(VirtualAddress::new(0x200000).unwrap()), + Page::::containing_address_2mb(VirtualAddress::new(0x1000000 - 1).unwrap())); + let mut num_iters = 0; + for _ in r { + num_iters += 1; + } + assert_eq!(num_iters, 7); +} + +#[test] +fn huge_1gb_range_iteration() { + let r = PageRange::::new( + Page::::containing_address_1gb(VirtualAddress::new(0x40000000).unwrap()), + Page::::containing_address_1gb(VirtualAddress::new(0x80000000 - 1).unwrap())); + let mut num_iters = 0; + for _ in r { + num_iters += 1; + } + assert_eq!(num_iters, 1); +} + +#[test] +fn huge_1gb_range_iteration2() { + let r = PageRange::::new( + Page::::containing_address_1gb(VirtualAddress::new(0x40000000).unwrap()), + Page::::containing_address_1gb(VirtualAddress::new(0x100000000 - 1).unwrap())); + assert_eq!(r.size_in_pages(), 3); + + let mut num_iters = 0; + for _ in r { + num_iters += 1; + } + assert_eq!(num_iters, 3); +} + +#[test] +fn huge_1gb_from_4kb() { + let r = PageRange::new( + Page::containing_address(VirtualAddress::new(0x40000000).unwrap()), + Page::containing_address(VirtualAddress::new(0x80000000 - 1).unwrap())); + + + let new1gb = PageRange::::try_from(r).unwrap(); + + assert!(matches!(new1gb.start().page_size(), MemChunkSize::Huge1G)); + + let r = PageRange::new( + Page::containing_address(VirtualAddress::new(0x40000000).unwrap()), + Page::containing_address(VirtualAddress::new(0x42000000).unwrap())); + + let new1gb = PageRange::::try_from(r); + assert!(new1gb.is_err()); +} + +#[test] +fn huge_2gb_from_4kb() { + let r = PageRange::new( + Page::containing_address(VirtualAddress::new(0x200000).unwrap()), + Page::containing_address(VirtualAddress::new(0x800000 - 1).unwrap())); + + let new2mb = PageRange::::try_from(r).unwrap(); // r.into_2mb_range().unwrap(); + assert!(matches!(new2mb.start().page_size(), MemChunkSize::Huge2M)); + + let r = PageRange::new( + Page::containing_address(VirtualAddress::new(0x30000).unwrap()), + Page::containing_address(VirtualAddress::new(0x40000).unwrap())); + + let new2mb = PageRange::::try_from(r); + assert!(new2mb.is_err()); +} + +#[test] +fn standard_sized_from_1gb() { + let r = PageRange::::new( + Page::::containing_address_1gb(VirtualAddress::new(0x40000000).unwrap()), + Page::::containing_address_1gb(VirtualAddress::new(0x48000000 - 1).unwrap())); + + // Compiler needs to the size to be explicitly provided + let converted = PageRange::::from(r); + + assert!(matches!(converted.start().page_size(), MemChunkSize::Normal4K)); +} + +#[test] +fn standard_sized_from_2mb() { + let r = PageRange::::new( + Page::::containing_address_2mb(VirtualAddress::new(0x40000).unwrap()), + Page::::containing_address_2mb(VirtualAddress::new(0x80000 - 1).unwrap())); + + let converted = PageRange::::from(r); + + assert!(matches!(converted.start().page_size(), MemChunkSize::Normal4K)); +} + +#[test] +fn try_from_conversions() { + let r = PageRange::new( + Page::containing_address(VirtualAddress::new(0x200000).unwrap()), + Page::containing_address(VirtualAddress::new(0x800000 - 1).unwrap())); + assert_eq!(r.size_in_pages(), 1536); + + let new2mb = PageRange::::try_from(r).unwrap(); + assert!(matches!(new2mb.start().page_size(), MemChunkSize::Huge2M)); + assert_eq!(new2mb.size_in_pages(), 3); + + let r = PageRange::new( + Page::containing_address(VirtualAddress::new(0x40000000).unwrap()), + Page::containing_address(VirtualAddress::new(0x80000000 - 1).unwrap())); + assert_eq!(r.size_in_pages(), 262144); + + let new1gb = PageRange::::try_from(r).unwrap(); + assert!(matches!(new1gb.start().page_size(), MemChunkSize::Huge1G)); + assert_eq!(new1gb.size_in_pages(), 1); +} + +#[test] +fn test_chunk_addition() { + let page_2mb = Page::::containing_address_2mb(VirtualAddress::new(0x200000).unwrap()); + let page_1gb = Page::::containing_address_1gb(VirtualAddress::new(0x40000000).unwrap()); + + // original page num = 512. 512 + 1 = 1024 (which is the next huge page) + assert_eq!((page_2mb + 1).number(), 1024); + assert_eq!((page_1gb + 1).number(), 524288); + + assert_eq!((page_2mb + 2).number(), 1536); + assert_eq!((page_1gb + 2).number(), 786432); +} + +#[test] +fn test_chunk_subtraction() { + let page_2mb = Page::::containing_address_2mb(VirtualAddress::new(0x400000).unwrap()); + let page_1gb = Page::::containing_address_1gb(VirtualAddress::new(0x80000000).unwrap()); + + // original page num = 512. 512 + 1 = 1024 (which is the next huge page) + assert_eq!((page_2mb - 1).number(), 512); + assert_eq!((page_1gb - 1).number(), 262144); +} \ No newline at end of file