use std::{
alloc::{self, Layout},
collections::HashSet,
fmt::Debug,
mem,
sync::{Arc, Mutex},
};
use page_table_generic::*;
use tock_registers::{interfaces::*, register_bitfields, registers::*};
register_bitfields! [
u64,
PTE64 [
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) [
],
CACHE OFFSET(54) NUMBITS(2) [
NonCache = 0,
Normal = 0b01,
Device = 0b10,
],
VALID OFFSET(63) NUMBITS(1) [
]
],
];
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct PteImpl(pub u64);
impl PteImpl {
fn reg(&self) -> &ReadWrite<u64, PTE64::Register> {
unsafe { mem::transmute(self) }
}
}
impl Debug for PteImpl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let config = self.to_config(false);
if !config.valid {
return write!(f, "invalid");
}
write!(f, "PTE PA: {:?} Block: {:?}", config.paddr, config.huge)
}
}
impl PageTableEntry for PteImpl {
fn from_config(config: PteConfig) -> Self {
let mut pte = Self(0);
let paddr = config.paddr.raw() >> 12;
pte.reg().modify(PTE64::PA.val(paddr as _));
if config.valid {
pte.reg().modify(PTE64::VALID::SET);
}
if config.read {
pte.reg().modify(PTE64::READ::SET);
}
if config.writable {
pte.reg().modify(PTE64::WRITE::SET);
}
if config.executable {
pte.reg().modify(PTE64::PRIVILEGE_EXECUTE::SET);
}
if config.lower {
pte.reg().modify(PTE64::USER_ACCESS::SET);
}
if config.huge {
pte.reg().modify(PTE64::BLOCK::SET);
}
let cache = match config.mem_attr {
MemAttributes::Device => 2,
MemAttributes::Uncached => 0,
MemAttributes::Normal | MemAttributes::PerCpu => 1,
};
pte.reg().modify(PTE64::CACHE.val(cache));
pte
}
fn to_config(&self, _is_dir: bool) -> PteConfig {
PteConfig {
paddr: ((self.reg().read(PTE64::PA) << 12) as usize).into(),
valid: self.reg().is_set(PTE64::VALID),
read: self.reg().is_set(PTE64::READ),
writable: self.reg().is_set(PTE64::WRITE),
executable: self.reg().is_set(PTE64::PRIVILEGE_EXECUTE),
lower: self.reg().is_set(PTE64::USER_ACCESS),
dirty: false, global: false, is_dir: _is_dir,
huge: self.reg().is_set(PTE64::BLOCK),
mem_attr: match self.reg().read(PTE64::CACHE) {
1 => MemAttributes::Normal,
2 => MemAttributes::Device,
_ => MemAttributes::Uncached,
},
}
}
fn valid(&self) -> bool {
self.reg().is_set(PTE64::VALID)
}
}
impl PteImpl {
pub fn new_with_flags(
read: bool,
write: bool,
user_execute: bool,
user_access: bool,
privilege_execute: bool,
cache: u64, valid: bool,
is_block: bool,
) -> Self {
let pte = PteImpl(0);
if read {
pte.reg().modify(PTE64::READ::SET);
}
if write {
pte.reg().modify(PTE64::WRITE::SET);
}
if user_execute {
pte.reg().modify(PTE64::USER_EXECUTE::SET);
}
if user_access {
pte.reg().modify(PTE64::USER_ACCESS::SET);
}
if privilege_execute {
pte.reg().modify(PTE64::PRIVILEGE_EXECUTE::SET);
}
pte.reg().modify(PTE64::CACHE.val(cache));
if valid {
pte.reg().modify(PTE64::VALID::SET);
}
if is_block {
pte.reg().modify(PTE64::BLOCK::SET);
}
pte
}
pub fn user_mode() -> Self {
Self::new_with_flags(
true, true, true, true, false, 1, true, false, )
}
pub fn kernel_mode() -> Self {
Self::new_with_flags(
true, true, false, false, true, 1, true, false, )
}
pub fn user_mode_config() -> PteConfig {
PteConfig {
valid: true,
read: true,
writable: true,
executable: true,
lower: true,
mem_attr: MemAttributes::Normal,
..Default::default()
}
}
pub fn kernel_mode_config() -> PteConfig {
PteConfig {
valid: true,
read: true,
writable: true,
executable: true,
lower: false,
mem_attr: MemAttributes::Normal,
..Default::default()
}
}
pub fn read_only() -> Self {
Self::new_with_flags(
true, false, false, false, false, 1, true, false, )
}
pub fn read_write() -> Self {
Self::new_with_flags(
true, true, false, false, false, 1, true, false, )
}
pub fn device_memory() -> Self {
Self::new_with_flags(
true, true, false, false, false, 2, true, true, )
}
pub fn mmap_io() -> Self {
Self::new_with_flags(
true, false, false, true, false, 2, true, false, )
}
pub fn is_readable(&self) -> bool {
self.reg().is_set(PTE64::READ)
}
pub fn is_writable(&self) -> bool {
self.reg().is_set(PTE64::WRITE)
}
pub fn is_user_executable(&self) -> bool {
self.reg().is_set(PTE64::USER_EXECUTE)
}
pub fn is_user_accessible(&self) -> bool {
self.reg().is_set(PTE64::USER_ACCESS)
}
pub fn is_privilege_executable(&self) -> bool {
self.reg().is_set(PTE64::PRIVILEGE_EXECUTE)
}
pub fn cache_mode(&self) -> u64 {
self.reg().read(PTE64::CACHE)
}
pub fn new() -> Self {
Self(0)
}
pub fn read_execute() -> Self {
Self::new_with_flags(
true, false, true, false, false, 1, true, false, )
}
pub fn all_permissions() -> Self {
Self::new_with_flags(
true, true, true, true, true, 1, true, false, )
}
pub fn user_execute() -> Self {
Self::new_with_flags(
true, false, true, false, false, 1, true, false, )
}
pub fn privilege_execute() -> Self {
Self::new_with_flags(
true, false, false, false, true, 1, true, false, )
}
pub fn non_cache() -> Self {
Self::new_with_flags(
true, false, false, false, false, 0, true, false, )
}
pub fn normal_cache() -> Self {
Self::new_with_flags(
true, false, false, false, false, 1, true, false, )
}
pub fn device_cache() -> Self {
Self::new_with_flags(
true, false, false, false, false, 2, true, false, )
}
pub fn complex_user_mapping() -> Self {
Self::new_with_flags(
true, true, true, true, true, 1, true, true, )
}
pub fn complex_user_mapping_config() -> PteConfig {
PteConfig {
valid: true,
read: true,
writable: true,
executable: true,
lower: true,
huge: true,
mem_attr: MemAttributes::Normal,
..Default::default()
}
}
pub fn complex_kernel_mapping() -> Self {
Self::new_with_flags(
true, true, false, false, true, 1, true, false, )
}
pub fn complex_kernel_mapping_config() -> PteConfig {
PteConfig {
valid: true,
read: true,
writable: true,
executable: true,
lower: false,
huge: false,
mem_attr: MemAttributes::Normal,
..Default::default()
}
}
pub fn mem_config(&self) -> MemConfig {
let config = self.to_config(false);
let mut access = AccessFlags::empty();
if config.writable {
access |= AccessFlags::WRITE;
}
if config.executable {
access |= AccessFlags::EXECUTE;
}
if config.lower {
access |= AccessFlags::LOWER;
}
if config.valid {
access |= AccessFlags::READ;
}
MemConfig {
access,
attrs: config.mem_attr,
}
}
pub fn set_mem_config(&mut self, config: MemConfig) {
let current_config = self.to_config(false);
let new_config = PteConfig {
writable: config.access.contains(AccessFlags::WRITE),
executable: config.access.contains(AccessFlags::EXECUTE),
lower: config.access.contains(AccessFlags::LOWER),
mem_attr: config.attrs,
..current_config
};
*self = Self::from_config(new_config);
}
}
#[derive(Debug, Clone, Copy)]
pub struct T4kL3;
impl TableMeta for T4kL3 {
type P = PteImpl;
const PAGE_SIZE: usize = 0x1000;
const MAX_BLOCK_LEVEL: usize = 2;
fn flush(vaddr: Option<VirtAddr>) {
let _ = vaddr;
}
const LEVEL_BITS: &[usize] = &[9, 9, 9];
}
#[derive(Debug, Clone, Copy)]
pub struct T4kL4;
impl TableMeta for T4kL4 {
type P = PteImpl;
const PAGE_SIZE: usize = 0x1000;
const MAX_BLOCK_LEVEL: usize = 3;
fn flush(vaddr: Option<VirtAddr>) {
let _ = vaddr;
}
const LEVEL_BITS: &[usize] = &[9, 9, 9, 9];
}
#[derive(Debug, Clone, Copy)]
pub struct T4kL5;
impl TableMeta for T4kL5 {
type P = PteImpl;
const PAGE_SIZE: usize = 0x1000;
const MAX_BLOCK_LEVEL: usize = 4;
fn flush(vaddr: Option<VirtAddr>) {
let _ = vaddr;
}
const LEVEL_BITS: &[usize] = &[9, 9, 9, 9, 9];
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Fram4k;
impl FrameAllocator for Fram4k {
fn alloc_frame(&self) -> Option<PhysAddr> {
let layout = Layout::from_size_align(4096, 4096).unwrap();
let ptr = unsafe { alloc::alloc(layout) };
if ptr.is_null() {
None
} else {
Some(PhysAddr::new(ptr as usize))
}
}
fn dealloc_frame(&self, frame: PhysAddr) {
let layout = Layout::from_size_align(4096, 4096).unwrap();
unsafe {
alloc::dealloc(frame.raw() as *mut u8, layout);
}
}
fn phys_to_virt(&self, paddr: PhysAddr) -> *mut u8 {
paddr.raw() as *mut u8
}
}
#[derive(Debug, Clone, Copy)]
pub struct TrackedFram4k {
allocated_frames: *const Mutex<HashSet<usize>>,
}
impl Default for TrackedFram4k {
fn default() -> Self {
Self::new()
}
}
impl TrackedFram4k {
pub fn new() -> Self {
let frames = Arc::new(Mutex::new(HashSet::new()));
let ptr = Arc::into_raw(frames);
Self {
allocated_frames: ptr,
}
}
pub fn allocated_count(&self) -> usize {
unsafe {
let frames = &*self.allocated_frames;
frames.lock().unwrap().len()
}
}
#[allow(dead_code)]
pub fn allocated_frames(&self) -> Vec<usize> {
unsafe {
let frames = &*self.allocated_frames;
frames.lock().unwrap().iter().copied().collect()
}
}
pub fn has_leaks(&self) -> bool {
unsafe {
let frames = &*self.allocated_frames;
!frames.lock().unwrap().is_empty()
}
}
pub fn print_stats(&self) {
unsafe {
let frames = &*self.allocated_frames;
let frames = frames.lock().unwrap();
println!("分配器统计: {} 个帧已分配", frames.len());
if !frames.is_empty() {
println!("未释放的帧地址:");
for addr in frames.iter() {
println!(" - {:#x}", addr);
}
}
}
}
}
unsafe impl Send for TrackedFram4k {}
unsafe impl Sync for TrackedFram4k {}
impl FrameAllocator for TrackedFram4k {
fn alloc_frame(&self) -> Option<PhysAddr> {
let layout = Layout::from_size_align(4096, 4096).unwrap();
let ptr = unsafe { alloc::alloc(layout) };
if ptr.is_null() {
None
} else {
let addr = ptr as usize;
unsafe {
let frames = &*self.allocated_frames;
frames.lock().unwrap().insert(addr);
}
Some(PhysAddr::new(addr))
}
}
fn dealloc_frame(&self, frame: PhysAddr) {
let addr = frame.raw();
unsafe {
let frames = &*self.allocated_frames;
let removed = frames.lock().unwrap().remove(&addr);
if !removed {
panic!("尝试释放未跟踪的帧地址: {:#x}", addr);
}
}
let layout = Layout::from_size_align(4096, 4096).unwrap();
unsafe {
alloc::dealloc(addr as *mut u8, layout);
}
}
fn phys_to_virt(&self, paddr: PhysAddr) -> *mut u8 {
paddr.raw() as *mut u8
}
}