diff --git a/sys/amd64/amd64/db_trace.c b/sys/amd64/amd64/db_trace.c --- a/sys/amd64/amd64/db_trace.c +++ b/sys/amd64/amd64/db_trace.c @@ -128,11 +128,6 @@ static void db_print_stack_entry(const char *, db_addr_t, void *); static void decode_syscall(int, struct thread *); -static const char * watchtype_str(int type); -int amd64_set_watch(int watchnum, unsigned long watchaddr, int size, - int access, struct dbreg *d); -int amd64_clr_watch(int watchnum, struct dbreg *d); - static void db_print_stack_entry(const char *name, db_addr_t callpc, void *frame) { @@ -391,223 +386,22 @@ } int -amd64_set_watch(watchnum, watchaddr, size, access, d) - int watchnum; - unsigned long watchaddr; - int size; - int access; - struct dbreg *d; -{ - int i, len; - - if (watchnum == -1) { - for (i = 0; i < 4; i++) - if (!DBREG_DR7_ENABLED(d->dr[7], i)) - break; - if (i < 4) - watchnum = i; - else - return (-1); - } - - switch (access) { - case DBREG_DR7_EXEC: - size = 1; /* size must be 1 for an execution breakpoint */ - /* fall through */ - case DBREG_DR7_WRONLY: - case DBREG_DR7_RDWR: - break; - default: - return (-1); - } - - /* - * we can watch a 1, 2, 4, or 8 byte sized location - */ - switch (size) { - case 1: - len = DBREG_DR7_LEN_1; - break; - case 2: - len = DBREG_DR7_LEN_2; - break; - case 4: - len = DBREG_DR7_LEN_4; - break; - case 8: - len = DBREG_DR7_LEN_8; - break; - default: - return (-1); - } - - /* clear the bits we are about to affect */ - d->dr[7] &= ~DBREG_DR7_MASK(watchnum); - - /* set drN register to the address, N=watchnum */ - DBREG_DRX(d, watchnum) = watchaddr; - - /* enable the watchpoint */ - d->dr[7] |= DBREG_DR7_SET(watchnum, len, access, - DBREG_DR7_GLOBAL_ENABLE); - - return (watchnum); -} - -int -amd64_clr_watch(watchnum, d) - int watchnum; - struct dbreg *d; -{ - - if (watchnum < 0 || watchnum >= 4) - return (-1); - - d->dr[7] &= ~DBREG_DR7_MASK(watchnum); - DBREG_DRX(d, watchnum) = 0; - - return (0); -} - -int -db_md_set_watchpoint(addr, size) - db_expr_t addr; - db_expr_t size; +db_md_set_watchpoint(db_expr_t addr, db_expr_t size) { - struct dbreg *d; - struct pcpu *pc; - int avail, c, cpu, i, wsize; - - d = (struct dbreg *)PCPU_PTR(dbreg); - cpu = PCPU_GET(cpuid); - fill_dbregs(NULL, d); - - avail = 0; - for (i = 0; i < 4; i++) { - if (!DBREG_DR7_ENABLED(d->dr[7], i)) - avail++; - } - - if (avail * 8 < size) - return (-1); - - for (i = 0; i < 4 && size > 0; i++) { - if (!DBREG_DR7_ENABLED(d->dr[7], i)) { - if (size >= 8 || (avail == 1 && size > 4)) - wsize = 8; - else if (size > 2) - wsize = 4; - else - wsize = size; - amd64_set_watch(i, addr, wsize, DBREG_DR7_WRONLY, d); - addr += wsize; - size -= wsize; - avail--; - } - } - - set_dbregs(NULL, d); - CPU_FOREACH(c) { - if (c == cpu) - continue; - pc = pcpu_find(c); - memcpy(pc->pc_dbreg, d, sizeof(*d)); - pc->pc_dbreg_cmd = PC_DBREG_CMD_LOAD; - } - return (0); + return (dbreg_set_watchpoint((vm_offset_t)addr, (vm_size_t)size)); } int -db_md_clr_watchpoint(addr, size) - db_expr_t addr; - db_expr_t size; +db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) { - struct dbreg *d; - struct pcpu *pc; - int i, c, cpu; - - d = (struct dbreg *)PCPU_PTR(dbreg); - cpu = PCPU_GET(cpuid); - fill_dbregs(NULL, d); - - for (i = 0; i < 4; i++) { - if (DBREG_DR7_ENABLED(d->dr[7], i)) { - if (DBREG_DRX((d), i) >= addr && - DBREG_DRX((d), i) < addr + size) - amd64_clr_watch(i, d); - } - } - - set_dbregs(NULL, d); - CPU_FOREACH(c) { - if (c == cpu) - continue; - pc = pcpu_find(c); - memcpy(pc->pc_dbreg, d, sizeof(*d)); - pc->pc_dbreg_cmd = PC_DBREG_CMD_LOAD; - } - return (0); -} - -static const char * -watchtype_str(type) - int type; -{ - switch (type) { - case DBREG_DR7_EXEC : return "execute"; break; - case DBREG_DR7_RDWR : return "read/write"; break; - case DBREG_DR7_WRONLY : return "write"; break; - default : return "invalid"; break; - } + return (dbreg_clr_watchpoint((vm_offset_t)addr, (vm_size_t)size)); } void db_md_list_watchpoints(void) { - struct dbreg d; - int i, len, type; - - fill_dbregs(NULL, &d); - - db_printf("\nhardware watchpoints:\n"); - db_printf(" watch status type len address\n"); - db_printf(" ----- -------- ---------- --- ------------------\n"); - for (i = 0; i < 4; i++) { - if (DBREG_DR7_ENABLED(d.dr[7], i)) { - type = DBREG_DR7_ACCESS(d.dr[7], i); - len = DBREG_DR7_LEN(d.dr[7], i); - if (len == DBREG_DR7_LEN_8) - len = 8; - else - len++; - db_printf(" %-5d %-8s %10s %3d ", - i, "enabled", watchtype_str(type), len); - db_printsym((db_addr_t)DBREG_DRX(&d, i), DB_STGY_ANY); - db_printf("\n"); - } else { - db_printf(" %-5d disabled\n", i); - } - } - db_printf("\ndebug register values:\n"); - for (i = 0; i < 8; i++) - if (i != 4 && i != 5) - db_printf(" dr%d 0x%016lx\n", i, DBREG_DRX(&d, i)); - db_printf("\n"); -} - -void -amd64_db_resume_dbreg(void) -{ - struct dbreg *d; - - switch (PCPU_GET(dbreg_cmd)) { - case PC_DBREG_CMD_LOAD: - d = (struct dbreg *)PCPU_PTR(dbreg); - set_dbregs(NULL, d); - PCPU_SET(dbreg_cmd, PC_DBREG_CMD_NONE); - break; - } + dbreg_list_watchpoints(); } diff --git a/sys/conf/files.x86 b/sys/conf/files.x86 --- a/sys/conf/files.x86 +++ b/sys/conf/files.x86 @@ -320,6 +320,7 @@ x86/x86/busdma_bounce.c standard x86/x86/busdma_machdep.c standard x86/x86/cpu_machdep.c standard +x86/x86/dbreg.c optional ddb x86/x86/dump_machdep.c standard x86/x86/fdt_machdep.c optional fdt x86/x86/identcpu.c standard diff --git a/sys/i386/i386/db_trace.c b/sys/i386/i386/db_trace.c --- a/sys/i386/i386/db_trace.c +++ b/sys/i386/i386/db_trace.c @@ -198,11 +198,6 @@ void *); static void decode_syscall(int, struct thread *); -static const char * watchtype_str(int type); -int i386_set_watch(int watchnum, unsigned int watchaddr, int size, int access, - struct dbreg *d); -int i386_clr_watch(int watchnum, struct dbreg *d); - /* * Figure out how many arguments were passed into the frame at "fp". */ @@ -618,180 +613,22 @@ } int -i386_set_watch(watchnum, watchaddr, size, access, d) - int watchnum; - unsigned int watchaddr; - int size; - int access; - struct dbreg *d; +db_md_set_watchpoint(db_expr_t addr, db_expr_t size) { - int i, len; - - if (watchnum == -1) { - for (i = 0; i < 4; i++) - if (!DBREG_DR7_ENABLED(d->dr[7], i)) - break; - if (i < 4) - watchnum = i; - else - return (-1); - } - - switch (access) { - case DBREG_DR7_EXEC: - size = 1; /* size must be 1 for an execution breakpoint */ - /* fall through */ - case DBREG_DR7_WRONLY: - case DBREG_DR7_RDWR: - break; - default: - return (-1); - } - /* - * we can watch a 1, 2, or 4 byte sized location - */ - switch (size) { - case 1: - len = DBREG_DR7_LEN_1; - break; - case 2: - len = DBREG_DR7_LEN_2; - break; - case 4: - len = DBREG_DR7_LEN_4; - break; - default: - return (-1); - } - - /* clear the bits we are about to affect */ - d->dr[7] &= ~DBREG_DR7_MASK(watchnum); - - /* set drN register to the address, N=watchnum */ - DBREG_DRX(d, watchnum) = watchaddr; - - /* enable the watchpoint */ - d->dr[7] |= DBREG_DR7_SET(watchnum, len, access, - DBREG_DR7_GLOBAL_ENABLE); - - return (watchnum); + return (dbreg_set_watchpoint((vm_offset_t)addr, (vm_size_t)size)); } int -i386_clr_watch(watchnum, d) - int watchnum; - struct dbreg *d; +db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) { - if (watchnum < 0 || watchnum >= 4) - return (-1); - - d->dr[7] &= ~DBREG_DR7_MASK(watchnum); - DBREG_DRX(d, watchnum) = 0; - - return (0); -} - -int -db_md_set_watchpoint(addr, size) - db_expr_t addr; - db_expr_t size; -{ - struct dbreg d; - int avail, i, wsize; - - fill_dbregs(NULL, &d); - - avail = 0; - for(i = 0; i < 4; i++) { - if (!DBREG_DR7_ENABLED(d.dr[7], i)) - avail++; - } - - if (avail * 4 < size) - return (-1); - - for (i = 0; i < 4 && (size > 0); i++) { - if (!DBREG_DR7_ENABLED(d.dr[7], i)) { - if (size > 2) - wsize = 4; - else - wsize = size; - i386_set_watch(i, addr, wsize, - DBREG_DR7_WRONLY, &d); - addr += wsize; - size -= wsize; - } - } - - set_dbregs(NULL, &d); - - return(0); -} - -int -db_md_clr_watchpoint(addr, size) - db_expr_t addr; - db_expr_t size; -{ - struct dbreg d; - int i; - - fill_dbregs(NULL, &d); - - for(i = 0; i < 4; i++) { - if (DBREG_DR7_ENABLED(d.dr[7], i)) { - if ((DBREG_DRX((&d), i) >= addr) && - (DBREG_DRX((&d), i) < addr+size)) - i386_clr_watch(i, &d); - } - } - - set_dbregs(NULL, &d); - - return(0); -} - -static const char * -watchtype_str(type) - int type; -{ - switch (type) { - case DBREG_DR7_EXEC : return "execute"; break; - case DBREG_DR7_RDWR : return "read/write"; break; - case DBREG_DR7_WRONLY : return "write"; break; - default : return "invalid"; break; - } + return (dbreg_clr_watchpoint((vm_offset_t)addr, (vm_size_t)size)); } void db_md_list_watchpoints(void) { - struct dbreg d; - int i, len, type; - - fill_dbregs(NULL, &d); - - db_printf("\nhardware watchpoints:\n"); - db_printf(" watch status type len address\n"); - db_printf(" ----- -------- ---------- --- ----------\n"); - for (i = 0; i < 4; i++) { - if (DBREG_DR7_ENABLED(d.dr[7], i)) { - type = DBREG_DR7_ACCESS(d.dr[7], i); - len = DBREG_DR7_LEN(d.dr[7], i); - db_printf(" %-5d %-8s %10s %3d ", - i, "enabled", watchtype_str(type), len + 1); - db_printsym((db_addr_t)DBREG_DRX(&d, i), DB_STGY_ANY); - db_printf("\n"); - } else { - db_printf(" %-5d disabled\n", i); - } - } - db_printf("\ndebug register values:\n"); - for (i = 0; i < 8; i++) - if (i != 4 && i != 5) - db_printf(" dr%d 0x%08x\n", i, DBREG_DRX(&d, i)); - db_printf("\n"); + dbreg_list_watchpoints(); } diff --git a/sys/x86/include/x86_var.h b/sys/x86/include/x86_var.h --- a/sys/x86/include/x86_var.h +++ b/sys/x86/include/x86_var.h @@ -120,6 +120,9 @@ bool cpu_mwait_usable(void); void cpu_probe_amdc1e(void); void cpu_setregs(void); +int dbreg_set_watchpoint(vm_offset_t addr, vm_size_t size); +int dbreg_clr_watchpoint(vm_offset_t addr, vm_size_t size); +void dbreg_list_watchpoints(void); bool disable_wp(void); void restore_wp(bool old_wp); void finishidentcpu(void); diff --git a/sys/x86/x86/dbreg.c b/sys/x86/x86/dbreg.c new file mode 100644 --- /dev/null +++ b/sys/x86/x86/dbreg.c @@ -0,0 +1,267 @@ +/*- + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#include "opt_ddb.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define NDBREGS 4 +#ifdef __amd64__ +#define MAXWATCHSIZE 8 +#else +#define MAXWATCHSIZE 4 +#endif + +/* + * Set a watchpoint in the debug register denoted by 'watchnum'. + */ +static void +dbreg_set_watchreg(int watchnum, vm_offset_t watchaddr, vm_size_t size, + int access, struct dbreg *d) +{ + int len; + + MPASS(watchnum >= 0 && watchnum < NDBREGS); + + /* size must be 1 for an execution breakpoint */ + if (access == DBREG_DR7_EXEC) + size = 1; + + /* + * we can watch a 1, 2, or 4 byte sized location + */ + switch (size) { + case 1: + len = DBREG_DR7_LEN_1; + break; + case 2: + len = DBREG_DR7_LEN_2; + break; + case 4: + len = DBREG_DR7_LEN_4; + break; +#if MAXWATCHSIZE >= 8 + case 8: + len = DBREG_DR7_LEN_8; + break; +#endif + default: + return; + } + + /* clear the bits we are about to affect */ + d->dr[7] &= ~DBREG_DR7_MASK(watchnum); + + /* set drN register to the address, N=watchnum */ + DBREG_DRX(d, watchnum) = watchaddr; + + /* enable the watchpoint */ + d->dr[7] |= DBREG_DR7_SET(watchnum, len, access, + DBREG_DR7_GLOBAL_ENABLE); +} + +/* + * Remove a watchpoint from the debug register denoted by 'watchnum'. + */ +static void +dbreg_clr_watchreg(int watchnum, struct dbreg *d) +{ + MPASS(watchnum >= 0 && watchnum < NDBREGS); + + d->dr[7] &= ~DBREG_DR7_MASK(watchnum); + DBREG_DRX(d, watchnum) = 0; +} + +/* + * Sync the debug registers. Other cores will read these values from the PCPU + * area when they resume. See amd64_db_resume_dbreg() below. + */ +static void +dbreg_sync(struct dbreg *dp) +{ +#ifdef __amd64__ + struct pcpu *pc; + int cpu, c; + + cpu = PCPU_GET(cpuid); + CPU_FOREACH(c) { + if (c == cpu) + continue; + pc = pcpu_find(c); + memcpy(pc->pc_dbreg, dp, sizeof(*dp)); + pc->pc_dbreg_cmd = PC_DBREG_CMD_LOAD; + } +#endif +} + +int +dbreg_set_watchpoint(vm_offset_t addr, vm_size_t size) +{ + struct dbreg *d; + int avail, i, wsize; + +#ifdef __amd64__ + d = (struct dbreg *)PCPU_PTR(dbreg); +#else + /* debug registers aren't stored in PCPU on i386. */ + struct dbreg d_temp; + d = &d_temp; +#endif + + fill_dbregs(NULL, d); + + /* + * Check if there are enough available registers to cover the desired + * area. + */ + avail = 0; + for (i = 0; i < NDBREGS; i++) { + if (!DBREG_DR7_ENABLED(d->dr[7], i)) + avail++; + } + + if (avail * MAXWATCHSIZE < size) + return (-1); + + for (i = 0; i < NDBREGS && size > 0; i++) { + if (!DBREG_DR7_ENABLED(d->dr[7], i)) { + if ((size >= 8 || (avail == 1 && size > 4)) && + MAXWATCHSIZE == 8) + wsize = 8; + else if (size > 2) + wsize = 4; + else + wsize = size; + dbreg_set_watchreg(i, addr, wsize, DBREG_DR7_WRONLY, d); + addr += wsize; + size -= wsize; + avail--; + } + } + + set_dbregs(NULL, d); + dbreg_sync(d); + + return (0); +} + +int +dbreg_clr_watchpoint(vm_offset_t addr, vm_size_t size) +{ + struct dbreg *d; + int i; + +#ifdef __amd64__ + d = (struct dbreg *)PCPU_PTR(dbreg); +#else + /* debug registers aren't stored in PCPU on i386. */ + struct dbreg d_temp; + d = &d_temp; +#endif + fill_dbregs(NULL, d); + + for (i = 0; i < NDBREGS; i++) { + if (DBREG_DR7_ENABLED(d->dr[7], i)) { + if (DBREG_DRX((d), i) >= addr && + DBREG_DRX((d), i) < addr + size) + dbreg_clr_watchreg(i, d); + } + } + + set_dbregs(NULL, d); + dbreg_sync(d); + + return (0); +} + +#ifdef DDB +static const char * +watchtype_str(int type) +{ + + switch (type) { + case DBREG_DR7_EXEC: + return ("execute"); + case DBREG_DR7_RDWR: + return ("read/write"); + case DBREG_DR7_WRONLY: + return ("write"); + default: + return ("invalid"); + } +} + +void +dbreg_list_watchpoints(void) +{ + struct dbreg d; + int i, len, type; + + fill_dbregs(NULL, &d); + + db_printf("\nhardware watchpoints:\n"); + db_printf(" watch status type len address\n"); + db_printf(" ----- -------- ---------- --- ----------\n"); + for (i = 0; i < NDBREGS; i++) { + if (DBREG_DR7_ENABLED(d.dr[7], i)) { + type = DBREG_DR7_ACCESS(d.dr[7], i); + len = DBREG_DR7_LEN(d.dr[7], i); + db_printf(" %-5d %-8s %10s %3d ", + i, "enabled", watchtype_str(type), len + 1); + db_printsym((db_addr_t)DBREG_DRX(&d, i), DB_STGY_ANY); + db_printf("\n"); + } else { + db_printf(" %-5d disabled\n", i); + } + } +} +#endif + +#ifdef __amd64__ +/* Sync debug registers when resuming from debugger. */ +void +amd64_db_resume_dbreg(void) +{ + struct dbreg *d; + + switch (PCPU_GET(dbreg_cmd)) { + case PC_DBREG_CMD_LOAD: + d = (struct dbreg *)PCPU_PTR(dbreg); + set_dbregs(NULL, d); + PCPU_SET(dbreg_cmd, PC_DBREG_CMD_NONE); + break; + } +} +#endif