Skip to content

Commit

Permalink
Merge branch 'master' into next
Browse files Browse the repository at this point in the history
Signed-off-by: Joe Richey <joerichey@google.com>
  • Loading branch information
josephlr committed Mar 26, 2022
2 parents 2da1eb6 + 2c9a807 commit 89662dc
Show file tree
Hide file tree
Showing 8 changed files with 536 additions and 213 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,16 @@ edition = "2018"
bit_field = "0.10.1"
bitflags = "1.3.2"
volatile = "0.4.4"
rustversion = "1.0.5"

[features]
default = [ "nightly", "instructions" ]
instructions = []
nightly = [ "const_fn", "abi_x86_interrupt", "asm_const", "doc_cfg" ]
nightly = [ "const_fn", "step_trait", "abi_x86_interrupt", "asm_const", "doc_cfg" ]
abi_x86_interrupt = []
const_fn = []
asm_const = []
step_trait = []
doc_cfg = []

[package.metadata.release]
Expand Down
213 changes: 205 additions & 8 deletions src/addr.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
//! Physical and virtual addresses manipulation
#[cfg(feature = "step_trait")]
use core::convert::TryFrom;
use core::fmt;
#[cfg(feature = "step_trait")]
use core::iter::Step;
use core::ops::{Add, AddAssign, Sub, SubAssign};

use crate::structures::paging::page_table::PageTableLevel;
use crate::structures::paging::{PageOffset, PageTableIndex};
use bit_field::BitField;

#[cfg(feature = "step_trait")]
const ADDRESS_SPACE_SIZE: u64 = 0x1_0000_0000_0000;

/// A canonical 64-bit virtual memory address.
///
/// This is a wrapper type around an `u64`, so it is always 8 bytes, even when compiled
Expand Down Expand Up @@ -40,9 +47,18 @@ pub struct PhysAddr(u64);
/// a valid sign extension and are not null either. So automatic sign extension would have
/// overwritten possibly meaningful bits. This likely indicates a bug, for example an invalid
/// address calculation.
#[derive(Debug)]
///
/// Contains the invalid address.
pub struct VirtAddrNotValid(pub u64);

impl core::fmt::Debug for VirtAddrNotValid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("VirtAddrNotValid")
.field(&format_args!("{:#x}", self.0))
.finish()
}
}

impl VirtAddr {
/// Creates a new canonical virtual address.
///
Expand Down Expand Up @@ -70,7 +86,7 @@ impl VirtAddr {
match addr.get_bits(47..64) {
0 | 0x1ffff => Ok(VirtAddr(addr)), // address is canonical
1 => Ok(VirtAddr::new_truncate(addr)), // address needs sign extension
other => Err(VirtAddrNotValid(other)),
_ => Err(VirtAddrNotValid(addr)),
}
}

Expand Down Expand Up @@ -322,12 +338,81 @@ impl Sub<VirtAddr> for VirtAddr {
}
}

#[cfg(feature = "step_trait")]
impl Step for VirtAddr {
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
let mut steps = end.0.checked_sub(start.0)?;

// Check if we jumped the gap.
if end.0.get_bit(47) && !start.0.get_bit(47) {
steps = steps.checked_sub(0xffff_0000_0000_0000).unwrap();
}

usize::try_from(steps).ok()
}

fn forward_checked(start: Self, count: usize) -> Option<Self> {
let offset = u64::try_from(count).ok()?;
if offset > ADDRESS_SPACE_SIZE {
return None;
}

let mut addr = start.0.checked_add(offset)?;

match addr.get_bits(47..) {
0x1 => {
// Jump the gap by sign extending the 47th bit.
addr.set_bits(47.., 0x1ffff);
}
0x2 => {
// Address overflow
return None;
}
_ => {}
}

Some(Self::new(addr))
}

fn backward_checked(start: Self, count: usize) -> Option<Self> {
let offset = u64::try_from(count).ok()?;
if offset > ADDRESS_SPACE_SIZE {
return None;
}

let mut addr = start.0.checked_sub(offset)?;

match addr.get_bits(47..) {
0x1fffe => {
// Jump the gap by sign extending the 47th bit.
addr.set_bits(47.., 0);
}
0x1fffd => {
// Address underflow
return None;
}
_ => {}
}

Some(Self::new(addr))
}
}

