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