use std::{ alloc::{self, Layout}, mem, ptr::NonNull, }; use log::trace; use page_table_generic::*; use tock_registers::{interfaces::*, register_bitfields, registers::*}; 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 => {} CacheSetting::ToDevice => {} CacheSetting::FromDevice => {} } if config.is_block { v += PTE::BLOCK::SET; } v.value as _ } fn read_pte(pte: usize) -> PTEGeneric { let v: ReadWrite = 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> { let ptr = alloc::alloc(layout); trace!("alloc: {:?}", ptr); NonNull::new(ptr) } unsafe fn dealloc(&mut self, mut ptr: NonNull, 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::::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::>() .join(", "); println!("vec: {}", msg); let list = pg.iter_all(&access).collect::>(); 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::::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::>() .join(", "); println!("vec: {}", msg); let list = pg.iter_all(&access).collect::>(); 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::::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); }