Introduce PT_GETDBREGS and PT_SETDBREGS in ptrace(2) on i386 and amd64 trunk
authorkamil <kamil@NetBSD.org>
Thu, 23 Feb 2017 03:34:22 +0000
branchtrunk
changeset 248941 cc110d240bd0
parent 248940 2f59cda44228
child 248942 3f87adbac767
Introduce PT_GETDBREGS and PT_SETDBREGS in ptrace(2) on i386 and amd64 This interface is modeled after FreeBSD API with the usage. This replaced previous watchpoint API. The previous one was introduced recently in NetBSD-current and remove its spurs without any backward-compatibility. Design choices for Debug Register accessors: - exec() (TRAP_EXEC event) must remove debug registers from LWP - debug registers are only per-LWP, not per-process globally - debug registers must not be inherited after (v)forking a process - debug registers must not be inherited after forking a thread - a debugger is responsible to set global watchpoints/breakpoints with the debug registers, to achieve this PTRACE_LWP_CREATE/PTRACE_LWP_EXIT event monitoring function is designed to be used - debug register traps must generate SIGTRAP with si_code TRAP_DBREG - debugger is responsible to retrieve debug register state to distinguish the exact debug register trap (DR6 is Status Register on x86) - kernel must not remove debug register traps after triggering a trap event a debugger is responsible to detach this trap with appropriate PT_SETDBREGS call (DR7 is Control Register on x86) - debug registers must not be exposed in mcontext - userland must not be allowed to set a trap on the kernel Implementation notes on i386 and amd64: - the initial state of debug register is retrieved on boot and this value is stored in a local copy (initdbregs), this value is used to initialize dbreg context after PT_GETDBREGS - struct dbregs is stored in pcb as a pointer and by default not initialized - reserved registers (DR4-DR5, DR9-DR15) are ignored Further ideas: - restrict this interface with securelevel Tested on real hardware i386 (Intel Pentium IV) and amd64 (Intel i7). This commit enables 390 debug register ATF tests in kernel/arch/x86. All tests are passing. This commit does not cover netbsd32 compat code. Currently other interface PT_GET_SIGINFO/PT_SET_SIGINFO is required in netbsd32 compat code in order to validate reliably PT_GETDBREGS/PT_SETDBREGS. This implementation does not cover FreeBSD specific defines in their <x86/reg.h>: DBREG_DR7_LOCAL_ENABLE, DBREG_DR7_GLOBAL_ENABLE, DBREG_DR7_LEN_1 etc. These values tend to be reinvented by each tracer on its own. GNU Debugger (GDB) works with NetBSD debug registers after adding this patch: --- gdb/amd64bsd-nat.c.orig 2016-02-10 03:19:39.000000000 +0000 +++ gdb/amd64bsd-nat.c @@ -167,6 +167,10 @@ amd64bsd_target (void) #ifdef HAVE_PT_GETDBREGS +#ifndef DBREG_DRX +#define DBREG_DRX(d,x) ((d)->dr[(x)]) +#endif + static unsigned long amd64bsd_dr_get (ptid_t ptid, int regnum) { Another reason to stop introducing unpopular defines covering machine specific register macros is that these value varies across generations of the same CPU family. GDB demo: (gdb) c Continuing. Watchpoint 2: traceme Old value = 0 New value = 16 main (argc=1, argv=0x7f7fff79fe30) at test.c:8 8 printf("traceme=%d\n", traceme); (Currently the GDB interface is not reliable due to NetBSD support bugs) Sponsored by <The NetBSD Foundation>
distrib/sets/lists/comp/md.amd64
distrib/sets/lists/comp/md.i386
sys/arch/amd64/amd64/machdep.c
sys/arch/amd64/amd64/netbsd32_machdep.c
sys/arch/amd64/amd64/process_machdep.c
sys/arch/amd64/amd64/trap.c
sys/arch/amd64/include/netbsd32_machdep.h
sys/arch/amd64/include/pcb.h
sys/arch/amd64/include/proc.h
sys/arch/amd64/include/ptrace.h
sys/arch/amd64/include/reg.h
sys/arch/amd64/include/userret.h
sys/arch/i386/i386/machdep.c
sys/arch/i386/i386/process_machdep.c
sys/arch/i386/i386/trap.c
sys/arch/i386/include/pcb.h
sys/arch/i386/include/proc.h
sys/arch/i386/include/ptrace.h
sys/arch/i386/include/reg.h
sys/arch/i386/include/userret.h
sys/arch/x86/include/dbregs.h
sys/arch/x86/x86/dbregs.c
sys/arch/x86/x86/vm_machdep.c
sys/compat/netbsd32/netbsd32_ptrace.c
sys/kern/sys_ptrace.c
sys/kern/sys_ptrace_common.c
sys/sys/proc.h
sys/sys/ptrace.h
--- a/distrib/sets/lists/comp/md.amd64	Thu Feb 23 02:28:10 2017 +0000
+++ b/distrib/sets/lists/comp/md.amd64	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: md.amd64,v 1.242 2017/01/11 12:02:24 joerg Exp $
+# $NetBSD: md.amd64,v 1.243 2017/02/23 03:34:22 kamil Exp $
 
 ./usr/include/amd64				comp-c-include
 ./usr/include/amd64/ansi.h			comp-c-include
@@ -552,7 +552,7 @@
 ./usr/include/x86/cpu_ucode.h			comp-c-include
 ./usr/include/x86/cputypes.h			comp-c-include
 ./usr/include/x86/cpuvar.h			comp-c-include
-./usr/include/x86/dbregs.h			comp-c-include
+./usr/include/x86/dbregs.h			comp-obsolete		obsolete
 ./usr/include/x86/float.h			comp-c-include
 ./usr/include/x86/fpu.h				comp-c-include
 ./usr/include/x86/ieee.h			comp-c-include
--- a/distrib/sets/lists/comp/md.i386	Thu Feb 23 02:28:10 2017 +0000
+++ b/distrib/sets/lists/comp/md.i386	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: md.i386,v 1.162 2017/01/11 12:02:24 joerg Exp $
+# $NetBSD: md.i386,v 1.163 2017/02/23 03:34:22 kamil Exp $
 ./usr/include/clang-3.4/__wmmintrin_aes.h	comp-obsolete		obsolete
 ./usr/include/clang-3.4/__wmmintrin_pclmul.h	comp-obsolete		obsolete
 ./usr/include/clang-3.4/ammintrin.h		comp-obsolete		obsolete
@@ -426,7 +426,7 @@
 ./usr/include/x86/cpu_ucode.h			comp-c-include
 ./usr/include/x86/cputypes.h			comp-c-include
 ./usr/include/x86/cpuvar.h			comp-c-include
-./usr/include/x86/dbregs.h			comp-c-include
+./usr/include/x86/dbregs.h			comp-obsolete		obsolete
 ./usr/include/x86/float.h			comp-c-include
 ./usr/include/x86/fpu.h				comp-c-include
 ./usr/include/x86/ieee.h			comp-c-include
--- a/sys/arch/amd64/amd64/machdep.c	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/amd64/amd64/machdep.c	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: machdep.c,v 1.251 2017/02/05 08:36:08 maxv Exp $	*/
+/*	$NetBSD: machdep.c,v 1.252 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1996, 1997, 1998, 2000, 2006, 2007, 2008, 2011
@@ -111,7 +111,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.251 2017/02/05 08:36:08 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.252 2017/02/23 03:34:22 kamil Exp $");
 
 /* #define XENDEBUG_LOW  */
 
@@ -175,6 +175,7 @@
 #include <machine/specialreg.h>
 #include <machine/bootinfo.h>
 #include <x86/fpu.h>
+#include <x86/dbregs.h>
 #include <machine/mtrr.h>
 #include <machine/mpbiosvar.h>
 
@@ -282,6 +283,7 @@
 void (*initclock_func)(void) = xen_initclocks;
 #endif
 
+struct pool x86_dbregspl;
 
 /*
  * Size of memory segments, before any memory is stolen.
@@ -469,11 +471,11 @@
 	pcb->pcb_gs = 0;
 	pcb->pcb_rsp0 = (uvm_lwp_getuarea(l) + USPACE - 16) & ~0xf;
 	pcb->pcb_iopl = SEL_KPL;
+	pcb->pcb_dbregs = NULL;
 
 	pmap_kernel()->pm_ldt_sel = GSYSSEL(GLDT_SEL, SEL_KPL);
 	pcb->pcb_cr0 = rcr0() & ~CR0_TS;
 	l->l_md.md_regs = (struct trapframe *)pcb->pcb_rsp0 - 1;
-	memset(l->l_md.md_watchpoint, 0, sizeof(*l->l_md.md_watchpoint));
 
 #if !defined(XEN)
 	lldt(pmap_kernel()->pm_ldt_sel);
@@ -1316,11 +1318,13 @@
 	fpu_save_area_clear(l, pack->ep_osversion >= 699002600
 	    ? __NetBSD_NPXCW__ : __NetBSD_COMPAT_NPXCW__);
 	pcb->pcb_flags = 0;
+	if (pcb->pcb_dbregs != NULL) {
+		pool_put(&x86_dbregspl, pcb->pcb_dbregs);
+		pcb->pcb_dbregs = NULL;
+	}
 
 	l->l_proc->p_flag &= ~PK_32;
 
-	memset(l->l_md.md_watchpoint, 0, sizeof(*l->l_md.md_watchpoint));
-
 	tf = l->l_md.md_regs;
 	tf->tf_ds = LSEL(LUDATA_SEL, SEL_UPL);
 	tf->tf_es = LSEL(LUDATA_SEL, SEL_UPL);
@@ -1491,6 +1495,7 @@
 	struct region_descriptor region;
 	struct mem_segment_descriptor *ldt_segp;
 	int x;
+	struct pcb *pcb;
 #ifndef XEN
 	extern paddr_t local_apic_pa;
 	int ist;
@@ -1510,8 +1515,8 @@
 
 	use_pae = 1; /* PAE always enabled in long mode */
 
+	pcb = lwp_getpcb(&lwp0);
 #ifdef XEN
-	struct pcb *pcb = lwp_getpcb(&lwp0);
 	mutex_init(&pte_lock, MUTEX_DEFAULT, IPL_VM);
 	pcb->pcb_cr3 = xen_start_info.pt_base - KERNBASE;
 	__PRINTK(("pcb_cr3 0x%lx\n", xen_start_info.pt_base - KERNBASE));
@@ -1774,6 +1779,13 @@
 		kgdb_connect(1);
 	}
 #endif
+
+	pcb->pcb_dbregs = NULL;
+
+	x86_dbregs_setup_initdbstate();
+
+	pool_init(&x86_dbregspl, sizeof(struct dbreg), 16, 0, 0, "dbregs",
+	    NULL, IPL_NONE);
 }
 
 void
--- a/sys/arch/amd64/amd64/netbsd32_machdep.c	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/amd64/amd64/netbsd32_machdep.c	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: netbsd32_machdep.c,v 1.103 2017/02/14 09:03:48 maxv Exp $	*/
+/*	$NetBSD: netbsd32_machdep.c,v 1.104 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*
  * Copyright (c) 2001 Wasabi Systems, Inc.
@@ -36,7 +36,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: netbsd32_machdep.c,v 1.103 2017/02/14 09:03:48 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: netbsd32_machdep.c,v 1.104 2017/02/23 03:34:22 kamil Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_compat_netbsd.h"
@@ -80,6 +80,8 @@
 #include <compat/sys/signal.h>
 #include <compat/sys/signalvar.h>
 
+extern struct pool x86_dbregspl;
+
 /* Provide a the name of the architecture we're emulating */
 const char	machine32[] = "i386";
 const char	machine_arch32[] = "i386";	
@@ -136,9 +138,12 @@
 	fpu_save_area_clear(l, pack->ep_osversion >= 699002600
 	    ?  __NetBSD_NPXCW__ : __NetBSD_COMPAT_NPXCW__);
 
-	p->p_flag |= PK_32;
+	if (pcb->pcb_dbregs != NULL) {
+		pool_put(&x86_dbregspl, pcb->pcb_dbregs);
+		pcb->pcb_dbregs = NULL;
+	}
 
