baryl 0.0.2

Public SDK for Baryl, a full-system emulation and introspection engine
//! The two guest addresses, and the macro that gives an address newtype its
//! arithmetic.
//!
//! [`VirtAddr`] and [`PhysAddr`] are distinct types over `u64`. Neither is
//! accepted where the other is asked for; go across with `.0`, `to_u64()`, or
//! `VirtAddr::from(x)`. Both are `#[repr(transparent)]`, so either crosses the
//! ABI as the bare number, and [`newtype_ops!`](crate::newtype_ops) gives both
//! arithmetic, `align_up`/`align_down`, `page_base` and `page_offset`.

use core::fmt;

use derive_more::{From, LowerHex, UpperHex};
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};

use super::SandboxSafe;

/// `Display` as `0x{:x}` and `Debug` as `Type(0x{:x})`, so a log line reads in
/// hex without `{:#x}` at every call site.
macro_rules! hex_fmt {
    ($ty:ident) => {
        impl fmt::Display for $ty {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                write!(f, "{:#x}", self.0)
            }
        }
        impl fmt::Debug for $ty {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                write!(f, "{}({:#x})", stringify!($ty), self.0)
            }
        }
    };
}

/// A guest virtual address — what code running inside the guest sees.
///
/// A transparent `u64` that can be read straight out of guest bytes
/// (`FromBytes`/`IntoBytes`) and used as a map key. `Display` prints `0x1234`,
/// `Debug` prints `VirtAddr(0x1234)`, and the masks, shifts and page helpers
/// [`newtype_ops!`](crate::newtype_ops) adds are all here.
///
/// # Examples
///
/// ```ignore
/// let sp = VirtAddr(0xffff_8801_2345_6789);
/// assert!(sp.is_canonical());
/// assert_eq!(sp.page_base(), VirtAddr(0xffff_8801_2345_6000));
/// assert_eq!(sp.page_offset(), 0x789);
/// assert_eq!(format!("{sp}"), "0xffff880123456789");
/// ```
#[derive(
    Clone,
    Copy,
    Default,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    From,
    LowerHex,
    UpperHex,
    FromBytes,
    IntoBytes,
    Immutable,
    KnownLayout,
)]
#[repr(transparent)]
pub struct VirtAddr(pub u64);
unsafe impl SandboxSafe for VirtAddr {}
hex_fmt!(VirtAddr);

impl VirtAddr {
    /// True when the top 17 bits are all zero or all one — the x86-64
    /// canonical-address rule (Intel SDM Vol. 1 §3.3.7.1).
    ///
    /// Worth checking before a translate on a value that came out of guest
    /// memory: a pointer field holding a small integer or a poison pattern
    /// fails this, and failing it here is cheaper than a page walk.
    pub const fn is_canonical(self) -> bool {
        let top = (self.0 >> 47) & 0x1ffff;
        top == 0 || top == 0x1ffff
    }
}

/// A guest physical address — what a virtual address translates to, and what
/// the machine's RAM is addressed by.
///
/// Same shape as [`VirtAddr`]: transparent `u64`, hex formatting, the
/// [`newtype_ops!`](crate::newtype_ops) helpers. Breakpoints are armed on one of these, so a hit
/// fires however the guest reached the site.
#[derive(
    Clone,
    Copy,
    Default,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    From,
    LowerHex,
    UpperHex,
    FromBytes,
    IntoBytes,
    Immutable,
    KnownLayout,
)]
#[repr(transparent)]
pub struct PhysAddr(pub u64);
unsafe impl SandboxSafe for PhysAddr {}
hex_fmt!(PhysAddr);

// From<usize>, so a host-sized index converts without a cast

impl From<usize> for VirtAddr {
    fn from(v: usize) -> Self {
        Self(v as u64)
    }
}
impl From<usize> for PhysAddr {
    fn from(v: usize) -> Self {
        Self(v as u64)
    }
}

