diff --git a/Cargo.lock b/Cargo.lock index 7256286..5b281dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "libsys" -version = "0.3.5" +version = "0.4.0" dependencies = [ "num_enum", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 6e16b38..bed7c86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libsys" -version = "0.3.5" +version = "0.4.0" edition = "2024" [dependencies] diff --git a/src/address/error.rs b/src/address/error.rs deleted file mode 100644 index 42d9d12..0000000 --- a/src/address/error.rs +++ /dev/null @@ -1,25 +0,0 @@ -/// Error indicating that attempting to convert a raw address to a structured -/// `Address` found non-canonical bits. -#[derive(Debug, PartialEq, Eq)] -pub struct NonCanonicalError; - -impl core::error::Error for NonCanonicalError { - fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { - None - } - - #[allow(clippy::unnecessary_literal_bound)] - fn description(&self) -> &str { - "description() is deprecated; use Display" - } - - fn cause(&self) -> Option<&dyn core::error::Error> { - self.source() - } -} - -impl core::fmt::Display for NonCanonicalError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_str("attempt to convert a raw address found non-canonical bits") - } -} diff --git a/src/address/frame.rs b/src/address/frame.rs deleted file mode 100644 index b0b10cb..0000000 --- a/src/address/frame.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::{ - address::{Address, AddressKind, NonCanonicalError, Physical}, - constants::{is_physical_address_canonical, page_bits, page_mask, truncate_physical_address}, -}; - -pub struct Frame; - -impl AddressKind for Frame { - type Repr = usize; -} - -impl Copy for Address {} -impl Clone for Address { - fn clone(&self) -> Self { - *self - } -} - -impl Eq for Address {} -impl PartialEq for Address { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Ord for Address { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.0.cmp(&other.0) - } -} - -impl PartialOrd for Address { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Address { - /// Creates a new [`Address`] with the provided address. - /// - /// # Errors - /// - /// - [`NonCanonicalError`] if `address` contains any non-canonical bits. - pub fn new(address: usize) -> Result { - if ((address & page_mask()) == 0) && is_physical_address_canonical(address) { - Ok(Self(address)) - } else { - Err(NonCanonicalError) - } - } - - /// Creates a new [`Address`] with the provided address, truncating - /// any non-canonical bits. - #[must_use] - pub fn new_truncate(address: usize) -> Self { - Self(truncate_physical_address(address) & !page_mask()) - } - - /// # Safety - /// - /// - `address` must be page-aligned. - /// - `address` must have only canonical physical address bits set. - #[must_use] - pub unsafe fn new_unchecked(address: usize) -> Self { - Self(address) - } - - /// Gets the inner value. - #[must_use] - pub fn get(&self) -> Address { - // Safety: `Address` is a superset of `Address`s canonicality. - unsafe { Address::::new_unchecked(self.0) } - } - - /// Creates a new [`Address`] with the provided frame index. - /// - /// # Errors - /// - /// - [`NonCanonicalError`] if `index` would create a non-canonical address. - pub fn from_index(index: usize) -> Result { - let physical_address = index << page_bits().get(); - - if is_physical_address_canonical(physical_address) { - Ok(Self(physical_address)) - } else { - Err(NonCanonicalError) - } - } - - /// Gets the index of the frame this address points to. - #[must_use] - pub fn index(&self) -> usize { - self.0 >> page_bits().get() - } -} - -impl core::iter::Step for Address { - fn steps_between(start: &Self, end: &Self) -> (usize, Option) { - core::iter::Step::steps_between(&start.index(), &end.index()) - } - - fn forward_checked(start: Self, count: usize) -> Option { - start - .index() - .checked_add(count) - .and_then(|next_index| Self::from_index(next_index).ok()) - } - - fn backward_checked(start: Self, count: usize) -> Option { - start - .index() - .checked_sub(count) - .and_then(|next_index| Self::from_index(next_index).ok()) - } -} - -impl core::fmt::Debug for Address { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("Address").field(&self.0).finish() - } -} - -#[cfg(test)] -mod tests { - use crate::address::{Address, Frame, NonCanonicalError, Physical}; - - #[test] - fn get() { - assert_eq!( - (unsafe { Address::::new_unchecked(0xF000) }).get(), - unsafe { Address::::new_unchecked(0xF000) } - ); - } - - #[test] - fn new() { - assert_eq!( - Address::::new(0xFFFF_0000_0000_F000), - Err(NonCanonicalError) - ); - } - - #[test] - fn new_truncate() { - assert_eq!( - Address::::new_truncate(0xFFF0_0000_0000_F000).get(), - Address::::new_truncate(0xFFF0_0000_0000_F000) - ); - } - - #[test] - fn index() { - assert_eq!( - Address::::from_index(0xF).map(|frame| frame.index()), - Ok(0xF) - ); - } - - #[test] - fn from_index() { - assert_eq!( - Address::::from_index(0xFFF0_0000_0000_F), - Err(NonCanonicalError) - ); - assert_eq!( - Address::::from_index(0xF).map(|address| address.get()), - Ok(unsafe { Address::::new_unchecked(0xF000) }) - ); - } -} diff --git a/src/address/mod.rs b/src/address/mod.rs deleted file mode 100644 index 9d03f1a..0000000 --- a/src/address/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -mod frame; -pub use frame::*; - -mod page; -pub use page::*; - -mod physical; -pub use physical::*; - -mod r#virtual; -pub use r#virtual::*; - -mod error; -pub use error::*; - -#[repr(transparent)] -pub struct Address(T::Repr); - -pub trait AddressKind { - type Repr; -} diff --git a/src/address/page.rs b/src/address/page.rs deleted file mode 100644 index 93bab26..0000000 --- a/src/address/page.rs +++ /dev/null @@ -1,198 +0,0 @@ -use core::ptr::NonNull; - -use crate::{ - address::{Address, AddressKind, NonCanonicalError, Virtual}, - constants::{ - is_virtual_address_canonical, page_bits, page_mask, page_size, truncate_virtual_address, - }, -}; - -pub struct Page; - -impl AddressKind for Page { - type Repr = usize; -} - -impl Copy for Address {} -impl Clone for Address { - fn clone(&self) -> Self { - *self - } -} - -impl Eq for Address {} -impl PartialEq for Address { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Ord for Address { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.0.cmp(&other.0) - } -} - -impl PartialOrd for Address { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Address { - /// Creates a new [`Address`] with the provided address. - /// - /// # Errors - /// - /// - [`NonCanonicalError`] if `address` contains any non-canonical bits. - pub fn new(address: usize) -> Result { - if ((address & page_mask()) == 0) && is_virtual_address_canonical(address) { - Ok(Self(address)) - } else { - Err(NonCanonicalError) - } - } - - /// Creates a new [`Address`] with the provided address, truncating - /// any non-canonical bits. - #[must_use] - pub fn new_truncate(address: usize) -> Self { - Self(truncate_virtual_address(address) & !page_mask()) - } - - /// # Safety - /// - /// - `address` must be page-aligned. - /// - `address` must have only canonical virtual address bits set. - #[must_use] - pub unsafe fn new_unchecked(address: usize) -> Self { - Self(address) - } - - /// Gets the inner value. - #[must_use] - pub fn get(&self) -> Address { - // Safety: `Address` is a superset of `Address`s canonicality. - unsafe { Address::::new_unchecked(self.0) } - } - - /// Creates a new [`Address`] with the provided frame index. - /// - /// # Errors - /// - /// - [`NonCanonicalError`] if `index` would create a non-canonical address. - pub fn from_index(index: usize) -> Result { - let virtual_address = index << page_bits().get(); - - if is_virtual_address_canonical(virtual_address) { - Ok(Self(virtual_address)) - } else { - Err(NonCanonicalError) - } - } - - /// Gets the index of the page this address points to. - #[must_use] - pub fn index(&self) -> usize { - self.0 >> page_bits().get() - } -} - -impl core::iter::Step for Address { - fn steps_between(start: &Self, end: &Self) -> (usize, Option) { - core::iter::Step::steps_between(&start.index(), &end.index()) - } - - fn forward_checked(start: Self, count: usize) -> Option { - start - .index() - .checked_add(count) - .and_then(|next_index| Self::from_index(next_index).ok()) - } - - fn backward_checked(start: Self, count: usize) -> Option { - start - .index() - .checked_sub(count) - .and_then(|next_index| Self::from_index(next_index).ok()) - } -} - -impl TryFrom<*mut T> for Address { - type Error = NonCanonicalError; - - fn try_from(value: *mut T) -> Result { - if value.is_aligned_to(page_size()) { - Ok(Self(value.addr())) - } else { - Err(NonCanonicalError) - } - } -} - -impl TryFrom> for Address { - type Error = NonCanonicalError; - - fn try_from(value: NonNull) -> Result { - if value.is_aligned_to(page_size()) { - Ok(Self(value.addr().get())) - } else { - Err(NonCanonicalError) - } - } -} - -impl core::fmt::Debug for Address { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("Address").field(&self.0).finish() - } -} - -#[cfg(test)] -mod tests { - use crate::address::{Address, NonCanonicalError, Page, Virtual}; - - #[test] - fn get() { - assert_eq!( - (unsafe { Address::::new_unchecked(0xF000) }).get(), - unsafe { Address::::new_unchecked(0xF000) } - ); - } - - #[test] - fn new() { - assert_eq!( - Address::::new(0xFFFF_0000_0000_F000), - Err(NonCanonicalError) - ); - } - - #[test] - fn new_truncate() { - assert_eq!( - Address::::new_truncate(0xFFF0_0000_0000_F000).get(), - Address::::new_truncate(0xFFF0_0000_0000_F000) - ); - } - - #[test] - fn index() { - assert_eq!( - Address::::from_index(0xF).map(|page| page.index()), - Ok(0xF) - ); - } - - #[test] - fn from_index() { - assert_eq!( - Address::::from_index(0xFFF0_0000_0000_F), - Err(NonCanonicalError) - ); - assert_eq!( - Address::::from_index(0xF).map(|address| address.get()), - Ok(unsafe { Address::::new_unchecked(0xF000) }) - ); - } -} diff --git a/src/address/physical.rs b/src/address/physical.rs deleted file mode 100644 index 9ddea11..0000000 --- a/src/address/physical.rs +++ /dev/null @@ -1,112 +0,0 @@ -use crate::{ - address::{Address, AddressKind, NonCanonicalError}, - constants::{is_physical_address_canonical, truncate_physical_address}, -}; - -#[derive(Debug)] -pub struct Physical; - -impl AddressKind for Physical { - type Repr = usize; -} - -impl Copy for Address {} -impl Clone for Address { - fn clone(&self) -> Self { - *self - } -} - -impl Eq for Address {} -impl PartialEq for Address { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Ord for Address { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.0.cmp(&other.0) - } -} - -impl PartialOrd for Address { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Address { - /// Creates a new [`Address`] with the provided address. - /// - /// # Errors - /// - /// - [`NonCanonicalError`] if `address` contains any non-canonical bits. - pub fn new(address: usize) -> Result { - if is_physical_address_canonical(address) { - Ok(Self(address)) - } else { - Err(NonCanonicalError) - } - } - - /// Creates a new [`Address`] with the provided address, truncating - /// any non-canonical bits. - #[must_use] - pub fn new_truncate(address: usize) -> Self { - Self(truncate_physical_address(address)) - } - - /// # Safety - /// - /// - `address` must have only canonical physical address bits set. - #[must_use] - pub unsafe fn new_unchecked(address: usize) -> Self { - Self(address) - } - - /// Gets the inner value. - #[must_use] - pub fn get(&self) -> usize { - self.0 - } -} - -impl core::fmt::Debug for Address { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("Address").field(&self.0).finish() - } -} - -#[cfg(test)] -mod tests { - use crate::address::{Address, NonCanonicalError, Physical}; - - #[test] - fn get() { - assert_eq!( - (unsafe { Address::::new_unchecked(0xF) }).get(), - 0xF - ); - } - - #[test] - fn new() { - assert_eq!( - Address::::new(0xFFF0_0000_0000_000F), - Err(NonCanonicalError) - ); - assert_eq!( - Address::::new(0xF).map(|address| address.get()), - Ok(0xF) - ); - } - - #[test] - fn new_truncate() { - assert_eq!( - Address::::new_truncate(0xFFF0_0000_0000_000F).get(), - 0xF - ); - } -} diff --git a/src/address/virtual.rs b/src/address/virtual.rs deleted file mode 100644 index 47a151a..0000000 --- a/src/address/virtual.rs +++ /dev/null @@ -1,126 +0,0 @@ -use core::ptr::NonNull; - -use crate::{ - address::{Address, AddressKind, NonCanonicalError}, - constants::{is_virtual_address_canonical, truncate_virtual_address}, -}; - -#[derive(Debug)] -pub struct Virtual; - -impl AddressKind for Virtual { - type Repr = usize; -} - -impl Copy for Address {} -impl Clone for Address { - fn clone(&self) -> Self { - *self - } -} - -impl Eq for Address {} -impl PartialEq for Address { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Ord for Address { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.0.cmp(&other.0) - } -} - -impl PartialOrd for Address { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Address { - /// Creates a new [`Address`] with the provided address. - /// - /// # Errors - /// - /// - [`NonCanonicalError`] if `address` contains any non-canonical bits. - pub fn new(address: usize) -> Result { - if is_virtual_address_canonical(address) { - Ok(Self(address)) - } else { - Err(NonCanonicalError) - } - } - - /// Creates a new [`Address`] with the provided address, truncating - /// any non-canonical bits. - #[must_use] - pub fn new_truncate(address: usize) -> Self { - Self(truncate_virtual_address(address)) - } - - /// # Safety - /// - /// - `address` must have only canonical virtual address bits set. - #[must_use] - pub unsafe fn new_unchecked(address: usize) -> Self { - Self(address) - } - - /// Gets the inner value. - #[must_use] - pub fn get(&self) -> usize { - self.0 - } -} - -impl From<*mut T> for Address { - fn from(ptr: *mut T) -> Self { - Self(ptr.addr()) - } -} - -impl From> for Address { - fn from(ptr: NonNull) -> Self { - Self(ptr.addr().get()) - } -} - -impl core::fmt::Debug for Address { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("Address").field(&self.0).finish() - } -} - -#[cfg(test)] -mod tests { - use crate::address::{Address, NonCanonicalError, Virtual}; - - #[test] - fn get() { - assert_eq!( - (unsafe { Address::::new_unchecked(0xF) }).get(), - 0xF - ); - } - - #[test] - fn new() { - assert_eq!( - Address::::new(0xFFF0_0000_0000_000F), - Err(NonCanonicalError) - ); - assert_eq!( - Address::::new(0xF).map(|address| address.get()), - Ok(0xF) - ); - } - - #[test] - fn new_truncate() { - assert_eq!( - Address::::new_truncate(0xFFF0_0000_0000_000F).get(), - 0xF - ); - } -} diff --git a/src/constants/mod.rs b/src/constants/mod.rs deleted file mode 100644 index 4bd2cbd..0000000 --- a/src/constants/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[cfg(target_arch = "x86_64")] -mod x86_64; -#[cfg(target_arch = "x86_64")] -pub use x86_64::*; diff --git a/src/constants/x86_64.rs b/src/constants/x86_64.rs deleted file mode 100644 index e70e662..0000000 --- a/src/constants/x86_64.rs +++ /dev/null @@ -1,164 +0,0 @@ -use core::num::NonZero; - -#[cfg(test)] -#[must_use] -pub fn get_paging_depth() -> NonZero { - // Safety: Value is non-zero. - unsafe { NonZero::new_unchecked(4) } -} - -#[cfg(not(test))] -#[must_use] -pub fn get_paging_depth() -> NonZero { - const CR4_LA57_BIT: usize = 1 << 12; - - let cr4: usize; - - unsafe { - core::arch::asm!( - "mov {}, cr4", - out(reg) cr4, - options(nostack, nomem, preserves_flags) - ); - } - - if (cr4 & CR4_LA57_BIT) == 0 { - // Safety: Value is non-zero. - unsafe { NonZero::new_unchecked(4) } - } else { - // Safety: Value is non-zero. - unsafe { NonZero::new_unchecked(5) } - } -} - -/// Bit shift required to offset page indexes. -#[must_use] -pub const fn page_bits() -> NonZero { - // Safety: Value is non-zero. - unsafe { NonZero::::new_unchecked(12) } -} - -/// Bit shift required to offset large page indexes. -#[must_use] -pub const fn large_page_bits() -> NonZero { - // Safety: Value is non-zero. - unsafe { NonZero::::new_unchecked(page_bits().get() + table_index_bits().get()) } -} - -/// Bit shift required to offset huge page indexes. -#[must_use] -pub const fn huge_page_bits() -> NonZero { - // Safety: Value is non-zero. - unsafe { NonZero::::new_unchecked(large_page_bits().get() + table_index_bits().get()) } -} - -/// The size of a page in bytes. -#[must_use] -pub const fn page_size() -> usize { - 1 << page_bits().get() -} - -/// The size of a large page in bytes. -#[must_use] -pub const fn large_page_size() -> usize { - 1 << large_page_bits().get() -} - -/// The size of a huge page in bytes. -#[must_use] -pub const fn huge_page_size() -> usize { - 1 << huge_page_bits().get() -} - -/// Bit-mask of non-index page bytes. -#[must_use] -pub const fn page_mask() -> usize { - page_size() - 1 -} - -/// Bit-mask of non-index large page bytes. -#[must_use] -pub const fn large_page_mask() -> usize { - large_page_size() - 1 -} - -/// Bit-mask of non-index huge page bytes. -#[must_use] -pub const fn huge_page_mask() -> usize { - huge_page_size() - 1 -} - -/// Shift (in bits) of a page table index. -#[must_use] -pub const fn table_index_bits() -> NonZero { - // Safety: Value is non-zero. - unsafe { NonZero::::new_unchecked(9) } -} - -/// Size (in bytes) of a page table index. -#[must_use] -pub const fn table_index_size() -> usize { - 1 << table_index_bits().get() -} - -/// Bit-mask of a page table index. -#[must_use] -pub const fn table_index_mask() -> usize { - table_index_size() - 1 -} - -/// Number of bits in a canonical physical address. -#[must_use] -pub const fn physical_address_bits() -> NonZero { - // Safety: Value is non-zero. - unsafe { NonZero::::new_unchecked(52) } -} - -/// The maximum physical address size (in bytes). -#[must_use] -pub const fn physical_address_size() -> usize { - 1 << physical_address_bits().get() -} - -/// Bit-mask of canonical physical bits. -#[must_use] -pub const fn physical_address_mask() -> usize { - physical_address_size() - 1 -} - -/// Checks if the provided `physical_address` is canonical. -#[must_use] -pub const fn is_physical_address_canonical(physical_address: usize) -> bool { - (physical_address & !physical_address_mask()) == 0 -} - -/// Bit-shift to reach non-canonical bits of a virtual address. -#[must_use] -pub fn virtual_address_bits() -> NonZero { - let table_indexes_shift = table_index_bits().get() * get_paging_depth().get(); - let total_shift = table_indexes_shift + page_bits().get(); - - // Safety: Operations cannot overflow with the provided constants. - unsafe { NonZero::::new_unchecked(total_shift) } -} - -/// Checks whether a provided address has only the canonical virtual bits. -#[must_use] -pub fn is_virtual_address_canonical(virtual_address: usize) -> bool { - let sign_extension_check_shift = virtual_address_bits().get() - 1; - matches!(virtual_address >> sign_extension_check_shift, 0 | 0x1FFFF) -} - -/// Truncates all non-canonical physical bits from an address. -#[must_use] -pub fn truncate_physical_address(address: usize) -> usize { - address & physical_address_mask() -} - -/// Truncates all non-canonical virtual bits from an address. -#[must_use] -#[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)] -pub fn truncate_virtual_address(address: usize) -> usize { - let sign_extension_shift = usize::BITS - virtual_address_bits().get(); - (((address << sign_extension_shift) as isize) >> sign_extension_shift) as usize -} diff --git a/src/lib.rs b/src/lib.rs index 35b3579..e89e708 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,4 @@ // introduce special types like `uptr` or `ureg` to handle the differing // sizes of the CPU-internal structures. -pub mod address; -pub mod constants; -pub mod math; pub mod syscall; diff --git a/src/math.rs b/src/math.rs deleted file mode 100644 index f70e82c..0000000 --- a/src/math.rs +++ /dev/null @@ -1,58 +0,0 @@ -use core::num::NonZero; - -#[must_use] -pub const fn align_up(value: usize, alignment_bits: NonZero) -> usize { - (value.wrapping_neg() & (1usize << alignment_bits.get()).wrapping_neg()).wrapping_neg() -} - -#[must_use] -pub const fn align_up_div(value: usize, alignment_bits: NonZero) -> usize { - align_up(value, alignment_bits) / (1usize << alignment_bits.get()) -} - -#[must_use] -pub const fn align_down(value: usize, alignment_bits: NonZero) -> usize { - (value >> alignment_bits.get()) << alignment_bits.get() -} - -#[must_use] -pub const fn align_down_div(value: usize, alignment_bits: NonZero) -> usize { - value >> alignment_bits.get() -} - -#[cfg(test)] -mod tests { - use core::num::NonZero; - - #[test] - pub fn align_up() { - assert_eq!( - super::align_up(0xFC, unsafe { NonZero::new_unchecked(4) }), - 0x100 - ); - } - - #[test] - pub fn align_up_div() { - assert_eq!( - super::align_up_div(0xFC, unsafe { NonZero::new_unchecked(4) }), - 0x10 - ); - } - - #[test] - pub fn align_down() { - assert_eq!( - super::align_down(0xFC, unsafe { NonZero::new_unchecked(4) }), - 0xF0 - ); - } - - #[test] - pub fn align_down_div() { - assert_eq!( - super::align_down_div(0xFC, unsafe { NonZero::new_unchecked(4) }), - 0xF - ); - } -}