use align_address::Align;
use core::fmt;
#[cfg(feature = "conv-x86")]
use x86::bits64::paging::{PAddr as x86_PAddr, VAddr as x86_VAddr};
use crate::impl_address;
use x86_64::structures::paging::page_table::PageTableLevel;
use x86_64::structures::paging::{PageOffset, PageTableIndex};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(transparent)]
pub struct VirtAddr(u64);
impl_address!(VirtAddr, u64, as_u64);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(transparent)]
pub struct PhysAddr(u64);
impl_address!(PhysAddr, u64, as_u64);
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 {
#[inline]
pub const fn new(addr: u64) -> VirtAddr {
match Self::try_new(addr) {
Ok(v) => v,
Err(_) => panic!("virtual address must be sign extended in bits 48 to 64"),
}
}
#[inline]
pub const fn try_new(addr: u64) -> Result<VirtAddr, VirtAddrNotValid> {
let v = Self::new_truncate(addr);
if v.0 == addr {
Ok(v)
} else {
Err(VirtAddrNotValid(addr))
}
}
#[inline]
pub const fn new_truncate(addr: u64) -> VirtAddr {
VirtAddr(((addr << 16) as i64 >> 16) as u64)
}
#[cfg(target_pointer_width = "64")]
#[inline]
pub fn from_ptr<T: ?Sized>(ptr: *const T) -> Self {
Self::new(ptr as *const () as u64)
}
#[cfg(target_pointer_width = "64")]
#[inline]
pub const fn as_ptr<T>(self) -> *const T {
self.as_u64() as *const T
}
#[cfg(target_pointer_width = "64")]
#[inline]
pub const fn as_mut_ptr<T>(self) -> *mut T {
self.as_ptr::<T>() as *mut T
}
#[cfg(target_pointer_width = "64")]
pub const fn as_usize(&self) -> usize {
self.0 as usize
}
#[inline]
pub fn is_aligned(self, align: u64) -> bool {
self.align_down(align).as_u64() == self.as_u64()
}
#[inline]
pub const fn page_offset(self) -> PageOffset {
PageOffset::new_truncate(self.0 as u16)
}
#[inline]
pub const fn p1_index(self) -> PageTableIndex {
PageTableIndex::new_truncate((self.0 >> 12) as u16)
}
#[inline]
pub const fn p2_index(self) -> PageTableIndex {
PageTableIndex::new_truncate((self.0 >> 12 >> 9) as u16)
}
#[inline]
pub const fn p3_index(self) -> PageTableIndex {
PageTableIndex::new_truncate((self.0 >> 12 >> 9 >> 9) as u16)
}
#[inline]
pub const fn p4_index(self) -> PageTableIndex {
PageTableIndex::new_truncate((self.0 >> 12 >> 9 >> 9 >> 9) as u16)
}
#[inline]
pub const fn page_table_index(self, level: PageTableLevel) -> PageTableIndex {
PageTableIndex::new_truncate((self.0 >> 12 >> ((level as u8 - 1) * 9)) as u16)
}
}
impl Align<u64> for VirtAddr {
#[inline]
fn align_down(self, align: u64) -> Self {
Self::new_truncate(self.0.align_down(align))
}
#[inline]
fn checked_align_up(self, align: u64) -> Option<Self> {
let addr = self.0.checked_align_up(align)?;
Some(Self::new_truncate(addr))
}
}
#[cfg(target_pointer_width = "64")]
impl From<usize> for VirtAddr {
fn from(addr: usize) -> VirtAddr {
Self::new_truncate(addr as u64)
}
}
#[cfg(target_pointer_width = "64")]
impl core::ops::Add<usize> for VirtAddr {
type Output = Self;
#[inline]
fn add(self, rhs: usize) -> Self::Output {
VirtAddr::new(self.0 + rhs as u64)
}
}
#[cfg(target_pointer_width = "64")]
impl core::ops::AddAssign<usize> for VirtAddr {
#[inline]
fn add_assign(&mut self, rhs: usize) {
*self = *self + rhs;
}
}
#[cfg(target_pointer_width = "64")]
impl core::ops::Sub<usize> for VirtAddr {
type Output = Self;
#[inline]
fn sub(self, rhs: usize) -> Self::Output {
VirtAddr::new(self.0.checked_sub(rhs as u64).unwrap())
}
}
#[cfg(target_pointer_width = "64")]
impl core::ops::SubAssign<usize> for VirtAddr {
#[inline]
fn sub_assign(&mut self, rhs: usize) {
*self = *self - rhs;
}
}
#[cfg(feature = "conv-x86_64")]
impl From<x86_64::VirtAddr> for VirtAddr {
fn from(addr: x86_64::VirtAddr) -> Self {
Self(addr.as_u64())
}
}
#[cfg(feature = "conv-x86_64")]
impl From<&x86_64::VirtAddr> for VirtAddr {
fn from(addr: &x86_64::VirtAddr) -> Self {
Self(addr.as_u64())
}
}
#[cfg(feature = "conv-x86_64")]
impl From<VirtAddr> for x86_64::VirtAddr {
fn from(addr: VirtAddr) -> x86_64::VirtAddr {
x86_64::VirtAddr::new(addr.0)
}
}
#[cfg(feature = "conv-x86_64")]
impl From<&VirtAddr> for x86_64::VirtAddr {
fn from(addr: &VirtAddr) -> x86_64::VirtAddr {
x86_64::VirtAddr::new(addr.0)
}
}
#[cfg(feature = "conv-x86")]
impl From<x86_VAddr> for VirtAddr {
fn from(addr: x86_VAddr) -> Self {
Self(addr.as_u64())
}
}
#[cfg(feature = "conv-x86")]
impl From<&x86_VAddr> for VirtAddr {
fn from(addr: &x86_VAddr) -> Self {
Self(addr.as_u64())
}
}
#[cfg(feature = "conv-x86")]
impl From<VirtAddr> for x86_VAddr {
fn from(addr: VirtAddr) -> x86_VAddr {
x86_VAddr(addr.0)
}
}
#[cfg(feature = "conv-x86")]
impl From<&VirtAddr> for x86_VAddr {
fn from(addr: &VirtAddr) -> x86_VAddr {
x86_VAddr(addr.0)
}
}
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 {
#[inline]
pub const fn new(addr: u64) -> Self {
match Self::try_new(addr) {
Ok(p) => p,
Err(_) => panic!("physical addresses must not have any bits in the range 52 to 64 set"),
}
}
#[inline]
pub const fn new_truncate(addr: u64) -> PhysAddr {
PhysAddr(addr % (1 << 52))
}
#[inline]
pub const fn try_new(addr: u64) -> Result<Self, PhysAddrNotValid> {
let p = Self::new_truncate(addr);
if p.0 == addr {
Ok(p)
} else {
Err(PhysAddrNotValid(addr))
}
}
#[cfg(target_pointer_width = "64")]
pub const fn as_usize(&self) -> usize {
self.0 as usize
}
}
impl Align<u64> for PhysAddr {
#[inline]
fn align_down(self, align: u64) -> Self {
Self::new(self.as_u64().align_down(align))
}
#[inline]
fn checked_align_up(self, align: u64) -> Option<Self> {
let addr = self.0.checked_align_up(align)?;
let this = Self::try_new(addr).ok()?;
Some(this)
}
}
#[cfg(target_pointer_width = "64")]
impl From<usize> for PhysAddr {
fn from(addr: usize) -> PhysAddr {
Self::new_truncate(addr as u64)
}
}
#[cfg(target_pointer_width = "64")]
impl core::ops::Add<usize> for PhysAddr {
type Output = Self;
#[inline]
fn add(self, rhs: usize) -> Self::Output {
PhysAddr::new(self.0 + rhs as u64)
}
}
#[cfg(target_pointer_width = "64")]
impl core::ops::AddAssign<usize> for PhysAddr {
#[inline]
fn add_assign(&mut self, rhs: usize) {
*self = *self + rhs;
}
}
#[cfg(target_pointer_width = "64")]
impl core::ops::Sub<usize> for PhysAddr {
type Output = Self;
#[inline]
fn sub(self, rhs: usize) -> Self::Output {
PhysAddr::new(self.0.checked_sub(rhs as u64).unwrap())
}
}
#[cfg(target_pointer_width = "64")]
impl core::ops::SubAssign<usize> for PhysAddr {
#[inline]
fn sub_assign(&mut self, rhs: usize) {
*self = *self - rhs;
}
}
#[cfg(feature = "conv-x86_64")]
impl From<x86_64::PhysAddr> for PhysAddr {
fn from(addr: x86_64::PhysAddr) -> Self {
Self(addr.as_u64())
}
}
#[cfg(feature = "conv-x86_64")]
impl From<&x86_64::PhysAddr> for PhysAddr {
fn from(addr: &x86_64::PhysAddr) -> Self {
Self(addr.as_u64())
}
}
#[cfg(feature = "conv-x86_64")]
impl From<PhysAddr> for x86_64::PhysAddr {
fn from(addr: PhysAddr) -> x86_64::PhysAddr {
x86_64::PhysAddr::new(addr.0)
}
}
#[cfg(feature = "conv-x86_64")]
impl From<&PhysAddr> for x86_64::PhysAddr {
fn from(addr: &PhysAddr) -> x86_64::PhysAddr {
x86_64::PhysAddr::new(addr.0)
}
}
#[cfg(feature = "conv-x86")]
impl From<x86_PAddr> for PhysAddr {
fn from(addr: x86_PAddr) -> Self {
Self(addr.as_u64())
}
}
#[cfg(feature = "conv-x86")]
impl From<&x86_PAddr> for PhysAddr {
fn from(addr: &x86_PAddr) -> Self {
Self(addr.as_u64())
}
}
#[cfg(feature = "conv-x86")]
impl From<PhysAddr> for x86_PAddr {
fn from(addr: PhysAddr) -> x86_PAddr {
x86_PAddr(addr.0)
}
}
#[cfg(feature = "conv-x86")]
impl From<&PhysAddr> for x86_PAddr {
fn from(addr: &PhysAddr) -> x86_PAddr {
x86_PAddr(addr.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
pub fn virtaddr_new_truncate() {
assert_eq!(VirtAddr::new_truncate(0), VirtAddr(0));
assert_eq!(VirtAddr::new_truncate(1 << 47), VirtAddr(0xfffff << 47));
assert_eq!(VirtAddr::new_truncate(123), VirtAddr(123));
assert_eq!(VirtAddr::new_truncate(123 << 47), VirtAddr(0xfffff << 47));
}
#[test]
fn test_virt_addr_align_up() {
assert_eq!(
VirtAddr::new(0x7fff_ffff_ffff).align_up(2u64),
VirtAddr::new(0xffff_8000_0000_0000)
);
assert_eq!(
VirtAddr::new(0xffff_ffff_ffff_ffff).checked_align_up(2u64),
None
);
}
#[test]
fn test_virt_addr_align_down() {
assert_eq!(
VirtAddr::new(0xffff_8000_0000_0000).align_down(1u64 << 48),
VirtAddr::new(0)
);
}
#[test]
fn test_phys_addr_align_up() {
assert_eq!(
PhysAddr::new(0x000f_ffff_ffff_ffff).checked_align_up(2u64),
None
);
}
#[test]
fn test_from_ptr_array() {
let slice = &[1, 2, 3, 4, 5];
assert_eq!(VirtAddr::from_ptr(slice), VirtAddr::from_ptr(&slice[0]));
}
}