AArch64 Support
Relevant source files
This document covers the AArch64 (ARM64) architecture implementation in the page_table_multiarch library, including VMSAv8-64 translation table format support, memory attributes, and AArch64-specific page table operations. For information about other supported architectures, see Architecture Support.
Overview
The AArch64 implementation provides support for the VMSAv8-64 translation table format used by ARM64 processors. The implementation consists of two main components: the low-level page table entry definitions in the page_table_entry crate and the high-level page table metadata and operations in the page_table_multiarch crate.
AArch64 Implementation Architecture
flowchart TD
subgraph subGraph3["Hardware Interaction"]
TLBI["TLBI instructions"]
MAIR_EL1_reg["MAIR_EL1 register"]
VMSAv8["VMSAv8-64 format"]
end
subgraph subGraph2["Core Traits"]
PagingMetaData_trait["PagingMetaData"]
GenericPTE_trait["GenericPTE"]
PageTable64_trait["PageTable64"]
end
subgraph page_table_entry/src/arch/aarch64.rs["page_table_entry/src/arch/aarch64.rs"]
A64PTE["A64PTE"]
DescriptorAttr["DescriptorAttr"]
MemAttr["MemAttr"]
MAIR_VALUE["MAIR_VALUE"]
end
subgraph page_table_multiarch/src/arch/aarch64.rs["page_table_multiarch/src/arch/aarch64.rs"]
A64PageTable["A64PageTable<H>"]
A64PagingMetaData["A64PagingMetaData"]
FlushTLB["flush_tlb()"]
end
A64PTE --> DescriptorAttr
A64PTE --> GenericPTE_trait
A64PTE --> VMSAv8
A64PageTable --> PageTable64_trait
A64PagingMetaData --> FlushTLB
A64PagingMetaData --> PagingMetaData_trait
DescriptorAttr --> MemAttr
DescriptorAttr --> VMSAv8
FlushTLB --> TLBI
MAIR_VALUE --> MAIR_EL1_reg
MemAttr --> MAIR_VALUE
Sources: page_table_multiarch/src/arch/aarch64.rs(L1 - L38) page_table_entry/src/arch/aarch64.rs(L1 - L265)
Page Table Entry Implementation
The A64PTE struct implements the GenericPTE trait and represents VMSAv8-64 translation table descriptors. It uses the DescriptorAttr bitflags to manage AArch64-specific memory attributes and access permissions.
AArch64 Page Table Entry Structure
flowchart TD
subgraph subGraph3["Memory Attributes"]
MemAttr_enum["MemAttr enum"]
Device["Device (0)"]
Normal["Normal (1)"]
NormalNonCacheable["NormalNonCacheable (2)"]
end
subgraph subGraph2["GenericPTE Methods"]
new_page["new_page()"]
new_table["new_table()"]
paddr["paddr()"]
flags["flags()"]
is_huge["is_huge()"]
is_present["is_present()"]
end
subgraph subGraph1["DescriptorAttr Bitflags"]
VALID["VALID (bit 0)"]
NON_BLOCK["NON_BLOCK (bit 1)"]
ATTR_INDX["ATTR_INDX (bits 2-4)"]
AP_EL0["AP_EL0 (bit 6)"]
AP_RO["AP_RO (bit 7)"]
AF["AF (bit 10)"]
PXN["PXN (bit 53)"]
UXN["UXN (bit 54)"]
end
subgraph subGraph0["A64PTE Structure"]
A64PTE_struct["A64PTE(u64)"]
PHYS_ADDR_MASK["PHYS_ADDR_MASK0x0000_ffff_ffff_f000bits 12..48"]
end
A64PTE_struct --> flags
A64PTE_struct --> is_huge
A64PTE_struct --> is_present
A64PTE_struct --> new_page
A64PTE_struct --> new_table
A64PTE_struct --> paddr
AF --> new_page
AP_EL0 --> flags
AP_RO --> flags
ATTR_INDX --> MemAttr_enum
MemAttr_enum --> Device
MemAttr_enum --> Normal
MemAttr_enum --> NormalNonCacheable
NON_BLOCK --> is_huge
PHYS_ADDR_MASK --> paddr
PXN --> flags
UXN --> flags
VALID --> is_present
Sources: page_table_entry/src/arch/aarch64.rs(L191 - L253) page_table_entry/src/arch/aarch64.rs(L9 - L58)
Key Methods and Bit Manipulation
The A64PTE implementation provides several key methods for page table entry manipulation:
| Method | Purpose | Physical Address Mask |
|---|---|---|
| new_page() | Creates page descriptor with mapping flags | 0x0000_ffff_ffff_f000 |
| new_table() | Creates table descriptor for next level | 0x0000_ffff_ffff_f000 |
| paddr() | Extracts physical address from bits 12-47 | 0x0000_ffff_ffff_f000 |
| is_huge() | Checks if NON_BLOCK bit is clear | N/A |
| is_present() | Checks VALID bit | N/A |
Sources: page_table_entry/src/arch/aarch64.rs(L200 - L253)
Memory Attributes and MAIR Configuration
The AArch64 implementation uses the Memory Attribute Indirection Register (MAIR) to define memory types. The MemAttr enum defines three memory attribute types that correspond to MAIR indices.
Memory Attribute Configuration
flowchart TD
subgraph subGraph2["DescriptorAttr Conversion"]
from_mem_attr["from_mem_attr()"]
INNER_SHAREABLE["INNER + SHAREABLEfor Normal memory"]
end
subgraph subGraph1["MAIR_EL1 Register Values"]
MAIR_VALUE["MAIR_VALUE: 0x44_ff_04"]
attr0["attr0: Device-nGnREnonGathering_nonReordering_EarlyWriteAck"]
attr1["attr1: WriteBack_NonTransient_ReadWriteAllocInner + Outer"]
attr2["attr2: NonCacheableInner + Outer"]
end
subgraph subGraph0["MemAttr Types"]
Device_attr["Device (index 0)Device-nGnRE"]
Normal_attr["Normal (index 1)WriteBack cacheable"]
NormalNC_attr["NormalNonCacheable (index 2)Non-cacheable"]
end
Device_attr --> attr0
Device_attr --> from_mem_attr
NormalNC_attr --> attr2
NormalNC_attr --> from_mem_attr
Normal_attr --> attr1
Normal_attr --> from_mem_attr
attr0 --> MAIR_VALUE
attr1 --> MAIR_VALUE
attr2 --> MAIR_VALUE
from_mem_attr --> INNER_SHAREABLE
Sources: page_table_entry/src/arch/aarch64.rs(L62 - L112) page_table_entry/src/arch/aarch64.rs(L73 - L97)
Flag Conversion
The implementation provides bidirectional conversion between generic MappingFlags and AArch64-specific DescriptorAttr:
Flag Mapping Table
| MappingFlags | DescriptorAttr | Description |
|---|---|---|
| READ | VALID | Entry is valid and readable |
| WRITE | !AP_RO | Write access permitted (when AP_RO is clear) |
| EXECUTE | !PXNor!UXN | Execute permissions based on privilege level |
| USER | AP_EL0 | Accessible from EL0 (user space) |
| DEVICE | MemAttr::Device | Device memory with ATTR_INDX = 0 |
| UNCACHED | MemAttr::NormalNonCacheable | Non-cacheable normal memory |
Sources: page_table_entry/src/arch/aarch64.rs(L114 - L189)
Page Table Metadata
The A64PagingMetaData struct implements the PagingMetaData trait and defines AArch64-specific constants and operations for the VMSAv8-64 translation scheme.
Configuration Constants
| Constant | Value | Description |
|---|---|---|
| LEVELS | 4 | Number of page table levels |
| PA_MAX_BITS | 48 | Maximum physical address bits |
| VA_MAX_BITS | 48 | Maximum virtual address bits |
Sources: page_table_multiarch/src/arch/aarch64.rs(L11 - L15)
Virtual Address Validation
The vaddr_is_valid() method implements AArch64's canonical address validation:
fn vaddr_is_valid(vaddr: usize) -> bool {
let top_bits = vaddr >> Self::VA_MAX_BITS;
top_bits == 0 || top_bits == 0xffff
}
This ensures that virtual addresses are either in the lower canonical range (top 16 bits all zeros) or upper canonical range (top 16 bits all ones).
Sources: page_table_multiarch/src/arch/aarch64.rs(L17 - L20)
TLB Management
The AArch64 implementation provides TLB (Translation Lookaside Buffer) invalidation through assembly instructions in the flush_tlb() method.
TLB Invalidation Operations
flowchart TD
subgraph subGraph0["flush_tlb() Implementation"]
flush_tlb_method["flush_tlb(vaddr: Option<VirtAddr>)"]
vaddr_check["vaddr.is_some()?"]
specific_tlbi["TLBI VAAE1ISTLB Invalidate by VAAll ASID, EL1, Inner Shareable"]
global_tlbi["TLBI VMALLE1TLB Invalidate by VMIDAll at stage 1, EL1"]
dsb_sy["DSB SYData Synchronization Barrier"]
isb["ISBInstruction Synchronization Barrier"]
end
dsb_sy --> isb
flush_tlb_method --> vaddr_check
global_tlbi --> dsb_sy
specific_tlbi --> dsb_sy
vaddr_check --> global_tlbi
vaddr_check --> specific_tlbi
Sources: page_table_multiarch/src/arch/aarch64.rs(L22 - L34)
TLB Instruction Details
| Operation | Instruction | Scope | Description |
|---|---|---|---|
| Specific invalidation | tlbi vaae1is, {vaddr} | Single VA | Invalidates TLB entries for specific virtual address across all ASIDs |
| Global invalidation | tlbi vmalle1 | All VAs | Invalidates all stage 1 TLB entries for current VMID |
| Memory barrier | dsb sy; isb | System-wide | Ensures TLB operations complete before subsequent instructions |
Sources: page_table_multiarch/src/arch/aarch64.rs(L24 - L32)
EL2 Support
The implementation includes conditional compilation support for ARM Exception Level 2 (EL2) through the arm-el2 feature flag. This affects execute permission handling in the flag conversion logic.
Permission Handling Differences
| Configuration | User Execute | Kernel Execute | Implementation |
|---|---|---|---|
| Withoutarm-el2 | UsesUXNwhenAP_EL0set | UsesPXNfor kernel-only | Separate handling for user/kernel |
| Witharm-el2 | UsesUXNonly | UsesUXNonly | Simplified execute control |
Sources: page_table_entry/src/arch/aarch64.rs(L123 - L186)
Type Alias and Integration
The A64PageTable<H> type alias provides the complete AArch64 page table implementation by combining the metadata, page table entry type, and handler:
pub type A64PageTable<H> = PageTable64<A64PagingMetaData, A64PTE, H>;
This creates a fully configured page table type that applications can use with their chosen PagingHandler implementation.