-	memset(l->l_md.md_watchpoint, 0, sizeof(*l->l_md.md_watchpoint));
+	p->p_flag |= PK_32;
 
 	tf = l->l_md.md_regs;
 	tf->tf_ds = LSEL(LUDATA32_SEL, SEL_UPL);
@@ -529,6 +534,28 @@
 }
 
 int
+netbsd32_process_read_dbregs(struct lwp *l, struct dbreg32 *regs, size_t *sz)
+{
+#if notyet
+	struct pcb *pcb;
+
+	pcb = lwp_getpcb(l);
+
+	regs->dr[0] = pcb->pcb_dbregs->dr[0] & 0xffffffff;
+	regs->dr[1] = pcb->pcb_dbregs->dr[1] & 0xffffffff;
+	regs->dr[2] = pcb->pcb_dbregs->dr[2] & 0xffffffff;
+	regs->dr[3] = pcb->pcb_dbregs->dr[3] & 0xffffffff;
+
+	regs->dr[6] = pcb->pcb_dbregs->dr[6] & 0xffffffff;
+	regs->dr[7] = pcb->pcb_dbregs->dr[7] & 0xffffffff;
+
+	return 0;
+#else
+	return ENOTSUP;
+#endif
+}
+
+int
 netbsd32_process_write_regs(struct lwp *l, const struct reg32 *regs)
 {
 	struct trapframe *tf;
@@ -589,6 +616,29 @@
 }
 
 int
+netbsd32_process_write_dbregs(struct lwp *l, const struct dbreg32 *regs,
+    size_t sz)
+{
+#if notyet
+	struct pcb *pcb;
+
+	pcb = lwp_getpcb(l);
+
+	pcb->pcb_dbregs->dr[0] = regs->dr[0];
+	pcb->pcb_dbregs->dr[1] = regs->dr[1];
+	pcb->pcb_dbregs->dr[2] = regs->dr[2];
+	pcb->pcb_dbregs->dr[3] = regs->dr[3];
+
+	pcb->pcb_dbregs->dr[6] = regs->dr[6];
+	pcb->pcb_dbregs->dr[7] = regs->dr[7];
+
+	return 0;
+#else
+	return ENOTSUP;
+#endif
+}
+
+int
 netbsd32_sysarch(struct lwp *l, const struct netbsd32_sysarch_args *uap, register_t *retval)
 {
 	/* {
--- a/sys/arch/amd64/amd64/process_machdep.c	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/amd64/amd64/process_machdep.c	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: process_machdep.c,v 1.31 2017/01/16 21:35:59 kamil Exp $	*/
+/*	$NetBSD: process_machdep.c,v 1.32 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
@@ -44,26 +44,39 @@
  *	registers or privileged bits in the PSL.
  *	The process is stopped at the time write_regs is called.
  *
+ * process_read_fpregs(proc, regs, sz)
+ *	Get the current user-visible register set from the process
+ *	and copy it into the regs structure (<machine/reg.h>).
+ *	The process is stopped at the time read_fpregs is called.
+ *
+ * process_write_fpregs(proc, regs, sz)
+ *	Update the current register set from the passed in regs
+ *	structure.  Take care to avoid clobbering special CPU
+ *	registers or privileged bits in the PSL.
+ *	The process is stopped at the time write_fpregs is called.
+ *
+ * process_read_dbregs(proc, regs, sz)
+ *	Get the current user-visible register set from the process
+ *	and copy it into the regs structure (<machine/reg.h>).
+ *	The process is stopped at the time read_dbregs is called.
+ *
+ * process_write_dbregs(proc, regs, sz)
+ *	Update the current register set from the passed in regs
+ *	structure.  Take care to avoid clobbering special CPU
+ *	registers or privileged bits in the PSL.
+ *	The process is stopped at the time write_dbregs is called.
+ *
  * process_sstep(proc)
  *	Arrange for the process to trap after executing a single instruction.
  *
  * process_set_pc(proc)
  *	Set the process's program counter.
  *
- * process_count_watchpoints(proc, retval)
- *	Return the number of supported hardware watchpoints.
- *
- * process_read_watchpoint(proc, watchpoint)
- *	Read hardware watchpoint of the given index.
- *
- * process_write_watchpoint(proc, watchpoint)
- *	Write hardware watchpoint of the given index.
- *
  */
 
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: process_machdep.c,v 1.31 2017/01/16 21:35:59 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: process_machdep.c,v 1.32 2017/02/23 03:34:22 kamil Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -114,6 +127,15 @@
 }
 
 int
+process_read_dbregs(struct lwp *l, struct dbreg *regs, size_t *sz)
+{
+
+	x86_dbregs_read(l, regs);
+
+	return 0;
+}
+
+int
 process_write_regs(struct lwp *l, const struct reg *regp)
 {
 	struct trapframe *tf = process_frame(l);
@@ -145,6 +167,23 @@
 }
 
 int
+process_write_dbregs(struct lwp *l, const struct dbreg *regs, size_t sz)
+{
+	int error;
+
+	/*
+	 * Check for security violations.
+	 */
+	error = x86_dbregs_validate(regs);
+	if (error != 0)
+		return error;
+
+	x86_dbregs_write(l, regs);
+
+	return 0;
+}
+
+int
 process_sstep(struct lwp *l, int sstep)
 {
 	struct trapframe *tf = process_frame(l);
@@ -168,85 +207,3 @@
 
 	return (0);
 }
-
-int
-process_count_watchpoints(struct lwp *l, register_t *retval)
-{
-
-	*retval = X86_HW_WATCHPOINTS;
-
-	return (0);
-}
-
-int
-process_read_watchpoint(struct lwp *l, struct ptrace_watchpoint *pw)
-{
-
-	pw->pw_type = PTRACE_PW_TYPE_DBREGS;
-	pw->pw_md.md_address =
-	    (void*)(intptr_t)l->l_md.md_watchpoint[pw->pw_index].address;
-	pw->pw_md.md_condition = l->l_md.md_watchpoint[pw->pw_index].condition;
-	pw->pw_md.md_length = l->l_md.md_watchpoint[pw->pw_index].length;
-
-	return (0);
-}
-
-static void
-update_mdl_x86_hw_watchpoints(struct lwp *l)
-{
-	size_t i;
-
-	for (i = 0; i < X86_HW_WATCHPOINTS; i++) {
-		if (l->l_md.md_watchpoint[0].address != 0) {
-			return;
-		}
-	}
-	l->l_md.md_flags &= ~MDL_X86_HW_WATCHPOINTS;
-}
-
-int
-process_write_watchpoint(struct lwp *l, struct ptrace_watchpoint *pw)
-{
-
-	if (pw->pw_index > X86_HW_WATCHPOINTS)
-		return (EINVAL);
-
-	if (pw->pw_type != PTRACE_PW_TYPE_DBREGS)
-		return (EINVAL);
-
-	if (pw->pw_md.md_address == 0) {
-		l->l_md.md_watchpoint[pw->pw_index].address = 0;
-		update_mdl_x86_hw_watchpoints(l);
-		return (0);
-	}
-
-	if ((vaddr_t)pw->pw_md.md_address > VM_MAXUSER_ADDRESS)
-		return (EINVAL);
-
-	switch (pw->pw_md.md_condition) {
-	case X86_HW_WATCHPOINT_DR7_CONDITION_EXECUTION:
-	case X86_HW_WATCHPOINT_DR7_CONDITION_DATA_WRITE:
-	case X86_HW_WATCHPOINT_DR7_CONDITION_DATA_READWRITE:
-		break;
-	default:
-		return (EINVAL);
-	}
-
-	switch (pw->pw_md.md_length) {
-	case X86_HW_WATCHPOINT_DR7_LENGTH_BYTE:
-	case X86_HW_WATCHPOINT_DR7_LENGTH_TWOBYTES:
-	case X86_HW_WATCHPOINT_DR7_LENGTH_FOURBYTES:
-		break;
-	default:
-		return (EINVAL);
-	}
-
-	l->l_md.md_watchpoint[pw->pw_index].address =
-	    (vaddr_t)pw->pw_md.md_address;
-	l->l_md.md_watchpoint[pw->pw_index].condition = pw->pw_md.md_condition;
-	l->l_md.md_watchpoint[pw->pw_index].length = pw->pw_md.md_length;
-
-	l->l_md.md_flags |= MDL_X86_HW_WATCHPOINTS;
-
-	return (0);
-}
--- a/sys/arch/amd64/amd64/trap.c	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/amd64/amd64/trap.c	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: trap.c,v 1.91 2017/02/17 01:14:31 kamil Exp $	*/
+/*	$NetBSD: trap.c,v 1.92 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
@@ -68,7 +68,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.91 2017/02/17 01:14:31 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.92 2017/02/23 03:34:22 kamil Exp $");
 
 #include "opt_ddb.h"
 #include "opt_kgdb.h"
@@ -230,7 +230,7 @@
 #endif
 	ksiginfo_t ksi;
 	void *onfault;
-	int type, error, wptnfo;
+	int type, error;
 	uint64_t cr2;
 	bool pfail;
 
@@ -687,7 +687,7 @@
 		 * in kernel space because that is useful when
 		 * debugging the kernel.
 		 */
-		if (user_trap_x86_hw_watchpoint())
+		if (x86_dbregs_user_trap())
 			break;
 
 		/* Check whether they single-stepped into a lcall. */
@@ -710,10 +710,9 @@
 			KSI_INIT_TRAP(&ksi);
 			ksi.ksi_signo = SIGTRAP;
 			ksi.ksi_trap = type & ~T_USER;
-			if ((wptnfo = user_trap_x86_hw_watchpoint())) {
+			if (x86_dbregs_user_trap()) {
+				x86_dbregs_store_dr6(l);
 				ksi.ksi_code = TRAP_DBREG;
-				ksi.ksi_trap2 = x86_hw_watchpoint_reg(wptnfo);
-				ksi.ksi_trap3 = x86_hw_watchpoint_type(wptnfo);
 			} else if (type == (T_BPTFLT|T_USER))
 				ksi.ksi_code = TRAP_BRKPT;
 			else
--- a/sys/arch/amd64/include/netbsd32_machdep.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/amd64/include/netbsd32_machdep.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: netbsd32_machdep.h,v 1.21 2017/02/06 16:02:17 maxv Exp $	*/
+/*	$NetBSD: netbsd32_machdep.h,v 1.22 2017/02/23 03:34:22 kamil Exp $	*/
 
 #ifndef _MACHINE_NETBSD32_H_
 #define _MACHINE_NETBSD32_H_
@@ -114,6 +114,10 @@
 	char	__data[108];
 };
 
+struct dbreg32 {
+	int	dr[8];
+};
+
 struct x86_get_ldt_args32 {
 	int32_t start;
 	uint32_t desc;
@@ -149,8 +153,10 @@
 
 int netbsd32_process_read_regs(struct lwp *, struct reg32 *);
 int netbsd32_process_read_fpregs(struct lwp *, struct fpreg32 *, size_t *);
+int netbsd32_process_read_dbregs(struct lwp *, struct dbreg32 *, size_t *);
 
 int netbsd32_process_write_regs(struct lwp *, const struct reg32 *);
 int netbsd32_process_write_fpregs(struct lwp *, const struct fpreg32 *, size_t);
+int netbsd32_process_write_dbregs(struct lwp *, const struct dbreg32 *, size_t);
 
 #endif /* _MACHINE_NETBSD32_H_ */
--- a/sys/arch/amd64/include/pcb.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/amd64/include/pcb.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: pcb.h,v 1.25 2014/02/20 18:19:10 dsl Exp $	*/
+/*	$NetBSD: pcb.h,v 1.26 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -73,6 +73,7 @@
 #ifdef __x86_64__
 
 #include <x86/cpu_extended_state.h>
+#include <amd64/reg.h>
 
 #define	NIOPORTS	1024		/* # of ports we allow to be mapped */
 
@@ -89,9 +90,10 @@
 	void     *pcb_onfault;		/* copyin/out fault recovery */
 	uint64_t  pcb_fs;
 	uint64_t  pcb_gs;
+	struct dbreg *pcb_dbregs;
 	int pcb_iopl;
 
-	uint32_t pcb_unused[11];		/* unused */
+	uint32_t pcb_unused[9];		/* unused */
 
 	struct cpu_info *pcb_fpcpu;	/* cpu holding our fp state. */
 	union savefpu	pcb_savefpu __aligned(64); /* floating point state */
--- a/sys/arch/amd64/include/proc.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/amd64/include/proc.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: proc.h,v 1.20 2016/12/15 12:04:17 kamil Exp $	*/
+/*	$NetBSD: proc.h,v 1.21 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*
  * Copyright (c) 1991 Regents of the University of California.
@@ -52,12 +52,10 @@
 	struct vm_page *md_gc_ptp;	/* pages from pmap g/c */
 	int	md_flags;		/* machine-dependent flags */
 	volatile int md_astpending;
-	struct	x86_hw_watchpoint md_watchpoint[X86_HW_WATCHPOINTS];
 };
 
 #define	MDL_COMPAT32		0x0008	/* i386, always return via iret */
 #define	MDL_IRET		0x0010	/* force return via iret, not sysret */
