use super::{AccessType, SecurityState, PA};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PagePermissions(u8);
impl PagePermissions {
const READ: u8 = 0b0001;
const WRITE: u8 = 0b0010;
const EXEC: u8 = 0b0100;
const PRIV_ONLY: u8 = 0b1000;
#[must_use]
#[inline]
pub const fn new(read: bool, write: bool, execute: bool) -> Self {
let mut bits = 0u8;
if read {
bits |= Self::READ;
}
if write {
bits |= Self::WRITE;
}
if execute {
bits |= Self::EXEC;
}
Self(bits)
}
#[must_use]
#[inline]
pub const fn none() -> Self {
Self::new(false, false, false)
}
#[must_use]
#[inline]
pub const fn read_only() -> Self {
Self::new(true, false, false)
}
#[must_use]
#[inline]
pub const fn read_only_privileged() -> Self {
Self(Self::READ | Self::PRIV_ONLY)
}
#[must_use]
#[inline]
pub const fn with_privileged_only(self, priv_only: bool) -> Self {
if priv_only {
Self(self.0 | Self::PRIV_ONLY)
} else {
Self(self.0 & !Self::PRIV_ONLY)
}
}
#[must_use]
#[inline]
pub const fn write_only() -> Self {
Self::new(false, true, false)
}
#[must_use]
#[inline]
pub const fn execute_only() -> Self {
Self::new(false, false, true)
}
#[must_use]
#[inline]
pub const fn read_write() -> Self {
Self::new(true, true, false)
}
#[must_use]
#[inline]
pub const fn read_execute() -> Self {
Self::new(true, false, true)
}
#[must_use]
#[inline]
pub const fn all() -> Self {
Self::new(true, true, true)
}
#[must_use]
#[inline(always)]
pub const fn read(self) -> bool {
(self.0 & Self::READ) != 0
}
#[must_use]
#[inline(always)]
pub const fn write(self) -> bool {
(self.0 & Self::WRITE) != 0
}
#[must_use]
#[inline(always)]
pub const fn execute(self) -> bool {
(self.0 & Self::EXEC) != 0
}
#[must_use]
#[inline(always)]
pub const fn privileged_only(self) -> bool {
(self.0 & Self::PRIV_ONLY) != 0
}
#[must_use]
#[inline]
pub const fn allows(self, access: AccessType) -> bool {
match access {
AccessType::None => true, AccessType::Read => self.read(),
AccessType::Write => self.write(),
AccessType::Execute => self.execute(),
AccessType::ReadWrite => self.read() && self.write(),
AccessType::ReadExecute => self.read() && self.execute(),
AccessType::WriteExecute => self.write() && self.execute(),
AccessType::ReadWriteExecute => self.read() && self.write() && self.execute(),
AccessType::ReadPrivileged => self.read(),
AccessType::WritePrivileged => self.write(),
AccessType::ReadWritePrivileged => self.read() && self.write(),
AccessType::ExecutePrivileged => self.execute(),
AccessType::ReadExecutePrivileged => self.read() && self.execute(),
AccessType::WriteExecutePrivileged => self.write() && self.execute(),
AccessType::ReadWriteExecutePrivileged => self.read() && self.write() && self.execute(),
}
}
#[must_use]
#[inline]
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[must_use]
#[inline]
pub const fn intersection(self, other: Self) -> Self {
let rwx = self.0 & other.0 & 0b0111;
let priv_bit = (self.0 | other.0) & Self::PRIV_ONLY;
Self(rwx | priv_bit)
}
#[must_use]
#[inline]
pub const fn is_subset_of(self, other: &Self) -> bool {
(self.0 & !other.0) == 0
}
}
impl Default for PagePermissions {
#[inline]
fn default() -> Self {
Self::none()
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PageEntry {
physical_address: PA,
permissions: PagePermissions,
valid: bool,
security_state: SecurityState,
cacheable: bool,
shareable: bool,
device_memory: bool,
access_flag: bool,
dirty: bool,
}
impl PageEntry {
#[must_use]
pub const fn new(physical_address: PA, permissions: PagePermissions) -> Self {
Self {
physical_address,
permissions,
valid: true,
security_state: SecurityState::NonSecure,
cacheable: true,
shareable: true,
device_memory: false,
access_flag: false,
dirty: false,
}
}
#[must_use]
pub const fn with_security_state(
physical_address: PA,
permissions: PagePermissions,
security_state: SecurityState,
) -> Self {
Self {
physical_address,
permissions,
valid: true,
security_state,
cacheable: true,
shareable: true,
device_memory: false,
access_flag: false,
dirty: false,
}
}
#[must_use]
pub const fn builder() -> PageEntryBuilder {
PageEntryBuilder::new()
}
#[must_use]
#[inline]
pub const fn physical_address(&self) -> PA {
self.physical_address
}
#[must_use]
#[inline]
pub const fn permissions(&self) -> PagePermissions {
self.permissions
}
#[must_use]
#[inline]
pub const fn security_state(&self) -> SecurityState {
self.security_state
}
#[must_use]
#[inline]
pub const fn is_valid(&self) -> bool {
self.valid
}
#[must_use]
#[inline]
pub const fn is_cacheable(&self) -> bool {
self.cacheable
}
#[must_use]
#[inline]
pub const fn is_shareable(&self) -> bool {
self.shareable
}
#[must_use]
#[inline]
pub const fn is_device_memory(&self) -> bool {
self.device_memory
}
#[must_use]
pub const fn mark_valid(mut self) -> Self {
self.valid = true;
self
}
#[must_use]
pub const fn mark_invalid(mut self) -> Self {
self.valid = false;
self
}
#[must_use]
pub const fn with_cacheable(mut self, cacheable: bool) -> Self {
self.cacheable = cacheable;
if self.device_memory && cacheable {
self.cacheable = false;
}
self
}
#[must_use]
pub const fn with_shareable(mut self, shareable: bool) -> Self {
self.shareable = shareable;
self
}
#[must_use]
pub const fn with_device_memory(mut self, device_memory: bool) -> Self {
self.device_memory = device_memory;
if device_memory {
self.cacheable = false;
}
self
}
#[must_use]
pub const fn with_permissions(mut self, permissions: PagePermissions) -> Self {
self.permissions = permissions;
self
}
#[must_use]
#[inline]
pub const fn is_access_flag_set(&self) -> bool {
self.access_flag
}
#[must_use]
#[inline]
pub const fn is_dirty(&self) -> bool {
self.dirty
}
#[must_use]
pub const fn with_access_flag(mut self, v: bool) -> Self {
self.access_flag = v;
self
}
#[must_use]
pub const fn with_dirty(mut self, v: bool) -> Self {
self.dirty = v;
self
}
}
impl Default for PageEntry {
fn default() -> Self {
Self {
physical_address: PA::new(0).unwrap_or_else(|_| unreachable!()),
permissions: PagePermissions::default(),
valid: false,
security_state: SecurityState::NonSecure,
cacheable: false,
shareable: false,
device_memory: false,
access_flag: false,
dirty: false,
}
}
}
#[derive(Debug, Clone)]
pub struct PageEntryBuilder {
physical_address: Option<PA>,
permissions: PagePermissions,
security_state: SecurityState,
cacheable: bool,
shareable: bool,
device_memory: bool,
access_flag: bool,
dirty: bool,
}
impl PageEntryBuilder {
#[must_use]
const fn new() -> Self {
Self {
physical_address: None,
permissions: PagePermissions::none(),
security_state: SecurityState::NonSecure,
cacheable: true,
shareable: true,
device_memory: false,
access_flag: false,
dirty: false,
}
}
#[must_use]
pub const fn physical_address(mut self, pa: PA) -> Self {
self.physical_address = Some(pa);
self
}
#[must_use]
pub const fn permissions(mut self, perms: PagePermissions) -> Self {
self.permissions = perms;
self
}
#[must_use]
pub const fn security_state(mut self, state: SecurityState) -> Self {
self.security_state = state;
self
}
#[must_use]
pub const fn cacheable(mut self, cacheable: bool) -> Self {
self.cacheable = cacheable;
self
}
#[must_use]
pub const fn shareable(mut self, shareable: bool) -> Self {
self.shareable = shareable;
self
}
#[must_use]
pub const fn device_memory(mut self, device_memory: bool) -> Self {
self.device_memory = device_memory;
self
}
#[must_use]
pub const fn access_flag(mut self, v: bool) -> Self {
self.access_flag = v;
self
}
#[must_use]
pub const fn dirty(mut self, v: bool) -> Self {
self.dirty = v;
self
}
#[must_use]
pub fn build(self) -> PageEntry {
let physical_address = self.physical_address.expect("Physical address must be set");
let mut entry = PageEntry {
physical_address,
permissions: self.permissions,
valid: true,
security_state: self.security_state,
cacheable: self.cacheable,
shareable: self.shareable,
device_memory: self.device_memory,
access_flag: self.access_flag,
dirty: self.dirty,
};
if entry.device_memory {
entry.cacheable = false;
}
entry
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_page_entry_access_flag_default_false() {
let pa = PA::new(0x1000).unwrap();
let entry = PageEntry::new(pa, PagePermissions::read_write());
assert!(!entry.is_access_flag_set());
}
#[test]
fn test_page_entry_dirty_default_false() {
let pa = PA::new(0x1000).unwrap();
let entry = PageEntry::new(pa, PagePermissions::read_write());
assert!(!entry.is_dirty());
}
#[test]
fn test_page_entry_with_access_flag() {
let pa = PA::new(0x1000).unwrap();
let entry = PageEntry::new(pa, PagePermissions::read_write()).with_access_flag(true);
assert!(entry.is_access_flag_set());
}
#[test]
fn test_page_entry_with_dirty() {
let pa = PA::new(0x1000).unwrap();
let entry = PageEntry::new(pa, PagePermissions::read_write()).with_dirty(true);
assert!(entry.is_dirty());
}
#[test]
fn test_page_permissions_basic() {
let perms = PagePermissions::new(true, false, true);
assert!(perms.read());
assert!(!perms.write());
assert!(perms.execute());
}
#[test]
fn test_page_entry_basic() {
let pa = PA::new(0x1000).unwrap();
let perms = PagePermissions::read_write();
let entry = PageEntry::new(pa, perms);
assert!(entry.is_valid());
assert_eq!(entry.physical_address(), pa);
assert_eq!(entry.permissions(), perms);
}
#[test]
fn test_device_memory_not_cacheable() {
let pa = PA::new(0x2000).unwrap();
let entry = PageEntry::new(pa, PagePermissions::default())
.with_device_memory(true)
.with_cacheable(true);
assert!(entry.is_device_memory());
assert!(!entry.is_cacheable(), "Device memory should not be cacheable");
}
}