x86/traps: Initialize DR6 by writing its architectural reset value
[ Upstream commit 5f465c148c61e876b6d6eacd8e8e365f2d47758f ]
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: ebb1064e7c
("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: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
16254aa985
commit
bceae1daf3
@@ -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 */
|
||||||
|
@@ -2218,17 +2218,16 @@ EXPORT_PER_CPU_SYMBOL(__stack_chk_guard);
|
|||||||
/*
|
/*
|
||||||
* Clear all 6 debug registers:
|
* Clear all 6 debug registers:
|
||||||
*/
|
*/
|
||||||
static void clear_all_debug_regs(void)
|
static void initialize_debug_regs(void)
|
||||||
{
|
{
|
||||||
int i;
|
/* Control register first -- to make sure everything is disabled. */
|
||||||
|
set_debugreg(0, 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
|
||||||
@@ -2371,7 +2370,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();
|
||||||
|
@@ -975,24 +975,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;
|
||||||
}
|
}
|
||||||
@@ -1188,19 +1196,19 @@ 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());
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/* 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);
|
||||||
|
Reference in New Issue
Block a user