use core::{fmt::Debug, mem::transmute};
use page_table_generic::{GenericPTE, PTEConfig, PageAttribute};
#[allow(unused)]
#[repr(u64)]
pub enum AttrIndex {
Device = 0,
Normal = 1,
NonCacheable = 2,
}
impl AttrIndex {
#[cfg(target_arch = "aarch64")]
pub const fn mair_value() -> u64 {
use aarch64_cpu::registers::*;
let attr0 = MAIR_EL1::Attr0_Device::nonGathering_nonReordering_EarlyWriteAck.value;
let attr1 = MAIR_EL1::Attr1_Normal_Inner::WriteBack_NonTransient_ReadWriteAlloc.value
| MAIR_EL1::Attr1_Normal_Outer::WriteBack_NonTransient_ReadWriteAlloc.value;
let attr2 = MAIR_EL1::Attr2_Normal_Inner::NonCacheable.value
| MAIR_EL1::Attr2_Normal_Outer::NonCacheable.value;
attr0 | attr1 | attr2 }
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct PTE(u64);
impl PTE {
pub fn set_is_block(&mut self, val: bool) {
if val {
self.0 &= !DescriptorAttr::NON_BLOCK.bits();
} else {
self.0 |= DescriptorAttr::NON_BLOCK.bits();
}
}
pub const fn empty() -> Self {
Self(0)
}
pub fn invalid(&self) -> bool {
self.0 & DescriptorAttr::VALID.bits() == 0
}
pub fn set_valid(&mut self) {
self.0 |= DescriptorAttr::VALID.bits();
}
pub fn clear_valid(&mut self) {
self.0 &= !DescriptorAttr::VALID.bits();
}
pub fn is_block(&self) -> bool {
self.0 & DescriptorAttr::NON_BLOCK.bits() == 0
}
fn paddr(&self) -> usize {
(self.0 & DescriptorAttr::PHYS_ADDR_MASK) as usize
}
}
impl Into<u64> for PTE {
fn into(self) -> u64 {
self.0
}
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
pub struct DescriptorAttr: u64 {
const VALID = 1 << 0;
const NON_BLOCK = 1 << 1;
const ATTR_INDX = 0b111 << 2;
const NS = 1 << 5;
const AP_EL0 = 1 << 6;
const AP_RO = 1 << 7;
const INNER = 1 << 8;
const SHAREABLE = 1 << 9;
const AF = 1 << 10;
const NG = 1 << 11;
const CONTIGUOUS = 1 << 52;
const PXN = 1 << 53;
const UXN = 1 << 54;
const PXN_TABLE = 1 << 59;
const XN_TABLE = 1 << 60;
const AP_NO_EL0_TABLE = 1 << 61;
const AP_NO_WRITE_TABLE = 1 << 62;
const NS_TABLE = 1 << 63;
}
}
impl From<PageAttribute> for DescriptorAttr {
fn from(value: PageAttribute) -> Self {
let mut attr = if value.contains(PageAttribute::Device) {
DescriptorAttr::from_index(AttrIndex::Device)
} else if value.contains(PageAttribute::NonCache) {
DescriptorAttr::from_index(AttrIndex::NonCacheable)
} else {
DescriptorAttr::from_index(AttrIndex::Normal)
} | DescriptorAttr::AF;
if value.contains(PageAttribute::Read) {
attr |= DescriptorAttr::VALID | DescriptorAttr::SHAREABLE;
}
if !value.contains(PageAttribute::Write) {
attr |= DescriptorAttr::AP_RO;
}
if value.contains(PageAttribute::User) {
attr |= Self::AP_EL0 | Self::PXN;
if !value.contains(PageAttribute::Execute) {
attr |= Self::UXN;
}
} else {
attr |= Self::UXN;
if !value.contains(PageAttribute::Execute) {
attr |= Self::PXN;
}
}
attr
}
}
impl From<DescriptorAttr> for PageAttribute {
fn from(attr: DescriptorAttr) -> Self {
if !attr.contains(DescriptorAttr::VALID) {
return Self::empty();
}
let mut flags = Self::Read;
if !attr.contains(DescriptorAttr::AP_RO) {
flags |= Self::Write;
}
if attr.contains(DescriptorAttr::AP_EL0) {
flags |= Self::User;
if !attr.contains(DescriptorAttr::UXN) {
flags |= Self::Execute;
}
} else if !attr.intersects(DescriptorAttr::PXN) {
flags |= Self::Execute;
}
let idx: AttrIndex = unsafe { transmute(attr.attr_index()) };
match idx {
AttrIndex::Device => flags |= Self::Device,
AttrIndex::NonCacheable => flags |= Self::NonCache,
_ => {}
}
flags
}
}
impl DescriptorAttr {
const PHYS_ADDR_MASK: u64 = 0x0000_ffff_ffff_f000;
pub fn new(mair_idx: u64) -> Self {
let bits = (mair_idx) << 2;
Self::from_bits_retain(bits) | Self::VALID
}
pub fn attr_index(&self) -> u64 {
(self.bits() & 0b11100) >> 2
}
pub fn from_index(idx: AttrIndex) -> Self {
Self::new(idx as u64)
}
}
impl From<PTEConfig> for DescriptorAttr {
fn from(value: PTEConfig) -> Self {
let mut des = DescriptorAttr::from(value.attributes);
if value.is_block {
des &= !DescriptorAttr::NON_BLOCK;
} else {
des |= DescriptorAttr::NON_BLOCK;
}
des |=
DescriptorAttr::from_bits_retain(value.paddr as u64 & Self::PHYS_ADDR_MASK);
des
}
}
impl GenericPTE for PTE {
fn read(&self) -> PTEConfig {
PTEConfig {
paddr: PTE::paddr(self),
is_block: self.is_block(),
attributes: DescriptorAttr::from_bits_retain(self.0).into(),
}
}
fn set(&mut self, pte: PTEConfig) {
let des = DescriptorAttr::from(pte);
self.0 = des.bits();
}
fn new_page(pte: PTEConfig) -> Self {
let des = DescriptorAttr::from(pte);
Self(des.bits())
}
fn paddr(&self) -> usize {
PTE::paddr(self)
}
}
impl Debug for PTE {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let pte = self.read();
let attr = DescriptorAttr::from_bits_retain(self.0);
write!(f, "PTE @{:#x}, attrs: {:?}", pte.paddr, attr)
}
}