Commit 9f58fdde authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Thomas Gleixner

x86/db: Split out dr6/7 handling

DR6/7 should be handled before nmi_enter() is invoked and restore after
nmi_exit() to minimize the exposure.

Split it out into helper inlines and bring it into the correct order.
Signed-off-by: default avatarPeter Zijlstra <peterz@infradead.org>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Reviewed-by: default avatarAlexandre Chartre <alexandre.chartre@oracle.com>
Acked-by: default avatarAndy Lutomirski <luto@kernel.org>
Link: https://lkml.kernel.org/r/20200505135314.808628211@linutronix.de


parent f051f697
...@@ -464,7 +464,7 @@ static int hw_breakpoint_handler(struct die_args *args) ...@@ -464,7 +464,7 @@ static int hw_breakpoint_handler(struct die_args *args)
{ {
int i, cpu, rc = NOTIFY_STOP; int i, cpu, rc = NOTIFY_STOP;
struct perf_event *bp; struct perf_event *bp;
unsigned long dr7, dr6; unsigned long dr6;
unsigned long *dr6_p; unsigned long *dr6_p;
/* The DR6 value is pointed by args->err */ /* The DR6 value is pointed by args->err */
...@@ -479,9 +479,6 @@ static int hw_breakpoint_handler(struct die_args *args) ...@@ -479,9 +479,6 @@ static int hw_breakpoint_handler(struct die_args *args)
if ((dr6 & DR_TRAP_BITS) == 0) if ((dr6 & DR_TRAP_BITS) == 0)
return NOTIFY_DONE; return NOTIFY_DONE;
get_debugreg(dr7, 7);
/* Disable breakpoints during exception handling */
set_debugreg(0UL, 7);
/* /*
* Assert that local interrupts are disabled * Assert that local interrupts are disabled
* Reset the DRn bits in the virtualized register value. * Reset the DRn bits in the virtualized register value.
...@@ -538,7 +535,6 @@ static int hw_breakpoint_handler(struct die_args *args) ...@@ -538,7 +535,6 @@ static int hw_breakpoint_handler(struct die_args *args)
(dr6 & (~DR_TRAP_BITS))) (dr6 & (~DR_TRAP_BITS)))
rc = NOTIFY_DONE; rc = NOTIFY_DONE;
set_debugreg(dr7, 7);
put_cpu(); put_cpu();
return rc; return rc;
......
...@@ -700,6 +700,57 @@ static bool is_sysenter_singlestep(struct pt_regs *regs) ...@@ -700,6 +700,57 @@ static bool is_sysenter_singlestep(struct pt_regs *regs)
#endif #endif
} }
static __always_inline void debug_enter(unsigned long *dr6, unsigned long *dr7)
{
/*
* Disable breakpoints during exception handling; recursive exceptions
* are exceedingly 'fun'.
*
* Since this function is NOKPROBE, and that also applies to
* HW_BREAKPOINT_X, we can't hit a breakpoint before this (XXX except a
* HW_BREAKPOINT_W on our stack)
*
* Entry text is excluded for HW_BP_X and cpu_entry_area, which
* includes the entry stack is excluded for everything.
*/
get_debugreg(*dr7, 7);
set_debugreg(0, 7);
/*
* Ensure the compiler doesn't lower the above statements into
* the critical section; disabling breakpoints late would not
* be good.
*/
barrier();
/*
* The Intel SDM says:
*
* Certain debug exceptions may clear bits 0-3. The remaining
* contents of the DR6 register are never cleared by the
* processor. To avoid confusion in identifying debug
* exceptions, debug handlers should clear the register before
* returning to the interrupted task.
*
* Keep it simple: clear DR6 immediately.
*/
get_debugreg(*dr6, 6);
set_debugreg(0, 6);
/* Filter out all the reserved bits which are preset to 1 */
*dr6 &= ~DR6_RESERVED;
}
static __always_inline void debug_exit(unsigned long dr7)
{
/*
* Ensure the compiler doesn't raise this statement into
* the critical section; enabling breakpoints early would
* not be good.
*/
barrier();
set_debugreg(dr7, 7);
}
/* /*
* Our handling of the processor debug registers is non-trivial. * Our handling of the processor debug registers is non-trivial.
* We do not clear them on entry and exit from the kernel. Therefore * We do not clear them on entry and exit from the kernel. Therefore
...@@ -727,28 +778,13 @@ static bool is_sysenter_singlestep(struct pt_regs *regs) ...@@ -727,28 +778,13 @@ static bool is_sysenter_singlestep(struct pt_regs *regs)
dotraplinkage void do_debug(struct pt_regs *regs, long error_code) dotraplinkage void do_debug(struct pt_regs *regs, long error_code)
{ {
struct task_struct *tsk = current; struct task_struct *tsk = current;
unsigned long dr6, dr7;
int user_icebp = 0; int user_icebp = 0;
unsigned long dr6;
int si_code; int si_code;
nmi_enter(); debug_enter(&dr6, &dr7);
get_debugreg(dr6, 6);
/*
* The Intel SDM says:
*
* Certain debug exceptions may clear bits 0-3. The remaining
* contents of the DR6 register are never cleared by the
* processor. To avoid confusion in identifying debug
* exceptions, debug handlers should clear the register before
* returning to the interrupted task.
*
* Keep it simple: clear DR6 immediately.
*/
set_debugreg(0, 6);
/* Filter out all the reserved bits which are preset to 1 */ nmi_enter();
dr6 &= ~DR6_RESERVED;
/* /*
* The SDM says "The processor clears the BTF flag when it * The SDM says "The processor clears the BTF flag when it
...@@ -786,7 +822,7 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code) ...@@ -786,7 +822,7 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code)
#endif #endif
if (notify_die(DIE_DEBUG, "debug", regs, (long)&dr6, error_code, if (notify_die(DIE_DEBUG, "debug", regs, (long)&dr6, error_code,
SIGTRAP) == NOTIFY_STOP) SIGTRAP) == NOTIFY_STOP)
goto exit; goto exit;
/* /*
...@@ -825,6 +861,7 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code) ...@@ -825,6 +861,7 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code)
exit: exit:
nmi_exit(); nmi_exit();
debug_exit(dr7);
} }
NOKPROBE_SYMBOL(do_debug); NOKPROBE_SYMBOL(do_debug);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment