diff --git a/src/addr.rs b/src/addr.rs index 8afe53f3..6cd0b892 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -239,26 +239,32 @@ impl VirtAddr { PageTableIndex::new_truncate((self.0 >> 12 >> ((level as u8 - 1) * 9)) as u16) } - // FIXME: Move this into the `Step` impl, once `Step` is stabilized. + /// Returns the number of *successor* steps required to get from `start` to `end`. + /// + /// This is simlar to [Step::steps_between] but can not overflow. + /// Returns `None` if `end > start` #[cfg(any(feature = "instructions", feature = "step_trait"))] - pub(crate) fn steps_between_impl(start: &Self, end: &Self) -> Option { + pub fn steps_between_u64(start: &Self, end: &Self) -> Option { let mut steps = end.0.checked_sub(start.0)?; // Mask away extra bits that appear while jumping the gap. steps &= 0xffff_ffff_ffff; - usize::try_from(steps).ok() + Some(steps) } - // FIXME: Move this into the `Step` impl, once `Step` is stabilized. - #[inline] - pub(crate) fn forward_checked_impl(start: Self, count: usize) -> Option { - let offset = u64::try_from(count).ok()?; - if offset > ADDRESS_SPACE_SIZE { + /// Returns the value that would be obtainted by taking the *successor* of + /// `self` `count` times. + /// + /// If this would overflow the range of valid addresses, returns `None`. + /// + /// See [core::iter::Step::forward_checked]. + pub fn forward_checked_u64(start: Self, count: u64) -> Option { + if count > ADDRESS_SPACE_SIZE { return None; } - let mut addr = start.0.checked_add(offset)?; + let mut addr = start.0.checked_add(count)?; match addr.get_bits(47..) { 0x1 => { @@ -360,13 +366,22 @@ impl Sub for VirtAddr { #[cfg(feature = "step_trait")] impl Step for VirtAddr { #[inline] - fn steps_between(start: &Self, end: &Self) -> Option { - Self::steps_between_impl(start, end) + fn steps_between(start: &Self, end: &Self) -> (usize, Option) { + use core::usize; + + let Some(steps) = VirtAddr::steps_between_u64(start, end) else { + return (0, None); + }; + + match usize::try_from(steps) { + Ok(steps) => (steps, Some(steps)), + Err(_) => (usize::MAX, None), + } } #[inline] fn forward_checked(start: Self, count: usize) -> Option { - Self::forward_checked_impl(start, count) + Self::forward_checked_u64(start, count as u64) } #[inline] @@ -721,43 +736,49 @@ mod tests { #[test] #[cfg(feature = "step_trait")] fn virtaddr_steps_between() { - assert_eq!(Step::steps_between(&VirtAddr(0), &VirtAddr(0)), Some(0)); - assert_eq!(Step::steps_between(&VirtAddr(0), &VirtAddr(1)), Some(1)); - assert_eq!(Step::steps_between(&VirtAddr(1), &VirtAddr(0)), None); + assert_eq!( + Step::steps_between(&VirtAddr(0), &VirtAddr(0)), + (0, Some(0)) + ); + assert_eq!( + Step::steps_between(&VirtAddr(0), &VirtAddr(1)), + (1, Some(1)) + ); + assert_eq!(Step::steps_between(&VirtAddr(1), &VirtAddr(0)), (0, None)); assert_eq!( Step::steps_between( &VirtAddr(0x7fff_ffff_ffff), &VirtAddr(0xffff_8000_0000_0000) ), - Some(1) + (1, Some(1)) ); assert_eq!( Step::steps_between( &VirtAddr(0xffff_8000_0000_0000), &VirtAddr(0x7fff_ffff_ffff) ), - None + (0, None) ); assert_eq!( Step::steps_between( &VirtAddr(0xffff_8000_0000_0000), &VirtAddr(0xffff_8000_0000_0000) ), - Some(0) + (0, Some(0)) ); assert_eq!( Step::steps_between( &VirtAddr(0xffff_8000_0000_0000), &VirtAddr(0xffff_8000_0000_0001) ), - Some(1) + (1, Some(1)) ); assert_eq!( Step::steps_between( &VirtAddr(0xffff_8000_0000_0001), &VirtAddr(0xffff_8000_0000_0000) ), - None + (0, None) ); } diff --git a/src/instructions/tlb.rs b/src/instructions/tlb.rs index 3f5c4467..223849d8 100644 --- a/src/instructions/tlb.rs +++ b/src/instructions/tlb.rs @@ -315,14 +315,14 @@ where if let Some(mut pages) = self.page_range { while !pages.is_empty() { // Calculate out how many pages we still need to flush. - let count = Page::::steps_between_impl(&pages.start, &pages.end).unwrap(); + let count = Page::::steps_between_u64(&pages.start, &pages.end).unwrap(); // Make sure that we never jump the gap in the address space when flushing. let second_half_start = Page::::containing_address(VirtAddr::new(0xffff_8000_0000_0000)); let count = if pages.start < second_half_start { let count_to_second_half = - Page::steps_between_impl(&pages.start, &second_half_start).unwrap(); + Page::steps_between_u64(&pages.start, &second_half_start).unwrap(); cmp::min(count, count_to_second_half) } else { count @@ -348,8 +348,7 @@ where // Even if the count is zero, one page is still flushed and so // we need to advance by at least one. let inc_count = cmp::max(count, 1); - pages.start = - Page::forward_checked_impl(pages.start, usize::from(inc_count)).unwrap(); + pages.start = Page::forward_checked_u64(pages.start, u64::from(inc_count)).unwrap(); } } else { unsafe { diff --git a/src/structures/paging/mapper/mapped_page_table.rs b/src/structures/paging/mapper/mapped_page_table.rs index b50f072e..e246a6b1 100644 --- a/src/structures/paging/mapper/mapped_page_table.rs +++ b/src/structures/paging/mapper/mapped_page_table.rs @@ -646,11 +646,9 @@ impl CleanUp for MappedPageTable<'_, P> { .skip(usize::from(start)) { if let Ok(page_table) = page_table_walker.next_table_mut(entry) { - let start = VirtAddr::forward_checked_impl( - table_addr, - (offset_per_entry as usize) * i, - ) - .unwrap(); + let start = + VirtAddr::forward_checked_u64(table_addr, offset_per_entry * i as u64) + .unwrap(); let end = start + (offset_per_entry - 1); let start = Page::::containing_address(start); let start = start.max(range.start); diff --git a/src/structures/paging/mapper/recursive_page_table.rs b/src/structures/paging/mapper/recursive_page_table.rs index ff427ffa..599e7f98 100644 --- a/src/structures/paging/mapper/recursive_page_table.rs +++ b/src/structures/paging/mapper/recursive_page_table.rs @@ -891,11 +891,9 @@ impl CleanUp for RecursivePageTable<'_> { }) { if let Ok(frame) = entry.frame() { - let start = VirtAddr::forward_checked_impl( - table_addr, - (offset_per_entry as usize) * i, - ) - .unwrap(); + let start = + VirtAddr::forward_checked_u64(table_addr, offset_per_entry * i as u64) + .unwrap(); let end = start + (offset_per_entry - 1); let start = Page::::containing_address(start); let start = start.max(range.start); diff --git a/src/structures/paging/page.rs b/src/structures/paging/page.rs index 55720cf4..feef3f16 100644 --- a/src/structures/paging/page.rs +++ b/src/structures/paging/page.rs @@ -158,18 +158,27 @@ impl Page { PageRangeInclusive { start, end } } - // FIXME: Move this into the `Step` impl, once `Step` is stabilized. + /// Returns the number of *successor* steps required to get from `start` to `end`. + /// + /// This is simlar to [Step::steps_between] but can not overflow. + /// Returns `None` if `end > start` #[cfg(any(feature = "instructions", feature = "step_trait"))] - pub(crate) fn steps_between_impl(start: &Self, end: &Self) -> Option { - VirtAddr::steps_between_impl(&start.start_address, &end.start_address) - .map(|steps| steps / S::SIZE as usize) + pub(crate) fn steps_between_u64(start: &Self, end: &Self) -> Option { + let steps = VirtAddr::steps_between_u64(&start.start_address, &end.start_address)?; + + Some(steps / S::SIZE) } - // FIXME: Move this into the `Step` impl, once `Step` is stabilized. + /// Returns the value that would be obtainted by taking the *successor* of + /// `self` `count` times. + /// + /// If this would overflow the range of valid addresses, returns `None`. + /// + /// See [core::iter::Step::forward_checked]. #[cfg(any(feature = "instructions", feature = "step_trait"))] - pub(crate) fn forward_checked_impl(start: Self, count: usize) -> Option { - let count = count.checked_mul(S::SIZE as usize)?; - let start_address = VirtAddr::forward_checked_impl(start.start_address, count)?; + pub(crate) fn forward_checked_u64(start: Self, count: u64) -> Option { + let count = count.checked_mul(S::SIZE)?; + let start_address = VirtAddr::forward_checked_u64(start.start_address, count)?; Some(Self { start_address, size: PhantomData, @@ -293,12 +302,22 @@ impl Sub for Page { #[cfg(feature = "step_trait")] impl Step for Page { - fn steps_between(start: &Self, end: &Self) -> Option { - Self::steps_between_impl(start, end) + fn steps_between(start: &Self, end: &Self) -> (usize, Option) { + use core::convert::TryFrom; + use core::usize; + + let Some(steps) = Self::steps_between_u64(start, end) else { + return (0, None); + }; + + match usize::try_from(steps) { + Ok(steps) => (steps, Some(steps)), + Err(_) => (usize::MAX, None), + } } fn forward_checked(start: Self, count: usize) -> Option { - Self::forward_checked_impl(start, count) + Self::forward_checked_u64(start, count as u64) } fn backward_checked(start: Self, count: usize) -> Option { diff --git a/src/structures/paging/page_table.rs b/src/structures/paging/page_table.rs index fe9ebde0..01ecfbbb 100644 --- a/src/structures/paging/page_table.rs +++ b/src/structures/paging/page_table.rs @@ -353,8 +353,12 @@ impl From for usize { #[cfg(feature = "step_trait")] impl Step for PageTableIndex { #[inline] - fn steps_between(start: &Self, end: &Self) -> Option { - end.0.checked_sub(start.0).map(usize::from) + fn steps_between(start: &Self, end: &Self) -> (usize, Option) { + end.0 + .checked_sub(start.0) + .map(usize::from) + .map(|steps| (steps, Some(steps))) + .unwrap_or((0, None)) } #[inline]