-#define	MDL_X86_HW_WATCHPOINTS	0x0020	/* has hardware watchpoints */
 
 struct mdproc {
 	int	md_flags;
--- a/sys/arch/amd64/include/ptrace.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/amd64/include/ptrace.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: ptrace.h,v 1.9 2017/01/16 21:35:59 kamil Exp $	*/
+/*	$NetBSD: ptrace.h,v 1.10 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*
  * Copyright (c) 1993 Christopher G. Demetriou
@@ -41,9 +41,8 @@
 #define	PT_SETREGS		(PT_FIRSTMACH + 2)
 #define	PT_GETFPREGS		(PT_FIRSTMACH + 3)
 #define	PT_SETFPREGS		(PT_FIRSTMACH + 4)
-#define	PT_READ_WATCHPOINT	(PT_FIRSTMACH + 5)
-#define	PT_WRITE_WATCHPOINT	(PT_FIRSTMACH + 6)
-#define	PT_COUNT_WATCHPOINTS	(PT_FIRSTMACH + 7)
+#define	PT_GETDBREGS		(PT_FIRSTMACH + 5)
+#define	PT_SETDBREGS		(PT_FIRSTMACH + 6)
 
 #define PT_MACHDEP_STRINGS \
 	"PT_STEP", \
@@ -51,9 +50,8 @@
 	"PT_SETREGS", \
 	"PT_GETFPREGS", \
 	"PT_SETFPREGS", \
-	"PT_READ_WATCHPOINT", \
-	"PT_WRITE_WATCHPOINT", \
-	"PT_COUNT_WATCHPOINTS"
+	"PT_GETDBREGS", \
+	"PT_SETDBREGS",
 
 #include <machine/reg.h>
 #define PTRACE_REG_PC(r)	(r)->regs[_REG_RIP]
@@ -65,50 +63,6 @@
 #define PTRACE_BREAKPOINT_SIZE	1
 #define PTRACE_BREAKPOINT_ADJ	1
 
-#define __HAVE_PTRACE_WATCHPOINTS
-
-/*
- * The current list of supported hardware watchpoints
- */
-#define PTRACE_PW_TYPE_DBREGS	1
-
-struct mdpw {
-	union {
-		/* Debug Registers DR0-3, DR6, DR7 */
-		struct {
-			void	*_md_address;
-			int	 _md_condition;
-			int	 _md_length;
-		} _dbregs;
-	} _type;
-};
-
-/*
- * This MD structure translates into x86_hw_watchpoint
- *
- * pw_address - 0 represents disabled hardware watchpoint
- *
- * conditions:
- *     0b00 - execution
- *     0b01 - data write
- *     0b10 - io read/write (not implemented)
- *     0b11 - data read/write
- *
- * length:
- *     0b00 - 1 byte
- *     0b01 - 2 bytes
- *     0b10 - undefined (8 bytes in modern CPUs - not implemented)
- *     0b11 - 4 bytes
- *
- * Helper symbols for conditions and length are available in <x86/dbregs.h>
- *
- */
-
-#define	md_address	_type._dbregs._md_address
-#define	md_condition	_type._dbregs._md_condition
-#define	md_length	_type._dbregs._md_length
-
-
 #ifdef _KERNEL_OPT
 #include "opt_compat_netbsd32.h"
 
@@ -117,17 +71,15 @@
 
 #define process_read_regs32	netbsd32_process_read_regs
 #define process_read_fpregs32	netbsd32_process_read_fpregs
+#define process_read_dbregs32	netbsd32_process_read_dbregs
 
 #define process_write_regs32	netbsd32_process_write_regs
 #define process_write_fpregs32	netbsd32_process_write_fpregs
-
-#define process_write_watchpoint32	netbsd32_process_write_watchpoint
-#define process_read_watchpoint32	netbsd32_process_read_watchpoint
-#define process_count_watchpoint32	netbsd32_process_count_watchpoint
+#define process_write_dbregs32	netbsd32_process_write_dbregs
 
 #define process_reg32		struct reg32
 #define process_fpreg32		struct fpreg32
-#define process_watchpoint32	struct ptrace_watchpoint32
+#define process_dbreg32		struct dbreg32
 #endif	/* COMPAT_NETBSD32 */
 #endif	/* _KERNEL_OPT */
 
--- a/sys/arch/amd64/include/reg.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/amd64/include/reg.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: reg.h,v 1.9 2014/02/11 20:17:16 dsl Exp $	*/
+/*	$NetBSD: reg.h,v 1.10 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1990 The Regents of the University of California.
@@ -56,6 +56,19 @@
 	struct fxsave fxstate;
 };
 
+/*
+ * Debug Registers
+ *
+ * DR0-DR3  Debug Address Registers
+ * DR4-DR5  Reserved
+ * DR6      Debug Status Register
+ * DR7      Debug Control Register
+ * DR8-DR15 Reserved
+ */
+struct dbreg {
+	long	dr[16];
+};
+
 #else	/*	__x86_64__	*/
 
 #include <i386/reg.h>
--- a/sys/arch/amd64/include/userret.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/amd64/include/userret.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: userret.h,v 1.11 2017/01/16 21:19:35 kamil Exp $	*/
+/*	$NetBSD: userret.h,v 1.12 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*
  * XXXfvdl same as i386 counterpart, but should probably be independent.
@@ -78,15 +78,13 @@
 static __inline void
 userret(struct lwp *l)
 {
+	struct pcb *pcb = lwp_getpcb(l);
 
 	/* Invoke MI userret code */
 	mi_userret(l);
 
-	/*
-	 * Allow to mix debug registers with single step.
-	 */
-	if (l->l_md.md_flags & MDL_X86_HW_WATCHPOINTS)
-		set_x86_hw_watchpoints(l);
+	if (pcb->pcb_dbregs)
+		x86_dbregs_set(l);
 	else
-		clear_x86_hw_watchpoints();
+		x86_dbregs_clear(l);
 }
--- a/sys/arch/i386/i386/machdep.c	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/i386/i386/machdep.c	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: machdep.c,v 1.779 2017/02/17 12:10:40 maxv Exp $	*/
+/*	$NetBSD: machdep.c,v 1.780 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1996, 1997, 1998, 2000, 2004, 2006, 2008, 2009
@@ -67,7 +67,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.779 2017/02/17 12:10:40 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.780 2017/02/23 03:34:22 kamil Exp $");
 
 #include "opt_beep.h"
 #include "opt_compat_ibcs2.h"
@@ -144,6 +144,7 @@
 #include <x86/x86/tsc.h>
 
 #include <x86/fpu.h>
+#include <x86/dbregs.h>
 #include <x86/machdep.h>
 
 #include <machine/multiboot.h>
@@ -237,6 +238,8 @@
 int i386_has_sse;
 int i386_has_sse2;
 
+struct pool x86_dbregspl;
+
 vaddr_t idt_vaddr;
 paddr_t idt_paddr;
 vaddr_t gdt_vaddr;
@@ -508,7 +511,7 @@
 	l->l_md.md_regs = (struct trapframe *)pcb->pcb_esp0 - 1;
 	memcpy(&pcb->pcb_fsd, &gdtstore[GUDATA_SEL], sizeof(pcb->pcb_fsd));
 	memcpy(&pcb->pcb_gsd, &gdtstore[GUDATA_SEL], sizeof(pcb->pcb_gsd));
-	memset(l->l_md.md_watchpoint, 0, sizeof(*l->l_md.md_watchpoint));
+	pcb->pcb_dbregs = NULL;
 
 #ifndef XEN
 	lldt(pmap_kernel()->pm_ldt_sel);
@@ -841,8 +844,10 @@
 
 	memcpy(&pcb->pcb_fsd, &gdtstore[GUDATA_SEL], sizeof(pcb->pcb_fsd));
 	memcpy(&pcb->pcb_gsd, &gdtstore[GUDATA_SEL], sizeof(pcb->pcb_gsd));
-
-	memset(l->l_md.md_watchpoint, 0, sizeof(*l->l_md.md_watchpoint));
+	if (pcb->pcb_dbregs != NULL) {
+		pool_put(&x86_dbregspl, pcb->pcb_dbregs);
+		pcb->pcb_dbregs = NULL;
+	}
 
 	tf = l->l_md.md_regs;
 	tf->tf_gs = GSEL(GUGS_SEL, SEL_UPL);
@@ -1089,6 +1094,7 @@
 	extern int biostramp_image_size;
 	extern u_char biostramp_image[];
 #endif
+	struct pcb *pcb;
 
 	KASSERT(first_avail % PAGE_SIZE == 0);
 
@@ -1110,8 +1116,8 @@
 	use_pae = 0;
 #endif
 
+	pcb = lwp_getpcb(&lwp0);
 #ifdef XEN
-	struct pcb *pcb = lwp_getpcb(&lwp0);
 	pcb->pcb_cr3 = PDPpaddr;
 	__PRINTK(("pcb_cr3 0x%lx cr3 0x%lx\n",
 	    PDPpaddr, xpmap_ptom(PDPpaddr)));
@@ -1405,6 +1411,13 @@
 	rw_init(&svr4_fasttrap_lock);
 
 	pmc_init();
+
+	pcb->pcb_dbregs = NULL;
+
+	x86_dbregs_setup_initdbstate();
+
+	pool_init(&x86_dbregspl, sizeof(struct dbreg), 16, 0, 0, "dbregs",                                                                                   
+	    NULL, IPL_NONE);
 }
 
 #include <dev/ic/mc146818reg.h>		/* for NVRAM POST */
