Module Name: src Committed By: kamil Date: Thu Dec 15 12:04:18 UTC 2016
Modified Files: src/distrib/sets/lists/comp: md.amd64 md.i386 src/sys/arch/amd64/amd64: machdep.c netbsd32_machdep.c process_machdep.c trap.c src/sys/arch/amd64/conf: files.amd64 src/sys/arch/amd64/include: proc.h ptrace.h userret.h src/sys/arch/i386/conf: files.i386 src/sys/arch/i386/i386: machdep.c process_machdep.c src/sys/arch/i386/include: proc.h ptrace.h userret.h src/sys/arch/x86/include: Makefile dbregs.h src/sys/arch/x86/x86: vm_machdep.c src/sys/arch/xen/conf: files.xen src/sys/compat/netbsd32: netbsd32_ptrace.c src/sys/kern: sys_ptrace.c sys_ptrace_common.c src/sys/sys: ptrace.h Added Files: src/sys/arch/x86/x86: dbregs.c Log Message: Add support for hardware assisted watchpoints/breakpoints API in ptrace(2) Add new ptrace(2) calls: - PT_COUNT_WATCHPOINTS - count the number of available hardware watchpoints - PT_READ_WATCHPOINT - read struct ptrace_watchpoint from the kernel state - PT_WRITE_WATCHPOINT - write new struct ptrace_watchpoint state, this includes enabling and disabling watchpoints The ptrace_watchpoint structure contains MI and MD parts: typedef struct ptrace_watchpoint { int pw_index; /* HW Watchpoint ID (count from 0) */ lwpid_t pw_lwpid; /* LWP described */ struct mdpw pw_md; /* MD fields */ } ptrace_watchpoint_t; For example amd64 defines MD as follows: struct mdpw { void *md_address; int md_condition; int md_length; }; These calls are protected with the __HAVE_PTRACE_WATCHPOINTS guard. Tested on amd64, initial support added for i386 and XEN. Sponsored by <The NetBSD Foundation> To generate a diff of this commit: cvs rdiff -u -r1.239 -r1.240 src/distrib/sets/lists/comp/md.amd64 cvs rdiff -u -r1.159 -r1.160 src/distrib/sets/lists/comp/md.i386 cvs rdiff -u -r1.237 -r1.238 src/sys/arch/amd64/amd64/machdep.c cvs rdiff -u -r1.97 -r1.98 src/sys/arch/amd64/amd64/netbsd32_machdep.c cvs rdiff -u -r1.29 -r1.30 src/sys/arch/amd64/amd64/process_machdep.c cvs rdiff -u -r1.87 -r1.88 src/sys/arch/amd64/amd64/trap.c cvs rdiff -u -r1.87 -r1.88 src/sys/arch/amd64/conf/files.amd64 cvs rdiff -u -r1.19 -r1.20 src/sys/arch/amd64/include/proc.h cvs rdiff -u -r1.7 -r1.8 src/sys/arch/amd64/include/ptrace.h cvs rdiff -u -r1.9 -r1.10 src/sys/arch/amd64/include/userret.h cvs rdiff -u -r1.375 -r1.376 src/sys/arch/i386/conf/files.i386 cvs rdiff -u -r1.766 -r1.767 src/sys/arch/i386/i386/machdep.c cvs rdiff -u -r1.86 -r1.87 src/sys/arch/i386/i386/process_machdep.c cvs rdiff -u -r1.42 -r1.43 src/sys/arch/i386/include/proc.h cvs rdiff -u -r1.15 -r1.16 src/sys/arch/i386/include/ptrace.h cvs rdiff -u -r1.11 -r1.12 src/sys/arch/i386/include/userret.h cvs rdiff -u -r1.20 -r1.21 src/sys/arch/x86/include/Makefile cvs rdiff -u -r1.1 -r1.2 src/sys/arch/x86/include/dbregs.h cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/x86/dbregs.c cvs rdiff -u -r1.26 -r1.27 src/sys/arch/x86/x86/vm_machdep.c cvs rdiff -u -r1.142 -r1.143 src/sys/arch/xen/conf/files.xen cvs rdiff -u -r1.2 -r1.3 src/sys/compat/netbsd32/netbsd32_ptrace.c cvs rdiff -u -r1.2 -r1.3 src/sys/kern/sys_ptrace.c cvs rdiff -u -r1.6 -r1.7 src/sys/kern/sys_ptrace_common.c cvs rdiff -u -r1.49 -r1.50 src/sys/sys/ptrace.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/comp/md.amd64 diff -u src/distrib/sets/lists/comp/md.amd64:1.239 src/distrib/sets/lists/comp/md.amd64:1.240 --- src/distrib/sets/lists/comp/md.amd64:1.239 Wed Dec 14 12:59:51 2016 +++ src/distrib/sets/lists/comp/md.amd64 Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -# $NetBSD: md.amd64,v 1.239 2016/12/14 12:59:51 kre Exp $ +# $NetBSD: md.amd64,v 1.240 2016/12/15 12:04:18 kamil Exp $ ./usr/include/amd64 comp-c-include ./usr/include/amd64/ansi.h comp-c-include @@ -496,6 +496,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/float.h comp-c-include ./usr/include/x86/fpu.h comp-c-include ./usr/include/x86/ieee.h comp-c-include Index: src/distrib/sets/lists/comp/md.i386 diff -u src/distrib/sets/lists/comp/md.i386:1.159 src/distrib/sets/lists/comp/md.i386:1.160 --- src/distrib/sets/lists/comp/md.i386:1.159 Sat Oct 15 11:34:30 2016 +++ src/distrib/sets/lists/comp/md.i386 Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -# $NetBSD: md.i386,v 1.159 2016/10/15 11:34:30 christos Exp $ +# $NetBSD: md.i386,v 1.160 2016/12/15 12:04:18 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 @@ -370,6 +370,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/float.h comp-c-include ./usr/include/x86/fpu.h comp-c-include ./usr/include/x86/ieee.h comp-c-include Index: src/sys/arch/amd64/amd64/machdep.c diff -u src/sys/arch/amd64/amd64/machdep.c:1.237 src/sys/arch/amd64/amd64/machdep.c:1.238 --- src/sys/arch/amd64/amd64/machdep.c:1.237 Mon Dec 12 02:51:24 2016 +++ src/sys/arch/amd64/amd64/machdep.c Thu Dec 15 12:04:17 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: machdep.c,v 1.237 2016/12/12 02:51:24 pgoyette Exp $ */ +/* $NetBSD: machdep.c,v 1.238 2016/12/15 12:04:17 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.237 2016/12/12 02:51:24 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.238 2016/12/15 12:04:17 kamil Exp $"); /* #define XENDEBUG_LOW */ @@ -482,6 +482,7 @@ x86_64_proc0_tss_ldt_init(void) 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,6 +1317,8 @@ setregs(struct lwp *l, struct exec_packa 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); Index: src/sys/arch/amd64/amd64/netbsd32_machdep.c diff -u src/sys/arch/amd64/amd64/netbsd32_machdep.c:1.97 src/sys/arch/amd64/amd64/netbsd32_machdep.c:1.98 --- src/sys/arch/amd64/amd64/netbsd32_machdep.c:1.97 Wed Oct 19 09:44:00 2016 +++ src/sys/arch/amd64/amd64/netbsd32_machdep.c Thu Dec 15 12:04:17 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: netbsd32_machdep.c,v 1.97 2016/10/19 09:44:00 skrll Exp $ */ +/* $NetBSD: netbsd32_machdep.c,v 1.98 2016/12/15 12:04:17 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.97 2016/10/19 09:44:00 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: netbsd32_machdep.c,v 1.98 2016/12/15 12:04:17 kamil Exp $"); #ifdef _KERNEL_OPT #include "opt_compat_netbsd.h" @@ -129,6 +129,8 @@ netbsd32_setregs(struct lwp *l, struct e p->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(LUDATA32_SEL, SEL_UPL); tf->tf_es = LSEL(LUDATA32_SEL, SEL_UPL); Index: src/sys/arch/amd64/amd64/process_machdep.c diff -u src/sys/arch/amd64/amd64/process_machdep.c:1.29 src/sys/arch/amd64/amd64/process_machdep.c:1.30 --- src/sys/arch/amd64/amd64/process_machdep.c:1.29 Sat Feb 15 22:20:41 2014 +++ src/sys/arch/amd64/amd64/process_machdep.c Thu Dec 15 12:04:17 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: process_machdep.c,v 1.29 2014/02/15 22:20:41 dsl Exp $ */ +/* $NetBSD: process_machdep.c,v 1.30 2016/12/15 12:04:17 kamil Exp $ */ /*- * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. @@ -49,11 +49,21 @@ * * 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.29 2014/02/15 22:20:41 dsl Exp $"); +__KERNEL_RCSID(0, "$NetBSD: process_machdep.c,v 1.30 2016/12/15 12:04:17 kamil Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -66,6 +76,7 @@ __KERNEL_RCSID(0, "$NetBSD: process_mach #include <machine/psl.h> #include <machine/reg.h> #include <machine/segments.h> +#include <x86/dbregs.h> #include <x86/fpu.h> static inline struct trapframe *process_frame(struct lwp *); @@ -157,3 +168,81 @@ process_set_pc(struct lwp *l, void *addr 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_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_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); +} Index: src/sys/arch/amd64/amd64/trap.c diff -u src/sys/arch/amd64/amd64/trap.c:1.87 src/sys/arch/amd64/amd64/trap.c:1.88 --- src/sys/arch/amd64/amd64/trap.c:1.87 Wed Oct 26 22:02:14 2016 +++ src/sys/arch/amd64/amd64/trap.c Thu Dec 15 12:04:17 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: trap.c,v 1.87 2016/10/26 22:02:14 christos Exp $ */ +/* $NetBSD: trap.c,v 1.88 2016/12/15 12:04:17 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.87 2016/10/26 22:02:14 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.88 2016/12/15 12:04:17 kamil Exp $"); #include "opt_ddb.h" #include "opt_kgdb.h" @@ -673,6 +673,19 @@ faultcommon: } 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 (user_trap_x86_hw_watchpoint()) + break; + /* Check whether they single-stepped into a lcall. */ if (frame->tf_rip == (uint64_t)IDTVEC(oosyscall) || frame->tf_rip == (uint64_t)IDTVEC(osyscall) || Index: src/sys/arch/amd64/conf/files.amd64 diff -u src/sys/arch/amd64/conf/files.amd64:1.87 src/sys/arch/amd64/conf/files.amd64:1.88 --- src/sys/arch/amd64/conf/files.amd64:1.87 Sun Sep 6 07:17:14 2015 +++ src/sys/arch/amd64/conf/files.amd64 Thu Dec 15 12:04:17 2016 @@ -1,4 +1,4 @@ -# $NetBSD: files.amd64,v 1.87 2015/09/06 07:17:14 uebayasi Exp $ +# $NetBSD: files.amd64,v 1.88 2016/12/15 12:04:17 kamil Exp $ # # new style config file for amd64 architecture # @@ -49,6 +49,7 @@ file arch/amd64/amd64/machdep.c machdep file arch/amd64/amd64/process_machdep.c machdep file arch/amd64/amd64/trap.c machdep file arch/x86/x86/fpu.c machdep +file arch/x86/x86/dbregs.c machdep file arch/x86/x86/convert_xmm_s87.c machdep file arch/amd64/amd64/lock_stubs.S machdep file dev/cons.c machdep Index: src/sys/arch/amd64/include/proc.h diff -u src/sys/arch/amd64/include/proc.h:1.19 src/sys/arch/amd64/include/proc.h:1.20 --- src/sys/arch/amd64/include/proc.h:1.19 Thu Feb 20 18:19:10 2014 +++ src/sys/arch/amd64/include/proc.h Thu Dec 15 12:04:17 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: proc.h,v 1.19 2014/02/20 18:19:10 dsl Exp $ */ +/* $NetBSD: proc.h,v 1.20 2016/12/15 12:04:17 kamil Exp $ */ /* * Copyright (c) 1991 Regents of the University of California. @@ -38,6 +38,7 @@ #include <machine/frame.h> #include <machine/pcb.h> +#include <x86/dbregs.h> /* * Machine-dependent part of the lwp structure for amd64. @@ -51,10 +52,12 @@ struct mdlwp { 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_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; Index: src/sys/arch/amd64/include/ptrace.h diff -u src/sys/arch/amd64/include/ptrace.h:1.7 src/sys/arch/amd64/include/ptrace.h:1.8 --- src/sys/arch/amd64/include/ptrace.h:1.7 Wed Oct 19 09:44:00 2016 +++ src/sys/arch/amd64/include/ptrace.h Thu Dec 15 12:04:17 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: ptrace.h,v 1.7 2016/10/19 09:44:00 skrll Exp $ */ +/* $NetBSD: ptrace.h,v 1.8 2016/12/15 12:04:17 kamil Exp $ */ /* * Copyright (c) 1993 Christopher G. Demetriou @@ -36,18 +36,24 @@ /* * i386-dependent ptrace definitions */ -#define PT_STEP (PT_FIRSTMACH + 0) -#define PT_GETREGS (PT_FIRSTMACH + 1) -#define PT_SETREGS (PT_FIRSTMACH + 2) -#define PT_GETFPREGS (PT_FIRSTMACH + 3) -#define PT_SETFPREGS (PT_FIRSTMACH + 4) +#define PT_STEP (PT_FIRSTMACH + 0) +#define PT_GETREGS (PT_FIRSTMACH + 1) +#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_MACHDEP_STRINGS \ "PT_STEP", \ "PT_GETREGS", \ "PT_SETREGS", \ "PT_GETFPREGS", \ - "PT_SETFPREGS", + "PT_SETFPREGS", \ + "PT_READ_WATCHPOINT", \ + "PT_WRITE_WATCHPOINT", \ + "PT_COUNT_WATCHPOINTS" #include <machine/reg.h> #define PTRACE_REG_PC(r) (r)->regs[_REG_RIP] @@ -59,6 +65,34 @@ #define PTRACE_BREAKPOINT_SIZE 1 #define PTRACE_BREAKPOINT_ADJ 1 +#define __HAVE_PTRACE_WATCHPOINTS + +/* + * 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> + * + */ +struct mdpw { + void *md_address; + int md_condition; + int md_length; +}; + #ifdef _KERNEL_OPT #include "opt_compat_netbsd32.h" @@ -71,8 +105,13 @@ #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_reg32 struct reg32 #define process_fpreg32 struct fpreg32 +#define process_watchpoint32 struct ptrace_watchpoint32 #endif /* COMPAT_NETBSD32 */ #endif /* _KERNEL_OPT */ Index: src/sys/arch/amd64/include/userret.h diff -u src/sys/arch/amd64/include/userret.h:1.9 src/sys/arch/amd64/include/userret.h:1.10 --- src/sys/arch/amd64/include/userret.h:1.9 Mon Apr 28 20:23:12 2008 +++ src/sys/arch/amd64/include/userret.h Thu Dec 15 12:04:17 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: userret.h,v 1.9 2008/04/28 20:23:12 martin Exp $ */ +/* $NetBSD: userret.h,v 1.10 2016/12/15 12:04:17 kamil Exp $ */ /* * XXXfvdl same as i386 counterpart, but should probably be independent. @@ -67,6 +67,7 @@ */ #include <sys/userret.h> +#include <x86/dbregs.h> static __inline void userret(struct lwp *); @@ -80,4 +81,15 @@ userret(struct lwp *l) /* Invoke MI userret code */ mi_userret(l); + + /* + * Never mix debug registers with single step, while technically + * possible on x86 CPUs, it adds unnecessary complications we do + * not want to handle it. + */ + if ((l->l_md.md_regs->tf_rflags & PSL_T) == 0 && + l->l_md.md_flags & MDL_X86_HW_WATCHPOINTS) + set_x86_hw_watchpoints(l); + else + clear_x86_hw_watchpoints(); } Index: src/sys/arch/i386/conf/files.i386 diff -u src/sys/arch/i386/conf/files.i386:1.375 src/sys/arch/i386/conf/files.i386:1.376 --- src/sys/arch/i386/conf/files.i386:1.375 Tue Dec 13 10:54:27 2016 +++ src/sys/arch/i386/conf/files.i386 Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -# $NetBSD: files.i386,v 1.375 2016/12/13 10:54:27 kamil Exp $ +# $NetBSD: files.i386,v 1.376 2016/12/15 12:04:18 kamil Exp $ # # new style config file for i386 architecture # @@ -79,6 +79,7 @@ file arch/x86/x86/convert_xmm_s87.c file arch/i386/i386/trap.c file dev/cons.c file arch/x86/x86/fpu.c +file arch/x86/x86/dbregs.c file arch/i386/i386/mptramp.S multiprocessor Index: src/sys/arch/i386/i386/machdep.c diff -u src/sys/arch/i386/i386/machdep.c:1.766 src/sys/arch/i386/i386/machdep.c:1.767 --- src/sys/arch/i386/i386/machdep.c:1.766 Sun Dec 11 22:38:50 2016 +++ src/sys/arch/i386/i386/machdep.c Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: machdep.c,v 1.766 2016/12/11 22:38:50 martin Exp $ */ +/* $NetBSD: machdep.c,v 1.767 2016/12/15 12:04:18 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.766 2016/12/11 22:38:50 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.767 2016/12/15 12:04:18 kamil Exp $"); #include "opt_beep.h" #include "opt_compat_ibcs2.h" @@ -513,6 +513,7 @@ i386_proc0_tss_ldt_init(void) l->l_md.md_regs = (struct trapframe *)pcb->pcb_esp0 - 1; memcpy(&pcb->pcb_fsd, &gdt[GUDATA_SEL], sizeof(pcb->pcb_fsd)); memcpy(&pcb->pcb_gsd, &gdt[GUDATA_SEL], sizeof(pcb->pcb_gsd)); + memset(l->l_md.md_watchpoint, 0, sizeof(*l->l_md.md_watchpoint)); #ifndef XEN lldt(pmap_kernel()->pm_ldt_sel); @@ -846,6 +847,8 @@ setregs(struct lwp *l, struct exec_packa memcpy(&pcb->pcb_fsd, &gdt[GUDATA_SEL], sizeof(pcb->pcb_fsd)); memcpy(&pcb->pcb_gsd, &gdt[GUDATA_SEL], sizeof(pcb->pcb_gsd)); + memset(l->l_md.md_watchpoint, 0, sizeof(*l->l_md.md_watchpoint)); + tf = l->l_md.md_regs; tf->tf_gs = GSEL(GUGS_SEL, SEL_UPL); tf->tf_fs = GSEL(GUFS_SEL, SEL_UPL); Index: src/sys/arch/i386/i386/process_machdep.c diff -u src/sys/arch/i386/i386/process_machdep.c:1.86 src/sys/arch/i386/i386/process_machdep.c:1.87 --- src/sys/arch/i386/i386/process_machdep.c:1.86 Wed Nov 2 00:11:59 2016 +++ src/sys/arch/i386/i386/process_machdep.c Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: process_machdep.c,v 1.86 2016/11/02 00:11:59 pgoyette Exp $ */ +/* $NetBSD: process_machdep.c,v 1.87 2016/12/15 12:04:18 kamil Exp $ */ /*- * Copyright (c) 1998, 2000, 2001, 2008 The NetBSD Foundation, Inc. @@ -49,10 +49,20 @@ * * 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.86 2016/11/02 00:11:59 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: process_machdep.c,v 1.87 2016/12/15 12:04:18 kamil Exp $"); #include "opt_vm86.h" #include "opt_ptrace.h" @@ -71,6 +81,7 @@ __KERNEL_RCSID(0, "$NetBSD: process_mach #include <machine/reg.h> #include <machine/segments.h> +#include <x86/dbregs.h> #include <x86/fpu.h> #ifdef VM86 @@ -337,3 +348,81 @@ process_machdep_validxmmregs(struct proc } #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_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_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); +} Index: src/sys/arch/i386/include/proc.h diff -u src/sys/arch/i386/include/proc.h:1.42 src/sys/arch/i386/include/proc.h:1.43 --- src/sys/arch/i386/include/proc.h:1.42 Thu Feb 20 18:19:10 2014 +++ src/sys/arch/i386/include/proc.h Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: proc.h,v 1.42 2014/02/20 18:19:10 dsl Exp $ */ +/* $NetBSD: proc.h,v 1.43 2016/12/15 12:04:18 kamil Exp $ */ /* * Copyright (c) 1991 Regents of the University of California. @@ -36,6 +36,7 @@ #include <machine/frame.h> #include <machine/pcb.h> +#include <x86/dbregs.h> /* * Machine-dependent part of the lwp structure for i386. @@ -49,10 +50,12 @@ struct mdlwp { 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_IOPL 0x0002 /* XEN: i/o privilege */ +#define MDL_X86_HW_WATCHPOINTS 0x0004 /* has hardware watchpoints */ struct mdproc { int md_flags; Index: src/sys/arch/i386/include/ptrace.h diff -u src/sys/arch/i386/include/ptrace.h:1.15 src/sys/arch/i386/include/ptrace.h:1.16 --- src/sys/arch/i386/include/ptrace.h:1.15 Fri Sep 25 16:05:17 2015 +++ src/sys/arch/i386/include/ptrace.h Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: ptrace.h,v 1.15 2015/09/25 16:05:17 christos Exp $ */ +/* $NetBSD: ptrace.h,v 1.16 2016/12/15 12:04:18 kamil Exp $ */ /* * Copyright (c) 2001 Wasabi Systems, Inc. @@ -84,8 +84,11 @@ #define __HAVE_PROCFS_MACHDEP /* The machine-dependent ptrace(2) requests. */ -#define PT_GETXMMREGS (PT_FIRSTMACH + 5) -#define PT_SETXMMREGS (PT_FIRSTMACH + 6) +#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_MACHDEP_STRINGS \ "PT_STEP", \ @@ -94,7 +97,10 @@ "PT_GETFPREGS", \ "PT_SETFPREGS", \ "PT_GETXMMREGS", \ - "PT_SETXMMREGS", + "PT_SETXMMREGS", \ + "PT_READ_WATCHPOINT", \ + "PT_WRITE_WATCHPOINT", \ + "PT_COUNT_WATCHPOINTS" #include <machine/reg.h> #define PTRACE_REG_PC(r) (r)->r_eip @@ -106,6 +112,34 @@ #define PTRACE_BREAKPOINT_SIZE 1 #define PTRACE_BREAKPOINT_ADJ sizeof(PTRACE_BREAKPOINT) +#define __HAVE_PTRACE_WATCHPOINTS + +/* + * 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> + * + */ +struct mdpw { + void *md_address; + int md_condition; + int md_length; +}; + #ifdef _KERNEL /* Index: src/sys/arch/i386/include/userret.h diff -u src/sys/arch/i386/include/userret.h:1.11 src/sys/arch/i386/include/userret.h:1.12 --- src/sys/arch/i386/include/userret.h:1.11 Mon Apr 28 20:23:24 2008 +++ src/sys/arch/i386/include/userret.h Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: userret.h,v 1.11 2008/04/28 20:23:24 martin Exp $ */ +/* $NetBSD: userret.h,v 1.12 2016/12/15 12:04:18 kamil Exp $ */ /*- * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. @@ -63,6 +63,7 @@ */ #include <sys/userret.h> +#include <x86/dbregs.h> static __inline void userret(struct lwp *); @@ -76,4 +77,15 @@ userret(struct lwp *l) /* Invoke MI userret code */ mi_userret(l); + + /* + * Never mix debug registers with single step, while technically + * possible on x86 CPUs, it adds unnecessary complications we do + * not want to handle it. + */ + if ((l->l_md.md_regs->tf_eflags & PSL_T) == 0 && + l->l_md.md_flags & MDL_X86_HW_WATCHPOINTS) + set_x86_hw_watchpoints(l); + else + clear_x86_hw_watchpoints(); } Index: src/sys/arch/x86/include/Makefile diff -u src/sys/arch/x86/include/Makefile:1.20 src/sys/arch/x86/include/Makefile:1.21 --- src/sys/arch/x86/include/Makefile:1.20 Sat Feb 27 00:09:45 2016 +++ src/sys/arch/x86/include/Makefile Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.20 2016/02/27 00:09:45 tls Exp $ +# $NetBSD: Makefile,v 1.21 2016/12/15 12:04:18 kamil Exp $ INCSDIR=/usr/include/x86 @@ -11,6 +11,7 @@ INCS= aout_machdep.h \ cpu_ucode.h \ cputypes.h \ cpuvar.h \ + dbregs.h \ float.h \ fpu.h \ ieee.h ieeefp.h \ Index: src/sys/arch/x86/include/dbregs.h diff -u src/sys/arch/x86/include/dbregs.h:1.1 src/sys/arch/x86/include/dbregs.h:1.2 --- src/sys/arch/x86/include/dbregs.h:1.1 Sun Nov 27 14:49:21 2016 +++ src/sys/arch/x86/include/dbregs.h Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: dbregs.h,v 1.1 2016/11/27 14:49:21 kamil Exp $ */ +/* $NetBSD: dbregs.h,v 1.2 2016/12/15 12:04:18 kamil Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. @@ -30,16 +30,115 @@ #ifndef _X86_DBREGS_H_ #define _X86_DBREGS_H_ -#ifdef _KERNEL +#if defined(_KMEMUSER) || defined(_KERNEL) -struct lwp; -struct dbreg; +#include <sys/param.h> +#include <sys/types.h> -void reset_dbregs(void); +/* + * 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) + +/* + * CPU Debug Control Register (DR7) + * + * LOCAL_EXACT_BREAKPOINT and GLOBAL_EXACT_BREAKPOINT are no longer used since + * the P6 processor family - portable code should set these bits + * unconditionally in oder to get exact breakpoints + * + * 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_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(_KMEMUSER) && !defined(_KERNEL) */ + +/* + * X86_HW_WATCHPOINT_DR7_CONDITION_IO_READWRITE is 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 +}; + +#if defined(_KMEMUSER) || defined(_KERNEL) + +/* + * 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 + */ +#define X86_HW_WATCHPOINTS 4 + +/* + * lwpid - 0 means all LWPs in the process + * address - 0 means that watchpoint is disabled + */ +struct x86_hw_watchpoint { + vaddr_t address; + enum x86_hw_watchpoint_condition condition; + enum x86_hw_watchpoint_length length; +}; + +/* + * Set CPU Debug Registers - to be used before entering user-land context + */ +void set_x86_hw_watchpoints(struct lwp *l); -int process_write_dbregs(struct lwp *lwp, const struct dbreg *regs); -int process_read_dbregs(struct lwp *lwp, struct dbreg *regs); +/* + * Reset CPU Debug Registers - to be used after entering kernel context + */ +void clear_x86_hw_watchpoints(void); + +/* + * Check if trap is triggered from user-land if so return nonzero value + * + * This resets Debug Status Register (DR6) break point detection + */ +int user_trap_x86_hw_watchpoint(void); -#endif +#endif /* !defined(_KMEMUSER) && !defined(_KERNEL) */ -#endif /* _X86_FPU_H_ */ +#endif /* !_X86_DBREGS_H_ */ Index: src/sys/arch/x86/x86/vm_machdep.c diff -u src/sys/arch/x86/x86/vm_machdep.c:1.26 src/sys/arch/x86/x86/vm_machdep.c:1.27 --- src/sys/arch/x86/x86/vm_machdep.c:1.26 Tue Nov 8 03:05:36 2016 +++ src/sys/arch/x86/x86/vm_machdep.c Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: vm_machdep.c,v 1.26 2016/11/08 03:05:36 christos Exp $ */ +/* $NetBSD: vm_machdep.c,v 1.27 2016/12/15 12:04:18 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.26 2016/11/08 03:05:36 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: vm_machdep.c,v 1.27 2016/12/15 12:04:18 kamil Exp $"); #include "opt_mtrr.h" @@ -231,6 +231,12 @@ cpu_lwp_fork(struct lwp *l1, struct lwp 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)); } /* Index: src/sys/arch/xen/conf/files.xen diff -u src/sys/arch/xen/conf/files.xen:1.142 src/sys/arch/xen/conf/files.xen:1.143 --- src/sys/arch/xen/conf/files.xen:1.142 Tue Dec 13 10:54:27 2016 +++ src/sys/arch/xen/conf/files.xen Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -# $NetBSD: files.xen,v 1.142 2016/12/13 10:54:27 kamil Exp $ +# $NetBSD: files.xen,v 1.143 2016/12/15 12:04:18 kamil Exp $ # NetBSD: files.x86,v 1.10 2003/10/08 17:30:00 bouyer Exp # NetBSD: files.i386,v 1.254 2004/03/25 23:32:10 jmc Exp @@ -84,6 +84,7 @@ file arch/x86/x86/convert_xmm_s87.c file arch/x86/x86/db_memrw.c ddb | kgdb file arch/x86/x86/db_trace.c ddb file arch/x86/x86/fpu.c +file arch/x86/x86/dbregs.c file arch/xen/x86/hypervisor_machdep.c # file arch/x86/x86/mtrr_i686.c mtrr file arch/x86/x86/syscall.c Index: src/sys/compat/netbsd32/netbsd32_ptrace.c diff -u src/sys/compat/netbsd32/netbsd32_ptrace.c:1.2 src/sys/compat/netbsd32/netbsd32_ptrace.c:1.3 --- src/sys/compat/netbsd32/netbsd32_ptrace.c:1.2 Wed Nov 2 00:11:59 2016 +++ src/sys/compat/netbsd32/netbsd32_ptrace.c Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: netbsd32_ptrace.c,v 1.2 2016/11/02 00:11:59 pgoyette Exp $ */ +/* $NetBSD: netbsd32_ptrace.c,v 1.3 2016/12/15 12:04:18 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.2 2016/11/02 00:11:59 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: netbsd32_ptrace.c,v 1.3 2016/12/15 12:04:18 kamil Exp $"); #if defined(_KERNEL_OPT) #include "opt_ptrace.h" @@ -58,6 +58,8 @@ static int netbsd32_copyinpiod(struct pt 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 @@ -165,11 +167,22 @@ netbsd32_dofpregs(struct lwp *curl /*tra #endif } +static int +netbsd32_dowatchpoint(struct lwp *curl /*tracer*/, struct lwp *l /*traced*/, + int write, struct ptrace_watchpoint *pw, void *addr, register_t *retval) +{ + + /* unimplemented */ + + return EINVAL; +} + static struct ptrace_methods netbsd32_ptm = { .ptm_copyinpiod = netbsd32_copyinpiod, .ptm_copyoutpiod = netbsd32_copyoutpiod, .ptm_doregs = netbsd32_doregs, - .ptm_dofpregs = netbsd32_dofpregs + .ptm_dofpregs = netbsd32_dofpregs, + .ptm_dowatchpoint = netbsd32_dowatchpoint }; Index: src/sys/kern/sys_ptrace.c diff -u src/sys/kern/sys_ptrace.c:1.2 src/sys/kern/sys_ptrace.c:1.3 --- src/sys/kern/sys_ptrace.c:1.2 Thu Nov 3 03:57:05 2016 +++ src/sys/kern/sys_ptrace.c Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: sys_ptrace.c,v 1.2 2016/11/03 03:57:05 pgoyette Exp $ */ +/* $NetBSD: sys_ptrace.c,v 1.3 2016/12/15 12:04:18 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.2 2016/11/03 03:57:05 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sys_ptrace.c,v 1.3 2016/12/15 12:04:18 kamil Exp $"); #ifdef _KERNEL_OPT #include "opt_ptrace.h" @@ -169,6 +169,7 @@ static struct ptrace_methods native_ptm .ptm_copyoutpiod = ptrace_copyoutpiod, .ptm_doregs = process_doregs, .ptm_dofpregs = process_dofpregs, + .ptm_dowatchpoint = process_dowatchpoint, }; static const struct syscall_package ptrace_syscalls[] = { Index: src/sys/kern/sys_ptrace_common.c diff -u src/sys/kern/sys_ptrace_common.c:1.6 src/sys/kern/sys_ptrace_common.c:1.7 --- src/sys/kern/sys_ptrace_common.c:1.6 Mon Dec 5 22:07:16 2016 +++ src/sys/kern/sys_ptrace_common.c Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: sys_ptrace_common.c,v 1.6 2016/12/05 22:07:16 christos Exp $ */ +/* $NetBSD: sys_ptrace_common.c,v 1.7 2016/12/15 12:04:18 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.6 2016/12/05 22:07:16 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.7 2016/12/15 12:04:18 kamil Exp $"); #ifdef _KERNEL_OPT #include "opt_ptrace.h" @@ -202,6 +202,11 @@ ptrace_listener_cb(kauth_cred_t cred, ka #ifdef PT_SETFPREGS case PT_SETFPREGS: #endif +#ifdef __HAVE_PTRACE_WATCHPOINTS + case PT_READ_WATCHPOINT: + case PT_WRITE_WATCHPOINT: + case PT_COUNT_WATCHPOINTS: +#endif case PT_SET_EVENT_MASK: case PT_GET_EVENT_MASK: case PT_GET_PROCESS_STATE: @@ -295,6 +300,9 @@ do_ptrace(struct ptrace_methods *ptm, st struct ptrace_event pe; struct ptrace_state ps; struct ptrace_lwpinfo pl; +#ifdef __HAVE_PTRACE_WATCHPOINTS + struct ptrace_watchpoint pw; +#endif struct vmspace *vm; int error, write, tmp, pheld; int signo = 0; @@ -405,6 +413,11 @@ do_ptrace(struct ptrace_methods *ptm, st #ifdef PT_SETFPREGS case PT_SETFPREGS: #endif +#ifdef __HAVE_PTRACE_WATCHPOINTS + case PT_READ_WATCHPOINT: + case PT_WRITE_WATCHPOINT: + case PT_COUNT_WATCHPOINTS: +#endif #ifdef __HAVE_PTRACE_MACHDEP PTRACE_MACHDEP_REQUEST_CASES #endif @@ -993,6 +1006,54 @@ do_ptrace(struct ptrace_methods *ptm, st 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: + 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; + if (tmp != 0 && t->p_nlwps > 1) { + lwp_delref(lt); + mutex_enter(t->p_lock); + lt = lwp_find(t, tmp); + if (lt == NULL) { + mutex_exit(t->p_lock); + error = ESRCH; + break; + } + lwp_addref(lt); + mutex_exit(t->p_lock); + } + ++write; + case PT_COUNT_WATCHPOINTS: + if (!process_validwatchpoint(lt)) + error = EINVAL; + else { + lwp_lock(lt); + error = ptm->ptm_dowatchpoint(l, lt, write, &pw, addr, + retval); + lwp_unlock(lt); + } + break; +#endif + #ifdef __HAVE_PTRACE_MACHDEP PTRACE_MACHDEP_REQUEST_CASES error = ptrace_machdep_dorequest(l, lt, req, addr, data); @@ -1135,6 +1196,45 @@ process_auxv_offset(struct proc *p, stru #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, ""); @@ -1158,4 +1258,3 @@ ptrace_common_modcmd(modcmd_t cmd, void } return error; } - Index: src/sys/sys/ptrace.h diff -u src/sys/sys/ptrace.h:1.49 src/sys/sys/ptrace.h:1.50 --- src/sys/sys/ptrace.h:1.49 Fri Nov 4 18:14:04 2016 +++ src/sys/sys/ptrace.h Thu Dec 15 12:04:18 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: ptrace.h,v 1.49 2016/11/04 18:14:04 christos Exp $ */ +/* $NetBSD: ptrace.h,v 1.50 2016/12/15 12:04:18 kamil Exp $ */ /*- * Copyright (c) 1984, 1993 @@ -118,6 +118,19 @@ struct ptrace_lwpinfo { #define PL_EVENT_NONE 0 #define PL_EVENT_SIGNAL 1 +#ifdef __HAVE_PTRACE_WATCHPOINTS +/* + * 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 */ + struct mdpw pw_md; /* MD fields */ +} ptrace_watchpoint_t; +#endif + #ifdef _KERNEL #if defined(PT_GETREGS) || defined(PT_SETREGS) @@ -138,12 +151,22 @@ struct fpreg; #define process_fpreg64 struct fpreg #endif #endif +#ifdef __HAVE_PTRACE_WATCHPOINTS +#ifndef process_watchpoint32 +#define process_watchpoint32 struct ptrace_watchpoint +#endif +#ifndef process_watchpoint64 +#define process_watchpoint64 struct ptrace_watchpoint +#endif +#endif struct ptrace_methods { int (*ptm_copyinpiod)(struct ptrace_io_desc *, const void *); 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 ptrace_init(void); @@ -156,6 +179,10 @@ int process_validregs(struct lwp *); 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_domem(struct lwp *, struct lwp *, struct uio *); void process_stoptrace(void); @@ -211,6 +238,32 @@ int process_write_regs(struct lwp *, con #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 + #ifdef __HAVE_PROCFS_MACHDEP int ptrace_machdep_dorequest(struct lwp *, struct lwp *, int, void *, int); Added files: Index: src/sys/arch/x86/x86/dbregs.c diff -u /dev/null src/sys/arch/x86/x86/dbregs.c:1.1 --- /dev/null Thu Dec 15 12:04:19 2016 +++ src/sys/arch/x86/x86/dbregs.c Thu Dec 15 12:04:18 2016 @@ -0,0 +1,230 @@ +/* $NetBSD: dbregs.c,v 1.1 2016/12/15 12:04:18 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/lwp.h> +#include <x86/cpufunc.h> +#include <x86/dbregs.h> + +#include <uvm/uvm_prot.h> +#include <uvm/uvm_pmap.h> + +#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; + + KASSERT(address < VM_MAXUSER_ADDRESS); + + /* Read the original DR7 value in order to save existing watchpoints */ + dr = rdr7(); + + 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 +set_x86_hw_watchpoints(struct lwp *l) +{ + size_t i; + + /* Assert that Debug Registers are not mixed with Debug Trap Flag */ +#ifdef __x86_64__ + KASSERT((l->l_md.md_regs->tf_rflags & PSL_T) == 0); +#else + KASSERT((l->l_md.md_regs->tf_eflags & PSL_T) == 0); +#endif + + /* Assert that there are available watchpoints */ + KASSERT(l->l_md.md_flags & MDL_X86_HW_WATCHPOINTS); + + /* Clear Debug Control Register (DR7) first */ + ldr7(0); + + /* + * 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); + } + } +} + +void +clear_x86_hw_watchpoints(void) +{ + + /* + * 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)); +} + +int +user_trap_x86_hw_watchpoint(void) +{ + register_t dr7, dr6; /* debug registers dr6 and dr7 */ + register_t bp; /* breakpoint bits extracted from dr6 */ + int nbp; /* number of breakpoints that triggered */ + vaddr_t addr[X86_HW_WATCHPOINTS]; /* breakpoint addresses */ + int i; + + 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) { + /* + * all Global Breakpoint 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 & \ + (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); + + 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 & X86_HW_WATCHPOINT_DR6_DR0_BREAKPOINT_CONDITION_DETECTED) + addr[nbp++] = (vaddr_t)rdr0(); + if (bp & X86_HW_WATCHPOINT_DR6_DR1_BREAKPOINT_CONDITION_DETECTED) + addr[nbp++] = (vaddr_t)rdr1(); + if (bp & X86_HW_WATCHPOINT_DR6_DR2_BREAKPOINT_CONDITION_DETECTED) + addr[nbp++] = (vaddr_t)rdr2(); + if (bp & X86_HW_WATCHPOINT_DR6_DR3_BREAKPOINT_CONDITION_DETECTED) + addr[nbp++] = (vaddr_t)rdr3(); + + for (i = 0; i < nbp; i++) { + /* Check if addr[i] is in user space */ + if (addr[i] >= (vaddr_t)VM_MAXUSER_ADDRESS) + continue; + + /* + * Clear Status Register (DR6) now as it's not done by CPU. + * + * Clear BREAKPOINT_CONDITION_DETECTED 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)); + + return nbp; + } + + /* + * None of the breakpoints are in user space. + */ + return 0; +}