- Make sure DR6 and DR7 are initialized to their architectural values and not

accidentally cleared, leading to misconfigurations
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEzv7L6UO9uDPlPSfHEsHwGGHeVUoFAmhg/UQACgkQEsHwGGHe
 VUrmXBAAtOrpEtR4geeBeZtCEaUxE4DE8Zvj36dr+sAScHTXNTzYK94mAy/AHU22
 V3rF12/kuyyZSrwROXLBD6PgkHEn8u0WLztSeqP/SisnoLjMTV9H9TuGYBUoz1NS
 clkoElQ6DJP5BVzmYpZlJrcofqNjkS/mAxfMRoIAq+LzKkb3iL/Lge+Ox/IDUg8z
 L9wRlKh/IaJ5EETWlqh0gkFeS/M9DXYmfkasDQeVkUxnKFeXBdyUGc2jFzBGX5RA
 rsdnz+C3x3ow2U9N+ZMVr+n06yTZvh+fAiU8emeBQm0q5fZBBHWDbnZZtWf+KG6s
 43tlWyVqic5yzyQbUpRC2sttOkIAtOCMx36XexbGm1eKRNNc6fTz9IlgO/97HkuE
 lYBNq0zd/p5Kb53lXb3uwBVy4sjIEZUyD/K5DO4YfTgamcwXl8BP5xnKtNPqImI5
 aaF3xKKLOUDOTL1CcK5YG0joaU1k+I0F0KO7HYqkDi8Uf5naWZSUNil8nPQn8RX7
 3f3LJx0e3j2o0f60AHI4mjUAUJHsxExmpaTl079k03wt8YVE3ucNaUN6se6nidVz
 H5q0JU4q3C3DCu0I3Ub4wa5QXGA+TOHKuqhJCapKAVAAQDlbV2z8GxA/3B2YbRM+
 eZ6/RVyk++VrRXIyfmfwLPu0CLVoSNhaUhu/hFrYbjA3NX+85qw=
 =fjgT
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2025-06-29 08:28:24 -07:00
commit cc69ac7a65
9 changed files with 72 additions and 38 deletions

View File

