2 * Mach Operating System
3 * Copyright (c) 1991,1990 Carnegie Mellon University
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
16 * Carnegie Mellon requests users of this software to return to
18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
19 * School of Computer Science
20 * Carnegie Mellon University
21 * Pittsburgh PA 15213-3890
23 * any improvements or extensions that they make and grant Carnegie the
24 * rights to redistribute these changes.
29 #include <sys/types.h>
33 #include <sys/systm.h>
35 #include <machine/frame.h>
36 #include <machine/kdb.h>
37 #include <machine/md_var.h>
40 #include <ddb/db_sym.h>
44 #define MAXWATCHSIZE 8
46 #define MAXWATCHSIZE 4
50 * Set a watchpoint in the debug register denoted by 'watchnum'.
53 dbreg_set_watchreg(int watchnum, vm_offset_t watchaddr, vm_size_t size,
54 int access, struct dbreg *d)
58 MPASS(watchnum >= 0 && watchnum < NDBREGS);
60 /* size must be 1 for an execution breakpoint */
61 if (access == DBREG_DR7_EXEC)
65 * we can watch a 1, 2, or 4 byte sized location
69 len = DBREG_DR7_LEN_1;
72 len = DBREG_DR7_LEN_2;
75 len = DBREG_DR7_LEN_4;
79 len = DBREG_DR7_LEN_8;
86 /* clear the bits we are about to affect */
87 d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
89 /* set drN register to the address, N=watchnum */
90 DBREG_DRX(d, watchnum) = watchaddr;
92 /* enable the watchpoint */
93 d->dr[7] |= DBREG_DR7_SET(watchnum, len, access,
94 DBREG_DR7_GLOBAL_ENABLE);
98 * Remove a watchpoint from the debug register denoted by 'watchnum'.
101 dbreg_clr_watchreg(int watchnum, struct dbreg *d)
103 MPASS(watchnum >= 0 && watchnum < NDBREGS);
105 d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
106 DBREG_DRX(d, watchnum) = 0;
110 * Sync the debug registers. Other cores will read these values from the PCPU
111 * area when they resume. See amd64_db_resume_dbreg() below.
114 dbreg_sync(struct dbreg *dp)
120 cpu = PCPU_GET(cpuid);
125 memcpy(pc->pc_dbreg, dp, sizeof(*dp));
126 pc->pc_dbreg_cmd = PC_DBREG_CMD_LOAD;
132 dbreg_set_watchpoint(vm_offset_t addr, vm_size_t size, int access)
138 d = (struct dbreg *)PCPU_PTR(dbreg);
140 /* debug registers aren't stored in PCPU on i386. */
145 /* Validate the access type */
146 if (access != DBREG_DR7_EXEC && access != DBREG_DR7_WRONLY &&
147 access != DBREG_DR7_RDWR)
150 fill_dbregs(NULL, d);
153 * Check if there are enough available registers to cover the desired
157 for (i = 0; i < NDBREGS; i++) {
158 if (!DBREG_DR7_ENABLED(d->dr[7], i))
162 if (avail * MAXWATCHSIZE < size)
165 for (i = 0; i < NDBREGS && size > 0; i++) {
166 if (!DBREG_DR7_ENABLED(d->dr[7], i)) {
167 if ((size >= 8 || (avail == 1 && size > 4)) &&
174 dbreg_set_watchreg(i, addr, wsize, access, d);
188 dbreg_clr_watchpoint(vm_offset_t addr, vm_size_t size)
194 d = (struct dbreg *)PCPU_PTR(dbreg);
196 /* debug registers aren't stored in PCPU on i386. */
200 fill_dbregs(NULL, d);
202 for (i = 0; i < NDBREGS; i++) {
203 if (DBREG_DR7_ENABLED(d->dr[7], i)) {
204 if (DBREG_DRX((d), i) >= addr &&
205 DBREG_DRX((d), i) < addr + size)
206 dbreg_clr_watchreg(i, d);
218 watchtype_str(int type)
225 return ("read/write");
226 case DBREG_DR7_WRONLY:
234 dbreg_list_watchpoints(void)
239 fill_dbregs(NULL, &d);
241 db_printf("\nhardware watchpoints:\n");
242 db_printf(" watch status type len address\n");
243 db_printf(" ----- -------- ---------- --- ----------\n");
244 for (i = 0; i < NDBREGS; i++) {
245 if (DBREG_DR7_ENABLED(d.dr[7], i)) {
246 type = DBREG_DR7_ACCESS(d.dr[7], i);
247 len = DBREG_DR7_LEN(d.dr[7], i);
248 db_printf(" %-5d %-8s %10s %3d ",
249 i, "enabled", watchtype_str(type), len + 1);
250 db_printsym((db_addr_t)DBREG_DRX(&d, i), DB_STGY_ANY);
253 db_printf(" %-5d disabled\n", i);
260 /* Sync debug registers when resuming from debugger. */
262 amd64_db_resume_dbreg(void)
266 switch (PCPU_GET(dbreg_cmd)) {
267 case PC_DBREG_CMD_LOAD:
268 d = (struct dbreg *)PCPU_PTR(dbreg);
270 PCPU_SET(dbreg_cmd, PC_DBREG_CMD_NONE);
277 kdb_cpu_set_watchpoint(vm_offset_t addr, vm_size_t size, int access)
280 /* Convert the KDB access type */
282 case KDB_DBG_ACCESS_W:
283 access = DBREG_DR7_WRONLY;
285 case KDB_DBG_ACCESS_RW:
286 access = DBREG_DR7_RDWR;
288 case KDB_DBG_ACCESS_R:
289 /* FALLTHROUGH: read-only not supported */
294 return (dbreg_set_watchpoint(addr, size, access));
298 kdb_cpu_clr_watchpoint(vm_offset_t addr, vm_size_t size)
301 return (dbreg_clr_watchpoint(addr, size));