use crate::Translation;
use crate::paging::LEAF_LEVEL;
use crate::paging::PAGE_SIZE;
use crate::paging::PageTableWithLevel;
use bitflags::bitflags;
use core::fmt::{self, Debug, Display, Formatter};
use core::marker::PhantomData;
use core::ops::{Add, BitAnd, BitOr, BitXor, Not, Sub};
use core::sync::atomic::{AtomicUsize, Ordering};
#[derive(Copy, Clone, Default, Eq, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct VirtualAddress(pub usize);
impl Display for VirtualAddress {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{:#018x}", self.0)
}
}
impl Debug for VirtualAddress {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "VirtualAddress({})", self)
}
}
impl Sub for VirtualAddress {
type Output = usize;
fn sub(self, other: Self) -> Self::Output {
self.0 - other.0
}
}
impl Add<usize> for VirtualAddress {
type Output = Self;
fn add(self, other: usize) -> Self {
Self(self.0 + other)
}
}
impl Sub<usize> for VirtualAddress {
type Output = Self;
fn sub(self, other: usize) -> Self {
Self(self.0 - other)
}
}
#[derive(Copy, Clone, Default, Eq, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct PhysicalAddress(pub usize);
impl Display for PhysicalAddress {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{:#018x}", self.0)
}
}
impl Debug for PhysicalAddress {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "PhysicalAddress({})", self)
}
}
impl Sub for PhysicalAddress {
type Output = usize;
fn sub(self, other: Self) -> Self::Output {
self.0 - other.0
}
}
impl Add<usize> for PhysicalAddress {
type Output = Self;
fn add(self, other: usize) -> Self {
Self(self.0 + other)
}
}
impl Sub<usize> for PhysicalAddress {
type Output = Self;
fn sub(self, other: usize) -> Self {
Self(self.0 - other)
}
}
pub trait PagingAttributes:
bitflags::Flags<Bits = usize>
+ Copy
+ Clone
+ Debug
+ PartialEq
+ Default
+ Send
+ Sync
+ PartialOrd
+ BitOr<Output = Self>
+ BitAnd<Output = Self>
+ BitXor<Output = Self>
+ Sub<Output = Self>
+ Not<Output = Self>
{
const VALID: Self;
const TABLE_OR_PAGE: Self;
fn is_bbm_safe(old: Self, new: Self) -> bool;
}
bitflags! {
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct El1Attributes: usize {
const VALID = 1 << 0;
const TABLE_OR_PAGE = 1 << 1;
const ATTRIBUTE_INDEX_0 = 0 << 2;
const ATTRIBUTE_INDEX_1 = 1 << 2;
const ATTRIBUTE_INDEX_2 = 2 << 2;
const ATTRIBUTE_INDEX_3 = 3 << 2;
const ATTRIBUTE_INDEX_4 = 4 << 2;
const ATTRIBUTE_INDEX_5 = 5 << 2;
const ATTRIBUTE_INDEX_6 = 6 << 2;
const ATTRIBUTE_INDEX_7 = 7 << 2;
const OUTER_SHAREABLE = 2 << 8;
const INNER_SHAREABLE = 3 << 8;
const NS = 1 << 5;
const USER = 1 << 6;
const READ_ONLY = 1 << 7;
const ACCESSED = 1 << 10;
const NON_GLOBAL = 1 << 11;
const GP = 1 << 50;
const DBM = 1 << 51;
const PXN = 1 << 53;
const UXN = 1 << 54;
const SWFLAG_0 = 1 << 55;
const SWFLAG_1 = 1 << 56;
const SWFLAG_2 = 1 << 57;
const SWFLAG_3 = 1 << 58;
const PXN_TABLE = 1 << 59;
const XN_TABLE = 1 << 60;
const AP_TABLE_NO_EL0 = 1 << 61;
const AP_TABLE_NO_WRITE = 1 << 62;
const NS_TABLE = 1 << 63;
}
}
impl PagingAttributes for El1Attributes {
const VALID: Self = Self::VALID;
const TABLE_OR_PAGE: Self = Self::TABLE_OR_PAGE;
fn is_bbm_safe(old: Self, new: Self) -> bool {
let clear_allowed_mask = Self::VALID
| Self::READ_ONLY
| Self::ACCESSED
| Self::DBM
| Self::PXN
| Self::UXN
| Self::SWFLAG_0
| Self::SWFLAG_1
| Self::SWFLAG_2
| Self::SWFLAG_3;
let set_allowed_mask = clear_allowed_mask | Self::NON_GLOBAL;
(!old & new & !set_allowed_mask).is_empty() && (old & !new & !clear_allowed_mask).is_empty()
}
}
impl El1Attributes {
pub const SHAREABILITY_MASK: Self = Self::INNER_SHAREABLE;
pub const ATTRIBUTE_INDEX_MASK: Self = Self::ATTRIBUTE_INDEX_7;
}
bitflags! {
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct El23Attributes: usize {
const VALID = 1 << 0;
const TABLE_OR_PAGE = 1 << 1;
const ATTRIBUTE_INDEX_0 = 0 << 2;
const ATTRIBUTE_INDEX_1 = 1 << 2;
const ATTRIBUTE_INDEX_2 = 2 << 2;
const ATTRIBUTE_INDEX_3 = 3 << 2;
const ATTRIBUTE_INDEX_4 = 4 << 2;
const ATTRIBUTE_INDEX_5 = 5 << 2;
const ATTRIBUTE_INDEX_6 = 6 << 2;
const ATTRIBUTE_INDEX_7 = 7 << 2;
const OUTER_SHAREABLE = 2 << 8;
const INNER_SHAREABLE = 3 << 8;
const NS = 1 << 5;
const READ_ONLY = 1 << 7;
const ACCESSED = 1 << 10;
const NON_GLOBAL = 1 << 11;
const GP = 1 << 50;
const DBM = 1 << 51;
const XN = 1 << 53;
const SWFLAG_0 = 1 << 55;
const SWFLAG_1 = 1 << 56;
const SWFLAG_2 = 1 << 57;
const SWFLAG_3 = 1 << 58;
const PXN_TABLE = 1 << 59;
const XN_TABLE = 1 << 60;
const AP_TABLE_NO_EL0 = 1 << 61;
const AP_TABLE_NO_WRITE = 1 << 62;
const NS_TABLE = 1 << 63;
}
}
impl PagingAttributes for El23Attributes {
const VALID: Self = Self::VALID;
const TABLE_OR_PAGE: Self = Self::TABLE_OR_PAGE;
fn is_bbm_safe(old: Self, new: Self) -> bool {
let clear_allowed_mask = Self::VALID
| Self::READ_ONLY
| Self::ACCESSED
| Self::DBM
| Self::XN
| Self::SWFLAG_0
| Self::SWFLAG_1
| Self::SWFLAG_2
| Self::SWFLAG_3;
let set_allowed_mask = clear_allowed_mask | Self::NON_GLOBAL;
(!old & new & !set_allowed_mask).is_empty() && (old & !new & !clear_allowed_mask).is_empty()
}
}
impl El23Attributes {
pub const SHAREABILITY_MASK: Self = Self::INNER_SHAREABLE;
pub const ATTRIBUTE_INDEX_MASK: Self = Self::ATTRIBUTE_INDEX_7;
}
bitflags! {
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Stage2Attributes: usize {
const VALID = 1 << 0;
const TABLE_OR_PAGE = 1 << 1;
const MEMATTR_DEVICE_nGnRnE = 0 << 2;
const MEMATTR_DEVICE_nGnRE = 1 << 2;
const MEMATTR_DEVICE_nGRE = 2 << 2;
const MEMATTR_DEVICE_GRE = 3 << 2;
const MEMATTR_NORMAL_INNER_NC = 1 << 2;
const MEMATTR_NORMAL_INNER_WT = 2 << 2;
const MEMATTR_NORMAL_INNER_WB = 3 << 2;
const MEMATTR_NORMAL_OUTER_NC = 1 << 4;
const MEMATTR_NORMAL_OUTER_WT = 2 << 4;
const MEMATTR_NORMAL_OUTER_WB = 3 << 4;
const S2AP_ACCESS_NONE = 0 << 6;
const S2AP_ACCESS_RO = 1 << 6;
const S2AP_ACCESS_WO = 2 << 6;
const S2AP_ACCESS_RW = 3 << 6;
const SH_NONE = 0 << 8;
const SH_OUTER = 2 << 8;
const SH_INNER = 3 << 8;
const ACCESS_FLAG = 1 << 10;
const XN = 1 << 54;
const SWFLAG_0 = 1 << 55;
const SWFLAG_1 = 1 << 56;
const SWFLAG_2 = 1 << 57;
const SWFLAG_3 = 1 << 58;
}
}
impl PagingAttributes for Stage2Attributes {
const VALID: Self = Self::VALID;
const TABLE_OR_PAGE: Self = Self::TABLE_OR_PAGE;
fn is_bbm_safe(old: Self, new: Self) -> bool {
let allowed_mask = Self::VALID
| Self::S2AP_ACCESS_RW | Self::ACCESS_FLAG
| Self::XN
| Self::SWFLAG_0
| Self::SWFLAG_1
| Self::SWFLAG_2
| Self::SWFLAG_3;
((old ^ new) & !allowed_mask).is_empty()
}
}
pub(crate) type DescriptorBits = usize;
#[repr(C)]
pub struct Descriptor<A: PagingAttributes>(pub(crate) AtomicUsize, PhantomData<A>);
impl<A: PagingAttributes> Descriptor<A> {
pub const EMPTY: Self = Self::new(0);
const PHYSICAL_ADDRESS_BITMASK: usize = !(PAGE_SIZE - 1) & !(0xffff << 48);
pub(crate) const fn new(value: usize) -> Self {
Descriptor(AtomicUsize::new(value), PhantomData)
}
pub(crate) fn bits(&self) -> DescriptorBits {
self.0.load(Ordering::Acquire)
}
pub fn output_address(&self) -> PhysicalAddress {
Self::output_address_from_bits(self.bits())
}
fn output_address_from_bits(bits: DescriptorBits) -> PhysicalAddress {
PhysicalAddress(bits & Self::PHYSICAL_ADDRESS_BITMASK)
}
fn flags_from_bits(bits: DescriptorBits) -> A {
A::from_bits_retain(bits & !Self::PHYSICAL_ADDRESS_BITMASK)
}
pub fn flags(&self) -> A {
Self::flags_from_bits(self.bits())
}
pub fn is_valid(&self) -> bool {
(self.bits() & A::VALID.bits()) != 0
}
pub fn is_table_or_page(&self) -> bool {
self.flags().contains(A::TABLE_OR_PAGE | A::VALID)
}
pub(crate) fn set(&mut self, pa: PhysicalAddress, flags: A) {
self.0.store(
(pa.0 & Self::PHYSICAL_ADDRESS_BITMASK) | flags.bits(),
Ordering::Release,
);
}
pub(crate) fn subtable<T: Translation<A>>(
&self,
translation: &T,
level: usize,
) -> Option<PageTableWithLevel<T, A>> {
if level < LEAF_LEVEL && self.is_table_or_page() {
let output_address = self.output_address();
let table = translation.physical_to_virtual(output_address);
return Some(PageTableWithLevel::from_pointer(table, level + 1));
}
None
}
}
impl<A: PagingAttributes> Debug for Descriptor<A> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{:#016x}", self.bits())?;
if self.is_valid() {
write!(f, " ({}, {:?})", self.output_address(), self.flags())?;
}
Ok(())
}
}
enum DescriptorEnum<'a, A: PagingAttributes> {
Active(&'a mut Descriptor<A>),
Inactive(&'a mut Descriptor<A>),
ActiveClone(DescriptorBits, PhantomData<A>),
}
pub struct UpdatableDescriptor<'a, A: PagingAttributes> {
descriptor: DescriptorEnum<'a, A>,
level: usize,
updated: bool,
}
impl<'a, A: PagingAttributes> UpdatableDescriptor<'a, A> {
pub(crate) fn new(desc: &'a mut Descriptor<A>, level: usize, live: bool) -> Self {
Self {
descriptor: if live {
DescriptorEnum::Active(desc)
} else {
DescriptorEnum::Inactive(desc)
},
level,
updated: false,
}
}
pub(crate) fn clone_from(d: &Descriptor<A>, level: usize) -> Self {
Self {
descriptor: DescriptorEnum::ActiveClone(d.bits(), PhantomData),
level,
updated: false,
}
}
pub fn level(&self) -> usize {
self.level
}
pub fn updated(&self) -> bool {
self.updated
}
pub fn is_table(&self) -> bool {
self.level < 3 && self.flags().contains(A::TABLE_OR_PAGE)
}
pub fn bits(&self) -> DescriptorBits {
match &self.descriptor {
DescriptorEnum::Active(d) | DescriptorEnum::Inactive(d) => d.bits(),
DescriptorEnum::ActiveClone(d, _) => *d,
}
}
pub fn set(&mut self, pa: PhysicalAddress, flags: A) -> Result<(), ()> {
if !self.bbm_permits_update(pa, flags) {
return Err(());
}
let val = (pa.0 & Descriptor::<A>::PHYSICAL_ADDRESS_BITMASK) | flags.bits();
match &mut self.descriptor {
DescriptorEnum::Active(d) | DescriptorEnum::Inactive(d) => {
self.updated |= val != d.0.swap(val, Ordering::Release)
}
DescriptorEnum::ActiveClone(d, _) => {
self.updated |= *d != val;
*d = val
}
};
Ok(())
}
pub fn output_address(&self) -> PhysicalAddress {
Descriptor::<A>::output_address_from_bits(self.bits())
}
pub fn flags(&self) -> A {
Descriptor::<A>::flags_from_bits(self.bits())
}
fn is_live_and_valid(&self) -> bool {
match &self.descriptor {
DescriptorEnum::Inactive(_) => false,
_ => self.flags().contains(A::VALID),
}
}
fn bbm_permits_update(&self, pa: PhysicalAddress, flags: A) -> bool {
if !self.is_live_and_valid() || !flags.contains(A::VALID) {
return true;
}
if pa != self.output_address() {
return false;
}
A::is_bbm_safe(self.flags(), flags)
}
pub fn modify_flags(&mut self, set: A, clear: A) -> Result<(), ()> {
let oldval = self.flags();
let flags = (oldval | set) & !clear;
if (oldval ^ flags).contains(A::TABLE_OR_PAGE) {
return Err(());
}
let oa = self.output_address();
self.set(oa, flags)
}
}