@ -9,6 +9,14 @@
#include <asm/cpufeature.h> #include <asm/cpufeature.h>
#include <asm/msr.h> #include <asm/msr.h>
/*
* Define bits that are always set to 1 in DR7, only bit 10 is
* architecturally reserved to '1'.
*
* This is also the init/reset value for DR7.
*/
#define DR7_FIXED_1 0x00000400
DECLARE_PER_CPU(unsigned long, cpu_dr7); DECLARE_PER_CPU(unsigned long, cpu_dr7);
#ifndef CONFIG_PARAVIRT_XXL #ifndef CONFIG_PARAVIRT_XXL
@ -100,8 +108,8 @@ static __always_inline void native_set_debugreg(int regno, unsigned long value)
static inline void hw_breakpoint_disable(void) static inline void hw_breakpoint_disable(void)
{ {
/* Zero the control register for HW Breakpoint */ /* Reset the control register for HW Breakpoint */
set_debugreg(0UL, 7); set_debugreg(DR7_FIXED_1, 7);
/* Zero-out the individual HW breakpoint address registers */ /* Zero-out the individual HW breakpoint address registers */
set_debugreg(0UL, 0); set_debugreg(0UL, 0);
@ -125,9 +133,12 @@ static __always_inline unsigned long local_db_save(void)
return 0; return 0;
get_debugreg(dr7, 7); get_debugreg(dr7, 7);
dr7 &= ~0x400; /* architecturally set bit */
/* Architecturally set bit */
dr7 &= ~DR7_FIXED_1;
if (dr7) if (dr7)
set_debugreg(0, 7); set_debugreg(DR7_FIXED_1, 7);
/* /*
* Ensure the compiler doesn't lower the above statements into * Ensure the compiler doesn't lower the above statements into
* the critical section; disabling breakpoints late would not * the critical section; disabling breakpoints late would not

View File

@ -31,6 +31,7 @@
#include <asm/apic.h> #include <asm/apic.h>
#include <asm/pvclock-abi.h> #include <asm/pvclock-abi.h>
#include <asm/debugreg.h>
#include <asm/desc.h> #include <asm/desc.h>
#include <asm/mtrr.h> #include <asm/mtrr.h>
#include <asm/msr-index.h> #include <asm/msr-index.h>
@ -249,7 +250,6 @@ enum x86_intercept_stage;
#define DR7_BP_EN_MASK 0x000000ff #define DR7_BP_EN_MASK 0x000000ff
#define DR7_GE (1 << 9) #define DR7_GE (1 << 9)
#define DR7_GD (1 << 13) #define DR7_GD (1 << 13)
#define DR7_FIXED_1 0x00000400
#define DR7_VOLATILE 0xffff2bff #define DR7_VOLATILE 0xffff2bff
#define KVM_GUESTDBG_VALID_MASK \ #define KVM_GUESTDBG_VALID_MASK \

View File

@ -15,7 +15,26 @@
which debugging register was responsible for the trap. The other bits which debugging register was responsible for the trap. The other bits
are either reserved or not of interest to us. */ are either reserved or not of interest to us. */
/* Define reserved bits in DR6 which are always set to 1 */ /*
* Define bits in DR6 which are set to 1 by default.
*
* This is also the DR6 architectural value following Power-up, Reset or INIT.
*
* Note, with the introduction of Bus Lock Detection (BLD) and Restricted
* Transactional Memory (RTM), the DR6 register has been modified:
*
* 1) BLD flag (bit 11) is no longer reserved to 1 if the CPU supports
* Bus Lock Detection. The assertion of a bus lock could clear it.
*
* 2) RTM flag (bit 16) is no longer reserved to 1 if the CPU supports
* restricted transactional memory. #DB occurred inside an RTM region
* could clear it.
*
* Apparently, DR6.BLD and DR6.RTM are active low bits.
*
* As a result, DR6_RESERVED is an incorrect name now, but it is kept for
* compatibility.
*/
#define DR6_RESERVED (0xFFFF0FF0) #define DR6_RESERVED (0xFFFF0FF0)
#define DR_TRAP0 (0x1) /* db0 */ #define DR_TRAP0 (0x1) /* db0 */

View File

@ -2243,20 +2243,16 @@ EXPORT_PER_CPU_SYMBOL(__stack_chk_guard);
#endif #endif
#endif #endif
/* static void initialize_debug_regs(void)
* Clear all 6 debug registers:
*/
static void clear_all_debug_regs(void)
{ {
int i; /* Control register first -- to make sure everything is disabled. */
set_debugreg(DR7_FIXED_1, 7);
for (i = 0; i < 8; i++) { set_debugreg(DR6_RESERVED, 6);
/* Ignore db4, db5 */ /* dr5 and dr4 don't exist */
if ((i == 4) || (i == 5)) set_debugreg(0, 3);
continue; set_debugreg(0, 2);
set_debugreg(0, 1);
set_debugreg(0, i); set_debugreg(0, 0);
}
} }
#ifdef CONFIG_KGDB #ifdef CONFIG_KGDB
@ -2417,7 +2413,7 @@ void cpu_init(void)
load_mm_ldt(&init_mm); load_mm_ldt(&init_mm);
clear_all_debug_regs(); initialize_debug_regs();
dbg_restore_debug_regs(); dbg_restore_debug_regs();
doublefault_init_cpu_tss(); doublefault_init_cpu_tss();

View File

@ -385,7 +385,7 @@ static void kgdb_disable_hw_debug(struct pt_regs *regs)
struct perf_event *bp; struct perf_event *bp;
/* Disable hardware debugging while we are in kgdb: */ /* Disable hardware debugging while we are in kgdb: */
set_debugreg(0UL, 7); set_debugreg(DR7_FIXED_1, 7);
for (i = 0; i < HBP_NUM; i++) { for (i = 0; i < HBP_NUM; i++) {
if (!breakinfo[i].enabled) if (!breakinfo[i].enabled)
continue; continue;

View File

@ -93,7 +93,7 @@ void __show_regs(struct pt_regs *regs, enum show_regs_mode mode,
/* Only print out debug registers if they are in their non-default state. */ /* Only print out debug registers if they are in their non-default state. */
if ((d0 == 0) && (d1 == 0) && (d2 == 0) && (d3 == 0) && if ((d0 == 0) && (d1 == 0) && (d2 == 0) && (d3 == 0) &&
(d6 == DR6_RESERVED) && (d7 == 0x400)) (d6 == DR6_RESERVED) && (d7 == DR7_FIXED_1))
return; return;
printk("%sDR0: %08lx DR1: %08lx DR2: %08lx DR3: %08lx\n", printk("%sDR0: %08lx DR1: %08lx DR2: %08lx DR3: %08lx\n",

View File

@ -133,7 +133,7 @@ void __show_regs(struct pt_regs *regs, enum show_regs_mode mode,
/* Only print out debug registers if they are in their non-default state. */ /* Only print out debug registers if they are in their non-default state. */
if (!((d0 == 0) && (d1 == 0) && (d2 == 0) && (d3 == 0) && if (!((d0 == 0) && (d1 == 0) && (d2 == 0) && (d3 == 0) &&
(d6 == DR6_RESERVED) && (d7 == 0x400))) { (d6 == DR6_RESERVED) && (d7 == DR7_FIXED_1))) {
printk("%sDR0: %016lx DR1: %016lx DR2: %016lx\n", printk("%sDR0: %016lx DR1: %016lx DR2: %016lx\n",
log_lvl, d0, d1, d2); log_lvl, d0, d1, d2);
printk("%sDR3: %016lx DR6: %016lx DR7: %016lx\n", printk("%sDR3: %016lx DR6: %016lx DR7: %016lx\n",

View File

@ -1022,24 +1022,32 @@ static bool is_sysenter_singlestep(struct pt_regs *regs)
#endif #endif
} }
static __always_inline unsigned long debug_read_clear_dr6(void) static __always_inline unsigned long debug_read_reset_dr6(void)
{ {
unsigned long dr6; unsigned long dr6;
get_debugreg(dr6, 6);
dr6 ^= DR6_RESERVED; /* Flip to positive polarity */
/* /*
* The Intel SDM says: * The Intel SDM says:
* *
* Certain debug exceptions may clear bits 0-3. The remaining * Certain debug exceptions may clear bits 0-3 of DR6.
* contents of the DR6 register are never cleared by the
* processor. To avoid confusion in identifying debug
* exceptions, debug handlers should clear the register before
* returning to the interrupted task.
* *
* Keep it simple: clear DR6 immediately. * BLD induced #DB clears DR6.BLD and any other debug
* exception doesn't modify DR6.BLD.
*
* 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.
*
* Keep it simple: write DR6 with its architectural reset
* value 0xFFFF0FF0, defined as DR6_RESERVED, immediately.
*/ */
get_debugreg(dr6, 6);
set_debugreg(DR6_RESERVED, 6); set_debugreg(DR6_RESERVED, 6);
dr6 ^= DR6_RESERVED; /* Flip to positive polarity */
return dr6; return dr6;
} }
@ -1239,13 +1247,13 @@ out:
/* IST stack entry */ /* IST stack entry */
DEFINE_IDTENTRY_DEBUG(exc_debug) DEFINE_IDTENTRY_DEBUG(exc_debug)
{ {
exc_debug_kernel(regs, debug_read_clear_dr6()); exc_debug_kernel(regs, debug_read_reset_dr6());
} }
/* User entry, runs on regular task stack */ /* User entry, runs on regular task stack */
DEFINE_IDTENTRY_DEBUG_USER(exc_debug) DEFINE_IDTENTRY_DEBUG_USER(exc_debug)
{ {
exc_debug_user(regs, debug_read_clear_dr6()); exc_debug_user(regs, debug_read_reset_dr6());
} }
#ifdef CONFIG_X86_FRED #ifdef CONFIG_X86_FRED
@ -1264,7 +1272,7 @@ DEFINE_FREDENTRY_DEBUG(exc_debug)
{ {
/* /*
* FRED #DB stores DR6 on the stack in the format which * FRED #DB stores DR6 on the stack in the format which
* debug_read_clear_dr6() returns for the IDT entry points. * debug_read_reset_dr6() returns for the IDT entry points.
*/ */
unsigned long dr6 = fred_event_data(regs); unsigned long dr6 = fred_event_data(regs);
@ -1279,7 +1287,7 @@ DEFINE_FREDENTRY_DEBUG(exc_debug)
/* 32 bit does not have separate entry points. */ /* 32 bit does not have separate entry points. */
DEFINE_IDTENTRY_RAW(exc_debug) DEFINE_IDTENTRY_RAW(exc_debug)
{ {
unsigned long dr6 = debug_read_clear_dr6(); unsigned long dr6 = debug_read_reset_dr6();
if (user_mode(regs)) if (user_mode(regs))
exc_debug_user(regs, dr6); exc_debug_user(regs, dr6);

View File

@ -11035,7 +11035,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
if (unlikely(vcpu->arch.switch_db_regs && if (unlikely(vcpu->arch.switch_db_regs &&
!(vcpu->arch.switch_db_regs & KVM_DEBUGREG_AUTO_SWITCH))) { !(vcpu->arch.switch_db_regs & KVM_DEBUGREG_AUTO_SWITCH))) {
set_debugreg(0, 7); set_debugreg(DR7_FIXED_1, 7);
set_debugreg(vcpu->arch.eff_db[0], 0); set_debugreg(vcpu->arch.eff_db[0], 0);
set_debugreg(vcpu->arch.eff_db[1], 1); set_debugreg(vcpu->arch.eff_db[1], 1);
set_debugreg(vcpu->arch.eff_db[2], 2); set_debugreg(vcpu->arch.eff_db[2], 2);
@ -11044,7 +11044,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
if (unlikely(vcpu->arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT)) if (unlikely(vcpu->arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT))
kvm_x86_call(set_dr6)(vcpu, vcpu->arch.dr6); kvm_x86_call(set_dr6)(vcpu, vcpu->arch.dr6);
} else if (unlikely(hw_breakpoint_active())) { } else if (unlikely(hw_breakpoint_active())) {
set_debugreg(0, 7); set_debugreg(DR7_FIXED_1, 7);
} }
vcpu->arch.host_debugctl = get_debugctlmsr(); vcpu->arch.host_debugctl = get_debugctlmsr();