use core::fmt;
use memory_addr::PhysAddr;
use crate::{GenericPTE, MappingFlags};
bitflags::bitflags! {
#[derive(Debug, Clone, Copy)]
pub struct DescriptorAttr: u32 {
const TYPE_BIT0 = 1 << 0;
const TYPE_BIT1 = 1 << 1;
const B = 1 << 2;
const C = 1 << 3;
const XN_SMALL = 1 << 4;
const DOMAIN_MASK = 0b1111 << 5;
const IMP = 1 << 9;
const AP0 = 1 << 10;
const AP1 = 1 << 11;
const TEX0 = 1 << 12;
const TEX1 = 1 << 13;
const TEX2 = 1 << 14;
const AP2 = 1 << 15;
const S = 1 << 16;
const NG = 1 << 17;
const NS = 1 << 19;
const SECTION = Self::TYPE_BIT1.bits();
const PAGE_TABLE = Self::TYPE_BIT0.bits();
const SMALL_PAGE = Self::TYPE_BIT1.bits();
const NORMAL_MEMORY = Self::TEX0.bits() | Self::C.bits() | Self::B.bits();
const DEVICE_MEMORY = Self::B.bits();
const NORMAL_SHAREABLE = Self::NORMAL_MEMORY.bits() | Self::S.bits();
const AP_PRIV_RW = Self::AP0.bits();
const AP_USER_RW = Self::AP0.bits() | Self::AP1.bits();
const AP_PRIV_RO = Self::AP2.bits() | Self::AP0.bits();
const AP_USER_RO = Self::AP2.bits() | Self::AP0.bits() | Self::AP1.bits();
}
}
impl DescriptorAttr {
#[inline]
pub const fn common_flags(flags: MappingFlags) -> u32 {
let mut bits = 0;
if flags.contains(MappingFlags::DEVICE) {
bits |= Self::DEVICE_MEMORY.bits();
} else if flags.contains(MappingFlags::UNCACHED) {
bits |= Self::TEX0.bits();
} else {
bits |= Self::NORMAL_SHAREABLE.bits();
}
let has_write = flags.contains(MappingFlags::WRITE);
let has_user = flags.contains(MappingFlags::USER);
if has_user {
if has_write {
bits |= Self::AP_USER_RW.bits();
} else {
bits |= Self::AP_USER_RO.bits();
}
} else if has_write {
bits |= Self::AP_PRIV_RW.bits();
} else {
bits |= Self::AP_PRIV_RO.bits();
}
bits
}
#[inline]
pub const fn from_mapping_flags_section(flags: MappingFlags) -> Self {
let mut bits = Self::SECTION.bits();
if flags.is_empty() {
return Self::from_bits_retain(0);
}
bits |= Self::common_flags(flags);
if !flags.contains(MappingFlags::EXECUTE) {
bits |= Self::XN_SMALL.bits();
}
Self::from_bits_retain(bits)
}
#[inline]
pub const fn from_mapping_flags_small_page(flags: MappingFlags) -> Self {
let mut bits = Self::SMALL_PAGE.bits();
if flags.is_empty() {
return Self::from_bits_retain(0);
}
let attr = Self::common_flags(flags);
bits |= attr & (Self::B.bits() | Self::C.bits());
let shift_mask = Self::AP0.bits()
| Self::AP1.bits()
| Self::TEX0.bits()
| Self::TEX1.bits()
| Self::TEX2.bits()
| Self::AP2.bits()
| Self::S.bits()
| Self::NG.bits();
bits |= (attr & shift_mask) >> 6;
if !flags.contains(MappingFlags::EXECUTE) {
bits |= Self::TYPE_BIT0.bits();
}
Self::from_bits_retain(bits)
}
pub const fn descriptor_type(&self) -> u32 {
self.bits() & 0b11
}
pub const fn is_valid(&self) -> bool {
self.descriptor_type() != 0
}
pub const fn is_section(&self) -> bool {
self.descriptor_type() == 0b10
}
pub const fn is_page_table(&self) -> bool {
self.descriptor_type() == 0b01
}
pub const fn is_small_page(&self) -> bool {
self.descriptor_type() == 0b10
}
}
impl From<DescriptorAttr> for MappingFlags {
#[inline]
fn from(attr: DescriptorAttr) -> Self {
if !attr.is_valid() {
return Self::empty();
}
let mut flags = Self::READ;
let ap = ((attr.bits() >> 10) & 0b11) | (((attr.bits() >> 15) & 1) << 2);
match ap {
0b001 | 0b011 => flags |= Self::WRITE, _ => {}
}
if (ap & 0b10) != 0 {
flags |= Self::USER;
}
if !attr.contains(DescriptorAttr::XN_SMALL) {
flags |= Self::EXECUTE;
}
let tex = (attr.bits() >> 12) & 0b111;
let c = (attr.bits() >> 3) & 1;
let b = (attr.bits() >> 2) & 1;
if tex == 0 && c == 0 && b == 1 {
flags |= Self::DEVICE;
} else if tex == 1 && c == 0 && b == 0 {
flags |= Self::UNCACHED;
}
flags
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct A32PTE(u32);
impl A32PTE {
const SECTION_ADDR_MASK: u32 = 0xfff0_0000;
const PAGE_TABLE_ADDR_MASK: u32 = 0xffff_fc00;
const SMALL_PAGE_ADDR_MASK: u32 = 0xffff_f000;
pub const fn empty() -> Self {
Self(0)
}
#[inline]
pub const fn new_section(paddr: PhysAddr, flags: MappingFlags) -> Self {
let attr = DescriptorAttr::from_mapping_flags_section(flags);
Self(attr.bits() | (paddr.as_usize() as u32 & Self::SECTION_ADDR_MASK))
}
#[inline]
pub const fn new_small_page(paddr: PhysAddr, flags: MappingFlags) -> Self {
let attr = DescriptorAttr::from_mapping_flags_small_page(flags);
Self(attr.bits() | (paddr.as_usize() as u32 & Self::SMALL_PAGE_ADDR_MASK))
}
pub const fn descriptor_type(&self) -> u32 {
self.0 & 0b11
}
pub const fn is_section(&self) -> bool {
(self.0 & 0b11) == 0b10
}
}
impl GenericPTE for A32PTE {
#[inline]
fn new_page(paddr: PhysAddr, flags: MappingFlags, is_huge: bool) -> Self {
if is_huge {
Self::new_section(paddr, flags)
} else {
Self::new_small_page(paddr, flags)
}
}
#[inline]
fn new_table(paddr: PhysAddr) -> Self {
let attr = DescriptorAttr::PAGE_TABLE;
Self(attr.bits() | (paddr.as_usize() as u32 & Self::PAGE_TABLE_ADDR_MASK))
}
fn paddr(&self) -> PhysAddr {
let desc_type = self.descriptor_type();
let addr = match desc_type {
0b01 => self.0 & Self::PAGE_TABLE_ADDR_MASK, 0b10 => {
if (self.0 & Self::SECTION_ADDR_MASK) >= 0x10_0000 {
self.0 & Self::SECTION_ADDR_MASK } else {
self.0 & Self::SMALL_PAGE_ADDR_MASK }
}
0b11 => self.0 & Self::SMALL_PAGE_ADDR_MASK, _ => 0,
};
PhysAddr::from(addr as usize)
}
fn flags(&self) -> MappingFlags {
DescriptorAttr::from_bits_truncate(self.0).into()
}
fn set_paddr(&mut self, paddr: PhysAddr) {
let desc_type = self.descriptor_type();
match desc_type {
0b01 => {
self.0 = (self.0 & !Self::PAGE_TABLE_ADDR_MASK)
| (paddr.as_usize() as u32 & Self::PAGE_TABLE_ADDR_MASK);
}
0b10 => {
if self.is_section() {
self.0 = (self.0 & !Self::SECTION_ADDR_MASK)
| (paddr.as_usize() as u32 & Self::SECTION_ADDR_MASK);
} else {
self.0 = (self.0 & !Self::SMALL_PAGE_ADDR_MASK)
| (paddr.as_usize() as u32 & Self::SMALL_PAGE_ADDR_MASK);
}
}
_ => {}
}
}
fn set_flags(&mut self, flags: MappingFlags, is_huge: bool) {
let paddr = self.paddr();
*self = if is_huge {
Self::new_section(paddr, flags)
} else {
Self::new_small_page(paddr, flags)
};
}
fn bits(self) -> usize {
self.0 as usize
}
fn is_unused(&self) -> bool {
self.0 == 0
}
fn is_present(&self) -> bool {
self.descriptor_type() != 0
}
fn is_huge(&self) -> bool {
self.is_section()
}
fn clear(&mut self) {
self.0 = 0;
}
}
impl fmt::Debug for A32PTE {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut f = f.debug_struct("A32PTE");
f.field("raw", &format_args!("{:#010x}", self.0))
.field(
"type",
&match self.descriptor_type() {
0b00 => "Invalid",
0b01 => "PageTable",
0b10 => {
if self.is_section() {
"Section"
} else {
"SmallPage"
}
}
0b11 => "Reserved",
_ => "Unknown",
},
)
.field("paddr", &self.paddr())
.field("flags", &self.flags())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_section_descriptor() {
let paddr = PhysAddr::from(0x4000_0000);
let flags = MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE;
let pte = A32PTE::new_section(paddr, flags);
assert!(pte.is_present());
assert!(pte.is_huge());
assert_eq!(pte.paddr(), paddr);
assert!(pte.flags().contains(MappingFlags::READ));
assert!(pte.flags().contains(MappingFlags::WRITE));
}
#[test]
fn test_small_page_descriptor() {
let paddr = PhysAddr::from(0x4000_1000);
let flags = MappingFlags::READ | MappingFlags::WRITE;
let pte = A32PTE::new_small_page(paddr, flags);
assert!(pte.is_present());
assert_eq!(pte.paddr(), paddr);
assert!(pte.flags().contains(MappingFlags::READ));
}
#[test]
fn test_page_table_descriptor() {
let paddr = PhysAddr::from(0x4000_0400);
let pte = A32PTE::new_table(paddr);
assert!(pte.is_present());
assert!(!pte.is_huge());
assert_eq!(pte.paddr(), paddr);
}
}