PMP pmp Physical Memory Protection: up to 64 M-mode-only WARL entries (8-bit cfg: R/W/X, A = OFF/TOR/NA4/NAPOT, L = lock+apply-to-M; pmpaddr = PA bits 55:2). Lowest-numbered matching entry wins and must cover ALL bytes; no match: M succeeds, S/U fails. Grain 2^(G+2); NAPOT size in trailing ones. Changes need SFENCE.VMA. defined in ch. II·3 — open in glossary 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)
| Rule | |
|---|---|
| NAPOT sizes | Trailing 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 |
| TOR | pmpaddr[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) |
| Grain | Uniform 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 |
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?