mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

Initialize DR7 by writing its architectural reset value to always set bit 10, which is reserved to '1', when "clearing" DR7 so as not to trigger unanticipated behavior if said bit is ever unreserved, e.g. as a feature enabling flag with inverted polarity. 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> Acked-by: Sean Christopherson <seanjc@google.com> Tested-by: Sohil Mehta <sohil.mehta@intel.com> Cc:stable@vger.kernel.org Link: https://lore.kernel.org/all/20250620231504.2676902-3-xin%40zytor.com
197 lines
4.4 KiB
C
197 lines
4.4 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _ASM_X86_DEBUGREG_H
|
|
#define _ASM_X86_DEBUGREG_H
|
|
|
|
#include <linux/bug.h>
|
|
#include <linux/percpu.h>
|
|
#include <uapi/asm/debugreg.h>
|
|
|
|
#include <asm/cpufeature.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);
|
|
|
|
#ifndef CONFIG_PARAVIRT_XXL
|
|
/*
|
|
* These special macros can be used to get or set a debugging register
|
|
*/
|
|
#define get_debugreg(var, register) \
|
|
(var) = native_get_debugreg(register)
|
|
#define set_debugreg(value, register) \
|
|
native_set_debugreg(register, value)
|
|
#endif
|
|
|
|
static __always_inline unsigned long native_get_debugreg(int regno)
|
|
{
|
|
unsigned long val;
|
|
|
|
switch (regno) {
|
|
case 0:
|
|
asm("mov %%db0, %0" :"=r" (val));
|
|
break;
|
|
case 1:
|
|
asm("mov %%db1, %0" :"=r" (val));
|
|
break;
|
|
case 2:
|
|
asm("mov %%db2, %0" :"=r" (val));
|
|
break;
|
|
case 3:
|
|
asm("mov %%db3, %0" :"=r" (val));
|
|
break;
|
|
case 6:
|
|
asm("mov %%db6, %0" :"=r" (val));
|
|
break;
|
|
case 7:
|
|
/*
|
|
* Use "asm volatile" for DR7 reads to forbid re-ordering them
|
|
* with other code.
|
|
*
|
|
* This is needed because a DR7 access can cause a #VC exception
|
|
* when running under SEV-ES. Taking a #VC exception is not a
|
|
* safe thing to do just anywhere in the entry code and
|
|
* re-ordering might place the access into an unsafe location.
|
|
*
|
|
* This happened in the NMI handler, where the DR7 read was
|
|
* re-ordered to happen before the call to sev_es_ist_enter(),
|
|
* causing stack recursion.
|
|
*/
|
|
asm volatile("mov %%db7, %0" : "=r" (val));
|
|
break;
|
|
default:
|
|
BUG();
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static __always_inline void native_set_debugreg(int regno, unsigned long value)
|
|
{
|
|
switch (regno) {
|
|
case 0:
|
|
asm("mov %0, %%db0" ::"r" (value));
|
|
break;
|
|
case 1:
|
|
asm("mov %0, %%db1" ::"r" (value));
|
|
break;
|
|
case 2:
|
|
asm("mov %0, %%db2" ::"r" (value));
|
|
break;
|
|
case 3:
|
|
asm("mov %0, %%db3" ::"r" (value));
|
|
break;
|
|
case 6:
|
|
asm("mov %0, %%db6" ::"r" (value));
|
|
break;
|
|
case 7:
|
|
/*
|
|
* Use "asm volatile" for DR7 writes to forbid re-ordering them
|
|
* with other code.
|
|
*
|
|
* While is didn't happen with a DR7 write (see the DR7 read
|
|
* comment above which explains where it happened), add the
|
|
* "asm volatile" here too to avoid similar problems in the
|
|
* future.
|
|
*/
|
|
asm volatile("mov %0, %%db7" ::"r" (value));
|
|
break;
|
|
default:
|
|
BUG();
|
|
}
|
|
}
|
|
|
|
static inline void hw_breakpoint_disable(void)
|
|
{
|
|
/* Reset the control register for HW Breakpoint */
|
|
set_debugreg(DR7_FIXED_1, 7);
|
|
|
|
/* Zero-out the individual HW breakpoint address registers */
|
|
set_debugreg(0UL, 0);
|
|
set_debugreg(0UL, 1);
|
|
set_debugreg(0UL, 2);
|
|
set_debugreg(0UL, 3);
|
|
}
|
|
|
|
static __always_inline bool hw_breakpoint_active(void)
|
|
{
|
|
return __this_cpu_read(cpu_dr7) & DR_GLOBAL_ENABLE_MASK;
|
|
}
|
|
|
|
extern void hw_breakpoint_restore(void);
|
|
|
|
static __always_inline unsigned long local_db_save(void)
|
|
{
|
|
unsigned long dr7;
|
|
|
|
if (static_cpu_has(X86_FEATURE_HYPERVISOR) && !hw_breakpoint_active())
|
|
return 0;
|
|
|
|
get_debugreg(dr7, 7);
|
|
|
|
/* Architecturally set bit */
|
|
dr7 &= ~DR7_FIXED_1;
|
|
if (dr7)
|
|
set_debugreg(DR7_FIXED_1, 7);
|
|
|
|
/*
|
|
* Ensure the compiler doesn't lower the above statements into
|
|
* the critical section; disabling breakpoints late would not
|
|
* be good.
|
|
*/
|
|
barrier();
|
|
|
|
return dr7;
|
|
}
|
|
|
|
static __always_inline void local_db_restore(unsigned long dr7)
|
|
{
|
|
/*
|
|
* Ensure the compiler doesn't raise this statement into
|
|
* the critical section; enabling breakpoints early would
|
|
* not be good.
|
|
*/
|
|
barrier();
|
|
if (dr7)
|
|
set_debugreg(dr7, 7);
|
|
}
|
|
|
|
#ifdef CONFIG_CPU_SUP_AMD
|
|
extern void amd_set_dr_addr_mask(unsigned long mask, unsigned int dr);
|
|
extern unsigned long amd_get_dr_addr_mask(unsigned int dr);
|
|
#else
|
|
static inline void amd_set_dr_addr_mask(unsigned long mask, unsigned int dr) { }
|
|
static inline unsigned long amd_get_dr_addr_mask(unsigned int dr)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static inline unsigned long get_debugctlmsr(void)
|
|
{
|
|
unsigned long debugctlmsr = 0;
|
|
|
|
#ifndef CONFIG_X86_DEBUGCTLMSR
|
|
if (boot_cpu_data.x86 < 6)
|
|
return 0;
|
|
#endif
|
|
rdmsrq(MSR_IA32_DEBUGCTLMSR, debugctlmsr);
|
|
|
|
return debugctlmsr;
|
|
}
|
|
|
|
static inline void update_debugctlmsr(unsigned long debugctlmsr)
|
|
{
|
|
#ifndef CONFIG_X86_DEBUGCTLMSR
|
|
if (boot_cpu_data.x86 < 6)
|
|
return;
|
|
#endif
|
|
wrmsrq(MSR_IA32_DEBUGCTLMSR, debugctlmsr);
|
|
}
|
|
|
|
#endif /* _ASM_X86_DEBUGREG_H */
|