xref: /netbsd-src/sys/arch/x86/x86/dbregs.c (revision fa9e56d0be6641ac8ff3f6574b6bb9e1287237e1)
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