xref: /linux/arch/x86/include/asm/debugreg.h (revision f728c17fc97aea7a33151d9ba64106291c62bb02)
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 
10 DECLARE_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 
22 static __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 
64 static __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 
99 static 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 
111 static __always_inline bool hw_breakpoint_active(void)
112 {
113 	return __this_cpu_read(cpu_dr7) & DR_GLOBAL_ENABLE_MASK;
114 }
115 
116 extern void hw_breakpoint_restore(void);
117 
118 static __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 
139 static __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
152 extern void amd_set_dr_addr_mask(unsigned long mask, unsigned int dr);
153 extern unsigned long amd_get_dr_addr_mask(unsigned int dr);
154 #else
155 static inline void amd_set_dr_addr_mask(unsigned long mask, unsigned int dr) { }
156 static 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 */
163