Skip to content

Commit 5f465c1

Browse files
xinli-intelhansendc
authored andcommitted
x86/traps: Initialize DR6 by writing its architectural reset value
Initialize DR6 by writing its architectural reset value to avoid incorrectly zeroing DR6 to clear DR6.BLD at boot time, which leads to a false bus lock detected warning. The Intel SDM says: 1) Certain debug exceptions may clear bits 0-3 of DR6. 2) BLD induced #DB clears DR6.BLD and any other debug exception doesn't modify DR6.BLD. 3) RTM induced #DB clears DR6.RTM and any other debug exception sets DR6.RTM. To avoid confusion in identifying debug exceptions, debug handlers should set DR6.BLD and DR6.RTM, and clear other DR6 bits before returning. The DR6 architectural reset value 0xFFFF0FF0, already defined as macro DR6_RESERVED, satisfies these requirements, so just use it to reinitialize DR6 whenever needed. Since clear_all_debug_regs() no longer zeros all debug registers, rename it to initialize_debug_regs() to better reflect its current behavior. Since debug_read_clear_dr6() no longer clears DR6, rename it to debug_read_reset_dr6() to better reflect its current behavior. Fixes: ebb1064 ("x86/traps: Handle #DB for bus lock") Reported-by: Sohil Mehta <[email protected]> Suggested-by: H. Peter Anvin (Intel) <[email protected]> Signed-off-by: Xin Li (Intel) <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Reviewed-by: H. Peter Anvin (Intel) <[email protected]> Reviewed-by: Sohil Mehta <[email protected]> Acked-by: Peter Zijlstra (Intel) <[email protected]> Tested-by: Sohil Mehta <[email protected]> Link: https://lore.kernel.org/lkml/[email protected]/ Cc:[email protected] Link: https://lore.kernel.org/all/20250620231504.2676902-2-xin%40zytor.com
1 parent 2aebf5e commit 5f465c1

File tree

3 files changed

+51
-28
lines changed

3 files changed

+51
-28
lines changed

arch/x86/include/uapi/asm/debugreg.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,26 @@
1515
which debugging register was responsible for the trap. The other bits
1616
are either reserved or not of interest to us. */
1717

18-
/* Define reserved bits in DR6 which are always set to 1 */
18+
/*
19+
* Define bits in DR6 which are set to 1 by default.
20+
*
21+
* This is also the DR6 architectural value following Power-up, Reset or INIT.
22+
*
23+
* Note, with the introduction of Bus Lock Detection (BLD) and Restricted
24+
* Transactional Memory (RTM), the DR6 register has been modified:
25+
*
26+
* 1) BLD flag (bit 11) is no longer reserved to 1 if the CPU supports
27+
* Bus Lock Detection. The assertion of a bus lock could clear it.
28+
*
29+
* 2) RTM flag (bit 16) is no longer reserved to 1 if the CPU supports
30+
* restricted transactional memory. #DB occurred inside an RTM region
31+
* could clear it.
32+
*
33+
* Apparently, DR6.BLD and DR6.RTM are active low bits.
34+
*
35+
* As a result, DR6_RESERVED is an incorrect name now, but it is kept for
36+
* compatibility.
37+
*/
1938
#define DR6_RESERVED (0xFFFF0FF0)
2039

2140
#define DR_TRAP0 (0x1) /* db0 */

arch/x86/kernel/cpu/common.c

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2243,20 +2243,16 @@ EXPORT_PER_CPU_SYMBOL(__stack_chk_guard);
22432243
#endif
22442244
#endif
22452245

2246-
/*
2247-
* Clear all 6 debug registers:
2248-
*/
2249-
static void clear_all_debug_regs(void)
2246+
static void initialize_debug_regs(void)
22502247
{
2251-
int i;
2252-
2253-
for (i = 0; i < 8; i++) {
2254-
/* Ignore db4, db5 */
2255-
if ((i == 4) || (i == 5))
2256-
continue;
2257-
2258-
set_debugreg(0, i);
2259-
}
2248+
/* Control register first -- to make sure everything is disabled. */
2249+
set_debugreg(0, 7);
2250+
set_debugreg(DR6_RESERVED, 6);
2251+
/* dr5 and dr4 don't exist */
2252+
set_debugreg(0, 3);
2253+
set_debugreg(0, 2);
2254+
set_debugreg(0, 1);
2255+
set_debugreg(0, 0);
22602256
}
22612257

