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 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 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 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 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 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 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 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 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 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 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 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 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