1 /* $NetBSD: fbt_isa.c,v 1.3 2020/05/02 11:37:17 maxv Exp $ */ 2 3 /* 4 * CDDL HEADER START 5 * 6 * The contents of this file are subject to the terms of the 7 * Common Development and Distribution License (the "License"). 8 * You may not use this file except in compliance with the License. 9 * 10 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 11 * or http://www.opensolaris.org/os/licensing. 12 * See the License for the specific language governing permissions 13 * and limitations under the License. 14 * 15 * When distributing Covered Code, include this CDDL HEADER in each 16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 17 * If applicable, add the following below this CDDL HEADER, with the 18 * fields enclosed by brackets "[]" replaced with your own identifying 19 * information: Portions Copyright [yyyy] [name of copyright owner] 20 * 21 * CDDL HEADER END 22 * 23 * Portions Copyright 2006-2008 John Birrell jb@freebsd.org 24 * 25 * $FreeBSD: head/sys/cddl/dev/fbt/x86/fbt_isa.c 309785 2016-12-10 03:11:05Z markj $ 26 * 27 */ 28 29 /* 30 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 31 * Use is subject to license terms. 32 */ 33 34 #include <sys/cdefs.h> 35 #include <sys/proc.h> 36 #include <sys/param.h> 37 #include <sys/cpu.h> 38 #include <sys/module.h> 39 #include <sys/kmem.h> 40 41 #include <sys/dtrace.h> 42 43 #if 1 44 #include <machine/cpufunc.h> 45 #include <machine/specialreg.h> 46 #if 0 47 #include <x86/cpuvar.h> 48 #endif 49 #include <x86/cputypes.h> 50 #endif 51 52 #include "fbt.h" 53 54 #define FBT_PUSHL_EBP 0x55 55 #define FBT_MOVL_ESP_EBP0_V0 0x8b 56 #define FBT_MOVL_ESP_EBP1_V0 0xec 57 #define FBT_MOVL_ESP_EBP0_V1 0x89 58 #define FBT_MOVL_ESP_EBP1_V1 0xe5 59 #define FBT_REX_RSP_RBP 0x48 60 61 #define FBT_POPL_EBP 0x5d 62 #define FBT_RET 0xc3 63 #define FBT_RET_IMM16 0xc2 64 #define FBT_LEAVE 0xc9 65 66 #ifdef __amd64__ 67 #define FBT_PATCHVAL 0xcc 68 #else 69 #define FBT_PATCHVAL 0xf0 70 #endif 71 72 #define FBT_ENTRY "entry" 73 #define FBT_RETURN "return" 74 75 int 76 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval) 77 { 78 solaris_cpu_t *cpu; 79 uintptr_t *stack; 80 uintptr_t arg0, arg1, arg2, arg3, arg4; 81 fbt_probe_t *fbt; 82 83 #ifdef __amd64__ 84 stack = (uintptr_t *)frame->tf_rsp; 85 #else 86 /* Skip hardware-saved registers. */ 87 #ifdef __NetBSD__ 88 stack = (uintptr_t *)&frame->tf_esp; 89 #else 90 stack = (uintptr_t *)frame->tf_isp + 3; 91 #endif 92 #endif 93 94 cpu = &solaris_cpu[cpu_number()]; 95 fbt = fbt_probetab[FBT_ADDR2NDX(addr)]; 96 for (; fbt != NULL; fbt = fbt->fbtp_hashnext) { 97 if ((uintptr_t)fbt->fbtp_patchpoint == addr) { 98 if (fbt->fbtp_roffset == 0) { 99 #ifdef __amd64__ 100 /* fbt->fbtp_rval == DTRACE_INVOP_PUSHQ_RBP */ 101 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); 102 cpu->cpu_dtrace_caller = stack[0]; 103 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | 104 CPU_DTRACE_BADADDR); 105 106 arg0 = frame->tf_rdi; 107 arg1 = frame->tf_rsi; 108 arg2 = frame->tf_rdx; 109 arg3 = frame->tf_rcx; 110 arg4 = frame->tf_r8; 111 #else 112 int i = 0; 113 114 /* 115 * When accessing the arguments on the stack, 116 * we must protect against accessing beyond 117 * the stack. We can safely set NOFAULT here 118 * -- we know that interrupts are already 119 * disabled. 120 */ 121 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); 122 cpu->cpu_dtrace_caller = stack[i++]; 123 arg0 = stack[i++]; 124 arg1 = stack[i++]; 125 arg2 = stack[i++]; 126 arg3 = stack[i++]; 127 arg4 = stack[i++]; 128 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | 129 CPU_DTRACE_BADADDR); 130 #endif 131 132 dtrace_probe(fbt->fbtp_id, arg0, arg1, 133 arg2, arg3, arg4); 134 135 cpu->cpu_dtrace_caller = 0; 136 } else { 137 #ifdef __amd64__ 138 /* 139 * On amd64, we instrument the ret, not the 140 * leave. We therefore need to set the caller 141 * to ensure that the top frame of a stack() 142 * action is correct. 143 */ 144 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); 145 cpu->cpu_dtrace_caller = stack[0]; 146 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | 147 CPU_DTRACE_BADADDR); 148 #endif 149 150 dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset, 151 rval, 0, 0, 0); 152 cpu->cpu_dtrace_caller = 0; 153 } 154 155 return (fbt->fbtp_rval); 156 } 157 } 158 159 return (0); 160 } 161 162 163 164 165 #ifdef __FreeBSD__ 166 void 167 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val) 168 { 169 170 *fbt->fbtp_patchpoint = val; 171 } 172 #endif 173 174 #ifdef __NetBSD__ 175 /* 176 * XXX XXX XXX This is absolutely unsafe, the mere existence of this code is a 177 * problem, because this function is too easily ROP-able. But this gets 178 * compiled as a module and never in the kernel, so we are fine "by default". 179 * XXX Add a #warning if it gets compiled in the kernel? 180 */ 181 void 182 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val) 183 { 184 u_long psl, cr0; 185 186 /* Disable interrupts. */ 187 psl = x86_read_psl(); 188 x86_disable_intr(); 189 190 /* Disable write protection in supervisor mode. */ 191 cr0 = rcr0(); 192 lcr0(cr0 & ~CR0_WP); 193 194 /* XXX XXX XXX Shouldn't rely on caller-provided dst! */ 195 /* XXX XXX XXX Shouldn't rely on caller-provided val! */ 196 for (; fbt != NULL; fbt = fbt->fbtp_next) { 197 *fbt->fbtp_patchpoint = val; 198 } 199 200 /* Write back and invalidate cache, flush pipelines. */ 201 wbinvd(); 202 x86_flush(); 203 204 /* Re-enable write protection. */ 205 lcr0(cr0); 206 207 /* Restore the PSL, potentially re-enabling interrupts. */ 208 x86_write_psl(psl); 209 } 210 #endif 211 212 213 #ifdef __FreeBSD__ 214 int 215 fbt_provide_module_function(linker_file_t lf, int symindx, 216 linker_symval_t *symval, void *opaque) 217 #endif 218 #ifdef __NetBSD__ 219 int 220 fbt_provide_module_cb(const char *name, int symindx, void *value, 221 uint32_t symsize, int type, void *opaque) 222 #endif 223 { 224 fbt_probe_t *fbt, *retfbt; 225 u_int8_t *instr, *limit; 226 int j; 227 int size; 228 229 #ifdef __FreeBSD_ 230 char *modname = opaque; 231 const char *name = symval->name; 232 size_t symsize = symval->size; 233 void *value = symval->value; 234 235 /* 236 * trap_check() is a wrapper for DTrace's fault handler, so we don't 237 * want to be able to instrument it. 238 */ 239 if (strcmp(name, "trap_check") == 0) 240 return (0); 241 #endif 242 #ifdef __NetBSD__ 243 struct fbt_ksyms_arg *fka = opaque; 244 modctl_t *mod = fka->fka_mod; 245 const char *modname = module_name(mod); 246 247 /* got a function? */ 248 if (ELF_ST_TYPE(type) != STT_FUNC) 249 return 0; 250 251 /* 252 * Exclude some more symbols which can be called from probe context. 253 */ 254 if (strcmp(name, "x86_curcpu") == 0 || 255 strcmp(name, "x86_curlwp") == 0) { 256 return 0; 257 } 258 #endif 259 260 if (fbt_excluded(name)) 261 return (0); 262 263 instr = (u_int8_t *) value; 264 limit = (u_int8_t *) value + symsize; 265 266 #ifdef __amd64__ 267 while (instr < limit) { 268 if (*instr == FBT_PUSHL_EBP) 269 break; 270 271 if ((size = dtrace_instr_size(instr)) <= 0) 272 break; 273 274 instr += size; 275 } 276 277 if (instr >= limit || *instr != FBT_PUSHL_EBP) { 278 /* 279 * We either don't save the frame pointer in this 280 * function, or we ran into some disassembly 281 * screw-up. Either way, we bail. 282 */ 283 return (0); 284 } 285 #else 286 if (instr[0] != FBT_PUSHL_EBP) 287 return (0); 288 289 if (!(instr[1] == FBT_MOVL_ESP_EBP0_V0 && 290 instr[2] == FBT_MOVL_ESP_EBP1_V0) && 291 !(instr[1] == FBT_MOVL_ESP_EBP0_V1 && 292 instr[2] == FBT_MOVL_ESP_EBP1_V1)) 293 return (0); 294 #endif 295 296 fbt = kmem_zalloc(sizeof (*fbt), KM_SLEEP); 297 fbt->fbtp_name = name; 298 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 299 name, FBT_ENTRY, 3, fbt); 300 fbt->fbtp_patchpoint = instr; 301 #ifdef __FreeBSD__ 302 fbt->fbtp_ctl = lf; 303 fbt->fbtp_loadcnt = lf->loadcnt; 304 #endif 305 #ifdef __NetBSD__ 306 fbt->fbtp_ctl = mod; 307 #endif 308 fbt->fbtp_rval = DTRACE_INVOP_PUSHL_EBP; 309 fbt->fbtp_savedval = *instr; 310 fbt->fbtp_patchval = FBT_PATCHVAL; 311 fbt->fbtp_symindx = symindx; 312 313 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 314 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 315 #ifdef __FreeBSD__ 316 lf->fbt_nentries++; 317 #endif 318 319 retfbt = NULL; 320 again: 321 if (instr >= limit) 322 return (0); 323 324 /* 325 * If this disassembly fails, then we've likely walked off into 326 * a jump table or some other unsuitable area. Bail out of the 327 * disassembly now. 328 */ 329 if ((size = dtrace_instr_size(instr)) <= 0) 330 return (0); 331 332 #ifdef __amd64__ 333 /* 334 * We only instrument "ret" on amd64 -- we don't yet instrument 335 * ret imm16, largely because the compiler doesn't seem to 336 * (yet) emit them in the kernel... 337 */ 338 if (*instr != FBT_RET) { 339 instr += size; 340 goto again; 341 } 342 #else 343 if (!(size == 1 && 344 (*instr == FBT_POPL_EBP || *instr == FBT_LEAVE) && 345 (*(instr + 1) == FBT_RET || 346 *(instr + 1) == FBT_RET_IMM16))) { 347 instr += size; 348 goto again; 349 } 350 #endif 351 352 /* 353 * We (desperately) want to avoid erroneously instrumenting a 354 * jump table, especially given that our markers are pretty 355 * short: two bytes on x86, and just one byte on amd64. To 356 * determine if we're looking at a true instruction sequence 357 * or an inline jump table that happens to contain the same 358 * byte sequences, we resort to some heuristic sleeze: we 359 * treat this instruction as being contained within a pointer, 360 * and see if that pointer points to within the body of the 361 * function. If it does, we refuse to instrument it. 362 */ 363 for (j = 0; j < sizeof (uintptr_t); j++) { 364 caddr_t check = (caddr_t) instr - j; 365 uint8_t *ptr; 366 367 if (check < (caddr_t)value) 368 break; 369 370 if (check + sizeof (caddr_t) > (caddr_t)limit) 371 continue; 372 373 ptr = *(uint8_t **)check; 374 375 if (ptr >= (uint8_t *) value && ptr < limit) { 376 instr += size; 377 goto again; 378 } 379 } 380 381 /* 382 * We have a winner! 383 */ 384 fbt = kmem_zalloc(sizeof (*fbt), KM_SLEEP); 385 fbt->fbtp_name = name; 386 387 if (retfbt == NULL) { 388 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 389 name, FBT_RETURN, 3, fbt); 390 } else { 391 retfbt->fbtp_next = fbt; 392 fbt->fbtp_id = retfbt->fbtp_id; 393 } 394 395 retfbt = fbt; 396 fbt->fbtp_patchpoint = instr; 397 #ifdef __FreeBSD__ 398 fbt->fbtp_ctl = lf; 399 fbt->fbtp_loadcnt = lf->loadcnt; 400 #endif 401 #ifdef __NetBSD__ 402 fbt->fbtp_ctl = mod; 403 #endif 404 fbt->fbtp_symindx = symindx; 405 406 #ifndef __amd64__ 407 if (*instr == FBT_POPL_EBP) { 408 fbt->fbtp_rval = DTRACE_INVOP_POPL_EBP; 409 } else { 410 ASSERT(*instr == FBT_LEAVE); 411 fbt->fbtp_rval = DTRACE_INVOP_LEAVE; 412 } 413 fbt->fbtp_roffset = 414 (uintptr_t)(instr - (uint8_t *) value) + 1; 415 416 #else 417 ASSERT(*instr == FBT_RET); 418 fbt->fbtp_rval = DTRACE_INVOP_RET; 419 fbt->fbtp_roffset = 420 (uintptr_t)(instr - (uint8_t *) value); 421 #endif 422 423 fbt->fbtp_savedval = *instr; 424 fbt->fbtp_patchval = FBT_PATCHVAL; 425 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 426 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 427 428 #ifdef __FreeBSD__ 429 lf->fbt_nentries++; 430 #endif 431 432 instr += size; 433 goto again; 434 } 435