Memory Mapping Flags

Relevant source files

This document explains the memory mapping flags system that provides a generic abstraction layer for page table entry permissions and attributes across different processor architectures. The MappingFlags type serves as the common interface that is converted to and from architecture-specific page table entry flags, enabling consistent memory management behavior regardless of the underlying hardware platform.

For information about the overall page table implementation, see PageTable64 Implementation. For details about the trait system that enables this abstraction, see Generic Traits System.

MappingFlags Structure

The MappingFlags type is defined as a bitflags structure that represents generic memory permissions and attributes that are common across all supported architectures.

flowchart TD
subgraph subGraph0["MappingFlags Bitfield Structure"]
    MF["MappingFlags (usize)"]
    READ["READ (1 << 0)Memory is readable"]
    WRITE["WRITE (1 << 1)Memory is writable"]
    EXECUTE["EXECUTE (1 << 2)Memory is executable"]
    USER["USER (1 << 3)Memory is user accessible"]
    DEVICE["DEVICE (1 << 4)Memory is device memory"]
    UNCACHED["UNCACHED (1 << 5)Memory is uncached"]
end

MF --> DEVICE
MF --> EXECUTE
MF --> READ
MF --> UNCACHED
MF --> USER
MF --> WRITE

The flags are designed to capture the essential memory access properties that operating systems need to control:

FlagPurposeTypical Usage
READMemory can be readAll mapped pages typically have this
WRITEMemory can be writtenData pages, stack, heap
EXECUTEMemory can contain executable codeCode segments, shared libraries
USERMemory accessible from user modeUser space mappings
DEVICEMemory-mapped device registersHardware device interfaces
UNCACHEDMemory should not be cachedPerformance-critical or coherency-sensitive regions

Sources: page_table_entry/src/lib.rs(L12 - L30) 

Architecture Conversion System

Each supported architecture implements bidirectional conversion between MappingFlags and its native page table entry flag representation through From trait implementations.

flowchart TD
subgraph subGraph3["RISC-V Architecture"]
    RVPTF["RV Page Flags"]
    RVPTE["Rv64PTE"]
end
subgraph subGraph2["AArch64 Architecture"]
    A64ATTR["DescriptorAttr"]
    A64PTE["A64PTE"]
    MATTR["MemAttr enum"]
end
subgraph subGraph1["x86_64 Architecture"]
    X86PTF["PageTableFlags (PTF)"]
    X86PTE["X64PTE"]
end
subgraph subGraph0["Generic Layer"]
    MF["MappingFlags"]
    GPTE["GenericPTE trait"]
end

A64ATTR --> A64PTE
A64ATTR --> MATTR
A64ATTR --> MF
A64PTE --> GPTE
MF --> A64ATTR
MF --> RVPTF
MF --> X86PTF
RVPTE --> GPTE
RVPTF --> MF
RVPTF --> RVPTE
X86PTE --> GPTE
X86PTF --> MF
X86PTF --> X86PTE

Sources: page_table_entry/src/arch/x86_64.rs(L10 - L52)  page_table_entry/src/arch/aarch64.rs(L114 - L189) 

x86_64 Flag Mapping

The x86_64 implementation uses the PageTableFlags from the x86_64 crate and maps generic flags to hardware-specific bits:

flowchart TD
subgraph subGraph0["MappingFlags to x86_64 PTF"]
    MF_READ["MappingFlags::READ"]
    PTF_PRESENT["PTF::PRESENT"]
    MF_WRITE["MappingFlags::WRITE"]
    PTF_WRITABLE["PTF::WRITABLE"]
    MF_EXEC["MappingFlags::EXECUTE"]
    PTF_NO_EXECUTE["PTF::NO_EXECUTE"]
    MF_USER["MappingFlags::USER"]
    PTF_USER_ACC["PTF::USER_ACCESSIBLE"]
    MF_DEVICE["MappingFlags::DEVICE"]
    PTF_NO_CACHE["PTF::NO_CACHE"]
    PTF_WRITE_THROUGH["PTF::WRITE_THROUGH"]
    MF_UNCACHED["MappingFlags::UNCACHED"]
end

MF_DEVICE --> PTF_NO_CACHE
MF_DEVICE --> PTF_WRITE_THROUGH
MF_EXEC --> PTF_NO_EXECUTE
MF_READ --> PTF_PRESENT
MF_UNCACHED --> PTF_NO_CACHE
MF_UNCACHED --> PTF_WRITE_THROUGH
MF_USER --> PTF_USER_ACC
MF_WRITE --> PTF_WRITABLE

Key mapping behaviors:

  • Any non-empty MappingFlags automatically gets PTF::PRESENT
  • Execute permission is inverted: absence of EXECUTE sets NO_EXECUTE
  • Both DEVICE and UNCACHED flags result in cache-disabled memory
  • Table entries always get PRESENT | WRITABLE | USER_ACCESSIBLE regardless of flags

