use core::ffi::c_void;
#[cfg(feature = "x86_64")]
use core::ffi::c_int;
pub use crate::abi::{BARYL_SCAN_NO_MATCH, ProgressFn, ProgressSink, ScanRange};
#[cfg(feature = "x86_64")]
use crate::abi::{PhysAddr, SbxPtr, VirtAddr, progress_thunk};
#[cfg(feature = "x86_64")]
mod generated {
#![allow(non_camel_case_types, non_upper_case_globals, dead_code)]
include!("generated-x86_64.rs");
}
#[cfg(not(feature = "x86_64"))]
mod generated {
#![allow(non_camel_case_types, non_upper_case_globals, dead_code)]
include!("generated.rs");
}
pub use generated::*;
pub type CpuExcCb = unsafe extern "C" fn(*mut c_void, *mut c_void, *const CpuExceptionContext);
pub type AspaceChgCb = unsafe extern "C" fn(*mut c_void, *mut c_void, u64);
#[cfg(feature = "x86_64")]
mod exception;
#[cfg(feature = "x86_64")]
pub use exception::AccessType;
#[cfg(feature = "x86_64")]
mod mem;
#[cfg(feature = "x86_64")]
pub use mem::{GuestMem, MAX_INLINE_STR};
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct AddressSpace(pub u64);
const ROOT_FLAG_MASK: u64 = 0xFFF;
impl AddressSpace {
pub fn normalize(self) -> Self {
Self(self.0 & !ROOT_FLAG_MASK)
}
}
pub trait Arch {
const ID: u32;
}
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct ArchId(pub u32);
impl ArchId {
pub const ANY: Self = Self(0);
pub const X86_64: Self = Self(BARYL_ARCH_X86_64);
}
#[cfg(feature = "x86_64")]
pub use generated::RegLayoutX64 as X64Regs;
#[cfg(feature = "x86_64")]
impl Arch for X64Regs {
const ID: u32 = BARYL_ARCH_X86_64;
}
#[cfg(feature = "x86_64")]
impl X64Regs {
pub fn rip(&self) -> SbxPtr<u64> {
unsafe { SbxPtr::from_raw(self.rip) }
}
pub fn rflags(&self) -> SbxPtr<u64> {
unsafe { SbxPtr::from_raw(self.rflags) }
}
pub fn gpr(&self, r: X64GPR) -> SbxPtr<u64> {
unsafe { SbxPtr::from_raw((self.gpr as *mut u64).add(r as usize)) }
}
pub fn seg(&self, s: X64Seg) -> SbxPtr<X64Segment> {
unsafe { SbxPtr::from_raw(self.seg.add(s as usize)) }
}
pub fn cr(&self, n: usize) -> SbxPtr<u64> {
unsafe { SbxPtr::from_raw(self.cr.add(n)) }
}
pub fn dr(&self, n: usize) -> SbxPtr<u64> {
unsafe { SbxPtr::from_raw(self.dr.add(n)) }
}
pub fn efer(&self) -> SbxPtr<u64> {
unsafe { SbxPtr::from_raw(self.efer) }
}
pub fn lstar(&self) -> SbxPtr<u64> {
unsafe { SbxPtr::from_raw(self.lstar) }
}
pub fn kernel_gs_base(&self) -> SbxPtr<u64> {
unsafe { SbxPtr::from_raw(self.kernel_gs_base) }
}
pub fn percpu_base(&self) -> u64 {
if self.seg(X64Seg::Cs).selector & CS_RPL_MASK == 0 {
self.seg(X64Seg::Gs).base
} else {
*self.kernel_gs_base()
}
}
}
#[cfg(feature = "x86_64")]
const CS_RPL_MASK: u32 = 0b11;
#[cfg(feature = "x86_64")]
crate::abi::impl_sandbox_safe!(GuestAddr);
#[cfg(feature = "x86_64")]
impl GuestAddr {
pub fn block_hash(&self) -> u64 {
let root = self.cr3 & !ROOT_FLAG_MASK;
let mut h = self.va ^ root.wrapping_mul(BARYL_BLOCK_HASH_C1 as u64);
h ^= h >> BARYL_BLOCK_HASH_SHIFT1;
h = h.wrapping_mul(BARYL_BLOCK_HASH_C2 as u64);
h ^= h >> BARYL_BLOCK_HASH_SHIFT2;
h
}
}
#[cfg(feature = "x86_64")]
impl MemAttr {
pub fn present(&self) -> bool {
self.perms & BARYL_X64_PTE_PRESENT != 0
}
pub fn writable(&self) -> bool {
self.perms & BARYL_X64_PTE_WRITE != 0
}
pub fn user(&self) -> bool {
self.perms & BARYL_X64_PTE_USER != 0
}
pub fn executable(&self) -> bool {
self.perms & BARYL_X64_PTE_EXEC != 0
}
}
#[cfg(feature = "x86_64")]
impl ArchVtable {
pub const ABSENT: ArchVtable = ArchVtable {
translate: None,
read_virt: None,
write_virt: None,
tlb_invalidate: None,
virt_scan_for_pattern: None,
virt_scan_for_u64: None,
virt_scan_for_u32: None,
virt_walk_mappings: None,
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VirtAccessError;
impl core::fmt::Display for VirtAccessError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "virtual memory access failed")
}
}
impl core::error::Error for VirtAccessError {}
#[cfg(feature = "x86_64")]
impl ArchRef {
pub unsafe fn translate_with_cr3(&self, va: VirtAddr, cr3: u64) -> Option<PhysAddr> {
let f = unsafe { (*self.vtable).translate }?;
let mut pa = 0u64;
let rc = unsafe { f(*self, va.0, cr3, &raw mut pa, core::ptr::null_mut()) };
(rc == 0).then_some(PhysAddr(pa))
}
pub unsafe fn translate_attr_with_cr3(
&self,
va: VirtAddr,
cr3: u64,
) -> Option<(PhysAddr, MemAttr)> {
let f = unsafe { (*self.vtable).translate }?;
let (mut pa, mut attr) = (0u64, MemAttr::default());
let rc = unsafe { f(*self, va.0, cr3, &raw mut pa, &raw mut attr) };
(rc == 0).then_some((PhysAddr(pa), attr))
}
pub unsafe fn read_virt_into_with_cr3(
&self,
buf: &mut [u8],
va: u64,
cr3: u64,
) -> Result<(), VirtAccessError> {
let f = unsafe { (*self.vtable).read_virt }.ok_or(VirtAccessError)?;
match unsafe { f(*self, va, cr3, buf.as_mut_ptr(), buf.len() as u64) } {
0 => Ok(()),
_ => Err(VirtAccessError),
}
}
pub unsafe fn write_virt_from_with_cr3(
&self,
buf: &[u8],
va: u64,
cr3: u64,
) -> Result<(), VirtAccessError> {
let f = unsafe { (*self.vtable).write_virt }.ok_or(VirtAccessError)?;
match unsafe { f(*self, va, cr3, buf.as_ptr(), buf.len() as u64) } {
0 => Ok(()),
_ => Err(VirtAccessError),
}
}
pub unsafe fn tlb_invalidate(&self) {
let Some(f) = (unsafe { (*self.vtable).tlb_invalidate }) else {
return;
};
unsafe { f(*self) };
}
pub unsafe fn virt_scan_for_pattern_with_cr3(
&self,
cr3: u64,
range: ScanRange,
pat: &[u8],
progress: Option<ProgressSink<'_>>,
) -> Option<u64> {
let f = unsafe { (*self.vtable).virt_scan_for_pattern }?;
let mut progress = progress;
let (obj, cb) = progress_thunk(&mut progress);
let hit = unsafe {
f(
*self,
cr3,
range.start,
range.end,
range.stride,
pat.as_ptr(),
pat.len() as u64,
obj,
cb,
)
};
(hit != BARYL_SCAN_NO_MATCH).then_some(hit)
}
pub unsafe fn virt_scan_for_u64_with_cr3(
&self,
cr3: u64,
range: ScanRange,
target: u64,
progress: Option<ProgressSink<'_>>,
) -> Option<u64> {
let f = unsafe { (*self.vtable).virt_scan_for_u64 }?;
let mut progress = progress;
let (obj, cb) = progress_thunk(&mut progress);
let hit = unsafe { f(*self, cr3, range.start, range.end, range.stride, target, obj, cb) };
(hit != BARYL_SCAN_NO_MATCH).then_some(hit)
}
pub unsafe fn virt_scan_for_u32_with_cr3(
&self,
cr3: u64,
range: ScanRange,
target: u32,
progress: Option<ProgressSink<'_>>,
) -> Option<u64> {
let f = unsafe { (*self.vtable).virt_scan_for_u32 }?;
let mut progress = progress;
let (obj, cb) = progress_thunk(&mut progress);
let hit = unsafe { f(*self, cr3, range.start, range.end, range.stride, target, obj, cb) };
(hit != BARYL_SCAN_NO_MATCH).then_some(hit)
}
pub unsafe fn walk_mappings_of(
&self,
cr3: u64,
pa: u64,
f: impl FnMut(GuestAddr) -> bool,
) -> u64 {
let Some(w) = (unsafe { (*self.vtable).virt_walk_mappings }) else {
return 0;
};
let mut f = f;
let mut sink: &mut dyn FnMut(GuestAddr) -> bool = &mut f;
unsafe {
w(
*self,
cr3,
pa,
core::ptr::from_mut(&mut sink).cast(),
Some(visit),
None,
)
}
}
}
#[cfg(feature = "x86_64")]
unsafe extern "C" fn visit(obj: *mut c_void, addr: *const GuestAddr) -> c_int {
let f: &mut &mut dyn FnMut(GuestAddr) -> bool = unsafe { &mut *obj.cast() };
c_int::from(!f(unsafe { *addr }))
}