22622258
#ifdef CONFIG_KGDB
@@ -2417,7 +2413,7 @@ void cpu_init(void)
24172413

24182414
load_mm_ldt(&init_mm);
24192415

2420-
clear_all_debug_regs();
2416+
initialize_debug_regs();
24212417
dbg_restore_debug_regs();
24222418

24232419
doublefault_init_cpu_tss();

arch/x86/kernel/traps.c

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,24 +1022,32 @@ static bool is_sysenter_singlestep(struct pt_regs *regs)
10221022
#endif
10231023
}
10241024

1025-
static __always_inline unsigned long debug_read_clear_dr6(void)
1025+
static __always_inline unsigned long debug_read_reset_dr6(void)
10261026
{
10271027
unsigned long dr6;
10281028

1029+
get_debugreg(dr6, 6);
1030+
dr6 ^= DR6_RESERVED; /* Flip to positive polarity */
1031+
10291032
/*
10301033
* The Intel SDM says:
10311034
*
1032-
* Certain debug exceptions may clear bits 0-3. The remaining
1033-
* contents of the DR6 register are never cleared by the
1034-
* processor. To avoid confusion in identifying debug
1035-
* exceptions, debug handlers should clear the register before
1036-
* returning to the interrupted task.
1035+
* Certain debug exceptions may clear bits 0-3 of DR6.
1036+
*
1037+
* BLD induced #DB clears DR6.BLD and any other debug
1038+
* exception doesn't modify DR6.BLD.
10371039
*
1038-
* Keep it simple: clear DR6 immediately.
1040+
* RTM induced #DB clears DR6.RTM and any other debug
1041+
* exception sets DR6.RTM.
1042+
*
1043+
* To avoid confusion in identifying debug exceptions,
1044+
* debug handlers should set DR6.BLD and DR6.RTM, and
1045+
* clear other DR6 bits before returning.
1046+
*
1047+
* Keep it simple: write DR6 with its architectural reset
1048+
* value 0xFFFF0FF0, defined as DR6_RESERVED, immediately.
10391049
*/
1040-
get_debugreg(dr6, 6);
10411050
set_debugreg(DR6_RESERVED, 6);
1042-
dr6 ^= DR6_RESERVED; /* Flip to positive polarity */
10431051

10441052
return dr6;
10451053
}
@@ -1239,13 +1247,13 @@ static noinstr void exc_debug_user(struct pt_regs *regs, unsigned long dr6)
12391247
/* IST stack entry */
12401248
DEFINE_IDTENTRY_DEBUG(exc_debug)
12411249
{
1242-
exc_debug_kernel(regs, debug_read_clear_dr6());
1250+
exc_debug_kernel(regs, debug_read_reset_dr6());
12431251
}
12441252

12451253
/* User entry, runs on regular task stack */
12461254
DEFINE_IDTENTRY_DEBUG_USER(exc_debug)
12471255
{
1248-
exc_debug_user(regs, debug_read_clear_dr6());
1256+
exc_debug_user(regs, debug_read_reset_dr6());
12491257
}
12501258

12511259
#ifdef CONFIG_X86_FRED
@@ -1264,7 +1272,7 @@ DEFINE_FREDENTRY_DEBUG(exc_debug)
12641272
{
12651273
/*
12661274
* FRED #DB stores DR6 on the stack in the format which
1267-
* debug_read_clear_dr6() returns for the IDT entry points.
1275+
* debug_read_reset_dr6() returns for the IDT entry points.
12681276
*/
12691277
unsigned long dr6 = fred_event_data(regs);
12701278

@@ -1279,7 +1287,7 @@ DEFINE_FREDENTRY_DEBUG(exc_debug)
12791287
/* 32 bit does not have separate entry points. */
12801288
DEFINE_IDTENTRY_RAW(exc_debug)
12811289
{
1282-
unsigned long dr6 = debug_read_clear_dr6();
1290+
unsigned long dr6 = debug_read_reset_dr6();
12831291

12841292
if (user_mode(regs))
12851293
exc_debug_user(regs, dr6);

0 commit comments

Comments
 (0)