Documentation

//! A module providing utilities and structures for handling VM exits.
//!
//! This module focuses on the reasons for VM exits, VM instruction errors, and the associated handlers for each exit type.
//! The handlers interpret and respond to different VM exit reasons, ensuring the safe and correct execution of the virtual machine.

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;

/// Represents the type of VM exit.
#[derive(PartialOrd, PartialEq)]
pub enum ExitType {
    ExitHypervisor,
    IncrementRIP,
    Continue,
}

/// Represents a VM exit, which can be caused by various reasons.
///
/// A VM exit transfers control from the guest to the host (hypervisor).
/// The `VmExit` structure provides methods to handle various VM exit reasons and ensures the correct and safe continuation of the guest's execution.
pub struct VmExit;

impl VmExit {
    pub fn new() -> Self {
        Self
    }

    /// Handles the VM-exit.
    ///
    /// This function interprets the VM exit reason and invokes the appropriate handler based on the exit type.
    ///
    /// # Arguments
    ///
    /// * `registers` - A mutable reference to the guest's current register state.
    ///
    /// # Returns
    ///
    /// A result containing the VM exit reason if the handling was successful or an error if the VM exit reason is unknown or unsupported.
    ///
    /// Reference: IntelĀ® 64 and IA-32 Architectures Software Developer's Manual: 25.9 VM-EXIT INFORMATION FIELDS
    /// - APPENDIX C VMX BASIC EXIT REASONS
    /// - Table C-1. Basic Exit Reasons
    pub fn handle_vmexit<T: Addrtransfer + GetEPTP>(
        &self,
        guest_registers: &mut GuestRegisters,
        vmx: &mut T,
    ) -> Result<(), HypervisorError> {
        log::trace!("Handling VMEXIT...");

        // Upon VM-exit, transfer the guest register values from VMCS to `self.registers` to ensure it reflects the latest and complete state.
        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
        );

        // IntelĀ® 64 and IA-32 Architectures Software Developer's Manual: 26.1.2 Instructions That Cause VM Exits Unconditionally:
        // - The following instructions cause VM exits when they are executed in VMX non-root operation: CPUID, GETSEC, INVD, and XSETBV.
        // - This is also true of instructions introduced with VMX, which include: INVEPT, INVVPID, VMCALL, VMCLEAR, VMLAUNCH, VMPTRLD, VMPTRST, VMRESUME, VMXOFF, and VMXON.
        //
        // 26.1.3 Instructions That Cause VM Exits Conditionally: Certain instructions cause VM exits in VMX non-root operation depending on the setting of the VM-execution controls.
        let exit_type = match basic_exit_reason {
            VmxBasicExitReason::ExceptionOrNmi => handle_exception(guest_registers, vmx),
            VmxBasicExitReason::Cpuid => handle_cpuid(guest_registers),

            // Grouping multiple exit reasons that are handled by the same function
            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::Vmread
            // |VmxBasicExitReason::Vmwrite =>ExitType::ExitHypervisor,
            // guest_registers.rflags |= 0x41;  vmwrite(guest::RFLAGS, guest_registers.rflags).unwrap();  ExitType::IncrementRIP},
            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::Rdtsc => handle_rdtsc(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(());
    }

    /// Advances the guest's instruction pointer (RIP) after a VM exit.
    ///
    /// When a VM exit occurs, the guest's execution is interrupted, and control is transferred
    /// to the hypervisor. To ensure that the guest does not re-execute the instruction that
    /// caused the VM exit, the hypervisor needs to advance the guest's RIP to the next instruction.
    #[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)?;
       //log::info!("Guest RIP  to: {:#x}",guest_registers.rip);
        guest_registers.rip += len;
        vmwrite(guest::RIP, guest_registers.rip)?;
        
         //log::info!("Guest RIP GOLABE to: {:#x}",guest_registers.rip  );
        log::trace!("Guest RIP advanced to: {:#x}", vmread(guest::RIP)?);
        Ok(())
    }
}