Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src Introduce PT_GETDBREGS and PT_SETDBREGS in ptrace(2) on i386...



details:   https://anonhg.NetBSD.org/src/rev/85b75f48a96a
branches:  trunk
changeset: 821953:85b75f48a96a
user:      kamil <kamil%NetBSD.org@localhost>
date:      Thu Feb 23 03:34:22 2017 +0000

description:
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>

diffstat:

 distrib/sets/lists/comp/md.amd64          |    4 +-
 distrib/sets/lists/comp/md.i386           |    4 +-
 sys/arch/amd64/amd64/machdep.c            |   24 +-
 sys/arch/amd64/amd64/netbsd32_machdep.c   |   58 +++++-
 sys/arch/amd64/amd64/process_machdep.c    |  143 +++++----------
 sys/arch/amd64/amd64/trap.c               |   13 +-
 sys/arch/amd64/include/netbsd32_machdep.h |    8 +-
 sys/arch/amd64/include/pcb.h              |    6 +-
 sys/arch/amd64/include/proc.h             |    4 +-
 sys/arch/amd64/include/ptrace.h           |   64 +------
 sys/arch/amd64/include/reg.h              |   15 +-
 sys/arch/amd64/include/userret.h          |   12 +-
 sys/arch/i386/i386/machdep.c              |   25 ++-
 sys/arch/i386/i386/process_machdep.c      |  142 +++++----------
 sys/arch/i386/i386/trap.c                 |   25 ++-
 sys/arch/i386/include/pcb.h               |    5 +-
 sys/arch/i386/include/proc.h              |    4 +-
 sys/arch/i386/include/ptrace.h            |   57 +-----
 sys/arch/i386/include/reg.h               |   14 +-
 sys/arch/i386/include/userret.h           |   12 +-
 sys/arch/x86/include/dbregs.h             |  138 ++++++--------
 sys/arch/x86/x86/dbregs.c                 |  273 +++++++++++------------------
 sys/arch/x86/x86/vm_machdep.c             |   24 +-
 sys/compat/netbsd32/netbsd32_ptrace.c     |   43 +++-
 sys/kern/sys_ptrace.c                     |    6 +-
 sys/kern/sys_ptrace_common.c              |  167 +++++++++--------
 sys/sys/proc.h                            |    4 +-
 sys/sys/ptrace.h                          |   81 +++-----
 28 files changed, 629 insertions(+), 746 deletions(-)

diffs (truncated from 2357 to 300 lines):

diff -r f2d772281658 -r 85b75f48a96a distrib/sets/lists/comp/md.amd64
--- 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
diff -r f2d772281658 -r 85b75f48a96a distrib/sets/lists/comp/md.i386
--- 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
diff -r f2d772281658 -r 85b75f48a96a sys/arch/amd64/amd64/machdep.c
--- 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
diff -r f2d772281658 -r 85b75f48a96a sys/arch/amd64/amd64/netbsd32_machdep.c
--- 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)
 {
        /* {
diff -r f2d772281658 -r 85b75f48a96a sys/arch/amd64/amd64/process_machdep.c
--- 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);
+



Home | Main Index | Thread Index | Old Index