Skip to content

Commit 9f58fdd

Browse files
Peter ZijlstraKAGA-KOKO
authored andcommitted
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 <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-by: Alexandre Chartre <[email protected]> Acked-by: Andy Lutomirski <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent f051f69 commit 9f58fdd

File tree

2 files changed

+57
-24
lines changed

2 files changed

+57
-24
lines changed

arch/x86/kernel/hw_breakpoint.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ static int hw_breakpoint_handler(struct die_args *args)
464464
{
465465
int i, cpu, rc = NOTIFY_STOP;
466466
struct perf_event *bp;
467-
unsigned long dr7, dr6;
467+
unsigned long dr6;
468468
unsigned long *dr6_p;
469469

470470
/* The DR6 value is pointed by args->err */
@@ -479,9 +479,6 @@ static int hw_breakpoint_handler(struct die_args *args)
479479
if ((dr6 & DR_TRAP_BITS) == 0)
480480
return NOTIFY_DONE;
481481

482-
get_debugreg(dr7, 7);
483-
/* Disable breakpoints during exception handling */
484-
set_debugreg(0UL, 7);
485482
/*
486483
* Assert that local interrupts are disabled
487484
* Reset the DRn bits in the virtualized register value.
@@ -538,7 +535,6 @@ static int hw_breakpoint_handler(struct die_args *args)
538535
(dr6 & (~DR_TRAP_BITS)))
539536
rc = NOTIFY_DONE;
540537

541-
set_debugreg(dr7, 7);
542538
put_cpu();
543539

544540
return rc;

arch/x86/kernel/traps.c

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,57 @@ static bool is_sysenter_singlestep(struct pt_regs *regs)
700700
#endif
701701
}
702702

703+
static __always_inline void debug_enter(unsigned long *dr6, unsigned long *dr7)
704+
{
705+
/*
706+
* Disable breakpoints during exception handling; recursive exceptions
707+
* are exceedingly 'fun'.
708+
*
709+
* Since this function is NOKPROBE, and that also applies to
710+
* HW_BREAKPOINT_X, we can't hit a breakpoint before this (XXX except a
711+
* HW_BREAKPOINT_W on our stack)
712+
*
713+
* Entry text is excluded for HW_BP_X and cpu_entry_area, which
714+
* includes the entry stack is excluded for everything.
715+
*/
716+
get_debugreg(*dr7, 7);
717+
set_debugreg(0, 7);
718+
719+
/*
720+
* Ensure the compiler doesn't lower the above statements into
721+
* the critical section; disabling breakpoints late would not
722+
* be good.
723+
*/
724+
barrier();
725+
726+
/*
727+
* The Intel SDM says:
728+
*
729+
* Certain debug exceptions may clear bits 0-3. The remaining
730+
* contents of the DR6 register are never cleared by the
731+
* processor. To avoid confusion in identifying debug
732+
* exceptions, debug handlers should clear the register before
733+
* returning to the interrupted task.
734+
*
735+
* Keep it simple: clear DR6 immediately.
736+
*/
737+
get_debugreg(*dr6, 6);
738+
set_debugreg(0, 6);
739+
/* Filter out all the reserved bits which are preset to 1 */
740+
*dr6 &= ~DR6_RESERVED;
741+
}
742+
743+
static __always_inline void debug_exit(unsigned long dr7)
744+
{
745+
/*
746+
* Ensure the compiler doesn't raise this statement into
747+
* the critical section; enabling breakpoints early would
748+
* not be good.
749+
*/
750+
barrier();
751+
set_debugreg(dr7, 7);
752+
}
753+
703754
/*
704755
* Our handling of the processor debug registers is non-trivial.
705756
* 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)
727778
dotraplinkage void do_debug(struct pt_regs *regs, long error_code)
728779
{
729780
struct task_struct *tsk = current;
781+
unsigned long dr6, dr7;
730782
int user_icebp = 0;
731-
unsigned long dr6;
732783
int si_code;
733784

734-
nmi_enter();
735-
736-
get_debugreg(dr6, 6);
737-
/*
738-
* The Intel SDM says:
739-
*
740-
* Certain debug exceptions may clear bits 0-3. The remaining
741-
* contents of the DR6 register are never cleared by the
742-
* processor. To avoid confusion in identifying debug
743-
* exceptions, debug handlers should clear the register before
744-
* returning to the interrupted task.
745-
*
746-
* Keep it simple: clear DR6 immediately.
747-
*/
748-
set_debugreg(0, 6);
785+
debug_enter(&dr6, &dr7);
749786

750-
/* Filter out all the reserved bits which are preset to 1 */
751-
dr6 &= ~DR6_RESERVED;
787+
nmi_enter();
752788

753789
/*
754790
* 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)
786822
#endif
787823

788824
if (notify_die(DIE_DEBUG, "debug", regs, (long)&dr6, error_code,
789-
SIGTRAP) == NOTIFY_STOP)
825+
SIGTRAP) == NOTIFY_STOP)
790826
goto exit;
791827

792828
/*
@@ -825,6 +861,7 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code)
825861

826862
exit:
827863
nmi_exit();
864+
debug_exit(dr7);
828865
}
829866
NOKPROBE_SYMBOL(do_debug);
830867

0 commit comments

Comments
 (0)