Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/aarch64/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,31 @@ impl crate::arch::PageTableEntry for PageTableEntryAArch64 {
attributes
}

/// Returns the hierarchical access attributes from AArch64 table descriptors.
/// These are stored in separate bit fields (`uxn_table`, `pxn_table`,
/// `ap_table`) from the leaf-level attributes and restrict child entries.
fn get_inheritable_attributes(&self) -> MemoryAttributes {
let mut attributes = MemoryAttributes::empty();

// UXNTable: if set, all child entries are execute-never at EL0
// PXNTable: if set, all child entries are execute-never at EL1/EL2
// Either restriction means the page is effectively not executable.
if self.uxn_table() || self.pxn_table() {
attributes |= MemoryAttributes::ExecuteProtect;
}

// APTable:
// 0b00 - no restriction
// 0b01 - no EL0 access (not currently modeled in MemoryAttributes)
// 0b10 - read-only at all exception levels
// 0b11 - read-only at all exception levels, no EL0 access
if self.ap_table() & 0b10 != 0 {
attributes |= MemoryAttributes::ReadOnly;
}

attributes
}

fn set_present_bit(&mut self, value: bool, va: VirtualAddress) {
// PageTableEntryAArch64 is Copy, so we can make a copy to modify and then swap it in
let mut entry = *self;
Expand Down
11 changes: 11 additions & 0 deletions src/arch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ pub(crate) trait PageTableEntry {
fn set_present_bit(&mut self, value: bool, va: VirtualAddress);
fn get_next_address(&self) -> PhysicalAddress;
fn get_attributes(&self) -> MemoryAttributes;

/// Returns the access attributes that non-leaf (table) entries impose on
/// their children. On architectures where every level uses the same access
/// bits (x86_64), the default — delegating to [`get_attributes`] — is
/// correct. On architectures with separate hierarchical fields (AArch64
/// `ap_table`/`uxn_table`/`pxn_table`), this must be overridden to read
/// those fields instead.
fn get_inheritable_attributes(&self) -> MemoryAttributes {
self.get_attributes()
}

fn dump_entry_header();
fn dump_entry(&self, va: VirtualAddress, level: PageLevel) -> Result<(), PtError>;
fn points_to_pa(&self, level: PageLevel) -> bool;
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ bitflags! {
const ReadProtect = 0x00000000_00002000u64; // Maps to Present bit on X64
const ExecuteProtect = 0x00000000_00004000u64; // Maps to NX bit on X64
const ReadOnly = 0x00000000_00020000u64; // Maps to Read/Write bit on X64

const Supervisor = 0x00000000_00040000u64; // Maps to User/Supervisor bit on X64

const CacheAttributesMask = Self::Uncached.bits() |
Self::WriteCombining.bits() |
Expand Down
11 changes: 10 additions & 1 deletion src/paging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ impl<P: PageAllocator, Arch: PageTableHal> PageTableInternal<P, Arch> {
Ok(())
}

#[allow(clippy::too_many_arguments)]
fn query_memory_region_internal(
&self,
start_va: VirtualAddress,
Expand All @@ -408,6 +409,7 @@ impl<P: PageAllocator, Arch: PageTableHal> PageTableInternal<P, Arch> {
base: PhysicalAddress,
prev_attributes: &mut RangeMappingState,
state: PageTableState,
inherited_attrs: MemoryAttributes,
) -> Result<MemoryAttributes, PtError> {
let mut va = start_va;

Expand Down Expand Up @@ -444,7 +446,12 @@ impl<P: PageAllocator, Arch: PageTableHal> PageTableInternal<P, Arch> {
}

if entry.points_to_pa(level) {
let current_attributes = entry.get_attributes();
// Compose the leaf entry's attributes with any restrictive attributes inherited from
// parent page table entries. The HW enforces "most restrictive wins" across all
// levels, which maps to OR of the restrictive flag bits in MemoryAttributes.
// Inherited attributes are accumulated via get_inheritable_attributes() on non-leaf
// entries, which handles arch-specific differences (e.g., AArch64 hierarchical fields).
let current_attributes = entry.get_attributes() | inherited_attrs;
match prev_attributes {
RangeMappingState::Uninitialized => {
*prev_attributes = RangeMappingState::Mapped(current_attributes)
Expand Down Expand Up @@ -481,6 +488,7 @@ impl<P: PageAllocator, Arch: PageTableHal> PageTableInternal<P, Arch> {
next_base,
prev_attributes,
state,
inherited_attrs | entry.get_inheritable_attributes(),
) {
Ok(_) | Err(PtError::NoMapping) => {}
Err(e) => return Err(e),
Expand Down Expand Up @@ -782,6 +790,7 @@ impl<P: PageAllocator, Arch: PageTableHal> PageTableInternal<P, Arch> {
self.base,
&mut prev_attributes,
self.get_state(),
MemoryAttributes::empty(),
)
}

Expand Down