use ax_memory_addr::{PAGE_SIZE_4K, PhysAddr};
use page_table_generic::PageTableEntry;
use crate::paging::MappingFlags;
pub(crate) const PAGE_SIZE: usize = PAGE_SIZE_4K;
pub(crate) const LEVEL_BITS: &[usize] = &[9, 9, 9, 9];
pub(crate) const MAX_BLOCK_LEVEL: usize = 3;
pub(super) const PWCL_VALUE: u32 = 12 | (9 << 5) | (21 << 10) | (9 << 15) | (30 << 20) | (9 << 25);
pub(super) const PWCH_VALUE: u32 = 39 | (9 << 6);
bitflags::bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct DescriptorFlags: u64 {
const V = 1 << 0;
const D = 1 << 1;
const PLVL = 1 << 2;
const PLVH = 1 << 3;
const MATL = 1 << 4;
const MATH = 1 << 5;
const GH = 1 << 6;
const P = 1 << 7;
const W = 1 << 8;
const G = 1 << 12;
const NR = 1 << 61;
const NX = 1 << 62;
}
}
#[derive(Clone, Copy, Default)]
#[repr(transparent)]
pub struct La64Pte(u64);
pub type Pte = La64Pte;
impl La64Pte {
const PHYS_ADDR_MASK: u64 = 0x0000_ffff_ffff_f000;
pub const fn from_parts(paddr: PhysAddr, flags: DescriptorFlags) -> Self {
Self((paddr.as_usize() as u64 & Self::PHYS_ADDR_MASK) | flags.bits())
}
pub fn flags(self) -> DescriptorFlags {
DescriptorFlags::from_bits_truncate(self.0)
}
fn paddr(self) -> PhysAddr {
PhysAddr::from_usize((self.0 & Self::PHYS_ADDR_MASK) as usize)
}
fn leaf_flags(config: MappingFlags, is_huge: bool) -> DescriptorFlags {
if config.is_empty() {
return if is_huge {
DescriptorFlags::GH
} else {
DescriptorFlags::P
};
}
let mut flags = DescriptorFlags::V | DescriptorFlags::P;
if !config.contains(MappingFlags::READ) {
flags |= DescriptorFlags::NR;
}
if config.contains(MappingFlags::WRITE) {
flags |= DescriptorFlags::W | DescriptorFlags::D;
}
if !config.contains(MappingFlags::EXECUTE) {
flags |= DescriptorFlags::NX;
}
if config.contains(MappingFlags::USER) {
flags |= DescriptorFlags::PLVL | DescriptorFlags::PLVH;
}
if config.contains(MappingFlags::UNCACHED) {
flags |= DescriptorFlags::MATH;
} else if !config.contains(MappingFlags::DEVICE) {
flags |= DescriptorFlags::MATL;
}
let global = !config.contains(MappingFlags::USER);
if is_huge {
flags |= DescriptorFlags::GH;
if global {
flags |= DescriptorFlags::G;
}
} else if global {
flags |= DescriptorFlags::GH;
}
flags
}
fn is_table(self) -> bool {
self.paddr().as_usize() != 0 && (self.0 & !Self::PHYS_ADDR_MASK) == 0
}
}
impl PageTableEntry for La64Pte {
type PteConfig = MappingFlags;
fn new_page(paddr: PhysAddr, config: Self::PteConfig, is_huge: bool) -> Self {
if config.is_empty() && paddr.as_usize() == 0 {
return Self(0);
}
Self::from_parts(paddr, Self::leaf_flags(config, is_huge))
}
fn new_table(paddr: PhysAddr) -> Self {
Self::from_parts(paddr, DescriptorFlags::empty())
}
fn paddr(&self, is_dir: bool) -> PhysAddr {
let flags = self.flags();
let huge = is_dir && flags.contains(DescriptorFlags::GH);
if huge {
PhysAddr::from_usize(
La64Pte::paddr(*self).as_usize() & !(DescriptorFlags::G.bits() as usize),
)
} else {
La64Pte::paddr(*self)
}
}
fn config(&self, _is_dir: bool) -> Self::PteConfig {
let flags = self.flags();
if !flags.contains(DescriptorFlags::V) {
return MappingFlags::empty();
}
let mut config = MappingFlags::empty();
config.set(MappingFlags::READ, !flags.contains(DescriptorFlags::NR));
config.set(MappingFlags::WRITE, flags.contains(DescriptorFlags::W));
config.set(MappingFlags::EXECUTE, !flags.contains(DescriptorFlags::NX));
config.set(
MappingFlags::USER,
flags.contains(DescriptorFlags::PLVL | DescriptorFlags::PLVH),
);
if !flags.contains(DescriptorFlags::MATL) {
config |= if flags.contains(DescriptorFlags::MATH) {
MappingFlags::UNCACHED
} else {
MappingFlags::DEVICE
};
}
config
}
fn present(&self) -> bool {
self.flags().contains(DescriptorFlags::V) || self.is_table()
}
fn huge(&self, is_dir: bool) -> bool {
is_dir && self.flags().contains(DescriptorFlags::GH)
}
fn unused(&self) -> bool {
self.0 == 0
}
fn clear(&mut self) {
self.0 = 0;
}
}
impl core::fmt::Debug for La64Pte {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("La64Pte")
.field("raw", &self.0)
.field("config", &self.config(false))
.finish()
}
}