Line data Source code
1 : // SPDX-License-Identifier: Apache-2.0
2 : // Copyright lowRISC contributors.
3 : // Copyright 2023 Antmicro, Ltd. <www.antmicro.com>
4 : //
5 : // Licensed under the Apache License, Version 2.0 (the "License");
6 : // you may not use this file except in compliance with the License.
7 : // You may obtain a copy of the License at
8 : //
9 : // http://www.apache.org/licenses/LICENSE-2.0
10 : //
11 : // Unless required by applicable law or agreed to in writing, software
12 : // distributed under the License is distributed on an "AS IS" BASIS,
13 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : // See the License for the specific language governing permissions and
15 : // limitations under the License.
16 :
17 : module el2_pmp
18 : import el2_pkg::*;
19 : #(
20 : parameter PMP_CHANNELS = 3,
21 : // Granularity of NAPOT access,
22 : // 0 = No restriction, 1 = 8 byte, 2 = 16 byte, 3 = 32 byte, etc.
23 : parameter PMP_GRANULARITY = 0, // TODO: Move to veer.config
24 : `include "el2_param.vh"
25 : ) (
26 69837820 : input logic clk, // Top level clock
27 338 : input logic rst_l, // Reset
28 : /* verilator coverage_off */
29 : input logic scan_mode, // Scan mode
30 : /* verilator coverage_on */
31 :
32 : `ifdef RV_SMEPMP
33 2 : input el2_mseccfg_pkt_t mseccfg, // mseccfg CSR content, RLB, MMWP and MML bits
34 : `endif
35 :
36 : `ifdef RV_USER_MODE
37 864 : input logic priv_mode_ns, // operating privilege mode (next clock cycle)
38 958 : input logic priv_mode_eff, // operating effective privilege mode
39 : `endif
40 :
41 0 : input el2_pmp_cfg_pkt_t pmp_pmpcfg [pt.PMP_ENTRIES],
42 : input logic [31:0] pmp_pmpaddr[pt.PMP_ENTRIES],
43 :
44 593877 : input logic [31:0] pmp_chan_addr[PMP_CHANNELS],
45 769 : input el2_pmp_type_pkt_t pmp_chan_type[PMP_CHANNELS],
46 137254 : output logic pmp_chan_err [PMP_CHANNELS]
47 : );
48 :
49 : logic [ 33:0] csr_pmp_addr_i [pt.PMP_ENTRIES];
50 789180 : logic [ 33:0] pmp_req_addr_i [ PMP_CHANNELS];
51 :
52 : logic [ 33:0] region_start_addr [pt.PMP_ENTRIES];
53 : logic [33:PMP_GRANULARITY+2] region_addr_mask [pt.PMP_ENTRIES];
54 583 : logic [ PMP_CHANNELS-1:0][pt.PMP_ENTRIES-1:0] region_match_gt;
55 1 : logic [ PMP_CHANNELS-1:0][pt.PMP_ENTRIES-1:0] region_match_lt;
56 921 : logic [ PMP_CHANNELS-1:0][pt.PMP_ENTRIES-1:0] region_match_eq;
57 2 : logic [ PMP_CHANNELS-1:0][pt.PMP_ENTRIES-1:0] region_match_all;
58 0 : logic [ PMP_CHANNELS-1:0][pt.PMP_ENTRIES-1:0] region_basic_perm_check;
59 350 : logic [ PMP_CHANNELS-1:0][pt.PMP_ENTRIES-1:0] region_perm_check;
60 :
61 : `ifdef RV_USER_MODE
62 75 : logic any_region_enabled;
63 : `endif
64 :
65 : ///////////////////////
66 : // Functions for PMP //
67 : ///////////////////////
68 :
69 : // Flow of the PMP checking operation follows as below
70 : //
71 : // basic_perm_check ---> perm_check_wrapper ---> orig_perm_check ---/
72 : // |
73 : // region_match_all -----------------> access_fault_check <----------
74 : // |
75 : // \--> pmp_chan_err
76 :
77 : // A wrapper function in which it is decided which form of permission check function gets called
78 16416 : function automatic logic perm_check_wrapper(el2_mseccfg_pkt_t csr_pmp_mseccfg,
79 : el2_pmp_cfg_pkt_t csr_pmp_cfg,
80 : el2_pmp_type_pkt_t req_type,
81 : logic priv_mode,
82 : logic permission_check);
83 :
84 16416 : return csr_pmp_mseccfg.MML ? mml_perm_check(csr_pmp_cfg,
85 16416 : req_type,
86 16416 : priv_mode,
87 16416 : permission_check) :
88 16416 : orig_perm_check(csr_pmp_cfg.lock,
89 16416 : priv_mode,
90 16416 : permission_check);
91 : endfunction
92 :
93 : // Compute permissions checks that apply when MSECCFG.MML is set. Added for Smepmp support.
94 1691520 : function automatic logic mml_perm_check(el2_pmp_cfg_pkt_t csr_pmp_cfg,
95 : el2_pmp_type_pkt_t pmp_req_type,
96 : logic priv_mode,
97 : logic permission_check);
98 : logic result;
99 1691520 : logic unused_cfg = |csr_pmp_cfg.mode;
100 :
101 211440 : if (!csr_pmp_cfg.read && csr_pmp_cfg.write) begin
102 : // Special-case shared regions where R = 0, W = 1
103 211440 : unique case ({csr_pmp_cfg.lock, csr_pmp_cfg.execute})
104 : // Read/write in M, read only in U
105 105720 : 2'b00: result =
106 105720 : (pmp_req_type == READ) |
107 105720 : ((pmp_req_type == WRITE) & ~priv_mode);
108 : // Read/write in M/U
109 0 : 2'b01: result =
110 0 : (pmp_req_type == READ) |
111 0 : (pmp_req_type == WRITE);
112 : // Execute only on M/U
113 0 : 2'b10: result = (pmp_req_type == EXEC);
114 : // Read/execute in M, execute only on U
115 105720 : 2'b11: result =
116 105720 : (pmp_req_type == EXEC) |
117 105720 : ((pmp_req_type == READ) & ~priv_mode);
118 : /* verilator coverage_off */
119 : default: ;
120 : /* verilator coverage_on */
121 : endcase
122 1480080 : end else begin
123 1480080 : if (csr_pmp_cfg.read & csr_pmp_cfg.write & csr_pmp_cfg.execute & csr_pmp_cfg.lock) begin
124 : // Special-case shared read only region when R = 1, W = 1, X = 1, L = 1
125 0 : result = (pmp_req_type == READ);
126 1480080 : end else begin
127 : // Otherwise use basic permission check. Permission is always denied if in U mode and
128 : // L is set or if in M mode and L is unset.
129 1480080 : result = permission_check & (priv_mode ? ~csr_pmp_cfg.lock : csr_pmp_cfg.lock);
130 : end
131 : end
132 1691520 : return result;
133 : endfunction
134 :
135 : // Compute permissions checks that apply when MSECCFG.MML is unset. This is the original PMP
136 : // behaviour before Smepmp was added.
137 2852679504 : function automatic logic orig_perm_check(logic pmp_cfg_lock,
138 : logic priv_mode,
139 : logic permission_check);
140 : // For M-mode, any region which matches with the L-bit clear, or with sufficient
141 : // access permissions will be allowed.
142 : // For other modes, the lock bit doesn't matter
143 2852679504 : return priv_mode ? (permission_check) : (~pmp_cfg_lock | permission_check);
144 : endfunction
145 :
146 : // Access fault determination / prioritization
147 1026 : function automatic logic access_fault_check(el2_mseccfg_pkt_t csr_pmp_mseccfg,
148 : el2_pmp_type_pkt_t req_type,
149 : logic [pt.PMP_ENTRIES-1:0] match_all,
150 : logic any_region_enabled,
151 : logic priv_mode,
152 : logic [pt.PMP_ENTRIES-1:0] final_perm_check);
153 :
154 : `ifdef RV_USER_MODE
155 : `ifdef RV_SMEPMP
156 : // When MSECCFG.MMWP is set default deny always, otherwise allow for M-mode, deny for other
157 : // modes. Also deny unmatched for M-mode when MSECCFG.MML is set and request type is EXEC.
158 264 : logic access_fail = csr_pmp_mseccfg.MMWP | priv_mode |
159 264 : (csr_pmp_mseccfg.MML && (req_type == EXEC));
160 : `else
161 : // When in user mode and at least one PMP region is enabled deny access by default.
162 : logic access_fail = any_region_enabled & priv_mode;
163 : `endif
164 : `else
165 762 : logic access_fail = 1'b0;
166 : `endif
167 :
168 1026 : logic matched = 1'b0;
169 :
170 : // PMP entries are statically prioritized, from 0 to N-1
171 : // The lowest-numbered PMP entry which matches an address determines accessibility
172 1026 : for (int r = 0; r < pt.PMP_ENTRIES; r++) begin
173 1370957959 : if (!matched && match_all[r]) begin
174 86959783 : access_fail = ~final_perm_check[r];
175 86959783 : matched = 1'b1;
176 : end
177 : end
178 1026 : return access_fail;
179 : endfunction
180 :
181 : // ---------------
182 : // Access checking
183 : // ---------------
184 :
185 : `ifdef RV_USER_MODE
186 0 : logic [pt.PMP_ENTRIES-1:0] region_enabled;
187 : for (genvar r = 0; r < pt.PMP_ENTRIES; r++) begin : g_reg_ena
188 : assign region_enabled[r] = pmp_pmpcfg[r].mode != OFF;
189 : end
190 : assign any_region_enabled = |region_enabled;
191 : `endif
192 :
193 : for (genvar r = 0; r < pt.PMP_ENTRIES; r++) begin : g_addr_exp
194 : assign csr_pmp_addr_i[r] = {
195 : pmp_pmpaddr[r], 2'b00
196 : }; // addr conv.: word @ 32-bit -> byte @ 34-bit
197 : // Start address for TOR matching
198 : if (r == 0) begin : g_entry0
199 : assign region_start_addr[r] = (pmp_pmpcfg[r].mode == TOR) ? 34'h000000000 : csr_pmp_addr_i[r];
200 : end else begin : g_oth
201 : assign region_start_addr[r] = (pmp_pmpcfg[r].mode == TOR) ? csr_pmp_addr_i[r-1] :
202 : csr_pmp_addr_i[r];
203 : end
204 : // Address mask for NA matching
205 : for (genvar b = PMP_GRANULARITY + 2; b < 34; b++) begin : g_bitmask
206 : if (b == 2) begin : g_bit0
207 : // Always mask bit 2 for NAPOT
208 : assign region_addr_mask[r][b] = (pmp_pmpcfg[r].mode != NAPOT);
209 : end else begin : g_others
210 : // We will mask this bit if it is within the programmed granule
211 : // i.e. addr = yyyy 0111
212 : // ^
213 : // | This bit pos is the top of the mask, all lower bits set
214 : // thus mask = 1111 0000
215 : if (PMP_GRANULARITY == 0) begin : g_region_addr_mask_zero_granularity
216 : assign region_addr_mask[r][b] = (pmp_pmpcfg[r].mode != NAPOT) |
217 : ~&csr_pmp_addr_i[r][b-1:2];
218 : end else begin : g_region_addr_mask_other_granularity
219 : assign region_addr_mask[r][b] = (pmp_pmpcfg[r].mode != NAPOT) |
220 : ~&csr_pmp_addr_i[r][b-1:PMP_GRANULARITY+1];
221 : end
222 : end
223 : end
224 : end
225 :
226 : `ifdef RV_USER_MODE
227 872 : logic [PMP_CHANNELS-1:0] pmp_priv_mode_eff;
228 : for (genvar c = 0; c < PMP_CHANNELS; c++) begin : g_priv_mode_eff
229 : assign pmp_priv_mode_eff[c] = (
230 : ((pmp_chan_type[c] == EXEC) & priv_mode_ns) |
231 : ((pmp_chan_type[c] != EXEC) & priv_mode_eff)); // RW affected by mstatus.MPRV
232 : end
233 : `endif
234 :
235 0 : for (genvar c = 0; c < PMP_CHANNELS; c++) begin : g_access_check
236 : assign pmp_req_addr_i[c] = {2'b00, pmp_chan_addr[c]}; // addr. widening: 32-bit -> 34-bit
237 0 : for (genvar r = 0; r < pt.PMP_ENTRIES; r++) begin : g_regions
238 : // Comparators are sized according to granularity
239 : assign region_match_eq[c][r] = (pmp_req_addr_i[c][33:PMP_GRANULARITY+2] &
240 : region_addr_mask[r]) ==
241 : (region_start_addr[r][33:PMP_GRANULARITY+2] &
242 : region_addr_mask[r]);
243 : assign region_match_gt[c][r] = pmp_req_addr_i[c][33:PMP_GRANULARITY+2] >
244 : region_start_addr[r][33:PMP_GRANULARITY+2];
245 : assign region_match_lt[c][r] = pmp_req_addr_i[c][33:PMP_GRANULARITY+2] <
246 : csr_pmp_addr_i[r][33:PMP_GRANULARITY+2];
247 :
248 16416 : always_comb begin
249 16416 : region_match_all[c][r] = 1'b0;
250 16416 : unique case (pmp_pmpcfg[r].mode)
251 1269814266 : OFF: region_match_all[c][r] = 1'b0;
252 77241 : NA4: region_match_all[c][r] = region_match_eq[c][r];
253 37419627 : NAPOT: region_match_all[c][r] = region_match_eq[c][r];
254 50076114 : TOR: begin
255 50076114 : region_match_all[c][r] = (region_match_eq[c][r] | region_match_gt[c][r]) &
256 50076114 : region_match_lt[c][r];
257 : end
258 0 : default: region_match_all[c][r] = 1'b0;
259 : endcase
260 : end
261 :
262 : // Basic permission check compares cfg register only.
263 : assign region_basic_perm_check[c][r] =
264 : ((pmp_chan_type[c] == EXEC) & pmp_pmpcfg[r].execute) |
265 : ((pmp_chan_type[c] == WRITE) & pmp_pmpcfg[r].write) |
266 : ((pmp_chan_type[c] == READ) & pmp_pmpcfg[r].read);
267 :
268 : // Check specific required permissions since the behaviour is different
269 : // between Smepmp implementation and original PMP.
270 : assign region_perm_check[c][r] = perm_check_wrapper(
271 : `ifdef RV_SMEPMP
272 : mseccfg,
273 : `else
274 : 3'b000,
275 : `endif
276 : pmp_pmpcfg[r],
277 : pmp_chan_type[c],
278 : `ifdef RV_USER_MODE
279 : pmp_priv_mode_eff[c],
280 : `else
281 : 1'b0,
282 : `endif
283 : region_basic_perm_check[c][r]
284 : );
285 :
286 : // Address bits below PMP granularity (which starts at 4 byte) are deliberately unused.
287 : logic unused_sigs;
288 : assign unused_sigs = ^{region_start_addr[r][PMP_GRANULARITY+2-1:0],
289 : pmp_req_addr_i[c][PMP_GRANULARITY+2-1:0]};
290 : end
291 :
292 : // Once the permission checks of the regions are done, decide if the access is
293 : // denied by figuring out the matching region and its permission check.
294 : assign pmp_chan_err[c] = access_fault_check(
295 : `ifdef RV_SMEPMP
296 : mseccfg,
297 : `else
298 : 3'b000,
299 : `endif
300 : pmp_chan_type[c],
301 : region_match_all[c],
302 : `ifdef RV_USER_MODE
303 : any_region_enabled,
304 : pmp_priv_mode_eff[c],
305 : `else
306 : 1'b0,
307 : 1'b0,
308 : `endif
309 : region_perm_check[c]);
310 :
311 : end
312 :
313 : endmodule // el2_pmp
|