page_table_entry Crate

Relevant source files

This document covers the page_table_entry crate, which provides low-level page table entry definitions and abstractions for multiple hardware architectures. This crate serves as the foundation layer that defines the GenericPTE trait and architecture-specific page table entry implementations.

For information about the high-level page table management abstractions that build upon this crate, see page_table_multiarch Crate.

Purpose and Core Functionality

The page_table_entry crate abstracts page table entry manipulation across different processor architectures through a unified trait-based interface. It provides architecture-specific implementations of page table entries while maintaining a common API for higher-level page table management code.

Core Components:

  • GenericPTE trait defining unified page table entry operations
  • MappingFlags bitflags for architecture-independent memory permissions
  • Architecture-specific PTE implementations: X64PTE, A64PTE, Rv64PTE, LA64PTE

Sources: page_table_entry/README.md(L7 - L18)  page_table_entry/src/lib.rs(L38 - L68) 

GenericPTE Trait Architecture

The GenericPTE trait provides the core abstraction that enables architecture-independent page table entry manipulation. All architecture-specific page table entry types implement this trait.

GenericPTE Interface

classDiagram
class GenericPTE {
    <<trait>>
    
    +new_page(paddr: PhysAddr, flags: MappingFlags, is_huge: bool) Self
    +new_table(paddr: PhysAddr) Self
    +paddr() PhysAddr
    +flags() MappingFlags
    +set_paddr(paddr: PhysAddr)
    +set_flags(flags: MappingFlags, is_huge: bool)
    +bits() usize
    +is_unused() bool
    +is_present() bool
    +is_huge() bool
    +clear()
}

class X64PTE {
    
    +new_page()
    +new_table()
    +paddr()
    +flags()
}

class A64PTE {
    
    +new_page()
    +new_table()
    +paddr()
    +flags()
}

class Rv64PTE {
    
    +new_page()
    +new_table()
    +paddr()
    +flags()
}

class LA64PTE {
    
    +new_page()
    +new_table()
    +paddr()
    +flags()
}

GenericPTE  ..|>  X64PTE
GenericPTE  ..|>  A64PTE
GenericPTE  ..|>  Rv64PTE
LA64PTE  ..|>  A64PTE

Key Methods

MethodPurposeReturn Type
new_page()Create PTE for terminal page/block mappingSelf
new_table()Create PTE pointing to next-level page tableSelf
paddr()Extract mapped physical addressPhysAddr
flags()Get generic mapping flagsMappingFlags
set_paddr()Modify mapped physical address()
set_flags()Update mapping permissions()
bits()Get raw PTE bitsusize
is_present()Check if mapping is validbool
is_huge()Check if PTE maps large pagebool

Sources: page_table_entry/src/lib.rs(L41 - L68) 

MappingFlags System

The MappingFlags bitflags provide architecture-independent representation of memory mapping permissions and attributes.

flowchart TD
subgraph subGraph1["Architecture Conversion"]
    X86_R["x86: PRESENT"]
    X86_W["x86: WRITE"]
    X86_X["x86: !NO_EXECUTE"]
    X86_U["x86: USER"]
    ARM_R["ARM: AF + validity"]
    ARM_W["ARM: AP[1:0]"]
    ARM_X["ARM: !PXN/UXN"]
    ARM_D["ARM: AttrIndx"]
    RV_R["RISC-V: R"]
    RV_W["RISC-V: W"]
    RV_X["RISC-V: X"]
    RV_U["RISC-V: U"]
end
subgraph subGraph0["MappingFlags Bitflags"]
    READ["READ (1 << 0)"]
    WRITE["WRITE (1 << 1)"]
    EXECUTE["EXECUTE (1 << 2)"]
    USER["USER (1 << 3)"]
    DEVICE["DEVICE (1 << 4)"]
    UNCACHED["UNCACHED (1 << 5)"]
end

DEVICE --> ARM_D
EXECUTE --> ARM_X
EXECUTE --> RV_X
EXECUTE --> X86_X
READ --> ARM_R
READ --> RV_R
READ --> X86_R
USER --> RV_U
USER --> X86_U
WRITE --> ARM_W
WRITE --> RV_W
WRITE --> X86_W

Flag Definitions

