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:

MethodPurposePhysical Address Mask
new_page()Creates page descriptor with mapping flags0x0000_ffff_ffff_f000
new_table()Creates table descriptor for next level0x0000_ffff_ffff_f000
paddr()Extracts physical address from bits 12-470x0000_ffff_ffff_f000
is_huge()Checks if NON_BLOCK bit is clearN/A
is_present()Checks VALID bitN/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

MappingFlagsDescriptorAttrDescription
READVALIDEntry is valid and readable
WRITE!AP_ROWrite access permitted (when AP_RO is clear)
EXECUTE!PXNor!UXNExecute permissions based on privilege level
USERAP_EL0Accessible from EL0 (user space)
DEVICEMemAttr::DeviceDevice memory with ATTR_INDX = 0
UNCACHEDMemAttr::NormalNonCacheableNon-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

ConstantValueDescription
LEVELS4Number of page table levels
PA_MAX_BITS48Maximum physical address bits
VA_MAX_BITS48Maximum 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

OperationInstructionScopeDescription
Specific invalidationtlbi vaae1is, {vaddr}Single VAInvalidates TLB entries for specific virtual address across all ASIDs
Global invalidationtlbi vmalle1All VAsInvalidates all stage 1 TLB entries for current VMID
Memory barrierdsb sy; isbSystem-wideEnsures 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

ConfigurationUser ExecuteKernel ExecuteImplementation
Withoutarm-el2UsesUXNwhenAP_EL0setUsesPXNfor kernel-onlySeparate handling for user/kernel
Witharm-el2UsesUXNonlyUsesUXNonlySimplified 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.

Sources: page_table_multiarch/src/arch/aarch64.rs(L37)