/// A passed `u64` was not a valid physical address.
///
/// This means that bits 52 to 64 were not all null.
#[derive(Debug)]
///
/// Contains the invalid address.
pub struct PhysAddrNotValid(pub u64);

impl core::fmt::Debug for PhysAddrNotValid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PhysAddrNotValid")
.field(&format_args!("{:#x}", self.0))
.finish()
}
}

impl PhysAddr {
/// Creates a new physical address.
///
Expand Down Expand Up @@ -367,7 +452,7 @@ impl PhysAddr {
pub fn try_new(addr: u64) -> Result<PhysAddr, PhysAddrNotValid> {
match addr.get_bits(52..64) {
0 => Ok(PhysAddr(addr)), // address is valid
other => Err(PhysAddrNotValid(other)),
_ => Err(PhysAddrNotValid(addr)),
}
}

Expand Down Expand Up @@ -540,8 +625,7 @@ impl Sub<PhysAddr> for PhysAddr {
///
/// Returns the greatest `x` with alignment `align` so that `x <= addr`.
///
/// Panics if the alignment is not a power of two. Without the `const_fn`
/// feature, the panic message will be "index out of bounds".
/// Panics if the alignment is not a power of two.
#[inline]
pub const fn align_down(addr: u64, align: u64) -> u64 {
assert!(align.is_power_of_two(), "`align` must be a power of two");
Expand All @@ -552,8 +636,7 @@ pub const fn align_down(addr: u64, align: u64) -> u64 {
///
/// Returns the smallest `x` with alignment `align` so that `x >= addr`.
///
/// Panics if the alignment is not a power of two. Without the `const_fn`
/// feature, the panic message will be "index out of bounds".
/// Panics if the alignment is not a power of two.
#[inline]
pub const fn align_up(addr: u64, align: u64) -> u64 {
assert!(align.is_power_of_two(), "`align` must be a power of two");
Expand All @@ -577,6 +660,120 @@ mod tests {
assert_eq!(VirtAddr::new_truncate(123 << 47), VirtAddr(0xfffff << 47));
}

#[test]
#[cfg(feature = "step_trait")]
fn virtaddr_step_forward() {
assert_eq!(Step::forward(VirtAddr(0), 0), VirtAddr(0));
assert_eq!(Step::forward(VirtAddr(0), 1), VirtAddr(1));
assert_eq!(
Step::forward(VirtAddr(0x7fff_ffff_ffff), 1),
VirtAddr(0xffff_8000_0000_0000)
);
assert_eq!(
Step::forward(VirtAddr(0xffff_8000_0000_0000), 1),
VirtAddr(0xffff_8000_0000_0001)
);
assert_eq!(
Step::forward_checked(VirtAddr(0xffff_ffff_ffff_ffff), 1),
None
);
assert_eq!(
Step::forward(VirtAddr(0x7fff_ffff_ffff), 0x1234_5678_9abd),
VirtAddr(0xffff_9234_5678_9abc)
);
assert_eq!(
Step::forward(VirtAddr(0x7fff_ffff_ffff), 0x8000_0000_0000),
VirtAddr(0xffff_ffff_ffff_ffff)
);
assert_eq!(
Step::forward(VirtAddr(0x7fff_ffff_ff00), 0x8000_0000_00ff),
VirtAddr(0xffff_ffff_ffff_ffff)
);
assert_eq!(
Step::forward_checked(VirtAddr(0x7fff_ffff_ff00), 0x8000_0000_0100),
None
);
assert_eq!(
Step::forward_checked(VirtAddr(0x7fff_ffff_ffff), 0x8000_0000_0001),
None
);
}

#[test]
#[cfg(feature = "step_trait")]
fn virtaddr_step_backward() {
assert_eq!(Step::backward(VirtAddr(0), 0), VirtAddr(0));
assert_eq!(Step::backward_checked(VirtAddr(0), 1), None);
assert_eq!(Step::backward(VirtAddr(1), 1), VirtAddr(0));
assert_eq!(
Step::backward(VirtAddr(0xffff_8000_0000_0000), 1),
VirtAddr(0x7fff_ffff_ffff)
);
assert_eq!(
Step::backward(VirtAddr(0xffff_8000_0000_0001), 1),
VirtAddr(0xffff_8000_0000_0000)
);
assert_eq!(
Step::backward(VirtAddr(0xffff_9234_5678_9abc), 0x1234_5678_9abd),
VirtAddr(0x7fff_ffff_ffff)
);
assert_eq!(
Step::backward(VirtAddr(0xffff_8000_0000_0000), 0x8000_0000_0000),
VirtAddr(0)
);
assert_eq!(
Step::backward(VirtAddr(0xffff_8000_0000_0000), 0x7fff_ffff_ff01),
VirtAddr(0xff)
);
assert_eq!(
Step::backward_checked(VirtAddr(0xffff_8000_0000_0000), 0x8000_0000_0001),
None
);
}

#[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(0x7fff_ffff_ffff),
&VirtAddr(0xffff_8000_0000_0000)
),
Some(1)
);
assert_eq!(
Step::steps_between(
&VirtAddr(0xffff_8000_0000_0000),
&VirtAddr(0x7fff_ffff_ffff)
),
None
);
assert_eq!(
Step::steps_between(
&VirtAddr(0xffff_8000_0000_0000),
&VirtAddr(0xffff_8000_0000_0000)
),
Some(0)
);
assert_eq!(
Step::steps_between(
&VirtAddr(0xffff_8000_0000_0000),
&VirtAddr(0xffff_8000_0000_0001)
),
Some(1)
);
assert_eq!(
Step::steps_between(
&VirtAddr(0xffff_8000_0000_0001),
&VirtAddr(0xffff_8000_0000_0000)
),
None
);
}

#[test]
pub fn test_align_up() {
// align 1
Expand Down
34 changes: 1 addition & 33 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
#![cfg_attr(not(test), no_std)]
#![cfg_attr(feature = "const_fn", feature(const_mut_refs))] // GDT add_entry()
#![cfg_attr(feature = "const_fn", feature(const_fn_fn_ptr_basics))] // IDT new()
#![cfg_attr(feature = "const_fn", feature(const_fn_trait_bound))] // PageSize marker trait
#![cfg_attr(feature = "asm_const", feature(asm_const))]
#![cfg_attr(feature = "abi_x86_interrupt", feature(abi_x86_interrupt))]
#![cfg_attr(feature = "step_trait", feature(step_trait))]
#![cfg_attr(feature = "doc_cfg", feature(doc_cfg))]
#![warn(missing_docs)]
#![deny(missing_debug_implementations)]
Expand All @@ -17,37 +16,6 @@ use core::sync::atomic::{AtomicBool, Ordering};

pub use crate::addr::{align_down, align_up, PhysAddr, VirtAddr};

/// Makes a function const only when `feature = "const_fn"` is enabled.
///
/// This is needed for const functions with bounds on their generic parameters,
/// such as those in `Page` and `PhysFrame` and many more.
macro_rules! const_fn {
(
$(#[$attr:meta])*
$sv:vis fn $($fn:tt)*
) => {
$(#[$attr])*
#[cfg(feature = "const_fn")]
$sv const fn $($fn)*

$(#[$attr])*
#[cfg(not(feature = "const_fn"))]
$sv fn $($fn)*
};
(
$(#[$attr:meta])*
$sv:vis unsafe fn $($fn:tt)*
) => {
$(#[$attr])*
#[cfg(feature = "const_fn")]
$sv const unsafe fn $($fn)*

$(#[$attr])*
#[cfg(not(feature = "const_fn"))]
$sv unsafe fn $($fn)*
};
}

pub mod addr;
pub mod instructions;
pub mod registers;
Expand Down
Loading

0 comments on commit 89662dc

Please sign in to comment.