use core::ops::Range;
use cfg_if::cfg_if;
pub(crate) use util::{
__atomic_cmpxchg_fallible, __atomic_load_fallible, __memcpy_fallible, __memset_fallible,
};
use x86_64::{
VirtAddr,
instructions::tlb,
registers::model_specific::{Efer, EferFlags},
structures::paging::PhysFrame,
};
use crate::mm::{
PAGE_SIZE, Paddr, PagingConstsTrait, PagingLevel, PodOnce, Vaddr,
dma::DmaDirection,
page_prop::{
CachePolicy, PageFlags, PageProperty, PageTableFlags, PrivilegedPageFlags as PrivFlags,
},
page_table::{PteScalar, PteTrait},
};
mod pat;
mod util;
use self::pat::{cache_policy_to_flags, configure_pat, flags_to_cache_policy};
#[derive(Clone, Debug, Default)]
pub(crate) 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 = size_of::<PageTableEntry>();
}
bitflags::bitflags! {
#[repr(C)]
#[derive(Pod)]
pub(crate) struct PteFlags: 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;
});
}
}
pub(crate) fn can_sync_dma() -> bool {
true
}
pub(crate) unsafe fn sync_dma_range<D: DmaDirection>(_range: Range<Vaddr>) {
}
pub(crate) 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 (only WB, WT, and UC are allowed)"
)
}
};
unsafe { x86_64::registers::control::Cr3::write(addr, flags) };
}
pub(crate) fn current_page_table_paddr() -> Paddr {
x86_64::registers::control::Cr3::read_raw()
.0
.start_address()
.as_u64() as Paddr
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod)]
pub(crate) struct PageTableEntry(usize);
macro_rules! parse_flags {
($val:expr, $from:expr, $to:expr) => {
(($val as usize & $from.bits() as usize) >> $from.bits().ilog2() << $to.bits().ilog2())
};
}
impl PageTableEntry {
cfg_if! {
if #[cfg(feature = "cvm_guest")] {
const PHYS_ADDR_MASK_LVL1: usize = 0x7_ffff_ffff_f000;
const PHYS_ADDR_MASK_LVL2: usize = 0x7_ffff_ffe0_0000;
const PHYS_ADDR_MASK_LVL3: usize = 0x7_ffff_c000_0000;
} else {
const PHYS_ADDR_MASK_LVL1: usize = 0xf_ffff_ffff_f000;
const PHYS_ADDR_MASK_LVL2: usize = 0xf_ffff_ffe0_0000;
const PHYS_ADDR_MASK_LVL3: usize = 0xf_ffff_c000_0000;
}
}
const CHILD_PT_ADDR_MASK: usize = Self::PHYS_ADDR_MASK_LVL1;
fn pa_mask_at_level(level: PagingLevel) -> usize {
match level {
1 => Self::PHYS_ADDR_MASK_LVL1,
2 => Self::PHYS_ADDR_MASK_LVL2,
3 => Self::PHYS_ADDR_MASK_LVL3,
_ => panic!("invalid level {} for page entry", level),
}
}
fn is_present(&self) -> bool {
self.0 & PteFlags::PRESENT.bits() != 0 || self.0 & PteFlags::HUGE.bits() != 0
}
fn prop(&self) -> PageProperty {
let flags = parse_flags!(self.0, PteFlags::PRESENT, PageFlags::R)
| parse_flags!(self.0, PteFlags::WRITABLE, PageFlags::W)
| parse_flags!(!self.0, PteFlags::NO_EXECUTE, PageFlags::X)
| parse_flags!(self.0, PteFlags::ACCESSED, PageFlags::ACCESSED)
| parse_flags!(self.0, PteFlags::DIRTY, PageFlags::DIRTY)
| parse_flags!(self.0, PteFlags::HIGH_IGN2, PageFlags::AVAIL2);
let priv_flags = parse_flags!(self.0, PteFlags::USER, PrivFlags::USER)
| parse_flags!(self.0, PteFlags::GLOBAL, PrivFlags::GLOBAL)
| parse_flags!(self.0, PteFlags::HIGH_IGN1, PrivFlags::AVAIL1);
#[cfg(feature = "cvm_guest")]
let priv_flags = priv_flags | parse_flags!(self.0, PteFlags::SHARED, PrivFlags::SHARED);
let cache = flags_to_cache_policy(PteFlags::from_bits_truncate(self.0));
PageProperty {
flags: PageFlags::from_bits(flags as u8).unwrap(),
cache,
priv_flags: PrivFlags::from_bits(priv_flags as u8).unwrap(),
}
}
fn pt_flags(&self) -> PageTableFlags {
let bits = PageTableFlags::empty().bits() as usize
| parse_flags!(self.0, PteFlags::HIGH_IGN1, PageTableFlags::AVAIL1)
| parse_flags!(self.0, PteFlags::HIGH_IGN2, PageTableFlags::AVAIL2);
PageTableFlags::from_bits(bits as u8).unwrap()
}
fn new_page(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> Self {
let mut flags = PteFlags::HUGE.bits();
flags |= parse_flags!(prop.flags.bits(), PageFlags::R, PteFlags::PRESENT)
| parse_flags!(prop.flags.bits(), PageFlags::W, PteFlags::WRITABLE)
| parse_flags!(!prop.flags.bits(), PageFlags::X, PteFlags::NO_EXECUTE)
| parse_flags!(prop.flags.bits(), PageFlags::ACCESSED, PteFlags::ACCESSED)
| parse_flags!(prop.flags.bits(), PageFlags::DIRTY, PteFlags::DIRTY)
| parse_flags!(
prop.priv_flags.bits(),
PrivFlags::AVAIL1,
PteFlags::HIGH_IGN1
)
| parse_flags!(prop.flags.bits(), PageFlags::AVAIL2, PteFlags::HIGH_IGN2)
| parse_flags!(prop.priv_flags.bits(), PrivFlags::USER, PteFlags::USER)
| parse_flags!(prop.priv_flags.bits(), PrivFlags::GLOBAL, PteFlags::GLOBAL);
#[cfg(feature = "cvm_guest")]
{
flags |= parse_flags!(prop.priv_flags.bits(), PrivFlags::SHARED, PteFlags::SHARED);
}
flags |= cache_policy_to_flags(prop.cache).bits();
assert_eq!(
paddr & !Self::pa_mask_at_level(level),
0,
"page physical address contains invalid bits"
);
Self(paddr | flags)
}
fn new_pt(paddr: Paddr, flags: PageTableFlags) -> Self {
let flags = PteFlags::PRESENT.bits()
| PteFlags::WRITABLE.bits()
| PteFlags::USER.bits()
| parse_flags!(flags.bits(), PageTableFlags::AVAIL1, PteFlags::HIGH_IGN1)
| parse_flags!(flags.bits(), PageTableFlags::AVAIL2, PteFlags::HIGH_IGN2);
assert_eq!(
paddr & !Self::CHILD_PT_ADDR_MASK,
0,
"page table physical address contains invalid bits"
);
Self(paddr | flags)
}
}
impl PodOnce for PageTableEntry {}
unsafe impl PteTrait for PageTableEntry {
fn from_repr(repr: &PteScalar, level: PagingLevel) -> Self {
match repr {
PteScalar::Absent => PageTableEntry(0),
PteScalar::PageTable(paddr, flags) => Self::new_pt(*paddr, *flags),
PteScalar::Mapped(paddr, prop) => Self::new_page(*paddr, level, *prop),
}
}
fn to_repr(&self, level: PagingLevel) -> PteScalar {
if !self.is_present() {
return PteScalar::Absent;
}
if self.0 & PteFlags::HUGE.bits() != 0 {
let paddr = self.0 & Self::pa_mask_at_level(level);
PteScalar::Mapped(paddr, self.prop())
} else {
let paddr = self.0 & Self::CHILD_PT_ADDR_MASK;
PteScalar::PageTable(paddr, self.pt_flags())
}
}
}
pub(super) fn enable_essential_features() {
configure_pat();
unsafe {
Efer::update(|efer| {
*efer |= EferFlags::NO_EXECUTE_ENABLE;
});
}
}