Skip to content

Commit d53d9bc

Browse files
Peter ZijlstraKAGA-KOKO
authored andcommitted
x86/debug: Change thread.debugreg6 to thread.virtual_dr6
Current usage of thread.debugreg6 is convoluted at best. It starts life as a copy of the hardware DR6 value, but then various bits are cleared and set. Replace this with a new variable thread.virtual_dr6 that is initialized to 0 when DR6 is read and only gains bits, at the same time the actual (on stack) dr6 value which is read from the hardware only gets bits cleared. Suggested-by: Andy Lutomirski <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Tested-by: Daniel Thompson <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent f4956cf commit d53d9bc

File tree

5 files changed

+26
-24
lines changed

5 files changed

+26
-24
lines changed

arch/x86/include/asm/processor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ struct thread_struct {
517517
/* Save middle states of ptrace breakpoints */
518518
struct perf_event *ptrace_bps[HBP_NUM];
519519
/* Debug status used for traps, single steps, etc... */
520-
unsigned long debugreg6;
520+
unsigned long virtual_dr6;
521521
/* Keep track of the exact dr7 value set by the user */
522522
unsigned long ptrace_dr7;
523523
/* Fault info: */

arch/x86/kernel/hw_breakpoint.c

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
454454
t->ptrace_bps[i] = NULL;
455455
}
456456

457-
t->debugreg6 = 0;
457+
t->virtual_dr6 = 0;
458458
t->ptrace_dr7 = 0;
459459
}
460460