// Into<u64>, so an address reaches a call that takes the bare number
impl From<VirtAddr> for u64 {
    fn from(a: VirtAddr) -> u64 {
        a.0
    }
}
impl From<PhysAddr> for u64 {
    fn from(a: PhysAddr) -> u64 {
        a.0
    }
}
/// Give an address newtype the arithmetic an address wants: conversions,
/// masks, shifts and 4 KiB page alignment.
///
/// Write it after a `#[repr(transparent)] pub struct Ty(pub u64);` of your own.
/// Already applied to [`VirtAddr`] and [`PhysAddr`], so those have everything
/// below without you doing anything.
///
/// Adds `to_u64`, `to_usize`, `is_zero`, `align_down`, `align_up`,
/// `page_offset` and `page_base`, plus `&`, `|`, `!`, `<<` and `>>`. The
/// bitwise and shift operators take a bare number on the right and give the
/// newtype back, so a mask literal needs no wrapping.
///
/// `align_down` and `align_up` are AND-and-round, so `align` must be a power of
/// two — anything else silently produces nonsense. `align_up` near `u64::MAX`
/// overflows. `page_offset` and `page_base` assume a 4 KiB page.
///
/// # Examples
///
/// ```ignore
/// #[repr(transparent)]
/// #[derive(Clone, Copy, PartialEq, Eq, Debug)]
/// pub struct Rva(pub u64);
/// newtype_ops!(Rva);
///
/// let off = Rva(0x4789);
/// assert_eq!(off.page_offset(), 0x789);
/// assert_eq!(off.page_base(), Rva(0x4000));
/// assert_eq!(off.align_up(0x1000), Rva(0x5000));
/// assert_eq!((off & 0xfff).to_u64(), 0x789);
/// assert_eq!((off >> 12).to_usize(), 4);
/// ```
#[macro_export]
macro_rules! newtype_ops {
    ($ty:ident) => {
        impl $ty {
            #[inline]
            pub fn to_u64(self) -> u64 {
                self.0 as u64
            }
            #[inline]
            pub fn to_usize(self) -> usize {
                self.0 as usize
            }
            #[inline]
            pub fn is_zero(&self) -> bool {
                self.0 == 0
            }

            #[inline]
            pub fn align_down(self, align: u64) -> Self {
                Self(self.0 & !(align - 1))
            }
            #[inline]
            pub fn align_up(self, align: u64) -> Self {
                Self((self.0 + align - 1) & !(align - 1))
            }
            #[inline]
            pub fn page_offset(&self) -> usize {
                (self.0 & 0xFFF) as usize
            }
            #[inline]
            pub fn page_base(self) -> Self {
                self.align_down(0x1000)
            }
        }

        // A bare u64 on the right, so a mask literal infers without a wrapper
        impl ::core::ops::BitAnd<u64> for $ty {
            type Output = $ty;
            #[inline]
            fn bitand(self, rhs: u64) -> $ty {
                $ty(self.0 & rhs)
            }
        }
        impl ::core::ops::BitOr<u64> for $ty {
            type Output = $ty;
            #[inline]
            fn bitor(self, rhs: u64) -> $ty {
                $ty(self.0 | rhs)
            }
        }
        impl ::core::ops::Not for $ty {
            type Output = $ty;
            #[inline]
            fn not(self) -> $ty {
                $ty(!self.0)
            }
        }

        impl ::core::ops::Shl<u32> for $ty {
            type Output = $ty;
            #[inline]
            fn shl(self, rhs: u32) -> $ty {
                $ty(self.0 << rhs)
            }
        }
        impl ::core::ops::Shr<u32> for $ty {
            type Output = $ty;
            #[inline]
            fn shr(self, rhs: u32) -> $ty {
                $ty(self.0 >> rhs)
            }
        }
    };
}

newtype_ops!(VirtAddr);
newtype_ops!(PhysAddr);