summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/amd64/amd64/machdep.c70
-rw-r--r--sys/amd64/amd64/support.S17
-rw-r--r--sys/amd64/amd64/support.s17
-rw-r--r--sys/amd64/amd64/trap.c20
-rw-r--r--sys/amd64/amd64/vm_machdep.c7
-rw-r--r--sys/amd64/include/cpufunc.h50
-rw-r--r--sys/amd64/include/md_var.h1
-rw-r--r--sys/i386/i386/machdep.c70
-rw-r--r--sys/i386/i386/support.s17
-rw-r--r--sys/i386/i386/trap.c20
-rw-r--r--sys/i386/i386/vm_machdep.c7
-rw-r--r--sys/i386/include/cpufunc.h50
-rw-r--r--sys/i386/include/md_var.h1
-rw-r--r--sys/kern/subr_trap.c20
14 files changed, 364 insertions, 3 deletions
diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c
index 397efdc..1efc7e4 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; i<nbp; i++) {
+ if (addr[i] <
+ (caddr_t)VM_MAXUSER_ADDRESS) {
+ /*
+ * addr[i] is in user space
+ */
+ return nbp;
+ }
+ }
+
+ /*
+ * None of the breakpoints are in user space.
+ */
+ return 0;
+}
+
+
#ifndef DDB
void
Debugger(const char *msg)
diff --git a/sys/amd64/amd64/support.S b/sys/amd64/amd64/support.S
index ae99af9..394848c 100644
--- a/sys/amd64/amd64/support.S
+++ b/sys/amd64/amd64/support.S
@@ -1586,6 +1586,23 @@ ENTRY(load_cr4)
movl %eax,%cr4
ret
+/* void load_dr6(u_int dr6) */
+ENTRY(load_dr6)
+ movl 4(%esp),%eax
+ movl %eax,%dr6
+ ret
+
+/* void reset_dbregs() */
+ENTRY(reset_dbregs)
+ movl $0,%eax
+ movl %eax,%dr7 /* disable all breapoints first */
+ movl %eax,%dr0
+ movl %eax,%dr1
+ movl %eax,%dr2
+ movl %eax,%dr3
+ movl %eax,%dr6
+ ret
+
/*****************************************************************************/
/* setjump, longjump */
/*****************************************************************************/
diff --git a/sys/amd64/amd64/support.s b/sys/amd64/amd64/support.s
index ae99af9..394848c 100644
--- a/sys/amd64/amd64/support.s
+++ b/sys/amd64/amd64/support.s
@@ -1586,6 +1586,23 @@ ENTRY(load_cr4)
movl %eax,%cr4
ret
+/* void load_dr6(u_int dr6) */
+ENTRY(load_dr6)
+ movl 4(%esp),%eax
+ movl %eax,%dr6
+ ret
+
+/* void reset_dbregs() */
+ENTRY(reset_dbregs)
+ movl $0,%eax
+ movl %eax,%dr7 /* disable all breapoints first */
+ movl %eax,%dr0
+ movl %eax,%dr1
+ movl %eax,%dr2
+ movl %eax,%dr3
+ movl %eax,%dr6
+ ret
+
/*****************************************************************************/
/* setjump, longjump */
/*****************************************************************************/
diff --git a/sys/amd64/amd64/trap.c b/sys/amd64/amd64/trap.c
index 4199346..a8b73cf 100644
--- a/sys/amd64/amd64/trap.c
+++ b/sys/amd64/amd64/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:
/*
diff --git a/sys/amd64/amd64/vm_machdep.c b/sys/amd64/amd64/vm_machdep.c
index d7fa73b1..cdcc278 100644
--- a/sys/amd64/amd64/vm_machdep.c
+++ b/sys/amd64/amd64/vm_machdep.c
@@ -246,6 +246,13 @@ cpu_exit(p)
#ifdef USER_LDT
user_ldt_free(pcb);
#endif
+ if (pcb->pcb_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 92e74ca..e1968f0 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 1c968f6..1c3fe64 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 397efdc..1efc7e4 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; i<nbp; i++) {
+ if (addr[i] <
+ (caddr_t)VM_MAXUSER_ADDRESS) {
+ /*
+ * addr[i] is in user space
+ */
+ return nbp;
+ }
+ }
+
+ /*
+ * None of the breakpoints are in user space.
+ */
+ return 0;
+}
+
+
#ifndef DDB
void
Debugger(const char *msg)
diff --git a/sys/i386/i386/support.s b/sys/i386/i386/support.s
index ae99af9..394848c 100644
--- a/sys/i386/i386/support.s
+++ b/sys/i386/i386/support.s
@@ -1586,6 +1586,23 @@ ENTRY(load_cr4)
movl %eax,%cr4
ret
+/* void load_dr6(u_int dr6) */
+ENTRY(load_dr6)
+ movl 4(%esp),%eax
+ movl %eax,%dr6
+ ret
+
+/* void reset_dbregs() */
+ENTRY(reset_dbregs)
+ movl $0,%eax
+ movl %eax,%dr7 /* disable all breapoints first */
+ movl %eax,%dr0
+ movl %eax,%dr1
+ movl %eax,%dr2
+ movl %eax,%dr3
+ movl %eax,%dr6
+ ret
+
/*****************************************************************************/
/* setjump, longjump */
/*****************************************************************************/
diff --git a/sys/i386/i386/trap.c b/sys/i386/i386/trap.c
index 4199346..a8b73cf 100644
--- a/sys/i386/i386/trap.c
+++ b/sys/i386/i386/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:
/*
diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c
index d7fa73b1..cdcc278 100644
--- a/sys/i386/i386/vm_machdep.c
+++ b/sys/i386/i386/vm_machdep.c
@@ -246,6 +246,13 @@ cpu_exit(p)
#ifdef USER_LDT
user_ldt_free(pcb);
#endif
+ if (pcb->pcb_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 92e74ca..e1968f0 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 1c968f6..1c3fe64 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 4199346..a8b73cf 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:
/*
OpenPOWER on IntegriCloud