.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * arch/arm64/kernel/ftrace.c |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2013 Linaro Limited |
---|
5 | 6 | * Author: AKASHI Takahiro <takahiro.akashi@linaro.org> |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or modify |
---|
8 | | - * it under the terms of the GNU General Public License version 2 as |
---|
9 | | - * published by the Free Software Foundation. |
---|
10 | 7 | */ |
---|
11 | 8 | |
---|
12 | 9 | #include <linux/ftrace.h> |
---|
.. | .. |
---|
65 | 62 | return ftrace_modify_code(pc, 0, new, false); |
---|
66 | 63 | } |
---|
67 | 64 | |
---|
| 65 | +static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr) |
---|
| 66 | +{ |
---|
| 67 | +#ifdef CONFIG_ARM64_MODULE_PLTS |
---|
| 68 | + struct plt_entry *plt = mod->arch.ftrace_trampolines; |
---|
| 69 | + |
---|
| 70 | + if (addr == FTRACE_ADDR) |
---|
| 71 | + return &plt[FTRACE_PLT_IDX]; |
---|
| 72 | + if (addr == FTRACE_REGS_ADDR && |
---|
| 73 | + IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS)) |
---|
| 74 | + return &plt[FTRACE_REGS_PLT_IDX]; |
---|
| 75 | +#endif |
---|
| 76 | + return NULL; |
---|
| 77 | +} |
---|
| 78 | + |
---|
| 79 | +/* |
---|
| 80 | + * Find the address the callsite must branch to in order to reach '*addr'. |
---|
| 81 | + * |
---|
| 82 | + * Due to the limited range of 'BL' instructions, modules may be placed too far |
---|
| 83 | + * away to branch directly and must use a PLT. |
---|
| 84 | + * |
---|
| 85 | + * Returns true when '*addr' contains a reachable target address, or has been |
---|
| 86 | + * modified to contain a PLT address. Returns false otherwise. |
---|
| 87 | + */ |
---|
| 88 | +static bool ftrace_find_callable_addr(struct dyn_ftrace *rec, |
---|
| 89 | + struct module *mod, |
---|
| 90 | + unsigned long *addr) |
---|
| 91 | +{ |
---|
| 92 | + unsigned long pc = rec->ip; |
---|
| 93 | + long offset = (long)*addr - (long)pc; |
---|
| 94 | + struct plt_entry *plt; |
---|
| 95 | + |
---|
| 96 | + /* |
---|
| 97 | + * When the target is within range of the 'BL' instruction, use 'addr' |
---|
| 98 | + * as-is and branch to that directly. |
---|
| 99 | + */ |
---|
| 100 | + if (offset >= -SZ_128M && offset < SZ_128M) |
---|
| 101 | + return true; |
---|
| 102 | + |
---|
| 103 | + /* |
---|
| 104 | + * When the target is outside of the range of a 'BL' instruction, we |
---|
| 105 | + * must use a PLT to reach it. We can only place PLTs for modules, and |
---|
| 106 | + * only when module PLT support is built-in. |
---|
| 107 | + */ |
---|
| 108 | + if (!IS_ENABLED(CONFIG_ARM64_MODULE_PLTS)) |
---|
| 109 | + return false; |
---|
| 110 | + |
---|
| 111 | + /* |
---|
| 112 | + * 'mod' is only set at module load time, but if we end up |
---|
| 113 | + * dealing with an out-of-range condition, we can assume it |
---|
| 114 | + * is due to a module being loaded far away from the kernel. |
---|
| 115 | + * |
---|
| 116 | + * NOTE: __module_text_address() must be called with preemption |
---|
| 117 | + * disabled, but we can rely on ftrace_lock to ensure that 'mod' |
---|
| 118 | + * retains its validity throughout the remainder of this code. |
---|
| 119 | + */ |
---|
| 120 | + if (!mod) { |
---|
| 121 | + preempt_disable(); |
---|
| 122 | + mod = __module_text_address(pc); |
---|
| 123 | + preempt_enable(); |
---|
| 124 | + } |
---|
| 125 | + |
---|
| 126 | + if (WARN_ON(!mod)) |
---|
| 127 | + return false; |
---|
| 128 | + |
---|
| 129 | + plt = get_ftrace_plt(mod, *addr); |
---|
| 130 | + if (!plt) { |
---|
| 131 | + pr_err("ftrace: no module PLT for %ps\n", (void *)*addr); |
---|
| 132 | + return false; |
---|
| 133 | + } |
---|
| 134 | + |
---|
| 135 | + *addr = (unsigned long)plt; |
---|
| 136 | + return true; |
---|
| 137 | +} |
---|
| 138 | + |
---|
68 | 139 | /* |
---|
69 | 140 | * Turn on the call to ftrace_caller() in instrumented function |
---|
70 | 141 | */ |
---|
.. | .. |
---|
72 | 143 | { |
---|
73 | 144 | unsigned long pc = rec->ip; |
---|
74 | 145 | u32 old, new; |
---|
75 | | - long offset = (long)pc - (long)addr; |
---|
76 | 146 | |
---|
77 | | - if (offset < -SZ_128M || offset >= SZ_128M) { |
---|
78 | | -#ifdef CONFIG_ARM64_MODULE_PLTS |
---|
79 | | - struct plt_entry trampoline, *dst; |
---|
80 | | - struct module *mod; |
---|
81 | | - |
---|
82 | | - /* |
---|
83 | | - * On kernels that support module PLTs, the offset between the |
---|
84 | | - * branch instruction and its target may legally exceed the |
---|
85 | | - * range of an ordinary relative 'bl' opcode. In this case, we |
---|
86 | | - * need to branch via a trampoline in the module. |
---|
87 | | - * |
---|
88 | | - * NOTE: __module_text_address() must be called with preemption |
---|
89 | | - * disabled, but we can rely on ftrace_lock to ensure that 'mod' |
---|
90 | | - * retains its validity throughout the remainder of this code. |
---|
91 | | - */ |
---|
92 | | - preempt_disable(); |
---|
93 | | - mod = __module_text_address(pc); |
---|
94 | | - preempt_enable(); |
---|
95 | | - |
---|
96 | | - if (WARN_ON(!mod)) |
---|
97 | | - return -EINVAL; |
---|
98 | | - |
---|
99 | | - /* |
---|
100 | | - * There is only one ftrace trampoline per module. For now, |
---|
101 | | - * this is not a problem since on arm64, all dynamic ftrace |
---|
102 | | - * invocations are routed via ftrace_caller(). This will need |
---|
103 | | - * to be revisited if support for multiple ftrace entry points |
---|
104 | | - * is added in the future, but for now, the pr_err() below |
---|
105 | | - * deals with a theoretical issue only. |
---|
106 | | - */ |
---|
107 | | - dst = mod->arch.ftrace_trampoline; |
---|
108 | | - trampoline = get_plt_entry(addr); |
---|
109 | | - if (!plt_entries_equal(dst, &trampoline)) { |
---|
110 | | - if (!plt_entries_equal(dst, &(struct plt_entry){})) { |
---|
111 | | - pr_err("ftrace: far branches to multiple entry points unsupported inside a single module\n"); |
---|
112 | | - return -EINVAL; |
---|
113 | | - } |
---|
114 | | - |
---|
115 | | - /* point the trampoline to our ftrace entry point */ |
---|
116 | | - module_disable_ro(mod); |
---|
117 | | - *dst = trampoline; |
---|
118 | | - module_enable_ro(mod, true); |
---|
119 | | - |
---|
120 | | - /* |
---|
121 | | - * Ensure updated trampoline is visible to instruction |
---|
122 | | - * fetch before we patch in the branch. Although the |
---|
123 | | - * architecture doesn't require an IPI in this case, |
---|
124 | | - * Neoverse-N1 erratum #1542419 does require one |
---|
125 | | - * if the TLB maintenance in module_enable_ro() is |
---|
126 | | - * skipped due to rodata_enabled. It doesn't seem worth |
---|
127 | | - * it to make it conditional given that this is |
---|
128 | | - * certainly not a fast-path. |
---|
129 | | - */ |
---|
130 | | - flush_icache_range((unsigned long)&dst[0], |
---|
131 | | - (unsigned long)&dst[1]); |
---|
132 | | - } |
---|
133 | | - addr = (unsigned long)dst; |
---|
134 | | -#else /* CONFIG_ARM64_MODULE_PLTS */ |
---|
| 147 | + if (!ftrace_find_callable_addr(rec, NULL, &addr)) |
---|
135 | 148 | return -EINVAL; |
---|
136 | | -#endif /* CONFIG_ARM64_MODULE_PLTS */ |
---|
137 | | - } |
---|
138 | 149 | |
---|
139 | 150 | old = aarch64_insn_gen_nop(); |
---|
140 | 151 | new = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK); |
---|
141 | 152 | |
---|
142 | 153 | return ftrace_modify_code(pc, old, new, true); |
---|
143 | 154 | } |
---|
| 155 | + |
---|
| 156 | +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS |
---|
| 157 | +int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, |
---|
| 158 | + unsigned long addr) |
---|
| 159 | +{ |
---|
| 160 | + unsigned long pc = rec->ip; |
---|
| 161 | + u32 old, new; |
---|
| 162 | + |
---|
| 163 | + if (!ftrace_find_callable_addr(rec, NULL, &old_addr)) |
---|
| 164 | + return -EINVAL; |
---|
| 165 | + if (!ftrace_find_callable_addr(rec, NULL, &addr)) |
---|
| 166 | + return -EINVAL; |
---|
| 167 | + |
---|
| 168 | + old = aarch64_insn_gen_branch_imm(pc, old_addr, |
---|
| 169 | + AARCH64_INSN_BRANCH_LINK); |
---|
| 170 | + new = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK); |
---|
| 171 | + |
---|
| 172 | + return ftrace_modify_code(pc, old, new, true); |
---|
| 173 | +} |
---|
| 174 | + |
---|
| 175 | +/* |
---|
| 176 | + * The compiler has inserted two NOPs before the regular function prologue. |
---|
| 177 | + * All instrumented functions follow the AAPCS, so x0-x8 and x19-x30 are live, |
---|
| 178 | + * and x9-x18 are free for our use. |
---|
| 179 | + * |
---|
| 180 | + * At runtime we want to be able to swing a single NOP <-> BL to enable or |
---|
| 181 | + * disable the ftrace call. The BL requires us to save the original LR value, |
---|
| 182 | + * so here we insert a <MOV X9, LR> over the first NOP so the instructions |
---|
| 183 | + * before the regular prologue are: |
---|
| 184 | + * |
---|
| 185 | + * | Compiled | Disabled | Enabled | |
---|
| 186 | + * +----------+------------+------------+ |
---|
| 187 | + * | NOP | MOV X9, LR | MOV X9, LR | |
---|
| 188 | + * | NOP | NOP | BL <entry> | |
---|
| 189 | + * |
---|
| 190 | + * The LR value will be recovered by ftrace_regs_entry, and restored into LR |
---|
| 191 | + * before returning to the regular function prologue. When a function is not |
---|
| 192 | + * being traced, the MOV is not harmful given x9 is not live per the AAPCS. |
---|
| 193 | + * |
---|
| 194 | + * Note: ftrace_process_locs() has pre-adjusted rec->ip to be the address of |
---|
| 195 | + * the BL. |
---|
| 196 | + */ |
---|
| 197 | +int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec) |
---|
| 198 | +{ |
---|
| 199 | + unsigned long pc = rec->ip - AARCH64_INSN_SIZE; |
---|
| 200 | + u32 old, new; |
---|
| 201 | + |
---|
| 202 | + old = aarch64_insn_gen_nop(); |
---|
| 203 | + new = aarch64_insn_gen_move_reg(AARCH64_INSN_REG_9, |
---|
| 204 | + AARCH64_INSN_REG_LR, |
---|
| 205 | + AARCH64_INSN_VARIANT_64BIT); |
---|
| 206 | + return ftrace_modify_code(pc, old, new, true); |
---|
| 207 | +} |
---|
| 208 | +#endif |
---|
144 | 209 | |
---|
145 | 210 | /* |
---|
146 | 211 | * Turn off the call to ftrace_caller() in instrumented function |
---|
.. | .. |
---|
149 | 214 | unsigned long addr) |
---|
150 | 215 | { |
---|
151 | 216 | unsigned long pc = rec->ip; |
---|
152 | | - bool validate = true; |
---|
153 | 217 | u32 old = 0, new; |
---|
154 | | - long offset = (long)pc - (long)addr; |
---|
155 | | - |
---|
156 | | - if (offset < -SZ_128M || offset >= SZ_128M) { |
---|
157 | | -#ifdef CONFIG_ARM64_MODULE_PLTS |
---|
158 | | - u32 replaced; |
---|
159 | | - |
---|
160 | | - /* |
---|
161 | | - * 'mod' is only set at module load time, but if we end up |
---|
162 | | - * dealing with an out-of-range condition, we can assume it |
---|
163 | | - * is due to a module being loaded far away from the kernel. |
---|
164 | | - */ |
---|
165 | | - if (!mod) { |
---|
166 | | - preempt_disable(); |
---|
167 | | - mod = __module_text_address(pc); |
---|
168 | | - preempt_enable(); |
---|
169 | | - |
---|
170 | | - if (WARN_ON(!mod)) |
---|
171 | | - return -EINVAL; |
---|
172 | | - } |
---|
173 | | - |
---|
174 | | - /* |
---|
175 | | - * The instruction we are about to patch may be a branch and |
---|
176 | | - * link instruction that was redirected via a PLT entry. In |
---|
177 | | - * this case, the normal validation will fail, but we can at |
---|
178 | | - * least check that we are dealing with a branch and link |
---|
179 | | - * instruction that points into the right module. |
---|
180 | | - */ |
---|
181 | | - if (aarch64_insn_read((void *)pc, &replaced)) |
---|
182 | | - return -EFAULT; |
---|
183 | | - |
---|
184 | | - if (!aarch64_insn_is_bl(replaced) || |
---|
185 | | - !within_module(pc + aarch64_get_branch_offset(replaced), |
---|
186 | | - mod)) |
---|
187 | | - return -EINVAL; |
---|
188 | | - |
---|
189 | | - validate = false; |
---|
190 | | -#else /* CONFIG_ARM64_MODULE_PLTS */ |
---|
191 | | - return -EINVAL; |
---|
192 | | -#endif /* CONFIG_ARM64_MODULE_PLTS */ |
---|
193 | | - } else { |
---|
194 | | - old = aarch64_insn_gen_branch_imm(pc, addr, |
---|
195 | | - AARCH64_INSN_BRANCH_LINK); |
---|
196 | | - } |
---|
197 | 218 | |
---|
198 | 219 | new = aarch64_insn_gen_nop(); |
---|
199 | 220 | |
---|
200 | | - return ftrace_modify_code(pc, old, new, validate); |
---|
| 221 | + /* |
---|
| 222 | + * When using mcount, callsites in modules may have been initalized to |
---|
| 223 | + * call an arbitrary module PLT (which redirects to the _mcount stub) |
---|
| 224 | + * rather than the ftrace PLT we'll use at runtime (which redirects to |
---|
| 225 | + * the ftrace trampoline). We can ignore the old PLT when initializing |
---|
| 226 | + * the callsite. |
---|
| 227 | + * |
---|
| 228 | + * Note: 'mod' is only set at module load time. |
---|
| 229 | + */ |
---|
| 230 | + if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS) && |
---|
| 231 | + IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) && mod) { |
---|
| 232 | + return aarch64_insn_patch_text_nosync((void *)pc, new); |
---|
| 233 | + } |
---|
| 234 | + |
---|
| 235 | + if (!ftrace_find_callable_addr(rec, mod, &addr)) |
---|
| 236 | + return -EINVAL; |
---|
| 237 | + |
---|
| 238 | + old = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK); |
---|
| 239 | + |
---|
| 240 | + return ftrace_modify_code(pc, old, new, true); |
---|
201 | 241 | } |
---|
202 | 242 | |
---|
203 | 243 | void arch_ftrace_update_code(int command) |
---|
204 | 244 | { |
---|
| 245 | + command |= FTRACE_MAY_SLEEP; |
---|
205 | 246 | ftrace_modify_all_code(command); |
---|
206 | 247 | } |
---|
207 | 248 | |
---|
.. | .. |
---|
220 | 261 | * |
---|
221 | 262 | * Note that @frame_pointer is used only for sanity check later. |
---|
222 | 263 | */ |
---|
223 | | -void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, |
---|
| 264 | +void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, |
---|
224 | 265 | unsigned long frame_pointer) |
---|
225 | 266 | { |
---|
226 | 267 | unsigned long return_hooker = (unsigned long)&return_to_handler; |
---|