#![cfg(all(target_os = "macos", target_arch = "aarch64"))]
use applevisor_sys as av;
#[derive(Debug)]
pub enum Error {
Hv(i32),
}
pub type Result<T> = std::result::Result<T, Error>;
#[inline]
fn check(rc: av::hv_return_t) -> Result<()> {
if rc == 0 {
Ok(())
} else {
Err(Error::Hv(rc as i32))
}
}
pub struct Vm;
impl Vm {
pub fn new() -> Result<Self> {
use crate::arch::aarch64::layout;
unsafe {
check(av::hv_vm_create(std::ptr::null_mut()))?;
let gic_cfg = av::hv_gic_config_create();
check(av::hv_gic_config_set_distributor_base(
gic_cfg,
layout::GICV3_DIST_BASE,
))?;
check(av::hv_gic_config_set_redistributor_base(
gic_cfg,
layout::GICV3_REDIST_BASE,
))?;
check(av::hv_gic_create(gic_cfg))?;
}
Ok(Vm)
}
pub unsafe fn map(&self, host_ptr: *mut u8, gpa: u64, len: usize, flags: u64) -> Result<()> {
check(unsafe { av::hv_vm_map(host_ptr as _, gpa, len, flags as _) })
}
}
impl Drop for Vm {
fn drop(&mut self) {
unsafe {
let _ = av::hv_vm_destroy();
}
}
}
pub struct Vcpu {
handle: av::hv_vcpu_t,
exit: *const av::hv_vcpu_exit_t,
}
impl Vcpu {
pub fn new() -> Result<Self> {
let mut handle: av::hv_vcpu_t = 0;
let mut exit: *const av::hv_vcpu_exit_t = std::ptr::null();
unsafe {
check(av::hv_vcpu_create(
&mut handle,
&mut exit,
std::ptr::null_mut(),
))?;
}
Ok(Self { handle, exit })
}
pub fn handle(&self) -> av::hv_vcpu_t {
self.handle
}
pub fn set_reg(&self, reg: av::hv_reg_t, value: u64) -> Result<()> {
unsafe { check(av::hv_vcpu_set_reg(self.handle, reg, value)) }
}
pub fn get_reg(&self, reg: av::hv_reg_t) -> Result<u64> {
let mut v: u64 = 0;
unsafe {
check(av::hv_vcpu_get_reg(self.handle, reg, &mut v))?;
}
Ok(v)
}
pub fn get_sys_reg(&self, reg: av::hv_sys_reg_t) -> Result<u64> {
let mut v: u64 = 0;
unsafe {
check(av::hv_vcpu_get_sys_reg(self.handle, reg, &mut v))?;
}
Ok(v)
}
pub fn get_x(&self, n: u32) -> Result<u64> {
let r = match n {
0..=30 => unsafe {
std::mem::transmute::<u32, av::hv_reg_t>(av::hv_reg_t::X0 as u32 + n)
},
_ => return Ok(0),
};
self.get_reg(r)
}
pub fn set_x(&self, n: u32, v: u64) -> Result<()> {
let r = match n {
0..=30 => unsafe {
std::mem::transmute::<u32, av::hv_reg_t>(av::hv_reg_t::X0 as u32 + n)
},
_ => return Ok(()),
};
self.set_reg(r, v)
}
pub fn set_sys_reg(&self, reg: av::hv_sys_reg_t, value: u64) -> Result<()> {
unsafe { check(av::hv_vcpu_set_sys_reg(self.handle, reg, value)) }
}
pub fn get_simd_fp_reg(&self, reg: av::hv_simd_fp_reg_t) -> Result<u128> {
let mut v: u128 = 0;
unsafe {
check(av::hv_vcpu_get_simd_fp_reg(self.handle, reg, &mut v))?;
}
Ok(v)
}
pub fn set_simd_fp_reg(&self, reg: av::hv_simd_fp_reg_t, value: u128) -> Result<()> {
unsafe { check(av::hv_vcpu_set_simd_fp_reg(self.handle, reg, value)) }
}
pub fn get_vtimer_offset(&self) -> Result<u64> {
let mut v: u64 = 0;
unsafe {
check(av::hv_vcpu_get_vtimer_offset(self.handle, &mut v))?;
}
Ok(v)
}
pub fn set_vtimer_offset(&self, value: u64) -> Result<()> {
unsafe { check(av::hv_vcpu_set_vtimer_offset(self.handle, value)) }
}
pub fn set_vtimer_mask(&self, masked: bool) -> Result<()> {
unsafe { check(av::hv_vcpu_set_vtimer_mask(self.handle, masked)) }
}
pub fn get_icc_reg(&self, reg: av::hv_gic_icc_reg_t) -> Result<u64> {
let mut v: u64 = 0;
unsafe {
check(av::hv_gic_get_icc_reg(self.handle, reg, &mut v))?;
}
Ok(v)
}
pub fn set_icc_reg(&self, reg: av::hv_gic_icc_reg_t, value: u64) -> Result<()> {
unsafe { check(av::hv_gic_set_icc_reg(self.handle, reg, value)) }
}
pub fn get_redist_reg(&self, reg: av::hv_gic_redistributor_reg_t) -> Result<u64> {
let mut v: u64 = 0;
unsafe {
check(av::hv_gic_get_redistributor_reg(self.handle, reg, &mut v))?;
}
Ok(v)
}
pub fn set_redist_reg(&self, reg: av::hv_gic_redistributor_reg_t, value: u64) -> Result<()> {
unsafe { check(av::hv_gic_set_redistributor_reg(self.handle, reg, value)) }
}
pub fn run(&self) -> Result<&av::hv_vcpu_exit_t> {
unsafe {
check(av::hv_vcpu_run(self.handle))?;
Ok(&*self.exit)
}
}
}
impl Drop for Vcpu {
fn drop(&mut self) {
unsafe {
let _ = av::hv_vcpu_destroy(self.handle);
}
}
}
pub fn gic_set_spi(intid: u32, level: bool) -> Result<()> {
unsafe { check(av::hv_gic_set_spi(intid, level)) }
}
pub fn gic_state_capture() -> Result<Vec<u8>> {
let state = unsafe { av::hv_gic_state_create() };
if state.is_null() {
return Err(Error::Hv(-1));
}
let mut sz: usize = 0;
let rc = unsafe { av::hv_gic_state_get_size(state, &mut sz) };
if rc != 0 {
unsafe {
av::os_release(state as *mut _);
}
return Err(Error::Hv(rc as i32));
}
let mut buf = vec![0u8; sz];
let rc = unsafe { av::hv_gic_state_get_data(state, buf.as_mut_ptr() as *mut _) };
unsafe {
av::os_release(state as *mut _);
}
if rc != 0 {
return Err(Error::Hv(rc as i32));
}
Ok(buf)
}
pub fn gic_state_restore(blob: &[u8]) -> Result<()> {
unsafe { check(av::hv_gic_set_state(blob.as_ptr() as *const _, blob.len())) }
}
pub mod prot {
pub const READ: u64 = 1;
pub const WRITE: u64 = 1 << 1;
pub const EXEC: u64 = 1 << 2;
pub const RWX: u64 = READ | WRITE | EXEC;
pub const RW: u64 = READ | WRITE;
pub const RX: u64 = READ | EXEC;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExitReason {
Canceled,
Exception,
VTimerActivated,
Unknown(u32),
}
impl From<u32> for ExitReason {
fn from(v: u32) -> Self {
match v {
0 => Self::Canceled,
1 => Self::Exception,
2 => Self::VTimerActivated,
_ => Self::Unknown(v),
}
}
}