1 /*-
2 * Copyright (c) 2014 The FreeBSD Foundation
3 *
4 * This software was developed by Semihalf under
5 * the sponsorship of the FreeBSD Foundation.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include "opt_ddb.h"
30 #include "opt_gdb.h"
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/kdb.h>
38 #include <sys/pcpu.h>
39 #include <sys/proc.h>
40 #include <sys/systm.h>
41 #include <sys/sysent.h>
42
43 #include <machine/armreg.h>
44 #include <machine/cpu.h>
45 #include <machine/debug_monitor.h>
46 #include <machine/kdb.h>
47
48 #ifdef DDB
49 #include <ddb/ddb.h>
50 #include <ddb/db_sym.h>
51 #endif
52
53 enum dbg_t {
54 DBG_TYPE_BREAKPOINT = 0,
55 DBG_TYPE_WATCHPOINT = 1,
56 };
57
58 static int dbg_watchpoint_num;
59 static int dbg_breakpoint_num;
60 static struct debug_monitor_state kernel_monitor = {
61 .dbg_flags = DBGMON_KERNEL
62 };
63
64 /* Called from the exception handlers */
65 void dbg_monitor_enter(struct thread *);
66 void dbg_monitor_exit(struct thread *, struct trapframe *);
67
68 /* Watchpoints/breakpoints control register bitfields */
69 #define DBG_WATCH_CTRL_LEN_1 (0x1 << 5)
70 #define DBG_WATCH_CTRL_LEN_2 (0x3 << 5)
71 #define DBG_WATCH_CTRL_LEN_4 (0xf << 5)
72 #define DBG_WATCH_CTRL_LEN_8 (0xff << 5)
73 #define DBG_WATCH_CTRL_LEN_MASK(x) ((x) & (0xff << 5))
74 #define DBG_WATCH_CTRL_EXEC (0x0 << 3)
75 #define DBG_WATCH_CTRL_LOAD (0x1 << 3)
76 #define DBG_WATCH_CTRL_STORE (0x2 << 3)
77 #define DBG_WATCH_CTRL_ACCESS_MASK(x) ((x) & (0x3 << 3))
78
79 /* Common for breakpoint and watchpoint */
80 #define DBG_WB_CTRL_EL1 (0x1 << 1)
81 #define DBG_WB_CTRL_EL0 (0x2 << 1)
82 #define DBG_WB_CTRL_ELX_MASK(x) ((x) & (0x3 << 1))
83 #define DBG_WB_CTRL_E (0x1 << 0)
84
85 #define DBG_REG_BASE_BVR 0
86 #define DBG_REG_BASE_BCR (DBG_REG_BASE_BVR + 16)
87 #define DBG_REG_BASE_WVR (DBG_REG_BASE_BCR + 16)
88 #define DBG_REG_BASE_WCR (DBG_REG_BASE_WVR + 16)
89
90 /* Watchpoint/breakpoint helpers */
91 #define DBG_WB_WVR "wvr"
92 #define DBG_WB_WCR "wcr"
93 #define DBG_WB_BVR "bvr"
94 #define DBG_WB_BCR "bcr"
95
96 #define DBG_WB_READ(reg, num, val) do { \
97 __asm __volatile("mrs %0, dbg" reg #num "_el1" : "=r" (val)); \
98 } while (0)
99
100 #define DBG_WB_WRITE(reg, num, val) do { \
101 __asm __volatile("msr dbg" reg #num "_el1, %0" :: "r" (val)); \
102 } while (0)
103
104 #define READ_WB_REG_CASE(reg, num, offset, val) \
105 case (num + offset): \
106 DBG_WB_READ(reg, num, val); \
107 break
108
109 #define WRITE_WB_REG_CASE(reg, num, offset, val) \
110 case (num + offset): \
111 DBG_WB_WRITE(reg, num, val); \
112 break
113
114 #define SWITCH_CASES_READ_WB_REG(reg, offset, val) \
115 READ_WB_REG_CASE(reg, 0, offset, val); \
116 READ_WB_REG_CASE(reg, 1, offset, val); \
117 READ_WB_REG_CASE(reg, 2, offset, val); \
118 READ_WB_REG_CASE(reg, 3, offset, val); \
119 READ_WB_REG_CASE(reg, 4, offset, val); \
120 READ_WB_REG_CASE(reg, 5, offset, val); \
121 READ_WB_REG_CASE(reg, 6, offset, val); \
122 READ_WB_REG_CASE(reg, 7, offset, val); \
123 READ_WB_REG_CASE(reg, 8, offset, val); \
124 READ_WB_REG_CASE(reg, 9, offset, val); \
125 READ_WB_REG_CASE(reg, 10, offset, val); \
126 READ_WB_REG_CASE(reg, 11, offset, val); \
127 READ_WB_REG_CASE(reg, 12, offset, val); \
128 READ_WB_REG_CASE(reg, 13, offset, val); \
129 READ_WB_REG_CASE(reg, 14, offset, val); \
130 READ_WB_REG_CASE(reg, 15, offset, val)
131
132 #define SWITCH_CASES_WRITE_WB_REG(reg, offset, val) \
133 WRITE_WB_REG_CASE(reg, 0, offset, val); \
134 WRITE_WB_REG_CASE(reg, 1, offset, val); \
135 WRITE_WB_REG_CASE(reg, 2, offset, val); \
136 WRITE_WB_REG_CASE(reg, 3, offset, val); \
137 WRITE_WB_REG_CASE(reg, 4, offset, val); \
138 WRITE_WB_REG_CASE(reg, 5, offset, val); \
139 WRITE_WB_REG_CASE(reg, 6, offset, val); \
140 WRITE_WB_REG_CASE(reg, 7, offset, val); \
141 WRITE_WB_REG_CASE(reg, 8, offset, val); \
142 WRITE_WB_REG_CASE(reg, 9, offset, val); \
143 WRITE_WB_REG_CASE(reg, 10, offset, val); \
144 WRITE_WB_REG_CASE(reg, 11, offset, val); \
145 WRITE_WB_REG_CASE(reg, 12, offset, val); \
146 WRITE_WB_REG_CASE(reg, 13, offset, val); \
147 WRITE_WB_REG_CASE(reg, 14, offset, val); \
148 WRITE_WB_REG_CASE(reg, 15, offset, val)
149
150 #ifdef DDB
151 static uint64_t
152 dbg_wb_read_reg(int reg, int n)
153 {
154 uint64_t val = 0;
155
156 switch (reg + n) {
157 SWITCH_CASES_READ_WB_REG(DBG_WB_WVR, DBG_REG_BASE_WVR, val);
158 SWITCH_CASES_READ_WB_REG(DBG_WB_WCR, DBG_REG_BASE_WCR, val);
159 SWITCH_CASES_READ_WB_REG(DBG_WB_BVR, DBG_REG_BASE_BVR, val);
160 SWITCH_CASES_READ_WB_REG(DBG_WB_BCR, DBG_REG_BASE_BCR, val);
161 default:
162 printf("trying to read from wrong debug register %d\n", n);
163 }
164
165 return val;
166 }
167 #endif /* DDB */
168
169 static void
170 dbg_wb_write_reg(int reg, int n, uint64_t val)
171 {
172 switch (reg + n) {
173 SWITCH_CASES_WRITE_WB_REG(DBG_WB_WVR, DBG_REG_BASE_WVR, val);
174 SWITCH_CASES_WRITE_WB_REG(DBG_WB_WCR, DBG_REG_BASE_WCR, val);
175 SWITCH_CASES_WRITE_WB_REG(DBG_WB_BVR, DBG_REG_BASE_BVR, val);
176 SWITCH_CASES_WRITE_WB_REG(DBG_WB_BCR, DBG_REG_BASE_BCR, val);
177 default:
178 printf("trying to write to wrong debug register %d\n", n);
179 return;
180 }
181 isb();
182 }
183
184 #if defined(DDB) || defined(GDB)
185 void
186 kdb_cpu_set_singlestep(void)
187 {
188
189 KASSERT((READ_SPECIALREG(daif) & PSR_D) == PSR_D,
190 ("%s: debug exceptions are not masked", __func__));
191
192 kdb_frame->tf_spsr |= PSR_SS;
193 WRITE_SPECIALREG(mdscr_el1, READ_SPECIALREG(mdscr_el1) |
194 MDSCR_SS | MDSCR_KDE);
195
196 /*
197 * Disable breakpoints and watchpoints, e.g. stepping
198 * over watched instruction will trigger break exception instead of
199 * single-step exception and locks CPU on that instruction for ever.
200 */
201 if ((kernel_monitor.dbg_flags & DBGMON_ENABLED) != 0) {
202 WRITE_SPECIALREG(mdscr_el1,
203 READ_SPECIALREG(mdscr_el1) & ~MDSCR_MDE);
204 }
205 }
206
207 void
208 kdb_cpu_clear_singlestep(void)
209 {
210
211 KASSERT((READ_SPECIALREG(daif) & PSR_D) == PSR_D,
212 ("%s: debug exceptions are not masked", __func__));
213
214 WRITE_SPECIALREG(mdscr_el1, READ_SPECIALREG(mdscr_el1) &
215 ~(MDSCR_SS | MDSCR_KDE));
216
217 /* Restore breakpoints and watchpoints */
218 if ((kernel_monitor.dbg_flags & DBGMON_ENABLED) != 0) {
219 WRITE_SPECIALREG(mdscr_el1,
220 READ_SPECIALREG(mdscr_el1) | MDSCR_MDE);
221
222 if ((kernel_monitor.dbg_flags & DBGMON_KERNEL) != 0) {
223 WRITE_SPECIALREG(mdscr_el1,
224 READ_SPECIALREG(mdscr_el1) | MDSCR_KDE);
225 }
226 }
227 }
228
229 int
230 kdb_cpu_set_watchpoint(vm_offset_t addr, vm_size_t size, int access)
231 {
232 enum dbg_access_t dbg_access;
233
234 switch (access) {
235 case KDB_DBG_ACCESS_R:
236 dbg_access = HW_BREAKPOINT_R;
237 break;
238 case KDB_DBG_ACCESS_W:
239 dbg_access = HW_BREAKPOINT_W;
240 break;
241 case KDB_DBG_ACCESS_RW:
242 dbg_access = HW_BREAKPOINT_RW;
243 break;
244 default:
245 return (EINVAL);
246 }
247
248 return (dbg_setup_watchpoint(NULL, addr, size, dbg_access));
249 }
250
251 int
252 kdb_cpu_clr_watchpoint(vm_offset_t addr, vm_size_t size)
253 {
254
255 return (dbg_remove_watchpoint(NULL, addr, size));
256 }
257 #endif /* DDB || GDB */
258
259 #ifdef DDB
260 static const char *
261 dbg_watchtype_str(uint32_t type)
262 {
263 switch (type) {
264 case DBG_WATCH_CTRL_EXEC:
265 return ("execute");
266 case DBG_WATCH_CTRL_STORE:
267 return ("write");
268 case DBG_WATCH_CTRL_LOAD:
269 return ("read");
270 case DBG_WATCH_CTRL_LOAD | DBG_WATCH_CTRL_STORE:
271 return ("read/write");
272 default:
273 return ("invalid");
274 }
275 }
276
277 static int
278 dbg_watchtype_len(uint32_t len)
279 {
280 switch (len) {
281 case DBG_WATCH_CTRL_LEN_1:
282 return (1);
283 case DBG_WATCH_CTRL_LEN_2:
284 return (2);
285 case DBG_WATCH_CTRL_LEN_4:
286 return (4);
287 case DBG_WATCH_CTRL_LEN_8:
288 return (8);
289 default:
290 return (0);
291 }
292 }
293
294 void
295 dbg_show_watchpoint(void)
296 {
297 uint32_t wcr, len, type;
298 uint64_t addr;
299 int i;
300
301 db_printf("\nhardware watchpoints:\n");
302 db_printf(" watch status type len address symbol\n");
303 db_printf(" ----- -------- ---------- --- ------------------ ------------------\n");
304 for (i = 0; i < dbg_watchpoint_num; i++) {
305 wcr = dbg_wb_read_reg(DBG_REG_BASE_WCR, i);
306 if ((wcr & DBG_WB_CTRL_E) != 0) {
307 type = DBG_WATCH_CTRL_ACCESS_MASK(wcr);
308 len = DBG_WATCH_CTRL_LEN_MASK(wcr);
309 addr = dbg_wb_read_reg(DBG_REG_BASE_WVR, i);
310 db_printf(" %-5d %-8s %10s %3d 0x%16lx ",
311 i, "enabled", dbg_watchtype_str(type),
312 dbg_watchtype_len(len), addr);
313 db_printsym((db_addr_t)addr, DB_STGY_ANY);
314 db_printf("\n");
315 } else {
316 db_printf(" %-5d disabled\n", i);
317 }
318 }
319 }
320 #endif /* DDB */
321
322 static int
323 dbg_find_free_slot(struct debug_monitor_state *monitor, enum dbg_t type)
324 {
325 uint64_t *reg;
326 u_int max, i;
327
328 switch(type) {
329 case DBG_TYPE_BREAKPOINT:
330 max = dbg_breakpoint_num;
331 reg = monitor->dbg_bcr;
332 break;
333 case DBG_TYPE_WATCHPOINT:
334 max = dbg_watchpoint_num;
335 reg = monitor->dbg_wcr;
336 break;
337 default:
338 printf("Unsupported debug type\n");
339 return (i);
340 }
341
342 for (i = 0; i < max; i++) {
343 if ((reg[i] & DBG_WB_CTRL_E) == 0)
344 return (i);
345 }
346
347 return (-1);
348 }
349
350 static int
351 dbg_find_slot(struct debug_monitor_state *monitor, enum dbg_t type,
352 vm_offset_t addr)
353 {
354 uint64_t *reg_addr, *reg_ctrl;
355 u_int max, i;
356
357 switch(type) {
358 case DBG_TYPE_BREAKPOINT:
359 max = dbg_breakpoint_num;
360 reg_addr = monitor->dbg_bvr;
361 reg_ctrl = monitor->dbg_bcr;
362 break;
363 case DBG_TYPE_WATCHPOINT:
364 max = dbg_watchpoint_num;
365 reg_addr = monitor->dbg_wvr;
366 reg_ctrl = monitor->dbg_wcr;
367 break;
368 default:
369 printf("Unsupported debug type\n");
370 return (i);
371 }
372
373 for (i = 0; i < max; i++) {
374 if (reg_addr[i] == addr &&
375 (reg_ctrl[i] & DBG_WB_CTRL_E) != 0)
376 return (i);
377 }
378
379 return (-1);
380 }
381
382 int
383 dbg_setup_watchpoint(struct debug_monitor_state *monitor, vm_offset_t addr,
384 vm_size_t size, enum dbg_access_t access)
385 {
386 uint64_t wcr_size, wcr_priv, wcr_access;
387 u_int i;
388
389 if (monitor == NULL)
390 monitor = &kernel_monitor;
391
392 i = dbg_find_free_slot(monitor, DBG_TYPE_WATCHPOINT);
393 if (i == -1) {
394 printf("Can not find slot for watchpoint, max %d"
395 " watchpoints supported\n", dbg_watchpoint_num);
396 return (EBUSY);
397 }
398
399 switch(size) {
400 case 1:
401 wcr_size = DBG_WATCH_CTRL_LEN_1;
402 break;
403 case 2:
404 wcr_size = DBG_WATCH_CTRL_LEN_2;
405 break;
406 case 4:
407 wcr_size = DBG_WATCH_CTRL_LEN_4;
408 break;
409 case 8:
410 wcr_size = DBG_WATCH_CTRL_LEN_8;
411 break;
412 default:
413 printf("Unsupported address size for watchpoint: %zu\n", size);
414 return (EINVAL);
415 }
416
417 if ((monitor->dbg_flags & DBGMON_KERNEL) == 0)
418 wcr_priv = DBG_WB_CTRL_EL0;
419 else
420 wcr_priv = DBG_WB_CTRL_EL1;
421
422 switch(access) {
423 case HW_BREAKPOINT_X:
424 wcr_access = DBG_WATCH_CTRL_EXEC;
425 break;
426 case HW_BREAKPOINT_R:
427 wcr_access = DBG_WATCH_CTRL_LOAD;
428 break;
429 case HW_BREAKPOINT_W:
430 wcr_access = DBG_WATCH_CTRL_STORE;
431 break;
432 case HW_BREAKPOINT_RW:
433 wcr_access = DBG_WATCH_CTRL_LOAD | DBG_WATCH_CTRL_STORE;
434 break;
435 default:
436 printf("Unsupported access type for watchpoint: %d\n", access);
437 return (EINVAL);
438 }
439
440 monitor->dbg_wvr[i] = addr;
441 monitor->dbg_wcr[i] = wcr_size | wcr_access | wcr_priv | DBG_WB_CTRL_E;
442 monitor->dbg_enable_count++;
443 monitor->dbg_flags |= DBGMON_ENABLED;
444
445 dbg_register_sync(monitor);
446 return (0);
447 }
448
449 int
450 dbg_remove_watchpoint(struct debug_monitor_state *monitor, vm_offset_t addr,
451 vm_size_t size)
452 {
453 u_int i;
454
455 if (monitor == NULL)
456 monitor = &kernel_monitor;
457
458 i = dbg_find_slot(monitor, DBG_TYPE_WATCHPOINT, addr);
459 if (i == -1) {
460 printf("Can not find watchpoint for address 0%lx\n", addr);
461 return (EINVAL);
462 }
463
464 monitor->dbg_wvr[i] = 0;
465 monitor->dbg_wcr[i] = 0;
466 monitor->dbg_enable_count--;
467 if (monitor->dbg_enable_count == 0)
468 monitor->dbg_flags &= ~DBGMON_ENABLED;
469
470 dbg_register_sync(monitor);
471 return (0);
472 }
473
474 void
475 dbg_register_sync(struct debug_monitor_state *monitor)
476 {
477 uint64_t mdscr;
478 int i;
479
480 if (monitor == NULL)
481 monitor = &kernel_monitor;
482
483 mdscr = READ_SPECIALREG(mdscr_el1);
484 if ((monitor->dbg_flags & DBGMON_ENABLED) == 0) {
485 mdscr &= ~(MDSCR_MDE | MDSCR_KDE);
486 } else {
487 for (i = 0; i < dbg_breakpoint_num; i++) {
488 dbg_wb_write_reg(DBG_REG_BASE_BCR, i,
489 monitor->dbg_bcr[i]);
490 dbg_wb_write_reg(DBG_REG_BASE_BVR, i,
491 monitor->dbg_bvr[i]);
492 }
493
494 for (i = 0; i < dbg_watchpoint_num; i++) {
495 dbg_wb_write_reg(DBG_REG_BASE_WCR, i,
496 monitor->dbg_wcr[i]);
497 dbg_wb_write_reg(DBG_REG_BASE_WVR, i,
498 monitor->dbg_wvr[i]);
499 }
500 mdscr |= MDSCR_MDE;
501 if ((monitor->dbg_flags & DBGMON_KERNEL) == DBGMON_KERNEL)
502 mdscr |= MDSCR_KDE;
503 }
504 WRITE_SPECIALREG(mdscr_el1, mdscr);
505 isb();
506 }
507
508 void
509 dbg_monitor_init(void)
510 {
511 uint64_t aa64dfr0;
512 u_int i;
513
514 /* Find out many breakpoints and watchpoints we can use */
515 aa64dfr0 = READ_SPECIALREG(id_aa64dfr0_el1);
516 dbg_watchpoint_num = ID_AA64DFR0_WRPs_VAL(aa64dfr0);
517 dbg_breakpoint_num = ID_AA64DFR0_BRPs_VAL(aa64dfr0);
518
519 if (bootverbose && PCPU_GET(cpuid) == 0) {
520 printf("%d watchpoints and %d breakpoints supported\n",
521 dbg_watchpoint_num, dbg_breakpoint_num);
522 }
523
524 /*
525 * We have limited number of {watch,break}points, each consists of
526 * two registers:
527 * - wcr/bcr regsiter configurates corresponding {watch,break}point
528 * behaviour
529 * - wvr/bvr register keeps address we are hunting for
530 *
531 * Reset all breakpoints and watchpoints.
532 */
533 for (i = 0; i < dbg_watchpoint_num; i++) {
534 dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0);
535 dbg_wb_write_reg(DBG_REG_BASE_WVR, i, 0);
536 }
537
538 for (i = 0; i < dbg_breakpoint_num; i++) {
539 dbg_wb_write_reg(DBG_REG_BASE_BCR, i, 0);
540 dbg_wb_write_reg(DBG_REG_BASE_BVR, i, 0);
541 }
542
543 dbg_enable();
544 }
545
546 void
547 dbg_monitor_enter(struct thread *thread)
548 {
549 int i;
550
551 if ((kernel_monitor.dbg_flags & DBGMON_ENABLED) != 0) {
552 /* Install the kernel version of the registers */
553 dbg_register_sync(&kernel_monitor);
554 } else if ((thread->td_pcb->pcb_dbg_regs.dbg_flags & DBGMON_ENABLED) != 0) {
555 /* Disable the user breakpoints until we return to userspace */
556 for (i = 0; i < dbg_watchpoint_num; i++) {
557 dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0);
558 dbg_wb_write_reg(DBG_REG_BASE_WVR, i, 0);
559 }
560
561 for (i = 0; i < dbg_breakpoint_num; ++i) {
562 dbg_wb_write_reg(DBG_REG_BASE_BCR, i, 0);
563 dbg_wb_write_reg(DBG_REG_BASE_BVR, i, 0);
564 }
565 WRITE_SPECIALREG(mdscr_el1,
566 READ_SPECIALREG(mdscr_el1) & ~(MDSCR_MDE | MDSCR_KDE));
567 isb();
568 }
569 }
570
571 void
572 dbg_monitor_exit(struct thread *thread, struct trapframe *frame)
573 {
574 int i;
575
576 /*
577 * PSR_D is an aarch64-only flag. On aarch32, it switches
578 * the processor to big-endian, so avoid setting it for
579 * 32bits binaries.
580 */
581 if (!(SV_PROC_FLAG(thread->td_proc, SV_ILP32)))
582 frame->tf_spsr |= PSR_D;
583 if ((thread->td_pcb->pcb_dbg_regs.dbg_flags & DBGMON_ENABLED) != 0) {
584 /* Install the thread's version of the registers */
585 dbg_register_sync(&thread->td_pcb->pcb_dbg_regs);
586 frame->tf_spsr &= ~PSR_D;
587 } else if ((kernel_monitor.dbg_flags & DBGMON_ENABLED) != 0) {
588 /* Disable the kernel breakpoints until we re-enter */
589 for (i = 0; i < dbg_watchpoint_num; i++) {
590 dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0);
591 dbg_wb_write_reg(DBG_REG_BASE_WVR, i, 0);
592 }
593
594 for (i = 0; i < dbg_breakpoint_num; ++i) {
595 dbg_wb_write_reg(DBG_REG_BASE_BCR, i, 0);
596 dbg_wb_write_reg(DBG_REG_BASE_BVR, i, 0);
597 }
598 WRITE_SPECIALREG(mdscr_el1,
599 READ_SPECIALREG(mdscr_el1) & ~(MDSCR_MDE | MDSCR_KDE));
600 isb();
601 }
602 }
Cache object: d515af335b5855050c724dd456a8ef6c
|