Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | // SPDX-License-Identifier: GPL-2.0 #include <linux/spinlock.h> #include <linux/hardirq.h> #include <linux/ftrace.h> #include <linux/percpu.h> #include <linux/init.h> #include <linux/list.h> #include <trace/syscall.h> #include <asm/ftrace.h> #ifdef CONFIG_DYNAMIC_FTRACE static const u32 ftrace_nop = 0x01000000; static u32 ftrace_call_replace(unsigned long ip, unsigned long addr) { u32 call; s32 off; off = ((s32)addr - (s32)ip); call = 0x40000000 | ((u32)off >> 2); return call; } static int ftrace_modify_code(unsigned long ip, u32 old, u32 new) { u32 replaced; int faulted; __asm__ __volatile__( "1: cas [%[ip]], %[old], %[new]\n" " flush %[ip]\n" " mov 0, %[faulted]\n" "2:\n" " .section .fixup,#alloc,#execinstr\n" " .align 4\n" "3: sethi %%hi(2b), %[faulted]\n" " jmpl %[faulted] + %%lo(2b), %%g0\n" " mov 1, %[faulted]\n" " .previous\n" " .section __ex_table,\"a\"\n" " .align 4\n" " .word 1b, 3b\n" " .previous\n" : "=r" (replaced), [faulted] "=r" (faulted) : [new] "0" (new), [old] "r" (old), [ip] "r" (ip) : "memory"); if (replaced != old && replaced != new) faulted = 2; return faulted; } int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long addr) { unsigned long ip = rec->ip; u32 old, new; old = ftrace_call_replace(ip, addr); new = ftrace_nop; return ftrace_modify_code(ip, old, new); } int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) { unsigned long ip = rec->ip; u32 old, new; old = ftrace_nop; new = ftrace_call_replace(ip, addr); return ftrace_modify_code(ip, old, new); } int ftrace_update_ftrace_func(ftrace_func_t func) { unsigned long ip = (unsigned long)(&ftrace_call); u32 old, new; old = *(u32 *) &ftrace_call; new = ftrace_call_replace(ip, (unsigned long)func); return ftrace_modify_code(ip, old, new); } #endif #ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_DYNAMIC_FTRACE extern void ftrace_graph_call(void); int ftrace_enable_ftrace_graph_caller(void) { unsigned long ip = (unsigned long)(&ftrace_graph_call); u32 old, new; old = *(u32 *) &ftrace_graph_call; new = ftrace_call_replace(ip, (unsigned long) &ftrace_graph_caller); return ftrace_modify_code(ip, old, new); } int ftrace_disable_ftrace_graph_caller(void) { unsigned long ip = (unsigned long)(&ftrace_graph_call); u32 old, new; old = *(u32 *) &ftrace_graph_call; new = ftrace_call_replace(ip, (unsigned long) &ftrace_stub); return ftrace_modify_code(ip, old, new); } #endif /* !CONFIG_DYNAMIC_FTRACE */ /* * Hook the return address and push it in the stack of return addrs * in current thread info. */ unsigned long prepare_ftrace_return(unsigned long parent, unsigned long self_addr, unsigned long frame_pointer) { unsigned long return_hooker = (unsigned long) &return_to_handler; if (unlikely(atomic_read(¤t->tracing_graph_pause))) return parent + 8UL; if (function_graph_enter(parent, self_addr, frame_pointer, NULL)) return parent + 8UL; return return_hooker; } #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ |