Skip to content

Commit cc69ac7

Browse files
committed
Merge tag 'x86_urgent_for_v6.16_rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 fixes from Borislav Petkov: - Make sure DR6 and DR7 are initialized to their architectural values and not accidentally cleared, leading to misconfigurations * tag 'x86_urgent_for_v6.16_rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/traps: Initialize DR7 by writing its architectural reset value x86/traps: Initialize DR6 by writing its architectural reset value
2 parents 2fc18d0 + fa7d0f8 commit cc69ac7

File tree

9 files changed

+72
-38
lines changed

9 files changed

+72
-38
lines changed

arch/x86/include/asm/debugreg.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
#include <asm/cpufeature.h>
1010
#include <asm/msr.h>
1111

12+
/*
13+
* Define bits that are always set to 1 in DR7, only bit 10 is
14+
* architecturally reserved to '1'.
15+
*
16+
* This is also the init/reset value for DR7.
17+
*/
18+
#define DR7_FIXED_1 0x00000400
19+
1220
DECLARE_PER_CPU(unsigned long, cpu_dr7);
1321

1422
#ifndef CONFIG_PARAVIRT_XXL
@@ -100,8 +108,8 @@ static __always_inline void native_set_debugreg(int regno, unsigned long value)
100108

101109
static inline void hw_breakpoint_disable(void)
102110
{
103-
/* Zero the control register for HW Breakpoint */
104-
set_debugreg(0UL, 7);
111+
/* Reset the control register for HW Breakpoint */
112+
set_debugreg(DR7_FIXED_1, 7);
105113

106114
/* Zero-out the individual HW breakpoint address registers */
107115
set_debugreg(0UL, 0);
@@ -125,9 +133,12 @@ static __always_inline unsigned long local_db_save(void)
125133
return 0;
126134

127135
get_debugreg(dr7, 7);
128-
dr7 &= ~0x400; /* architecturally set bit */
136+
137+
/* Architecturally set bit */
138+
dr7 &= ~DR7_FIXED_1;
129139
if (dr7)
130-
set_debugreg(0, 7);
140+
set_debugreg(DR7_FIXED_1, 7);
141+
131142
/*
132143
* Ensure the compiler doesn't lower the above statements into
133144
* the critical section; disabling breakpoints late would not

arch/x86/include/asm/kvm_host.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#include <asm/apic.h>
3333
#include <asm/pvclock-abi.h>
34+
#include <asm/debugreg.h>
3435
#include <asm/desc.h>
3536
#include <asm/mtrr.h>
3637
#include <asm/msr-index.h>
@@ -249,7 +250,6 @@ enum x86_intercept_stage;
249250
#define DR7_BP_EN_MASK 0x000000ff
250251
#define DR7_GE (1 << 9)
251252
#define DR7_GD (1 << 13)
252-
#define DR7_FIXED_1 0x00000400
253253
#define DR7_VOLATILE 0xffff2bff
254254

255255
#define KVM_GUESTDBG_VALID_MASK \

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(DR7_FIXED_1, 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/kgdb.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ static void kgdb_disable_hw_debug(struct pt_regs *regs)
385385
struct perf_event *bp;
386386

387387
/* Disable hardware debugging while we are in kgdb: */
388-
set_debugreg(0UL, 7);
388+
set_debugreg(DR7_FIXED_1, 7);
389389
for (i = 0; i < HBP_NUM; i++) {
390390
if (!breakinfo[i].enabled)
391391
continue;

arch/x86/kernel/process_32.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ void __show_regs(struct pt_regs *regs, enum show_regs_mode mode,
9393

9494
/* Only print out debug registers if they are in their non-default state. */
9595
if ((d0 == 0) && (d1 == 0) && (d2 == 0) && (d3 == 0) &&
96-
(d6 == DR6_RESERVED) && (d7 == 0x400))
96+
(d6 == DR6_RESERVED) && (d7 == DR7_FIXED_1))
9797
return;
9898

9999
printk("%sDR0: %08lx DR1: %08lx DR2: %08lx DR3: %08lx\n",

arch/x86/kernel/process_64.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ void __show_regs(struct pt_regs *regs, enum show_regs_mode mode,
133133

134134
/* Only print out debug registers if they are in their non-default state. */
135135
if (!((d0 == 0) && (d1 == 0) && (d2 == 0) && (d3 == 0) &&
136-
(d6 == DR6_RESERVED) && (d7 == 0x400))) {
136+
(d6 == DR6_RESERVED) && (d7 == DR7_FIXED_1))) {
137137
printk("%sDR0: %016lx DR1: %016lx DR2: %016lx\n",
138138
log_lvl, d0, d1, d2);
139139
printk("%sDR3: %016lx DR6: %016lx DR7: %016lx\n",

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);

arch/x86/kvm/x86.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11035,7 +11035,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
1103511035

1103611036
if (unlikely(vcpu->arch.switch_db_regs &&
1103711037
!(vcpu->arch.switch_db_regs & KVM_DEBUGREG_AUTO_SWITCH))) {
11038-
set_debugreg(0, 7);
11038+
set_debugreg(DR7_FIXED_1, 7);
1103911039
set_debugreg(vcpu->arch.eff_db[0], 0);
1104011040
set_debugreg(vcpu->arch.eff_db[1], 1);
1104111041
set_debugreg(vcpu->arch.eff_db[2], 2);
@@ -11044,7 +11044,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
1104411044
if (unlikely(vcpu->arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT))
1104511045
kvm_x86_call(set_dr6)(vcpu, vcpu->arch.dr6);
1104611046
} else if (unlikely(hw_breakpoint_active())) {
11047-
set_debugreg(0, 7);
11047+
set_debugreg(DR7_FIXED_1, 7);
1104811048
}
1104911049

1105011050
vcpu->arch.host_debugctl = get_debugctlmsr();

0 commit comments

Comments
 (0)