1 /* $NetBSD: fbt_isa.c,v 1.1 2018/05/28 23:47:39 chs 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 * Portions Copyright 2013 Justin Hibbits jhibbits@freebsd.org 25 * Portions Copyright 2013 Howard Su howardsu@freebsd.org 26 * 27 * $FreeBSD: head/sys/cddl/dev/fbt/arm/fbt_isa.c 312378 2017-01-18 13:27:24Z andrew $ 28 * 29 */ 30 31 /* 32 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 33 * Use is subject to license terms. 34 */ 35 36 #include <sys/cdefs.h> 37 #include <sys/param.h> 38 #include <sys/module.h> 39 #include <sys/kmem.h> 40 41 #include <sys/dtrace.h> 42 43 #include <machine/trap.h> 44 #include <arm/cpufunc.h> 45 #include <arm/armreg.h> 46 #include <arm/frame.h> 47 #include <uvm/uvm_extern.h> 48 49 #include "fbt.h" 50 51 #define FBT_PUSHM 0xe92d0000 52 #define FBT_POPM 0xe8bd0000 53 #define FBT_JUMP 0xea000000 54 #define FBT_SUBSP 0xe24dd000 55 56 #define FBT_ENTRY "entry" 57 #define FBT_RETURN "return" 58 59 int 60 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval) 61 { 62 solaris_cpu_t *cpu = &solaris_cpu[cpu_number()]; 63 fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)]; 64 register_t fifthparam; 65 66 for (; fbt != NULL; fbt = fbt->fbtp_hashnext) { 67 if ((uintptr_t)fbt->fbtp_patchpoint == addr) { 68 if (fbt->fbtp_roffset == 0) { 69 /* Get 5th parameter from stack */ 70 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); 71 fifthparam = *(register_t *)frame->tf_svc_sp; 72 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | 73 CPU_DTRACE_BADADDR); 74 75 cpu->cpu_dtrace_caller = frame->tf_svc_lr; 76 dtrace_probe(fbt->fbtp_id, frame->tf_r0, 77 frame->tf_r1, frame->tf_r2, 78 frame->tf_r3, fifthparam); 79 } else { 80 /* XXX set caller */ 81 cpu->cpu_dtrace_caller = 0; 82 dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset, 83 rval, 0, 0, 0); 84 } 85 86 cpu->cpu_dtrace_caller = 0; 87 return (fbt->fbtp_rval); 88 } 89 } 90 91 return (0); 92 } 93 94 95 void 96 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val) 97 { 98 dtrace_icookie_t c; 99 100 c = dtrace_interrupt_disable(); 101 102 ktext_write(fbt->fbtp_patchpoint, &val, sizeof (val)); 103 104 dtrace_interrupt_enable(c); 105 } 106 107 #ifdef __FreeBSD__ 108 109 int 110 fbt_provide_module_function(linker_file_t lf, int symindx, 111 linker_symval_t *symval, void *opaque) 112 { 113 char *modname = opaque; 114 const char *name = symval->name; 115 fbt_probe_t *fbt, *retfbt; 116 uint32_t *instr, *limit; 117 int popm; 118 119 if (fbt_excluded(name)) 120 return (0); 121 122 instr = (uint32_t *)symval->value; 123 limit = (uint32_t *)(symval->value + symval->size); 124 125 /* 126 * va_arg functions has first instruction of 127 * sub sp, sp, #? 128 */ 129 if ((*instr & 0xfffff000) == FBT_SUBSP) 130 instr++; 131 132 /* 133 * check if insn is a pushm with LR 134 */ 135 if ((*instr & 0xffff0000) != FBT_PUSHM || 136 (*instr & (1 << LR)) == 0) 137 return (0); 138 139 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP); 140 fbt->fbtp_name = name; 141 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 142 name, FBT_ENTRY, 5, fbt); 143 fbt->fbtp_patchpoint = instr; 144 fbt->fbtp_ctl = lf; 145 fbt->fbtp_loadcnt = lf->loadcnt; 146 fbt->fbtp_savedval = *instr; 147 fbt->fbtp_patchval = FBT_BREAKPOINT; 148 fbt->fbtp_rval = DTRACE_INVOP_PUSHM; 149 fbt->fbtp_symindx = symindx; 150 151 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 152 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 153 154 lf->fbt_nentries++; 155 156 popm = FBT_POPM | ((*instr) & 0x3FFF) | 0x8000; 157 158 retfbt = NULL; 159 again: 160 for (; instr < limit; instr++) { 161 if (*instr == popm) 162 break; 163 else if ((*instr & 0xff000000) == FBT_JUMP) { 164 uint32_t *target, *start; 165 int offset; 166 167 offset = (*instr & 0xffffff); 168 offset <<= 8; 169 offset /= 64; 170 target = instr + (2 + offset); 171 start = (uint32_t *)symval->value; 172 if (target >= limit || target < start) 173 break; 174 } 175 } 176 177 if (instr >= limit) 178 return (0); 179 180 /* 181 * We have a winner! 182 */ 183 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP); 184 fbt->fbtp_name = name; 185 if (retfbt == NULL) { 186 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 187 name, FBT_RETURN, 5, fbt); 188 } else { 189 retfbt->fbtp_next = fbt; 190 fbt->fbtp_id = retfbt->fbtp_id; 191 } 192 retfbt = fbt; 193 194 fbt->fbtp_patchpoint = instr; 195 fbt->fbtp_ctl = lf; 196 fbt->fbtp_loadcnt = lf->loadcnt; 197 fbt->fbtp_symindx = symindx; 198 if ((*instr & 0xff000000) == FBT_JUMP) 199 fbt->fbtp_rval = DTRACE_INVOP_B; 200 else 201 fbt->fbtp_rval = DTRACE_INVOP_POPM; 202 fbt->fbtp_savedval = *instr; 203 fbt->fbtp_patchval = FBT_BREAKPOINT; 204 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 205 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 206 207 lf->fbt_nentries++; 208 209 instr++; 210 goto again; 211 } 212 213 #endif /* __FreeBSD_ */ 214 215 #ifdef __NetBSD__ 216 217 #define FBT_PATCHVAL DTRACE_BREAKPOINT 218 219 /* entry and return */ 220 #define FBT_BX_LR_P(insn) (((insn) & ~INSN_COND_MASK) == 0x012fff1e) 221 #define FBT_B_LABEL_P(insn) (((insn) & 0xff000000) == 0xea000000) 222 /* entry */ 223 #define FBT_MOV_IP_SP_P(insn) ((insn) == 0xe1a0c00d) 224 /* index=1, add=1, wback=0 */ 225 #define FBT_LDR_IMM_P(insn) (((insn) & 0xfff00000) == 0xe5900000) 226 #define FBT_MOVW_P(insn) (((insn) & 0xfff00000) == 0xe3000000) 227 #define FBT_MOV_IMM_P(insn) (((insn) & 0xffff0000) == 0xe3a00000) 228 #define FBT_CMP_IMM_P(insn) (((insn) & 0xfff00000) == 0xe3500000) 229 #define FBT_PUSH_P(insn) (((insn) & 0xffff0000) == 0xe92d0000) 230 /* return */ 231 /* cond=always, writeback=no, rn=sp and register_list includes pc */ 232 #define FBT_LDM_P(insn) (((insn) & 0x0fff8000) == 0x089d8000) 233 #define FBT_LDMIB_P(insn) (((insn) & 0x0fff8000) == 0x099d8000) 234 #define FBT_MOV_PC_LR_P(insn) (((insn) & ~INSN_COND_MASK) == 0x01a0f00e) 235 /* cond=always, writeback=no, rn=sp and register_list includes lr, but not pc */ 236 #define FBT_LDM_LR_P(insn) (((insn) & 0xffffc000) == 0xe89d4000) 237 #define FBT_LDMIB_LR_P(insn) (((insn) & 0xffffc000) == 0xe99d4000) 238 239 /* rval = insn | invop_id (overwriting cond with invop ID) */ 240 #define BUILD_RVAL(insn, id) (((insn) & ~INSN_COND_MASK) | __SHIFTIN((id), INSN_COND_MASK)) 241 /* encode cond in the first byte */ 242 #define PATCHVAL_ENCODE_COND(insn) (FBT_PATCHVAL | __SHIFTOUT((insn), INSN_COND_MASK)) 243 244 int 245 fbt_provide_module_cb(const char *name, int symindx, void *value, 246 uint32_t symsize, int type, void *opaque) 247 { 248 fbt_probe_t *fbt, *retfbt; 249 uint32_t *instr, *limit; 250 bool was_ldm_lr = false; 251 int size; 252 253 struct fbt_ksyms_arg *fka = opaque; 254 modctl_t *mod = fka->fka_mod; 255 const char *modname = module_name(mod); 256 257 258 /* got a function? */ 259 if (ELF_ST_TYPE(type) != STT_FUNC) 260 return 0; 261 262 if (fbt_excluded(name)) 263 return (0); 264 265 /* 266 * Exclude some more symbols which can be called from probe context. 267 */ 268 if (strncmp(name, "_spl", 4) == 0 || 269 strcmp(name, "binuptime") == 0 || 270 strcmp(name, "nanouptime") == 0 || 271 strcmp(name, "dosoftints") == 0 || 272 strcmp(name, "fbt_emulate") == 0 || 273 strcmp(name, "undefinedinstruction") == 0 || 274 strncmp(name, "dmt_", 4) == 0 /* omap */ || 275 strncmp(name, "mvsoctmr_", 9) == 0 /* marvell */ ) { 276 return 0; 277 } 278 279 instr = (uint32_t *) value; 280 limit = (uint32_t *)((uintptr_t)value + symsize); 281 282 if (!FBT_MOV_IP_SP_P(*instr) 283 && !FBT_BX_LR_P(*instr) 284 && !FBT_MOVW_P(*instr) 285 && !FBT_MOV_IMM_P(*instr) 286 && !FBT_B_LABEL_P(*instr) 287 && !FBT_LDR_IMM_P(*instr) 288 && !FBT_CMP_IMM_P(*instr) 289 && !FBT_PUSH_P(*instr) 290 ) { 291 return 0; 292 } 293 294 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP); 295 fbt->fbtp_name = name; 296 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 297 name, FBT_ENTRY, 5, fbt); 298 fbt->fbtp_patchpoint = instr; 299 fbt->fbtp_ctl = mod; 300 /* fbt->fbtp_loadcnt = lf->loadcnt; */ 301 if (FBT_MOV_IP_SP_P(*instr)) 302 fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_MOV_IP_SP); 303 else if (FBT_LDR_IMM_P(*instr)) 304 fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_LDR_IMM); 305 else if (FBT_MOVW_P(*instr)) 306 fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_MOVW); 307 else if (FBT_MOV_IMM_P(*instr)) 308 fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_MOV_IMM); 309 else if (FBT_CMP_IMM_P(*instr)) 310 fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_CMP_IMM); 311 else if (FBT_BX_LR_P(*instr)) 312 fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_BX_LR); 313 else if (FBT_PUSH_P(*instr)) 314 fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_PUSHM); 315 else if (FBT_B_LABEL_P(*instr)) 316 fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_B); 317 else 318 KASSERT(0); 319 320 KASSERTMSG((fbt->fbtp_rval >> 28) != 0, 321 "fbt %p insn 0x%x name %s rval 0x%08x", 322 fbt, *instr, name, fbt->fbtp_rval); 323 324 fbt->fbtp_patchval = PATCHVAL_ENCODE_COND(*instr); 325 fbt->fbtp_savedval = *instr; 326 fbt->fbtp_symindx = symindx; 327 328 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 329 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 330 331 retfbt = NULL; 332 333 while (instr < limit) { 334 if (instr >= limit) 335 return (0); 336 337 size = 1; 338 339 if (!FBT_BX_LR_P(*instr) 340 && !FBT_MOV_PC_LR_P(*instr) 341 && !FBT_LDM_P(*instr) 342 && !FBT_LDMIB_P(*instr) 343 && !(was_ldm_lr && FBT_B_LABEL_P(*instr)) 344 ) { 345 if (FBT_LDM_LR_P(*instr) || FBT_LDMIB_LR_P(*instr)) 346 was_ldm_lr = true; 347 else 348 was_ldm_lr = false; 349 instr += size; 350 continue; 351 } 352 353 /* 354 * We have a winner! 355 */ 356 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP); 357 fbt->fbtp_name = name; 358 359 if (retfbt == NULL) { 360 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 361 name, FBT_RETURN, 5, fbt); 362 } else { 363 retfbt->fbtp_next = fbt; 364 fbt->fbtp_id = retfbt->fbtp_id; 365 } 366 367 retfbt = fbt; 368 fbt->fbtp_patchpoint = instr; 369 fbt->fbtp_ctl = mod; 370 /* fbt->fbtp_loadcnt = lf->loadcnt; */ 371 fbt->fbtp_symindx = symindx; 372 373 if (FBT_BX_LR_P(*instr)) 374 fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_BX_LR); 375 else if (FBT_MOV_PC_LR_P(*instr)) 376 fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_MOV_PC_LR); 377 else if (FBT_LDM_P(*instr)) 378 fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_LDM); 379 else if (FBT_LDMIB_P(*instr)) 380 fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_POPM); 381 else if (FBT_B_LABEL_P(*instr)) 382 fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_B); 383 else 384 KASSERT(0); 385 386 KASSERTMSG((fbt->fbtp_rval >> 28) != 0, "fbt %p name %s rval 0x%08x", 387 fbt, name, fbt->fbtp_rval); 388 389 fbt->fbtp_roffset = (uintptr_t)(instr - (uint32_t *) value); 390 fbt->fbtp_patchval = PATCHVAL_ENCODE_COND(*instr); 391 392 fbt->fbtp_savedval = *instr; 393 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 394 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 395 396 instr += size; 397 was_ldm_lr = false; 398 } 399 400 return 0; 401 } 402 403 #endif /* __NetBSD__ */ 404