use core::marker::PhantomData;
use super::{
super::{memory::ControlRegion, percpu::initialize_vmx_region},
fields::{self, VmcsField, VmcsReadWrite, VmcsValue},
};
use crate::{
PhysAddr,
virtualization::{Backend, ControlMemory, VirtualizationError},
};
pub struct Vmcs<M: ControlMemory> {
region: Option<ControlRegion<M>>,
initialized: bool,
bound: bool,
local: PhantomData<*mut ()>,
}
impl<M: ControlMemory> Vmcs<M> {
pub unsafe fn new(memory: M) -> Result<Self, VirtualizationError> {
if Backend::detect() != Some(Backend::Vmx) {
return Err(VirtualizationError::Unavailable);
}
let mut region = ControlRegion::new(memory, 4096, 4096)?;
unsafe { initialize_vmx_region(&mut region)? };
Ok(Self {
region: Some(region),
initialized: false,
bound: false,
local: PhantomData,
})
}
pub fn physical_address(&self) -> PhysAddr {
self.region
.as_ref()
.expect("VMCS retains its lease until retirement")
.physical_address()
}
pub const fn is_bound(&self) -> bool {
self.bound
}
pub unsafe fn bind(&mut self) -> Result<(), VirtualizationError> {
if self.bound {
return Err(VirtualizationError::AlreadyEnabled);
}
let address = self.physical_address().as_usize() as u64;
unsafe {
if !self.initialized {
x86::bits64::vmx::vmclear(address)
.map_err(|_| VirtualizationError::InstructionFailed)?;
self.initialized = true;
}
x86::bits64::vmx::vmptrld(address)
.map_err(|_| VirtualizationError::InstructionFailed)?;
}
self.bound = true;
Ok(())
}
pub unsafe fn unbind(&mut self) -> Result<(), VirtualizationError> {
if !self.bound {
return Err(VirtualizationError::NotEnabled);
}
unsafe { x86::bits64::vmx::vmclear(self.physical_address().as_usize() as u64) }
.map_err(|_| VirtualizationError::InstructionFailed)?;
self.bound = false;
Ok(())
}
pub fn read<T: VmcsValue, A>(&self, field: VmcsField<T, A>) -> Result<T, VirtualizationError> {
if !self.bound {
return Err(VirtualizationError::NotEnabled);
}
unsafe { x86::bits64::vmx::vmread(field.encoding()) }
.map(fields::from_raw)
.map_err(|_| VirtualizationError::InstructionFailed)
}
pub fn write<T: VmcsValue>(
&mut self,
field: VmcsField<T, VmcsReadWrite>,
value: T,
) -> Result<(), VirtualizationError> {
if !self.bound {
return Err(VirtualizationError::NotEnabled);
}
unsafe { x86::bits64::vmx::vmwrite(field.encoding(), fields::into_raw(value)) }
.map_err(|_| VirtualizationError::InstructionFailed)
}
pub fn into_memory(mut self) -> Result<M, Self> {
if self.bound {
return Err(self);
}
Ok(self
.region
.take()
.expect("VMCS retains its lease until retirement")
.into_memory())
}
}
impl<M: ControlMemory> Drop for Vmcs<M> {
fn drop(&mut self) {
if self.bound
&& let Some(region) = self.region.as_mut()
{
region.retain_on_failed_retirement();
}
}
}