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: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Alexandre Chartre <alexandre.chartre@oracle.com> Acked-by: Andy Lutomirski <luto@kernel.org> Link: https://lkml.kernel.org/r/20200505135314.808628211@linutronix.de
This commit is contained in:
parent
f051f69795
commit
9f58fdde95
@ -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)
|
|||||||
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)
|
|||||||
(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)
|
|||||||
#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,29 +778,14 @@ 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;
|
||||||
|
|
||||||
|
debug_enter(&dr6, &dr7);
|
||||||
|
|
||||||
nmi_enter();
|
nmi_enter();
|
||||||
|
|
||||||
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 */
|
|
||||||
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
|
||||||
* generates a debug exception." Clear TIF_BLOCKSTEP to keep
|
* generates a debug exception." Clear TIF_BLOCKSTEP to keep
|
||||||
@ -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)
|
|||||||
|
|
||||||
exit:
|
exit:
|
||||||
nmi_exit();
|
nmi_exit();
|
||||||
|
debug_exit(dr7);
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(do_debug);
|
NOKPROBE_SYMBOL(do_debug);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user