#[cfg(target_arch = "aarch64")]
mod aarch64;
mod gunyah_sys;
use std::cmp::Reverse;
use std::collections::BTreeMap;
use std::collections::BinaryHeap;
use std::collections::HashSet;
use std::ffi::CString;
use std::fs::File;
use std::mem::size_of;
use std::os::raw::c_ulong;
use std::os::unix::prelude::OsStrExt;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use base::errno_result;
use base::error;
use base::info;
use base::ioctl;
use base::ioctl_with_ref;
use base::ioctl_with_val;
use base::pagesize;
use base::warn;
use base::Error;
use base::FromRawDescriptor;
use base::MemoryMapping;
use base::MemoryMappingBuilder;
use base::MmapError;
use base::RawDescriptor;
use gunyah_sys::*;
use libc::open;
use libc::EFAULT;
use libc::EINVAL;
use libc::EIO;
use libc::ENOENT;
use libc::ENOSPC;
use libc::ENOTSUP;
use libc::EOVERFLOW;
use libc::O_CLOEXEC;
use libc::O_RDWR;
use sync::Mutex;
use vm_memory::MemoryRegionPurpose;
use crate::*;
pub struct Gunyah {
gunyah: SafeDescriptor,
}
impl AsRawDescriptor for Gunyah {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.gunyah.as_raw_descriptor()
}
}
impl Gunyah {
pub fn new_with_path(device_path: &Path) -> Result<Gunyah> {
let c_path = CString::new(device_path.as_os_str().as_bytes()).unwrap();
let ret = unsafe { open(c_path.as_ptr(), O_RDWR | O_CLOEXEC) };
if ret < 0 {
return errno_result();
}
Ok(Gunyah {
gunyah: unsafe { SafeDescriptor::from_raw_descriptor(ret) },
})
}
pub fn new() -> Result<Gunyah> {
Gunyah::new_with_path(&PathBuf::from("/dev/gunyah"))
}
}
impl Hypervisor for Gunyah {
fn try_clone(&self) -> Result<Self>
where
Self: Sized,
{
Ok(Gunyah {
gunyah: self.gunyah.try_clone()?,
})
}
fn check_capability(&self, cap: HypervisorCap) -> bool {
match cap {
HypervisorCap::UserMemory => true,
HypervisorCap::ImmediateExit => true,
HypervisorCap::StaticSwiotlbAllocationRequired => true,
HypervisorCap::HypervisorInitializedBootContext => true,
}
}
}
unsafe fn android_lend_user_memory_region(
vm: &SafeDescriptor,
slot: MemSlot,
read_only: bool,
guest_addr: u64,
memory_size: u64,
userspace_addr: *mut u8,
) -> Result<()> {
let mut flags = 0;
flags |= GH_MEM_ALLOW_READ | GH_MEM_ALLOW_EXEC;
if !read_only {
flags |= GH_MEM_ALLOW_WRITE;
}
let region = gh_userspace_memory_region {
label: slot,
flags,
guest_phys_addr: guest_addr,
memory_size,
userspace_addr: userspace_addr as u64,
};
let ret = ioctl_with_ref(vm, GH_VM_ANDROID_LEND_USER_MEM, ®ion);
if ret == 0 {
Ok(())
} else {
errno_result()
}
}
unsafe fn set_user_memory_region(
vm: &SafeDescriptor,
slot: MemSlot,
read_only: bool,
guest_addr: u64,
memory_size: u64,
userspace_addr: *mut u8,
) -> Result<()> {
let mut flags = 0;
flags |= GH_MEM_ALLOW_READ | GH_MEM_ALLOW_EXEC;
if !read_only {
flags |= GH_MEM_ALLOW_WRITE;
}
let region = gh_userspace_memory_region {
label: slot,
flags,
guest_phys_addr: guest_addr,
memory_size,
userspace_addr: userspace_addr as u64,
};
let ret = ioctl_with_ref(vm, GH_VM_SET_USER_MEM_REGION, ®ion);
if ret == 0 {
Ok(())
} else {
errno_result()
}
}
fn map_cma_region(
vm: &SafeDescriptor,
slot: MemSlot,
lend: bool,
read_only: bool,
guest_addr: u64,
guest_mem_fd: u32,
size: u64,
offset: u64,
) -> Result<()> {
let mut flags = 0;
flags |= GUNYAH_MEM_ALLOW_READ | GUNYAH_MEM_ALLOW_EXEC;
if !read_only {
flags |= GUNYAH_MEM_ALLOW_WRITE;
}
if lend {
flags |= GUNYAH_MEM_FORCE_LEND;
} else {
flags |= GUNYAH_MEM_FORCE_SHARE;
}
let region = gunyah_map_cma_mem_args {
label: slot,
guest_addr,
flags,
guest_mem_fd,
offset,
size,
};
let ret = unsafe { ioctl_with_ref(vm, GH_VM_ANDROID_MAP_CMA_MEM, ®ion) };
if ret == 0 {
Ok(())
} else {
errno_result()
}
}
#[derive(PartialEq, Eq, Hash)]
pub struct GunyahIrqRoute {
irq: u32,
level: bool,
}
pub struct GunyahVm {
gh: Gunyah,
vm: SafeDescriptor,
vm_id: Option<u16>,
pas_id: Option<u32>,
guest_mem: GuestMemory,
mem_regions: Arc<Mutex<BTreeMap<MemSlot, (Box<dyn MappedRegion>, GuestAddress)>>>,
mem_slot_gaps: Arc<Mutex<BinaryHeap<Reverse<MemSlot>>>>,
routes: Arc<Mutex<HashSet<GunyahIrqRoute>>>,
hv_cfg: crate::Config,
}
impl AsRawDescriptor for GunyahVm {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.vm.as_raw_descriptor()
}
}
impl GunyahVm {
pub fn new(
gh: &Gunyah,
vm_id: Option<u16>,
pas_id: Option<u32>,
guest_mem: GuestMemory,
cfg: Config,
) -> Result<GunyahVm> {
let ret = unsafe { ioctl_with_val(gh, GH_CREATE_VM, 0 as c_ulong) };
if ret < 0 {
return errno_result();
}
let vm_descriptor = unsafe { SafeDescriptor::from_raw_descriptor(ret) };
let mut cma_mapped = false;
for region in guest_mem.regions() {
let lend = if cfg.protection_type.isolates_memory() {
match region.options.purpose {
MemoryRegionPurpose::Bios => true,
MemoryRegionPurpose::GuestMemoryRegion => true,
#[cfg(target_arch = "aarch64")]
MemoryRegionPurpose::ProtectedFirmwareRegion => true,
MemoryRegionPurpose::ReservedMemory => true,
#[cfg(target_arch = "aarch64")]
MemoryRegionPurpose::StaticSwiotlbRegion => false,
}
} else {
false
};
if let Some(file_backed) = ®ion.options.file_backed {
if !cma_mapped {
map_cma_region(
&vm_descriptor,
region.index as MemSlot,
lend,
!file_backed.writable,
region.guest_addr.offset(),
region.shm.as_raw_descriptor().try_into().unwrap(),
region.size.try_into().unwrap(),
region.shm_offset,
)?;
cma_mapped = true;
continue;
}
}
let read_only = region
.options
.file_backed
.as_ref()
.is_some_and(|fb| !fb.writable);
unsafe {
if lend {
android_lend_user_memory_region(
&vm_descriptor,
region.index as MemSlot,
read_only,
region.guest_addr.offset(),
region.size.try_into().unwrap(),
region.host_addr as *mut u8,
)?;
} else {
set_user_memory_region(
&vm_descriptor,
region.index as MemSlot,
read_only,
region.guest_addr.offset(),
region.size.try_into().unwrap(),
region.host_addr as *mut u8,
)?;
}
}
}
Ok(GunyahVm {
gh: gh.try_clone()?,
vm: vm_descriptor,
vm_id,
pas_id,
guest_mem,
mem_regions: Arc::new(Mutex::new(BTreeMap::new())),
mem_slot_gaps: Arc::new(Mutex::new(BinaryHeap::new())),
routes: Arc::new(Mutex::new(HashSet::new())),
hv_cfg: cfg,
})
}
pub fn set_vm_auth_type_to_qcom_trusted_vm(
&self,
payload_start: GuestAddress,
payload_size: u64,
) -> Result<()> {
let gunyah_qtvm_auth_arg = gunyah_qtvm_auth_arg {
vm_id: self.vm_id.expect("VM ID not specified for a QTVM"),
pas_id: self.pas_id.expect("PAS ID not specified for a QTVM"),
guest_phys_addr: payload_start.offset(),
size: payload_size,
};
let gunyah_auth_desc = gunyah_auth_desc {
type_: gunyah_auth_type_GUNYAH_QCOM_TRUSTED_VM_TYPE,
arg_size: size_of::<gunyah_qtvm_auth_arg>() as u32,
arg: &gunyah_qtvm_auth_arg as *const gunyah_qtvm_auth_arg as u64,
};
let ret = unsafe { ioctl_with_ref(self, GH_VM_ANDROID_SET_AUTH_TYPE, &gunyah_auth_desc) };
if ret == 0 {
Ok(())
} else {
errno_result()
}
}
fn create_vcpu(&self, id: usize) -> Result<GunyahVcpu> {
let gh_fn_vcpu_arg = gh_fn_vcpu_arg {
id: id.try_into().unwrap(),
};
let function_desc = gh_fn_desc {
type_: GH_FN_VCPU,
arg_size: size_of::<gh_fn_vcpu_arg>() as u32,
arg: &gh_fn_vcpu_arg as *const gh_fn_vcpu_arg as u64,
};
let fd = unsafe { ioctl_with_ref(self, GH_VM_ADD_FUNCTION, &function_desc) };
if fd < 0 {
return errno_result();
}
let vcpu = unsafe { File::from_raw_descriptor(fd) };
let res = unsafe { ioctl(&vcpu, GH_VCPU_MMAP_SIZE) };
if res < 0 {
return errno_result();
}
let run_mmap_size = res as usize;
let run_mmap = MemoryMappingBuilder::new(run_mmap_size)
.from_file(&vcpu)
.build()
.map_err(|_| Error::new(ENOSPC))?;
Ok(GunyahVcpu {
vm: self.vm.try_clone()?,
vcpu,
id,
run_mmap: Arc::new(run_mmap),
})
}
pub fn register_irqfd(&self, label: u32, evt: &Event, level: bool) -> Result<()> {
let gh_fn_irqfd_arg = gh_fn_irqfd_arg {
fd: evt.as_raw_descriptor() as u32,
label,
flags: if level { GH_IRQFD_LEVEL } else { 0 },
..Default::default()
};
let function_desc = gh_fn_desc {
type_: GH_FN_IRQFD,
arg_size: size_of::<gh_fn_irqfd_arg>() as u32,
arg: &gh_fn_irqfd_arg as *const gh_fn_irqfd_arg as u64,
};
let ret = unsafe { ioctl_with_ref(self, GH_VM_ADD_FUNCTION, &function_desc) };
if ret == 0 {
self.routes
.lock()
.insert(GunyahIrqRoute { irq: label, level });
Ok(())
} else {
errno_result()
}
}
pub fn unregister_irqfd(&self, label: u32, _evt: &Event) -> Result<()> {
let gh_fn_irqfd_arg = gh_fn_irqfd_arg {
label,
..Default::default()
};
let function_desc = gh_fn_desc {
type_: GH_FN_IRQFD,
arg_size: size_of::<gh_fn_irqfd_arg>() as u32,
arg: &gh_fn_irqfd_arg as *const gh_fn_irqfd_arg as u64,
};
let ret = unsafe { ioctl_with_ref(self, GH_VM_REMOVE_FUNCTION, &function_desc) };
if ret == 0 {
Ok(())
} else {
errno_result()
}
}
pub fn try_clone(&self) -> Result<Self>
where
Self: Sized,
{
Ok(GunyahVm {
gh: self.gh.try_clone()?,
vm: self.vm.try_clone()?,
vm_id: self.vm_id,
pas_id: self.pas_id,
guest_mem: self.guest_mem.clone(),
mem_regions: self.mem_regions.clone(),
mem_slot_gaps: self.mem_slot_gaps.clone(),
routes: self.routes.clone(),
hv_cfg: self.hv_cfg,
})
}
fn set_dtb_config(&self, fdt_address: GuestAddress, fdt_size: usize) -> Result<()> {
let dtb_config = gh_vm_dtb_config {
guest_phys_addr: fdt_address.offset(),
size: fdt_size.try_into().unwrap(),
};
let ret = unsafe { ioctl_with_ref(self, GH_VM_SET_DTB_CONFIG, &dtb_config) };
if ret == 0 {
Ok(())
} else {
errno_result()
}
}
fn set_protected_vm_firmware_ipa(&self, fw_addr: GuestAddress, fw_size: u64) -> Result<()> {
let fw_config = gh_vm_firmware_config {
guest_phys_addr: fw_addr.offset(),
size: fw_size,
};
let ret = unsafe { ioctl_with_ref(self, GH_VM_ANDROID_SET_FW_CONFIG, &fw_config) };
if ret == 0 {
Ok(())
} else {
errno_result()
}
}
fn set_boot_pc(&self, value: u64) -> Result<()> {
self.set_boot_context(gh_vm_boot_context_reg::REG_SET_PC, 0, value)
}
fn set_boot_context(
&self,
reg_type: gh_vm_boot_context_reg::Type,
reg_idx: u8,
value: u64,
) -> Result<()> {
let reg_id = boot_context_reg_id(reg_type, reg_idx);
let boot_context = gh_vm_boot_context {
reg: reg_id,
value,
..Default::default()
};
let ret = unsafe { ioctl_with_ref(self, GH_VM_SET_BOOT_CONTEXT, &boot_context) };
if ret == 0 {
Ok(())
} else {
errno_result()
}
}
fn start(&self) -> Result<()> {
let ret = unsafe { ioctl(self, GH_VM_START) };
if ret == 0 {
Ok(())
} else {
errno_result()
}
}
fn handle_inflate(&self, guest_addr: GuestAddress, size: u64) -> Result<()> {
let range = gunyah_address_range {
guest_phys_addr: guest_addr.0,
size,
};
let ret = unsafe { ioctl_with_ref(self, GH_VM_RECLAIM_REGION, &range) };
if ret != 0 {
warn!("Gunyah failed to reclaim {:?}", range);
return errno_result();
}
match self.guest_mem.remove_range(guest_addr, size) {
Ok(_) => Ok(()),
Err(vm_memory::Error::MemoryAccess(_, MmapError::SystemCallFailed(e))) => Err(e),
Err(_) => Err(Error::new(EIO)),
}
}
}
impl Vm for GunyahVm {
fn try_clone(&self) -> Result<Self>
where
Self: Sized,
{
Ok(GunyahVm {
gh: self.gh.try_clone()?,
vm: self.vm.try_clone()?,
vm_id: self.vm_id,
pas_id: self.pas_id,
guest_mem: self.guest_mem.clone(),
mem_regions: self.mem_regions.clone(),
mem_slot_gaps: self.mem_slot_gaps.clone(),
routes: self.routes.clone(),
hv_cfg: self.hv_cfg,
})
}
fn try_clone_descriptor(&self) -> Result<SafeDescriptor> {
error!("try_clone_descriptor hasn't been tested on gunyah, returning -ENOTSUP");
Err(Error::new(ENOTSUP))
}
fn hypervisor_kind(&self) -> HypervisorKind {
HypervisorKind::Gunyah
}
fn check_capability(&self, c: VmCap) -> bool {
match c {
VmCap::ArmPmuV3 => false,
VmCap::DirtyLog => false,
VmCap::PvClock => false,
VmCap::Protected => true,
VmCap::EarlyInitCpuid => false,
#[cfg(target_arch = "x86_64")]
VmCap::BusLockDetect => false,
VmCap::ReadOnlyMemoryRegion => false,
VmCap::MemNoncoherentDma => false,
#[cfg(target_arch = "aarch64")]
VmCap::Sve => false,
}
}
fn get_guest_phys_addr_bits(&self) -> u8 {
40
}
fn get_memory(&self) -> &GuestMemory {
&self.guest_mem
}
fn add_memory_region(
&mut self,
guest_addr: GuestAddress,
mem_region: Box<dyn MappedRegion>,
read_only: bool,
_log_dirty_pages: bool,
_cache: MemCacheType,
) -> Result<MemSlot> {
let pgsz = pagesize() as u64;
let size = (mem_region.size() as u64).div_ceil(pgsz) * pgsz;
let end_addr = guest_addr.checked_add(size).ok_or(Error::new(EOVERFLOW))?;
if self.guest_mem.range_overlap(guest_addr, end_addr) {
return Err(Error::new(ENOSPC));
}
let mut regions = self.mem_regions.lock();
let mut gaps = self.mem_slot_gaps.lock();
let slot = match gaps.pop() {
Some(gap) => gap.0,
None => (regions.len() + self.guest_mem.num_regions() as usize) as MemSlot,
};
let res = unsafe {
set_user_memory_region(
&self.vm,
slot,
read_only,
guest_addr.offset(),
size,
mem_region.as_ptr(),
)
};
if let Err(e) = res {
gaps.push(Reverse(slot));
return Err(e);
}
regions.insert(slot, (mem_region, guest_addr));
Ok(slot)
}
fn msync_memory_region(&mut self, slot: MemSlot, offset: usize, size: usize) -> Result<()> {
let mut regions = self.mem_regions.lock();
let (mem, _) = regions.get_mut(&slot).ok_or_else(|| Error::new(ENOENT))?;
mem.msync(offset, size).map_err(|err| match err {
MmapError::InvalidAddress => Error::new(EFAULT),
MmapError::NotPageAligned => Error::new(EINVAL),
MmapError::SystemCallFailed(e) => e,
_ => Error::new(EIO),
})
}
fn madvise_pageout_memory_region(
&mut self,
_slot: MemSlot,
_offset: usize,
_size: usize,
) -> Result<()> {
Err(Error::new(ENOTSUP))
}
fn madvise_remove_memory_region(
&mut self,
_slot: MemSlot,
_offset: usize,
_size: usize,
) -> Result<()> {
Err(Error::new(ENOTSUP))
}
fn remove_memory_region(&mut self, _slot: MemSlot) -> Result<Box<dyn MappedRegion>> {
unimplemented!()
}
fn create_device(&self, _kind: DeviceKind) -> Result<SafeDescriptor> {
unimplemented!()
}
fn get_dirty_log(&self, _slot: MemSlot, _dirty_log: &mut [u8]) -> Result<()> {
unimplemented!()
}
fn register_ioevent(
&mut self,
evt: &Event,
addr: IoEventAddress,
datamatch: Datamatch,
) -> Result<()> {
let (do_datamatch, datamatch_value, datamatch_len) = match datamatch {
Datamatch::AnyLength => (false, 0, 0),
Datamatch::U8(v) => match v {
Some(u) => (true, u as u64, 1),
None => (false, 0, 1),
},
Datamatch::U16(v) => match v {
Some(u) => (true, u as u64, 2),
None => (false, 0, 2),
},
Datamatch::U32(v) => match v {
Some(u) => (true, u as u64, 4),
None => (false, 0, 4),
},
Datamatch::U64(v) => match v {
Some(u) => (true, u, 8),
None => (false, 0, 8),
},
};
let mut flags = 0;
if do_datamatch {
flags |= 1 << GH_IOEVENTFD_DATAMATCH;
}
let maddr = if let IoEventAddress::Mmio(maddr) = addr {
maddr
} else {
todo!()
};
let gh_fn_ioeventfd_arg = gh_fn_ioeventfd_arg {
fd: evt.as_raw_descriptor(),
datamatch: datamatch_value,
len: datamatch_len,
addr: maddr,
flags,
..Default::default()
};
let function_desc = gh_fn_desc {
type_: GH_FN_IOEVENTFD,
arg_size: size_of::<gh_fn_ioeventfd_arg>() as u32,
arg: &gh_fn_ioeventfd_arg as *const gh_fn_ioeventfd_arg as u64,
};
let ret = unsafe { ioctl_with_ref(self, GH_VM_ADD_FUNCTION, &function_desc) };
if ret == 0 {
Ok(())
} else {
errno_result()
}
}
fn unregister_ioevent(
&mut self,
_evt: &Event,
addr: IoEventAddress,
_datamatch: Datamatch,
) -> Result<()> {
let maddr = if let IoEventAddress::Mmio(maddr) = addr {
maddr
} else {
todo!()
};
let gh_fn_ioeventfd_arg = gh_fn_ioeventfd_arg {
addr: maddr,
..Default::default()
};
let function_desc = gh_fn_desc {
type_: GH_FN_IOEVENTFD,
arg_size: size_of::<gh_fn_ioeventfd_arg>() as u32,
arg: &gh_fn_ioeventfd_arg as *const gh_fn_ioeventfd_arg as u64,
};
let ret = unsafe { ioctl_with_ref(self, GH_VM_REMOVE_FUNCTION, &function_desc) };
if ret == 0 {
Ok(())
} else {
errno_result()
}
}
fn handle_io_events(&self, _addr: IoEventAddress, _data: &[u8]) -> Result<()> {
Ok(())
}
fn enable_hypercalls(&mut self, _nr: u64, _count: usize) -> Result<()> {
unimplemented!()
}
fn get_pvclock(&self) -> Result<ClockState> {
unimplemented!()
}
fn set_pvclock(&self, _state: &ClockState) -> Result<()> {
unimplemented!()
}
fn add_fd_mapping(
&mut self,
slot: u32,
offset: usize,
size: usize,
fd: &dyn AsRawDescriptor,
fd_offset: u64,
prot: Protection,
) -> Result<()> {
let mut regions = self.mem_regions.lock();
let (region, _) = regions.get_mut(&slot).ok_or_else(|| Error::new(EINVAL))?;
match region.add_fd_mapping(offset, size, fd, fd_offset, prot) {
Ok(()) => Ok(()),
Err(MmapError::SystemCallFailed(e)) => Err(e),
Err(_) => Err(Error::new(EIO)),
}
}
fn remove_mapping(&mut self, slot: u32, offset: usize, size: usize) -> Result<()> {
let mut regions = self.mem_regions.lock();
let (region, _) = regions.get_mut(&slot).ok_or_else(|| Error::new(EINVAL))?;
match region.remove_mapping(offset, size) {
Ok(()) => Ok(()),
Err(MmapError::SystemCallFailed(e)) => Err(e),
Err(_) => Err(Error::new(EIO)),
}
}
fn handle_balloon_event(&mut self, event: BalloonEvent) -> Result<()> {
match event {
BalloonEvent::Inflate(m) => self.handle_inflate(m.guest_address, m.size),
BalloonEvent::Deflate(_) => Ok(()),
BalloonEvent::BalloonTargetReached(_) => Ok(()),
}
}
}
const GH_RM_EXIT_TYPE_VM_EXIT: u16 = 0;
const GH_RM_EXIT_TYPE_PSCI_POWER_OFF: u16 = 1;
const GH_RM_EXIT_TYPE_PSCI_SYSTEM_RESET: u16 = 2;
const GH_RM_EXIT_TYPE_PSCI_SYSTEM_RESET2: u16 = 3;
const GH_RM_EXIT_TYPE_WDT_BITE: u16 = 4;
const GH_RM_EXIT_TYPE_HYP_ERROR: u16 = 5;
const GH_RM_EXIT_TYPE_ASYNC_EXT_ABORT: u16 = 6;
const GH_RM_EXIT_TYPE_VM_FORCE_STOPPED: u16 = 7;
pub struct GunyahVcpu {
vm: SafeDescriptor,
vcpu: File,
id: usize,
run_mmap: Arc<MemoryMapping>,
}
struct GunyahVcpuSignalHandle {
run_mmap: Arc<MemoryMapping>,
}
impl VcpuSignalHandleInner for GunyahVcpuSignalHandle {
fn signal_immediate_exit(&self) {
unsafe {
let run = self.run_mmap.as_ptr() as *mut gh_vcpu_run;
(*run).immediate_exit = 1;
}
}
}
impl AsRawDescriptor for GunyahVcpu {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.vcpu.as_raw_descriptor()
}
}
impl Vcpu for GunyahVcpu {
fn try_clone(&self) -> Result<Self>
where
Self: Sized,
{
let vcpu = self.vcpu.try_clone()?;
Ok(GunyahVcpu {
vm: self.vm.try_clone()?,
vcpu,
id: self.id,
run_mmap: self.run_mmap.clone(),
})
}
fn as_vcpu(&self) -> &dyn Vcpu {
self
}
fn run(&mut self) -> Result<VcpuExit> {
let ret = unsafe { ioctl(self, GH_VCPU_RUN) };
if ret != 0 {
return errno_result();
}
let run = unsafe { &mut *(self.run_mmap.as_ptr() as *mut gh_vcpu_run) };
match run.exit_reason {
GH_VCPU_EXIT_MMIO => Ok(VcpuExit::Mmio),
GH_VCPU_EXIT_STATUS => {
let status = unsafe { &mut run.__bindgen_anon_1.status };
match status.status {
GH_VM_STATUS_GH_VM_STATUS_LOAD_FAILED => Ok(VcpuExit::FailEntry {
hardware_entry_failure_reason: 0,
}),
GH_VM_STATUS_GH_VM_STATUS_CRASHED => Ok(VcpuExit::SystemEventCrash),
GH_VM_STATUS_GH_VM_STATUS_EXITED => {
info!("exit type {}", status.exit_info.type_);
match status.exit_info.type_ {
GH_RM_EXIT_TYPE_VM_EXIT => Ok(VcpuExit::SystemEventShutdown),
GH_RM_EXIT_TYPE_PSCI_POWER_OFF => Ok(VcpuExit::SystemEventShutdown),
GH_RM_EXIT_TYPE_PSCI_SYSTEM_RESET => Ok(VcpuExit::SystemEventReset),
GH_RM_EXIT_TYPE_PSCI_SYSTEM_RESET2 => Ok(VcpuExit::SystemEventReset),
GH_RM_EXIT_TYPE_WDT_BITE => Ok(VcpuExit::SystemEventCrash),
GH_RM_EXIT_TYPE_HYP_ERROR => Ok(VcpuExit::SystemEventCrash),
GH_RM_EXIT_TYPE_ASYNC_EXT_ABORT => Ok(VcpuExit::SystemEventCrash),
GH_RM_EXIT_TYPE_VM_FORCE_STOPPED => Ok(VcpuExit::SystemEventShutdown),
r => {
warn!("Unknown exit type: {}", r);
Err(Error::new(EINVAL))
}
}
}
r => {
warn!("Unknown vm status: {}", r);
Err(Error::new(EINVAL))
}
}
}
r => {
warn!("unknown gh exit reason: {}", r);
Err(Error::new(EINVAL))
}
}
}
fn id(&self) -> usize {
self.id
}
fn set_immediate_exit(&self, exit: bool) {
let run = unsafe { &mut *(self.run_mmap.as_ptr() as *mut gh_vcpu_run) };
run.immediate_exit = exit.into();
}
fn signal_handle(&self) -> VcpuSignalHandle {
VcpuSignalHandle {
inner: Box::new(GunyahVcpuSignalHandle {
run_mmap: self.run_mmap.clone(),
}),
}
}
fn handle_mmio(&self, handle_fn: &mut dyn FnMut(IoParams) -> Result<()>) -> Result<()> {
let run = unsafe { &mut *(self.run_mmap.as_ptr() as *mut gh_vcpu_run) };
assert!(run.exit_reason == GH_VCPU_EXIT_MMIO);
let mmio = unsafe { &mut run.__bindgen_anon_1.mmio };
let address = mmio.phys_addr;
let data = &mut mmio.data[..mmio.len as usize];
if mmio.is_write != 0 {
handle_fn(IoParams {
address,
operation: IoOperation::Write(data),
})
} else {
handle_fn(IoParams {
address,
operation: IoOperation::Read(data),
})
}
}
fn handle_io(&self, _handle_fn: &mut dyn FnMut(IoParams)) -> Result<()> {
unreachable!()
}
fn on_suspend(&self) -> Result<()> {
Ok(())
}
unsafe fn enable_raw_capability(&self, _cap: u32, _args: &[u64; 4]) -> Result<()> {
unimplemented!()
}
}