use crate::{
PhysAddr,
virtualization::{Backend, VirtualizationError},
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct EptPointer(u64);
impl EptPointer {
pub unsafe fn for_current_cpu(
root: PhysAddr,
accessed_dirty: bool,
) -> Result<Self, VirtualizationError> {
unsafe { EptCapabilities::current() }?.pointer(root, accessed_dirty)
}
pub const fn bits(self) -> u64 {
self.0
}
pub unsafe fn invalidate(self) -> Result<(), VirtualizationError> {
let capability = unsafe { x86::msr::rdmsr(0x48c) };
let scope = invalidation_scope(capability)?;
unsafe { super::invalidate_ept(scope, self.0) }
}
}
#[derive(Clone, Copy)]
pub(crate) struct EptCapabilities {
capability: u64,
address_bits: u32,
}
impl EptCapabilities {
pub(crate) unsafe fn current() -> Result<Self, VirtualizationError> {
if Backend::detect() != Some(Backend::Vmx) {
return Err(VirtualizationError::Unavailable);
}
let capability = unsafe { x86::msr::rdmsr(0x48c) };
Ok(Self {
capability,
address_bits: super::super::percpu::physical_address_bits().min(52),
})
}
fn pointer(
self,
root: PhysAddr,
accessed_dirty: bool,
) -> Result<EptPointer, VirtualizationError> {
if self.capability & ((1 << 6) | (1 << 14)) != ((1 << 6) | (1 << 14))
|| (accessed_dirty && self.capability & (1 << 21) == 0)
{
return Err(VirtualizationError::UnsupportedPaging);
}
let address = root.as_usize() as u64;
if address & 4095 != 0
|| !(12..64).contains(&self.address_bits)
|| address > (1u64 << self.address_bits) - 4096
{
return Err(VirtualizationError::InvalidRoot);
}
Ok(EptPointer(
address | 6 | (3 << 3) | (u64::from(accessed_dirty) << 6),
))
}
pub(crate) unsafe fn invalidate(self, bits: u64) -> Result<(), VirtualizationError> {
let root = PhysAddr::from_usize((bits & 0x000f_ffff_ffff_f000) as usize);
let pointer = self.pointer(root, bits & (1 << 6) != 0)?;
if pointer.bits() != bits {
return Err(VirtualizationError::InvalidRoot);
}
let scope = invalidation_scope(self.capability)?;
unsafe { super::invalidate_ept(scope, bits) }
}
}
fn invalidation_scope(capability: u64) -> Result<super::EptInvalidation, VirtualizationError> {
if capability & (1 << 20) == 0 {
Err(VirtualizationError::UnsupportedPaging)
} else if capability & (1 << 25) != 0 {
Ok(super::EptInvalidation::SingleContext)
} else if capability & (1 << 26) != 0 {
Ok(super::EptInvalidation::AllContexts)
} else {
Err(VirtualizationError::UnsupportedPaging)
}
}