Skip to content

Commit 33b4c2d

Browse files
authored
Merge pull request #342 from Freax13/step-virt-addr-and-page
implement `core::iter::Step` for `VirtAddr` and `Page`
2 parents 9112251 + fd8f2f1 commit 33b4c2d

File tree

4 files changed

+212
-1
lines changed

4 files changed

+212
-1
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ volatile = "0.4.4"
3333
[features]
3434
default = [ "nightly", "instructions" ]
3535
instructions = []
36-
nightly = [ "const_fn", "abi_x86_interrupt", "doc_cfg" ]
36+
nightly = [ "const_fn", "step_trait", "abi_x86_interrupt", "doc_cfg" ]
3737
abi_x86_interrupt = []
3838
const_fn = []
39+
step_trait = []
3940
doc_cfg = []
4041

4142
# These features are no longer used and only there for backwards compatibility.

src/addr.rs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
//! Physical and virtual addresses manipulation
22
3+
#[cfg(feature = "step_trait")]
4+
use core::convert::TryFrom;
35
use core::fmt;
6+
#[cfg(feature = "step_trait")]
7+
use core::iter::Step;
48
use core::ops::{Add, AddAssign, Sub, SubAssign};
59

610
use crate::structures::paging::page_table::PageTableLevel;
711
use crate::structures::paging::{PageOffset, PageTableIndex};
812
use bit_field::BitField;
913

14+
#[cfg(feature = "step_trait")]
15+
const ADDRESS_SPACE_SIZE: u64 = 0x1_0000_0000_0000;
16+
1017
/// A canonical 64-bit virtual memory address.
1118
///
1219
/// This is a wrapper type around an `u64`, so it is always 8 bytes, even when compiled
@@ -331,6 +338,66 @@ impl Sub<VirtAddr> for VirtAddr {
331338
}
332339
}
333340

341+
#[cfg(feature = "step_trait")]
342+
impl Step for VirtAddr {
343+
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
344+
let mut steps = end.0.checked_sub(start.0)?;
345+
346+
// Check if we jumped the gap.
347+
if end.0.get_bit(47) && !start.0.get_bit(47) {
348+
steps = steps.checked_sub(0xffff_0000_0000_0000).unwrap();
349+
}
350+
351+
usize::try_from(steps).ok()
352+
}
353+
354+
fn forward_checked(start: Self, count: usize) -> Option<Self> {
355+
let offset = u64::try_from(count).ok()?;
356+
if offset > ADDRESS_SPACE_SIZE {
357+
return None;
358+
}
359+
360+
let mut addr = start.0.checked_add(offset)?;
361+
362+
match addr.get_bits(47..) {
363+
0x1 => {
364+
// Jump the gap by sign extending the 47th bit.
365+
addr.set_bits(47.., 0x1ffff);
366+
}
367+
0x2 => {
368+
// Address overflow
369+
return None;
370+
}
371+
_ => {}
372+
}
373+
374+
Some(Self::new(addr))
375+
}
376+
377+
fn backward_checked(start: Self, count: usize) -> Option<Self> {
378+
let offset = u64::try_from(count).ok()?;
379+
if offset > ADDRESS_SPACE_SIZE {
380+
return None;
381+
}
382+
383+
let mut addr = start.0.checked_sub(offset)?;
384+
385+
match addr.get_bits(47..) {
386+
0x1fffe => {
387+
// Jump the gap by sign extending the 47th bit.
388+
addr.set_bits(47.., 0);
389+
}
390+
0x1fffd => {
391+
// Address underflow
392+
return None;
393+
}
394+
_ => {}
395+
}
396+
397+
Some(Self::new(addr))
398+
}
399+
}
400+
334401
/// A passed `u64` was not a valid physical address.
335402
///
336403
/// This means that bits 52 to 64 were not all null.
@@ -595,6 +662,120 @@ mod tests {
595662
assert_eq!(VirtAddr::new_truncate(123 << 47), VirtAddr(0xfffff << 47));
596663
}
597664

