mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-22 12:57:13 -04:00
[rtl] Add ePMP support to Ibex
This implemements the RISC-V Trusted Execution Environment (TEE) working group proposal 'PMP Enhancements for memory access and execution prevention on Machine mode'. The proposal is awaiting ratification and is not expected to change beyond minor tweaks before it becomes part of the RISC-V priviledged specification. No seperate 'classic' PMP only mode is provided as different PMP behaviour only occurs when the MSECCFG CSR is written to. This CSR is introduced by the proposal and has no specified function in the current RISC-V priviledged specification.
This commit is contained in:
parent
373212ee89
commit
374e05ec65
4 changed files with 158 additions and 37 deletions
|
@ -268,11 +268,12 @@ module ibex_core #(
|
|||
logic [31:0] csr_mepc, csr_depc;
|
||||
|
||||
// PMP signals
|
||||
logic [33:0] csr_pmp_addr [PMPNumRegions];
|
||||
pmp_cfg_t csr_pmp_cfg [PMPNumRegions];
|
||||
logic pmp_req_err [PMP_NUM_CHAN];
|
||||
logic instr_req_out;
|
||||
logic data_req_out;
|
||||
logic [33:0] csr_pmp_addr [PMPNumRegions];
|
||||
pmp_cfg_t csr_pmp_cfg [PMPNumRegions];
|
||||
pmp_mseccfg_t csr_pmp_mseccfg;
|
||||
logic pmp_req_err [PMP_NUM_CHAN];
|
||||
logic instr_req_out;
|
||||
logic data_req_out;
|
||||
|
||||
logic csr_save_if;
|
||||
logic csr_save_id;
|
||||
|
@ -1017,6 +1018,7 @@ module ibex_core #(
|
|||
// PMP
|
||||
.csr_pmp_cfg_o ( csr_pmp_cfg ),
|
||||
.csr_pmp_addr_o ( csr_pmp_addr ),
|
||||
.csr_pmp_mseccfg_o ( csr_pmp_mseccfg ),
|
||||
|
||||
// debug
|
||||
.csr_depc_o ( csr_depc ),
|
||||
|
@ -1086,30 +1088,33 @@ module ibex_core #(
|
|||
assign pmp_priv_lvl[PMP_D] = priv_mode_lsu;
|
||||
|
||||
ibex_pmp #(
|
||||
.PMPGranularity ( PMPGranularity ),
|
||||
.PMPNumChan ( PMP_NUM_CHAN ),
|
||||
.PMPNumRegions ( PMPNumRegions )
|
||||
.PMPGranularity ( PMPGranularity ),
|
||||
.PMPNumChan ( PMP_NUM_CHAN ),
|
||||
.PMPNumRegions ( PMPNumRegions )
|
||||
) pmp_i (
|
||||
.clk_i ( clk ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.clk_i ( clk ),
|
||||
.rst_ni ( rst_ni ),
|
||||
// Interface to CSRs
|
||||
.csr_pmp_cfg_i ( csr_pmp_cfg ),
|
||||
.csr_pmp_addr_i ( csr_pmp_addr ),
|
||||
.priv_mode_i ( pmp_priv_lvl ),
|
||||
.csr_pmp_cfg_i ( csr_pmp_cfg ),
|
||||
.csr_pmp_addr_i ( csr_pmp_addr ),
|
||||
.csr_pmp_mseccfg_i ( csr_pmp_mseccfg ),
|
||||
.priv_mode_i ( pmp_priv_lvl ),
|
||||
// Access checking channels
|
||||
.pmp_req_addr_i ( pmp_req_addr ),
|
||||
.pmp_req_type_i ( pmp_req_type ),
|
||||
.pmp_req_err_o ( pmp_req_err )
|
||||
.pmp_req_addr_i ( pmp_req_addr ),
|
||||
.pmp_req_type_i ( pmp_req_type ),
|
||||
.pmp_req_err_o ( pmp_req_err )
|
||||
);
|
||||
end else begin : g_no_pmp
|
||||
// Unused signal tieoff
|
||||
priv_lvl_e unused_priv_lvl_if, unused_priv_lvl_ls;
|
||||
logic [33:0] unused_csr_pmp_addr [PMPNumRegions];
|
||||
pmp_cfg_t unused_csr_pmp_cfg [PMPNumRegions];
|
||||
pmp_mseccfg_t unused_csr_pmp_mseccfg;
|
||||
assign unused_priv_lvl_if = priv_mode_if;
|
||||
assign unused_priv_lvl_ls = priv_mode_lsu;
|
||||
assign unused_csr_pmp_addr = csr_pmp_addr;
|
||||
assign unused_csr_pmp_cfg = csr_pmp_cfg;
|
||||
assign unused_csr_pmp_mseccfg = csr_pmp_mseccfg;
|
||||
|
||||
// Output tieoff
|
||||
assign pmp_req_err[PMP_I] = 1'b0;
|
||||
|
|
|
@ -66,8 +66,9 @@ module ibex_cs_registers #(
|
|||
output logic [31:0] csr_mepc_o,
|
||||
|
||||
// PMP
|
||||
output ibex_pkg::pmp_cfg_t csr_pmp_cfg_o [PMPNumRegions],
|
||||
output logic [33:0] csr_pmp_addr_o [PMPNumRegions],
|
||||
output ibex_pkg::pmp_cfg_t csr_pmp_cfg_o [PMPNumRegions],
|
||||
output logic [33:0] csr_pmp_addr_o [PMPNumRegions],
|
||||
output ibex_pkg::pmp_mseccfg_t csr_pmp_mseccfg_o,
|
||||
|
||||
// debug
|
||||
input logic debug_mode_i,
|
||||
|
@ -220,6 +221,7 @@ module ibex_cs_registers #(
|
|||
logic [31:0] pmp_addr_rdata [PMP_MAX_REGIONS];
|
||||
logic [PMP_CFG_W-1:0] pmp_cfg_rdata [PMP_MAX_REGIONS];
|
||||
logic pmp_csr_err;
|
||||
pmp_mseccfg_t pmp_mseccfg;
|
||||
|
||||
// Hardware performance monitor signals
|
||||
logic [31:0] mcountinhibit;
|
||||
|
@ -340,6 +342,13 @@ module ibex_cs_registers #(
|
|||
csr_rdata_int[CSR_MFIX_BIT_HIGH:CSR_MFIX_BIT_LOW] = mip.irq_fast;
|
||||
end
|
||||
|
||||
CSR_MSECCFG: begin
|
||||
csr_rdata_int = '0;
|
||||
csr_rdata_int[CSR_MSECCFG_MML_BIT] = pmp_mseccfg.mml;
|
||||
csr_rdata_int[CSR_MSECCFG_MMWP_BIT] = pmp_mseccfg.mmwp;
|
||||
csr_rdata_int[CSR_MSECCFG_RLB_BIT] = pmp_mseccfg.rlb;
|
||||
end
|
||||
|
||||
// PMP registers
|
||||
CSR_PMPCFG0: csr_rdata_int = {pmp_cfg_rdata[3], pmp_cfg_rdata[2],
|
||||
pmp_cfg_rdata[1], pmp_cfg_rdata[0]};
|
||||
|
@ -956,13 +965,18 @@ module ibex_cs_registers #(
|
|||
// -----------------
|
||||
|
||||
if (PMPEnable) begin : g_pmp_registers
|
||||
pmp_mseccfg_t pmp_mseccfg_q, pmp_mseccfg_d;
|
||||
logic pmp_mseccfg_we;
|
||||
logic pmp_mseccfg_err;
|
||||
pmp_cfg_t pmp_cfg [PMPNumRegions];
|
||||
logic [PMPNumRegions-1:0] pmp_cfg_locked;
|
||||
pmp_cfg_t pmp_cfg_wdata [PMPNumRegions];
|
||||
logic [PMPAddrWidth-1:0] pmp_addr [PMPNumRegions];
|
||||
logic [PMPNumRegions-1:0] pmp_cfg_we;
|
||||
logic [PMPNumRegions-1:0] pmp_cfg_err;
|
||||
logic [PMPNumRegions-1:0] pmp_addr_we;
|
||||
logic [PMPNumRegions-1:0] pmp_addr_err;
|
||||
logic any_pmp_entry_locked;
|
||||
|
||||
// Expanded / qualified register read data
|
||||
for (genvar i = 0; i < PMP_MAX_REGIONS; i++) begin : g_exp_rd_data
|
||||
|
@ -1011,7 +1025,7 @@ module ibex_cs_registers #(
|
|||
// -------------------------
|
||||
// Instantiate cfg registers
|
||||
// -------------------------
|
||||
assign pmp_cfg_we[i] = csr_we_int & ~pmp_cfg[i].lock &
|
||||
assign pmp_cfg_we[i] = csr_we_int & ~pmp_cfg_locked[i] &
|
||||
(csr_addr == (CSR_OFF_PMP_CFG + (i[11:0] >> 2)));
|
||||
|
||||
// Select the correct WDATA (each CSR contains 4 CFG fields, each with 2 RES bits)
|
||||
|
@ -1028,8 +1042,10 @@ module ibex_cs_registers #(
|
|||
endcase
|
||||
end
|
||||
assign pmp_cfg_wdata[i].exec = csr_wdata_int[(i%4)*PMP_CFG_W+2];
|
||||
// W = 1, R = 0 is a reserved combination. For now, we force W to 0 if R == 0
|
||||
assign pmp_cfg_wdata[i].write = &csr_wdata_int[(i%4)*PMP_CFG_W+:2];
|
||||
// When MSECCFG.MML is unset, W = 1, R = 0 is a reserved combination, so force W to 0 if R ==
|
||||
// 0. Otherwise allow all possible values to be written.
|
||||
assign pmp_cfg_wdata[i].write = pmp_mseccfg_q.mml ? csr_wdata_int[(i%4)*PMP_CFG_W+1] :
|
||||
&csr_wdata_int[(i%4)*PMP_CFG_W+:2];
|
||||
assign pmp_cfg_wdata[i].read = csr_wdata_int[(i%4)*PMP_CFG_W];
|
||||
|
||||
ibex_csr #(
|
||||
|
@ -1045,15 +1061,19 @@ module ibex_cs_registers #(
|
|||
.rd_error_o (pmp_cfg_err[i])
|
||||
);
|
||||
|
||||
// MSECCFG.RLB allows the lock bit to be bypassed (allowing cfg writes when MSECCFG.RLB is
|
||||
// set).
|
||||
assign pmp_cfg_locked[i] = pmp_cfg[i].lock & ~pmp_mseccfg_q.rlb;
|
||||
|
||||
// --------------------------
|
||||
// Instantiate addr registers
|
||||
// --------------------------
|
||||
if (i < PMPNumRegions - 1) begin : g_lower
|
||||
assign pmp_addr_we[i] = csr_we_int & ~pmp_cfg[i].lock &
|
||||
(~pmp_cfg[i+1].lock | (pmp_cfg[i+1].mode != PMP_MODE_TOR)) &
|
||||
assign pmp_addr_we[i] = csr_we_int & ~pmp_cfg_locked[i] &
|
||||
(~pmp_cfg_locked[i+1] | (pmp_cfg[i+1].mode != PMP_MODE_TOR)) &
|
||||
(csr_addr == (CSR_OFF_PMP_ADDR + i[11:0]));
|
||||
end else begin : g_upper
|
||||
assign pmp_addr_we[i] = csr_we_int & ~pmp_cfg[i].lock &
|
||||
assign pmp_addr_we[i] = csr_we_int & ~pmp_cfg_locked[i] &
|
||||
(csr_addr == (CSR_OFF_PMP_ADDR + i[11:0]));
|
||||
end
|
||||
|
||||
|
@ -1074,7 +1094,35 @@ module ibex_cs_registers #(
|
|||
assign csr_pmp_addr_o[i] = {pmp_addr_rdata[i], 2'b00};
|
||||
end
|
||||
|
||||
assign pmp_csr_err = (|pmp_cfg_err) | (|pmp_addr_err);
|
||||
assign pmp_mseccfg_we = csr_we_int & (csr_addr == CSR_MSECCFG);
|
||||
|
||||
// MSECCFG.MML/MSECCFG.MMWP cannot be unset once set
|
||||
assign pmp_mseccfg_d.mml = pmp_mseccfg_q.mml ? 1'b1 : csr_wdata_int[CSR_MSECCFG_MML_BIT];
|
||||
assign pmp_mseccfg_d.mmwp = pmp_mseccfg_q.mmwp ? 1'b1 : csr_wdata_int[CSR_MSECCFG_MMWP_BIT];
|
||||
|
||||
// pmp_cfg_locked factors in MSECCFG.RLB so any_pmp_entry_locked will only be set if MSECCFG.RLB
|
||||
// is unset
|
||||
assign any_pmp_entry_locked = |pmp_cfg_locked;
|
||||
|
||||
// When any PMP entry is locked (A PMP entry has the L bit set and MSECCFG.RLB is unset),
|
||||
// MSECCFG.RLB cannot be set again
|
||||
assign pmp_mseccfg_d.rlb = any_pmp_entry_locked ? 1'b0 : csr_wdata_int[CSR_MSECCFG_RLB_BIT];
|
||||
|
||||
ibex_csr #(
|
||||
.Width ($bits(pmp_mseccfg_t)),
|
||||
.ShadowCopy (ShadowCSR),
|
||||
.ResetValue ('0)
|
||||
) u_pmp_mseccfg (
|
||||
.clk_i (clk_i),
|
||||
.rst_ni (rst_ni),
|
||||
.wr_data_i (pmp_mseccfg_d),
|
||||
.wr_en_i (pmp_mseccfg_we),
|
||||
.rd_data_o (pmp_mseccfg_q),
|
||||
.rd_error_o (pmp_mseccfg_err)
|
||||
);
|
||||
|
||||
assign pmp_csr_err = (|pmp_cfg_err) | (|pmp_addr_err) | pmp_mseccfg_err;
|
||||
assign pmp_mseccfg = pmp_mseccfg_q;
|
||||
|
||||
end else begin : g_no_pmp_tieoffs
|
||||
// Generate tieoffs when PMP is not configured
|
||||
|
@ -1087,8 +1135,11 @@ module ibex_cs_registers #(
|
|||
assign csr_pmp_addr_o[i] = '0;
|
||||
end
|
||||
assign pmp_csr_err = 1'b0;
|
||||
assign pmp_mseccfg = '0;
|
||||
end
|
||||
|
||||
assign csr_pmp_mseccfg_o = pmp_mseccfg;
|
||||
|
||||
//////////////////////////
|
||||
// Performance monitor //
|
||||
//////////////////////////
|
||||
|
|
|
@ -330,6 +330,13 @@ typedef struct packed {
|
|||
logic read;
|
||||
} pmp_cfg_t;
|
||||
|
||||
// Machine Security Configuration (ePMP)
|
||||
typedef struct packed {
|
||||
logic rlb; // Rule Locking Bypass
|
||||
logic mmwp; // Machine Mode Whitelist Policy
|
||||
logic mml; // Machine Mode Lockdown
|
||||
} pmp_mseccfg_t;
|
||||
|
||||
// CSRs
|
||||
typedef enum logic[11:0] {
|
||||
// Machine information
|
||||
|
@ -348,6 +355,8 @@ typedef enum logic[11:0] {
|
|||
CSR_MTVAL = 12'h343,
|
||||
CSR_MIP = 12'h344,
|
||||
|
||||
CSR_MSECCFG = 12'h390,
|
||||
|
||||
// Physical memory protection
|
||||
CSR_PMPCFG0 = 12'h3A0,
|
||||
CSR_PMPCFG1 = 12'h3A1,
|
||||
|
@ -505,4 +514,9 @@ parameter int unsigned CSR_MEIX_BIT = 11;
|
|||
parameter int unsigned CSR_MFIX_BIT_LOW = 16;
|
||||
parameter int unsigned CSR_MFIX_BIT_HIGH = 30;
|
||||
|
||||
// CSR Machine Security Configuration bits
|
||||
parameter int unsigned CSR_MSECCFG_MML_BIT = 0;
|
||||
parameter int unsigned CSR_MSECCFG_MMWP_BIT = 1;
|
||||
parameter int unsigned CSR_MSECCFG_RLB_BIT = 2;
|
||||
|
||||
endpackage
|
||||
|
|
|
@ -16,8 +16,9 @@ module ibex_pmp #(
|
|||
input logic rst_ni,
|
||||
|
||||
// Interface to CSRs
|
||||
input ibex_pkg::pmp_cfg_t csr_pmp_cfg_i [PMPNumRegions],
|
||||
input logic [33:0] csr_pmp_addr_i [PMPNumRegions],
|
||||
input ibex_pkg::pmp_cfg_t csr_pmp_cfg_i [PMPNumRegions],
|
||||
input logic [33:0] csr_pmp_addr_i [PMPNumRegions],
|
||||
input ibex_pkg::pmp_mseccfg_t csr_pmp_mseccfg_i,
|
||||
|
||||
input ibex_pkg::priv_lvl_e priv_mode_i [PMPNumChan],
|
||||
// Access checking channels
|
||||
|
@ -36,7 +37,8 @@ module ibex_pmp #(
|
|||
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_match_lt;
|
||||
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_match_eq;
|
||||
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_match_all;
|
||||
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_perm_check;
|
||||
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_basic_perm_check;
|
||||
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_mml_perm_check;
|
||||
logic [PMPNumChan-1:0] access_fault;
|
||||
|
||||
|
||||
|
@ -102,27 +104,72 @@ module ibex_pmp #(
|
|||
end
|
||||
|
||||
// Check specific required permissions
|
||||
assign region_perm_check[c][r] =
|
||||
assign region_basic_perm_check[c][r] =
|
||||
((pmp_req_type_i[c] == PMP_ACC_EXEC) & csr_pmp_cfg_i[r].exec) |
|
||||
((pmp_req_type_i[c] == PMP_ACC_WRITE) & csr_pmp_cfg_i[r].write) |
|
||||
((pmp_req_type_i[c] == PMP_ACC_READ) & csr_pmp_cfg_i[r].read);
|
||||
|
||||
|
||||
// Compute permission checks that apply when MSECCFG.MML is set.
|
||||
always_comb begin
|
||||
region_mml_perm_check[c][r] = 1'b0;
|
||||
|
||||
if (!csr_pmp_cfg_i[r].read && csr_pmp_cfg_i[r].write) begin
|
||||
// Special-case shared regions where R = 0, W = 1
|
||||
unique case ({csr_pmp_cfg_i[r].lock, csr_pmp_cfg_i[r].exec})
|
||||
// Read/write in M, read only in S/U
|
||||
2'b00: region_mml_perm_check[c][r] =
|
||||
(pmp_req_type_i[c] == PMP_ACC_READ) |
|
||||
((pmp_req_type_i[c] == PMP_ACC_WRITE) & (priv_mode_i[c] == PRIV_LVL_M));
|
||||
// Read/write in M/S/U
|
||||
2'b01: region_mml_perm_check[c][r] =
|
||||
(pmp_req_type_i[c] == PMP_ACC_READ) | (pmp_req_type_i[c] == PMP_ACC_WRITE);
|
||||
// Execute only on M/S/U
|
||||
2'b10: region_mml_perm_check[c][r] = (pmp_req_type_i[c] == PMP_ACC_EXEC);
|
||||
// Read/execute in M, execute only on S/U
|
||||
2'b11: region_mml_perm_check[c][r] =
|
||||
(pmp_req_type_i[c] == PMP_ACC_EXEC) |
|
||||
((pmp_req_type_i[c] == PMP_ACC_READ) & (priv_mode_i[c] == PRIV_LVL_M));
|
||||
default: ;
|
||||
endcase
|
||||
end else begin
|
||||
if (csr_pmp_cfg_i[r].read & csr_pmp_cfg_i[r].write & csr_pmp_cfg_i[r].exec
|
||||
& csr_pmp_cfg_i[r].lock) begin
|
||||
// Special-case shared read only region when R = 1, W = 1, X = 1, L = 1
|
||||
region_mml_perm_check[c][r] = pmp_req_type_i[c] == PMP_ACC_READ;
|
||||
end else begin
|
||||
// Otherwise use basic permission check. Permission is always denied if in S/U mode and
|
||||
// L is set or if in M mode and L is unset.
|
||||
region_mml_perm_check[c][r] =
|
||||
priv_mode_i[c] == PRIV_LVL_M ? csr_pmp_cfg_i[r].lock & region_basic_perm_check[c][r] :
|
||||
~csr_pmp_cfg_i[r].lock & region_basic_perm_check[c][r];
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Access fault determination / prioritization
|
||||
always_comb begin
|
||||
// Default is allow for M-mode, deny for other modes
|
||||
access_fault[c] = (priv_mode_i[c] != PRIV_LVL_M);
|
||||
// When MSECCFG.MMWP is set default deny always, otherwise allow for M-mode, deny for other
|
||||
// modes
|
||||
access_fault[c] = csr_pmp_mseccfg_i.mmwp | (priv_mode_i[c] != PRIV_LVL_M);
|
||||
|
||||
// PMP entries are statically prioritized, from 0 to N-1
|
||||
// The lowest-numbered PMP entry which matches an address determines accessability
|
||||
for (int r = PMPNumRegions-1; r >= 0; r--) begin
|
||||
if (region_match_all[c][r]) begin
|
||||
access_fault[c] = (priv_mode_i[c] == PRIV_LVL_M) ?
|
||||
// For M-mode, any region which matches with the L-bit clear, or with sufficient
|
||||
// access permissions will be allowed
|
||||
(csr_pmp_cfg_i[r].lock & ~region_perm_check[c][r]) :
|
||||
// For other modes, the lock bit doesn't matter
|
||||
~region_perm_check[c][r];
|
||||
if (csr_pmp_mseccfg_i.mml) begin
|
||||
// When MSECCFG.MML is set use MML specific permission check
|
||||
access_fault[c] = ~region_mml_perm_check[c][r];
|
||||
end else begin
|
||||
// Otherwise use original PMP behaviour
|
||||
access_fault[c] = (priv_mode_i[c] == PRIV_LVL_M) ?
|
||||
// For M-mode, any region which matches with the L-bit clear, or with sufficient
|
||||
// access permissions will be allowed
|
||||
(csr_pmp_cfg_i[r].lock & ~region_basic_perm_check[c][r]) :
|
||||
// For other modes, the lock bit doesn't matter
|
||||
~region_basic_perm_check[c][r];
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -130,4 +177,8 @@ module ibex_pmp #(
|
|||
assign pmp_req_err_o[c] = access_fault[c];
|
||||
end
|
||||
|
||||
// RLB, rule locking bypass, is only relevant to ibex_cs_registers which controls writes to the
|
||||
// PMP CSRs. Tie to unused signal here to prevent lint warnings.
|
||||
logic unused_csr_pmp_mseccfg_rlb;
|
||||
assign unused_csr_pmp_mseccfg_rlb = csr_pmp_mseccfg_i.rlb;
|
||||
endmodule
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue