#![expect(dead_code)]
use alloc::fmt;
use core::ops::Range;
use cfg_if::cfg_if;
pub(crate) use util::{__memcpy_fallible, __memset_fallible};
use x86_64::{instructions::tlb, structures::paging::PhysFrame, VirtAddr};
use crate::{
mm::{
page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags as PrivFlags},
page_table::PageTableEntryTrait,
Paddr, PagingConstsTrait, PagingLevel, PodOnce, Vaddr, PAGE_SIZE,
},
Pod,
};
mod util;
pub(crate) const NR_ENTRIES_PER_PAGE: usize = 512;
#[derive(Clone, Debug, Default)]
pub struct PagingConsts {}
impl PagingConstsTrait for PagingConsts {
const BASE_PAGE_SIZE: usize = 4096;
const NR_LEVELS: PagingLevel = 4;
const ADDRESS_WIDTH: usize = 48;
const VA_SIGN_EXT: bool = true;
const HIGHEST_TRANSLATION_LEVEL: PagingLevel = 2;
const PTE_SIZE: usize = core::mem::size_of::<PageTableEntry>();
}
bitflags::bitflags! {
#[derive(Pod)]
#[repr(C)]
pub struct PageTableFlags: usize {
const PRESENT = 1 << 0;
const WRITABLE = 1 << 1;
const USER = 1 << 2;
const WRITE_THROUGH = 1 << 3;
const NO_CACHE = 1 << 4;
const ACCESSED = 1 << 5;
const DIRTY = 1 << 6;
const HUGE = 1 << 7;
const GLOBAL = 1 << 8;
#[cfg(feature = "cvm_guest")]
const SHARED = 1 << 51;
const HIGH_IGN1 = 1 << 52;
const HIGH_IGN2 = 1 << 53;
const NO_EXECUTE = 1 << 63;
}
}
pub(crate) fn tlb_flush_addr(vaddr: Vaddr) {
tlb::flush(VirtAddr::new(vaddr as u64));
}
pub(crate) fn tlb_flush_addr_range(range: &Range<Vaddr>) {
for vaddr in range.clone().step_by(PAGE_SIZE) {
tlb_flush_addr(vaddr);
}
}
pub(crate) fn tlb_flush_all_excluding_global() {
tlb::flush_all();
}
pub(crate) fn tlb_flush_all_including_global() {
unsafe {
x86_64::registers::control::Cr4::update(|cr4| {
*cr4 -= x86_64::registers::control::Cr4Flags::PAGE_GLOBAL;
});
x86_64::registers::control::Cr4::update(|cr4| {
*cr4 |= x86_64::registers::control::Cr4Flags::PAGE_GLOBAL;
});
}
}
#[derive(Clone, Copy, Pod, Default)]
#[repr(C)]
pub struct PageTableEntry(usize);
pub unsafe fn activate_page_table(root_paddr: Paddr, root_pt_cache: CachePolicy) {
let addr = PhysFrame::from_start_address(x86_64::PhysAddr::new(root_paddr as u64)).unwrap();
let flags = match root_pt_cache {
CachePolicy::Writeback => x86_64::registers::control::Cr3Flags::empty(),
CachePolicy::Writethrough => x86_64::registers::control::Cr3Flags::PAGE_LEVEL_WRITETHROUGH,
CachePolicy::Uncacheable => x86_64::registers::control::Cr3Flags::PAGE_LEVEL_CACHE_DISABLE,
_ => panic!("unsupported cache policy for the root page table"),
};
unsafe { x86_64::registers::control::Cr3::write(addr, flags) };
}
pub fn current_page_table_paddr() -> Paddr {
x86_64::registers::control::Cr3::read_raw()
.0
.start_address()
.as_u64() as Paddr
}
impl PageTableEntry {
cfg_if! {
if #[cfg(feature = "cvm_guest")] {
const PHYS_ADDR_MASK: usize = 0x7_FFFF_FFFF_F000;
} else {
const PHYS_ADDR_MASK: usize = 0xF_FFFF_FFFF_F000;
}
}
const PROP_MASK: usize = !Self::PHYS_ADDR_MASK & !PageTableFlags::HUGE.bits();
}
macro_rules! parse_flags {
($val:expr, $from:expr, $to:expr) => {
($val as usize & $from.bits() as usize) >> $from.bits().ilog2() << $to.bits().ilog2()
};
}
impl PodOnce for PageTableEntry {}
impl PageTableEntryTrait for PageTableEntry {
fn is_present(&self) -> bool {
self.0 & PageTableFlags::PRESENT.bits() != 0 || self.0 & PageTableFlags::HUGE.bits() != 0
}
fn new_page(paddr: Paddr, _level: PagingLevel, prop: PageProperty) -> Self {
let flags = PageTableFlags::HUGE.bits();
let mut pte = Self(paddr & Self::PHYS_ADDR_MASK | flags);
pte.set_prop(prop);
pte
}
fn new_pt(paddr: Paddr) -> Self {
let flags = PageTableFlags::PRESENT.bits()
| PageTableFlags::WRITABLE.bits()
| PageTableFlags::USER.bits();
Self(paddr & Self::PHYS_ADDR_MASK | flags)
}
fn paddr(&self) -> Paddr {
self.0 & Self::PHYS_ADDR_MASK
}
fn prop(&self) -> PageProperty {
let flags = (parse_flags!(self.0, PageTableFlags::PRESENT, PageFlags::R))
| (parse_flags!(self.0, PageTableFlags::WRITABLE, PageFlags::W))
| (parse_flags!(!self.0, PageTableFlags::NO_EXECUTE, PageFlags::X))
| (parse_flags!(self.0, PageTableFlags::ACCESSED, PageFlags::ACCESSED))
| (parse_flags!(self.0, PageTableFlags::DIRTY, PageFlags::DIRTY))
| (parse_flags!(self.0, PageTableFlags::HIGH_IGN1, PageFlags::AVAIL1))
| (parse_flags!(self.0, PageTableFlags::HIGH_IGN2, PageFlags::AVAIL2));
let priv_flags = (parse_flags!(self.0, PageTableFlags::USER, PrivFlags::USER))
| (parse_flags!(self.0, PageTableFlags::GLOBAL, PrivFlags::GLOBAL));
#[cfg(feature = "cvm_guest")]
let priv_flags =
priv_flags | (parse_flags!(self.0, PageTableFlags::SHARED, PrivFlags::SHARED));
let cache = if self.0 & PageTableFlags::NO_CACHE.bits() != 0 {
CachePolicy::Uncacheable
} else if self.0 & PageTableFlags::WRITE_THROUGH.bits() != 0 {
CachePolicy::Writethrough
} else {
CachePolicy::Writeback
};
PageProperty {
flags: PageFlags::from_bits(flags as u8).unwrap(),
cache,
priv_flags: PrivFlags::from_bits(priv_flags as u8).unwrap(),
}
}
fn set_prop(&mut self, prop: PageProperty) {
if !self.is_present() {
return;
}
let mut flags = PageTableFlags::empty().bits();
flags |= (parse_flags!(prop.flags.bits(), PageFlags::R, PageTableFlags::PRESENT))
| (parse_flags!(prop.flags.bits(), PageFlags::W, PageTableFlags::WRITABLE))
| (parse_flags!(!prop.flags.bits(), PageFlags::X, PageTableFlags::NO_EXECUTE))
| (parse_flags!(
prop.flags.bits(),
PageFlags::ACCESSED,
PageTableFlags::ACCESSED
))
| (parse_flags!(prop.flags.bits(), PageFlags::DIRTY, PageTableFlags::DIRTY))
| (parse_flags!(
prop.flags.bits(),
PageFlags::AVAIL1,
PageTableFlags::HIGH_IGN1
))
| (parse_flags!(
prop.flags.bits(),
PageFlags::AVAIL2,
PageTableFlags::HIGH_IGN2
))
| (parse_flags!(
prop.priv_flags.bits(),
PrivFlags::USER,
PageTableFlags::USER
))
| (parse_flags!(
prop.priv_flags.bits(),
PrivFlags::GLOBAL,
PageTableFlags::GLOBAL
));
#[cfg(feature = "cvm_guest")]
{
flags |= parse_flags!(
prop.priv_flags.bits(),
PrivFlags::SHARED,
PageTableFlags::SHARED
);
}
match prop.cache {
CachePolicy::Writeback => {}
CachePolicy::Writethrough => {
flags |= PageTableFlags::WRITE_THROUGH.bits();
}
CachePolicy::Uncacheable => {
flags |= PageTableFlags::NO_CACHE.bits();
}
_ => panic!("unsupported cache policy"),
}
self.0 = self.0 & !Self::PROP_MASK | flags;
}
fn is_last(&self, _level: PagingLevel) -> bool {
self.0 & PageTableFlags::HUGE.bits() != 0
}
}
impl fmt::Debug for PageTableEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut f = f.debug_struct("PageTableEntry");
f.field("raw", &format_args!("{:#x}", self.0))
.field("paddr", &format_args!("{:#x}", self.paddr()))
.field("present", &self.is_present())
.field(
"flags",
&PageTableFlags::from_bits_truncate(self.0 & !Self::PHYS_ADDR_MASK),
)
.field("prop", &self.prop())
.finish()
}
}