RISC-V Support

Relevant source files

This document covers RISC-V architecture support in the page_table_multiarch library, including both Sv39 (3-level) and Sv48 (4-level) page table configurations. The implementation provides a unified interface for RISC-V memory management that abstracts the differences between these two virtual memory systems while maintaining compatibility with the generic PageTable64 interface.

For information about other processor architectures, see x86_64 Support, AArch64 Support, and LoongArch64 Support. For details about the underlying generic abstractions, see Generic Traits System.

RISC-V Virtual Memory Systems

The RISC-V architecture defines multiple virtual memory systems. This library supports the two most common configurations used in 64-bit RISC-V systems.

Sv39 vs Sv48 Comparison

FeatureSv39Sv48
Page Table Levels34
Virtual Address Bits3948
Physical Address Bits5656
Virtual Address Space512 GiB256 TiB
Page Table StructurePage Directory → Page Table → PagePage Map Level 4 → Page Directory → Page Table → Page

RISC-V Page Table Type Hierarchy

flowchart TD
subgraph subGraph4["Virtual Address"]
    SVVA["SvVirtAddr trait"]
    VIRTADDR["memory_addr::VirtAddr"]
end
subgraph subGraph3["Entry Type"]
    RV64PTE["Rv64PTE"]
end
subgraph subGraph2["Metadata Types"]
    SV39_META["Sv39MetaData<VA>"]
    SV48_META["Sv48MetaData<VA>"]
end
subgraph subGraph1["RISC-V Implementations"]
    SV39["Sv39PageTable<H>"]
    SV48["Sv48PageTable<H>"]
end
subgraph subGraph0["Generic Layer"]
    PT64["PageTable64<M,PTE,H>"]
end

PT64 --> SV39
PT64 --> SV48
SV39 --> RV64PTE
SV39 --> SV39_META
SV39_META --> SVVA
SV48 --> RV64PTE
SV48 --> SV48_META
SV48_META --> SVVA
SVVA --> VIRTADDR

Sources: page_table_multiarch/src/arch/riscv.rs(L30 - L68) 

Core Components

Page Table Type Definitions

The RISC-V implementation provides two concrete page table types that specialize the generic PageTable64 for RISC-V systems:

// Sv39: 3-level page table with 39-bit virtual addresses
pub type Sv39PageTable<H> = PageTable64<Sv39MetaData<memory_addr::VirtAddr>, Rv64PTE, H>;

// Sv48: 4-level page table with 48-bit virtual addresses  
pub type Sv48PageTable<H> = PageTable64<Sv48MetaData<memory_addr::VirtAddr>, Rv64PTE, H>;

Both types use:

  • The same page table entry type (Rv64PTE)
  • The same virtual address type (memory_addr::VirtAddr)
  • Different metadata types that define the paging characteristics

Metadata Implementations

The metadata structures implement the PagingMetaData trait to define architecture-specific constants:

Metadata TypeLevelsPA Max BitsVA Max Bits
Sv39MetaData35639
Sv48MetaData45648

RISC-V Metadata Structure

flowchart TD
subgraph subGraph3["Virtual Address Support"]
    SVVA_TRAIT["SvVirtAddr trait"]
    FLUSH_METHOD["flush_tlb(vaddr: Option<Self>)"]
end
subgraph subGraph2["Sv48 Implementation"]
    SV48_META["Sv48MetaData<VA>"]
    SV48_CONSTANTS["LEVELS = 4PA_MAX_BITS = 56VA_MAX_BITS = 48"]
end
subgraph subGraph1["Sv39 Implementation"]
    SV39_META["Sv39MetaData<VA>"]
    SV39_CONSTANTS["LEVELS = 3PA_MAX_BITS = 56VA_MAX_BITS = 39"]
end
subgraph subGraph0["PagingMetaData Trait"]
    PMD["PagingMetaData"]
    PMD_METHODS["LEVELS: usizePA_MAX_BITS: usizeVA_MAX_BITS: usizeVirtAddr: typeflush_tlb(vaddr)"]
end

PMD --> PMD_METHODS
PMD_METHODS --> SV39_META
PMD_METHODS --> SV48_META
SV39_META --> SV39_CONSTANTS
SV39_META --> SVVA_TRAIT
SV48_META --> SV48_CONSTANTS
SV48_META --> SVVA_TRAIT
SVVA_TRAIT --> FLUSH_METHOD

