Module Name: src Committed By: kamil Date: Wed Jan 18 05:12:00 UTC 2017
Modified Files: src/sys/arch/amd64/amd64: trap.c src/sys/arch/i386/i386: trap.c src/sys/arch/x86/include: dbregs.h src/sys/arch/x86/x86: dbregs.c Log Message: Embed hardware trap and its type that fired (x86), information for tracers Now x86 throws SIGTRAP on hardware exception with: - si_code TRAP_HWWPT - dedicated for hw assisted watchpoint interface - si_trap - unchanged (T_TRCTRAP) - si_trap2 - watchpoint number that fired - si_trap3 - watchpoint specific event description x86 returns in si_trap3 one of the field from <x86/dbregs.h> - X86_HW_WATCHPOINT_EVENT_FIRED - watchpoint fired - X86_HW_WATCHPOINT_EVENT_FIRED_AND_SSTEP - watchpoint fired under PT_STEP Othe changes: - restrict more code from <x86/dbregs.h> to _KERNEL Sponsored bt <The NetBSD Foundation> To generate a diff of this commit: cvs rdiff -u -r1.88 -r1.89 src/sys/arch/amd64/amd64/trap.c cvs rdiff -u -r1.281 -r1.282 src/sys/arch/i386/i386/trap.c cvs rdiff -u -r1.2 -r1.3 src/sys/arch/x86/include/dbregs.h cvs rdiff -u -r1.1 -r1.2 src/sys/arch/x86/x86/dbregs.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/amd64/amd64/trap.c diff -u src/sys/arch/amd64/amd64/trap.c:1.88 src/sys/arch/amd64/amd64/trap.c:1.89 --- src/sys/arch/amd64/amd64/trap.c:1.88 Thu Dec 15 12:04:17 2016 +++ src/sys/arch/amd64/amd64/trap.c Wed Jan 18 05:11:59 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: trap.c,v 1.88 2016/12/15 12:04:17 kamil Exp $ */ +/* $NetBSD: trap.c,v 1.89 2017/01/18 05:11:59 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.88 2016/12/15 12:04:17 kamil Exp $"); +__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.89 2017/01/18 05:11:59 kamil Exp $"); #include "opt_ddb.h" #include "opt_kgdb.h" @@ -230,7 +230,7 @@ trap(struct trapframe *frame) #endif ksiginfo_t ksi; void *onfault; - int type, error; + int type, error, wptnfo; uint64_t cr2; bool pfail; @@ -706,7 +706,11 @@ faultcommon: KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGTRAP; ksi.ksi_trap = type & ~T_USER; - if (type == (T_BPTFLT|T_USER)) + if ((wptnfo = user_trap_x86_hw_watchpoint())) { + ksi.ksi_code = TRAP_HWWPT; + 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 ksi.ksi_code = TRAP_TRACE; Index: src/sys/arch/i386/i386/trap.c diff -u src/sys/arch/i386/i386/trap.c:1.281 src/sys/arch/i386/i386/trap.c:1.282 --- src/sys/arch/i386/i386/trap.c:1.281 Tue Dec 13 10:54:27 2016 +++ src/sys/arch/i386/i386/trap.c Wed Jan 18 05:11:59 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: trap.c,v 1.281 2016/12/13 10:54:27 kamil Exp $ */ +/* $NetBSD: trap.c,v 1.282 2017/01/18 05:11:59 kamil Exp $ */ /*- * Copyright (c) 1998, 2000, 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -68,7 +68,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.281 2016/12/13 10:54:27 kamil Exp $"); +__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.282 2017/01/18 05:11:59 kamil Exp $"); #include "opt_ddb.h" #include "opt_kgdb.h" @@ -251,7 +251,7 @@ trap(struct trapframe *frame) struct trapframe *vframe; ksiginfo_t ksi; void *onfault; - int type, error; + int type, error, wptnfo; uint32_t cr2; bool pfail; @@ -700,7 +700,11 @@ faultcommon: KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGTRAP; ksi.ksi_trap = type & ~T_USER; - if (type == (T_BPTFLT|T_USER)) + if ((wptnfo = user_trap_x86_hw_watchpoint())) { + ksi.ksi_code = TRAP_HWWPT; + 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 ksi.ksi_code = TRAP_TRACE; Index: src/sys/arch/x86/include/dbregs.h diff -u src/sys/arch/x86/include/dbregs.h:1.2 src/sys/arch/x86/include/dbregs.h:1.3 --- src/sys/arch/x86/include/dbregs.h:1.2 Thu Dec 15 12:04:18 2016 +++ src/sys/arch/x86/include/dbregs.h Wed Jan 18 05:12:00 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: dbregs.h,v 1.2 2016/12/15 12:04:18 kamil Exp $ */ +/* $NetBSD: dbregs.h,v 1.3 2017/01/18 05:12:00 kamil Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ #ifndef _X86_DBREGS_H_ #define _X86_DBREGS_H_ -#if defined(_KMEMUSER) || defined(_KERNEL) +#if defined(_KERNEL) #include <sys/param.h> #include <sys/types.h> @@ -79,10 +79,10 @@ #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) */ +#endif /* !defined(_KERNEL) */ /* - * X86_HW_WATCHPOINT_DR7_CONDITION_IO_READWRITE is unused + * X86_HW_WATCHPOINT_DR7_CONDITION_IO_READWRITE is currently unused * it requires DE (debug extension) flag in control register CR4 set * not all CPUs support it */ @@ -103,6 +103,15 @@ enum x86_hw_watchpoint_length { X86_HW_WATCHPOINT_DR7_LENGTH_FOURBYTES = 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, +}; + #if defined(_KMEMUSER) || defined(_KERNEL) /* @@ -122,6 +131,9 @@ struct x86_hw_watchpoint { enum x86_hw_watchpoint_length length; }; +#endif /* !defined(_KMEMUSER) && !defined(_KERNEL) */ + +#if defined(_KERNEL) /* * Set CPU Debug Registers - to be used before entering user-land context */ @@ -134,11 +146,19 @@ 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 /* !defined(_KMEMUSER) && !defined(_KERNEL) */ +/* + * Check if trap is triggered from user-land if so return nonzero value + */ +int x86_hw_watchpoint_type(int); + +/* + * Return register that fired + */ +int x86_hw_watchpoint_reg(int); + +#endif /* !defined(_KERNEL) */ #endif /* !_X86_DBREGS_H_ */ Index: src/sys/arch/x86/x86/dbregs.c diff -u src/sys/arch/x86/x86/dbregs.c:1.1 src/sys/arch/x86/x86/dbregs.c:1.2 --- src/sys/arch/x86/x86/dbregs.c:1.1 Thu Dec 15 12:04:18 2016 +++ src/sys/arch/x86/x86/dbregs.c Wed Jan 18 05:12:00 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: dbregs.c,v 1.1 2016/12/15 12:04:18 kamil Exp $ */ +/* $NetBSD: dbregs.c,v 1.2 2017/01/18 05:12:00 kamil Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. @@ -150,14 +150,17 @@ clear_x86_hw_watchpoints(void) X86_HW_WATCHPOINT_DR6_DR3_BREAKPOINT_CONDITION_DETECTED)); } +/* Local temporary bitfield concept to compose event and register that fired */ +#define DR_EVENT_MASK __BITS(15,8) +#define DR_REGISTER_MASK __BITS(7,0) + 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; + register_t dr; /* temporary value of dr0-dr3 */ + int rv; /* register and event that fired (if any) */ dr7 = rdr7(); if ((dr7 & @@ -173,7 +176,6 @@ user_trap_x86_hw_watchpoint(void) return 0; } - nbp = 0; dr6 = rdr6(); bp = dr6 & \ (X86_HW_WATCHPOINT_DR6_DR0_BREAKPOINT_CONDITION_DETECTED | @@ -190,41 +192,88 @@ user_trap_x86_hw_watchpoint(void) } /* + * 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) - 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; + 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; + } + + } - /* - * 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)); + 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; + } + + } - return nbp; + 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_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; + } + } - /* - * None of the breakpoints are in user space. - */ return 0; } + +int +x86_hw_watchpoint_type(int wptnfo) +{ + + return __SHIFTOUT(wptnfo, DR_EVENT_MASK); +} + +int +x86_hw_watchpoint_reg(int wptnfo) +{ + + return __SHIFTOUT(wptnfo, DR_REGISTER_MASK); +}