mod mocks;
use core::fmt;
use std::alloc::{self, Layout};
use mocks::{Fram4k, MappingFlags, PteImpl};
use page_table_generic::{
FrameAllocator, PageTable, PageTableEntry, PhysAddr, TableMeta, VirtAddr,
};
const PAGE_SIZE_16K: usize = 0x4000;
const BLOCK_SIZE_32M: usize = PAGE_SIZE_16K << 11;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct OpaqueConfig {
domain: u8,
}
#[derive(Clone, Copy)]
struct OpaquePte(u64);
impl OpaquePte {
const PRESENT: u64 = 1;
const TABLE: u64 = 1 << 1;
const HUGE: u64 = 1 << 2;
const CONFIG_SHIFT: usize = 3;
const CONFIG_MASK: u64 = 0xff << Self::CONFIG_SHIFT;
const PADDR_MASK: u64 = 0x000f_ffff_ffff_f000;
}
impl PageTableEntry for OpaquePte {
type PteConfig = OpaqueConfig;
fn new_page(paddr: PhysAddr, config: Self::PteConfig, is_huge: bool) -> Self {
let huge = if is_huge { Self::HUGE } else { 0 };
Self(
(paddr.as_usize() as u64 & Self::PADDR_MASK)
| Self::PRESENT
| huge
| (u64::from(config.domain) << Self::CONFIG_SHIFT),
)
}
fn new_table(paddr: PhysAddr) -> Self {
Self((paddr.as_usize() as u64 & Self::PADDR_MASK) | Self::PRESENT | Self::TABLE)
}
fn paddr(&self, _is_dir: bool) -> PhysAddr {
PhysAddr::from_usize((self.0 & Self::PADDR_MASK) as usize)
}
fn config(&self, _is_dir: bool) -> Self::PteConfig {
OpaqueConfig {
domain: ((self.0 & Self::CONFIG_MASK) >> Self::CONFIG_SHIFT) as u8,
}
}
fn present(&self) -> bool {
self.0 & Self::PRESENT != 0
}
fn huge(&self, is_dir: bool) -> bool {
is_dir && self.0 & Self::HUGE != 0
}
fn unused(&self) -> bool {
self.0 == 0
}
fn clear(&mut self) {
self.0 = 0;
}
}
impl fmt::Debug for OpaquePte {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("OpaquePte").field(&self.0).finish()
}
}
#[derive(Clone, Copy)]
struct OpaqueMeta;
impl TableMeta for OpaqueMeta {
type P = OpaquePte;
const PAGE_SIZE: usize = 0x1000;
const LEVEL_BITS: &[usize] = &[9, 9, 9];
const MAX_BLOCK_LEVEL: usize = 2;
fn flush(_vaddr: Option<VirtAddr>) {}
}
#[derive(Clone, Copy)]
struct T16kL4;
impl TableMeta for T16kL4 {
type P = PteImpl;
const PAGE_SIZE: usize = PAGE_SIZE_16K;
const LEVEL_BITS: &[usize] = &[11, 11, 11, 11];
const MAX_BLOCK_LEVEL: usize = 3;
fn flush(_vaddr: Option<VirtAddr>) {}
}
#[derive(Clone, Copy)]
struct Fram16k;
impl FrameAllocator for Fram16k {
fn alloc_frame(&self) -> Option<PhysAddr> {
let layout = Layout::from_size_align(PAGE_SIZE_16K, PAGE_SIZE_16K).unwrap();
let ptr = unsafe { alloc::alloc(layout) };
(!ptr.is_null()).then(|| PhysAddr::from_usize(ptr as usize))
}
fn dealloc_frame(&self, frame: PhysAddr) {
let layout = Layout::from_size_align(PAGE_SIZE_16K, PAGE_SIZE_16K).unwrap();
unsafe { alloc::dealloc(frame.as_usize() as *mut u8, layout) };
}
fn phys_to_virt(&self, paddr: PhysAddr) -> *mut u8 {
paddr.as_usize() as *mut u8
}
}
#[test]
fn maps_and_protects_with_an_opaque_pte_config() {
let mut page_table = PageTable::<OpaqueMeta, Fram4k>::new(Fram4k).unwrap();
let vaddr = VirtAddr::from_usize(0x4000);
let paddr = PhysAddr::from_usize(0x8000);
page_table
.map_page(vaddr, paddr, 0x1000, OpaqueConfig { domain: 0x2a })
.unwrap();
assert_eq!(page_table.query(vaddr).unwrap().1.domain, 0x2a);
page_table
.protect_page(vaddr, OpaqueConfig { domain: 0x7f })
.unwrap();
assert_eq!(page_table.query(vaddr).unwrap().1.domain, 0x7f);
}
#[test]
fn query_reports_arbitrary_base_page_size() {
let mut page_table = PageTable::<T16kL4, Fram16k>::new(Fram16k).unwrap();
let vaddr = VirtAddr::from_usize(PAGE_SIZE_16K);
let paddr = PhysAddr::from_usize(PAGE_SIZE_16K);
page_table
.map_page(vaddr, paddr, PAGE_SIZE_16K, MappingFlags::READ.into())
.unwrap();
let (mapped_paddr, _, page_size) = page_table.query(vaddr).unwrap();
assert_eq!(mapped_paddr, paddr);
assert_eq!(page_size, PAGE_SIZE_16K);
}
#[test]
fn map_region_selects_page_sizes_from_table_levels() {
let mut page_table = PageTable::<T16kL4, Fram16k>::new(Fram16k).unwrap();
let vaddr = VirtAddr::from_usize(BLOCK_SIZE_32M);
let paddr = PhysAddr::from_usize(BLOCK_SIZE_32M);
page_table
.map_region(
vaddr,
|current| paddr + (current - vaddr),
BLOCK_SIZE_32M,
MappingFlags::READ.into(),
true,
)
.unwrap();
let (mapped_paddr, _, page_size) = page_table.query(vaddr + PAGE_SIZE_16K).unwrap();
assert_eq!(mapped_paddr, paddr + PAGE_SIZE_16K);
assert_eq!(page_size, BLOCK_SIZE_32M);
}