Sources: page_table_entry/src/arch/x86_64.rs(L32 - L52)  page_table_entry/src/arch/x86_64.rs(L76 - L79) 

AArch64 Memory Attribute System

AArch64 implements a more sophisticated memory attribute system using the Memory Attribute Indirection Register (MAIR) approach:

flowchart TD
subgraph subGraph3["AArch64 Memory Attributes"]
    MF2["MappingFlags"]
    subgraph subGraph2["MAIR Configuration"]
        MAIR_0["MAIR[7:0] = Device-nGnRE"]
        MAIR_1["MAIR[15:8] = Normal WriteBack"]
        MAIR_2["MAIR[23:16] = Normal NonCacheable"]
    end
    subgraph subGraph1["Descriptor Attributes"]
        ATTR_INDX["ATTR_INDX[2:0]"]
        VALID["VALID"]
        AP_RO["AP_RO (read-only)"]
        AP_EL0["AP_EL0 (user access)"]
        UXN["UXN (user execute never)"]
        PXN["PXN (privileged execute never)"]
    end
    subgraph subGraph0["Memory Types"]
        DEVICE_MEM["MemAttr::Device"]
        NORMAL_MEM["MemAttr::Normal"]
        UNCACHED_MEM["MemAttr::NormalNonCacheable"]
    end
end

ATTR_INDX --> MAIR_0
ATTR_INDX --> MAIR_1
ATTR_INDX --> MAIR_2
DEVICE_MEM --> ATTR_INDX
MF2 --> DEVICE_MEM
MF2 --> NORMAL_MEM
MF2 --> UNCACHED_MEM
NORMAL_MEM --> ATTR_INDX
UNCACHED_MEM --> ATTR_INDX

The AArch64 system requires careful handling of execute permissions based on privilege level:

MappingFlags StateEL0 (User) AccessEL1+ (Kernel) Access
USER + EXECUTEAP_EL0, noUXNPXNset
USERonlyAP_EL0,UXNsetPXNset
EXECUTEonlyNoAP_EL0,UXNsetNoPXN
NeitherNoAP_EL0,UXNsetPXNset

Sources: page_table_entry/src/arch/aarch64.rs(L99 - L112)  page_table_entry/src/arch/aarch64.rs(L149 - L189)  page_table_entry/src/arch/aarch64.rs(L167 - L186) 

GenericPTE Integration

The GenericPTE trait methods utilize MappingFlags to create and manipulate page table entries in an architecture-neutral way:

sequenceDiagram
    participant ApplicationCode as "Application Code"
    participant GenericPTEImplementation as "GenericPTE Implementation"
    participant MappingFlags as "MappingFlags"
    participant ArchitectureFlags as "Architecture Flags"

    Note over ApplicationCode,ArchitectureFlags: Creating a Page Entry
    ApplicationCode ->> GenericPTEImplementation: new_page(paddr, flags, is_huge)
    GenericPTEImplementation ->> MappingFlags: Convert MappingFlags
    MappingFlags ->> ArchitectureFlags: From<MappingFlags>
    ArchitectureFlags ->> GenericPTEImplementation: Architecture-specific bits
    GenericPTEImplementation ->> ApplicationCode: Complete PTE
    Note over ApplicationCode,ArchitectureFlags: Reading Entry Flags
    ApplicationCode ->> GenericPTEImplementation: flags()
    GenericPTEImplementation ->> ArchitectureFlags: Extract raw bits
    ArchitectureFlags ->> MappingFlags: From<ArchFlags>
    MappingFlags ->> GenericPTEImplementation: Generic MappingFlags
    GenericPTEImplementation ->> ApplicationCode: MappingFlags

The trait provides these flag-related methods:

  • new_page(paddr, flags, is_huge) - Creates page entries with specified permissions
  • flags() - Extracts current permissions as MappingFlags
  • set_flags(flags, is_huge) - Updates entry permissions

Sources: page_table_entry/src/lib.rs(L41 - L56)  page_table_entry/src/arch/x86_64.rs(L69 - L95)  page_table_entry/src/arch/aarch64.rs(L210 - L236) 

Conditional Compilation Features

The flag conversion system supports conditional compilation for different execution environments:

FeatureEffectUsage
arm-el2Modifies AArch64 execute permission handlingHypervisor/EL2 execution
DefaultStandard EL0/EL1 permission modelNormal kernel/user operation

When arm-el2 is enabled, the execute permission logic is simplified to only use the UXN bit rather than the complex EL0/EL1+ distinction.

Sources: page_table_entry/src/arch/aarch64.rs(L123 - L139)  page_table_entry/src/arch/aarch64.rs(L181 - L186)