--- a/sys/arch/i386/i386/process_machdep.c	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/i386/i386/process_machdep.c	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: process_machdep.c,v 1.88 2017/01/16 21:35:59 kamil Exp $	*/
+/*	$NetBSD: process_machdep.c,v 1.89 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2000, 2001, 2008 The NetBSD Foundation, Inc.
@@ -44,25 +44,38 @@
  *	registers or privileged bits in the PSL.
  *	The process is stopped at the time write_regs is called.
  *
+ * process_read_fpregs(proc, regs, sz)
+ *	Get the current user-visible register set from the process
+ *	and copy it into the regs structure (<machine/reg.h>).
+ *	The process is stopped at the time read_fpregs is called.
+ *
+ * process_write_fpregs(proc, regs, sz)
+ *	Update the current register set from the passed in regs
+ *	structure.  Take care to avoid clobbering special CPU
+ *	registers or privileged bits in the PSL.
+ *	The process is stopped at the time write_fpregs is called.
+ *
+ * process_read_dbregs(proc, regs)
+ *	Get the current user-visible register set from the process
+ *	and copy it into the regs structure (<machine/reg.h>).
+ *	The process is stopped at the time read_dbregs is called.
+ *
+ * process_write_dbregs(proc, regs)
+ *	Update the current register set from the passed in regs
+ *	structure.  Take care to avoid clobbering special CPU
+ *	registers or privileged bits in the PSL.
+ *	The process is stopped at the time write_dbregs is called.
+ *
  * process_sstep(proc)
  *	Arrange for the process to trap after executing a single instruction.
  *
  * process_set_pc(proc)
  *	Set the process's program counter.
  *
- * process_count_watchpoints(proc, retval)
- *	Return the number of supported hardware watchpoints.
- *
- * process_read_watchpoint(proc, watchpoint)
- *	Read hardware watchpoint of the given index.
- *
- * process_write_watchpoint(proc, watchpoint)
- *	Write hardware watchpoint of the given index.
- *
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: process_machdep.c,v 1.88 2017/01/16 21:35:59 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: process_machdep.c,v 1.89 2017/02/23 03:34:22 kamil Exp $");
 
 #include "opt_vm86.h"
 #include "opt_ptrace.h"
@@ -140,6 +153,15 @@
 	return 0;
 }
 
+int
+process_read_dbregs(struct lwp *l, struct dbreg *regs, size_t *sz)
+{
+
+	x86_dbregs_read(l, regs);
+
+	return 0;
+}
+
 #ifdef PTRACE_HOOKS
 int
 process_write_regs(struct lwp *l, const struct reg *regs)
@@ -205,6 +227,22 @@
 	return 0;
 }
 
+int
+process_write_dbregs(struct lwp *l, const struct dbreg *regs, size_t sz)
+{
+	int error;
+
+	/*
+	 * Check for security violations.
+	 */
+	error = x86_dbregs_validate(regs);
+	if (error != 0)                                                                                                               
+		return error;
+
+	x86_dbregs_write(l, regs);
+
+	return 0;
+}
 
 int
 process_sstep(struct lwp *l, int sstep)
@@ -348,85 +386,3 @@
 }
 #endif /* __HAVE_PTRACE_MACHDEP */
 #endif /* PTRACE_HOOKS */
-
-int
-process_count_watchpoints(struct lwp *l, register_t *retval)
-{
-
-	*retval = X86_HW_WATCHPOINTS;
-
-	return (0);
-}
-
-int
-process_read_watchpoint(struct lwp *l, struct ptrace_watchpoint *pw)
-{
-
-	pw->pw_type = PTRACE_PW_TYPE_DBREGS;
-	pw->pw_md.md_address =
-	    (void*)(intptr_t)l->l_md.md_watchpoint[pw->pw_index].address;
-	pw->pw_md.md_condition = l->l_md.md_watchpoint[pw->pw_index].condition;
-	pw->pw_md.md_length = l->l_md.md_watchpoint[pw->pw_index].length;
-
-	return (0);
-}
-
-static void
-update_mdl_x86_hw_watchpoints(struct lwp *l)
-{
-	size_t i;
-		
-	for (i = 0; i < X86_HW_WATCHPOINTS; i++) {
-		if (l->l_md.md_watchpoint[0].address != 0) {
-			return;
-		}
-	}
-	l->l_md.md_flags &= ~MDL_X86_HW_WATCHPOINTS;
-}
-
-int
-process_write_watchpoint(struct lwp *l, struct ptrace_watchpoint *pw)
-{
-
-	if (pw->pw_index > X86_HW_WATCHPOINTS)
-		return (EINVAL);
-
-	if (pw->pw_type != PTRACE_PW_TYPE_DBREGS)
-		return (EINVAL);
-
-	if (pw->pw_md.md_address == 0) {
-		l->l_md.md_watchpoint[pw->pw_index].address = 0;
-		update_mdl_x86_hw_watchpoints(l);
-		return (0);
-	}
-
-	if ((vaddr_t)pw->pw_md.md_address > VM_MAXUSER_ADDRESS)
-		return (EINVAL);
-
-	switch (pw->pw_md.md_condition) {
-	case X86_HW_WATCHPOINT_DR7_CONDITION_EXECUTION:
-	case X86_HW_WATCHPOINT_DR7_CONDITION_DATA_WRITE:
-	case X86_HW_WATCHPOINT_DR7_CONDITION_DATA_READWRITE:
-		break;
-	default:
-		return (EINVAL);
-	}
-
-	switch (pw->pw_md.md_length) {
-	case X86_HW_WATCHPOINT_DR7_LENGTH_BYTE:
-	case X86_HW_WATCHPOINT_DR7_LENGTH_TWOBYTES:
-	case X86_HW_WATCHPOINT_DR7_LENGTH_FOURBYTES:
-		break;
-	default:
-		return (EINVAL);
-	}
-
-	l->l_md.md_watchpoint[pw->pw_index].address =
-	    (vaddr_t)pw->pw_md.md_address;
-	l->l_md.md_watchpoint[pw->pw_index].condition = pw->pw_md.md_condition;
-	l->l_md.md_watchpoint[pw->pw_index].length = pw->pw_md.md_length;
-
-	l->l_md.md_flags |= MDL_X86_HW_WATCHPOINTS;
-
-	return (0);
-}
--- a/sys/arch/i386/i386/trap.c	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/i386/i386/trap.c	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,5 @@
-/*	$NetBSD: trap.c,v 1.283 2017/02/17 01:14:31 kamil Exp $	*/
+
+/*	$NetBSD: trap.c,v 1.284 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2000, 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -68,7 +69,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.283 2017/02/17 01:14:31 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.284 2017/02/23 03:34:22 kamil Exp $");
 
 #include "opt_ddb.h"
 #include "opt_kgdb.h"
@@ -251,7 +252,7 @@
 	struct trapframe *vframe;
 	ksiginfo_t ksi;
 	void *onfault;
-	int type, error, wptnfo;
+	int type, error;
 	uint32_t cr2;
 	bool pfail;
 
@@ -681,6 +682,19 @@
 	}
 
 	case T_TRCTRAP:
+		/*
+		 * 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 (x86_dbregs_user_trap())
+			break;
+
 		/* Check whether they single-stepped into a lcall. */
 		if (frame->tf_eip == (int)IDTVEC(osyscall))
 			return;
@@ -700,10 +714,9 @@
 			KSI_INIT_TRAP(&ksi);
 			ksi.ksi_signo = SIGTRAP;
 			ksi.ksi_trap = type & ~T_USER;
-			if ((wptnfo = user_trap_x86_hw_watchpoint())) {
+			if (x86_dbregs_user_trap()) {
+				x86_dbregs_store_dr6(l);
 				ksi.ksi_code = TRAP_DBREG;
-				ksi.ksi_trap2 = x86_hw_watchpoint_reg(wptnfo);
-				ksi.ksi_trap3 = x86_hw_watchpoint_type(wptnfo);
 			} else if (type == (T_BPTFLT|T_USER))
 				ksi.ksi_code = TRAP_BRKPT;
 			else
--- a/sys/arch/i386/include/pcb.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/i386/include/pcb.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: pcb.h,v 1.54 2014/04/21 19:13:22 christos Exp $	*/
+/*	$NetBSD: pcb.h,v 1.55 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2009 The NetBSD Foundation, Inc.
@@ -93,8 +93,9 @@
 	int	vm86_flagmask;		/* flag mask for vm86 mode */
 	void	*vm86_userp;		/* XXX performance hack */
 	char	*pcb_iomap;		/* I/O permission bitmap */
+	struct dbreg	*pcb_dbregs;	/* CPU Debug Registers */
 
-	int	not_used[15];
+	int	not_used[14];
 
 	/* floating point state */
 	struct cpu_info	*pcb_fpcpu;	/* cpu holding our fp state. */
--- a/sys/arch/i386/include/proc.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/i386/include/proc.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: proc.h,v 1.43 2016/12/15 12:04:18 kamil Exp $	*/
+/*	$NetBSD: proc.h,v 1.44 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*
  * Copyright (c) 1991 Regents of the University of California.
@@ -50,12 +50,10 @@
 	volatile int md_astpending;	/* AST pending for this process */
 	struct pmap *md_gc_pmap;	/* pmap being garbage collected */
 	struct vm_page *md_gc_ptp;	/* pages from pmap g/c */
-	struct  x86_hw_watchpoint md_watchpoint[X86_HW_WATCHPOINTS];
 };
 
 /* md_flags */
 #define	MDL_IOPL		0x0002	/* XEN: i/o privilege */
-#define	MDL_X86_HW_WATCHPOINTS	0x0004	/* has hardware watchpoints */
 
 struct mdproc {
 	int	md_flags;
--- a/sys/arch/i386/include/ptrace.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/i386/include/ptrace.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: ptrace.h,v 1.17 2017/01/16 21:35:59 kamil Exp $	*/
+/*	$NetBSD: ptrace.h,v 1.18 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*
  * Copyright (c) 2001 Wasabi Systems, Inc.
@@ -86,9 +86,8 @@
 /* The machine-dependent ptrace(2) requests. */
 #define	PT_GETXMMREGS		(PT_FIRSTMACH + 5)
 #define	PT_SETXMMREGS		(PT_FIRSTMACH + 6)
-#define PT_READ_WATCHPOINT	(PT_FIRSTMACH + 7)
-#define PT_WRITE_WATCHPOINT	(PT_FIRSTMACH + 8)
-#define PT_COUNT_WATCHPOINTS	(PT_FIRSTMACH + 9)
+#define	PT_GETDBREGS		(PT_FIRSTMACH + 7)
+#define	PT_SETDBREGS		(PT_FIRSTMACH + 8)
 
 #define PT_MACHDEP_STRINGS \
 	"PT_STEP", \
@@ -98,9 +97,9 @@
 	"PT_SETFPREGS", \
 	"PT_GETXMMREGS", \
 	"PT_SETXMMREGS", \
-	"PT_READ_WATCHPOINT", \
-	"PT_WRITE_WATCHPOINT", \
-	"PT_COUNT_WATCHPOINTS"
+	"PT_GETDBREGS", \
+	"PT_SETDBREGS",
+
 
 #include <machine/reg.h>
 #define PTRACE_REG_PC(r)	(r)->r_eip
@@ -112,50 +111,6 @@
 #define PTRACE_BREAKPOINT_SIZE	1
 #define PTRACE_BREAKPOINT_ADJ	sizeof(PTRACE_BREAKPOINT)
 
-#define __HAVE_PTRACE_WATCHPOINTS
-
-/*
- * The current list of supported hardware watchpoints
- */
-#define	PTRACE_PW_TYPE_DBREGS	1
-
-struct mdpw {
-	union {
-		/* Debug Registers DR0-3, DR6, DR7 */
-		struct {
-			void	*_md_address;
-			int	_md_condition; 
-			int	_md_length;
-		} _dbregs;
-	} _type;
-};
-
-/*
- * This MD structure translates into x86_hw_watchpoint
- *
- * pw_address - 0 represents disabled hardware watchpoint
- *
- * conditions:
- *     0b00 - execution
- *     0b01 - data write
- *     0b10 - io read/write (not implemented)
- *     0b11 - data read/write
- *
- * length:
- *     0b00 - 1 byte
- *     0b01 - 2 bytes
- *     0b10 - undefined
- *     0b11 - 4 bytes
- *
- * Helper symbols for conditions and length are available in <x86/dbregs.h>
- *
- */
-
-#define	md_address	_type._dbregs._md_address
-#define	md_condition	_type._dbregs._md_condition
-#define	md_length	_type._dbregs._md_length
-
-
 #ifdef _KERNEL
 
 /*
--- a/sys/arch/i386/include/reg.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/i386/include/reg.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: reg.h,v 1.19 2008/01/16 09:37:08 ad Exp $	*/
+/*	$NetBSD: reg.h,v 1.20 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1990 The Regents of the University of California.
@@ -107,4 +107,16 @@
 	char	__data[512];
 };
 
+/*
+ * Debug Registers
+ *
+ * DR0-DR3  Debug Address Registers
+ * DR4-DR5  Reserved
+ * DR6      Debug Status Register
+ * DR7      Debug Control Register
+ */
+struct dbreg {
+	int	dr[8];
+};
+
 #endif /* !_I386_REG_H_ */
--- a/sys/arch/i386/include/userret.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/i386/include/userret.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: userret.h,v 1.13 2017/01/16 21:19:14 kamil Exp $	*/
+/*	$NetBSD: userret.h,v 1.14 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
@@ -74,15 +74,13 @@
 static __inline void
 userret(struct lwp *l)
 {
+	struct pcb *pcb = lwp_getpcb(l);
 
 	/* Invoke MI userret code */
 	mi_userret(l);
 
-	/*
-	 * Allow to mix debug registers with single step.
-	 */
-	if (l->l_md.md_flags & MDL_X86_HW_WATCHPOINTS)
-		set_x86_hw_watchpoints(l);
+	if (pcb->pcb_dbregs)
+		x86_dbregs_set(l);
 	else
-		clear_x86_hw_watchpoints();
+		x86_dbregs_clear(l);
 }
--- a/sys/arch/x86/include/dbregs.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/x86/include/dbregs.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: dbregs.h,v 1.3 2017/01/18 05:12:00 kamil Exp $	*/
+/*	$NetBSD: dbregs.h,v 1.4 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 2016 The NetBSD Foundation, Inc.
@@ -30,23 +30,22 @@
 #ifndef	_X86_DBREGS_H_
 #define	_X86_DBREGS_H_
 
-#if defined(_KERNEL)
-
 #include <sys/param.h>
 #include <sys/types.h>
+#include <machine/reg.h>
 
 /*
  * CPU Debug Status Register (DR6)
  *
  * Reserved bits: 4-12 and on x86_64 32-64
  */
-#define X86_HW_WATCHPOINT_DR6_DR0_BREAKPOINT_CONDITION_DETECTED	__BIT(0)
-#define X86_HW_WATCHPOINT_DR6_DR1_BREAKPOINT_CONDITION_DETECTED	__BIT(1)
-#define X86_HW_WATCHPOINT_DR6_DR2_BREAKPOINT_CONDITION_DETECTED	__BIT(2)
-#define X86_HW_WATCHPOINT_DR6_DR3_BREAKPOINT_CONDITION_DETECTED	__BIT(3)
-#define X86_HW_WATCHPOINT_DR6_DEBUG_REGISTER_ACCESS_DETECTED	__BIT(13)
-#define X86_HW_WATCHPOINT_DR6_SINGLE_STEP			__BIT(14)
-#define X86_HW_WATCHPOINT_DR6_TASK_SWITCH			__BIT(15)
+#define X86_DR6_DR0_BREAKPOINT_CONDITION_DETECTED	__BIT(0)
+#define X86_DR6_DR1_BREAKPOINT_CONDITION_DETECTED	__BIT(1)
+#define X86_DR6_DR2_BREAKPOINT_CONDITION_DETECTED	__BIT(2)
+#define X86_DR6_DR3_BREAKPOINT_CONDITION_DETECTED	__BIT(3)
+#define X86_DR6_DEBUG_REGISTER_ACCESS_DETECTED		__BIT(13)
+#define X86_DR6_SINGLE_STEP				__BIT(14)
+#define X86_DR6_TASK_SWITCH				__BIT(15)
 
 /*
  * CPU Debug Control Register (DR7)
@@ -57,108 +56,97 @@
  *
  * Reserved bits: 10, 12, 14-15 and on x86_64 32-64
  */
-#define X86_HW_WATCHPOINT_DR7_LOCAL_DR0_BREAKPOINT		__BIT(0)
-#define X86_HW_WATCHPOINT_DR7_GLOBAL_DR0_BREAKPOINT		__BIT(1)
-#define X86_HW_WATCHPOINT_DR7_LOCAL_DR1_BREAKPOINT		__BIT(2)
-#define X86_HW_WATCHPOINT_DR7_GLOBAL_DR1_BREAKPOINT		__BIT(3)
-#define X86_HW_WATCHPOINT_DR7_LOCAL_DR2_BREAKPOINT		__BIT(4)
-#define X86_HW_WATCHPOINT_DR7_GLOBAL_DR2_BREAKPOINT		__BIT(5)
-#define X86_HW_WATCHPOINT_DR7_LOCAL_DR3_BREAKPOINT		__BIT(6)
-#define X86_HW_WATCHPOINT_DR7_GLOBAL_DR3_BREAKPOINT		__BIT(7)
-#define X86_HW_WATCHPOINT_DR7_LOCAL_EXACT_BREAKPOINT		__BIT(8)
-#define X86_HW_WATCHPOINT_DR7_GLOBAL_EXACT_BREAKPOINT		__BIT(9)
-#define X86_HW_WATCHPOINT_DR7_RESTRICTED_TRANSACTIONAL_MEMORY	__BIT(11)
-#define X86_HW_WATCHPOINT_DR7_GENERAL_DETECT_ENABLE		__BIT(13)
+#define X86_DR7_LOCAL_DR0_BREAKPOINT		__BIT(0)
+#define X86_DR7_GLOBAL_DR0_BREAKPOINT		__BIT(1)
+#define X86_DR7_LOCAL_DR1_BREAKPOINT		__BIT(2)
+#define X86_DR7_GLOBAL_DR1_BREAKPOINT		__BIT(3)
+#define X86_DR7_LOCAL_DR2_BREAKPOINT		__BIT(4)
+#define X86_DR7_GLOBAL_DR2_BREAKPOINT		__BIT(5)
+#define X86_DR7_LOCAL_DR3_BREAKPOINT		__BIT(6)
+#define X86_DR7_GLOBAL_DR3_BREAKPOINT		__BIT(7)
+#define X86_DR7_LOCAL_EXACT_BREAKPOINT		__BIT(8)
+#define X86_DR7_GLOBAL_EXACT_BREAKPOINT		__BIT(9)
+#define X86_DR7_RESTRICTED_TRANSACTIONAL_MEMORY	__BIT(11)
+#define X86_DR7_GENERAL_DETECT_ENABLE		__BIT(13)
 
-#define X86_HW_WATCHPOINT_DR7_DR0_CONDITION_MASK		__BITS(16, 17)
-#define X86_HW_WATCHPOINT_DR7_DR0_LENGTH_MASK			__BITS(18, 19)
-#define X86_HW_WATCHPOINT_DR7_DR1_CONDITION_MASK		__BITS(20, 21)
-#define X86_HW_WATCHPOINT_DR7_DR1_LENGTH_MASK			__BITS(22, 23)
-#define X86_HW_WATCHPOINT_DR7_DR2_CONDITION_MASK		__BITS(24, 25)
-#define X86_HW_WATCHPOINT_DR7_DR2_LENGTH_MASK			__BITS(26, 27)
-#define X86_HW_WATCHPOINT_DR7_DR3_CONDITION_MASK		__BITS(28, 29)
-#define X86_HW_WATCHPOINT_DR7_DR3_LENGTH_MASK			__BITS(30, 31)
-
-#endif /* !defined(_KERNEL) */
+#define X86_DR7_DR0_CONDITION_MASK		__BITS(16, 17)
+#define X86_DR7_DR0_LENGTH_MASK			__BITS(18, 19)
+#define X86_DR7_DR1_CONDITION_MASK		__BITS(20, 21)
+#define X86_DR7_DR1_LENGTH_MASK			__BITS(22, 23)
+#define X86_DR7_DR2_CONDITION_MASK		__BITS(24, 25)
+#define X86_DR7_DR2_LENGTH_MASK			__BITS(26, 27)
+#define X86_DR7_DR3_CONDITION_MASK		__BITS(28, 29)
+#define X86_DR7_DR3_LENGTH_MASK			__BITS(30, 31)
 
 /*
- * X86_HW_WATCHPOINT_DR7_CONDITION_IO_READWRITE is currently unused
+ * X86_DR7_CONDITION_IO_READWRITE is currently unused
  * it requires DE (debug extension) flag in control register CR4 set
  * not all CPUs support it
  */
-enum x86_hw_watchpoint_condition {
-	X86_HW_WATCHPOINT_DR7_CONDITION_EXECUTION	= 0x0,
-	X86_HW_WATCHPOINT_DR7_CONDITION_DATA_WRITE	= 0x1,
-	X86_HW_WATCHPOINT_DR7_CONDITION_IO_READWRITE	= 0x2,
-	X86_HW_WATCHPOINT_DR7_CONDITION_DATA_READWRITE	= 0x3
-};
-
-/*
- * 0x2 is currently unimplemented - it reflects 8 bytes on modern CPUs
- */
-enum x86_hw_watchpoint_length {
-	X86_HW_WATCHPOINT_DR7_LENGTH_BYTE	= 0x0,
-	X86_HW_WATCHPOINT_DR7_LENGTH_TWOBYTES	= 0x1,
-	/* 0x2 undefined */
-	X86_HW_WATCHPOINT_DR7_LENGTH_FOURBYTES	= 0x3
+enum x86_dr7_condition {
+	X86_DR7_CONDITION_EXECUTION		= 0x0,
+	X86_DR7_CONDITION_DATA_WRITE		= 0x1,
+	X86_DR7_CONDITION_IO_READWRITE		= 0x2,
+	X86_DR7_CONDITION_DATA_READWRITE	= 0x3
 };
 
 /*
  * 0x2 is currently unimplemented - it reflects 8 bytes on modern CPUs
  */
-enum x86_hw_watchpoint_event {
-	X86_HW_WATCHPOINT_EVENT_NONE		= 0x0,
-	X86_HW_WATCHPOINT_EVENT_FIRED		= 0x1,
-	X86_HW_WATCHPOINT_EVENT_FIRED_AND_SSTEP	= 0x2,
+enum x86_dr7_length {
+	X86_DR7_LENGTH_BYTE		= 0x0,
+	X86_DR7_LENGTH_TWOBYTES		= 0x1,
+	/* 0x2 undefined */
+	X86_DR7_LENGTH_FOURBYTES	= 0x3
 };
 
-#if defined(_KMEMUSER) || defined(_KERNEL)
+/*
+ * The number of available watchpoint/breakpoint registers available since
+ * Intel 80386. New CPUs (x86_64) ship with up to 16 Debug Registers but they
+ * still offer the same number of watchpoints/breakpoints.
+ */
+#define X86_DBREGS	4
 
 /*
- * The number of available watchpoint registers available since Intel 80386
- * New CPUs ship with up to 16 Debug Registers but they still offer four
- * watchpoints, while there other registers are reserved
+ * Store the initial Debug Register state of CPU
+ * This copy will be used to initialize new debug register state
  */
-#define X86_HW_WATCHPOINTS	4
+void x86_dbregs_setup_initdbstate(void);
 
 /*
- * lwpid - 0 means all LWPs in the process
- * address - 0 means that watchpoint is disabled
+ * Reset CPU Debug Registers - to be used after returning to user context
  */
-struct x86_hw_watchpoint {
-	vaddr_t	address;
-	enum x86_hw_watchpoint_condition condition;
-	enum x86_hw_watchpoint_length length;
-};
+void x86_dbregs_clear(struct lwp *l);
 
-#endif /* !defined(_KMEMUSER) && !defined(_KERNEL) */
+/*
+ * Retrieve Debug Registers from LWP's PCB and save in regs
+ * In case of empty register set, initialize it
+ */
+void x86_dbregs_read(struct lwp *l, struct dbreg *regs);
 
-#if defined(_KERNEL)
 /*
  * Set CPU Debug Registers - to be used before entering user-land context
  */
-void set_x86_hw_watchpoints(struct lwp *l);
+void x86_dbregs_set(struct lwp *l);
 
 /*
- * Reset CPU Debug Registers - to be used after entering kernel context
+ * Store DR6 in LWP - to be used in trap function
  */
-void clear_x86_hw_watchpoints(void);
+void x86_dbregs_store_dr6(struct lwp *l);
 
 /*
  * Check if trap is triggered from user-land if so return nonzero value
  */
-int user_trap_x86_hw_watchpoint(void);
+int x86_dbregs_user_trap(void);
 
 /*
  * Check if trap is triggered from user-land if so return nonzero value
  */
-int x86_hw_watchpoint_type(int);
+int x86_dbregs_validate(const struct dbreg *regs);
 
 /*
- * Return register that fired
+ * Write new Debug Registers from regs into LWP's PCB
  */
-int x86_hw_watchpoint_reg(int);
-
-#endif /* !defined(_KERNEL) */
+void x86_dbregs_write(struct lwp *l, const struct dbreg *regs);
 
 #endif /* !_X86_DBREGS_H_ */
--- a/sys/arch/x86/x86/dbregs.c	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/x86/x86/dbregs.c	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: dbregs.c,v 1.4 2017/01/18 12:15:21 kamil Exp $	*/
+/*	$NetBSD: dbregs.c,v 1.5 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 2016 The NetBSD Foundation, Inc.
@@ -30,6 +30,7 @@
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/lwp.h>
+#include <sys/pool.h>
 #include <x86/cpufunc.h>
 #include <x86/dbregs.h>
 
@@ -38,129 +39,105 @@
 
 #include <machine/pmap.h>
 
-static void
-set_x86_hw_watchpoint(size_t idx, vaddr_t address,
-    enum x86_hw_watchpoint_condition condition,
-    enum x86_hw_watchpoint_length length)
-{
-	register_t dr;
+extern struct pool x86_dbregspl;
 
-	KASSERT(address < VM_MAXUSER_ADDRESS);
-
-	/* Read the original DR7 value in order to save existing watchpoints */
-	dr = rdr7();
+static struct dbreg initdbstate;
 
-	switch (idx) {
-	case 0:
-		ldr0(address);
-		dr |= X86_HW_WATCHPOINT_DR7_GLOBAL_DR0_BREAKPOINT;
-		dr |= __SHIFTIN(condition,
-		    X86_HW_WATCHPOINT_DR7_DR0_CONDITION_MASK);
-		dr |= __SHIFTIN(length,
-		    X86_HW_WATCHPOINT_DR7_DR0_LENGTH_MASK);
-		break;
-	case 1:
-		ldr1(address);
-		dr |= X86_HW_WATCHPOINT_DR7_GLOBAL_DR1_BREAKPOINT;
-		dr |= __SHIFTIN(condition,
-		    X86_HW_WATCHPOINT_DR7_DR1_CONDITION_MASK);
-		dr |= __SHIFTIN(length,
-		    X86_HW_WATCHPOINT_DR7_DR1_LENGTH_MASK);
-		break;
-	case 2:
-		ldr2(address);
-		dr |= X86_HW_WATCHPOINT_DR7_GLOBAL_DR2_BREAKPOINT;
-		dr |= __SHIFTIN(condition,
-		    X86_HW_WATCHPOINT_DR7_DR2_CONDITION_MASK);
-		dr |= __SHIFTIN(length,
-		    X86_HW_WATCHPOINT_DR7_DR2_LENGTH_MASK);
-		break;
-	case 3:
-		ldr3(address);
-		dr |= X86_HW_WATCHPOINT_DR7_GLOBAL_DR3_BREAKPOINT;
-		dr |= __SHIFTIN(condition,
-		    X86_HW_WATCHPOINT_DR7_DR3_CONDITION_MASK);
-		dr |= __SHIFTIN(length,
-		    X86_HW_WATCHPOINT_DR7_DR3_LENGTH_MASK);
-		break;
-	}
-
-	ldr7(dr);
+void
+x86_dbregs_setup_initdbstate(void)
+{
+	/* DR0-DR3 should always be 0 */
+	initdbstate.dr[0] = rdr0();
+	initdbstate.dr[1] = rdr1();
+	initdbstate.dr[2] = rdr2();
+	initdbstate.dr[3] = rdr3();
+	/* DR4-DR5 are reserved - skip */
+	/* DR6 and DR7 contain predefined nonzero bits */
+	initdbstate.dr[6] = rdr6();
+	initdbstate.dr[7] = rdr7();
+	/* DR8-DR15 are reserved - skip */
 }
 
-void
-set_x86_hw_watchpoints(struct lwp *l)
-{
-	size_t i;
-
-	/* Assert that there are available watchpoints */
-	KASSERT(l->l_md.md_flags & MDL_X86_HW_WATCHPOINTS);
-
-	/* Clear Debug Control Register (DR7) first */
-	ldr7(0);
+#define X86_BREAKPOINT_CONDITION_DETECTED	( \
+	X86_DR6_DR0_BREAKPOINT_CONDITION_DETECTED | \
+	X86_DR6_DR1_BREAKPOINT_CONDITION_DETECTED | \
+	X86_DR6_DR2_BREAKPOINT_CONDITION_DETECTED | \
+	X86_DR6_DR3_BREAKPOINT_CONDITION_DETECTED )
 
-	/*
-	 * Clear Debug Status Register (DR6) as these bits are never cleared
-	 * automatically by the processor
-	 *
-	 * Clear BREAKPOINT_CONDITION_DETECTED bits and ignore the rest
-	 */
-	ldr6(rdr6() &
-	    ~(X86_HW_WATCHPOINT_DR6_DR0_BREAKPOINT_CONDITION_DETECTED |
-	      X86_HW_WATCHPOINT_DR6_DR1_BREAKPOINT_CONDITION_DETECTED |
-	      X86_HW_WATCHPOINT_DR6_DR2_BREAKPOINT_CONDITION_DETECTED |
-	      X86_HW_WATCHPOINT_DR6_DR3_BREAKPOINT_CONDITION_DETECTED));
-
-	for (i = 0; i < X86_HW_WATCHPOINTS; i++) {
-		if (l->l_md.md_watchpoint[i].address != 0) {
-			set_x86_hw_watchpoint(i,
-			    l->l_md.md_watchpoint[i].address,
-			    l->l_md.md_watchpoint[i].condition,
-			    l->l_md.md_watchpoint[i].length);
-		}
-	}
-}
+#define X86_GLOBAL_BREAKPOINT	( \
+	X86_DR7_GLOBAL_DR0_BREAKPOINT | \
+	X86_DR7_GLOBAL_DR1_BREAKPOINT | \
+	X86_DR7_GLOBAL_DR2_BREAKPOINT | \
+	X86_DR7_GLOBAL_DR3_BREAKPOINT )
 
 void
-clear_x86_hw_watchpoints(void)
+x86_dbregs_clear(struct lwp *l)
 {
+	struct pcb *pcb = lwp_getpcb(l);
+
+	KASSERT(pcb->pcb_dbregs == NULL);
 
 	/*
 	 * It's sufficient to just disable Debug Control Register (DR7)
 	 * it will deactivate hardware watchpoints
 	 */
 	ldr7(0);
+
 	/*
 	 * However at some point we need to clear Debug Status Registers (DR6)
 	 * CPU will never do it automatically
 	 *
  	 * Clear BREAKPOINT_CONDITION_DETECTED bits and ignore the rest
 	 */
-	ldr6(rdr6() &
-	    ~(X86_HW_WATCHPOINT_DR6_DR0_BREAKPOINT_CONDITION_DETECTED |
-	      X86_HW_WATCHPOINT_DR6_DR1_BREAKPOINT_CONDITION_DETECTED |
-	      X86_HW_WATCHPOINT_DR6_DR2_BREAKPOINT_CONDITION_DETECTED |
-	      X86_HW_WATCHPOINT_DR6_DR3_BREAKPOINT_CONDITION_DETECTED));
+	ldr6(rdr6() & ~X86_BREAKPOINT_CONDITION_DETECTED);
+}
+
+void
+x86_dbregs_read(struct lwp *l, struct dbreg *regs)
+{
+	struct pcb *pcb = lwp_getpcb(l);
+
+	if (pcb->pcb_dbregs == NULL) {
+		pcb->pcb_dbregs = pool_get(&x86_dbregspl, PR_WAITOK);
+		memcpy(pcb->pcb_dbregs, &initdbstate, sizeof(initdbstate));
+	}
+	memcpy(regs, pcb->pcb_dbregs, sizeof(*regs));
 }
 
-/* Local temporary bitfield concept to compose event and register that fired */
-#define DR_EVENT_MASK		__BITS(7,0)
-#define DR_REGISTER_MASK	__BITS(15,8)
+void
+x86_dbregs_set(struct lwp *l)
+{
+	struct pcb *pcb = lwp_getpcb(l);
+
+	KASSERT(pcb->pcb_dbregs != NULL);
+
+	ldr0(pcb->pcb_dbregs->dr[0]);
+	ldr1(pcb->pcb_dbregs->dr[1]);
+	ldr2(pcb->pcb_dbregs->dr[2]);
+	ldr3(pcb->pcb_dbregs->dr[3]);
+
+	ldr6(pcb->pcb_dbregs->dr[6]);
+	ldr7(pcb->pcb_dbregs->dr[7]);
+}
+
+void
+x86_dbregs_store_dr6(struct lwp *l)
+{
+	struct pcb *pcb = lwp_getpcb(l);
+
+	KASSERT(pcb->pcb_dbregs != NULL);
+
+	pcb->pcb_dbregs->dr[6] = rdr6();
+}
 
 int
