3.7Physical Memory Protection (PMP)

Part II Linux boot: required Vol. II (Privileged) pp. 69–73 · ~4 min read

  • pmp

PMP bounds what physical addresses S/U-mode may touch — and, via locking, what even M-mode may. It’s how firmware protects itself from the OS it boots. Checks run in parallel with the PMA checker; violations are always precise access-faults.

Structure: up to 64 entries (0, 16, or 64 implemented, lowest first), each an 8-bit config + an address register. Configs pack into pmpcfg0-15 (RV32) or the even-numbered pmpcfg0,2,…,14 (RV64 — odd ones illegal, so entries 8-15 sit in pmpcfg2[31:0] under both XLENs). pmpaddrN holds physical-address bits 55:2 (RV64; 33:2 on RV32 — PMP must out-reach XLEN because Sv32 makes 34-bit PAs). All fields WARL, M-mode-only.

pmpNcfg (one byte of a pmpcfg CSR)

(next entries)318L7065A43X2W1R0
WPRI/RO WLRL WARL RW
Click a field for semantics, reset state, and the minimal-implementation note.
NAPOT size encoding (Table 24) and the grain rules
Rule
NAPOT sizesTrailing ones k in pmpaddr → 2^(k+3)-byte region: yyy0 = 8B, yy01 = 16B, y011 = 32B, 0111…= up to 2^(XLEN+3). NA4 covers the 4-byte case
TORpmpaddr[i-1] ≤ y < pmpaddr[i], ignoring pmpcfg[i-1]; i-1 ≥ i ⇒ matches nothing; the very top word of the PA space is unreachable by TOR (use NAPOT)
GrainUniform 2^(G+2) bytes: G≥1 removes NA4; NAPOT reads addr[G-2:0] as ones, OFF/TOR read addr[G-1:0] as zeros — underlying bits survive mode flips
Dotted-underlined cells have explanations — click one.

Matching and priority

  • Who gets checked: all S/U accesses (fetch + data), M-mode data accesses under MPRV with MPP∈{S,U}, and page-table walks (effective privilege S).
  • Statically prioritized: the lowest-numbered entry matching any byte decides. The entry must match all bytes of the operation — partial coverage fails regardless of permissions.
  • Verdict: matching entry with L=0 and M-mode access → success; otherwise R/W/X decides by access type. No match: M succeeds, S/U fails (if any PMP is implemented — all-OFF PMPs deny S/U everything).
  • Synchronization: PMP results may be cached with translations (Bare identity mappings included) — after any PMP write: SFENCE.VMA x0, x0.

Hardware Designer Notes

Hardware shape: 16-64 parallel range comparators (NAPOT = mask-compare, TOR = two magnitude compares), a priority mux, and the L/M-mode qualifier — one pipeline stage on the physical-address path, shared by fetch, LSU, and the page-table walker. OpenSBI’s boot sequence is the canonical client: lock firmware memory M-only, open everything else, SFENCE.VMA, MRET into the kernel. That completes machine level — next chapter: the S-mode twin world where Linux actually lives.

Minimal Linux-boot hart MUST

  • Implement the lowest-entry-wins, match-ALL-bytes comparator array; check PT walks and MPRV accesses too
  • Enforce locked entries against M-mode and keep them locked until reset
  • Apply the grain read-as-ones/zeros rules in the CSR read path while preserving underlying bits
  • Invalidate any cached PMP/translation results on SFENCE.VMA

MAY simplify / trap-and-emulate

  • Implement 0 entries (no PMP) for a trusted-firmware-only design — but Linux platforms want ≥16 for OpenSBI self-protection
  • Hardwire some entries (e.g. ROM as locked-RX) per platform

Check yourself — PMP

1.pmpaddr5 holds 0b...yy0111 (three trailing ones) with pmpcfg5.A=NAPOT. What region does entry 5 match?

2.An 8-byte S-mode load at 0x8 is checked while PMP entry 0 (highest priority) matches only bytes 0xC-0xF with full RWX. Result?

3.What two things does setting pmpcfg.L do (without Smepmp's MML)?

4.M-mode software rewrites pmpaddr/pmpcfg on a core with virtual memory. What must follow, and why?

5.Software probes PMP granularity: writes pmp0cfg=0, pmpaddr0=all-ones, reads back 0b...11110000. What's the grain?

5 questions