extern crate std;
use std::{
alloc::{self, Layout},
mem,
ptr::NonNull,
};
use log::trace;
use page_table_generic::*;
use tock_registers::{interfaces::Readable, register_bitfields, registers::ReadWrite};
register_bitfields! [
u64,
PTE [
PA OFFSET(0) NUMBITS(48) [
],
READ OFFSET(48) NUMBITS(1) [
],
WRITE OFFSET(49) NUMBITS(1) [
],
USER_EXECUTE OFFSET(50) NUMBITS(1) [
],
USER_ACCESS OFFSET(51) NUMBITS(1) [
],
PRIVILEGE_EXECUTE OFFSET(52) NUMBITS(1) [
],
BLOCK OFFSET(53) NUMBITS(1) [
],
DEVICE OFFSET(54) NUMBITS(1) [
],
],
];
#[derive(Clone, Copy)]
struct PteImpl;
impl PTEArch for PteImpl {
fn page_size() -> usize {
4096
}
fn new_pte(config: PTEGeneric) -> usize {
let paddr = config.paddr >> 12;
let mut v = PTE::PA.val(paddr as _);
if config.setting.privilege_access.readable() {
v += PTE::READ::SET;
}
if config.setting.privilege_access.writable() {
v += PTE::WRITE::SET;
}
if config.setting.user_access.readable() | config.setting.user_access.writable() {
v += PTE::USER_ACCESS::SET;
}
if config.setting.privilege_access.executable() {
v += PTE::PRIVILEGE_EXECUTE::SET;
}
match config.setting.cache_setting {
CacheSetting::Normal => {}
CacheSetting::Device => {
v += PTE::DEVICE::SET;
}
CacheSetting::NonCache => {}
}
if config.is_block {
v += PTE::BLOCK::SET;
}
v.value as _
}
fn read_pte(pte: usize) -> PTEGeneric {
let v: ReadWrite<u64, PTE::Register> = unsafe { mem::transmute(pte) };
let paddr: u64 = v.read(PTE::PA) << 12;
let mut privilege_access = AccessSetting::empty();
let mut user_access = AccessSetting::empty();
let mut is_valid = false;
if v.is_set(PTE::READ) {
privilege_access |= AccessSetting::Read;
is_valid = true;
}
if v.is_set(PTE::WRITE) {
privilege_access |= AccessSetting::Write;
}
if v.is_set(PTE::USER_ACCESS) {
user_access |= AccessSetting::Read;
if v.is_set(PTE::WRITE) {
user_access |= AccessSetting::Write;
}
}
if v.is_set(PTE::USER_EXECUTE) {
user_access |= AccessSetting::Execute;
}
if v.is_set(PTE::PRIVILEGE_EXECUTE) {
privilege_access |= AccessSetting::Execute;
}
let cache_setting = if v.is_set(PTE::DEVICE) {
CacheSetting::Device
} else {
CacheSetting::Normal
};
PTEGeneric {
paddr: paddr as usize,
is_block: v.is_set(PTE::BLOCK),
setting: PTESetting {
cache_setting,
privilege_access,
user_access,
is_global: true,
},
is_valid,
}
}
fn level() -> usize {
4
}
}
struct AccessImpl;
impl Access for AccessImpl {
fn va_offset(&self) -> usize {
0
}
unsafe fn alloc(&mut self, layout: Layout) -> Option<NonNull<u8>> {
let ptr = alloc::alloc(layout);
trace!("alloc: {:?}", ptr);
NonNull::new(ptr)
}
unsafe fn dealloc(&mut self, mut ptr: NonNull<u8>, layout: Layout) {
trace!("dealloc: {:?}", ptr);
alloc::dealloc(ptr.as_mut(), layout);
}
}
#[test]
fn test_pte() {
let want = PTEGeneric {
paddr: 0xfff123456780000,
is_block: false,
setting: PTESetting {
cache_setting: CacheSetting::Normal,
privilege_access: AccessSetting::empty(),
user_access: AccessSetting::empty(),
is_global: true,
},
is_valid: true,
};
let v = PteImpl::new_pte(want.clone());
let pte = PteImpl::read_pte(v);
assert_eq!(want.paddr, pte.paddr);
}
#[test]
fn test_new() {
let _ = env_logger::builder()
.is_test(true)
.filter_level(log::LevelFilter::Trace)
.try_init();
let mut access = AccessImpl;
let mut pg = PageTableRef::<PteImpl>::create_empty(&mut access).unwrap();
unsafe {
pg.map_region(
MapConfig::new(
0xffff000000000000usize as _,
0x0000,
AccessSetting::Read | AccessSetting::Write,
CacheSetting::Device,
),
0x2000,
false,
&mut access,
)
.unwrap();
}
let msg = pg
.as_slice(&access)
.iter()
.filter_map(|o| {
let v = o.as_usize();
if v > 0 {
Some(format!("{:#x}", v))
} else {
None
}
})
.collect::<Vec<_>>()
.join(", ");
println!("vec: {}", msg);
let list = pg.iter_all(&access).collect::<Vec<_>>();
for i in &list {
println!("l: {:x}, va: {:#p} c: {:?}", i.level, i.vaddr, i.pte);
}
assert_eq!(list.len(), 5);
}
#[test]
fn test_block() {
let _ = env_logger::builder()
.is_test(true)
.filter_level(log::LevelFilter::Trace)
.try_init();
let mut access = AccessImpl;
let mut pg = PageTableRef::<PteImpl>::create_empty(&mut access).unwrap();
unsafe {
pg.map_region(
MapConfig::new(
0xffff000000000000usize as _,
0x0000,
AccessSetting::Read | AccessSetting::Write,
CacheSetting::Device,
),
1024 * 1024 * 1024,
true,
&mut access,
)
.unwrap();
}
let msg = pg
.as_slice(&access)
.iter()
.filter_map(|o| {
let v = o.as_usize();
if v > 0 {
Some(format!("{:#x}", v))
} else {
None
}
})
.collect::<Vec<_>>()
.join(", ");
println!("vec: {}", msg);
let list = pg.iter_all(&access).collect::<Vec<_>>();
for i in &list {
println!("l: {:x}, va: {:#p} c: {:?}", i.level, i.vaddr, i.pte);
}
assert_eq!(list.len(), 2);
assert!(list.last().unwrap().pte.is_block);
}
#[test]
fn test_release() {
let _ = env_logger::builder()
.is_test(true)
.filter_level(log::LevelFilter::Trace)
.try_init();
let mut access = AccessImpl;
let mut pg = PageTableRef::<PteImpl>::create_empty(&mut access).unwrap();
unsafe {
pg.map_region(
MapConfig::new(
0xffff000000000000usize as _,
0x0000,
AccessSetting::Read | AccessSetting::Write,
CacheSetting::Device,
)
.set_user_access(AccessSetting::Read),
0x2000,
false,
&mut access,
)
.unwrap();
}
for i in pg.iter_all(&access) {
println!("l: {:x}, va: {:#p} c: {:?}", i.level, i.vaddr, i.pte);
}
pg.release(&mut access);
}