FlagBit PositionPurpose
READ0Memory is readable
WRITE1Memory is writable
EXECUTE2Memory is executable
USER3User-mode accessible
DEVICE4Device memory type
UNCACHED5Uncached memory access

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

Architecture Implementation Matrix

The crate supports four major processor architectures through dedicated PTE implementations:

flowchart TD
subgraph subGraph5["Hardware Dependencies"]
    X64_DEP["x86_64 crate v0.15.2"]
    ARM_DEP["aarch64-cpu crate v10.0"]
    RV_DEP["riscv crate (implied)"]
end
subgraph subGraph4["Architecture Support"]
    subgraph AArch64["AArch64"]
        A64PTE["A64PTE"]
        A64_BITS["64-bit entries"]
        A64_LEVELS["4-level translation"]
        A64_EL2["EL2 support (feature)"]
    end
    subgraph x86_64["x86_64"]
        X64PTE["X64PTE"]
        X64_BITS["64-bit entries"]
        X64_LEVELS["4-level paging"]
    end
    subgraph LoongArch64["LoongArch64"]
        LA64PTE["LA64PTE"]
        LA_BITS["64-bit entries"]
        LA_LEVELS["4-level paging"]
        Rv64PTE["Rv64PTE"]
        RV_BITS["64-bit entries"]
        RV_SV39["Sv39 (3-level)"]
    end
    subgraph RISC-V["RISC-V"]
        LA64PTE["LA64PTE"]
        LA_BITS["64-bit entries"]
        LA_LEVELS["4-level paging"]
        Rv64PTE["Rv64PTE"]
        RV_BITS["64-bit entries"]
        RV_SV39["Sv39 (3-level)"]
        RV_SV48["Sv48 (4-level)"]
    end
end

A64PTE --> ARM_DEP
A64_EL2 --> ARM_DEP
X64PTE --> X64_DEP

Conditional Compilation

The crate uses target-specific conditional compilation to include only necessary dependencies:

Target ArchitectureDependenciesFeatures
x86_64x86_64 = "0.15.2"Standard x86_64 support
aarch64aarch64-cpu = "10.0"ARM64, optionalarm-el2
riscv32/riscv64Architecture support built-inSv39/Sv48 modes
loongarch64Built-in supportLA64 paging
docAll dependenciesDocumentation builds

Sources: page_table_entry/Cargo.toml(L22 - L27)  page_table_entry/Cargo.toml(L15 - L16) 

Usage Patterns

Basic PTE Creation and Manipulation

The typical usage pattern involves creating PTEs through the GenericPTE trait methods:

// Example from documentation
use memory_addr::PhysAddr;
use page_table_entry::{GenericPTE, MappingFlags, x86_64::X64PTE};

let paddr = PhysAddr::from(0x233000);
let pte = X64PTE::new_page(
    paddr,
    MappingFlags::READ | MappingFlags::WRITE,
    false, // not huge page
);

PTE State Queries

stateDiagram-v2
[*] --> Unused : "clear()"
Unused --> Present : "new_page() / new_table()"
Present --> Modified : "set_paddr() / set_flags()"
Modified --> Present : "architecture conversion"
Present --> Unused : "clear()"
Present --> QueryState : "is_present()"
Present --> QueryAddr : "paddr()"
Present --> QueryFlags : "flags()"
Present --> QueryHuge : "is_huge()"
Present --> QueryBits : "bits()"
QueryState --> Present
QueryAddr --> Present
QueryFlags --> Present
QueryHuge --> Present
QueryBits --> Present

Sources: page_table_entry/README.md(L28 - L46)  page_table_entry/src/lib.rs(L41 - L68) 

Dependencies and Build Configuration

Core Dependencies

DependencyVersionPurpose
bitflags2.6MappingFlagsimplementation
memory_addr0.3PhysAddrtype for physical addresses

Architecture-Specific Dependencies

Architecture-specific crates are conditionally included based on compilation target:

  • x86_64: Uses x86_64 crate for hardware-specific page table flag definitions
  • AArch64: Uses aarch64-cpu crate for ARM64 system register and memory attribute definitions
  • RISC-V: Built-in support for RISC-V page table formats
  • LoongArch: Built-in support for LoongArch64 page table formats

The doc configuration includes all architecture dependencies to enable complete documentation generation.

Sources: page_table_entry/Cargo.toml(L18 - L29)