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