mod vmentry;
mod vmexit;
use std::collections::HashMap;
use std::ptr::null_mut;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{Receiver, Sender};
use std::sync::{Arc, mpsc};
use parking_lot::Mutex;
use snafu::ResultExt;
use crate::arch::reg::{MpidrEl1, Pstate, Reg, SReg};
use crate::hv::hvf::check_ret;
use crate::hv::hvf::vm::{HvfVm, VcpuEvent};
use crate::hv::{Result, Vcpu, VmEntry, VmExit, error};
use crate::sys::hvf::{
HvExitReason, HvReg, HvVcpuExit, hv_vcpu_create, hv_vcpu_destroy, hv_vcpu_get_reg,
hv_vcpu_get_sys_reg, hv_vcpu_run, hv_vcpu_set_reg, hv_vcpu_set_sys_reg,
};
#[derive(Debug)]
pub struct VcpuHandle {
pub vcpu_id: u64,
pub power_on: Arc<AtomicBool>,
pub sender: Sender<VcpuEvent>,
}
#[derive(Debug)]
pub struct HvfVcpu {
exit: *mut HvVcpuExit,
vcpu_id: u64,
vmexit: Option<VmExit>,
exit_reg: Option<HvReg>,
vcpus: Arc<Mutex<HashMap<MpidrEl1, Arc<VcpuHandle>>>>,
receiver: Receiver<VcpuEvent>,
power_on: Arc<AtomicBool>,
}
impl HvfVcpu {
fn power_on(&mut self, pc: u64, context: u64) -> Result<()> {
let pstate = (Pstate::EL_H | Pstate::EL_BIT2).bits() as u64;
self.set_regs(&[(Reg::Pc, pc), (Reg::X0, context), (Reg::Pstate, pstate)])?;
self.set_sregs(&[(SReg::SCTLR_EL1, 0x0)])?;
self.power_on.store(true, Ordering::Relaxed);
Ok(())
}
pub fn new(vm: &HvfVm, _: u16, identity: u64) -> Result<Self> {
let mut exit = null_mut();
let mut vcpu_id = 0;
let ret = unsafe { hv_vcpu_create(&mut vcpu_id, &mut exit, null_mut()) };
check_ret(ret).context(error::CreateVcpu)?;
let mpidr = MpidrEl1(identity);
let ret = unsafe { hv_vcpu_set_sys_reg(vcpu_id, SReg::MPIDR_EL1, mpidr.0) };
check_ret(ret).context(error::VcpuReg)?;
let (sender, receiver) = mpsc::channel();
let power_on = Arc::new(AtomicBool::new(false));
let handle = Arc::new(VcpuHandle {
vcpu_id,
sender,
power_on: power_on.clone(),
});
vm.vcpus.lock().insert(mpidr, handle);
Ok(HvfVcpu {
exit,
vcpu_id,
vmexit: None,
exit_reg: None,
vcpus: vm.vcpus.clone(),
receiver,
power_on,
})
}
}
impl Drop for HvfVcpu {
fn drop(&mut self) {
let ret = unsafe { hv_vcpu_destroy(self.vcpu_id) };
if let Err(e) = check_ret(ret) {
log::error!("hv_vcpu_destroy: {e:?}");
}
}
}
enum HvfReg {
Reg(HvReg),
SReg(SReg),
}
impl Reg {
fn to_hvf_reg(self) -> HvfReg {
match self {
Reg::X0 => HvfReg::Reg(HvReg::X0),
Reg::X1 => HvfReg::Reg(HvReg::X1),
Reg::X2 => HvfReg::Reg(HvReg::X2),
Reg::X3 => HvfReg::Reg(HvReg::X3),
Reg::X4 => HvfReg::Reg(HvReg::X4),
Reg::X5 => HvfReg::Reg(HvReg::X5),
Reg::X6 => HvfReg::Reg(HvReg::X6),
Reg::X7 => HvfReg::Reg(HvReg::X7),
Reg::X8 => HvfReg::Reg(HvReg::X8),
Reg::X9 => HvfReg::Reg(HvReg::X9),
Reg::X10 => HvfReg::Reg(HvReg::X10),
Reg::X11 => HvfReg::Reg(HvReg::X11),
Reg::X12 => HvfReg::Reg(HvReg::X12),
Reg::X13 => HvfReg::Reg(HvReg::X13),
Reg::X14 => HvfReg::Reg(HvReg::X14),
Reg::X15 => HvfReg::Reg(HvReg::X15),
Reg::X16 => HvfReg::Reg(HvReg::X16),
Reg::X17 => HvfReg::Reg(HvReg::X17),
Reg::X18 => HvfReg::Reg(HvReg::X18),
Reg::X19 => HvfReg::Reg(HvReg::X19),
Reg::X20 => HvfReg::Reg(HvReg::X20),
Reg::X21 => HvfReg::Reg(HvReg::X21),
Reg::X22 => HvfReg::Reg(HvReg::X22),
Reg::X23 => HvfReg::Reg(HvReg::X23),
Reg::X24 => HvfReg::Reg(HvReg::X24),
Reg::X25 => HvfReg::Reg(HvReg::X25),
Reg::X26 => HvfReg::Reg(HvReg::X26),
Reg::X27 => HvfReg::Reg(HvReg::X27),
Reg::X28 => HvfReg::Reg(HvReg::X28),
Reg::X29 => HvfReg::Reg(HvReg::X29),
Reg::X30 => HvfReg::Reg(HvReg::X30),
Reg::Sp => HvfReg::SReg(SReg::SP_EL0),
Reg::Pc => HvfReg::Reg(HvReg::PC),
Reg::Pstate => HvfReg::Reg(HvReg::CPSR),
}
}
}
impl Vcpu for HvfVcpu {
fn reset(&mut self, is_bsp: bool) -> Result<()> {
self.power_on.store(is_bsp, Ordering::Relaxed);
self.set_sregs(&[(SReg::SCTLR_EL1, 0x0)])
}
fn dump(&self) -> Result<()> {
unimplemented!()
}
fn run(&mut self, entry: VmEntry) -> Result<VmExit> {
match entry {
VmEntry::None => {}
VmEntry::Mmio { data } => self.entry_mmio(data)?,
VmEntry::Shutdown => return Ok(VmExit::Shutdown),
VmEntry::Reboot => return Ok(VmExit::Reboot),
VmEntry::Pause => return Ok(VmExit::Paused),
}
if !self.power_on.load(Ordering::Relaxed) {
match self.receiver.recv()? {
VcpuEvent::Interrupt => return Ok(VmExit::Interrupted),
VcpuEvent::PowerOn { pc, context } => {
self.power_on(pc, context)?;
}
}
}
loop {
let ret = unsafe { hv_vcpu_run(self.vcpu_id) };
check_ret(ret).context(error::RunVcpu)?;
let exit = unsafe { &*self.exit };
match exit.reason {
HvExitReason::EXCEPTION => {
self.handle_exception(&exit.exception)?;
}
HvExitReason::CANCEL => break Ok(VmExit::Interrupted),
_ => {
break error::VmExit {
msg: format!("{exit:x?}"),
}
.fail();
}
}
if let Some(exit) = self.vmexit.take() {
break Ok(exit);
}
}
}
fn get_reg(&self, reg: Reg) -> Result<u64> {
let hvf_reg = reg.to_hvf_reg();
let mut val = 0;
let ret = match hvf_reg {
HvfReg::Reg(r) => unsafe { hv_vcpu_get_reg(self.vcpu_id, r, &mut val) },
HvfReg::SReg(r) => unsafe { hv_vcpu_get_sys_reg(self.vcpu_id, r, &mut val) },
};
check_ret(ret).context(error::VcpuReg)?;
Ok(val)
}
fn set_regs(&mut self, vals: &[(Reg, u64)]) -> Result<()> {
for (reg, val) in vals {
let hvf_reg = reg.to_hvf_reg();
let ret = match hvf_reg {
HvfReg::Reg(r) => unsafe { hv_vcpu_set_reg(self.vcpu_id, r, *val) },
HvfReg::SReg(r) => unsafe { hv_vcpu_set_sys_reg(self.vcpu_id, r, *val) },
};
check_ret(ret).context(error::VcpuReg)?;
}
Ok(())
}
fn get_sreg(&self, reg: SReg) -> Result<u64> {
let mut val = 0;
let ret = unsafe { hv_vcpu_get_sys_reg(self.vcpu_id, reg, &mut val) };
check_ret(ret).context(error::VcpuReg)?;
Ok(val)
}
fn set_sregs(&mut self, sregs: &[(SReg, u64)]) -> Result<()> {
for (reg, val) in sregs {
let ret = unsafe { hv_vcpu_set_sys_reg(self.vcpu_id, *reg, *val) };
check_ret(ret).context(error::VcpuReg)?;
}
Ok(())
}
}
#[cfg(test)]
#[path = "vcpu_test.rs"]
mod tests;