@@ -489,8 +489,8 @@ static int hw_breakpoint_handler(struct die_args *args)
489489
{
490490
int i, rc = NOTIFY_STOP;
491491
struct perf_event *bp;
492-
unsigned long dr6;
493492
unsigned long *dr6_p;
493+
unsigned long dr6;
494494

495495
/* The DR6 value is pointed by args->err */
496496
dr6_p = (unsigned long *)ERR_PTR(args->err);
@@ -504,12 +504,6 @@ static int hw_breakpoint_handler(struct die_args *args)
504504
if ((dr6 & DR_TRAP_BITS) == 0)
505505
return NOTIFY_DONE;
506506

507-
/*
508-
* Reset the DRn bits in the virtualized register value.
509-
* The ptrace trigger routine will add in whatever is needed.
510-
*/
511-
current->thread.debugreg6 &= ~DR_TRAP_BITS;
512-
513507
/* Handle all the breakpoints that were triggered */
514508
for (i = 0; i < HBP_NUM; ++i) {
515509
if (likely(!(dr6 & (DR_TRAP0 << i))))
@@ -554,7 +548,7 @@ static int hw_breakpoint_handler(struct die_args *args)
554548
* breakpoints (to generate signals) and b) when the system has
555549
* taken exception due to multiple causes
556550
*/
557-
if ((current->thread.debugreg6 & DR_TRAP_BITS) ||
551+
if ((current->thread.virtual_dr6 & DR_TRAP_BITS) ||
558552
(dr6 & (~DR_TRAP_BITS)))
559553
rc = NOTIFY_DONE;
560554

arch/x86/kernel/kgdb.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -629,9 +629,10 @@ static void kgdb_hw_overflow_handler(struct perf_event *event,
629629
struct task_struct *tsk = current;
630630
int i;
631631

632-
for (i = 0; i < 4; i++)
632+
for (i = 0; i < 4; i++) {
633633
if (breakinfo[i].enabled)
634-
tsk->thread.debugreg6 |= (DR_TRAP0 << i);
634+
tsk->thread.virtual_dr6 |= (DR_TRAP0 << i);
635+
}
635636
}
636637

637638
void kgdb_arch_late(void)

arch/x86/kernel/ptrace.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ static void ptrace_triggered(struct perf_event *bp,
465465
break;
466466
}
467467

468-
thread->debugreg6 |= (DR_TRAP0 << i);
468+
thread->virtual_dr6 |= (DR_TRAP0 << i);
469469
}
470470

471471
/*
@@ -601,7 +601,7 @@ static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n)
601601
if (bp)
602602
val = bp->hw.info.address;
603603
} else if (n == 6) {
604-
val = thread->debugreg6 ^ DR6_RESERVED; /* Flip back to arch polarity */
604+
val = thread->virtual_dr6 ^ DR6_RESERVED; /* Flip back to arch polarity */
605605
} else if (n == 7) {
606606
val = thread->ptrace_dr7;
607607
}
@@ -657,7 +657,7 @@ static int ptrace_set_debugreg(struct task_struct *tsk, int n,
657657
if (n < HBP_NUM) {
658658
rc = ptrace_set_breakpoint_addr(tsk, n, val);
659659
} else if (n == 6) {
660-
thread->debugreg6 = val ^ DR6_RESERVED; /* Flip to positive polarity */
660+
thread->virtual_dr6 = val ^ DR6_RESERVED; /* Flip to positive polarity */
661661
rc = 0;
662662
} else if (n == 7) {
663663
rc = ptrace_write_dr7(tsk, val);

arch/x86/kernel/traps.c

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,12 @@ static __always_inline unsigned long debug_read_clear_dr6(void)
748748
set_debugreg(DR6_RESERVED, 6);
749749
dr6 ^= DR6_RESERVED; /* Flip to positive polarity */
750750

751+
/*
752+
* Clear the virtual DR6 value, ptrace routines will set bits here for
753+
* things we want signals for.
754+
*/
755+
current->thread.virtual_dr6 = 0;
756+
751757
/*
752758
* The SDM says "The processor clears the BTF flag when it
753759
* generates a debug exception." Clear TIF_BLOCKSTEP to keep
@@ -785,17 +791,16 @@ static __always_inline unsigned long debug_read_clear_dr6(void)
785791

786792
static bool notify_debug(struct pt_regs *regs, unsigned long *dr6)
787793
{
788-
struct task_struct *tsk = current;
789-
790-
/* Store the virtualized DR6 value */
791-
tsk->thread.debugreg6 = *dr6;
792-
794+
/*
795+
* Notifiers will clear bits in @dr6 to indicate the event has been
796+
* consumed - hw_breakpoint_handler(), single_stop_cont().
797+
*
798+
* Notifiers will set bits in @virtual_dr6 to indicate the desire
799+
* for signals - ptrace_triggered(), kgdb_hw_overflow_handler().
800+
*/
793801
if (notify_die(DIE_DEBUG, "debug", regs, (long)dr6, 0, SIGTRAP) == NOTIFY_STOP)
794802
return true;
795803

796-
/* Reload the DR6 value, the notifier might have changed it */
797-
*dr6 = tsk->thread.debugreg6;
798-
799804
return false;
800805
}
801806

@@ -853,7 +858,7 @@ static __always_inline void exc_debug_kernel(struct pt_regs *regs,
853858
* A known way to trigger this is through QEMU's GDB stub,
854859
* which leaks #DB into the guest and causes IST recursion.
855860
*/
856-
if (WARN_ON_ONCE(current->thread.debugreg6 & DR_STEP))
861+
if (WARN_ON_ONCE(dr6 & DR_STEP))
857862
regs->flags &= ~X86_EFLAGS_TF;
858863
out:
859864
instrumentation_end();
@@ -903,6 +908,8 @@ static __always_inline void exc_debug_user(struct pt_regs *regs,
903908
goto out_irq;
904909
}
905910

911+
/* Add the virtual_dr6 bits for signals. */
912+
dr6 |= current->thread.virtual_dr6;
906913
if (dr6 & (DR_STEP | DR_TRAP_BITS) || icebp)
907914
send_sigtrap(regs, 0, get_si_code(dr6));
908915

0 commit comments

Comments
 (0)