-user_trap_x86_hw_watchpoint(void)
+x86_dbregs_user_trap(void)
 {
 	register_t dr7, dr6;	/* debug registers dr6 and dr7 */
 	register_t bp;		/* breakpoint bits extracted from dr6 */
-	register_t dr;		/* temporary value of dr0-dr3 */
-	int rv;			/* register and event that fired (if any) */
 
 	dr7 = rdr7();
-	if ((dr7 &
-	    (X86_HW_WATCHPOINT_DR7_GLOBAL_DR0_BREAKPOINT |
-	     X86_HW_WATCHPOINT_DR7_GLOBAL_DR1_BREAKPOINT |
-	     X86_HW_WATCHPOINT_DR7_GLOBAL_DR2_BREAKPOINT |
-	     X86_HW_WATCHPOINT_DR7_GLOBAL_DR3_BREAKPOINT)) == 0) {
+	if ((dr7 & X86_GLOBAL_BREAKPOINT) == 0) {
 		/*
 		 * all Global Breakpoint bits in the DR7 register are zero,
 		 * thus the trap couldn't have been caused by the
@@ -170,11 +147,7 @@
 	}
 
 	dr6 = rdr6();
-	bp = dr6 & \
-	    (X86_HW_WATCHPOINT_DR6_DR0_BREAKPOINT_CONDITION_DETECTED |
-	     X86_HW_WATCHPOINT_DR6_DR1_BREAKPOINT_CONDITION_DETECTED |
-	     X86_HW_WATCHPOINT_DR6_DR2_BREAKPOINT_CONDITION_DETECTED |
-	     X86_HW_WATCHPOINT_DR6_DR3_BREAKPOINT_CONDITION_DETECTED);
+	bp = dr6 & X86_BREAKPOINT_CONDITION_DETECTED;
 
 	if (!bp) {
 		/*
@@ -185,88 +158,56 @@
 	}
 
 	/*
-	 * Clear Status Register (DR6) now as it's not done by CPU.
-	 *
- 	 * Clear BREAKPOINT_CONDITION_DETECTED and SINGLE_STEP bits and ignore
-	 * the rest.
-	 */
-	ldr6(dr6 &
-	   ~(X86_HW_WATCHPOINT_DR6_DR0_BREAKPOINT_CONDITION_DETECTED |
-	     X86_HW_WATCHPOINT_DR6_DR1_BREAKPOINT_CONDITION_DETECTED |
-	     X86_HW_WATCHPOINT_DR6_DR2_BREAKPOINT_CONDITION_DETECTED |
-	     X86_HW_WATCHPOINT_DR6_DR3_BREAKPOINT_CONDITION_DETECTED |
-	     X86_HW_WATCHPOINT_DR6_SINGLE_STEP));
-
-	/*
 	 * at least one of the breakpoints were hit, check to see
 	 * which ones and if any of them are user space addresses
 	 */
 
-	if (bp & X86_HW_WATCHPOINT_DR6_DR0_BREAKPOINT_CONDITION_DETECTED) {
-		dr = rdr0();	
-		if (dr < (vaddr_t)VM_MAXUSER_ADDRESS) {
-			rv = 0;
-			if (bp & X86_HW_WATCHPOINT_DR6_SINGLE_STEP)
-				rv |= X86_HW_WATCHPOINT_EVENT_FIRED_AND_SSTEP;
-			else
-				rv |= X86_HW_WATCHPOINT_EVENT_FIRED;
-			return rv;
-		}
-		
-	}
+	if (bp & X86_DR6_DR0_BREAKPOINT_CONDITION_DETECTED)
+		if (rdr0() < (vaddr_t)VM_MAXUSER_ADDRESS)
+			return 1;
 
-	if (bp & X86_HW_WATCHPOINT_DR6_DR1_BREAKPOINT_CONDITION_DETECTED) {
-		dr = rdr1();
-		if (dr < (vaddr_t)VM_MAXUSER_ADDRESS) {
-			rv = __SHIFTIN(1, DR_REGISTER_MASK);
-			if (bp & X86_HW_WATCHPOINT_DR6_SINGLE_STEP)
-				rv |= X86_HW_WATCHPOINT_EVENT_FIRED_AND_SSTEP;
-			else
-				rv |= X86_HW_WATCHPOINT_EVENT_FIRED;
-			return rv;
-		}
-		
-	}
+	if (bp & X86_DR6_DR1_BREAKPOINT_CONDITION_DETECTED)
+		if (rdr1() < (vaddr_t)VM_MAXUSER_ADDRESS)
+			return 1;
 
-	if (bp & X86_HW_WATCHPOINT_DR6_DR2_BREAKPOINT_CONDITION_DETECTED) {
-		dr = rdr2();
-		if (dr < (vaddr_t)VM_MAXUSER_ADDRESS) {
-			rv = __SHIFTIN(2, DR_REGISTER_MASK);
-			if (bp & X86_HW_WATCHPOINT_DR6_SINGLE_STEP)
-				rv |= X86_HW_WATCHPOINT_EVENT_FIRED_AND_SSTEP;
-			else
-				rv |= X86_HW_WATCHPOINT_EVENT_FIRED;
-			return rv;
-		}
-		
-	}
+	if (bp & X86_DR6_DR2_BREAKPOINT_CONDITION_DETECTED)
+		if (rdr2() < (vaddr_t)VM_MAXUSER_ADDRESS)
+			return 1;
 
-	if (bp & X86_HW_WATCHPOINT_DR6_DR3_BREAKPOINT_CONDITION_DETECTED) {
-		dr = rdr3();
-		if (dr < (vaddr_t)VM_MAXUSER_ADDRESS) {
-			rv = __SHIFTIN(3, DR_REGISTER_MASK);
-			if (bp & X86_HW_WATCHPOINT_DR6_SINGLE_STEP)
-				rv |= X86_HW_WATCHPOINT_EVENT_FIRED_AND_SSTEP;
-			else
-				rv |= X86_HW_WATCHPOINT_EVENT_FIRED;
-			return rv;
-		}
-		
-	}
+	if (bp & X86_DR6_DR3_BREAKPOINT_CONDITION_DETECTED)
+		if (rdr3() < (vaddr_t)VM_MAXUSER_ADDRESS)
+			return 1;
 
 	return 0;
 }
 
 int
-x86_hw_watchpoint_type(int wptnfo)
+x86_dbregs_validate(const struct dbreg *regs)
 {
+	size_t i;
 
-	return __SHIFTOUT(wptnfo, DR_EVENT_MASK);
+	/* Check that DR0-DR3 contain user-space address */
+	for (i = 0; i < X86_DBREGS; i++)
+		if (regs->dr[i] > (vaddr_t)VM_MAXUSER_ADDRESS)
+			return EINVAL;
+
+	/*
+	 * Skip checks for reserved registers (DR4-DR5, DR8-DR15).
+	 *
+	 * Don't validate DR6-DR7 as some bits are set by hardware and a user
+	 * cannot overwrite them.
+	 */
+
+	return 0;
 }
 
-int
-x86_hw_watchpoint_reg(int wptnfo)
+void
+x86_dbregs_write(struct lwp *l, const struct dbreg *regs)
 {
+	struct pcb *pcb = lwp_getpcb(l);
 
-	return __SHIFTOUT(wptnfo, DR_REGISTER_MASK);
+	if (pcb->pcb_dbregs == NULL)
+		pcb->pcb_dbregs = pool_get(&x86_dbregspl, PR_WAITOK);
+
+	memcpy(pcb->pcb_dbregs, regs, sizeof(*regs));
 }
--- a/sys/arch/x86/x86/vm_machdep.c	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/arch/x86/x86/vm_machdep.c	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: vm_machdep.c,v 1.27 2016/12/15 12:04:18 kamil Exp $	*/
+/*	$NetBSD: vm_machdep.c,v 1.28 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1982, 1986 The Regents of the University of California.
@@ -80,7 +80,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vm_machdep.c,v 1.27 2016/12/15 12:04:18 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vm_machdep.c,v 1.28 2017/02/23 03:34:22 kamil Exp $");
 
 #include "opt_mtrr.h"
 
@@ -105,6 +105,9 @@
 #endif
 
 #include <x86/fpu.h>
+#include <x86/dbregs.h>
+
+extern struct pool x86_dbregspl;
 
 void
 cpu_proc_fork(struct proc *p1, struct proc *p2)
@@ -157,6 +160,9 @@
 	/* Copy any additional fpu state */
 	fpu_save_area_fork(pcb2, pcb1);
 
+	/* Never inherit CPU Debug Registers */
+	pcb2->pcb_dbregs = NULL;
+
 #if defined(XEN)
 	pcb2->pcb_iopl = SEL_KPL;
 #endif
@@ -231,12 +237,6 @@
 	pcb2->pcb_esp = (int)sf;
 	pcb2->pcb_ebp = (int)l2;
 #endif
-
-	/*
-	 * Do not inherit hardware watchpoints. If they are desired, userland
-	 * should do it on its own.
-	 */
-	memset(l2->l_md.md_watchpoint, 0, sizeof(*l2->l_md.md_watchpoint));
 }
 
 /*
@@ -270,9 +270,17 @@
 void
 cpu_lwp_free2(struct lwp *l)
 {
+	struct pcb *pcb;
 
 	KASSERT(l->l_md.md_gc_ptp == NULL);
 	KASSERT(l->l_md.md_gc_pmap == NULL);
+
+	pcb = lwp_getpcb(l);
+
+	if (pcb->pcb_dbregs) {
+		pool_put(&x86_dbregspl, pcb->pcb_dbregs);
+		pcb->pcb_dbregs = NULL;
+	}
 }
 
 /*
--- a/sys/compat/netbsd32/netbsd32_ptrace.c	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/compat/netbsd32/netbsd32_ptrace.c	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: netbsd32_ptrace.c,v 1.3 2016/12/15 12:04:18 kamil Exp $	*/
+/*	$NetBSD: netbsd32_ptrace.c,v 1.4 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*
  * Copyright (c) 2016 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: netbsd32_ptrace.c,v 1.3 2016/12/15 12:04:18 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: netbsd32_ptrace.c,v 1.4 2017/02/23 03:34:22 kamil Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_ptrace.h"
@@ -58,8 +58,7 @@
 static void netbsd32_copyoutpiod(const struct ptrace_io_desc *, void *);
 static int netbsd32_doregs(struct lwp *, struct lwp *, struct uio *);
 static int netbsd32_dofpregs(struct lwp *, struct lwp *, struct uio *);
-static int netbsd32_dowatchpoint(struct lwp *, struct lwp *, int,
-	    struct ptrace_watchpoint *, void *, register_t *);
+static int netbsd32_dodbregs(struct lwp *, struct lwp *, struct uio *);
 
 
 static int
@@ -168,13 +167,41 @@
 }
 
 static int
-netbsd32_dowatchpoint(struct lwp *curl /*tracer*/, struct lwp *l /*traced*/,
-    int write, struct ptrace_watchpoint *pw, void *addr, register_t *retval)
+netbsd32_dodbregs(struct lwp *curl /*tracer*/,
+    struct lwp *l /*traced*/,
+    struct uio *uio)
 {
+#if defined(PT_GETDBREGS) || defined(PT_SETDBREGS)
+	process_dbreg32 r32;
+	int error;
+	char *kv;
+	size_t kl;
+
+	KASSERT(l->l_proc->p_flag & PK_32);
+	if (uio->uio_offset < 0 || uio->uio_offset > (off_t)sizeof(r32))
+		return EINVAL;
+	kl = sizeof(r32);
+	kv = (char *)&r32;
 
-	/* unimplemented */
+	kv += uio->uio_offset;
+	kl -= uio->uio_offset;
+	if (kl > uio->uio_resid)
+		kl = uio->uio_resid;
 
+	error = process_read_dbregs32(l, &r32, &kl);
+	if (error == 0)
+		error = uiomove(kv, kl, uio);
+	if (error == 0 && uio->uio_rw == UIO_WRITE) {
+		if (l->l_stat != LSSTOP)
+			error = EBUSY;
+		else
+			error = process_write_dbregs32(l, &r32, kl);
+	}
+	uio->uio_offset = 0;
+	return error;
+#else
 	return EINVAL;
+#endif
 }
 
 static struct ptrace_methods netbsd32_ptm = {
@@ -182,7 +209,7 @@
 	.ptm_copyoutpiod = netbsd32_copyoutpiod,
 	.ptm_doregs = netbsd32_doregs,
 	.ptm_dofpregs = netbsd32_dofpregs,
-	.ptm_dowatchpoint = netbsd32_dowatchpoint
+	.ptm_dodbregs = netbsd32_dodbregs
 };
 
 
--- a/sys/kern/sys_ptrace.c	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/kern/sys_ptrace.c	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: sys_ptrace.c,v 1.3 2016/12/15 12:04:18 kamil Exp $	*/
+/*	$NetBSD: sys_ptrace.c,v 1.4 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -118,7 +118,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_ptrace.c,v 1.3 2016/12/15 12:04:18 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_ptrace.c,v 1.4 2017/02/23 03:34:22 kamil Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ptrace.h"
@@ -169,7 +169,7 @@
 	.ptm_copyoutpiod = ptrace_copyoutpiod,
 	.ptm_doregs = process_doregs,
 	.ptm_dofpregs = process_dofpregs,
-	.ptm_dowatchpoint = process_dowatchpoint,
+	.ptm_dodbregs = process_dodbregs,
 };
 
 static const struct syscall_package ptrace_syscalls[] = {
--- a/sys/kern/sys_ptrace_common.c	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/kern/sys_ptrace_common.c	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: sys_ptrace_common.c,v 1.16 2017/02/23 00:50:09 kamil Exp $	*/
+/*	$NetBSD: sys_ptrace_common.c,v 1.17 2017/02/23 03:34:22 kamil Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -118,7 +118,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.16 2017/02/23 00:50:09 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.17 2017/02/23 03:34:22 kamil Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ptrace.h"
@@ -202,10 +202,11 @@
 #ifdef PT_SETFPREGS
 	case PT_SETFPREGS:
 #endif
-#ifdef __HAVE_PTRACE_WATCHPOINTS
-	case PT_READ_WATCHPOINT:
-	case PT_WRITE_WATCHPOINT:
-	case PT_COUNT_WATCHPOINTS:
+#ifdef PT_GETDBREGS
+	case PT_GETDBREGS:
+#endif
+#ifdef PT_SETDBREGS
+	case PT_SETDBREGS:
 #endif
 	case PT_SET_EVENT_MASK:
 	case PT_GET_EVENT_MASK:
@@ -307,9 +308,6 @@
 	struct ptrace_state ps;
 	struct ptrace_lwpinfo pl;
 	struct ptrace_siginfo psi;
-#ifdef __HAVE_PTRACE_WATCHPOINTS
-	struct ptrace_watchpoint pw;
-#endif
 	struct vmspace *vm;
 	int error, write, tmp, pheld;
 	int signo = 0;
@@ -424,10 +422,11 @@
 #ifdef PT_SETFPREGS
 	case  PT_SETFPREGS:
 #endif
-#ifdef __HAVE_PTRACE_WATCHPOINTS
-	case  PT_READ_WATCHPOINT:
-	case  PT_WRITE_WATCHPOINT:
-	case  PT_COUNT_WATCHPOINTS:
+#ifdef PT_GETDBREGS
+	case  PT_GETDBREGS:
+#endif
+#ifdef PT_SETDBREGS
+	case  PT_SETDBREGS:
 #endif
 #ifdef __HAVE_PTRACE_MACHDEP
 	PTRACE_MACHDEP_REQUEST_CASES
@@ -1226,29 +1225,17 @@
 		break;
 #endif
 
-#ifdef __HAVE_PTRACE_WATCHPOINTS
-		/*
-		 * The "write" variable is used as type of operation.
-		 * Possible values:
-		 *	0 - return the number of supported hardware watchpoints
-		 *	1 - set new watchpoint value
-		 *	2 - get existing watchpoint image
-		 */
-	case  PT_WRITE_WATCHPOINT:
+#ifdef PT_SETDBREGS
+	case  PT_SETDBREGS:
 		write = 1;
