use crate::{PhysAddr, paging::PageTableEntry};
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct EptFlags: u64 {
const READ = 1 << 0;
const WRITE = 1 << 1;
const EXECUTE = 1 << 2;
const MEM_TYPE_MASK = 7 << 3;
const IGNORE_PAT = 1 << 6;
const HUGE_PAGE = 1 << 7;
const ACCESSED = 1 << 8;
const DIRTY = 1 << 9;
const EXECUTE_FOR_USER = 1 << 10;
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EptMemoryType {
Uncached = 0,
WriteCombining = 1,
WriteThrough = 4,
WriteProtected = 5,
WriteBack = 6,
}
impl EptFlags {
pub const fn with_memory_type(self, memory_type: EptMemoryType) -> Self {
Self::from_bits_retain(
(self.bits() & !Self::MEM_TYPE_MASK.bits()) | ((memory_type as u64) << 3),
)
}
pub const fn memory_type(self) -> Option<EptMemoryType> {
match (self.bits() & Self::MEM_TYPE_MASK.bits()) >> 3 {
0 => Some(EptMemoryType::Uncached),
1 => Some(EptMemoryType::WriteCombining),
4 => Some(EptMemoryType::WriteThrough),
5 => Some(EptMemoryType::WriteProtected),
6 => Some(EptMemoryType::WriteBack),
_ => None,
}
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Default)]
pub struct EptEntry(u64);
impl EptEntry {
const PHYS_ADDR_MASK: u64 = 0x000f_ffff_ffff_f000;
pub const fn from_parts(paddr: PhysAddr, flags: EptFlags) -> Self {
Self((paddr.as_usize() as u64 & Self::PHYS_ADDR_MASK) | flags.bits())
}
pub fn flags(self) -> EptFlags {
EptFlags::from_bits_truncate(self.0)
}
}
impl PageTableEntry for EptEntry {
type PteConfig = EptFlags;
fn new_page(paddr: PhysAddr, mut flags: EptFlags, is_huge: bool) -> Self {
flags.set(EptFlags::HUGE_PAGE, is_huge);
Self::from_parts(paddr, flags)
}
fn new_table(paddr: PhysAddr) -> Self {
Self::from_parts(paddr, EptFlags::READ | EptFlags::WRITE | EptFlags::EXECUTE)
}
fn paddr(&self, _is_dir: bool) -> PhysAddr {
PhysAddr::from_usize((self.0 & Self::PHYS_ADDR_MASK) as usize)
}
fn config(&self, _is_dir: bool) -> EptFlags {
self.flags()
}
fn present(&self) -> bool {
self.flags()
.intersects(EptFlags::READ | EptFlags::WRITE | EptFlags::EXECUTE)
}
fn huge(&self, is_dir: bool) -> bool {
is_dir && self.flags().contains(EptFlags::HUGE_PAGE)
}
fn unused(&self) -> bool {
self.0 == 0
}
fn clear(&mut self) {
self.0 = 0;
}
}
impl core::fmt::Debug for EptEntry {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("EptEntry")
.field("raw", &self.0)
.field("paddr", &self.paddr(false))
.field("flags", &self.flags())
.field("memory_type", &self.flags().memory_type())
.finish()
}
}