197 lines · 4.4 KB · C
RawDownload
/* 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 (cpu_feature_enabled(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_AMDextern void amd_set_dr_addr_mask(unsigned long mask, unsigned int dr);extern unsigned long amd_get_dr_addr_mask(unsigned int dr);#elsestatic 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 */