-	case  PT_READ_WATCHPOINT:
-		/* write = 0 done above */
-
-		if (data != sizeof(pw)) {
-			DPRINTF(("ptrace(%d): %d != %zu\n", req,
-			    data, sizeof(pe)));
-			error = EINVAL;
-			break;
-		}
-		error = copyin(addr, &pw, sizeof(pw));
-		if (error)
-			break;
-		tmp = pw.pw_lwpid;
+		/*FALLTHROUGH*/
+#endif
+#ifdef PT_GETDBREGS
+	case  PT_GETDBREGS:
+		/* write = 0 done above. */
+#endif
+#if defined(PT_SETDBREGS) || defined(PT_GETDBREGS)
+		tmp = data;
 		if (tmp != 0 && t->p_nlwps > 1) {
 			lwp_delref(lt);
 			mutex_enter(t->p_lock);
@@ -1261,15 +1248,23 @@
 			lwp_addref(lt);
 			mutex_exit(t->p_lock);
 		}
-		++write;
-	case  PT_COUNT_WATCHPOINTS:
-		if (!process_validwatchpoint(lt))
+		if (!process_validdbregs(lt))
 			error = EINVAL;
 		else {
-			lwp_lock(lt);
-			error = ptm->ptm_dowatchpoint(l, lt, write, &pw, addr,
-			    retval);
-			lwp_unlock(lt);
+			error = proc_vmspace_getref(p, &vm);
+			if (error)
+				break;
+			iov.iov_base = addr;
+			iov.iov_len = PROC_DBREGSZ(p);
+			uio.uio_iov = &iov;
+			uio.uio_iovcnt = 1;
+			uio.uio_offset = 0;
+			uio.uio_resid = iov.iov_len;
+			uio.uio_rw = write ? UIO_WRITE : UIO_READ;
+			uio.uio_vmspace = vm;
+
+			error = ptm->ptm_dodbregs(l, lt, &uio);
+			uvmspace_free(vm);
 		}
 		break;
 #endif
@@ -1391,6 +1386,55 @@
 #endif
 }
 
+int
+process_dodbregs(struct lwp *curl /*tracer*/,
+    struct lwp *l /*traced*/,
+    struct uio *uio)
+{
+#if defined(PT_GETDBREGS) || defined(PT_SETDBREGS)
+	int error;
+	struct dbreg r;
+	char *kv;
+	size_t kl;
+
+	if (uio->uio_offset < 0 || uio->uio_offset > (off_t)sizeof(r))
+		return EINVAL;
+
+	kl = sizeof(r);
+	kv = (char *)&r;
+
+	kv += uio->uio_offset;
+	kl -= uio->uio_offset;
+	if (kl > uio->uio_resid)
+		kl = uio->uio_resid;
+
+	error = process_read_dbregs(l, &r, &kl);
+	if (error == 0)
+		error = uiomove(kv, kl, uio);
+	if (error == 0 && uio->uio_rw == UIO_WRITE) {
+		if (l->l_stat != LSSTOP)
+			error = EBUSY;
+		else
+			error = process_write_dbregs(l, &r, kl);
+	}
+	uio->uio_offset = 0;
+	return error;
+#else
+	return EINVAL;
+#endif
+}
+
+int
+process_validdbregs(struct lwp *l)
+{
+
+#if defined(PT_SETDBREGS) || defined(PT_GETDBREGS)
+	return (l->l_flag & LW_SYSTEM) == 0;
+#else
+	return 0;
+#endif
+}
+
 static int
 process_auxv_offset(struct proc *p, struct uio *uio)
 {
@@ -1416,45 +1460,6 @@
 #endif
 	return 0;
 }
-
-int
-process_dowatchpoint(struct lwp *curl /*tracer*/, struct lwp *l /*traced*/,
-    int operation, struct ptrace_watchpoint *pw, void *addr,
-    register_t *retval)
-{
-
-#ifdef __HAVE_PTRACE_WATCHPOINTS
-	int error;
-
-	KASSERT(operation >= 0);
-	KASSERT(operation <= 2);
-
-	switch (operation) {
-	case 0:
-		return process_count_watchpoints(l, retval);
-	case 1:
-		error = process_read_watchpoint(l, pw);
-		if (error)
-			return error;
-		return copyout(pw, addr, sizeof(*pw));
-	default:
-		return process_write_watchpoint(l, pw);
-	}
-#else
-	return EINVAL;
-#endif
-}
-
-int
-process_validwatchpoint(struct lwp *l)
-{
-
-#ifdef __HAVE_PTRACE_WATCHPOINTS
-	return (l->l_flag & LW_SYSTEM) == 0;
-#else
-	return 0;
-#endif
-}
 #endif /* PTRACE */
 
 MODULE(MODULE_CLASS_EXEC, ptrace_common, "");
--- a/sys/sys/proc.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/sys/proc.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: proc.h,v 1.337 2017/01/14 06:36:52 kamil Exp $	*/
+/*	$NetBSD: proc.h,v 1.338 2017/02/23 03:34:23 kamil Exp $	*/
 
 /*-
  * Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -573,6 +573,8 @@
     sizeof(process_reg32) : sizeof(struct reg))
 #define PROC_FPREGSZ(p) (((p)->p_flag & PK_32) ? \
     sizeof(process_fpreg32) : sizeof(struct fpreg))
+#define PROC_DBREGSZ(p) (((p)->p_flag & PK_32) ? \
+    sizeof(process_dbreg32) : sizeof(struct dbreg))
 
 /*
  * PROCLIST_FOREACH: iterate on the given proclist, skipping PK_MARKER ones.
--- a/sys/sys/ptrace.h	Thu Feb 23 02:28:10 2017 +0000
+++ b/sys/sys/ptrace.h	Thu Feb 23 03:34:22 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: ptrace.h,v 1.57 2017/02/22 23:43:43 kamil Exp $	*/
+/*	$NetBSD: ptrace.h,v 1.58 2017/02/23 03:34:23 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1984, 1993
@@ -144,20 +144,6 @@
 #define PL_EVENT_SUSPENDED	2
 
 /*
- * Hardware Watchpoints
- *
- * MD code handles switch informing whether a particular watchpoint is enabled
- */
-typedef struct ptrace_watchpoint {
-	int		pw_index;	/* HW Watchpoint ID (count from 0) */
-	lwpid_t		pw_lwpid;	/* LWP described */
-	int		pw_type;	/* HW Watchpoint type w/ MD content */
-#ifdef __HAVE_PTRACE_WATCHPOINTS
-	struct mdpw	pw_md;		/* MD fields */
-#endif
-} ptrace_watchpoint_t;
-
-/*
  * Signal Information structure
  */
 typedef struct ptrace_siginfo {
@@ -178,6 +164,7 @@
 #define process_reg64 struct reg
 #endif
 #endif
+
 #if defined(PT_GETFPREGS) || defined(PT_SETFPREGS)
 struct fpreg;
 #ifndef process_fpreg32
@@ -187,12 +174,14 @@
 #define process_fpreg64 struct fpreg
 #endif
 #endif
-#ifdef __HAVE_PTRACE_WATCHPOINTS
-#ifndef process_watchpoint32
-#define process_watchpoint32 struct ptrace_watchpoint
+
+#if defined(PT_GETDBREGS) || defined(PT_SETDBREGS)
+struct fpreg;
+#ifndef process_dbreg32
+#define process_dbreg32 struct dbreg
 #endif
-#ifndef process_watchpoint64
-#define process_watchpoint64 struct ptrace_watchpoint
+#ifndef process_dbreg64
+#define process_dbreg64 struct dbreg
 #endif
 #endif
 
@@ -201,8 +190,7 @@
 	void (*ptm_copyoutpiod)(const struct ptrace_io_desc *, void *);
 	int (*ptm_doregs)(struct lwp *, struct lwp *, struct uio *);
 	int (*ptm_dofpregs)(struct lwp *, struct lwp *, struct uio *);
-	int (*ptm_dowatchpoint)(struct lwp *, struct lwp *, int,
-	    struct ptrace_watchpoint *, void *, register_t *);
+	int (*ptm_dodbregs)(struct lwp *, struct lwp *, struct uio *);
 };
 
 int	ptrace_init(void);
@@ -215,9 +203,8 @@
 int	process_dofpregs(struct lwp *, struct lwp *, struct uio *);
 int	process_validfpregs(struct lwp *);
 
-int	process_dowatchpoint(struct lwp *, struct lwp *, int,
-	    struct ptrace_watchpoint *, void *, register_t *);
-int	process_validwatchpoint(struct lwp *);
+int	process_dodbregs(struct lwp *, struct lwp *, struct uio *);
+int	process_validdbregs(struct lwp *);
 
 int	process_domem(struct lwp *, struct lwp *, struct uio *);
 
@@ -235,6 +222,15 @@
  * will #define process_read_regs32 to netbsd32_process_read_regs (etc).
  * In all other cases these #defines drop the size suffix.
  */
+#ifdef PT_GETDBREGS
+int	process_read_dbregs(struct lwp *, struct dbreg *, size_t *);
+#ifndef process_read_dbregs32
+#define process_read_dbregs32	process_read_dbregs
+#endif
+#ifndef process_read_dbregs64
+#define process_read_dbregs64	process_read_dbregs
+#endif
+#endif
 #ifdef PT_GETFPREGS
 int	process_read_fpregs(struct lwp *, struct fpreg *, size_t *);
 #ifndef process_read_fpregs32
@@ -255,6 +251,15 @@
 #endif
 int	process_set_pc(struct lwp *, void *);
 int	process_sstep(struct lwp *, int);
+#ifdef PT_SETDBREGS
+int	process_write_dbregs(struct lwp *, const struct dbreg *, size_t);
+#ifndef process_write_dbregs32
+#define process_write_dbregs32	process_write_dbregs
+#endif
+#ifndef process_write_dbregs64
+#define process_write_dbregs64	process_write_dbregs
+#endif
+#endif
 #ifdef PT_SETFPREGS
 int	process_write_fpregs(struct lwp *, const struct fpreg *, size_t);
 #ifndef process_write_fpregs32
@@ -274,32 +279,6 @@
 #endif
 #endif
 
-#ifdef __HAVE_PTRACE_WATCHPOINTS
-int	process_count_watchpoints(struct lwp *, register_t *retval);
-#ifndef process_count_watchpoints32
-#define process_count_watchpoints32	process_count_watchpoints
-#endif
-#ifndef process_count_watchpoints64
-#define process_count_watchpoints64	process_count_watchpoints
-#endif
-
-int	process_read_watchpoint(struct lwp *, struct ptrace_watchpoint *);
-#ifndef process_read_watchpoint32
-#define process_read_watchpoint32	process_read_watchpoint
-#endif
-#ifndef process_read_watchpoint64
-#define process_read_watchpoint64	process_read_watchpoint
-#endif
-
-int	process_write_watchpoint(struct lwp *, struct ptrace_watchpoint *);
-#ifndef process_write_watchpoint32
-#define process_write_watchpoint32	process_write_watchpoint
-#endif
-#ifndef process_write_watchpoint64
-#define process_write_watchpoint64	process_write_watchpoint
-#endif
-#endif
-
 int	ptrace_machdep_dorequest(struct lwp *, struct lwp *, int,
 	    void *, int);