Sources: page_table_multiarch/src/arch/riscv.rs(L30 - L62) 

Page Table Entry Implementation

Rv64PTE Structure

The Rv64PTE struct implements the GenericPTE trait and provides RISC-V-specific page table entry functionality. It uses a 64-bit representation with specific bit field layouts defined by the RISC-V specification.

RISC-V PTE Bit Layout

flowchart TD
subgraph subGraph2["Physical Address"]
    PHYS_MASK["PHYS_ADDR_MASK= (1<<54) - (1<<10)= bits 10..54"]
end
subgraph subGraph1["Flag Bits (PTEFlags)"]
    FLAGS["V: Valid (bit 0)R: Read (bit 1)W: Write (bit 2)X: Execute (bit 3)U: User (bit 4)G: Global (bit 5)A: Accessed (bit 6)D: Dirty (bit 7)"]
end
subgraph subGraph0["Rv64PTE (64 bits)"]
    BITS["Bits 63-54: ReservedBits 53-10: Physical Page NumberBits 9-8: ReservedBits 7-0: Flags"]
end

BITS --> FLAGS
BITS --> PHYS_MASK

Sources: page_table_entry/src/arch/riscv.rs(L77 - L89) 

Flag Conversion

The implementation provides bidirectional conversion between RISC-V-specific PTEFlags and generic MappingFlags:

Generic FlagRISC-V FlagDescription
READRPage is readable
WRITEWPage is writable
EXECUTEXPage is executable
USERUPage is accessible in user mode

The conversion automatically sets additional RISC-V flags:

  • V (Valid) flag is set for any non-empty mapping
  • A (Accessed) and D (Dirty) flags are automatically set to avoid hardware page faults

Sources: page_table_entry/src/arch/riscv.rs(L33 - L75) 

GenericPTE Implementation

The Rv64PTE implements key GenericPTE methods:

// Create page entry with proper flag conversion
fn new_page(paddr: PhysAddr, flags: MappingFlags, _is_huge: bool) -> Self

// Create page table entry (only Valid flag set)
fn new_table(paddr: PhysAddr) -> Self

// Extract physical address from PTE
fn paddr(&self) -> PhysAddr

// Convert RISC-V flags to generic flags
fn flags(&self) -> MappingFlags

// Check if entry represents a huge page (has R or X flags)
fn is_huge(&self) -> bool

Sources: page_table_entry/src/arch/riscv.rs(L91 - L131) 

Virtual Address Management

SvVirtAddr Trait

The SvVirtAddr trait extends the generic MemoryAddr trait to add RISC-V-specific TLB management capabilities:

Virtual Address Trait Hierarchy

flowchart TD
subgraph subGraph2["Concrete Implementation"]
    VIRTADDR["memory_addr::VirtAddr"]
    IMPL_FLUSH["calls riscv_flush_tlb()"]
end
subgraph subGraph1["RISC-V Extension"]
    SVVA["SvVirtAddr trait"]
    FLUSH_FUNC["flush_tlb(vaddr: Option<Self>)"]
end
subgraph subGraph0["Base Traits"]
    MEMADDR["memory_addr::MemoryAddr"]
    SEND["Send"]
    SYNC["Sync"]
end

MEMADDR --> SVVA
SEND --> SVVA
SVVA --> FLUSH_FUNC
SVVA --> VIRTADDR
SYNC --> SVVA
VIRTADDR --> IMPL_FLUSH

Sources: page_table_multiarch/src/arch/riscv.rs(L17 - L28) 

TLB Management

TLB Flushing Implementation

RISC-V TLB management uses the sfence.vma instruction through the riscv crate:

fn riscv_flush_tlb(vaddr: Option<memory_addr::VirtAddr>) {
    unsafe {
        if let Some(vaddr) = vaddr {
            riscv::asm::sfence_vma(0, vaddr.as_usize())  // Flush specific address
        } else {
            riscv::asm::sfence_vma_all();                // Flush entire TLB
        }
    }
}

The TLB flush operation supports:

  • Selective flushing: Flush TLB entries for a specific virtual address
  • Global flushing: Flush all TLB entries when no address is specified

This functionality is integrated into the metadata types through the PagingMetaData::flush_tlb method, which delegates to the SvVirtAddr::flush_tlb implementation.

Sources: page_table_multiarch/src/arch/riscv.rs(L6 - L15)  page_table_multiarch/src/arch/riscv.rs(L46 - L61)