use crate::types::ValidationError;
use core::fmt;
pub const PAGE_SIZE: u64 = 0x1000;
const PAGE_OFFSET_MASK: u64 = PAGE_SIZE - 1;
const PAGE_SHIFT: u32 = 12;
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct IOVA(u64);
impl IOVA {
#[inline]
pub const fn new(addr: u64) -> Result<Self, ValidationError> {
Ok(Self(addr))
}
#[inline]
#[must_use]
pub const fn const_new(addr: u64) -> Self {
Self(addr)
}
#[inline]
pub const fn new_page_aligned(addr: u64) -> Result<Self, ValidationError> {
if addr & PAGE_OFFSET_MASK != 0 {
return Err(ValidationError::InvalidAlignment {
address: addr,
required_alignment: PAGE_SIZE,
});
}
Ok(Self(addr))
}
#[inline]
#[must_use]
pub const fn as_u64(self) -> u64 {
self.0
}
#[inline]
#[must_use]
pub const fn raw(self) -> u64 {
self.0
}
#[inline]
#[must_use]
pub const fn is_page_aligned(self) -> bool {
self.0 & PAGE_OFFSET_MASK == 0
}
#[inline]
#[must_use]
pub const fn align_down_to_page(self) -> Self {
Self(self.0 & !PAGE_OFFSET_MASK)
}
#[inline]
#[must_use]
pub const fn align_up_to_page(self) -> Self {
Self((self.0 + PAGE_OFFSET_MASK) & !PAGE_OFFSET_MASK)
}
#[inline]
#[must_use]
pub const fn page_offset(self) -> u64 {
self.0 & PAGE_OFFSET_MASK
}
#[inline]
#[must_use]
pub const fn page_number(self) -> u64 {
self.0 >> PAGE_SHIFT
}
#[inline]
#[must_use]
pub const fn add_offset(self, offset: u64) -> Self {
Self(self.0.wrapping_add(offset))
}
#[inline]
#[must_use]
pub const fn checked_add(self, offset: u64) -> Option<Self> {
match self.0.checked_add(offset) {
Some(addr) => Some(Self(addr)),
None => None,
}
}
#[inline]
#[must_use]
pub const fn mask(self, mask: u64) -> Self {
Self(self.0 & mask)
}
}
impl fmt::Debug for IOVA {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "IOVA({:#018x})", self.0)
}
}
impl fmt::Display for IOVA {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:#06x}_{:04x}_{:04x}_{:04x}",
(self.0 >> 48) & 0xFFFF,
(self.0 >> 32) & 0xFFFF,
(self.0 >> 16) & 0xFFFF,
self.0 & 0xFFFF)
}
}
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct IPA(u64);
impl IPA {
#[inline]
pub const fn new(addr: u64) -> Result<Self, ValidationError> {
Ok(Self(addr))
}
#[inline]
#[must_use]
pub const fn const_new(addr: u64) -> Self {
Self(addr)
}
#[inline]
pub const fn new_page_aligned(addr: u64) -> Result<Self, ValidationError> {
if addr & PAGE_OFFSET_MASK != 0 {
return Err(ValidationError::InvalidAlignment {
address: addr,
required_alignment: PAGE_SIZE,
});
}
Ok(Self(addr))
}
#[inline]
#[must_use]
pub const fn as_u64(self) -> u64 {
self.0
}
#[inline]
#[must_use]
pub const fn raw(self) -> u64 {
self.0
}
#[inline]
#[must_use]
pub const fn is_page_aligned(self) -> bool {
self.0 & PAGE_OFFSET_MASK == 0
}
#[inline]
#[must_use]
pub const fn align_down_to_page(self) -> Self {
Self(self.0 & !PAGE_OFFSET_MASK)
}
#[inline]
#[must_use]
pub const fn align_up_to_page(self) -> Self {
Self((self.0 + PAGE_OFFSET_MASK) & !PAGE_OFFSET_MASK)
}
#[inline]
#[must_use]
pub const fn page_offset(self) -> u64 {
self.0 & PAGE_OFFSET_MASK
}
#[inline]
#[must_use]
pub const fn page_number(self) -> u64 {
self.0 >> PAGE_SHIFT
}
#[inline]
#[must_use]
pub const fn add_offset(self, offset: u64) -> Self {
Self(self.0.wrapping_add(offset))
}
#[inline]
#[must_use]
pub const fn checked_add(self, offset: u64) -> Option<Self> {
match self.0.checked_add(offset) {
Some(addr) => Some(Self(addr)),
None => None,
}
}
#[inline]
#[must_use]
pub const fn mask(self, mask: u64) -> Self {
Self(self.0 & mask)
}
}
impl fmt::Debug for IPA {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "IPA({:#018x})", self.0)
}
}
impl fmt::Display for IPA {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:#06x}_{:04x}_{:04x}_{:04x}",
(self.0 >> 48) & 0xFFFF,
(self.0 >> 32) & 0xFFFF,
(self.0 >> 16) & 0xFFFF,
self.0 & 0xFFFF)
}
}
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PA(u64);
impl PA {
#[inline]
pub const fn new(addr: u64) -> Result<Self, ValidationError> {
Ok(Self(addr))
}
#[inline]
#[must_use]
pub const fn const_new(addr: u64) -> Self {
Self(addr)
}
#[inline]
pub const fn new_page_aligned(addr: u64) -> Result<Self, ValidationError> {
if addr & PAGE_OFFSET_MASK != 0 {
return Err(ValidationError::InvalidAlignment {
address: addr,
required_alignment: PAGE_SIZE,
});
}
Ok(Self(addr))
}
#[inline]
#[must_use]
pub const fn as_u64(self) -> u64 {
self.0
}
#[inline]
#[must_use]
pub const fn raw(self) -> u64 {
self.0
}
#[inline]
#[must_use]
pub const fn is_page_aligned(self) -> bool {
self.0 & PAGE_OFFSET_MASK == 0
}
#[inline]
#[must_use]
pub const fn align_down_to_page(self) -> Self {
Self(self.0 & !PAGE_OFFSET_MASK)
}
#[inline]
#[must_use]
pub const fn align_up_to_page(self) -> Self {
Self((self.0 + PAGE_OFFSET_MASK) & !PAGE_OFFSET_MASK)
}
#[inline]
#[must_use]
pub const fn page_offset(self) -> u64 {
self.0 & PAGE_OFFSET_MASK
}
#[inline]
#[must_use]
pub const fn page_number(self) -> u64 {
self.0 >> PAGE_SHIFT
}
#[inline]
#[must_use]
pub const fn add_offset(self, offset: u64) -> Self {
Self(self.0.wrapping_add(offset))
}
#[inline]
#[must_use]
pub const fn checked_add(self, offset: u64) -> Option<Self> {
match self.0.checked_add(offset) {
Some(addr) => Some(Self(addr)),
None => None,
}
}
#[inline]
#[must_use]
pub const fn mask(self, mask: u64) -> Self {
Self(self.0 & mask)
}
}
impl fmt::Debug for PA {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PA({:#018x})", self.0)
}
}
impl fmt::Display for PA {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:#06x}_{:04x}_{:04x}_{:04x}",
(self.0 >> 48) & 0xFFFF,
(self.0 >> 32) & 0xFFFF,
(self.0 >> 16) & 0xFFFF,
self.0 & 0xFFFF)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_page_size_constant() {
assert_eq!(PAGE_SIZE, 0x1000);
}
#[test]
fn test_iova_basic() {
let iova = IOVA::new(0x1000).unwrap();
assert_eq!(iova.as_u64(), 0x1000);
}
#[test]
fn test_ipa_basic() {
let ipa = IPA::new(0x2000).unwrap();
assert_eq!(ipa.as_u64(), 0x2000);
}
#[test]
fn test_pa_basic() {
let pa = PA::new(0x3000).unwrap();
assert_eq!(pa.as_u64(), 0x3000);
}
}