Commit | Line | Data |
---|---|---|
014c257c AS |
1 | /* |
2 | * Dynamic function tracing support. | |
3 | * | |
4 | * Copyright (C) 2008 Abhishek Sagar <sagar.abhishek@gmail.com> | |
5 | * | |
6 | * For licencing details, see COPYING. | |
7 | * | |
8 | * Defines low-level handling of mcount calls when the kernel | |
9 | * is compiled with the -pg flag. When using dynamic ftrace, the | |
10 | * mcount call-sites get patched lazily with NOP till they are | |
11 | * enabled. All code mutation routines here take effect atomically. | |
12 | */ | |
13 | ||
14 | #include <linux/ftrace.h> | |
395a59d0 | 15 | |
014c257c | 16 | #include <asm/cacheflush.h> |
395a59d0 | 17 | #include <asm/ftrace.h> |
014c257c | 18 | |
014c257c AS |
19 | #define PC_OFFSET 8 |
20 | #define BL_OPCODE 0xeb000000 | |
21 | #define BL_OFFSET_MASK 0x00ffffff | |
22 | ||
23 | static unsigned long bl_insn; | |
24 | static const unsigned long NOP = 0xe1a00000; /* mov r0, r0 */ | |
25 | ||
014c257c AS |
26 | unsigned char *ftrace_nop_replace(void) |
27 | { | |
28 | return (char *)&NOP; | |
29 | } | |
30 | ||
31 | /* construct a branch (BL) instruction to addr */ | |
32 | unsigned char *ftrace_call_replace(unsigned long pc, unsigned long addr) | |
33 | { | |
34 | long offset; | |
35 | ||
395a59d0 | 36 | offset = (long)addr - (long)(pc + PC_OFFSET); |
014c257c AS |
37 | if (unlikely(offset < -33554432 || offset > 33554428)) { |
38 | /* Can't generate branches that far (from ARM ARM). Ftrace | |
395a59d0 | 39 | * doesn't generate branches outside of kernel text. |
014c257c AS |
40 | */ |
41 | WARN_ON_ONCE(1); | |
42 | return NULL; | |
43 | } | |
44 | offset = (offset >> 2) & BL_OFFSET_MASK; | |
45 | bl_insn = BL_OPCODE | offset; | |
46 | return (unsigned char *)&bl_insn; | |
47 | } | |
48 | ||
49 | int ftrace_modify_code(unsigned long pc, unsigned char *old_code, | |
50 | unsigned char *new_code) | |
51 | { | |
52 | unsigned long err = 0, replaced = 0, old, new; | |
53 | ||
54 | old = *(unsigned long *)old_code; | |
55 | new = *(unsigned long *)new_code; | |
014c257c AS |
56 | |
57 | __asm__ __volatile__ ( | |
58 | "1: ldr %1, [%2] \n" | |
59 | " cmp %1, %4 \n" | |
60 | "2: streq %3, [%2] \n" | |
61 | " cmpne %1, %3 \n" | |
62 | " movne %0, #2 \n" | |
63 | "3:\n" | |
64 | ||
65 | ".section .fixup, \"ax\"\n" | |
66 | "4: mov %0, #1 \n" | |
67 | " b 3b \n" | |
68 | ".previous\n" | |
69 | ||
70 | ".section __ex_table, \"a\"\n" | |
71 | " .long 1b, 4b \n" | |
72 | " .long 2b, 4b \n" | |
73 | ".previous\n" | |
74 | ||
75 | : "=r"(err), "=r"(replaced) | |
76 | : "r"(pc), "r"(new), "r"(old), "0"(err), "1"(replaced) | |
77 | : "memory"); | |
78 | ||
79 | if (!err && (replaced == old)) | |
395a59d0 | 80 | flush_icache_range(pc, pc + MCOUNT_INSN_SIZE); |
014c257c AS |
81 | |
82 | return err; | |
83 | } | |
84 | ||
85 | int ftrace_update_ftrace_func(ftrace_func_t func) | |
86 | { | |
87 | int ret; | |
88 | unsigned long pc, old; | |
89 | unsigned char *new; | |
90 | ||
91 | pc = (unsigned long)&ftrace_call; | |
395a59d0 | 92 | memcpy(&old, &ftrace_call, MCOUNT_INSN_SIZE); |
014c257c AS |
93 | new = ftrace_call_replace(pc, (unsigned long)func); |
94 | ret = ftrace_modify_code(pc, (unsigned char *)&old, new); | |
95 | return ret; | |
96 | } | |
97 | ||
6ed70a79 | 98 | /* run from ftrace_init with irqs disabled */ |
014c257c AS |
99 | int __init ftrace_dyn_arch_init(void *data) |
100 | { | |
101 | ftrace_mcount_set(data); | |
102 | return 0; | |
103 | } |