Documentation
use core::arch::asm;

use thiserror_no_std::Error;

use crate::x86::rfalgs::{self, RFlags};

#[derive(Error,Debug, PartialEq)]
pub enum VmFail {
    /// VMCS pointer is valid, but some other error was encountered. Read
    /// VM-instruction error field of VMCS for more details.
    VmFailValid,
    /// VMCS pointer is not valid.
    VmFailInvalid,
}



pub type Result<T> = core::result::Result<T, VmFail>;

#[inline(always)]
pub fn vmx_capture_status() -> Result<()> {
    let flags = rfalgs::read();

    if flags.contains(RFlags::FLAGS_ZF) {
        Err(VmFail::VmFailValid)
    } else if flags.contains(RFlags::FLAGS_CF) {
        Err(VmFail::VmFailInvalid)
    } else {
        Ok(())
    }
}

/// Enable VMX operation.
///
/// `addr` specifies a 4KB-aligned physical address of VMXON region initialized
/// in accordance with Intel SDM, Volume 3C, Section 24.11.5.
///
/// # Safety
/// Needs CPL 0.
pub fn vmxon(addr: u64) -> Result<()> {
    unsafe { 
        asm!("vmxon ({0})", in(reg) &addr, options(att_syntax));
        vmx_capture_status()
    }
    
}

/// Disable VMX operation.
///
/// # Safety
/// Needs CPL 0.
pub  fn vmxoff() -> Result<()> {
    unsafe { asm!("vmxoff");
        vmx_capture_status()
    }
    
}

/// Clear VMCS.
///
/// Ensures that VMCS data maintained on the processor is copied to the VMCS region
/// located at 4KB-aligned physical address `addr` and initializes some parts of it.
///
/// # Safety
/// Needs CPL 0.
pub  fn vmclear(addr: u64) -> Result<()> {
    unsafe { 
        asm!("vmclear ({0})", in(reg) &addr, options(att_syntax));
        vmx_capture_status()
    }
    
}

/// Load current VMCS pointer.
///
/// Marks the current-VMCS pointer valid and loads it with the physical address `addr`.
///
/// # Safety
/// Needs CPL 0.
pub fn vmptrld(addr: u64) -> Result<()> {
    unsafe { asm!("vmptrld ({0})", in(reg) &addr, options(att_syntax));
        vmx_capture_status()
     }
   
}

/// Return current VMCS pointer.
///
/// # Safety
/// Needs CPL 0.
pub  fn vmptrst() -> Result<u64> {
    let value: u64 = 0;
    unsafe { asm!("vmptrst ({0})", in(reg) &value, options(att_syntax)) };
    vmx_capture_status().and(Ok(value))
}

/// Read a specified field from a VMCS.
///
/// # Safety
/// Needs CPL 0.
pub fn vmread(field: u32) -> Result<u64> {
    let field: u64 = field.into();
    let value: u64;
    unsafe { asm!("vmread {0}, {1}", in(reg) field, out(reg) value, options(att_syntax)) };
    vmx_capture_status().and(Ok(value))
}

/// Write to a specified field in a VMCS.
///
/// # Safety
/// Needs CPL 0.
pub fn vmwrite(field: u32, value: u64) -> Result<()> {
    let field: u64 = field.into();
    unsafe { asm!("vmwrite {1}, {0}", in(reg) field, in(reg) value, options(att_syntax))};
     vmx_capture_status()
}

/// Launch virtual machine.
///
/// # Safety
/// Needs CPL 0.
#[inline(always)]
pub  fn vmlaunch() -> Result<()> {
    unsafe { asm!("vmlaunch") };
    vmx_capture_status()
}

/// Resume virtual machine.
///
/// # Safety
/// Needs CPL 0.
#[inline(always)]
pub fn vmresume() -> Result<()> {
    unsafe { asm!("vmresume")}; 
    vmx_capture_status()
}