From 5ae7c38ff54b717d44fed98f2efccf00eca606d5 Mon Sep 17 00:00:00 2001 From: Ali Ahmet Memis Date: Mon, 24 Aug 2026 11:39:52 +0000 Subject: [PATCH 1/3] cappuccino: gate SPR writes on SR[SM] for l.mtspr spr_we and spr_bus_we_o were derived directly from spr_write_access, which a generic l.mtspr asserts regardless of privilege level. SR[SM] was only checked in the SR write path, leaving other SPRs and external SPR bus targets writable from user mode. Gate both write-enable signals with SR[SM] at their common source. Debug-unit writes remain independently authorized. Keep spr_write_access unchanged: it also drives the SPR bus strobe/ack handshake, and gating it would prevent unprivileged multi-cycle mtspr transactions from being acknowledged and stall the pipeline. Instead, an unprivileged mtspr completes as a bus no-op. Add a formal property ensuring that user-mode l.mtspr cannot assert spr_we or spr_bus_we_o. --- rtl/verilog/mor1kx_ctrl_cappuccino.v | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/rtl/verilog/mor1kx_ctrl_cappuccino.v b/rtl/verilog/mor1kx_ctrl_cappuccino.v index 8a562025..a7a96459 100644 --- a/rtl/verilog/mor1kx_ctrl_cappuccino.v +++ b/rtl/verilog/mor1kx_ctrl_cappuccino.v @@ -314,6 +314,7 @@ module mor1kx_ctrl_cappuccino /* Wires for SPR management */ wire spr_access_valid; wire spr_we; + wire spr_write_authorized; wire spr_read; wire spr_ack; wire [OPTION_OPERAND_WIDTH-1:0] spr_write_dat; @@ -1206,13 +1207,17 @@ module mor1kx_ctrl_cappuccino assign spr_read_access = (ctrl_op_mfspr_i | (du_access & !du_we_i)); assign spr_write_access = (ctrl_op_mtspr_i | (du_access & du_we_i)); + /* Check privilege before allowing an SPR write. */ + assign spr_write_authorized = (ctrl_op_mtspr_i & spr_sr[`OR1K_SPR_SR_SM]) | + (du_access & du_we_i); + assign spr_write_dat = du_access ? du_dat_i : ctrl_rfb_i; - assign spr_we = spr_write_access & spr_access_valid; + assign spr_we = spr_write_authorized & spr_access_valid; assign spr_read = spr_read_access & spr_access_valid; /* A bus out to other units that live outside of the control unit */ assign spr_bus_addr_o = spr_addr; - assign spr_bus_we_o = spr_write_access & spr_access_valid & spr_bus_access; + assign spr_bus_we_o = spr_write_authorized & spr_access_valid & spr_bus_access; assign spr_bus_stb_o = (spr_read_access | spr_write_access) & spr_access_valid & spr_bus_access; assign spr_bus_dat_o = spr_write_dat; @@ -1656,6 +1661,11 @@ endgenerate if (spr_bus_we_o) assert (spr_we); + // User-mode l.mtspr must never enable an SPR write. + always @* + if (ctrl_op_mtspr_i & !du_access & !spr_sr[`OR1K_SPR_SR_SM]) + assert (!spr_we & !spr_bus_we_o); + always @(posedge clk) begin if (f_past_valid && !$past(rst) && $past(exception_re)) begin assert (spr_sr[`OR1K_SPR_SR_SM]); From 1d24f750e9fd77f1a7c48fef232a093a14bd4edf Mon Sep 17 00:00:00 2001 From: Ali Ahmet Memis Date: Mon, 24 Aug 2026 12:47:04 +0000 Subject: [PATCH 2/3] espresso: gate SPR writes on SR[SM] for l.mtspr spr_we and spr_bus_we_o were derived from spr_write_access without checking SR[SM]. The SR write path checked the bit itself, but ESR, EPCR, DMR1, DSR and DRR did not. Add spr_write_authorized and derive both write enables from it. Keep spr_write_access unchanged so the existing SPR bus strobe/ack path is not affected. --- rtl/verilog/mor1kx_ctrl_espresso.v | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rtl/verilog/mor1kx_ctrl_espresso.v b/rtl/verilog/mor1kx_ctrl_espresso.v index b6913e08..c92178e7 100644 --- a/rtl/verilog/mor1kx_ctrl_espresso.v +++ b/rtl/verilog/mor1kx_ctrl_espresso.v @@ -308,6 +308,7 @@ module mor1kx_ctrl_espresso wire spr_group_present; wire [3:0] spr_group; wire spr_we; + wire spr_write_authorized; wire spr_read; wire [OPTION_OPERAND_WIDTH-1:0] spr_write_dat; wire [11:0] spr_access_ack; @@ -1067,13 +1068,17 @@ module mor1kx_ctrl_espresso assign spr_read_access = (op_mfspr | (du_access & !du_we_i)); assign spr_write_access = ((execute_done & op_mtspr) | (du_access & du_we_i)); + /* Check privilege before allowing an SPR write. */ + assign spr_write_authorized = (op_mtspr & spr_sr[`OR1K_SPR_SR_SM]) | + (du_access & du_we_i); + assign spr_write_dat = du_access ? du_dat_i : b; - assign spr_we = spr_write_access & spr_group_present; + assign spr_we = spr_write_authorized & spr_group_present; assign spr_read = spr_read_access & spr_group_present; /* A bus out to other units that live outside of the control unit */ assign spr_bus_addr_o = spr_addr; - assign spr_bus_we_o = spr_write_access & spr_group_present & spr_bus_access; + assign spr_bus_we_o = spr_write_authorized & spr_group_present & spr_bus_access; assign spr_bus_stb_o = (spr_read_access | spr_write_access) & spr_group_present & spr_bus_access; assign spr_bus_dat_o = spr_write_dat; From fb76b4a6cc76f1ff34739a752843cd36d164b5e3 Mon Sep 17 00:00:00 2001 From: Ali Ahmet Memis Date: Mon, 24 Aug 2026 12:47:11 +0000 Subject: [PATCH 3/3] prontoespresso: gate SPR writes on SR[SM] for l.mtspr spr_we and spr_bus_we_o were derived from spr_write_access without checking SR[SM]. The SR write path checked the bit itself, but the other SPR write paths did not. Add spr_write_authorized and derive both write enables from it. Keep spr_write_access unchanged so the existing SPR bus strobe/ack path is not affected. --- rtl/verilog/mor1kx_ctrl_prontoespresso.v | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rtl/verilog/mor1kx_ctrl_prontoespresso.v b/rtl/verilog/mor1kx_ctrl_prontoespresso.v index 76bf81dc..f167bab1 100644 --- a/rtl/verilog/mor1kx_ctrl_prontoespresso.v +++ b/rtl/verilog/mor1kx_ctrl_prontoespresso.v @@ -316,6 +316,7 @@ module mor1kx_ctrl_prontoespresso wire spr_group_present; wire [3:0] spr_group; wire spr_we; + wire spr_write_authorized; wire spr_read; wire [OPTION_OPERAND_WIDTH-1:0] spr_write_dat; wire [11:0] spr_access_ack; @@ -1056,13 +1057,17 @@ module mor1kx_ctrl_prontoespresso assign spr_read_access = (op_mfspr | (du_access & !du_we_i)); assign spr_write_access = ((execute_done & op_mtspr) | (du_access & du_we_i)); + /* Check privilege before allowing an SPR write. */ + assign spr_write_authorized = (op_mtspr & spr_sr[`OR1K_SPR_SR_SM]) | + (du_access & du_we_i); + assign spr_write_dat = du_access ? du_dat_i : b; - assign spr_we = spr_write_access & spr_group_present; + assign spr_we = spr_write_authorized & spr_group_present; assign spr_read = spr_read_access & spr_group_present; /* A bus out to other units that live outside of the control unit */ assign spr_bus_addr_o = spr_addr; - assign spr_bus_we_o = spr_write_access & spr_group_present & spr_bus_access; + assign spr_bus_we_o = spr_write_authorized & spr_group_present & spr_bus_access; assign spr_bus_stb_o = (spr_read_access | spr_write_access) & spr_group_present & spr_bus_access; assign spr_bus_dat_o = spr_write_dat;