use arbitrary_int::{u12, u2, u3, u4};
#[derive(Debug, Copy, Clone)]
#[repr(u8)]
pub enum AccessPermissions {
PermissionFault = 0b000,
PrivilegedOnly = 0b001,
NoUserWrite = 0b010,
FullAccess = 0b011,
_Reserved1 = 0b100,
PrivilegedReadOnly = 0b101,
ReadOnly = 0b110,
_Reserved2 = 0b111,
}
impl AccessPermissions {
const fn ap(&self) -> u8 {
(*self as u8) & 0b11
}
const fn apx(&self) -> bool {
(*self as u8) > (AccessPermissions::FullAccess as u8)
}
}
#[derive(Debug)]
#[repr(u8)]
#[bitbybit::bitenum(u2, exhaustive = true)]
pub enum L1EntryType {
Fault = 0b00,
PageTable = 0b01,
Section = 0b10,
Supersection = 0b11,
}
#[derive(Debug, Copy, Clone)]
pub struct MemoryRegionAttributesRaw {
type_extensions: u8,
c: bool,
b: bool,
}
impl MemoryRegionAttributesRaw {
pub const fn new(type_extensions: u8, c: bool, b: bool) -> Self {
Self {
type_extensions,
c,
b,
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum CacheableMemoryAttribute {
NonCacheable = 0b00,
WriteBackWriteAlloc = 0b01,
WriteThroughNoWriteAlloc = 0b10,
WriteBackNoWriteAlloc = 0b11,
}
#[derive(Debug, Copy, Clone)]
pub enum MemoryRegionAttributes {
StronglyOrdered,
ShareableDevice,
OuterAndInnerWriteThroughNoWriteAlloc,
OuterAndInnerWriteBackNoWriteAlloc,
OuterAndInnerNonCacheable,
OuterAndInnerWriteBackWriteAlloc,
NonShareableDevice,
CacheableMemory {
inner: CacheableMemoryAttribute,
outer: CacheableMemoryAttribute,
},
}
impl MemoryRegionAttributes {
pub const fn as_raw(&self) -> MemoryRegionAttributesRaw {
match self {
MemoryRegionAttributes::StronglyOrdered => {
MemoryRegionAttributesRaw::new(0b000, false, false)
}
MemoryRegionAttributes::ShareableDevice => {
MemoryRegionAttributesRaw::new(0b000, false, true)
}
MemoryRegionAttributes::OuterAndInnerWriteThroughNoWriteAlloc => {
MemoryRegionAttributesRaw::new(0b000, true, false)
}
MemoryRegionAttributes::OuterAndInnerWriteBackNoWriteAlloc => {
MemoryRegionAttributesRaw::new(0b000, true, true)
}
MemoryRegionAttributes::OuterAndInnerNonCacheable => {
MemoryRegionAttributesRaw::new(0b001, false, false)
}
MemoryRegionAttributes::OuterAndInnerWriteBackWriteAlloc => {
MemoryRegionAttributesRaw::new(0b001, true, true)
}
MemoryRegionAttributes::NonShareableDevice => {
MemoryRegionAttributesRaw::new(0b010, false, false)
}
MemoryRegionAttributes::CacheableMemory { inner, outer } => {
MemoryRegionAttributesRaw::new(
(1 << 2) | (*outer as u8),
(*inner as u8 & 0b10) != 0,
(*inner as u8 & 0b01) != 0,
)
}
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct SectionAttributes {
pub non_global: bool,
pub p_bit: bool,
pub shareable: bool,
pub access: AccessPermissions,
pub memory_attrs: MemoryRegionAttributesRaw,
pub domain: u8,
pub execute_never: bool,
}
#[bitbybit::bitfield(u32)]
pub struct L1Section {
#[bits(20..=31, rw)]
base_addr: u12,
#[bit(16, rw)]
ng: bool,
#[bit(16, rw)]
s: bool,
#[bit(15, rw)]
apx: bool,
#[bits(12..=14, rw)]
tex: u3,
#[bits(10..=11, rw)]
ap: u2,
#[bit(9, rw)]
p_bit: bool,
#[bits(5..=8, rw)]
domain: u4,
#[bit(4, rw)]
xn: bool,
#[bit(3, rw)]
c: bool,
#[bit(2, rw)]
b: bool,
#[bits(0..=1, rw)]
entry_type: L1EntryType,
}
impl core::fmt::Debug for L1Section {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"L1Section {{ base_addr={:#x} ng={} s={} apx={} tex={:#b} ap={:#b} domain={:#b} xn={} c={} b={} }}",
self.base_addr(),
self.ng() as u8,
self.s() as u8,
self.apx() as u8,
self.tex(),
self.ap(),
self.domain(),
self.xn() as u8,
self.c() as u8,
self.b() as u8,
)
}
}
impl L1Section {
pub const fn new(phys_addr: u32, section_attrs: SectionAttributes) -> Self {
if phys_addr & 0x000F_FFFF != 0 {
panic!("physical base address for L1 section must be aligned to 1 MB");
}
let higher_bits = phys_addr >> 20;
let raw = (higher_bits << 20)
| ((section_attrs.non_global as u32) << 17)
| ((section_attrs.shareable as u32) << 16)
| ((section_attrs.access.apx() as u32) << 15)
| ((section_attrs.memory_attrs.type_extensions as u32) << 12)
| ((section_attrs.access.ap() as u32) << 10)
| ((section_attrs.p_bit as u32) << 9)
| ((section_attrs.domain as u32) << 5)
| ((section_attrs.execute_never as u32) << 4)
| ((section_attrs.memory_attrs.c as u32) << 3)
| ((section_attrs.memory_attrs.b as u32) << 2)
| L1EntryType::Section as u32;
Self::new_with_raw_value(raw)
}
}