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
| Feature | Sv39 | Sv48 |
|---|---|---|
| Page Table Levels | 3 | 4 |
| Virtual Address Bits | 39 | 48 |
| Physical Address Bits | 56 | 56 |
| Virtual Address Space | 512 GiB | 256 TiB |
| Page Table Structure | Page Directory → Page Table → Page | Page 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 Type | Levels | PA Max Bits | VA Max Bits |
|---|---|---|---|
| Sv39MetaData | 3 | 56 | 39 |
| Sv48MetaData | 4 | 56 | 48 |
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 Flag | RISC-V Flag | Description |
|---|---|---|
| READ | R | Page is readable |
| WRITE | W | Page is writable |
| EXECUTE | X | Page is executable |
| USER | U | Page is accessible in user mode |
The conversion automatically sets additional RISC-V flags:
V(Valid) flag is set for any non-empty mappingA(Accessed) andD(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)