]>
Commit | Line | Data |
---|---|---|
95799e36 BM |
1 | /* |
2 | * QEMU RISC-V Native Debug Support | |
3 | * | |
4 | * Copyright (c) 2022 Wind River Systems, Inc. | |
5 | * | |
6 | * Author: | |
7 | * Bin Meng <[email protected]> | |
8 | * | |
9 | * This provides the native debug support via the Trigger Module, as defined | |
10 | * in the RISC-V Debug Specification: | |
11 | * https://github.com/riscv/riscv-debug-spec/raw/master/riscv-debug-stable.pdf | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or modify it | |
14 | * under the terms and conditions of the GNU General Public License, | |
15 | * version 2 or later, as published by the Free Software Foundation. | |
16 | * | |
17 | * This program is distributed in the hope it will be useful, but WITHOUT | |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
20 | * more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License along with | |
23 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
24 | */ | |
25 | ||
26 | #include "qemu/osdep.h" | |
27 | #include "qemu/log.h" | |
28 | #include "qapi/error.h" | |
29 | #include "cpu.h" | |
30 | #include "trace.h" | |
31 | #include "exec/exec-all.h" | |
32 | ||
33 | /* | |
34 | * The following M-mode trigger CSRs are implemented: | |
35 | * | |
36 | * - tselect | |
37 | * - tdata1 | |
38 | * - tdata2 | |
39 | * - tdata3 | |
40 | * | |
41 | * We don't support writable 'type' field in the tdata1 register, so there is | |
42 | * no need to implement the "tinfo" CSR. | |
43 | * | |
44 | * The following triggers are implemented: | |
45 | * | |
46 | * Index | Type | tdata mapping | Description | |
47 | * ------+------+------------------------+------------ | |
48 | * 0 | 2 | tdata1, tdata2 | Address / Data Match | |
49 | * 1 | 2 | tdata1, tdata2 | Address / Data Match | |
50 | */ | |
51 | ||
52 | /* tdata availability of a trigger */ | |
53 | typedef bool tdata_avail[TDATA_NUM]; | |
54 | ||
55 | static tdata_avail tdata_mapping[TRIGGER_NUM] = { | |
56 | [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = { true, true, false }, | |
57 | }; | |
58 | ||
59 | /* only breakpoint size 1/2/4/8 supported */ | |
60 | static int access_size[SIZE_NUM] = { | |
61 | [SIZE_ANY] = 0, | |
62 | [SIZE_1B] = 1, | |
63 | [SIZE_2B] = 2, | |
64 | [SIZE_4B] = 4, | |
65 | [SIZE_6B] = -1, | |
66 | [SIZE_8B] = 8, | |
67 | [6 ... 15] = -1, | |
68 | }; | |
69 | ||
70 | static inline target_ulong trigger_type(CPURISCVState *env, | |
71 | trigger_type_t type) | |
72 | { | |
73 | target_ulong tdata1; | |
74 | ||
75 | switch (riscv_cpu_mxl(env)) { | |
76 | case MXL_RV32: | |
77 | tdata1 = RV32_TYPE(type); | |
78 | break; | |
79 | case MXL_RV64: | |
80 | tdata1 = RV64_TYPE(type); | |
81 | break; | |
82 | default: | |
83 | g_assert_not_reached(); | |
84 | } | |
85 | ||
86 | return tdata1; | |
87 | } | |
88 | ||
89 | bool tdata_available(CPURISCVState *env, int tdata_index) | |
90 | { | |
91 | if (unlikely(tdata_index >= TDATA_NUM)) { | |
92 | return false; | |
93 | } | |
94 | ||
95 | if (unlikely(env->trigger_cur >= TRIGGER_NUM)) { | |
96 | return false; | |
97 | } | |
98 | ||
99 | return tdata_mapping[env->trigger_cur][tdata_index]; | |
100 | } | |
101 | ||
102 | target_ulong tselect_csr_read(CPURISCVState *env) | |
103 | { | |
104 | return env->trigger_cur; | |
105 | } | |
106 | ||
107 | void tselect_csr_write(CPURISCVState *env, target_ulong val) | |
108 | { | |
109 | /* all target_ulong bits of tselect are implemented */ | |
110 | env->trigger_cur = val; | |
111 | } | |
112 | ||
113 | static target_ulong tdata1_validate(CPURISCVState *env, target_ulong val, | |
114 | trigger_type_t t) | |
115 | { | |
116 | uint32_t type, dmode; | |
117 | target_ulong tdata1; | |
118 | ||
119 | switch (riscv_cpu_mxl(env)) { | |
120 | case MXL_RV32: | |
121 | type = extract32(val, 28, 4); | |
122 | dmode = extract32(val, 27, 1); | |
123 | tdata1 = RV32_TYPE(t); | |
124 | break; | |
125 | case MXL_RV64: | |
126 | type = extract64(val, 60, 4); | |
127 | dmode = extract64(val, 59, 1); | |
128 | tdata1 = RV64_TYPE(t); | |
129 | break; | |
130 | default: | |
131 | g_assert_not_reached(); | |
132 | } | |
133 | ||
134 | if (type != t) { | |
135 | qemu_log_mask(LOG_GUEST_ERROR, | |
136 | "ignoring type write to tdata1 register\n"); | |
137 | } | |
138 | if (dmode != 0) { | |
139 | qemu_log_mask(LOG_UNIMP, "debug mode is not supported\n"); | |
140 | } | |
141 | ||
142 | return tdata1; | |
143 | } | |
144 | ||
145 | static inline void warn_always_zero_bit(target_ulong val, target_ulong mask, | |
146 | const char *msg) | |
147 | { | |
148 | if (val & mask) { | |
149 | qemu_log_mask(LOG_UNIMP, "%s bit is always zero\n", msg); | |
150 | } | |
151 | } | |
152 | ||
153 | static uint32_t type2_breakpoint_size(CPURISCVState *env, target_ulong ctrl) | |
154 | { | |
155 | uint32_t size, sizelo, sizehi = 0; | |
156 | ||
157 | if (riscv_cpu_mxl(env) == MXL_RV64) { | |
158 | sizehi = extract32(ctrl, 21, 2); | |
159 | } | |
160 | sizelo = extract32(ctrl, 16, 2); | |
161 | size = (sizehi << 2) | sizelo; | |
162 | ||
163 | return size; | |
164 | } | |
165 | ||
166 | static inline bool type2_breakpoint_enabled(target_ulong ctrl) | |
167 | { | |
168 | bool mode = !!(ctrl & (TYPE2_U | TYPE2_S | TYPE2_M)); | |
169 | bool rwx = !!(ctrl & (TYPE2_LOAD | TYPE2_STORE | TYPE2_EXEC)); | |
170 | ||
171 | return mode && rwx; | |
172 | } | |
173 | ||
174 | static target_ulong type2_mcontrol_validate(CPURISCVState *env, | |
175 | target_ulong ctrl) | |
176 | { | |
177 | target_ulong val; | |
178 | uint32_t size; | |
179 | ||
180 | /* validate the generic part first */ | |
181 | val = tdata1_validate(env, ctrl, TRIGGER_TYPE_AD_MATCH); | |
182 | ||
183 | /* validate unimplemented (always zero) bits */ | |
184 | warn_always_zero_bit(ctrl, TYPE2_MATCH, "match"); | |
185 | warn_always_zero_bit(ctrl, TYPE2_CHAIN, "chain"); | |
186 | warn_always_zero_bit(ctrl, TYPE2_ACTION, "action"); | |
187 | warn_always_zero_bit(ctrl, TYPE2_TIMING, "timing"); | |
188 | warn_always_zero_bit(ctrl, TYPE2_SELECT, "select"); | |
189 | warn_always_zero_bit(ctrl, TYPE2_HIT, "hit"); | |
190 | ||
191 | /* validate size encoding */ | |
192 | size = type2_breakpoint_size(env, ctrl); | |
193 | if (access_size[size] == -1) { | |
194 | qemu_log_mask(LOG_UNIMP, "access size %d is not supported, using SIZE_ANY\n", | |
195 | size); | |
196 | } else { | |
197 | val |= (ctrl & TYPE2_SIZELO); | |
198 | if (riscv_cpu_mxl(env) == MXL_RV64) { | |
199 | val |= (ctrl & TYPE2_SIZEHI); | |
200 | } | |
201 | } | |
202 | ||
203 | /* keep the mode and attribute bits */ | |
204 | val |= (ctrl & (TYPE2_U | TYPE2_S | TYPE2_M | | |
205 | TYPE2_LOAD | TYPE2_STORE | TYPE2_EXEC)); | |
206 | ||
207 | return val; | |
208 | } | |
209 | ||
210 | static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index) | |
211 | { | |
212 | target_ulong ctrl = env->type2_trig[index].mcontrol; | |
213 | target_ulong addr = env->type2_trig[index].maddress; | |
214 | bool enabled = type2_breakpoint_enabled(ctrl); | |
215 | CPUState *cs = env_cpu(env); | |
216 | int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; | |
217 | uint32_t size; | |
218 | ||
219 | if (!enabled) { | |
220 | return; | |
221 | } | |
222 | ||
223 | if (ctrl & TYPE2_EXEC) { | |
224 | cpu_breakpoint_insert(cs, addr, flags, &env->type2_trig[index].bp); | |
225 | } | |
226 | ||
227 | if (ctrl & TYPE2_LOAD) { | |
228 | flags |= BP_MEM_READ; | |
229 | } | |
230 | if (ctrl & TYPE2_STORE) { | |
231 | flags |= BP_MEM_WRITE; | |
232 | } | |
233 | ||
234 | if (flags & BP_MEM_ACCESS) { | |
235 | size = type2_breakpoint_size(env, ctrl); | |
236 | if (size != 0) { | |
237 | cpu_watchpoint_insert(cs, addr, size, flags, | |
238 | &env->type2_trig[index].wp); | |
239 | } else { | |
240 | cpu_watchpoint_insert(cs, addr, 8, flags, | |
241 | &env->type2_trig[index].wp); | |
242 | } | |
243 | } | |
244 | } | |
245 | ||
246 | static void type2_breakpoint_remove(CPURISCVState *env, target_ulong index) | |
247 | { | |
248 | CPUState *cs = env_cpu(env); | |
249 | ||
250 | if (env->type2_trig[index].bp) { | |
251 | cpu_breakpoint_remove_by_ref(cs, env->type2_trig[index].bp); | |
252 | env->type2_trig[index].bp = NULL; | |
253 | } | |
254 | ||
255 | if (env->type2_trig[index].wp) { | |
256 | cpu_watchpoint_remove_by_ref(cs, env->type2_trig[index].wp); | |
257 | env->type2_trig[index].wp = NULL; | |
258 | } | |
259 | } | |
260 | ||
261 | static target_ulong type2_reg_read(CPURISCVState *env, | |
262 | target_ulong trigger_index, int tdata_index) | |
263 | { | |
264 | uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0; | |
265 | target_ulong tdata; | |
266 | ||
267 | switch (tdata_index) { | |
268 | case TDATA1: | |
269 | tdata = env->type2_trig[index].mcontrol; | |
270 | break; | |
271 | case TDATA2: | |
272 | tdata = env->type2_trig[index].maddress; | |
273 | break; | |
274 | default: | |
275 | g_assert_not_reached(); | |
276 | } | |
277 | ||
278 | return tdata; | |
279 | } | |
280 | ||
281 | static void type2_reg_write(CPURISCVState *env, target_ulong trigger_index, | |
282 | int tdata_index, target_ulong val) | |
283 | { | |
284 | uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0; | |
285 | target_ulong new_val; | |
286 | ||
287 | switch (tdata_index) { | |
288 | case TDATA1: | |
289 | new_val = type2_mcontrol_validate(env, val); | |
290 | if (new_val != env->type2_trig[index].mcontrol) { | |
291 | env->type2_trig[index].mcontrol = new_val; | |
292 | type2_breakpoint_remove(env, index); | |
293 | type2_breakpoint_insert(env, index); | |
294 | } | |
295 | break; | |
296 | case TDATA2: | |
297 | if (val != env->type2_trig[index].maddress) { | |
298 | env->type2_trig[index].maddress = val; | |
299 | type2_breakpoint_remove(env, index); | |
300 | type2_breakpoint_insert(env, index); | |
301 | } | |
302 | break; | |
303 | default: | |
304 | g_assert_not_reached(); | |
305 | } | |
306 | ||
307 | return; | |
308 | } | |
309 | ||
310 | typedef target_ulong (*tdata_read_func)(CPURISCVState *env, | |
311 | target_ulong trigger_index, | |
312 | int tdata_index); | |
313 | ||
314 | static tdata_read_func trigger_read_funcs[TRIGGER_NUM] = { | |
315 | [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_read, | |
316 | }; | |
317 | ||
318 | typedef void (*tdata_write_func)(CPURISCVState *env, | |
319 | target_ulong trigger_index, | |
320 | int tdata_index, | |
321 | target_ulong val); | |
322 | ||
323 | static tdata_write_func trigger_write_funcs[TRIGGER_NUM] = { | |
324 | [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_write, | |
325 | }; | |
326 | ||
327 | target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index) | |
328 | { | |
329 | tdata_read_func read_func = trigger_read_funcs[env->trigger_cur]; | |
330 | ||
331 | return read_func(env, env->trigger_cur, tdata_index); | |
332 | } | |
333 | ||
334 | void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val) | |
335 | { | |
336 | tdata_write_func write_func = trigger_write_funcs[env->trigger_cur]; | |
337 | ||
338 | return write_func(env, env->trigger_cur, tdata_index, val); | |
339 | } | |
b5f6379d BM |
340 | |
341 | void riscv_cpu_debug_excp_handler(CPUState *cs) | |
342 | { | |
343 | RISCVCPU *cpu = RISCV_CPU(cs); | |
344 | CPURISCVState *env = &cpu->env; | |
345 | ||
346 | if (cs->watchpoint_hit) { | |
347 | if (cs->watchpoint_hit->flags & BP_CPU) { | |
348 | cs->watchpoint_hit = NULL; | |
349 | riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0); | |
350 | } | |
351 | } else { | |
352 | if (cpu_breakpoint_test(cs, env->pc, BP_CPU)) { | |
353 | riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0); | |
354 | } | |
355 | } | |
356 | } | |
357 | ||
358 | bool riscv_cpu_debug_check_breakpoint(CPUState *cs) | |
359 | { | |
360 | RISCVCPU *cpu = RISCV_CPU(cs); | |
361 | CPURISCVState *env = &cpu->env; | |
362 | CPUBreakpoint *bp; | |
363 | target_ulong ctrl; | |
364 | target_ulong pc; | |
365 | int i; | |
366 | ||
367 | QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { | |
368 | for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { | |
369 | ctrl = env->type2_trig[i].mcontrol; | |
370 | pc = env->type2_trig[i].maddress; | |
371 | ||
372 | if ((ctrl & TYPE2_EXEC) && (bp->pc == pc)) { | |
373 | /* check U/S/M bit against current privilege level */ | |
374 | if ((ctrl >> 3) & BIT(env->priv)) { | |
375 | return true; | |
376 | } | |
377 | } | |
378 | } | |
379 | } | |
380 | ||
381 | return false; | |
382 | } | |
383 | ||
384 | bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) | |
385 | { | |
386 | RISCVCPU *cpu = RISCV_CPU(cs); | |
387 | CPURISCVState *env = &cpu->env; | |
388 | target_ulong ctrl; | |
389 | target_ulong addr; | |
390 | int flags; | |
391 | int i; | |
392 | ||
393 | for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { | |
394 | ctrl = env->type2_trig[i].mcontrol; | |
395 | addr = env->type2_trig[i].maddress; | |
396 | flags = 0; | |
397 | ||
398 | if (ctrl & TYPE2_LOAD) { | |
399 | flags |= BP_MEM_READ; | |
400 | } | |
401 | if (ctrl & TYPE2_STORE) { | |
402 | flags |= BP_MEM_WRITE; | |
403 | } | |
404 | ||
405 | if ((wp->flags & flags) && (wp->vaddr == addr)) { | |
406 | /* check U/S/M bit against current privilege level */ | |
407 | if ((ctrl >> 3) & BIT(env->priv)) { | |
408 | return true; | |
409 | } | |
410 | } | |
411 | } | |
412 | ||
413 | return false; | |
414 | } | |
b6092544 BM |
415 | |
416 | void riscv_trigger_init(CPURISCVState *env) | |
417 | { | |
418 | target_ulong type2 = trigger_type(env, TRIGGER_TYPE_AD_MATCH); | |
419 | int i; | |
420 | ||
421 | /* type 2 triggers */ | |
422 | for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { | |
423 | /* | |
424 | * type = TRIGGER_TYPE_AD_MATCH | |
425 | * dmode = 0 (both debug and M-mode can write tdata) | |
426 | * maskmax = 0 (unimplemented, always 0) | |
427 | * sizehi = 0 (match against any size, RV64 only) | |
428 | * hit = 0 (unimplemented, always 0) | |
429 | * select = 0 (always 0, perform match on address) | |
430 | * timing = 0 (always 0, trigger before instruction) | |
431 | * sizelo = 0 (match against any size) | |
432 | * action = 0 (always 0, raise a breakpoint exception) | |
433 | * chain = 0 (unimplemented, always 0) | |
434 | * match = 0 (always 0, when any compare value equals tdata2) | |
435 | */ | |
436 | env->type2_trig[i].mcontrol = type2; | |
437 | env->type2_trig[i].maddress = 0; | |
438 | env->type2_trig[i].bp = NULL; | |
439 | env->type2_trig[i].wp = NULL; | |
440 | } | |
441 | } |