665+
#[test]
666+
#[cfg(feature = "step_trait")]
667+
fn virtaddr_step_forward() {
668+
assert_eq!(Step::forward(VirtAddr(0), 0), VirtAddr(0));
669+
assert_eq!(Step::forward(VirtAddr(0), 1), VirtAddr(1));
670+
assert_eq!(
671+
Step::forward(VirtAddr(0x7fff_ffff_ffff), 1),
672+
VirtAddr(0xffff_8000_0000_0000)
673+
);
674+
assert_eq!(
675+
Step::forward(VirtAddr(0xffff_8000_0000_0000), 1),
676+
VirtAddr(0xffff_8000_0000_0001)
677+
);
678+
assert_eq!(
679+
Step::forward_checked(VirtAddr(0xffff_ffff_ffff_ffff), 1),
680+
None
681+
);
682+
assert_eq!(
683+
Step::forward(VirtAddr(0x7fff_ffff_ffff), 0x1234_5678_9abd),
684+
VirtAddr(0xffff_9234_5678_9abc)
685+
);
686+
assert_eq!(
687+
Step::forward(VirtAddr(0x7fff_ffff_ffff), 0x8000_0000_0000),
688+
VirtAddr(0xffff_ffff_ffff_ffff)
689+
);
690+
assert_eq!(
691+
Step::forward(VirtAddr(0x7fff_ffff_ff00), 0x8000_0000_00ff),
692+
VirtAddr(0xffff_ffff_ffff_ffff)
693+
);
694+
assert_eq!(
695+
Step::forward_checked(VirtAddr(0x7fff_ffff_ff00), 0x8000_0000_0100),
696+
None
697+
);
698+
assert_eq!(
699+
Step::forward_checked(VirtAddr(0x7fff_ffff_ffff), 0x8000_0000_0001),
700+
None
701+
);
702+
}
703+
704+
#[test]
705+
#[cfg(feature = "step_trait")]
706+
fn virtaddr_step_backward() {
707+
assert_eq!(Step::backward(VirtAddr(0), 0), VirtAddr(0));
708+
assert_eq!(Step::backward_checked(VirtAddr(0), 1), None);
709+
assert_eq!(Step::backward(VirtAddr(1), 1), VirtAddr(0));
710+
assert_eq!(
711+
Step::backward(VirtAddr(0xffff_8000_0000_0000), 1),
712+
VirtAddr(0x7fff_ffff_ffff)
713+
);
714+
assert_eq!(
715+
Step::backward(VirtAddr(0xffff_8000_0000_0001), 1),
716+
VirtAddr(0xffff_8000_0000_0000)
717+
);
718+
assert_eq!(
719+
Step::backward(VirtAddr(0xffff_9234_5678_9abc), 0x1234_5678_9abd),
720+
VirtAddr(0x7fff_ffff_ffff)
721+
);
722+
assert_eq!(
723+
Step::backward(VirtAddr(0xffff_8000_0000_0000), 0x8000_0000_0000),
724+
VirtAddr(0)
725+
);
726+
assert_eq!(
727+
Step::backward(VirtAddr(0xffff_8000_0000_0000), 0x7fff_ffff_ff01),
728+
VirtAddr(0xff)
729+
);
730+
assert_eq!(
731+
Step::backward_checked(VirtAddr(0xffff_8000_0000_0000), 0x8000_0000_0001),
732+
None
733+
);
734+
}
735+
736+
#[test]
737+
#[cfg(feature = "step_trait")]
738+
fn virtaddr_steps_between() {
739+
assert_eq!(Step::steps_between(&VirtAddr(0), &VirtAddr(0)), Some(0));
740+
assert_eq!(Step::steps_between(&VirtAddr(0), &VirtAddr(1)), Some(1));
741+
assert_eq!(Step::steps_between(&VirtAddr(1), &VirtAddr(0)), None);
742+
assert_eq!(
743+
Step::steps_between(
744+
&VirtAddr(0x7fff_ffff_ffff),
745+
&VirtAddr(0xffff_8000_0000_0000)
746+
),
747+
Some(1)
748+
);
749+
assert_eq!(
750+
Step::steps_between(
751+
&VirtAddr(0xffff_8000_0000_0000),
752+
&VirtAddr(0x7fff_ffff_ffff)
753+
),
754+
None
755+
);
756+
assert_eq!(
757+
Step::steps_between(
758+
&VirtAddr(0xffff_8000_0000_0000),
759+
&VirtAddr(0xffff_8000_0000_0000)
760+
),
761+
Some(0)
762+
);
763+
assert_eq!(
764+
Step::steps_between(
765+
&VirtAddr(0xffff_8000_0000_0000),
766+
&VirtAddr(0xffff_8000_0000_0001)
767+
),
768+
Some(1)
769+
);
770+
assert_eq!(
771+
Step::steps_between(
772+
&VirtAddr(0xffff_8000_0000_0001),
773+
&VirtAddr(0xffff_8000_0000_0000)
774+
),
775+
None
776+
);
777+
}
778+
598779
#[test]
599780
pub fn test_align_up() {
600781
// align 1

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#![cfg_attr(feature = "const_fn", feature(const_fn_fn_ptr_basics))] // IDT new()
77
#![cfg_attr(feature = "const_fn", feature(const_fn_trait_bound))] // PageSize marker trait
88
#![cfg_attr(feature = "abi_x86_interrupt", feature(abi_x86_interrupt))]
9+
#![cfg_attr(feature = "step_trait", feature(step_trait))]
910
#![cfg_attr(feature = "doc_cfg", feature(doc_cfg))]
1011
#![warn(missing_docs)]
1112
#![deny(missing_debug_implementations)]

src/structures/paging/page.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use crate::structures::paging::page_table::PageTableLevel;
44
use crate::structures::paging::PageTableIndex;
55
use crate::VirtAddr;
66
use core::fmt;
7+
#[cfg(feature = "step_trait")]
8+
use core::iter::Step;
79
use core::marker::PhantomData;
810
use core::ops::{Add, AddAssign, Sub, SubAssign};
911

@@ -274,6 +276,32 @@ impl<S: PageSize> Sub<Self> for Page<S> {
274276
}
275277
}
276278

279+
#[cfg(feature = "step_trait")]
280+
impl<S: PageSize> Step for Page<S> {
281+
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
282+
Step::steps_between(&start.start_address, &end.start_address)
283+
.map(|steps| steps / S::SIZE as usize)
284+
}
285+
286+
fn forward_checked(start: Self, count: usize) -> Option<Self> {
287+
let count = count.checked_mul(S::SIZE as usize)?;
288+
let start_address = Step::forward_checked(start.start_address, count)?;
289+
Some(Self {
290+
start_address,
291+
size: PhantomData,
292+
})
293+
}
294+
295+
fn backward_checked(start: Self, count: usize) -> Option<Self> {
296+
let count = count.checked_mul(S::SIZE as usize)?;
297+
let start_address = Step::backward_checked(start.start_address, count)?;
298+
Some(Self {
299+
start_address,
300+
size: PhantomData,
301+
})
302+
}
303+
}
304+
277305
/// A range of pages with exclusive upper bound.
278306
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
279307
#[repr(C)]

0 commit comments

Comments
 (0)