at v6.8 162 lines 3.9 kB view raw
1/* SPDX-License-Identifier: GPL-2.0 */ 2#ifndef _ASM_X86_DEBUGREG_H 3#define _ASM_X86_DEBUGREG_H 4 5#include <linux/bug.h> 6#include <linux/percpu.h> 7#include <uapi/asm/debugreg.h> 8#include <asm/cpufeature.h> 9 10DECLARE_PER_CPU(unsigned long, cpu_dr7); 11 12#ifndef CONFIG_PARAVIRT_XXL 13/* 14 * These special macros can be used to get or set a debugging register 15 */ 16#define get_debugreg(var, register) \ 17 (var) = native_get_debugreg(register) 18#define set_debugreg(value, register) \ 19 native_set_debugreg(register, value) 20#endif 21 22static __always_inline unsigned long native_get_debugreg(int regno) 23{ 24 unsigned long val = 0; /* Damn you, gcc! */ 25 26 switch (regno) { 27 case 0: 28 asm("mov %%db0, %0" :"=r" (val)); 29 break; 30 case 1: 31 asm("mov %%db1, %0" :"=r" (val)); 32 break; 33 case 2: 34 asm("mov %%db2, %0" :"=r" (val)); 35 break; 36 case 3: 37 asm("mov %%db3, %0" :"=r" (val)); 38 break; 39 case 6: 40 asm("mov %%db6, %0" :"=r" (val)); 41 break; 42 case 7: 43 /* 44 * Apply __FORCE_ORDER to DR7 reads to forbid re-ordering them 45 * with other code. 46 * 47 * This is needed because a DR7 access can cause a #VC exception 48 * when running under SEV-ES. Taking a #VC exception is not a 49 * safe thing to do just anywhere in the entry code and 50 * re-ordering might place the access into an unsafe location. 51 * 52 * This happened in the NMI handler, where the DR7 read was 53 * re-ordered to happen before the call to sev_es_ist_enter(), 54 * causing stack recursion. 55 */ 56 asm volatile("mov %%db7, %0" : "=r" (val) : __FORCE_ORDER); 57 break; 58 default: 59 BUG(); 60 } 61 return val; 62} 63 64static __always_inline void native_set_debugreg(int regno, unsigned long value) 65{ 66 switch (regno) { 67 case 0: 68 asm("mov %0, %%db0" ::"r" (value)); 69 break; 70 case 1: 71 asm("mov %0, %%db1" ::"r" (value)); 72 break; 73 case 2: 74 asm("mov %0, %%db2" ::"r" (value)); 75 break; 76 case 3: 77 asm("mov %0, %%db3" ::"r" (value)); 78 break; 79 case 6: 80 asm("mov %0, %%db6" ::"r" (value)); 81 break; 82 case 7: 83 /* 84 * Apply __FORCE_ORDER to DR7 writes to forbid re-ordering them 85 * with other code. 86 * 87 * While is didn't happen with a DR7 write (see the DR7 read 88 * comment above which explains where it happened), add the 89 * __FORCE_ORDER here too to avoid similar problems in the 90 * future. 91 */ 92 asm volatile("mov %0, %%db7" ::"r" (value), __FORCE_ORDER); 93 break; 94 default: 95 BUG(); 96 } 97} 98 99static inline void hw_breakpoint_disable(void) 100{ 101 /* Zero the control register for HW Breakpoint */ 102 set_debugreg(0UL, 7); 103 104 /* Zero-out the individual HW breakpoint address registers */ 105 set_debugreg(0UL, 0); 106 set_debugreg(0UL, 1); 107 set_debugreg(0UL, 2); 108 set_debugreg(0UL, 3); 109} 110 111static __always_inline bool hw_breakpoint_active(void) 112{ 113 return __this_cpu_read(cpu_dr7) & DR_GLOBAL_ENABLE_MASK; 114} 115 116extern void hw_breakpoint_restore(void); 117 118static __always_inline unsigned long local_db_save(void) 119{ 120 unsigned long dr7; 121 122 if (static_cpu_has(X86_FEATURE_HYPERVISOR) && !hw_breakpoint_active()) 123 return 0; 124 125 get_debugreg(dr7, 7); 126 dr7 &= ~0x400; /* architecturally set bit */ 127 if (dr7) 128 set_debugreg(0, 7); 129 /* 130 * Ensure the compiler doesn't lower the above statements into 131 * the critical section; disabling breakpoints late would not 132 * be good. 133 */ 134 barrier(); 135 136 return dr7; 137} 138 139static __always_inline void local_db_restore(unsigned long dr7) 140{ 141 /* 142 * Ensure the compiler doesn't raise this statement into 143 * the critical section; enabling breakpoints early would 144 * not be good. 145 */ 146 barrier(); 147 if (dr7) 148 set_debugreg(dr7, 7); 149} 150 151#ifdef CONFIG_CPU_SUP_AMD 152extern void amd_set_dr_addr_mask(unsigned long mask, unsigned int dr); 153extern unsigned long amd_get_dr_addr_mask(unsigned int dr); 154#else 155static inline void amd_set_dr_addr_mask(unsigned long mask, unsigned int dr) { } 156static inline unsigned long amd_get_dr_addr_mask(unsigned int dr) 157{ 158 return 0; 159} 160#endif 161 162#endif /* _ASM_X86_DEBUGREG_H */