| 1 | |
| 2 | |
| 3 | * arch/arm64/kernel/ftrace.c
|
| 4 | *
|
| 5 | * Copyright (C) 2013 Linaro Limited
|
| 6 | * Author: AKASHI Takahiro <[email protected]>
|
| 7 |
|
| 8 |
|
| 9 | #include <linux/ftrace.h>
|
| 10 | #include <linux/module.h>
|
| 11 | #include <linux/swab.h>
|
| 12 | #include <linux/uaccess.h>
|
| 13 |
|
| 14 | #include <asm/cacheflush.h>
|
| 15 | #include <asm/debug-monitors.h>
|
| 16 | #include <asm/ftrace.h>
|
| 17 | #include <asm/insn.h>
|
| 18 | #include <asm/text-patching.h>
|
| 19 |
|
| 20 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
|
| 21 | struct fregs_offset {
|
| 22 | const char *name;
|
| 23 | int offset;
|
| 24 | };
|
| 25 |
|
| 26 | #define FREGS_OFFSET(n, field) \
|
| 27 | { \
|
| 28 | .name = n, \
|
| 29 | .offset = offsetof(struct __arch_ftrace_regs, field), \
|
| 30 | }
|
| 31 |
|
| 32 | static const struct fregs_offset fregs_offsets[] = {
|
| 33 | FREGS_OFFSET("x0", regs[0]),
|
| 34 | FREGS_OFFSET("x1", regs[1]),
|
| 35 | FREGS_OFFSET("x2", regs[2]),
|
| 36 | FREGS_OFFSET("x3", regs[3]),
|
| 37 | FREGS_OFFSET("x4", regs[4]),
|
| 38 | FREGS_OFFSET("x5", regs[5]),
|
| 39 | FREGS_OFFSET("x6", regs[6]),
|
| 40 | FREGS_OFFSET("x7", regs[7]),
|
| 41 | FREGS_OFFSET("x8", regs[8]),
|
| 42 |
|
| 43 | FREGS_OFFSET("x29", fp),
|
| 44 | FREGS_OFFSET("x30", lr),
|
| 45 | FREGS_OFFSET("lr", lr),
|
| 46 |
|
| 47 | FREGS_OFFSET("sp", sp),
|
| 48 | FREGS_OFFSET("pc", pc),
|
| 49 | };
|
| 50 |
|
| 51 | int ftrace_regs_query_register_offset(const char *name)
|
| 52 | {
|
| 53 | for (int i = 0; i < ARRAY_SIZE(fregs_offsets); i++) {
|
| 54 | const struct fregs_offset *roff = &fregs_offsets[i];
|
| 55 | if (!strcmp(roff->name, name))
|
| 56 | return roff->offset;
|
| 57 | }
|
| 58 |
|
| 59 | return -EINVAL;
|
| 60 | }
|
| 61 | #endif
|
| 62 |
|
| 63 | unsigned long ftrace_call_adjust(unsigned long addr)
|
| 64 | {
|
| 65 | |
| 66 | * When using mcount, addr is the address of the mcount call
|
| 67 | * instruction, and no adjustment is necessary.
|
| 68 |
|
| 69 | if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_ARGS))
|
| 70 | return addr;
|
| 71 |
|
| 72 | |
| 73 | * When using patchable-function-entry without pre-function NOPS, addr
|
| 74 | * is the address of the first NOP after the function entry point.
|
| 75 | *
|
| 76 | * The compiler has either generated:
|
| 77 | *
|
| 78 | * addr+00: func: NOP // To be patched to MOV X9, LR
|
| 79 | * addr+04: NOP // To be patched to BL <caller>
|
| 80 | *
|
| 81 | * Or:
|
| 82 | *
|
| 83 | * addr-04: BTI C
|
| 84 | * addr+00: func: NOP // To be patched to MOV X9, LR
|
| 85 | * addr+04: NOP // To be patched to BL <caller>
|
| 86 | *
|
| 87 | * We must adjust addr to the address of the NOP which will be patched
|
| 88 | * to `BL <caller>`, which is at `addr + 4` bytes in either case.
|
| 89 | *
|
| 90 |
|
| 91 | if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS))
|
| 92 | return addr + AARCH64_INSN_SIZE;
|
| 93 |
|
| 94 | |
| 95 | * When using patchable-function-entry with pre-function NOPs, addr is
|
| 96 | * the address of the first pre-function NOP.
|
| 97 | *
|
| 98 | * Starting from an 8-byte aligned base, the compiler has either
|
| 99 | * generated:
|
| 100 | *
|
| 101 | * addr+00: NOP // Literal (first 32 bits)
|
| 102 | * addr+04: NOP // Literal (last 32 bits)
|
| 103 | * addr+08: func: NOP // To be patched to MOV X9, LR
|
| 104 | * addr+12: NOP // To be patched to BL <caller>
|
| 105 | *
|
| 106 | * Or:
|
| 107 | *
|
| 108 | * addr+00: NOP // Literal (first 32 bits)
|
| 109 | * addr+04: NOP // Literal (last 32 bits)
|
| 110 | * addr+08: func: BTI C
|
| 111 | * addr+12: NOP // To be patched to MOV X9, LR
|
| 112 | * addr+16: NOP // To be patched to BL <caller>
|
| 113 | *
|
| 114 | * We must adjust addr to the address of the NOP which will be patched
|
| 115 | * to `BL <caller>`, which is at either addr+12 or addr+16 depending on
|
| 116 | * whether there is a BTI.
|
| 117 |
|
| 118 |
|
| 119 | if (!IS_ALIGNED(addr, sizeof(unsigned long))) {
|
| 120 | WARN_RATELIMIT(1, "Misaligned patch-site %pS\n",
|
| 121 | (void *)(addr + 8));
|
| 122 | return 0;
|
| 123 | }
|
| 124 |
|
| 125 |
|
| 126 | addr += 2 * AARCH64_INSN_SIZE;
|
| 127 |
|
| 128 |
|
| 129 | if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)) {
|
| 130 | u32 insn = le32_to_cpu(*(__le32 *)addr);
|
| 131 |
|
| 132 | if (aarch64_insn_is_bti(insn)) {
|
| 133 | addr += AARCH64_INSN_SIZE;
|
| 134 | } else if (insn != aarch64_insn_gen_nop()) {
|
| 135 | WARN_RATELIMIT(1, "unexpected insn in patch-site %pS: 0x%08x\n",
|
| 136 | (void *)addr, insn);
|
| 137 | }
|
| 138 | }
|
| 139 |
|
| 140 |
|
| 141 | addr += AARCH64_INSN_SIZE;
|
| 142 |
|
| 143 | return addr;
|
| 144 | }
|
| 145 |
|
| 146 |
|
| 147 | unsigned long arch_ftrace_get_symaddr(unsigned long fentry_ip)
|
| 148 | {
|
| 149 | u32 insn;
|
| 150 |
|
| 151 | |
| 152 | * When using patchable-function-entry without pre-function NOPS, ftrace
|
| 153 | * entry is the address of the first NOP after the function entry point.
|
| 154 | *
|
| 155 | * The compiler has either generated:
|
| 156 | *
|
| 157 | * func+00: func: NOP // To be patched to MOV X9, LR
|
| 158 | * func+04: NOP // To be patched to BL <caller>
|
| 159 | *
|
| 160 | * Or:
|
| 161 | *
|
| 162 | * func-04: BTI C
|
| 163 | * func+00: func: NOP // To be patched to MOV X9, LR
|
| 164 | * func+04: NOP // To be patched to BL <caller>
|
| 165 | *
|
| 166 | * The fentry_ip is the address of `BL <caller>` which is at `func + 4`
|
| 167 | * bytes in either case.
|
| 168 |
|
| 169 | if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS))
|
| 170 | return fentry_ip - AARCH64_INSN_SIZE;
|
| 171 |
|
| 172 | |
| 173 | * When using patchable-function-entry with pre-function NOPs, BTI is
|
| 174 | * a bit different.
|
| 175 | *
|
| 176 | * func+00: func: NOP // To be patched to MOV X9, LR
|
| 177 | * func+04: NOP // To be patched to BL <caller>
|
| 178 | *
|
| 179 | * Or:
|
| 180 | *
|
| 181 | * func+00: func: BTI C
|
| 182 | * func+04: NOP // To be patched to MOV X9, LR
|
| 183 | * func+08: NOP // To be patched to BL <caller>
|
| 184 | *
|
| 185 | * The fentry_ip is the address of `BL <caller>` which is at either
|
| 186 | * `func + 4` or `func + 8` depends on whether there is a BTI.
|
| 187 |
|
| 188 |
|
| 189 |
|
| 190 | if (!IS_ENABLED(CONFIG_ARM64_BTI_KERNEL))
|
| 191 | return fentry_ip - AARCH64_INSN_SIZE;
|
| 192 |
|
| 193 | |
| 194 | * but otherwise we need to avoid get_kernel_nofault()'s overhead.
|
| 195 |
|
| 196 | if ((fentry_ip & ~PAGE_MASK) < AARCH64_INSN_SIZE * 2) {
|
| 197 | if (get_kernel_nofault(insn, (u32 *)(fentry_ip - AARCH64_INSN_SIZE * 2)))
|
| 198 | return 0;
|
| 199 | } else {
|
| 200 | insn = *(u32 *)(fentry_ip - AARCH64_INSN_SIZE * 2);
|
| 201 | }
|
| 202 |
|
| 203 | if (aarch64_insn_is_bti(le32_to_cpu((__le32)insn)))
|
| 204 | return fentry_ip - AARCH64_INSN_SIZE * 2;
|
| 205 |
|
| 206 | return fentry_ip - AARCH64_INSN_SIZE;
|
| 207 | }
|
| 208 |
|
| 209 | |
| 210 | * Replace a single instruction, which may be a branch or NOP.
|
| 211 | * If @validate == true, a replaced instruction is checked against 'old'.
|
| 212 |
|
| 213 | static int ftrace_modify_code(unsigned long pc, u32 old, u32 new,
|
| 214 | bool validate)
|
| 215 | {
|
| 216 | u32 replaced;
|
| 217 |
|
| 218 | |
| 219 | * Note:
|
| 220 | * We are paranoid about modifying text, as if a bug were to happen, it
|
| 221 | * could cause us to read or write to someplace that could cause harm.
|
| 222 | * Carefully read and modify the code with aarch64_insn_*() which uses
|
| 223 | * probe_kernel_*(), and make sure what we read is what we expected it
|
| 224 | * to be before modifying it.
|
| 225 |
|
| 226 | if (validate) {
|
| 227 | if (aarch64_insn_read((void *)pc, &replaced))
|
| 228 | return -EFAULT;
|
| 229 |
|
| 230 | if (replaced != old)
|
| 231 | return -EINVAL;
|
| 232 | }
|
| 233 | if (aarch64_insn_patch_text_nosync((void *)pc, new))
|
| 234 | return -EPERM;
|
| 235 |
|
| 236 | return 0;
|
| 237 | }
|
| 238 |
|
| 239 | |
| 240 | * Replace tracer function in ftrace_caller()
|
| 241 |
|
| 242 | int ftrace_update_ftrace_func(ftrace_func_t func)
|
| 243 | {
|
| 244 | unsigned long pc;
|
| 245 | u32 new;
|
| 246 |
|
| 247 | |
| 248 | * When using CALL_OPS, the function to call is associated with the
|
| 249 | * call site, and we don't have a global function pointer to update.
|
| 250 |
|
| 251 | if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS))
|
| 252 | return 0;
|
| 253 |
|
| 254 | pc = (unsigned long)ftrace_call;
|
| 255 | new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func,
|
| 256 | AARCH64_INSN_BRANCH_LINK);
|
| 257 |
|
| 258 | return ftrace_modify_code(pc, 0, new, false);
|
| 259 | }
|
| 260 |
|
| 261 | static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)
|
| 262 | {
|
| 263 | #ifdef CONFIG_MODULES
|
| 264 | struct plt_entry *plt = NULL;
|
| 265 |
|
| 266 | if (within_module_mem_type(addr, mod, MOD_INIT_TEXT))
|
| 267 | plt = mod->arch.init_ftrace_trampolines;
|
| 268 | else if (within_module_mem_type(addr, mod, MOD_TEXT))
|
| 269 | plt = mod->arch.ftrace_trampolines;
|
| 270 | else
|
| 271 | return NULL;
|
| 272 |
|
| 273 | return &plt[FTRACE_PLT_IDX];
|
| 274 | #else
|
| 275 | return NULL;
|
| 276 | #endif
|
| 277 | }
|
| 278 |
|
| 279 | static bool reachable_by_bl(unsigned long addr, unsigned long pc)
|
| 280 | {
|
| 281 | long offset = (long)addr - (long)pc;
|
| 282 |
|
| 283 | return offset >= -SZ_128M && offset < SZ_128M;
|
| 284 | }
|
| 285 |
|
| 286 | |
| 287 | * Find the address the callsite must branch to in order to reach '*addr'.
|
| 288 | *
|
| 289 | * Due to the limited range of 'BL' instructions, modules may be placed too far
|
| 290 | * away to branch directly and must use a PLT.
|
| 291 | *
|
| 292 | * Returns true when '*addr' contains a reachable target address, or has been
|
| 293 | * modified to contain a PLT address. Returns false otherwise.
|
| 294 |
|
| 295 | static bool ftrace_find_callable_addr(struct dyn_ftrace *rec,
|
| 296 | struct module *mod,
|
| 297 | unsigned long *addr)
|
| 298 | {
|
| 299 | unsigned long pc = rec->ip;
|
| 300 | struct plt_entry *plt;
|
| 301 |
|
| 302 | |
| 303 | * If a custom trampoline is unreachable, rely on the ftrace_caller
|
| 304 | * trampoline which knows how to indirectly reach that trampoline
|
| 305 | * through ops->direct_call.
|
| 306 |
|
| 307 | if (*addr != FTRACE_ADDR && !reachable_by_bl(*addr, pc))
|
| 308 | *addr = FTRACE_ADDR;
|
| 309 |
|
| 310 | |
| 311 | * When the target is within range of the 'BL' instruction, use 'addr'
|
| 312 | * as-is and branch to that directly.
|
| 313 |
|
| 314 | if (reachable_by_bl(*addr, pc))
|
| 315 | return true;
|
| 316 |
|
| 317 | |
| 318 | * When the target is outside of the range of a 'BL' instruction, we
|
| 319 | * must use a PLT to reach it. We can only place PLTs for modules, and
|
| 320 | * only when module PLT support is built-in.
|
| 321 |
|
| 322 | if (!IS_ENABLED(CONFIG_MODULES))
|
| 323 | return false;
|
| 324 |
|
| 325 | |
| 326 | * 'mod' is only set at module load time, but if we end up
|
| 327 | * dealing with an out-of-range condition, we can assume it
|
| 328 | * is due to a module being loaded far away from the kernel.
|
| 329 | *
|
| 330 | * NOTE: __module_text_address() must be called within a RCU read
|
| 331 | * section, but we can rely on ftrace_lock to ensure that 'mod'
|
| 332 | * retains its validity throughout the remainder of this code.
|
| 333 |
|
| 334 | if (!mod) {
|
| 335 | guard(rcu)();
|
| 336 | mod = __module_text_address(pc);
|
| 337 | }
|
| 338 |
|
| 339 | if (WARN_ON(!mod))
|
| 340 | return false;
|
| 341 |
|
| 342 | plt = get_ftrace_plt(mod, pc);
|
| 343 | if (!plt) {
|
| 344 | pr_err("ftrace: no module PLT for %ps\n", (void *)*addr);
|
| 345 | return false;
|
| 346 | }
|
| 347 |
|
| 348 | *addr = (unsigned long)plt;
|
| 349 | return true;
|
| 350 | }
|
| 351 |
|
| 352 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS
|
| 353 | static const struct ftrace_ops *arm64_rec_get_ops(struct dyn_ftrace *rec)
|
| 354 | {
|
| 355 | const struct ftrace_ops *ops = NULL;
|
| 356 |
|
| 357 | if (rec->flags & FTRACE_FL_CALL_OPS_EN) {
|
| 358 | ops = ftrace_find_unique_ops(rec);
|
| 359 | WARN_ON_ONCE(!ops);
|
| 360 | }
|
| 361 |
|
| 362 | if (!ops)
|
| 363 | ops = &ftrace_list_ops;
|
| 364 |
|
| 365 | return ops;
|
| 366 | }
|
| 367 |
|
| 368 | static int ftrace_rec_set_ops(const struct dyn_ftrace *rec,
|
| 369 | const struct ftrace_ops *ops)
|
| 370 | {
|
| 371 | unsigned long literal = ALIGN_DOWN(rec->ip - 12, 8);
|
| 372 | return aarch64_insn_write_literal_u64((void *)literal,
|
| 373 | (unsigned long)ops);
|
| 374 | }
|
| 375 |
|
| 376 | static int ftrace_rec_set_nop_ops(struct dyn_ftrace *rec)
|
| 377 | {
|
| 378 | return ftrace_rec_set_ops(rec, &ftrace_nop_ops);
|
| 379 | }
|
| 380 |
|
| 381 | static int ftrace_rec_update_ops(struct dyn_ftrace *rec)
|
| 382 | {
|
| 383 | return ftrace_rec_set_ops(rec, arm64_rec_get_ops(rec));
|
| 384 | }
|
| 385 | #else
|
| 386 | static int ftrace_rec_set_nop_ops(struct dyn_ftrace *rec) { return 0; }
|
| 387 | static int ftrace_rec_update_ops(struct dyn_ftrace *rec) { return 0; }
|
| 388 | #endif
|
| 389 |
|
| 390 | |
| 391 | * Turn on the call to ftrace_caller() in instrumented function
|
| 392 |
|
| 393 | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
| 394 | {
|
| 395 | unsigned long pc = rec->ip;
|
| 396 | u32 old, new;
|
| 397 | int ret;
|
| 398 |
|
| 399 | ret = ftrace_rec_update_ops(rec);
|
| 400 | if (ret)
|
| 401 | return ret;
|
| 402 |
|
| 403 | if (!ftrace_find_callable_addr(rec, NULL, &addr))
|
| 404 | return -EINVAL;
|
| 405 |
|
| 406 | old = aarch64_insn_gen_nop();
|
| 407 | new = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK);
|
| 408 |
|
| 409 | return ftrace_modify_code(pc, old, new, true);
|
| 410 | }
|
| 411 |
|
| 412 | #if defined(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS) || \
|
| 413 | defined(CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS)
|
| 414 | int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
|
| 415 | unsigned long addr)
|
| 416 | {
|
| 417 | unsigned long pc = rec->ip;
|
| 418 | u32 old, new;
|
| 419 | int ret;
|
| 420 |
|
| 421 | ret = ftrace_rec_update_ops(rec);
|
| 422 | if (ret)
|
| 423 | return ret;
|
| 424 |
|
| 425 | if (!ftrace_find_callable_addr(rec, NULL, &old_addr))
|
| 426 | return -EINVAL;
|
| 427 | if (!ftrace_find_callable_addr(rec, NULL, &addr))
|
| 428 | return -EINVAL;
|
| 429 |
|
| 430 | old = aarch64_insn_gen_branch_imm(pc, old_addr,
|
| 431 | AARCH64_INSN_BRANCH_LINK);
|
| 432 | new = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK);
|
| 433 |
|
| 434 | return ftrace_modify_code(pc, old, new, true);
|
| 435 | }
|
| 436 | #endif
|
| 437 |
|
| 438 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
|
| 439 | |
| 440 | * The compiler has inserted two NOPs before the regular function prologue.
|
| 441 | * All instrumented functions follow the AAPCS, so x0-x8 and x19-x30 are live,
|
| 442 | * and x9-x18 are free for our use.
|
| 443 | *
|
| 444 | * At runtime we want to be able to swing a single NOP <-> BL to enable or
|
| 445 | * disable the ftrace call. The BL requires us to save the original LR value,
|
| 446 | * so here we insert a <MOV X9, LR> over the first NOP so the instructions
|
| 447 | * before the regular prologue are:
|
| 448 | *
|
| 449 | * | Compiled | Disabled | Enabled |
|
| 450 | * +----------+------------+------------+
|
| 451 | * | NOP | MOV X9, LR | MOV X9, LR |
|
| 452 | * | NOP | NOP | BL <entry> |
|
| 453 | *
|
| 454 | * The LR value will be recovered by ftrace_caller, and restored into LR
|
| 455 | * before returning to the regular function prologue. When a function is not
|
| 456 | * being traced, the MOV is not harmful given x9 is not live per the AAPCS.
|
| 457 | *
|
| 458 | * Note: ftrace_process_locs() has pre-adjusted rec->ip to be the address of
|
| 459 | * the BL.
|
| 460 |
|
| 461 | int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
|
| 462 | {
|
| 463 | unsigned long pc = rec->ip - AARCH64_INSN_SIZE;
|
| 464 | u32 old, new;
|
| 465 | int ret;
|
| 466 |
|
| 467 | ret = ftrace_rec_set_nop_ops(rec);
|
| 468 | if (ret)
|
| 469 | return ret;
|
| 470 |
|
| 471 | old = aarch64_insn_gen_nop();
|
| 472 | new = aarch64_insn_gen_move_reg(AARCH64_INSN_REG_9,
|
| 473 | AARCH64_INSN_REG_LR,
|
| 474 | AARCH64_INSN_VARIANT_64BIT);
|
| 475 | return ftrace_modify_code(pc, old, new, true);
|
| 476 | }
|
| 477 | #endif
|
| 478 |
|
| 479 | |
| 480 | * Turn off the call to ftrace_caller() in instrumented function
|
| 481 |
|
| 482 | int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
|
| 483 | unsigned long addr)
|
| 484 | {
|
| 485 | unsigned long pc = rec->ip;
|
| 486 | u32 old = 0, new;
|
| 487 | int ret;
|
| 488 |
|
| 489 | new = aarch64_insn_gen_nop();
|
| 490 |
|
| 491 | ret = ftrace_rec_set_nop_ops(rec);
|
| 492 | if (ret)
|
| 493 | return ret;
|
| 494 |
|
| 495 | |
| 496 | * When using mcount, callsites in modules may have been initialized to
|
| 497 | * call an arbitrary module PLT (which redirects to the _mcount stub)
|
| 498 | * rather than the ftrace PLT we'll use at runtime (which redirects to
|
| 499 | * the ftrace trampoline). We can ignore the old PLT when initializing
|
| 500 | * the callsite.
|
| 501 | *
|
| 502 | * Note: 'mod' is only set at module load time.
|
| 503 |
|
| 504 | if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_ARGS) && mod)
|
| 505 | return aarch64_insn_patch_text_nosync((void *)pc, new);
|
| 506 |
|
| 507 | if (!ftrace_find_callable_addr(rec, mod, &addr))
|
| 508 | return -EINVAL;
|
| 509 |
|
| 510 | old = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK);
|
| 511 |
|
| 512 | return ftrace_modify_code(pc, old, new, true);
|
| 513 | }
|
| 514 |
|
| 515 | void arch_ftrace_update_code(int command)
|
| 516 | {
|
| 517 | command |= FTRACE_MAY_SLEEP;
|
| 518 | ftrace_modify_all_code(command);
|
| 519 | }
|
| 520 |
|
| 521 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
| 522 | |
| 523 | * function_graph tracer expects ftrace_return_to_handler() to be called
|
| 524 | * on the way back to parent. For this purpose, this function is called
|
| 525 | * in _mcount() or ftrace_caller() to replace return address (*parent) on
|
| 526 | * the call stack to return_to_handler.
|
| 527 |
|
| 528 | void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
|
| 529 | unsigned long frame_pointer)
|
| 530 | {
|
| 531 | unsigned long return_hooker = (unsigned long)&return_to_handler;
|
| 532 | unsigned long old;
|
| 533 |
|
| 534 | if (unlikely(atomic_read(¤t->tracing_graph_pause)))
|
| 535 | return;
|
| 536 |
|
| 537 | |
| 538 | * Note:
|
| 539 | * No protection against faulting at *parent, which may be seen
|
| 540 | * on other archs. It's unlikely on AArch64.
|
| 541 |
|
| 542 | old = *parent;
|
| 543 |
|
| 544 | if (!function_graph_enter(old, self_addr, frame_pointer,
|
| 545 | (void *)frame_pointer)) {
|
| 546 | *parent = return_hooker;
|
| 547 | }
|
| 548 | }
|
| 549 |
|
| 550 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
|
| 551 | void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
|
| 552 | struct ftrace_ops *op, struct ftrace_regs *fregs)
|
| 553 | {
|
| 554 | unsigned long return_hooker = (unsigned long)&return_to_handler;
|
| 555 | unsigned long frame_pointer = arch_ftrace_regs(fregs)->fp;
|
| 556 | unsigned long *parent = &arch_ftrace_regs(fregs)->lr;
|
| 557 | unsigned long old;
|
| 558 |
|
| 559 | if (unlikely(atomic_read(¤t->tracing_graph_pause)))
|
| 560 | return;
|
| 561 |
|
| 562 | old = *parent;
|
| 563 |
|
| 564 | if (!function_graph_enter_regs(old, ip, frame_pointer,
|
| 565 | (void *)frame_pointer, fregs)) {
|
| 566 | *parent = return_hooker;
|
| 567 | }
|
| 568 | }
|
| 569 | #else
|
| 570 | |
| 571 | * Turn on/off the call to ftrace_graph_caller() in ftrace_caller()
|
| 572 | * depending on @enable.
|
| 573 |
|
| 574 | static int ftrace_modify_graph_caller(bool enable)
|
| 575 | {
|
| 576 | unsigned long pc = (unsigned long)&ftrace_graph_call;
|
| 577 | u32 branch, nop;
|
| 578 |
|
| 579 | branch = aarch64_insn_gen_branch_imm(pc,
|
| 580 | (unsigned long)ftrace_graph_caller,
|
| 581 | AARCH64_INSN_BRANCH_NOLINK);
|
| 582 | nop = aarch64_insn_gen_nop();
|
| 583 |
|
| 584 | if (enable)
|
| 585 | return ftrace_modify_code(pc, nop, branch, true);
|
| 586 | else
|
| 587 | return ftrace_modify_code(pc, branch, nop, true);
|
| 588 | }
|
| 589 |
|
| 590 | int ftrace_enable_ftrace_graph_caller(void)
|
| 591 | {
|
| 592 | return ftrace_modify_graph_caller(true);
|
| 593 | }
|
| 594 |
|
| 595 | int ftrace_disable_ftrace_graph_caller(void)
|
| 596 | {
|
| 597 | return ftrace_modify_graph_caller(false);
|
| 598 | }
|
| 599 | #endif
|
| 600 | #endif
|