1214501Srpaulo/* $NetBSD: fbt_isa.c,v 1.2 2023/05/22 15:12:54 riastradh Exp $ */ 2214501Srpaulo 3289549Srpaulo/* 4214501Srpaulo * CDDL HEADER START 5252726Srpaulo * 6252726Srpaulo * The contents of this file are subject to the terms of the 7214501Srpaulo * Common Development and Distribution License (the "License"). 8214501Srpaulo * You may not use this file except in compliance with the License. 9214501Srpaulo * 10214501Srpaulo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 11214501Srpaulo * or http://www.opensolaris.org/os/licensing. 12214501Srpaulo * See the License for the specific language governing permissions 13214501Srpaulo * and limitations under the License. 14214501Srpaulo * 15214501Srpaulo * When distributing Covered Code, include this CDDL HEADER in each 16214501Srpaulo * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 17214501Srpaulo * If applicable, add the following below this CDDL HEADER, with the 18214501Srpaulo * fields enclosed by brackets "[]" replaced with your own identifying 19214501Srpaulo * information: Portions Copyright [yyyy] [name of copyright owner] 20214501Srpaulo * 21214501Srpaulo * CDDL HEADER END 22214501Srpaulo * 23214501Srpaulo * Portions Copyright 2006-2008 John Birrell jb@freebsd.org 24214501Srpaulo * Portions Copyright 2013 Justin Hibbits jhibbits@freebsd.org 25214501Srpaulo * Portions Copyright 2013 Howard Su howardsu@freebsd.org 26214501Srpaulo * 27281806Srpaulo * $FreeBSD: head/sys/cddl/dev/fbt/arm/fbt_isa.c 312378 2017-01-18 13:27:24Z andrew $ 28281806Srpaulo * 29281806Srpaulo */ 30289549Srpaulo 31346981Scy/* 32214501Srpaulo * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 33214501Srpaulo * Use is subject to license terms. 34214501Srpaulo */ 35214501Srpaulo 36214501Srpaulo#include <sys/cdefs.h> 37214501Srpaulo#include <sys/param.h> 38214501Srpaulo#include <sys/module.h> 39351611Scy#include <sys/kmem.h> 40351611Scy 41214501Srpaulo#include <sys/dtrace.h> 42351611Scy 43214501Srpaulo#include <machine/trap.h> 44214501Srpaulo#include <arm/cpufunc.h> 45252726Srpaulo#include <arm/armreg.h> 46252726Srpaulo#include <arm/frame.h> 47252726Srpaulo#include <uvm/uvm_extern.h> 48281806Srpaulo 49281806Srpaulo#include "fbt.h" 50214501Srpaulo 51214501Srpaulo#define FBT_PUSHM 0xe92d0000 52214501Srpaulo#define FBT_POPM 0xe8bd0000 53214501Srpaulo#define FBT_JUMP 0xea000000 54214501Srpaulo#define FBT_SUBSP 0xe24dd000 55214501Srpaulo 56281806Srpaulo#define FBT_ENTRY "entry" 57214501Srpaulo#define FBT_RETURN "return" 58214501Srpaulo 59214501Srpaulostatic uint32_t 60214501Srpauloldinstr(const uint32_t *instr) 61214501Srpaulo{ 62214501Srpaulo#ifdef _ARM_ARCH_BE8 /* big-endian data, big-endian instructions */ 63214501Srpaulo return *instr; 64214501Srpaulo#else /* little-endian instructions */ 65214501Srpaulo return le32toh(*instr); 66214501Srpaulo#endif 67214501Srpaulo} 68214501Srpaulo 69214501Srpaulostatic void 70214501Srpaulostinstr(uint32_t *instr, uint32_t val) 71214501Srpaulo{ 72281806Srpaulo 73281806Srpaulo#ifdef _ARM_ARCH_BE8 /* big-endian data, big-endian instructions */ 74214501Srpaulo val = bswap32(val); 75214501Srpaulo#endif 76214501Srpaulo ktext_write(instr, &val, sizeof(val)); /* write little-endian */ 77214501Srpaulo} 78214501Srpaulo 79214501Srpauloint 80214501Srpaulofbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval) 81214501Srpaulo{ 82214501Srpaulo solaris_cpu_t *cpu = &solaris_cpu[cpu_number()]; 83281806Srpaulo fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)]; 84281806Srpaulo register_t fifthparam; 85281806Srpaulo 86214501Srpaulo for (; fbt != NULL; fbt = fbt->fbtp_hashnext) { 87214501Srpaulo if ((uintptr_t)fbt->fbtp_patchpoint == addr) { 88214501Srpaulo if (fbt->fbtp_roffset == 0) { 89214501Srpaulo /* Get 5th parameter from stack */ 90214501Srpaulo DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); 91214501Srpaulo fifthparam = *(register_t *)frame->tf_svc_sp; 92214501Srpaulo DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | 93214501Srpaulo CPU_DTRACE_BADADDR); 94252726Srpaulo 95252726Srpaulo cpu->cpu_dtrace_caller = frame->tf_svc_lr; 96252726Srpaulo dtrace_probe(fbt->fbtp_id, frame->tf_r0, 97214501Srpaulo frame->tf_r1, frame->tf_r2, 98214501Srpaulo frame->tf_r3, fifthparam); 99281806Srpaulo } else { 100281806Srpaulo /* XXX set caller */ 101214501Srpaulo cpu->cpu_dtrace_caller = 0; 102289549Srpaulo dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset, 103289549Srpaulo rval, 0, 0, 0); 104289549Srpaulo } 105214501Srpaulo 106214501Srpaulo cpu->cpu_dtrace_caller = 0; 107 return (fbt->fbtp_rval); 108 } 109 } 110 111 return (0); 112} 113 114 115void 116fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val) 117{ 118 dtrace_icookie_t c; 119 120 c = dtrace_interrupt_disable(); 121 stinstr(fbt->fbtp_patchpoint, val); 122 dtrace_interrupt_enable(c); 123} 124 125#ifdef __FreeBSD__ 126 127int 128fbt_provide_module_function(linker_file_t lf, int symindx, 129 linker_symval_t *symval, void *opaque) 130{ 131 char *modname = opaque; 132 const char *name = symval->name; 133 fbt_probe_t *fbt, *retfbt; 134 uint32_t *instr, *limit; 135 int popm; 136 137 if (fbt_excluded(name)) 138 return (0); 139 140 instr = (uint32_t *)symval->value; 141 limit = (uint32_t *)(symval->value + symval->size); 142 143 /* 144 * va_arg functions has first instruction of 145 * sub sp, sp, #? 146 */ 147 if ((ldinstr(instr) & 0xfffff000) == FBT_SUBSP) 148 instr++; 149 150 /* 151 * check if insn is a pushm with LR 152 */ 153 if ((ldinstr(instr) & 0xffff0000) != FBT_PUSHM || 154 (ldinstr(instr) & (1 << LR)) == 0) 155 return (0); 156 157 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP); 158 fbt->fbtp_name = name; 159 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 160 name, FBT_ENTRY, 5, fbt); 161 fbt->fbtp_patchpoint = instr; 162 fbt->fbtp_ctl = lf; 163 fbt->fbtp_loadcnt = lf->loadcnt; 164 fbt->fbtp_savedval = ldinstr(instr); 165 fbt->fbtp_patchval = FBT_BREAKPOINT; 166 fbt->fbtp_rval = DTRACE_INVOP_PUSHM; 167 fbt->fbtp_symindx = symindx; 168 169 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 170 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 171 172 lf->fbt_nentries++; 173 174 popm = FBT_POPM | (ldinstr(instr) & 0x3FFF) | 0x8000; 175 176 retfbt = NULL; 177again: 178 for (; instr < limit; instr++) { 179 if (ldinstr(instr) == popm) 180 break; 181 else if ((ldinstr(instr) & 0xff000000) == FBT_JUMP) { 182 uint32_t *target, *start; 183 int offset; 184 185 offset = (ldinstr(instr) & 0xffffff); 186 offset <<= 8; 187 offset /= 64; 188 target = instr + (2 + offset); 189 start = (uint32_t *)symval->value; 190 if (target >= limit || target < start) 191 break; 192 } 193 } 194 195 if (instr >= limit) 196 return (0); 197 198 /* 199 * We have a winner! 200 */ 201 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP); 202 fbt->fbtp_name = name; 203 if (retfbt == NULL) { 204 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 205 name, FBT_RETURN, 5, fbt); 206 } else { 207 retfbt->fbtp_next = fbt; 208 fbt->fbtp_id = retfbt->fbtp_id; 209 } 210 retfbt = fbt; 211 212 fbt->fbtp_patchpoint = instr; 213 fbt->fbtp_ctl = lf; 214 fbt->fbtp_loadcnt = lf->loadcnt; 215 fbt->fbtp_symindx = symindx; 216 if ((ldinstr(instr) & 0xff000000) == FBT_JUMP) 217 fbt->fbtp_rval = DTRACE_INVOP_B; 218 else 219 fbt->fbtp_rval = DTRACE_INVOP_POPM; 220 fbt->fbtp_savedval = ldinstr(instr); 221 fbt->fbtp_patchval = FBT_BREAKPOINT; 222 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 223 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 224 225 lf->fbt_nentries++; 226 227 instr++; 228 goto again; 229} 230 231#endif /* __FreeBSD_ */ 232 233#ifdef __NetBSD__ 234 235#define FBT_PATCHVAL DTRACE_BREAKPOINT 236 237/* entry and return */ 238#define FBT_BX_LR_P(insn) (((insn) & ~INSN_COND_MASK) == 0x012fff1e) 239#define FBT_B_LABEL_P(insn) (((insn) & 0xff000000) == 0xea000000) 240/* entry */ 241#define FBT_MOV_IP_SP_P(insn) ((insn) == 0xe1a0c00d) 242/* index=1, add=1, wback=0 */ 243#define FBT_LDR_IMM_P(insn) (((insn) & 0xfff00000) == 0xe5900000) 244#define FBT_MOVW_P(insn) (((insn) & 0xfff00000) == 0xe3000000) 245#define FBT_MOV_IMM_P(insn) (((insn) & 0xffff0000) == 0xe3a00000) 246#define FBT_CMP_IMM_P(insn) (((insn) & 0xfff00000) == 0xe3500000) 247#define FBT_PUSH_P(insn) (((insn) & 0xffff0000) == 0xe92d0000) 248/* return */ 249/* cond=always, writeback=no, rn=sp and register_list includes pc */ 250#define FBT_LDM_P(insn) (((insn) & 0x0fff8000) == 0x089d8000) 251#define FBT_LDMIB_P(insn) (((insn) & 0x0fff8000) == 0x099d8000) 252#define FBT_MOV_PC_LR_P(insn) (((insn) & ~INSN_COND_MASK) == 0x01a0f00e) 253/* cond=always, writeback=no, rn=sp and register_list includes lr, but not pc */ 254#define FBT_LDM_LR_P(insn) (((insn) & 0xffffc000) == 0xe89d4000) 255#define FBT_LDMIB_LR_P(insn) (((insn) & 0xffffc000) == 0xe99d4000) 256 257/* rval = insn | invop_id (overwriting cond with invop ID) */ 258#define BUILD_RVAL(insn, id) (((insn) & ~INSN_COND_MASK) | __SHIFTIN((id), INSN_COND_MASK)) 259/* encode cond in the first byte */ 260#define PATCHVAL_ENCODE_COND(insn) (FBT_PATCHVAL | __SHIFTOUT((insn), INSN_COND_MASK)) 261 262int 263fbt_provide_module_cb(const char *name, int symindx, void *value, 264 uint32_t symsize, int type, void *opaque) 265{ 266 fbt_probe_t *fbt, *retfbt; 267 uint32_t *instr, *limit; 268 bool was_ldm_lr = false; 269 int size; 270 271 struct fbt_ksyms_arg *fka = opaque; 272 modctl_t *mod = fka->fka_mod; 273 const char *modname = module_name(mod); 274 275 276 /* got a function? */ 277 if (ELF_ST_TYPE(type) != STT_FUNC) 278 return 0; 279 280 if (fbt_excluded(name)) 281 return (0); 282 283 /* 284 * Exclude some more symbols which can be called from probe context. 285 */ 286 if (strncmp(name, "_spl", 4) == 0 || 287 strcmp(name, "binuptime") == 0 || 288 strcmp(name, "nanouptime") == 0 || 289 strcmp(name, "dosoftints") == 0 || 290 strcmp(name, "fbt_emulate") == 0 || 291 strcmp(name, "undefinedinstruction") == 0 || 292 strncmp(name, "dmt_", 4) == 0 /* omap */ || 293 strncmp(name, "mvsoctmr_", 9) == 0 /* marvell */ ) { 294 return 0; 295 } 296 297 instr = (uint32_t *) value; 298 limit = (uint32_t *)((uintptr_t)value + symsize); 299 300 if (!FBT_MOV_IP_SP_P(ldinstr(instr)) 301 && !FBT_BX_LR_P(ldinstr(instr)) 302 && !FBT_MOVW_P(ldinstr(instr)) 303 && !FBT_MOV_IMM_P(ldinstr(instr)) 304 && !FBT_B_LABEL_P(ldinstr(instr)) 305 && !FBT_LDR_IMM_P(ldinstr(instr)) 306 && !FBT_CMP_IMM_P(ldinstr(instr)) 307 && !FBT_PUSH_P(ldinstr(instr)) 308 ) { 309 return 0; 310 } 311 312 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP); 313 fbt->fbtp_name = name; 314 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 315 name, FBT_ENTRY, 5, fbt); 316 fbt->fbtp_patchpoint = instr; 317 fbt->fbtp_ctl = mod; 318 /* fbt->fbtp_loadcnt = lf->loadcnt; */ 319 if (FBT_MOV_IP_SP_P(ldinstr(instr))) { 320 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), 321 DTRACE_INVOP_MOV_IP_SP); 322 } else if (FBT_LDR_IMM_P(ldinstr(instr))) { 323 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), 324 DTRACE_INVOP_LDR_IMM); 325 } else if (FBT_MOVW_P(ldinstr(instr))) { 326 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), DTRACE_INVOP_MOVW); 327 } else if (FBT_MOV_IMM_P(ldinstr(instr))) { 328 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), 329 DTRACE_INVOP_MOV_IMM); 330 } else if (FBT_CMP_IMM_P(ldinstr(instr))) { 331 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), 332 DTRACE_INVOP_CMP_IMM); 333 } else if (FBT_BX_LR_P(ldinstr(instr))) { 334 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), 335 DTRACE_INVOP_BX_LR); 336 } else if (FBT_PUSH_P(ldinstr(instr))) { 337 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), 338 DTRACE_INVOP_PUSHM); 339 } else if (FBT_B_LABEL_P(ldinstr(instr))) { 340 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), 341 DTRACE_INVOP_B); 342 } else { 343 KASSERT(0); 344 } 345 346 KASSERTMSG((fbt->fbtp_rval >> 28) != 0, 347 "fbt %p insn 0x%x name %s rval 0x%08x", 348 fbt, ldinstr(instr), name, fbt->fbtp_rval); 349 350 fbt->fbtp_patchval = PATCHVAL_ENCODE_COND(ldinstr(instr)); 351 fbt->fbtp_savedval = ldinstr(instr); 352 fbt->fbtp_symindx = symindx; 353 354 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 355 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 356 357 retfbt = NULL; 358 359 while (instr < limit) { 360 if (instr >= limit) 361 return (0); 362 363 size = 1; 364 365 if (!FBT_BX_LR_P(ldinstr(instr)) 366 && !FBT_MOV_PC_LR_P(ldinstr(instr)) 367 && !FBT_LDM_P(ldinstr(instr)) 368 && !FBT_LDMIB_P(ldinstr(instr)) 369 && !(was_ldm_lr && FBT_B_LABEL_P(ldinstr(instr))) 370 ) { 371 if (FBT_LDM_LR_P(ldinstr(instr)) || 372 FBT_LDMIB_LR_P(ldinstr(instr))) 373 was_ldm_lr = true; 374 else 375 was_ldm_lr = false; 376 instr += size; 377 continue; 378 } 379 380 /* 381 * We have a winner! 382 */ 383 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP); 384 fbt->fbtp_name = name; 385 386 if (retfbt == NULL) { 387 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 388 name, FBT_RETURN, 5, fbt); 389 } else { 390 retfbt->fbtp_next = fbt; 391 fbt->fbtp_id = retfbt->fbtp_id; 392 } 393 394 retfbt = fbt; 395 fbt->fbtp_patchpoint = instr; 396 fbt->fbtp_ctl = mod; 397 /* fbt->fbtp_loadcnt = lf->loadcnt; */ 398 fbt->fbtp_symindx = symindx; 399 400 if (FBT_BX_LR_P(ldinstr(instr))) { 401 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), 402 DTRACE_INVOP_BX_LR); 403 } else if (FBT_MOV_PC_LR_P(ldinstr(instr))) { 404 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), 405 DTRACE_INVOP_MOV_PC_LR); 406 } else if (FBT_LDM_P(ldinstr(instr))) { 407 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), 408 DTRACE_INVOP_LDM); 409 } else if (FBT_LDMIB_P(ldinstr(instr))) { 410 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), 411 DTRACE_INVOP_POPM); 412 } else if (FBT_B_LABEL_P(ldinstr(instr))) { 413 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), 414 DTRACE_INVOP_B); 415 } else { 416 KASSERT(0); 417 } 418 419 KASSERTMSG((fbt->fbtp_rval >> 28) != 0, "fbt %p name %s rval 0x%08x", 420 fbt, name, fbt->fbtp_rval); 421 422 fbt->fbtp_roffset = (uintptr_t)(instr - (uint32_t *) value); 423 fbt->fbtp_patchval = PATCHVAL_ENCODE_COND(ldinstr(instr)); 424 425 fbt->fbtp_savedval = ldinstr(instr); 426 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; 427 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; 428 429 instr += size; 430 was_ldm_lr = false; 431 } 432 433 return 0; 434} 435 436#endif /* __NetBSD__ */ 437