diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index 397efdcdd463..1efc7e48f8e8 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -2239,6 +2239,76 @@ set_dbregs(p, dbregs) return (0); } +/* + * Return > 0 if a hardware breakpoint has been hit, and the + * breakpoint was in user space. Return 0, otherwise. + */ +int +user_dbreg_trap(void) +{ + u_int32_t dr7, dr6; /* debug registers dr6 and dr7 */ + u_int32_t bp; /* breakpoint bits extracted from dr6 */ + int nbp; /* number of breakpoints that triggered */ + caddr_t addr[4]; /* breakpoint addresses */ + int i; + + dr7 = rdr7(); + if ((dr7 & 0x000000ff) == 0) { + /* + * all GE and LE bits in the dr7 register are zero, + * thus the trap couldn't have been caused by the + * hardware debug registers + */ + return 0; + } + + nbp = 0; + dr6 = rdr6(); + bp = dr6 & 0x0000000f; + + if (!bp) { + /* + * None of the breakpoint bits are set meaning this + * trap was not caused by any of the debug registers + */ + return 0; + } + + /* + * at least one of the breakpoints were hit, check to see + * which ones and if any of them are user space addresses + */ + + if (bp & 0x01) { + addr[nbp++] = (caddr_t)rdr0(); + } + if (bp & 0x02) { + addr[nbp++] = (caddr_t)rdr1(); + } + if (bp & 0x04) { + addr[nbp++] = (caddr_t)rdr2(); + } + if (bp & 0x08) { + addr[nbp++] = (caddr_t)rdr3(); + } + + for (i=0; ipcb_flags & PCB_DBREGS) { + /* + * disable all hardware breakpoints + */ + reset_dbregs(); + pcb->pcb_flags &= ~PCB_DBREGS; + } cnt.v_swtch++; cpu_switch(p); panic("cpu_exit"); diff --git a/sys/amd64/include/cpufunc.h b/sys/amd64/include/cpufunc.h index 92e74ca6d068..e1968f04adf8 100644 --- a/sys/amd64/include/cpufunc.h +++ b/sys/amd64/include/cpufunc.h @@ -452,6 +452,54 @@ load_gs(u_int sel) __asm __volatile("movl %0,%%gs" : : "rm" (sel)); } +static __inline u_int +rdr0(void) +{ + u_int data; + __asm __volatile("movl %%dr0,%0" : "=rm" (data)); + return (data); +} + +static __inline u_int +rdr1(void) +{ + u_int data; + __asm __volatile("movl %%dr1,%0" : "=rm" (data)); + return (data); +} + +static __inline u_int +rdr2(void) +{ + u_int data; + __asm __volatile("movl %%dr2,%0" : "=rm" (data)); + return (data); +} + +static __inline u_int +rdr3(void) +{ + u_int data; + __asm __volatile("movl %%dr3,%0" : "=rm" (data)); + return (data); +} + +static __inline u_int +rdr6(void) +{ + u_int data; + __asm __volatile("movl %%dr6,%0" : "=rm" (data)); + return (data); +} + +static __inline u_int +rdr7(void) +{ + u_int data; + __asm __volatile("movl %%dr7,%0" : "=rm" (data)); + return (data); +} + #else /* !__GNUC__ */ int breakpoint __P((void)); @@ -497,5 +545,7 @@ void ltr __P((u_short sel)); u_int rcr0 __P((void)); u_int rcr3 __P((void)); u_int rcr4 __P((void)); +void load_dr6 __P((u_int dr6)); +void reset_dbregs __P((void)); #endif /* !_MACHINE_CPUFUNC_H_ */ diff --git a/sys/amd64/include/md_var.h b/sys/amd64/include/md_var.h index 1c968f6a7d94..1c3fe64e413b 100644 --- a/sys/amd64/include/md_var.h +++ b/sys/amd64/include/md_var.h @@ -95,6 +95,7 @@ void setidt __P((int idx, alias_for_inthand_t *func, int typ, int dpl, int selec)); void swi_vm __P((void)); void userconfig __P((void)); +int user_dbreg_trap __P((void)); int vm_page_zero_idle __P((void)); #endif /* !_MACHINE_MD_VAR_H_ */ diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index 397efdcdd463..1efc7e48f8e8 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -2239,6 +2239,76 @@ set_dbregs(p, dbregs) return (0); } +/* + * Return > 0 if a hardware breakpoint has been hit, and the + * breakpoint was in user space. Return 0, otherwise. + */ +int +user_dbreg_trap(void) +{ + u_int32_t dr7, dr6; /* debug registers dr6 and dr7 */ + u_int32_t bp; /* breakpoint bits extracted from dr6 */ + int nbp; /* number of breakpoints that triggered */ + caddr_t addr[4]; /* breakpoint addresses */ + int i; + + dr7 = rdr7(); + if ((dr7 & 0x000000ff) == 0) { + /* + * all GE and LE bits in the dr7 register are zero, + * thus the trap couldn't have been caused by the + * hardware debug registers + */ + return 0; + } + + nbp = 0; + dr6 = rdr6(); + bp = dr6 & 0x0000000f; + + if (!bp) { + /* + * None of the breakpoint bits are set meaning this + * trap was not caused by any of the debug registers + */ + return 0; + } + + /* + * at least one of the breakpoints were hit, check to see + * which ones and if any of them are user space addresses + */ + + if (bp & 0x01) { + addr[nbp++] = (caddr_t)rdr0(); + } + if (bp & 0x02) { + addr[nbp++] = (caddr_t)rdr1(); + } + if (bp & 0x04) { + addr[nbp++] = (caddr_t)rdr2(); + } + if (bp & 0x08) { + addr[nbp++] = (caddr_t)rdr3(); + } + + for (i=0; ipcb_flags & PCB_DBREGS) { + /* + * disable all hardware breakpoints + */ + reset_dbregs(); + pcb->pcb_flags &= ~PCB_DBREGS; + } cnt.v_swtch++; cpu_switch(p); panic("cpu_exit"); diff --git a/sys/i386/include/cpufunc.h b/sys/i386/include/cpufunc.h index 92e74ca6d068..e1968f04adf8 100644 --- a/sys/i386/include/cpufunc.h +++ b/sys/i386/include/cpufunc.h @@ -452,6 +452,54 @@ load_gs(u_int sel) __asm __volatile("movl %0,%%gs" : : "rm" (sel)); } +static __inline u_int +rdr0(void) +{ + u_int data; + __asm __volatile("movl %%dr0,%0" : "=rm" (data)); + return (data); +} + +static __inline u_int +rdr1(void) +{ + u_int data; + __asm __volatile("movl %%dr1,%0" : "=rm" (data)); + return (data); +} + +static __inline u_int +rdr2(void) +{ + u_int data; + __asm __volatile("movl %%dr2,%0" : "=rm" (data)); + return (data); +} + +static __inline u_int +rdr3(void) +{ + u_int data; + __asm __volatile("movl %%dr3,%0" : "=rm" (data)); + return (data); +} + +static __inline u_int +rdr6(void) +{ + u_int data; + __asm __volatile("movl %%dr6,%0" : "=rm" (data)); + return (data); +} + +static __inline u_int +rdr7(void) +{ + u_int data; + __asm __volatile("movl %%dr7,%0" : "=rm" (data)); + return (data); +} + #else /* !__GNUC__ */ int breakpoint __P((void)); @@ -497,5 +545,7 @@ void ltr __P((u_short sel)); u_int rcr0 __P((void)); u_int rcr3 __P((void)); u_int rcr4 __P((void)); +void load_dr6 __P((u_int dr6)); +void reset_dbregs __P((void)); #endif /* !_MACHINE_CPUFUNC_H_ */ diff --git a/sys/i386/include/md_var.h b/sys/i386/include/md_var.h index 1c968f6a7d94..1c3fe64e413b 100644 --- a/sys/i386/include/md_var.h +++ b/sys/i386/include/md_var.h @@ -95,6 +95,7 @@ void setidt __P((int idx, alias_for_inthand_t *func, int typ, int dpl, int selec)); void swi_vm __P((void)); void userconfig __P((void)); +int user_dbreg_trap __P((void)); int vm_page_zero_idle __P((void)); #endif /* !_MACHINE_MD_VAR_H_ */ diff --git a/sys/kern/subr_trap.c b/sys/kern/subr_trap.c index 4199346bd5a6..a8b73cf6a02b 100644 --- a/sys/kern/subr_trap.c +++ b/sys/kern/subr_trap.c @@ -519,8 +519,26 @@ kernel_trap: frame.tf_eflags &= ~PSL_T; return; } + /* + * Ignore debug register trace traps due to + * accesses in the user's address space, which + * can happen under several conditions such as + * if a user sets a watchpoint on a buffer and + * then passes that buffer to a system call. + * We still want to get TRCTRAPS for addresses + * in kernel space because that is useful when + * debugging the kernel. + */ + if (user_dbreg_trap()) { + /* + * Reset breakpoint bits because the + * processor doesn't + */ + load_dr6(rdr6() & 0xfffffff0); + return; + } /* - * Fall through. + * Fall through (TRCTRAP kernel mode, kernel address) */ case T_BPTFLT: /*