Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge branch 'master' into next #356

Merged
merged 37 commits into from
Mar 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
204e6e3
Update changelog for #334 and #336
phil-opp Feb 3, 2022
5c4f5ee
Bump version to 0.14.8
phil-opp Feb 3, 2022
fc356f8
Merge pull request #339 from rust-osdev/release
phil-opp Feb 3, 2022
760fa3d
make fields public
Freax13 Feb 5, 2022
3d32678
Merge pull request #340 from Freax13/expose-fields-in-errors
phil-opp Feb 5, 2022
a143355
Remove external assembly (#343)
Freax13 Feb 25, 2022
3680907
implement `Step` for `VirtAddr`
Freax13 Feb 12, 2022
6f891df
impl `Step` for `Page`
Freax13 Feb 12, 2022
7916566
fix very large steps
Freax13 Mar 1, 2022
3e1ed63
Add some more test cases
phil-opp Mar 1, 2022
e2b5d01
Use `checked_sub` for `steps_between` implementation
phil-opp Mar 1, 2022
1602cfd
Use `Self::new` in `Step` methods as a safeguard
phil-opp Mar 1, 2022
2909487
Fix formatting
phil-opp Mar 1, 2022
b40eac1
Fix: `VirtAddrNotValid` and `PhysAddrNotValid` should contain the who…
phil-opp Mar 1, 2022
b644c2c
Format addresses as hexadecimal for VirtAddr/PhysAddr error types
phil-opp Mar 1, 2022
9bef8f0
fix overflow/underflow checks
Freax13 Mar 1, 2022
fd8f2f1
fix warning
Freax13 Mar 1, 2022
9112251
Merge pull request #347 from rust-osdev/fix-virtaddrnotvalid
phil-opp Mar 1, 2022
33b4c2d
Merge pull request #342 from Freax13/step-virt-addr-and-page
Freax13 Mar 1, 2022
efd7f00
Fixed overflow bug in PageRangeInclusive
drzewiec Mar 18, 2022
6c1d779
Added test to cover ranges ending in the max page
drzewiec Mar 18, 2022
d75d170
Updated name of PageRangeInclusive overflow test
drzewiec Mar 18, 2022
8ac2487
Merge pull request #351 from drzewiec/pagerangeinclusive_fix
Freax13 Mar 18, 2022
34d0615
feat: support for IA32_U_CET and IA32_S_CET
Mar 14, 2022
8bec7be
Merge pull request #349 from jarkkojs/feat/ucet
Freax13 Mar 19, 2022
e6ff36d
const_fn: Remove stablized features
josephlr Mar 24, 2022
0e1587b
Merge pull request #352 from rust-osdev/const_features
josephlr Mar 24, 2022
180a71c
Remove inaccurate comment about const_fn
josephlr Mar 24, 2022
9378dd3
Remove references to deprecated external_asm feature
josephlr Mar 24, 2022
12fd073
Use rustversion to conditionally make methods const
josephlr Mar 24, 2022
e74adfe
Remove const_fn macro
josephlr Mar 24, 2022
b87ecd0
Make `Entry::handler_addr()` a public method.
kevinaboos Mar 24, 2022
27032e3
Merge pull request #353 from rust-osdev/rustversion
josephlr Mar 24, 2022
6cb2ff7
Return a `VirtAddr` from `Entry::handler_addr()`.
kevinaboos Mar 24, 2022
e6486ea
Add comment explaining why calling new-truncate is fine
josephlr Mar 25, 2022
2c9a807
Merge pull request #354 from kevinaboos/make_handler_addr_pub
josephlr Mar 25, 2022
89662dc
Merge branch 'master' into next
josephlr Mar 26, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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