RISC-V Privileged Architecture
May 16, 2026·18 min read·advanced
This chapter covers the system-level architecture of RISC-V: privilege levels, control and status registers, exceptions and interrupts, virtual memory, the supervisor binary interface (SBI), the…
This chapter covers the system-level architecture of RISC-V: privilege levels, control and status registers, exceptions and interrupts, virtual memory, the supervisor binary interface (SBI), the optional hypervisor extension, and the boot sequence. It corresponds to Chapter 34 (x86-64 system architecture) and Chapter 39 (AArch64 system architecture).
The RISC-V privileged architecture is specified in a separate document from the unprivileged ISA. It defines what an OS, a hypervisor, and firmware see — the resources used to manage processes, virtual memory, traps, and the system at large. The design is, like the unprivileged ISA, more minimal than ARM's or x86's: fewer modes, simpler exception handling, and a deliberately small set of mandatory features.
01. Privilege Modes
RISC-V defines three core privilege modes:
- U (User mode): least privileged. Applications run here.
- S (Supervisor mode): the OS kernel.
- M (Machine mode): most privileged. Firmware and very low-level runtime.
An optional fourth mode comes from the H extension (Hypervisor):
- HS (Hypervisor-extended Supervisor mode): a supervisor-level mode with hypervisor capability, hosting guest supervisor mode (VS) and guest user mode (VU).
The numbering is opposite to ARM's: in RISC-V, M (machine) is the highest privilege; U is the lowest. Some implementations support only M-mode (microcontrollers); some support M+U (small embedded with simple OS); some support M+S+U (Linux-class systems); some support M+HS+VS+VU+U (virtualization).
A Linux-running RISC-V system has the layered structure:
- U-mode: applications.
- S-mode: Linux kernel.
- M-mode: SBI implementation (typically OpenSBI). Provides services to S-mode and handles platform-specific details.
The OS in S-mode does not access hardware directly for many operations; it makes calls into M-mode firmware via the SBI interface. This is unusual compared to ARM (where Linux mostly runs at EL1 directly without firmware mediation for normal operations) or x86-64 (where Linux runs in ring 0 directly).
The reason: RISC-V was designed to keep S-mode platform-independent. Hardware-specific quirks — power management, secondary core startup, interrupt routing — are handled in M-mode firmware behind the SBI ABI. The Linux kernel can be platform-agnostic; the firmware adapts to specific hardware.
02. Control and Status Registers (CSRs)
RISC-V uses a unified mechanism for system-level state: Control and Status Registers (CSRs). There can be up to 4096 CSRs, accessed via dedicated instructions:
csrr a0, satp # read CSR 'satp' into a0
csrw satp, a0 # write a0 into 'satp'
csrrw a0, satp, a1 # atomic swap: a0 = old satp; satp = a1
csrrs a0, sstatus, a1 # atomic OR: a0 = old; sstatus |= a1
csrrc a0, sstatus, a1 # atomic AND-NOT: a0 = old; sstatus &= ~a1
csrrwi, csrrsi, csrrci # immediate variants (5-bit immediate)CSRs are addressed by 12-bit numbers. The address space is partitioned by privilege required to access:
- 0x000-0x0FF: U-mode accessible (where allowed).
- 0x100-0x1FF: S-mode accessible.
- 0x200-0x2FF: H-mode (hypervisor).
- 0x300-0x3FF, 0x700-0x7BF, 0xB00-0xBFF: M-mode.
- 0xC00-0xC7F: U-mode read-only (counters).
- And several other regions for specific purposes.
Naming convention: a CSR's name has a prefix indicating the lowest privilege that accesses it: mstatus (M-mode), sstatus (S-mode), ustatus (U-mode, where applicable). Many CSRs come in M and S variants that are similar but reflect the corresponding mode's view.
Major CSRs by Mode
M-mode CSRs:
- mstatus: machine-mode status (MIE, MPIE, MPP, etc. — machine interrupt enable, previous IE, previous privilege).
- mepc: machine exception program counter (where the trap occurred).
- mcause: machine cause register (the trap reason).
- mtval: machine trap value (e.g., faulting address).
- mtvec: machine trap-vector base address (where M-mode traps go).
- mie: machine interrupt-enable.
- mip: machine interrupt-pending.
- mscratch: scratch register for trap handler.
- misa: ISA description (which extensions are present).
- mvendorid, marchid, mimpid, mhartid: implementation IDs and hart (hardware thread) ID.
- medeleg, mideleg: exception/interrupt delegation to S-mode.
- pmpaddrN, pmpcfgN: physical memory protection.
S-mode CSRs (parallel to M-mode):
- sstatus, sepc, scause, stval, stvec, sie, sip, sscratch: S-mode equivalents.
- satp: supervisor address translation and protection. Holds page-table base and ASID.
- sip / sie: subset of mip/mie that S-mode can manipulate.
U-mode CSRs:
- fcsr (with FP): FP rounding mode and flags.
- cycle, time, instret: 64-bit performance counters, read-only via U-mode where enabled.
Counter Access
A nice RISC-V feature: cycle, time, and instret counters are accessible from U-mode (with M-mode permission), without a syscall:
| rdcycle a0 # pseudo-instruction; reads 'cycle' CSR | |
| rdtime a0 # reads 'time' CSR (wall-clock) | |
| rdinstret a0 # reads instructions-retired counter |
These map to csrr a0, cycle, etc. In RV32, two reads are needed for the high and low halves; in RV64, the 64-bit values are read in one instruction.
time is wall-clock time; the frequency is platform-specific (typically a few MHz to a few hundred MHz). This is the equivalent of x86's TSC or AArch64's CNTVCT.
03. Traps
RISC-V uses a unified term trap for both synchronous exceptions (faults, syscalls) and asynchronous interrupts. When a trap occurs, the processor:
- Saves the current PC into
xepc(mepc or sepc, depending on target mode). - Saves status into
xstatus(mstatus or sstatus). - Saves the cause into
xcause. - Saves trap-specific value into
xtval. - Switches to the target mode (M or S).
- Jumps to the trap vector.
The trap vector is configured by xtvec. There are two modes: direct (all traps go to one address) or vectored (interrupts dispatch by cause to base + 4*cause).
Cause Codes
The xcause register has a high bit indicating interrupt vs. exception, and lower bits encoding the specific cause:
| Cause | Description (Exception) |
|---|---|
| 0 | Instruction address misaligned |
| 1 | Instruction access fault |
| 2 | Illegal instruction |
| 3 | Breakpoint |
| 4 | Load address misaligned |
| 5 | Load access fault |
| 6 | Store/AMO address misaligned |
| 7 | Store/AMO access fault |
| 8 | Environment call from U-mode (syscall) |
| 9 | Environment call from S-mode |
| 11 | Environment call from M-mode |
| 12 | Instruction page fault |
| 13 | Load page fault |
| 15 | Store/AMO page fault |
| 20 | Instruction guest-page fault (with H ext.) |
| 21 | Load guest-page fault |
| 23 | Store/AMO guest-page fault |
| Cause | Description (Interrupt) |
|---|---|
| 1 | Supervisor software interrupt |
| 3 | Machine software interrupt |
| 5 | Supervisor timer interrupt |
| 7 | Machine timer interrupt |
| 9 | Supervisor external interrupt |
| 11 | Machine external interrupt |
Software interrupts are IPIs (inter-processor interrupts). Timer interrupts are from the platform timer. External interrupts are everything else routed through the platform interrupt controller (PLIC or APLIC, see below).
Trap Delegation
By default, all traps go to M-mode. But for an OS-class system, most traps should be handled by S-mode (Linux). M-mode delegates to S-mode via the medeleg and mideleg registers:
- medeleg (machine exception delegation): bitmask of which exception causes are handled directly by S-mode.
- mideleg (machine interrupt delegation): bitmask for interrupts.
Linux on RISC-V depends on the firmware setting up sane delegations: page faults, syscalls (ECALL from U), illegal instructions, and so on are delegated to S-mode. Only platform-management traps (machine timer, machine software interrupt, etc.) stay in M-mode.
04. System Calls
User-mode programs invoke the kernel via the ECALL (Environment Call) instruction:
| li a7, syscall_number # by Linux convention | |
| mv a0, arg0 | |
| mv a1, arg1 | |
| ... | |
| ecall # trap into S-mode | |
| # Result in a0; -1 for error with errno in a0 (negative) |
ECALL causes a trap with cause = 8 (Environment call from U-mode). The kernel handler in S-mode reads sepc to know where to return, dispatches by syscall number (in a7 by Linux convention), and on completion does sret to return.
The convention is uniform — same instruction, same trap vector, same return path for all syscalls. There is no equivalent of x86's MSR-driven SYSCALL configuration; just the trap.
A typical S-mode trap entry:
.balign 4
trap_entry:
csrrw sp, sscratch, sp # swap sp with sscratch (kernel stack now in sp)
addi sp, sp, -256 # allocate trap frame
sd a0, 0(sp)
sd a1, 8(sp)
# ... save all registers ...
csrr a0, scause
csrr a1, stval
csrr a2, sepc
mv a3, sp # pass trap frame
call handle_trap # C handler
# ... restore registers ...
csrrw sp, sscratch, sp
sretThe csrrw sp, sscratch, sp is a clever trick: at trap entry, sscratch holds the kernel's stack pointer; the user's sp is in sp; the swap puts kernel sp in sp and user sp in sscratch (saved for restoration). This works because csrrw is atomic with respect to the trap.
Linux's S-mode trap entry is highly optimized; typical syscall path is ~80 ns on a fast core, comparable to x86 and ARM.
05. Interrupts and the PLIC / APLIC
RISC-V interrupt routing has been an evolving area. Two main systems:
PLIC (Platform-Level Interrupt Controller). The original RISC-V interrupt controller. A central PLIC connects all hardware interrupt sources to all harts. Each hart has a per-hart context with claim/complete registers. Interrupts have priorities (0-7); a hart receives the highest-priority pending interrupt.
The PLIC is memory-mapped: writes to its registers configure routing; reads claim or check pending interrupts. Linux drivers initialize the PLIC at boot based on device tree information.
APLIC (Advanced PLIC) and IMSIC (Incoming MSI Controller). Newer designs targeted at MSI-based interrupt delivery. PCIe MSIs are written directly to per-hart IMSIC registers; the APLIC handles legacy wired interrupts. Together they provide a more scalable architecture for many-hart systems.
The interrupt model also supports software interrupts (IPIs) via separate registers in the CLINT (Core-Local Interruptor) on simpler platforms or via SBI calls on more advanced ones.
Linux on RISC-V handles all this through standard drivers; the device-tree blob describes the interrupt topology.
06. Virtual Memory
RISC-V virtual memory uses page tables similar in structure to ARM and x86, but with several configurable schemes:
- Sv32: 2-level table, 32-bit virtual addresses, 4 KiB pages. RV32 systems.
- Sv39: 3-level table, 39-bit virtual addresses, 4 KiB pages. Most RV64 Linux systems use Sv39.
- Sv48: 4-level table, 48-bit virtual addresses. Larger systems.
- Sv57: 5-level table, 57-bit virtual addresses. Very large systems.
The active scheme is selected by the satp CSR's MODE field. Sv39 is by far the most common in current deployments.
Sv39 Layout
39-bit virtual address split:
| | 9 bits | 9 bits | 9 bits | 12 bits | | |
| VPN[2] VPN[1] VPN[0] offset |
Three levels of page tables (root, mid, leaf). Each table has 512 64-bit entries. A leaf entry maps a 4 KiB page; an intermediate entry can be a megapage (2 MiB) or gigapage (1 GiB) leaf if appropriate. The hierarchy mirrors x86-64's PML4-PDPT-PD-PT, just with 3 levels for 39-bit addressing.
Page Table Entry Format
A 64-bit Sv39/Sv48/Sv57 PTE has:
- V (valid): 1 if the entry is valid.
- R, W, X: read, write, execute permissions.
- U: user-accessible (1 = user; 0 = supervisor-only).
- G: global (entry doesn't change with ASID).
- A: accessed (set on access).
- D: dirty (set on write).
- PPN: physical page number (44 bits).
- RSW: reserved for software (2 bits, OS scratch).
Notable: there is no "no-execute" bit; instead, the X bit is positive — set X to make the page executable. Combinations of R/W/X define page semantics:
- R=0, W=0, X=0: pointer to next-level table (intermediate entry).
- R=1, W=0, X=0: read-only data page.
- R=1, W=1, X=0: read-write data page.
- R=1, W=0, X=1: read-execute (typical code page).
- R=1, W=1, X=1: read-write-execute (rare; typically a security risk).
- R=0, W=1, X=0 or R=0, W=, X=: invalid combinations.
TLB and SFENCE.VMA
The TLB is implementation-defined; high-end RISC-V cores have multi-level TLBs similar to ARM and x86. Invalidation:
| sfence.vma # invalidate all TLB entries (full flush) | |
| sfence.vma a0 # invalidate TLB entries for VA in a0 | |
| sfence.vma zero, a1 # invalidate all entries for ASID in a1 | |
| sfence.vma a0, a1 # invalidate VA a0 in ASID a1 |
SFENCE.VMA also serves as a memory barrier ordering memory accesses with respect to the TLB invalidation. The interaction is precise: any subsequent translation will see the new mapping.
In multi-core systems, TLB shootdown can be done via SFENCE.VMA in an IPI handler — similar to x86. There is no hardware-broadcast TLB invalidation in the base RISC-V spec (unlike ARM's TLBI ISH). The Svinval extension adds finer-grained barriers for performance-critical TLB management.
ASIDs
RISC-V supports Address Space Identifiers (ASIDs) in satp. Width is implementation-defined (0-16 bits). Each process gets an ASID; the kernel switches ASID on context switch instead of flushing the TLB. Equivalent in spirit to ARM's ASID and x86's PCID.
07. Physical Memory Protection (PMP)
Even without paging, RISC-V supports memory protection in M-mode via PMP (Physical Memory Protection). PMP defines up to 16 regions with R/W/X permissions, enforced by hardware.
| csrw pmpaddr0, t0 # set region 0 base/limit | |
| csrw pmpcfg0, t1 # configure region 0 permissions |
PMP is used by M-mode firmware to grant S-mode access to specific physical regions, while protecting M-mode's own data. It's a coarser mechanism than paging but enforced by hardware on every access.
For OS-running systems, PMP is usually configured once at boot (the firmware grants the kernel access to RAM and devices, then locks down its own region). Microcontrollers without an MMU rely on PMP as their primary memory protection.
08. SBI: Supervisor Binary Interface
The Supervisor Binary Interface (SBI) is the M-mode-to-S-mode ABI: a defined set of function calls that S-mode invokes to ask M-mode firmware to perform privileged or platform-specific operations.
Why SBI? RISC-V hardware is highly varied. Different boards have different timers, different IPI mechanisms, different power management. Rather than baking knowledge of every platform into Linux, the kernel calls SBI for these operations:
- sbi_set_timer: schedule the next timer interrupt.
- sbi_send_ipi: send an IPI to other harts.
- sbi_remote_fence_i, sbi_remote_sfence_vma: cross-hart fences (TLB shootdown).
- sbi_hsm_hart_start, sbi_hsm_hart_stop, sbi_hsm_hart_status: hart state management (CPU hotplug).
- sbi_system_reset: reboot, shutdown.
- sbi_console_putchar: legacy console output.
The most common SBI implementation is OpenSBI (BSD-2-Clause licensed). Vendors customize OpenSBI for their hardware and ship it as the M-mode firmware. RustSBI is an alternative implementation.
Calling SBI from S-mode:
| li a7, SBI_EXT_TIME # extension ID | |
| li a6, SBI_TIME_SET_TIMER # function ID | |
| mv a0, t0 # next timer value | |
| ecall # trap into M-mode | |
| # Result in a0 (error code) and a1 (return value) |
ECALL from S-mode triggers cause 9 (Environment call from S-mode), trapping to M-mode where OpenSBI's handler takes over.
This indirection is the source of the comment in Chapter 42: Linux on RISC-V often calls into firmware for operations that on x86/ARM would be direct hardware access. The performance impact is small (a fast SBI call is ~100 ns), and the portability gain is large.
09. Hypervisor Extension (H)
The optional H extension adds hardware-assisted virtualization. With H enabled in misa, S-mode becomes HS-mode (Hypervisor-extended Supervisor), and additional modes appear:
- VS-mode: virtual supervisor — guest kernel.
- VU-mode: virtual user — guest application.
The hypervisor runs in HS-mode. Guests run in VS/VU mode, with their own page tables (stage 1) and a hypervisor-controlled second-stage translation (stage 2) that maps guest physical to host physical.
H-mode CSRs include:
- hgatp: stage-2 page-table base.
- hstatus: hypervisor status.
- hideleg, hedeleg: delegate VS-mode traps directly to VS-mode where possible.
- hvip: virtual interrupt pending.
- htval, htinst: trap value and instruction for guest faults.
KVM on RISC-V uses the H extension. Adoption: H is in newer cores (T-Head C920, SiFive P-series, Veyron V2, AmpereOne RISC-V).
10. Performance Counters
RISC-V provides architectural performance counters:
- cycle: 64-bit cycle counter.
- time: 64-bit wall-clock time (frequency in
mtimeregister on the platform). - instret: 64-bit instructions retired.
- hpmcounter3 - hpmcounter31: programmable counters.
The programmable counters' events are configured via mhpmevent registers (M-mode only). Standard events include cache misses, branch mispredicts, TLB misses, etc. The exact set varies by implementation.
User-mode access is controlled by the *counteren CSRs. By default, U-mode can read cycle, time, and instret; the OS can extend this.
perf on Linux/RISC-V uses these counters via the kernel's perf events framework. ARM Streamline and similar profilers are in development for RISC-V.
11. Boot Sequence
A RISC-V system boots roughly as follows.
Reset
On hardware reset, all harts start in M-mode at a platform-defined reset vector (often physical address 0x80000000 in many SoCs). All harts start simultaneously; one is designated as the boot hart, the others spin in a holding pattern.
M-Mode Firmware
The first stage is M-mode firmware, often OpenSBI:
- Initializes basic platform: clocks, DRAM, low-level peripherals.
- Sets up M-mode CSRs: mtvec (trap vector), pmp (memory protection), delegations.
- Sets up S-mode environment: enables S-mode, prepares satp for the OS.
- Loads or finds the next-stage payload (Linux kernel + initramfs, or a more sophisticated bootloader like U-Boot).
- Drops to S-mode at the kernel's entry point.
- Stays resident in M-mode, servicing SBI calls.
S-Mode Bootloader (Optional)
For more complex boot scenarios, an S-mode bootloader (U-Boot in S-mode) loads the kernel from disk, sets up command line, and jumps to the kernel.
Linux Kernel
The kernel boots in S-mode:
- Sets up MMU with kernel page tables.
- Configures stvec, scause, sstatus, etc.
- Initializes drivers via device tree.
- Brings up other harts via SBI HSM (Hart State Management) calls.
- Mounts root filesystem and starts user space.
The key difference from x86 and ARM: secondary harts are brought up by SBI calls, not by direct hardware writes. This abstracts the (varied) hardware mechanisms behind a uniform interface.
Multi-Hart Bring-Up
| for (int i = 0; i < nr_harts; i++) { | |
| if (i == boot_hart) continue; | |
| sbi_hart_start(i, &ap_entry, ap_context); | |
| } |
Each AP comes up at ap_entry, configures its own CSRs, registers with the scheduler, and joins the runnable set. Conceptually similar to ARM's PSCI mechanism.
12. Security Features (Emerging)
Several security features are being added to RISC-V, in various stages of ratification:
Pointer Masking (Smnpm, Ssnpm): top-byte-ignore for pointers, allowing pointer tagging similar to ARM's TBI.
Control-Flow Integrity (Zicfilp, Zicfiss): landing pads (forward-edge CFI) and shadow stacks (backward-edge CFI). Counterparts to ARM BTI and PAC.
Memory Tagging (Smmtt, Ssmmtt): in-progress, similar to ARM MTE.
Confidential VMs (CoVE — Confidential VM Extension): TEE/attestation framework, similar to AMD SEV / Intel TDX / ARM CCA. Not yet ratified.
The RISC-V security story is younger than ARM's or x86's, but is catching up rapidly. Mass-market adoption of these features is several years away.
13. Comparing to x86-64 and AArch64
| Aspect | x86-64 | AArch64 | RISC-V |
|---|---|---|---|
| Privilege levels | 4 rings | 4 EL | 3 modes (M/S/U) + H |
| Firmware role | UEFI, mostly stays out of OS path | ATF in EL3, occasional SMC | OpenSBI in M, called frequently via SBI |
| System reg access | RDMSR / WRMSR (privileged) | MRS / MSR | CSR instructions |
| Virtual memory | x87 page tables, 4-5 levels | 4-5 level, configurable granule | Sv32/39/48/57 |
| TLB shootdown | IPI-based | Hardware-broadcast (TLBI ISH) | IPI-based or SBI |
| Interrupt controller | Local APIC + I/O APIC | GIC | PLIC / APLIC + IMSIC |
| Hypervisor support | VMX / SVM | EL2 + stage-2 | H extension |
| Memory model | TSO | Weak | Weak (RVWMO) |
RISC-V's architecture is the most minimal: fewest privilege levels, smallest mandatory feature set, most platform-specific work pushed to firmware. ARM is more centralized with hardware-broadcast TLBI and built-in interrupt architecture. x86 carries decades of accreted features.
14. Summary
RISC-V's privileged architecture defines three mandatory privilege modes (M, S, U) plus an optional hypervisor (HS/VS/VU) layer. CSRs hold all system state, accessed via dedicated instructions; the same instruction works at all privilege levels (with different sets visible). Traps unify exceptions and interrupts; trap delegation lets S-mode handle most things directly while M-mode firmware retains overall control.
Virtual memory uses configurable schemes (Sv32, Sv39, Sv48, Sv57) with hierarchical page tables, ASIDs for cheap context switching, and SFENCE.VMA for invalidation. The PMP mechanism enforces physical-memory protection at the M-mode level. The SBI ABI lets S-mode call into M-mode firmware for platform-specific operations, providing portability across diverse hardware. The hypervisor extension supports nested translation and a virtualized supervisor mode for KVM and similar.
The minimalism of the privileged architecture pushes some complexity to firmware (OpenSBI is essential for any practical system) and to software (TLB shootdown via IPIs, more bookkeeping in some operations) but in return gives a clean, portable, well-defined platform interface. Linux on RISC-V is structurally similar to Linux on ARM, with most differences hidden behind the SBI abstraction.
The next chapter looks at RISC-V micro-architecture: how modern high-performance RISC-V cores are built, comparing them to ARM and x86-64 contemporaries.