use crate::abi::Arch;
pub mod mem;
pub use mem::{GuestMemory, MemError, Prot};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Exit {
Syscall,
MemFault { addr: u64, write: bool },
IllegalInstruction { pc: u64 },
Interrupted,
Halt,
}
#[derive(Debug)]
pub enum VcpuError {
Unsupported {
host: &'static str,
guest: Arch,
},
Backend(String),
Mem(MemError),
}
impl core::fmt::Display for VcpuError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Unsupported { host, guest } => {
write!(
f,
"no vcpu backend for host {host} + guest {}",
guest.as_str()
)
}
Self::Backend(m) => write!(f, "vcpu backend error: {m}"),
Self::Mem(e) => write!(f, "guest memory error: {e:?}"),
}
}
}
impl std::error::Error for VcpuError {}
impl From<MemError> for VcpuError {
fn from(e: MemError) -> Self {
Self::Mem(e)
}
}
pub trait Vcpu: Send {
fn run(&mut self, mem: &mut GuestMemory) -> Result<Exit, VcpuError>;
fn syscall_nr(&self) -> u64;
fn syscall_args(&self) -> [u64; 6];
fn set_syscall_ret(&mut self, value: u64);
fn reg(&self, idx: usize) -> u64;
fn set_reg(&mut self, idx: usize, value: u64);
fn pc(&self) -> u64;
fn set_pc(&mut self, pc: u64);
fn sp(&self) -> u64;
fn set_sp(&mut self, sp: u64);
fn set_tls(&mut self, value: u64);
fn fork(&self) -> Box<dyn Vcpu>;
fn reset(&mut self, entry: u64, sp: u64);
}
pub trait Backend {
fn name(&self) -> &'static str;
fn guest_arch(&self) -> Arch;
fn new_vcpu(&self, entry: u64, stack: u64) -> Result<Box<dyn Vcpu>, VcpuError>;
}
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
pub mod hvf;
pub mod interp;
pub mod interp_x86;
pub fn select(guest: Arch) -> Result<Box<dyn Backend>, VcpuError> {
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
{
if guest == Arch::Aarch64 {
return hvf::HvfBackend::new().map(|b| Box::new(b) as Box<dyn Backend>);
}
}
if guest == Arch::X86_64 {
return interp_x86::X86Backend::new(guest).map(|b| Box::new(b) as Box<dyn Backend>);
}
interp::InterpBackend::new(guest).map(|b| Box::new(b) as Box<dyn Backend>)
}