Skip to content

Commit 2419efd

Browse files
author
Maxim Levitsky
committed
x86/traps: Initialize DR6 by writing its architectural reset value
JIRA: https://issues.redhat.com/browse/RHEL-120168 commit 5f465c1 Author: Xin Li (Intel) <xin@zytor.com> Date: Fri Jun 20 16:15:03 2025 -0700 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 <sohil.mehta@intel.com> Suggested-by: H. Peter Anvin (Intel) <hpa@zytor.com> Signed-off-by: Xin Li (Intel) <xin@zytor.com> Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Reviewed-by: H. Peter Anvin (Intel) <hpa@zytor.com> Reviewed-by: Sohil Mehta <sohil.mehta@intel.com> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Tested-by: Sohil Mehta <sohil.mehta@intel.com> Link: https://lore.kernel.org/lkml/06e68373-a92b-472e-8fd9-ba548119770c@intel.com/ Cc:stable@vger.kernel.org Link: https://lore.kernel.org/all/20250620231504.2676902-2-xin%40zytor.com Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
1 parent 27f31c8 commit 2419efd

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
@@ -2185,20 +2185,16 @@ EXPORT_PER_CPU_SYMBOL(__stack_chk_guard);
21852185

21862186
#endif /* CONFIG_X86_64 */
21872187

2188-
/*
2189-
* Clear all 6 debug registers:
2190-
*/
2191-
static void clear_all_debug_regs(void)
2188+
static void initialize_debug_regs(void)
21922189
{
2193-
int i;
2194-
2195-
for (i = 0; i < 8; i++) {
2196-
/* Ignore db4, db5 */
2197-
if ((i == 4) || (i == 5))
2198-
continue;
2199-
2200-
set_debugreg(0, i);
2201-
}
2190+
/* Control register first -- to make sure everything is disabled. */
2191+
set_debugreg(0, 7);
2192+
set_debugreg(DR6_RESERVED, 6);
2193+
/* dr5 and dr4 don't exist */
2194+
set_debugreg(0, 3);
2195+
set_debugreg(0, 2);
2196+
set_debugreg(0, 1);
2197+
set_debugreg(0, 0);
22022198
}
22032199

22042200
#ifdef CONFIG_KGDB
@@ -2359,7 +2355,7 @@ void cpu_init(void)
23592355

23602356
load_mm_ldt(&init_mm);
23612357

2362-
clear_all_debug_regs();
2358+
initialize_debug_regs();
23632359
dbg_restore_debug_regs();
23642360

23652361
doublefault_init_cpu_tss();

arch/x86/kernel/traps.c

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -939,24 +939,32 @@ static bool is_sysenter_singlestep(struct pt_regs *regs)
939939
#endif
940940
}
941941

942-
static __always_inline unsigned long debug_read_clear_dr6(void)
942+
static __always_inline unsigned long debug_read_reset_dr6(void)
943943
{
944944
unsigned long dr6;
945945

946+
get_debugreg(dr6, 6);
947+
dr6 ^= DR6_RESERVED; /* Flip to positive polarity */
948+
946949
/*
947950
* The Intel SDM says:
948951
*
949-
* Certain debug exceptions may clear bits 0-3. The remaining
950-
* contents of the DR6 register are never cleared by the
951-
* processor. To avoid confusion in identifying debug
952-
* exceptions, debug handlers should clear the register before
953-
* returning to the interrupted task.
952+
* Certain debug exceptions may clear bits 0-3 of DR6.
953+
*
954+
* BLD induced #DB clears DR6.BLD and any other debug
955+
* exception doesn't modify DR6.BLD.
954956
*
955-
* Keep it simple: clear DR6 immediately.
957+
* RTM induced #DB clears DR6.RTM and any other debug
958+
* exception sets DR6.RTM.
959+
*
960+
* To avoid confusion in identifying debug exceptions,
961+
* debug handlers should set DR6.BLD and DR6.RTM, and
962+
* clear other DR6 bits before returning.
963+
*
964+
* Keep it simple: write DR6 with its architectural reset
965+
* value 0xFFFF0FF0, defined as DR6_RESERVED, immediately.
956966
*/
957-
get_debugreg(dr6, 6);
958967
set_debugreg(DR6_RESERVED, 6);
959-
dr6 ^= DR6_RESERVED; /* Flip to positive polarity */
960968

961969
return dr6;
962970
}
@@ -1156,13 +1164,13 @@ static noinstr void exc_debug_user(struct pt_regs *regs, unsigned long dr6)
11561164
/* IST stack entry */
11571165
DEFINE_IDTENTRY_DEBUG(exc_debug)
11581166
{
1159-
exc_debug_kernel(regs, debug_read_clear_dr6());
1167+
exc_debug_kernel(regs, debug_read_reset_dr6());
11601168
}
11611169

11621170
/* User entry, runs on regular task stack */
11631171
DEFINE_IDTENTRY_DEBUG_USER(exc_debug)
11641172
{
1165-
exc_debug_user(regs, debug_read_clear_dr6());
1173+
exc_debug_user(regs, debug_read_reset_dr6());
11661174
}
11671175

11681176
#ifdef CONFIG_X86_FRED
@@ -1181,7 +1189,7 @@ DEFINE_FREDENTRY_DEBUG(exc_debug)
11811189
{
11821190
/*
11831191
* FRED #DB stores DR6 on the stack in the format which
1184-
* debug_read_clear_dr6() returns for the IDT entry points.
1192+
* debug_read_reset_dr6() returns for the IDT entry points.
11851193
*/
11861194
unsigned long dr6 = fred_event_data(regs);
11871195

@@ -1196,7 +1204,7 @@ DEFINE_FREDENTRY_DEBUG(exc_debug)
11961204
/* 32 bit does not have separate entry points. */
11971205
DEFINE_IDTENTRY_RAW(exc_debug)
11981206
{
1199-
unsigned long dr6 = debug_read_clear_dr6();
1207+
unsigned long dr6 = debug_read_reset_dr6();
12001208

12011209
if (user_mode(regs))
12021210
exc_debug_user(regs, dr6);

0 commit comments

Comments
 (0)