1 | /* |
2 | * RISC-V Control and Status Registers. |
3 | * |
4 | * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu |
5 | * Copyright (c) 2017-2018 SiFive, Inc. |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify it |
8 | * under the terms and conditions of the GNU General Public License, |
9 | * version 2 or later, as published by the Free Software Foundation. |
10 | * |
11 | * This program is distributed in the hope it will be useful, but WITHOUT |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
14 | * more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License along with |
17 | * this program. If not, see <http://www.gnu.org/licenses/>. |
18 | */ |
19 | |
20 | #include "qemu/osdep.h" |
21 | #include "qemu/log.h" |
22 | #include "cpu.h" |
23 | #include "qemu/main-loop.h" |
24 | #include "exec/exec-all.h" |
25 | |
26 | /* CSR function table */ |
27 | static riscv_csr_operations csr_ops[]; |
28 | |
29 | /* CSR function table constants */ |
30 | enum { |
31 | CSR_TABLE_SIZE = 0x1000 |
32 | }; |
33 | |
34 | /* CSR function table public API */ |
35 | void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) |
36 | { |
37 | *ops = csr_ops[csrno & (CSR_TABLE_SIZE - 1)]; |
38 | } |
39 | |
40 | void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops) |
41 | { |
42 | csr_ops[csrno & (CSR_TABLE_SIZE - 1)] = *ops; |
43 | } |
44 | |
45 | /* Predicates */ |
46 | static int fs(CPURISCVState *env, int csrno) |
47 | { |
48 | #if !defined(CONFIG_USER_ONLY) |
49 | if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { |
50 | return -1; |
51 | } |
52 | #endif |
53 | return 0; |
54 | } |
55 | |
56 | static int ctr(CPURISCVState *env, int csrno) |
57 | { |
58 | #if !defined(CONFIG_USER_ONLY) |
59 | CPUState *cs = env_cpu(env); |
60 | RISCVCPU *cpu = RISCV_CPU(cs); |
61 | uint32_t ctr_en = ~0u; |
62 | |
63 | if (!cpu->cfg.ext_counters) { |
64 | /* The Counters extensions is not enabled */ |
65 | return -1; |
66 | } |
67 | |
68 | /* |
69 | * The counters are always enabled at run time on newer priv specs, as the |
70 | * CSR has changed from controlling that the counters can be read to |
71 | * controlling that the counters increment. |
72 | */ |
73 | if (env->priv_ver > PRIV_VERSION_1_09_1) { |
74 | return 0; |
75 | } |
76 | |
77 | if (env->priv < PRV_M) { |
78 | ctr_en &= env->mcounteren; |
79 | } |
80 | if (env->priv < PRV_S) { |
81 | ctr_en &= env->scounteren; |
82 | } |
83 | if (!(ctr_en & (1u << (csrno & 31)))) { |
84 | return -1; |
85 | } |
86 | #endif |
87 | return 0; |
88 | } |
89 | |
90 | #if !defined(CONFIG_USER_ONLY) |
91 | static int any(CPURISCVState *env, int csrno) |
92 | { |
93 | return 0; |
94 | } |
95 | |
96 | static int smode(CPURISCVState *env, int csrno) |
97 | { |
98 | return -!riscv_has_ext(env, RVS); |
99 | } |
100 | |
101 | static int pmp(CPURISCVState *env, int csrno) |
102 | { |
103 | return -!riscv_feature(env, RISCV_FEATURE_PMP); |
104 | } |
105 | #endif |
106 | |
107 | /* User Floating-Point CSRs */ |
108 | static int read_fflags(CPURISCVState *env, int csrno, target_ulong *val) |
109 | { |
110 | #if !defined(CONFIG_USER_ONLY) |
111 | if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { |
112 | return -1; |
113 | } |
114 | #endif |
115 | *val = riscv_cpu_get_fflags(env); |
116 | return 0; |
117 | } |
118 | |
119 | static int write_fflags(CPURISCVState *env, int csrno, target_ulong val) |
120 | { |
121 | #if !defined(CONFIG_USER_ONLY) |
122 | if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { |
123 | return -1; |
124 | } |
125 | env->mstatus |= MSTATUS_FS; |
126 | #endif |
127 | riscv_cpu_set_fflags(env, val & (FSR_AEXC >> FSR_AEXC_SHIFT)); |
128 | return 0; |
129 | } |
130 | |
131 | static int read_frm(CPURISCVState *env, int csrno, target_ulong *val) |
132 | { |
133 | #if !defined(CONFIG_USER_ONLY) |
134 | if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { |
135 | return -1; |
136 | } |
137 | #endif |
138 | *val = env->frm; |
139 | return 0; |
140 | } |
141 | |
142 | static int write_frm(CPURISCVState *env, int csrno, target_ulong val) |
143 | { |
144 | #if !defined(CONFIG_USER_ONLY) |
145 | if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { |
146 | return -1; |
147 | } |
148 | env->mstatus |= MSTATUS_FS; |
149 | #endif |
150 | env->frm = val & (FSR_RD >> FSR_RD_SHIFT); |
151 | return 0; |
152 | } |
153 | |
154 | static int read_fcsr(CPURISCVState *env, int csrno, target_ulong *val) |
155 | { |
156 | #if !defined(CONFIG_USER_ONLY) |
157 | if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { |
158 | return -1; |
159 | } |
160 | #endif |
161 | *val = (riscv_cpu_get_fflags(env) << FSR_AEXC_SHIFT) |
162 | | (env->frm << FSR_RD_SHIFT); |
163 | return 0; |
164 | } |
165 | |
166 | static int write_fcsr(CPURISCVState *env, int csrno, target_ulong val) |
167 | { |
168 | #if !defined(CONFIG_USER_ONLY) |
169 | if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { |
170 | return -1; |
171 | } |
172 | env->mstatus |= MSTATUS_FS; |
173 | #endif |
174 | env->frm = (val & FSR_RD) >> FSR_RD_SHIFT; |
175 | riscv_cpu_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT); |
176 | return 0; |
177 | } |
178 | |
179 | /* User Timers and Counters */ |
180 | static int read_instret(CPURISCVState *env, int csrno, target_ulong *val) |
181 | { |
182 | #if !defined(CONFIG_USER_ONLY) |
183 | if (use_icount) { |
184 | *val = cpu_get_icount(); |
185 | } else { |
186 | *val = cpu_get_host_ticks(); |
187 | } |
188 | #else |
189 | *val = cpu_get_host_ticks(); |
190 | #endif |
191 | return 0; |
192 | } |
193 | |
194 | #if defined(TARGET_RISCV32) |
195 | static int read_instreth(CPURISCVState *env, int csrno, target_ulong *val) |
196 | { |
197 | #if !defined(CONFIG_USER_ONLY) |
198 | if (use_icount) { |
199 | *val = cpu_get_icount() >> 32; |
200 | } else { |
201 | *val = cpu_get_host_ticks() >> 32; |
202 | } |
203 | #else |
204 | *val = cpu_get_host_ticks() >> 32; |
205 | #endif |
206 | return 0; |
207 | } |
208 | #endif /* TARGET_RISCV32 */ |
209 | |
210 | #if defined(CONFIG_USER_ONLY) |
211 | static int read_time(CPURISCVState *env, int csrno, target_ulong *val) |
212 | { |
213 | *val = cpu_get_host_ticks(); |
214 | return 0; |
215 | } |
216 | |
217 | #if defined(TARGET_RISCV32) |
218 | static int read_timeh(CPURISCVState *env, int csrno, target_ulong *val) |
219 | { |
220 | *val = cpu_get_host_ticks() >> 32; |
221 | return 0; |
222 | } |
223 | #endif |
224 | |
225 | #else /* CONFIG_USER_ONLY */ |
226 | |
227 | /* Machine constants */ |
228 | |
229 | #define M_MODE_INTERRUPTS (MIP_MSIP | MIP_MTIP | MIP_MEIP) |
230 | #define S_MODE_INTERRUPTS (MIP_SSIP | MIP_STIP | MIP_SEIP) |
231 | |
232 | static const target_ulong delegable_ints = S_MODE_INTERRUPTS; |
233 | static const target_ulong all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS; |
234 | static const target_ulong delegable_excps = |
235 | (1ULL << (RISCV_EXCP_INST_ADDR_MIS)) | |
236 | (1ULL << (RISCV_EXCP_INST_ACCESS_FAULT)) | |
237 | (1ULL << (RISCV_EXCP_ILLEGAL_INST)) | |
238 | (1ULL << (RISCV_EXCP_BREAKPOINT)) | |
239 | (1ULL << (RISCV_EXCP_LOAD_ADDR_MIS)) | |
240 | (1ULL << (RISCV_EXCP_LOAD_ACCESS_FAULT)) | |
241 | (1ULL << (RISCV_EXCP_STORE_AMO_ADDR_MIS)) | |
242 | (1ULL << (RISCV_EXCP_STORE_AMO_ACCESS_FAULT)) | |
243 | (1ULL << (RISCV_EXCP_U_ECALL)) | |
244 | (1ULL << (RISCV_EXCP_S_ECALL)) | |
245 | (1ULL << (RISCV_EXCP_H_ECALL)) | |
246 | (1ULL << (RISCV_EXCP_M_ECALL)) | |
247 | (1ULL << (RISCV_EXCP_INST_PAGE_FAULT)) | |
248 | (1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT)) | |
249 | (1ULL << (RISCV_EXCP_STORE_PAGE_FAULT)); |
250 | static const target_ulong sstatus_v1_9_mask = SSTATUS_SIE | SSTATUS_SPIE | |
251 | SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS | |
252 | SSTATUS_SUM | SSTATUS_SD; |
253 | static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE | |
254 | SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS | |
255 | SSTATUS_SUM | SSTATUS_MXR | SSTATUS_SD; |
256 | static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | MIP_UEIP; |
257 | |
258 | #if defined(TARGET_RISCV32) |
259 | static const char valid_vm_1_09[16] = { |
260 | [VM_1_09_MBARE] = 1, |
261 | [VM_1_09_SV32] = 1, |
262 | }; |
263 | static const char valid_vm_1_10[16] = { |
264 | [VM_1_10_MBARE] = 1, |
265 | [VM_1_10_SV32] = 1 |
266 | }; |
267 | #elif defined(TARGET_RISCV64) |
268 | static const char valid_vm_1_09[16] = { |
269 | [VM_1_09_MBARE] = 1, |
270 | [VM_1_09_SV39] = 1, |
271 | [VM_1_09_SV48] = 1, |
272 | }; |
273 | static const char valid_vm_1_10[16] = { |
274 | [VM_1_10_MBARE] = 1, |
275 | [VM_1_10_SV39] = 1, |
276 | [VM_1_10_SV48] = 1, |
277 | [VM_1_10_SV57] = 1 |
278 | }; |
279 | #endif /* CONFIG_USER_ONLY */ |
280 | |
281 | /* Machine Information Registers */ |
282 | static int read_zero(CPURISCVState *env, int csrno, target_ulong *val) |
283 | { |
284 | return *val = 0; |
285 | } |
286 | |
287 | static int read_mhartid(CPURISCVState *env, int csrno, target_ulong *val) |
288 | { |
289 | *val = env->mhartid; |
290 | return 0; |
291 | } |
292 | |
293 | /* Machine Trap Setup */ |
294 | static int read_mstatus(CPURISCVState *env, int csrno, target_ulong *val) |
295 | { |
296 | *val = env->mstatus; |
297 | return 0; |
298 | } |
299 | |
300 | static int validate_vm(CPURISCVState *env, target_ulong vm) |
301 | { |
302 | return (env->priv_ver >= PRIV_VERSION_1_10_0) ? |
303 | valid_vm_1_10[vm & 0xf] : valid_vm_1_09[vm & 0xf]; |
304 | } |
305 | |
306 | static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) |
307 | { |
308 | target_ulong mstatus = env->mstatus; |
309 | target_ulong mask = 0; |
310 | |
311 | /* flush tlb on mstatus fields that affect VM */ |
312 | if (env->priv_ver <= PRIV_VERSION_1_09_1) { |
313 | if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | |
314 | MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_VM)) { |
315 | tlb_flush(env_cpu(env)); |
316 | } |
317 | mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | |
318 | MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | |
319 | MSTATUS_MPP | MSTATUS_MXR | |
320 | (validate_vm(env, get_field(val, MSTATUS_VM)) ? |
321 | MSTATUS_VM : 0); |
322 | } |
323 | if (env->priv_ver >= PRIV_VERSION_1_10_0) { |
324 | if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | MSTATUS_MPV | |
325 | MSTATUS_MPRV | MSTATUS_SUM)) { |
326 | tlb_flush(env_cpu(env)); |
327 | } |
328 | mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | |
329 | MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | |
330 | MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TVM | MSTATUS_TSR | |
331 | MSTATUS_TW; |
332 | #if defined(TARGET_RISCV64) |
333 | /* |
334 | * RV32: MPV and MTL are not in mstatus. The current plan is to |
335 | * add them to mstatush. For now, we just don't support it. |
336 | */ |
337 | mask |= MSTATUS_MPP | MSTATUS_MPV; |
338 | #endif |
339 | } |
340 | |
341 | mstatus = (mstatus & ~mask) | (val & mask); |
342 | |
343 | int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | |
344 | ((mstatus & MSTATUS_XS) == MSTATUS_XS); |
345 | mstatus = set_field(mstatus, MSTATUS_SD, dirty); |
346 | env->mstatus = mstatus; |
347 | |
348 | return 0; |
349 | } |
350 | |
351 | static int read_misa(CPURISCVState *env, int csrno, target_ulong *val) |
352 | { |
353 | *val = env->misa; |
354 | return 0; |
355 | } |
356 | |
357 | static int write_misa(CPURISCVState *env, int csrno, target_ulong val) |
358 | { |
359 | if (!riscv_feature(env, RISCV_FEATURE_MISA)) { |
360 | /* drop write to misa */ |
361 | return 0; |
362 | } |
363 | |
364 | /* 'I' or 'E' must be present */ |
365 | if (!(val & (RVI | RVE))) { |
366 | /* It is not, drop write to misa */ |
367 | return 0; |
368 | } |
369 | |
370 | /* 'E' excludes all other extensions */ |
371 | if (val & RVE) { |
372 | /* when we support 'E' we can do "val = RVE;" however |
373 | * for now we just drop writes if 'E' is present. |
374 | */ |
375 | return 0; |
376 | } |
377 | |
378 | /* Mask extensions that are not supported by this hart */ |
379 | val &= env->misa_mask; |
380 | |
381 | /* Mask extensions that are not supported by QEMU */ |
382 | val &= (RVI | RVE | RVM | RVA | RVF | RVD | RVC | RVS | RVU); |
383 | |
384 | /* 'D' depends on 'F', so clear 'D' if 'F' is not present */ |
385 | if ((val & RVD) && !(val & RVF)) { |
386 | val &= ~RVD; |
387 | } |
388 | |
389 | /* Suppress 'C' if next instruction is not aligned |
390 | * TODO: this should check next_pc |
391 | */ |
392 | if ((val & RVC) && (GETPC() & ~3) != 0) { |
393 | val &= ~RVC; |
394 | } |
395 | |
396 | /* misa.MXL writes are not supported by QEMU */ |
397 | val = (env->misa & MISA_MXL) | (val & ~MISA_MXL); |
398 | |
399 | /* flush translation cache */ |
400 | if (val != env->misa) { |
401 | tb_flush(env_cpu(env)); |
402 | } |
403 | |
404 | env->misa = val; |
405 | |
406 | return 0; |
407 | } |
408 | |
409 | static int read_medeleg(CPURISCVState *env, int csrno, target_ulong *val) |
410 | { |
411 | *val = env->medeleg; |
412 | return 0; |
413 | } |
414 | |
415 | static int write_medeleg(CPURISCVState *env, int csrno, target_ulong val) |
416 | { |
417 | env->medeleg = (env->medeleg & ~delegable_excps) | (val & delegable_excps); |
418 | return 0; |
419 | } |
420 | |
421 | static int read_mideleg(CPURISCVState *env, int csrno, target_ulong *val) |
422 | { |
423 | *val = env->mideleg; |
424 | return 0; |
425 | } |
426 | |
427 | static int write_mideleg(CPURISCVState *env, int csrno, target_ulong val) |
428 | { |
429 | env->mideleg = (env->mideleg & ~delegable_ints) | (val & delegable_ints); |
430 | return 0; |
431 | } |
432 | |
433 | static int read_mie(CPURISCVState *env, int csrno, target_ulong *val) |
434 | { |
435 | *val = env->mie; |
436 | return 0; |
437 | } |
438 | |
439 | static int write_mie(CPURISCVState *env, int csrno, target_ulong val) |
440 | { |
441 | env->mie = (env->mie & ~all_ints) | (val & all_ints); |
442 | return 0; |
443 | } |
444 | |
445 | static int read_mtvec(CPURISCVState *env, int csrno, target_ulong *val) |
446 | { |
447 | *val = env->mtvec; |
448 | return 0; |
449 | } |
450 | |
451 | static int write_mtvec(CPURISCVState *env, int csrno, target_ulong val) |
452 | { |
453 | /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ |
454 | if ((val & 3) < 2) { |
455 | env->mtvec = val; |
456 | } else { |
457 | qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: reserved mode not supported\n" ); |
458 | } |
459 | return 0; |
460 | } |
461 | |
462 | static int read_mcounteren(CPURISCVState *env, int csrno, target_ulong *val) |
463 | { |
464 | if (env->priv_ver < PRIV_VERSION_1_10_0) { |
465 | return -1; |
466 | } |
467 | *val = env->mcounteren; |
468 | return 0; |
469 | } |
470 | |
471 | static int write_mcounteren(CPURISCVState *env, int csrno, target_ulong val) |
472 | { |
473 | if (env->priv_ver < PRIV_VERSION_1_10_0) { |
474 | return -1; |
475 | } |
476 | env->mcounteren = val; |
477 | return 0; |
478 | } |
479 | |
480 | /* This regiser is replaced with CSR_MCOUNTINHIBIT in 1.11.0 */ |
481 | static int read_mscounteren(CPURISCVState *env, int csrno, target_ulong *val) |
482 | { |
483 | if (env->priv_ver > PRIV_VERSION_1_09_1 |
484 | && env->priv_ver < PRIV_VERSION_1_11_0) { |
485 | return -1; |
486 | } |
487 | *val = env->mcounteren; |
488 | return 0; |
489 | } |
490 | |
491 | /* This regiser is replaced with CSR_MCOUNTINHIBIT in 1.11.0 */ |
492 | static int write_mscounteren(CPURISCVState *env, int csrno, target_ulong val) |
493 | { |
494 | if (env->priv_ver > PRIV_VERSION_1_09_1 |
495 | && env->priv_ver < PRIV_VERSION_1_11_0) { |
496 | return -1; |
497 | } |
498 | env->mcounteren = val; |
499 | return 0; |
500 | } |
501 | |
502 | static int read_mucounteren(CPURISCVState *env, int csrno, target_ulong *val) |
503 | { |
504 | if (env->priv_ver > PRIV_VERSION_1_09_1) { |
505 | return -1; |
506 | } |
507 | *val = env->scounteren; |
508 | return 0; |
509 | } |
510 | |
511 | static int write_mucounteren(CPURISCVState *env, int csrno, target_ulong val) |
512 | { |
513 | if (env->priv_ver > PRIV_VERSION_1_09_1) { |
514 | return -1; |
515 | } |
516 | env->scounteren = val; |
517 | return 0; |
518 | } |
519 | |
520 | /* Machine Trap Handling */ |
521 | static int read_mscratch(CPURISCVState *env, int csrno, target_ulong *val) |
522 | { |
523 | *val = env->mscratch; |
524 | return 0; |
525 | } |
526 | |
527 | static int write_mscratch(CPURISCVState *env, int csrno, target_ulong val) |
528 | { |
529 | env->mscratch = val; |
530 | return 0; |
531 | } |
532 | |
533 | static int read_mepc(CPURISCVState *env, int csrno, target_ulong *val) |
534 | { |
535 | *val = env->mepc; |
536 | return 0; |
537 | } |
538 | |
539 | static int write_mepc(CPURISCVState *env, int csrno, target_ulong val) |
540 | { |
541 | env->mepc = val; |
542 | return 0; |
543 | } |
544 | |
545 | static int read_mcause(CPURISCVState *env, int csrno, target_ulong *val) |
546 | { |
547 | *val = env->mcause; |
548 | return 0; |
549 | } |
550 | |
551 | static int write_mcause(CPURISCVState *env, int csrno, target_ulong val) |
552 | { |
553 | env->mcause = val; |
554 | return 0; |
555 | } |
556 | |
557 | static int read_mbadaddr(CPURISCVState *env, int csrno, target_ulong *val) |
558 | { |
559 | *val = env->mbadaddr; |
560 | return 0; |
561 | } |
562 | |
563 | static int write_mbadaddr(CPURISCVState *env, int csrno, target_ulong val) |
564 | { |
565 | env->mbadaddr = val; |
566 | return 0; |
567 | } |
568 | |
569 | static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value, |
570 | target_ulong new_value, target_ulong write_mask) |
571 | { |
572 | RISCVCPU *cpu = env_archcpu(env); |
573 | /* Allow software control of delegable interrupts not claimed by hardware */ |
574 | target_ulong mask = write_mask & delegable_ints & ~env->miclaim; |
575 | uint32_t old_mip; |
576 | |
577 | if (mask) { |
578 | old_mip = riscv_cpu_update_mip(cpu, mask, (new_value & mask)); |
579 | } else { |
580 | old_mip = atomic_read(&env->mip); |
581 | } |
582 | |
583 | if (ret_value) { |
584 | *ret_value = old_mip; |
585 | } |
586 | |
587 | return 0; |
588 | } |
589 | |
590 | /* Supervisor Trap Setup */ |
591 | static int read_sstatus(CPURISCVState *env, int csrno, target_ulong *val) |
592 | { |
593 | target_ulong mask = ((env->priv_ver >= PRIV_VERSION_1_10_0) ? |
594 | sstatus_v1_10_mask : sstatus_v1_9_mask); |
595 | *val = env->mstatus & mask; |
596 | return 0; |
597 | } |
598 | |
599 | static int write_sstatus(CPURISCVState *env, int csrno, target_ulong val) |
600 | { |
601 | target_ulong mask = ((env->priv_ver >= PRIV_VERSION_1_10_0) ? |
602 | sstatus_v1_10_mask : sstatus_v1_9_mask); |
603 | target_ulong newval = (env->mstatus & ~mask) | (val & mask); |
604 | return write_mstatus(env, CSR_MSTATUS, newval); |
605 | } |
606 | |
607 | static int read_sie(CPURISCVState *env, int csrno, target_ulong *val) |
608 | { |
609 | *val = env->mie & env->mideleg; |
610 | return 0; |
611 | } |
612 | |
613 | static int write_sie(CPURISCVState *env, int csrno, target_ulong val) |
614 | { |
615 | target_ulong newval = (env->mie & ~env->mideleg) | (val & env->mideleg); |
616 | return write_mie(env, CSR_MIE, newval); |
617 | } |
618 | |
619 | static int read_stvec(CPURISCVState *env, int csrno, target_ulong *val) |
620 | { |
621 | *val = env->stvec; |
622 | return 0; |
623 | } |
624 | |
625 | static int write_stvec(CPURISCVState *env, int csrno, target_ulong val) |
626 | { |
627 | /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ |
628 | if ((val & 3) < 2) { |
629 | env->stvec = val; |
630 | } else { |
631 | qemu_log_mask(LOG_UNIMP, "CSR_STVEC: reserved mode not supported\n" ); |
632 | } |
633 | return 0; |
634 | } |
635 | |
636 | static int read_scounteren(CPURISCVState *env, int csrno, target_ulong *val) |
637 | { |
638 | if (env->priv_ver < PRIV_VERSION_1_10_0) { |
639 | return -1; |
640 | } |
641 | *val = env->scounteren; |
642 | return 0; |
643 | } |
644 | |
645 | static int write_scounteren(CPURISCVState *env, int csrno, target_ulong val) |
646 | { |
647 | if (env->priv_ver < PRIV_VERSION_1_10_0) { |
648 | return -1; |
649 | } |
650 | env->scounteren = val; |
651 | return 0; |
652 | } |
653 | |
654 | /* Supervisor Trap Handling */ |
655 | static int read_sscratch(CPURISCVState *env, int csrno, target_ulong *val) |
656 | { |
657 | *val = env->sscratch; |
658 | return 0; |
659 | } |
660 | |
661 | static int write_sscratch(CPURISCVState *env, int csrno, target_ulong val) |
662 | { |
663 | env->sscratch = val; |
664 | return 0; |
665 | } |
666 | |
667 | static int read_sepc(CPURISCVState *env, int csrno, target_ulong *val) |
668 | { |
669 | *val = env->sepc; |
670 | return 0; |
671 | } |
672 | |
673 | static int write_sepc(CPURISCVState *env, int csrno, target_ulong val) |
674 | { |
675 | env->sepc = val; |
676 | return 0; |
677 | } |
678 | |
679 | static int read_scause(CPURISCVState *env, int csrno, target_ulong *val) |
680 | { |
681 | *val = env->scause; |
682 | return 0; |
683 | } |
684 | |
685 | static int write_scause(CPURISCVState *env, int csrno, target_ulong val) |
686 | { |
687 | env->scause = val; |
688 | return 0; |
689 | } |
690 | |
691 | static int read_sbadaddr(CPURISCVState *env, int csrno, target_ulong *val) |
692 | { |
693 | *val = env->sbadaddr; |
694 | return 0; |
695 | } |
696 | |
697 | static int write_sbadaddr(CPURISCVState *env, int csrno, target_ulong val) |
698 | { |
699 | env->sbadaddr = val; |
700 | return 0; |
701 | } |
702 | |
703 | static int rmw_sip(CPURISCVState *env, int csrno, target_ulong *ret_value, |
704 | target_ulong new_value, target_ulong write_mask) |
705 | { |
706 | int ret = rmw_mip(env, CSR_MSTATUS, ret_value, new_value, |
707 | write_mask & env->mideleg & sip_writable_mask); |
708 | *ret_value &= env->mideleg; |
709 | return ret; |
710 | } |
711 | |
712 | /* Supervisor Protection and Translation */ |
713 | static int read_satp(CPURISCVState *env, int csrno, target_ulong *val) |
714 | { |
715 | if (!riscv_feature(env, RISCV_FEATURE_MMU)) { |
716 | *val = 0; |
717 | } else if (env->priv_ver >= PRIV_VERSION_1_10_0) { |
718 | if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) { |
719 | return -1; |
720 | } else { |
721 | *val = env->satp; |
722 | } |
723 | } else { |
724 | *val = env->sptbr; |
725 | } |
726 | return 0; |
727 | } |
728 | |
729 | static int write_satp(CPURISCVState *env, int csrno, target_ulong val) |
730 | { |
731 | if (!riscv_feature(env, RISCV_FEATURE_MMU)) { |
732 | return 0; |
733 | } |
734 | if (env->priv_ver <= PRIV_VERSION_1_09_1 && (val ^ env->sptbr)) { |
735 | tlb_flush(env_cpu(env)); |
736 | env->sptbr = val & (((target_ulong) |
737 | 1 << (TARGET_PHYS_ADDR_SPACE_BITS - PGSHIFT)) - 1); |
738 | } |
739 | if (env->priv_ver >= PRIV_VERSION_1_10_0 && |
740 | validate_vm(env, get_field(val, SATP_MODE)) && |
741 | ((val ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN))) |
742 | { |
743 | if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) { |
744 | return -1; |
745 | } else { |
746 | if((val ^ env->satp) & SATP_ASID) { |
747 | tlb_flush(env_cpu(env)); |
748 | } |
749 | env->satp = val; |
750 | } |
751 | } |
752 | return 0; |
753 | } |
754 | |
755 | /* Physical Memory Protection */ |
756 | static int read_pmpcfg(CPURISCVState *env, int csrno, target_ulong *val) |
757 | { |
758 | *val = pmpcfg_csr_read(env, csrno - CSR_PMPCFG0); |
759 | return 0; |
760 | } |
761 | |
762 | static int write_pmpcfg(CPURISCVState *env, int csrno, target_ulong val) |
763 | { |
764 | pmpcfg_csr_write(env, csrno - CSR_PMPCFG0, val); |
765 | return 0; |
766 | } |
767 | |
768 | static int read_pmpaddr(CPURISCVState *env, int csrno, target_ulong *val) |
769 | { |
770 | *val = pmpaddr_csr_read(env, csrno - CSR_PMPADDR0); |
771 | return 0; |
772 | } |
773 | |
774 | static int write_pmpaddr(CPURISCVState *env, int csrno, target_ulong val) |
775 | { |
776 | pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val); |
777 | return 0; |
778 | } |
779 | |
780 | #endif |
781 | |
782 | /* |
783 | * riscv_csrrw - read and/or update control and status register |
784 | * |
785 | * csrr <-> riscv_csrrw(env, csrno, ret_value, 0, 0); |
786 | * csrrw <-> riscv_csrrw(env, csrno, ret_value, value, -1); |
787 | * csrrs <-> riscv_csrrw(env, csrno, ret_value, -1, value); |
788 | * csrrc <-> riscv_csrrw(env, csrno, ret_value, 0, value); |
789 | */ |
790 | |
791 | int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, |
792 | target_ulong new_value, target_ulong write_mask) |
793 | { |
794 | int ret; |
795 | target_ulong old_value; |
796 | RISCVCPU *cpu = env_archcpu(env); |
797 | |
798 | /* check privileges and return -1 if check fails */ |
799 | #if !defined(CONFIG_USER_ONLY) |
800 | int csr_priv = get_field(csrno, 0x300); |
801 | int read_only = get_field(csrno, 0xC00) == 3; |
802 | if ((write_mask && read_only) || (env->priv < csr_priv)) { |
803 | return -1; |
804 | } |
805 | #endif |
806 | |
807 | /* ensure the CSR extension is enabled. */ |
808 | if (!cpu->cfg.ext_icsr) { |
809 | return -1; |
810 | } |
811 | |
812 | /* check predicate */ |
813 | if (!csr_ops[csrno].predicate || csr_ops[csrno].predicate(env, csrno) < 0) { |
814 | return -1; |
815 | } |
816 | |
817 | /* execute combined read/write operation if it exists */ |
818 | if (csr_ops[csrno].op) { |
819 | return csr_ops[csrno].op(env, csrno, ret_value, new_value, write_mask); |
820 | } |
821 | |
822 | /* if no accessor exists then return failure */ |
823 | if (!csr_ops[csrno].read) { |
824 | return -1; |
825 | } |
826 | |
827 | /* read old value */ |
828 | ret = csr_ops[csrno].read(env, csrno, &old_value); |
829 | if (ret < 0) { |
830 | return ret; |
831 | } |
832 | |
833 | /* write value if writable and write mask set, otherwise drop writes */ |
834 | if (write_mask) { |
835 | new_value = (old_value & ~write_mask) | (new_value & write_mask); |
836 | if (csr_ops[csrno].write) { |
837 | ret = csr_ops[csrno].write(env, csrno, new_value); |
838 | if (ret < 0) { |
839 | return ret; |
840 | } |
841 | } |
842 | } |
843 | |
844 | /* return old value */ |
845 | if (ret_value) { |
846 | *ret_value = old_value; |
847 | } |
848 | |
849 | return 0; |
850 | } |
851 | |
852 | /* |
853 | * Debugger support. If not in user mode, set env->debugger before the |
854 | * riscv_csrrw call and clear it after the call. |
855 | */ |
856 | int riscv_csrrw_debug(CPURISCVState *env, int csrno, target_ulong *ret_value, |
857 | target_ulong new_value, target_ulong write_mask) |
858 | { |
859 | int ret; |
860 | #if !defined(CONFIG_USER_ONLY) |
861 | env->debugger = true; |
862 | #endif |
863 | ret = riscv_csrrw(env, csrno, ret_value, new_value, write_mask); |
864 | #if !defined(CONFIG_USER_ONLY) |
865 | env->debugger = false; |
866 | #endif |
867 | return ret; |
868 | } |
869 | |
870 | /* Control and Status Register function table */ |
871 | static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { |
872 | /* User Floating-Point CSRs */ |
873 | [CSR_FFLAGS] = { fs, read_fflags, write_fflags }, |
874 | [CSR_FRM] = { fs, read_frm, write_frm }, |
875 | [CSR_FCSR] = { fs, read_fcsr, write_fcsr }, |
876 | |
877 | /* User Timers and Counters */ |
878 | [CSR_CYCLE] = { ctr, read_instret }, |
879 | [CSR_INSTRET] = { ctr, read_instret }, |
880 | #if defined(TARGET_RISCV32) |
881 | [CSR_CYCLEH] = { ctr, read_instreth }, |
882 | [CSR_INSTRETH] = { ctr, read_instreth }, |
883 | #endif |
884 | |
885 | /* User-level time CSRs are only available in linux-user |
886 | * In privileged mode, the monitor emulates these CSRs */ |
887 | #if defined(CONFIG_USER_ONLY) |
888 | [CSR_TIME] = { ctr, read_time }, |
889 | #if defined(TARGET_RISCV32) |
890 | [CSR_TIMEH] = { ctr, read_timeh }, |
891 | #endif |
892 | #endif |
893 | |
894 | #if !defined(CONFIG_USER_ONLY) |
895 | /* Machine Timers and Counters */ |
896 | [CSR_MCYCLE] = { any, read_instret }, |
897 | [CSR_MINSTRET] = { any, read_instret }, |
898 | #if defined(TARGET_RISCV32) |
899 | [CSR_MCYCLEH] = { any, read_instreth }, |
900 | [CSR_MINSTRETH] = { any, read_instreth }, |
901 | #endif |
902 | |
903 | /* Machine Information Registers */ |
904 | [CSR_MVENDORID] = { any, read_zero }, |
905 | [CSR_MARCHID] = { any, read_zero }, |
906 | [CSR_MIMPID] = { any, read_zero }, |
907 | [CSR_MHARTID] = { any, read_mhartid }, |
908 | |
909 | /* Machine Trap Setup */ |
910 | [CSR_MSTATUS] = { any, read_mstatus, write_mstatus }, |
911 | [CSR_MISA] = { any, read_misa, write_misa }, |
912 | [CSR_MIDELEG] = { any, read_mideleg, write_mideleg }, |
913 | [CSR_MEDELEG] = { any, read_medeleg, write_medeleg }, |
914 | [CSR_MIE] = { any, read_mie, write_mie }, |
915 | [CSR_MTVEC] = { any, read_mtvec, write_mtvec }, |
916 | [CSR_MCOUNTEREN] = { any, read_mcounteren, write_mcounteren }, |
917 | |
918 | /* Legacy Counter Setup (priv v1.9.1) */ |
919 | [CSR_MUCOUNTEREN] = { any, read_mucounteren, write_mucounteren }, |
920 | [CSR_MSCOUNTEREN] = { any, read_mscounteren, write_mscounteren }, |
921 | |
922 | /* Machine Trap Handling */ |
923 | [CSR_MSCRATCH] = { any, read_mscratch, write_mscratch }, |
924 | [CSR_MEPC] = { any, read_mepc, write_mepc }, |
925 | [CSR_MCAUSE] = { any, read_mcause, write_mcause }, |
926 | [CSR_MBADADDR] = { any, read_mbadaddr, write_mbadaddr }, |
927 | [CSR_MIP] = { any, NULL, NULL, rmw_mip }, |
928 | |
929 | /* Supervisor Trap Setup */ |
930 | [CSR_SSTATUS] = { smode, read_sstatus, write_sstatus }, |
931 | [CSR_SIE] = { smode, read_sie, write_sie }, |
932 | [CSR_STVEC] = { smode, read_stvec, write_stvec }, |
933 | [CSR_SCOUNTEREN] = { smode, read_scounteren, write_scounteren }, |
934 | |
935 | /* Supervisor Trap Handling */ |
936 | [CSR_SSCRATCH] = { smode, read_sscratch, write_sscratch }, |
937 | [CSR_SEPC] = { smode, read_sepc, write_sepc }, |
938 | [CSR_SCAUSE] = { smode, read_scause, write_scause }, |
939 | [CSR_SBADADDR] = { smode, read_sbadaddr, write_sbadaddr }, |
940 | [CSR_SIP] = { smode, NULL, NULL, rmw_sip }, |
941 | |
942 | /* Supervisor Protection and Translation */ |
943 | [CSR_SATP] = { smode, read_satp, write_satp }, |
944 | |
945 | /* Physical Memory Protection */ |
946 | [CSR_PMPCFG0 ... CSR_PMPADDR9] = { pmp, read_pmpcfg, write_pmpcfg }, |
947 | [CSR_PMPADDR0 ... CSR_PMPADDR15] = { pmp, read_pmpaddr, write_pmpaddr }, |
948 | |
949 | /* Performance Counters */ |
950 | [CSR_HPMCOUNTER3 ... CSR_HPMCOUNTER31] = { ctr, read_zero }, |
951 | [CSR_MHPMCOUNTER3 ... CSR_MHPMCOUNTER31] = { any, read_zero }, |
952 | [CSR_MHPMEVENT3 ... CSR_MHPMEVENT31] = { any, read_zero }, |
953 | #if defined(TARGET_RISCV32) |
954 | [CSR_HPMCOUNTER3H ... CSR_HPMCOUNTER31H] = { ctr, read_zero }, |
955 | [CSR_MHPMCOUNTER3H ... CSR_MHPMCOUNTER31H] = { any, read_zero }, |
956 | #endif |
957 | #endif /* !CONFIG_USER_ONLY */ |
958 | }; |
959 | |