use crate::{
cputure::GuestRegisters, error::HypervisorError, share_data::GetEPTP, vmexit::{
cpuid::handle_cpuid, exception::{handle_exception, handle_undefined_opcode_exception}, invd::handle_invd, invept::handle_invept, invpid::handle_invvpid, msrops::handle_msr_access, xsetbv::handle_xsetbv
}, vmxerror::VmxBasicExitReason, x86::{vmx_consts::{guest, ro}, vmx_support::{vmread, vmwrite}}, x86_64::addr::Addrtransfer
};
pub mod exception;
pub mod cpuid;
pub mod invd;
pub mod xsetbv;
pub mod msrops;
pub mod ept;
pub mod invept;
pub mod invpid;
#[derive(PartialOrd, PartialEq)]
pub enum ExitType {
ExitHypervisor,
IncrementRIP,
Continue,
}
pub struct VmExit;
impl VmExit {
pub fn new() -> Self {
Self
}
pub fn handle_vmexit<T: Addrtransfer + GetEPTP>(
&self,
guest_registers: &mut GuestRegisters,
vmx: &mut T,
) -> Result<(), HypervisorError> {
log::trace!("Handling VMEXIT...");
guest_registers.rip = vmread(guest::RIP)?;
guest_registers.rsp = vmread(guest::RSP)?;
guest_registers.rflags = vmread(guest::RFLAGS)?;
let exit_reason = vmread(ro::EXIT_REASON)? as u32;
let Some(basic_exit_reason) = VmxBasicExitReason::from_u32(exit_reason) else {
log::error!("Unknown exit reason: {:#x}", exit_reason);
return Err(HypervisorError::UnknownVMExitReason);
};
log::trace!("Basic Exit Reason: {}", basic_exit_reason);
log::trace!(
"Guest Registers before handling vmexit: {:#x?}",
guest_registers
);
let exit_type = match basic_exit_reason {
VmxBasicExitReason::ExceptionOrNmi => handle_exception(guest_registers, vmx),
VmxBasicExitReason::Cpuid => handle_cpuid(guest_registers),
VmxBasicExitReason::Getsec
| VmxBasicExitReason::Vmcall
| VmxBasicExitReason::Vmclear
| VmxBasicExitReason::Vmlaunch
| VmxBasicExitReason::Vmptrld
| VmxBasicExitReason::Vmptrst
| VmxBasicExitReason::Vmread
| VmxBasicExitReason::Vmresume
| VmxBasicExitReason::Vmwrite
| VmxBasicExitReason::Vmxon
| VmxBasicExitReason::Vmxoff => handle_undefined_opcode_exception(),
VmxBasicExitReason::Rdmsr => handle_msr_access(guest_registers, msrops::MsrAccessType::Read),
VmxBasicExitReason::Wrmsr => handle_msr_access(guest_registers, msrops::MsrAccessType::Write),
VmxBasicExitReason::Invd => handle_invd(guest_registers),
VmxBasicExitReason::EptViolation => ept::handle_ept_violation(guest_registers, vmx),
VmxBasicExitReason::EptMisconfiguration => ept::handle_ept_misconfiguration(),
VmxBasicExitReason::Invept => handle_invept(),
VmxBasicExitReason::Invvpid => handle_invvpid(),
VmxBasicExitReason::Xsetbv => handle_xsetbv(guest_registers),
_ => return Err(HypervisorError::UnhandledVmExit),
};
if exit_type == ExitType::IncrementRIP {
self.advance_guest_rip(guest_registers)?;
}
log::trace!(
"Guest registers after handling vmexit: {:#x?}",
guest_registers
);
log::trace!("VMEXIT handled successfully.");
return Ok(());
}
#[rustfmt::skip]
fn advance_guest_rip(&self, guest_registers: &mut GuestRegisters) -> Result<(), HypervisorError>{
log::trace!("Advancing guest RIP...");
let len = vmread(ro::VMEXIT_INSTRUCTION_LEN)?;
guest_registers.rip += len;
vmwrite(guest::RIP, guest_registers.rip)?;
log::trace!("Guest RIP advanced to: {:#x}", vmread(guest::RIP)?);
Ok(())
}
}