1 /* $NetBSD: dbregs.c,v 1.15 2020/01/31 08:55:38 maxv Exp $ */
2
3 /*
4 * Copyright (c) 2016 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/lwp.h>
32 #include <sys/pool.h>
33 #include <x86/cpufunc.h>
34 #include <x86/dbregs.h>
35
36 #include <uvm/uvm_prot.h>
37 #include <uvm/uvm_pmap.h>
38
39 #include <machine/pmap.h>
40
41 struct pool x86_dbregspl;
42 static struct dbreg initdbstate;
43
44 #define X86_BREAKPOINT_CONDITION_DETECTED ( \
45 X86_DR6_DR0_BREAKPOINT_CONDITION_DETECTED | \
46 X86_DR6_DR1_BREAKPOINT_CONDITION_DETECTED | \
47 X86_DR6_DR2_BREAKPOINT_CONDITION_DETECTED | \
48 X86_DR6_DR3_BREAKPOINT_CONDITION_DETECTED )
49
50 #define X86_GLOBAL_BREAKPOINT ( \
51 X86_DR7_GLOBAL_DR0_BREAKPOINT | \
52 X86_DR7_GLOBAL_DR1_BREAKPOINT | \
53 X86_DR7_GLOBAL_DR2_BREAKPOINT | \
54 X86_DR7_GLOBAL_DR3_BREAKPOINT )
55
56 void
x86_dbregs_init(void)57 x86_dbregs_init(void)
58 {
59 /* DR0-DR3 should always be 0 */
60 initdbstate.dr[0] = rdr0();
61 initdbstate.dr[1] = rdr1();
62 initdbstate.dr[2] = rdr2();
63 initdbstate.dr[3] = rdr3();
64 /* DR4-DR5 are reserved - skip */
65 /* DR6 and DR7 contain predefined nonzero bits */
66 initdbstate.dr[6] = rdr6();
67 initdbstate.dr[7] = rdr7();
68 /* DR8-DR15 are reserved - skip */
69
70 /*
71 * Explicitly reset some bits just in case they could be
72 * set by brave software/hardware before the kernel boot.
73 */
74 initdbstate.dr[6] &= ~X86_BREAKPOINT_CONDITION_DETECTED;
75 initdbstate.dr[7] &= ~X86_DR7_GENERAL_DETECT_ENABLE;
76
77 pool_init(&x86_dbregspl, sizeof(struct dbreg), 16, 0, 0, "dbregs",
78 NULL, IPL_NONE);
79 }
80
81 static void
x86_dbregs_reset(void)82 x86_dbregs_reset(void)
83 {
84 /*
85 * It's sufficient to just disable Debug Control Register (DR7).
86 * It will deactivate hardware watchpoints.
87 */
88 ldr7(0);
89
90 /*
91 * However at some point we need to clear Debug Status Registers
92 * (DR6). The CPU will never do it automatically.
93 *
94 * Clear BREAKPOINT_CONDITION_DETECTED bits and ignore the rest.
95 */
96 ldr6(rdr6() & ~X86_BREAKPOINT_CONDITION_DETECTED);
97 }
98
99 void
x86_dbregs_clear(struct lwp * l)100 x86_dbregs_clear(struct lwp *l)
101 {
102 struct pcb *pcb = lwp_getpcb(l);
103 struct dbreg *dbregs;
104
105 KASSERT(l == curlwp);
106
107 if (__predict_true(pcb->pcb_dbregs == NULL)) {
108 KASSERT((pcb->pcb_flags & PCB_DBREGS) == 0);
109 return;
110 }
111
112 dbregs = pcb->pcb_dbregs;
113
114 kpreempt_disable();
115 pcb->pcb_dbregs = NULL;
116 pcb->pcb_flags &= ~PCB_DBREGS;
117 x86_dbregs_reset();
118 kpreempt_enable();
119
120 pool_put(&x86_dbregspl, dbregs);
121 }
122
123 void
x86_dbregs_abandon(struct lwp * l)124 x86_dbregs_abandon(struct lwp *l)
125 {
126 struct pcb *pcb = lwp_getpcb(l);
127
128 kpreempt_disable();
129 pcb->pcb_flags &= ~PCB_DBREGS;
130 x86_dbregs_reset();
131 kpreempt_enable();
132 }
133
134 void
x86_dbregs_read(struct lwp * l,struct dbreg * regs)135 x86_dbregs_read(struct lwp *l, struct dbreg *regs)
136 {
137 struct pcb *pcb = lwp_getpcb(l);
138
139 if (pcb->pcb_dbregs == NULL) {
140 pcb->pcb_dbregs = pool_get(&x86_dbregspl, PR_WAITOK);
141 memcpy(pcb->pcb_dbregs, &initdbstate, sizeof(initdbstate));
142 pcb->pcb_flags |= PCB_DBREGS;
143 }
144 memcpy(regs, pcb->pcb_dbregs, sizeof(*regs));
145 }
146
147 void
x86_dbregs_save(struct lwp * l)148 x86_dbregs_save(struct lwp *l)
149 {
150 struct pcb *pcb = lwp_getpcb(l);
151
152 if (!(pcb->pcb_flags & PCB_DBREGS)) {
153 return;
154 }
155
156 KASSERT(pcb->pcb_dbregs != NULL);
157
158 pcb->pcb_dbregs->dr[0] = rdr0();
159 pcb->pcb_dbregs->dr[1] = rdr1();
160 pcb->pcb_dbregs->dr[2] = rdr2();
161 pcb->pcb_dbregs->dr[3] = rdr3();
162
163 pcb->pcb_dbregs->dr[6] = rdr6();
164 pcb->pcb_dbregs->dr[7] = rdr7();
165 }
166
167 void
x86_dbregs_restore(struct lwp * l)168 x86_dbregs_restore(struct lwp *l)
169 {
170 struct pcb *pcb = lwp_getpcb(l);
171
172 if (!(pcb->pcb_flags & PCB_DBREGS)) {
173 return;
174 }
175
176 KASSERT(pcb->pcb_dbregs != NULL);
177
178 ldr0(pcb->pcb_dbregs->dr[0]);
179 ldr1(pcb->pcb_dbregs->dr[1]);
180 ldr2(pcb->pcb_dbregs->dr[2]);
181 ldr3(pcb->pcb_dbregs->dr[3]);
182
183 ldr6(pcb->pcb_dbregs->dr[6]);
184 ldr7(pcb->pcb_dbregs->dr[7]);
185 }
186
187 void
x86_dbregs_store_dr6(struct lwp * l)188 x86_dbregs_store_dr6(struct lwp *l)
189 {
190 struct pcb *pcb = lwp_getpcb(l);
191
192 KASSERT(l == curlwp);
193 KASSERT(pcb->pcb_dbregs != NULL);
194
195 pcb->pcb_dbregs->dr[6] = rdr6();
196 }
197
198 int
x86_dbregs_user_trap(void)199 x86_dbregs_user_trap(void)
200 {
201 register_t dr7, dr6;
202 register_t bp;
203
204 dr7 = rdr7();
205 if ((dr7 & X86_GLOBAL_BREAKPOINT) == 0) {
206 /*
207 * All Global Breakpoint bits are zero, thus the trap couldn't
208 * have been caused by the hardware debug registers.
209 */
210 return 0;
211 }
212
213 dr6 = rdr6();
214 bp = dr6 & X86_BREAKPOINT_CONDITION_DETECTED;
215
216 if (!bp) {
217 /*
218 * None of the breakpoint bits are set, meaning this
219 * trap was not caused by any of the debug registers.
220 */
221 return 0;
222 }
223
224 /*
225 * At least one of the breakpoints was hit, check to see
226 * which ones and if any of them are user space addresses.
227 */
228
229 if (bp & X86_DR6_DR0_BREAKPOINT_CONDITION_DETECTED)
230 if (rdr0() < (vaddr_t)VM_MAXUSER_ADDRESS)
231 return 1;
232
233 if (bp & X86_DR6_DR1_BREAKPOINT_CONDITION_DETECTED)
234 if (rdr1() < (vaddr_t)VM_MAXUSER_ADDRESS)
235 return 1;
236
237 if (bp & X86_DR6_DR2_BREAKPOINT_CONDITION_DETECTED)
238 if (rdr2() < (vaddr_t)VM_MAXUSER_ADDRESS)
239 return 1;
240
241 if (bp & X86_DR6_DR3_BREAKPOINT_CONDITION_DETECTED)
242 if (rdr3() < (vaddr_t)VM_MAXUSER_ADDRESS)
243 return 1;
244
245 return 0;
246 }
247
248 int
x86_dbregs_validate(const struct dbreg * regs)249 x86_dbregs_validate(const struct dbreg *regs)
250 {
251 size_t i;
252
253 /* Check that DR0-DR3 contain user-space address */
254 for (i = 0; i < X86_DBREGS; i++) {
255 if (regs->dr[i] >= (vaddr_t)VM_MAXUSER_ADDRESS)
256 return EINVAL;
257 }
258
259 #ifndef i386
260 if (regs->dr[6] & X86_DR6_MBZ) {
261 return EINVAL;
262 }
263 if (regs->dr[7] & X86_DR7_MBZ) {
264 return EINVAL;
265 }
266 #endif
267 if (regs->dr[7] & X86_DR7_GENERAL_DETECT_ENABLE) {
268 return EINVAL;
269 }
270
271 /*
272 * Skip checks for reserved registers (DR4-DR5, DR8-DR15).
273 */
274
275 return 0;
276 }
277
278 void
x86_dbregs_write(struct lwp * l,const struct dbreg * regs)279 x86_dbregs_write(struct lwp *l, const struct dbreg *regs)
280 {
281 struct pcb *pcb = lwp_getpcb(l);
282
283 if (pcb->pcb_dbregs == NULL) {
284 pcb->pcb_dbregs = pool_get(&x86_dbregspl, PR_WAITOK);
285 }
286
287 memcpy(pcb->pcb_dbregs, regs, sizeof(*regs));
288 pcb->pcb_flags |= PCB_DBREGS;
289 }
290
291 /*
292 * Called with preemption disabled.
293 */
294 void
x86_dbregs_switch(struct lwp * oldlwp,struct lwp * newlwp)295 x86_dbregs_switch(struct lwp *oldlwp, struct lwp *newlwp)
296 {
297 struct pcb *oldpcb, *newpcb;
298 bool olddb, newdb;
299
300 oldpcb = lwp_getpcb(oldlwp);
301 newpcb = lwp_getpcb(newlwp);
302
303 olddb = (oldpcb->pcb_flags & PCB_DBREGS) != 0;
304 newdb = (newpcb->pcb_flags & PCB_DBREGS) != 0;
305
306 if (__predict_true(!olddb && !newdb)) {
307 /* fast path */
308 return;
309 }
310
311 if (olddb) {
312 x86_dbregs_save(oldlwp);
313 }
314 if (newdb) {
315 x86_dbregs_restore(newlwp);
316 } else if (olddb) {
317 x86_dbregs_reset();
318 }
319 }
320