Documentation
use crate::x86::msr;

#[derive(Clone, Copy)]
pub enum VmxControl {
    PinBased,
    ProcessorBased,
    ProcessorBased2,
    VmExit,
    VmEntry,
}
pub fn adjust_vmx_controls(control: VmxControl, requested_value: u64) -> u64 {
    const IA32_VMX_BASIC_VMX_CONTROLS_FLAG: u64 = 1 << 55;

    let vmx_basic =  msr::rdmsr(msr::IA32_VMX_BASIC);
    let true_cap_msr_supported = (vmx_basic & IA32_VMX_BASIC_VMX_CONTROLS_FLAG) != 0;

    let cap_msr = match (control, true_cap_msr_supported) {
        (VmxControl::PinBased, true) => msr::IA32_VMX_TRUE_PINBASED_CTLS,
        (VmxControl::PinBased, false) => msr::IA32_VMX_PINBASED_CTLS,
        (VmxControl::ProcessorBased, true) => msr::IA32_VMX_TRUE_PROCBASED_CTLS,
        (VmxControl::ProcessorBased, false) => msr::IA32_VMX_PROCBASED_CTLS,
        (VmxControl::VmExit, true) => msr::IA32_VMX_TRUE_EXIT_CTLS,
        (VmxControl::VmExit, false) => msr::IA32_VMX_EXIT_CTLS,
        (VmxControl::VmEntry, true) => msr::IA32_VMX_TRUE_ENTRY_CTLS,
        (VmxControl::VmEntry, false) => msr::IA32_VMX_ENTRY_CTLS,
        // There is no TRUE MSR for IA32_VMX_PROCBASED_CTLS2. Just use IA32_VMX_PROCBASED_CTLS2 unconditionally.
        (VmxControl::ProcessorBased2, _) => msr::IA32_VMX_PROCBASED_CTLS2,
    };

    let capabilities =msr::rdmsr(cap_msr);
    let allowed0 = capabilities as u32;
    let allowed1 = (capabilities >> 32) as u32;
    let mut effective_value = u32::try_from(requested_value).unwrap();
    effective_value |= allowed0;
    effective_value &= allowed1;
    u64::from(effective_value)
}