|
10 | 10 | */ |
11 | 11 |
|
12 | 12 | #include <linux/ftrace.h> |
| 13 | +#include <linux/module.h> |
13 | 14 | #include <linux/swab.h> |
14 | 15 | #include <linux/uaccess.h> |
15 | 16 |
|
16 | 17 | #include <asm/cacheflush.h> |
| 18 | +#include <asm/debug-monitors.h> |
17 | 19 | #include <asm/ftrace.h> |
18 | 20 | #include <asm/insn.h> |
19 | 21 |
|
@@ -69,8 +71,57 @@ int ftrace_update_ftrace_func(ftrace_func_t func) |
69 | 71 | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) |
70 | 72 | { |
71 | 73 | unsigned long pc = rec->ip; |
| 74 | + long offset = (long)pc - (long)addr; |
72 | 75 | u32 old, new; |
73 | 76 |
|
| 77 | + if (IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) && |
| 78 | + (offset < -SZ_128M || offset >= SZ_128M)) { |
| 79 | + unsigned long *trampoline; |
| 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 | + trampoline = (unsigned long *)mod->arch.ftrace_trampoline; |
| 108 | + if (trampoline[0] != addr) { |
| 109 | + if (trampoline[0] != 0) { |
| 110 | + pr_err("ftrace: far branches to multiple entry points unsupported inside a single module\n"); |
| 111 | + return -EINVAL; |
| 112 | + } |
| 113 | + |
| 114 | + /* point the trampoline to our ftrace entry point */ |
| 115 | + module_disable_ro(mod); |
| 116 | + trampoline[0] = addr; |
| 117 | + module_enable_ro(mod, true); |
| 118 | + |
| 119 | + /* update trampoline before patching in the branch */ |
| 120 | + smp_wmb(); |
| 121 | + } |
| 122 | + addr = (unsigned long)&trampoline[1]; |
| 123 | + } |
| 124 | + |
74 | 125 | old = aarch64_insn_gen_nop(); |
75 | 126 | new = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK); |
76 | 127 |
|
|
0 commit comments