From 200831ae624b6fe0349bcf271314218033d7d536 Mon Sep 17 00:00:00 2001 From: Dev Tyagi Date: Wed, 9 Sep 2026 13:34:04 +0530 Subject: [PATCH] pmp: do not suppress pmpcfg writes for OFF or empty-range TOR entries under MML With mseccfg.MML=1 and mseccfg.RLB=0, is_mml_m_exec_cfg checked only the L bit and the {R,W,X} permission bits, ignoring the A (address-matching) field. This caused three classes of writes to be incorrectly suppressed: 1. A=OFF, L=1, X=1 -- OFF creates no PMP rule; the write must not be blocked. 2. A=NA4, G=1, L=1, X=1 -- NA4 is converted to OFF when G>0, same as above. 3. A=TOR, L=1, X=1, pmpaddr[i-1] >= pmpaddr[i] -- empty range, no rule exists. The Smepmp specification (v20260120, section 3.1) restricts writes only when the resulting entry would produce a valid M-mode-only or Shared-Region rule. An entry with A=OFF or an empty TOR range matches no address in any mode and therefore cannot grant M-mode execute access. Fix: - is_mml_m_exec_cfg: add pmp_cfg.mode != PMP_MODE_OFF to the guard, covering cases 1 and 2. The previous code silenced the unused mode field with a parity reduction; that dummy assignment is removed. - pmp_cfg_wr_suppress: add a tor_range_nonempty signal, generated per-region inside the existing genvar loop: * For region 0 the implicit TOR base is 0, so the range is non-empty iff pmpaddr[0] != 0. * For region i > 0, the range is non-empty iff pmpaddr[i-1] < pmpaddr[i]. Suppression is gated on tor_range_nonempty, covering case 3. Fixes #2491 Signed-off-by: Dev Tyagi --- rtl/ibex_cs_registers.sv | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/rtl/ibex_cs_registers.sv b/rtl/ibex_cs_registers.sv index 8e381d44a5..3826a96dcc 100644 --- a/rtl/ibex_cs_registers.sv +++ b/rtl/ibex_cs_registers.sv @@ -161,11 +161,15 @@ module ibex_cs_registers import ibex_pkg::*, ibex_cheriot_pkg::*; #( // Is a PMP config a locked one that allows M-mode execution when MSECCFG.MML is set (either // M mode alone or shared M/U mode execution)? + // + // Returns 0 for A=OFF entries unconditionally: an OFF entry creates no PMP rule and therefore + // cannot grant M-mode execute access, regardless of the L and permission bits written. + // The write-suppression guard in Smepmp applies only to entries that would produce a valid rule. function automatic logic is_mml_m_exec_cfg(ibex_pkg::pmp_cfg_t pmp_cfg); - logic unused_cfg = ^{pmp_cfg.mode}; logic value = 1'b0; - if (pmp_cfg.lock) begin + // A=OFF entries are inert; never suppress their writes. + if (pmp_cfg.lock && (pmp_cfg.mode != ibex_pkg::PMP_MODE_OFF)) begin unique case ({pmp_cfg.read, pmp_cfg.write, pmp_cfg.exec}) 3'b001, 3'b010, 3'b011, 3'b101: value = 1'b1; default: value = 1'b0; @@ -1463,11 +1467,25 @@ module ibex_cs_registers import ibex_pkg::*, ibex_cheriot_pkg::*; #( assign pmp_cfg_locked[i] = pmp_cfg[i].lock & ~pmp_mseccfg_q.rlb; // When MSECCFG.MML is set cannot add new regions allowing M mode execution unless MSECCFG.RLB - // is set + // is set. Suppression is skipped for: + // - A=OFF entries (is_mml_m_exec_cfg returns 0 after the function fix above) + // - A=TOR entries where pmpaddr[i-1] >= pmpaddr[i]: that range is empty and creates no rule. + // For region 0 the implicit base is 0; if pmpaddr[0]==0 the range is also empty. + logic tor_range_nonempty; + if (i == 0) begin : g_tor_base0 + assign tor_range_nonempty = (pmp_cfg_wdata[i].mode == PMP_MODE_TOR) ? + (pmp_addr[i] != '0) : 1'b1; + end else begin : g_tor_base_prev + assign tor_range_nonempty = (pmp_cfg_wdata[i].mode == PMP_MODE_TOR) ? + (pmp_addr[i-1] < pmp_addr[i]) : 1'b1; + end + assign pmp_cfg_wr_suppress[i] = pmp_mseccfg_q.mml & ~pmp_mseccfg_q.rlb & + tor_range_nonempty & is_mml_m_exec_cfg(pmp_cfg_wdata[i]); + // -------------------------- // Instantiate addr registers // --------------------------