forked from lowRISC/ibex
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathibex_pmp.sv
248 lines (222 loc) · 12.1 KB
/
ibex_pmp.sv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
`include "dv_fcov_macros.svh"
module ibex_pmp #(
// Granularity of NAPOT access,
// 0 = No restriction, 1 = 8 byte, 2 = 16 byte, 3 = 32 byte, etc.
parameter int unsigned PMPGranularity = 0,
// Number of access channels (e.g. i-side + d-side)
parameter int unsigned PMPNumChan = 2,
// Number of implemented regions
parameter int unsigned PMPNumRegions = 4
) (
// 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_mseccfg_t csr_pmp_mseccfg_i,
input ibex_pkg::priv_lvl_e priv_mode_i [PMPNumChan],
// Access checking channels
input logic [33:0] pmp_req_addr_i [PMPNumChan],
input ibex_pkg::pmp_req_e pmp_req_type_i [PMPNumChan],
output logic pmp_req_err_o [PMPNumChan]
);
import ibex_pkg::*;
// Access Checking Signals
logic [33:0] region_start_addr [PMPNumRegions];
logic [33:PMPGranularity+2] region_addr_mask [PMPNumRegions];
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_match_gt;
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_basic_perm_check;
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_perm_check;
///////////////////////
// Functions for PMP //
///////////////////////
// Flow of the PMP checking operation follows as below
//
// basic_perm_check ---> perm_check_wrapper ---> mml_perm_check/orig_perm_check ---/
// |
// region_match_all --------------------------------> access_fault_check <----------
// |
// \--> pmp_req_err_o
// Compute permissions checks that apply when MSECCFG.MML is set. Added for Smepmp support.
function automatic logic mml_perm_check(ibex_pkg::pmp_cfg_t region_csr_pmp_cfg,
ibex_pkg::pmp_req_e pmp_req_type,
ibex_pkg::priv_lvl_e priv_mode,
logic permission_check);
logic result = 1'b0;
logic unused_cfg = |region_csr_pmp_cfg.mode;
if (!region_csr_pmp_cfg.read && region_csr_pmp_cfg.write) begin
// Special-case shared regions where R = 0, W = 1
unique case ({region_csr_pmp_cfg.lock, region_csr_pmp_cfg.exec})
// Read/write in M, read only in S/U
2'b00: result =
(pmp_req_type == PMP_ACC_READ) |
((pmp_req_type == PMP_ACC_WRITE) & (priv_mode == PRIV_LVL_M));
// Read/write in M/S/U
2'b01: result =
(pmp_req_type == PMP_ACC_READ) | (pmp_req_type == PMP_ACC_WRITE);
// Execute only on M/S/U
2'b10: result = (pmp_req_type == PMP_ACC_EXEC);
// Read/execute in M, execute only on S/U
2'b11: result =
(pmp_req_type == PMP_ACC_EXEC) |
((pmp_req_type == PMP_ACC_READ) & (priv_mode == PRIV_LVL_M));
default: ;
endcase
end else begin
if (region_csr_pmp_cfg.read & region_csr_pmp_cfg.write &
region_csr_pmp_cfg.exec & region_csr_pmp_cfg.lock) begin
// Special-case shared read only region when R = 1, W = 1, X = 1, L = 1
result = pmp_req_type == 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.
result = permission_check &
(priv_mode == PRIV_LVL_M ? region_csr_pmp_cfg.lock : ~region_csr_pmp_cfg.lock);
end
end
return result;
endfunction
// Compute permissions checks that apply when MSECCFG.MML is unset. This is the original PMP
// behaviour before Smepmp was added.
function automatic logic orig_perm_check(logic pmp_cfg_lock,
ibex_pkg::priv_lvl_e priv_mode,
logic permission_check);
return (priv_mode == PRIV_LVL_M) ?
// For M-mode, any region which matches with the L-bit clear, or with sufficient
// access permissions will be allowed
(~pmp_cfg_lock | permission_check) :
// For other modes, the lock bit doesn't matter
permission_check;
endfunction
// A wrapper function in which it is decided which form of permission check function gets called
function automatic logic perm_check_wrapper(logic csr_pmp_mseccfg_mml,
ibex_pkg::pmp_cfg_t region_csr_pmp_cfg,
ibex_pkg::pmp_req_e pmp_req_type,
ibex_pkg::priv_lvl_e priv_mode,
logic permission_check);
return csr_pmp_mseccfg_mml ? mml_perm_check(region_csr_pmp_cfg,
pmp_req_type,
priv_mode,
permission_check) :
orig_perm_check(region_csr_pmp_cfg.lock,
priv_mode,
permission_check);
endfunction
// Access fault determination / prioritization
function automatic logic access_fault_check (logic csr_pmp_mseccfg_mmwp,
logic csr_pmp_mseccfg_mml,
ibex_pkg::pmp_req_e pmp_req_type,
logic [PMPNumRegions-1:0] match_all,
ibex_pkg::priv_lvl_e priv_mode,
logic [PMPNumRegions-1:0] final_perm_check);
// When MSECCFG.MMWP is set default deny always, otherwise allow for M-mode, deny for other
// modes. Also deny unmatched for M-mode whe MSECCFG.MML is set and request type is EXEC.
logic access_fail = csr_pmp_mseccfg_mmwp | (priv_mode != PRIV_LVL_M) |
(csr_pmp_mseccfg_mml && (pmp_req_type == PMP_ACC_EXEC));
logic matched = 1'b0;
// PMP entries are statically prioritized, from 0 to N-1
// The lowest-numbered PMP entry which matches an address determines accessibility
for (int r = 0; r < PMPNumRegions; r++) begin
if (!matched && match_all[r]) begin
access_fail = ~final_perm_check[r];
matched = 1'b1;
end
end
return access_fail;
endfunction
// ---------------
// Access checking
// ---------------
for (genvar r = 0; r < PMPNumRegions; r++) begin : g_addr_exp
// Start address for TOR matching
if (r == 0) begin : g_entry0
assign region_start_addr[r] = (csr_pmp_cfg_i[r].mode == PMP_MODE_TOR) ? 34'h000000000 :
csr_pmp_addr_i[r];
end else begin : g_oth
assign region_start_addr[r] = (csr_pmp_cfg_i[r].mode == PMP_MODE_TOR) ? csr_pmp_addr_i[r-1] :
csr_pmp_addr_i[r];
end
// Address mask for NA matching
for (genvar b = PMPGranularity + 2; b < 34; b++) begin : g_bitmask
if (b == 2) begin : g_bit0
// Always mask bit 2 for NAPOT
assign region_addr_mask[r][b] = (csr_pmp_cfg_i[r].mode != PMP_MODE_NAPOT);
end else begin : g_others
// We will mask this bit if it is within the programmed granule
// i.e. addr = yyyy 0111
// ^
// | This bit pos is the top of the mask, all lower bits set
// thus mask = 1111 0000
if (PMPGranularity == 0) begin : g_region_addr_mask_zero_granularity
assign region_addr_mask[r][b] = (csr_pmp_cfg_i[r].mode != PMP_MODE_NAPOT) |
~&csr_pmp_addr_i[r][b-1:2];
end else begin : g_region_addr_mask_other_granularity
assign region_addr_mask[r][b] = (csr_pmp_cfg_i[r].mode != PMP_MODE_NAPOT) |
~&csr_pmp_addr_i[r][b-1:PMPGranularity+1];
end
end
end
end
for (genvar c = 0; c < PMPNumChan; c++) begin : g_access_check
for (genvar r = 0; r < PMPNumRegions; r++) begin : g_regions
// Comparators are sized according to granularity
assign region_match_eq[c][r] = (pmp_req_addr_i[c][33:PMPGranularity+2] &
region_addr_mask[r]) ==
(region_start_addr[r][33:PMPGranularity+2] &
region_addr_mask[r]);
assign region_match_gt[c][r] = pmp_req_addr_i[c][33:PMPGranularity+2] >
region_start_addr[r][33:PMPGranularity+2];
assign region_match_lt[c][r] = pmp_req_addr_i[c][33:PMPGranularity+2] <
csr_pmp_addr_i[r][33:PMPGranularity+2];
always_comb begin
region_match_all[c][r] = 1'b0;
unique case (csr_pmp_cfg_i[r].mode)
PMP_MODE_OFF: region_match_all[c][r] = 1'b0;
PMP_MODE_NA4: region_match_all[c][r] = region_match_eq[c][r];
PMP_MODE_NAPOT: region_match_all[c][r] = region_match_eq[c][r];
PMP_MODE_TOR: begin
region_match_all[c][r] = (region_match_eq[c][r] | region_match_gt[c][r]) &
region_match_lt[c][r];
end
default: region_match_all[c][r] = 1'b0;
endcase
end
// Basic permission check compares cfg register only.
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);
// Check specific required permissions since the behaviour is different
// between Smepmp implementation and original PMP.
assign region_perm_check[c][r] = perm_check_wrapper(csr_pmp_mseccfg_i.mml,
csr_pmp_cfg_i[r],
pmp_req_type_i[c],
priv_mode_i[c],
region_basic_perm_check[c][r]);
// Address bits below PMP granularity (which starts at 4 byte) are deliberately unused.
logic unused_sigs;
assign unused_sigs = ^{region_start_addr[r][PMPGranularity+2-1:0],
pmp_req_addr_i[c][PMPGranularity+2-1:0]};
end
// Once the permission checks of the regions are done, decide if the access is
// denied by figuring out the matching region and its permission check.
assign pmp_req_err_o[c] = access_fault_check(csr_pmp_mseccfg_i.mmwp,
csr_pmp_mseccfg_i.mml,
pmp_req_type_i[c],
region_match_all[c],
priv_mode_i[c],
region_perm_check[c]);
// Access fails check against one region but access allowed due to another higher-priority
// region.
`DV_FCOV_SIGNAL(logic, pmp_region_override,
~pmp_req_err_o[c] & |(region_match_all[c] & ~region_perm_check[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