diff options
| -rw-r--r-- | arch/blackfin/Kconfig | 2 | ||||
| -rw-r--r-- | arch/blackfin/include/asm/ftrace.h | 16 | ||||
| -rw-r--r-- | arch/blackfin/kernel/Makefile | 1 | ||||
| -rw-r--r-- | arch/blackfin/kernel/ftrace-entry.S | 85 | ||||
| -rw-r--r-- | arch/blackfin/kernel/ftrace.c | 86 |
5 files changed, 181 insertions, 9 deletions
diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index 0f63ed4fe143..10bdd8de0253 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig | |||
| @@ -25,6 +25,8 @@ config BLACKFIN | |||
| 25 | def_bool y | 25 | def_bool y |
| 26 | select HAVE_ARCH_KGDB | 26 | select HAVE_ARCH_KGDB |
| 27 | select HAVE_ARCH_TRACEHOOK | 27 | select HAVE_ARCH_TRACEHOOK |
| 28 | select HAVE_DYNAMIC_FTRACE | ||
| 29 | select HAVE_FTRACE_MCOUNT_RECORD | ||
| 28 | select HAVE_FUNCTION_GRAPH_TRACER | 30 | select HAVE_FUNCTION_GRAPH_TRACER |
| 29 | select HAVE_FUNCTION_TRACER | 31 | select HAVE_FUNCTION_TRACER |
| 30 | select HAVE_FUNCTION_TRACE_MCOUNT_TEST | 32 | select HAVE_FUNCTION_TRACE_MCOUNT_TEST |
diff --git a/arch/blackfin/include/asm/ftrace.h b/arch/blackfin/include/asm/ftrace.h index 4cfe2d9ba7e8..8a029505d7b7 100644 --- a/arch/blackfin/include/asm/ftrace.h +++ b/arch/blackfin/include/asm/ftrace.h | |||
| @@ -12,6 +12,22 @@ | |||
| 12 | 12 | ||
| 13 | #ifndef __ASSEMBLY__ | 13 | #ifndef __ASSEMBLY__ |
| 14 | 14 | ||
| 15 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
| 16 | |||
| 17 | extern void _mcount(void); | ||
| 18 | #define MCOUNT_ADDR ((unsigned long)_mcount) | ||
| 19 | |||
| 20 | static inline unsigned long ftrace_call_adjust(unsigned long addr) | ||
| 21 | { | ||
| 22 | return addr; | ||
| 23 | } | ||
| 24 | |||
| 25 | struct dyn_arch_ftrace { | ||
| 26 | /* No extra data needed for Blackfin */ | ||
| 27 | }; | ||
| 28 | |||
| 29 | #endif | ||
| 30 | |||
| 15 | #ifdef CONFIG_FRAME_POINTER | 31 | #ifdef CONFIG_FRAME_POINTER |
| 16 | #include <linux/mm.h> | 32 | #include <linux/mm.h> |
| 17 | 33 | ||
diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile index 30d0d1f01dc7..ca5ccc777772 100644 --- a/arch/blackfin/kernel/Makefile +++ b/arch/blackfin/kernel/Makefile | |||
| @@ -16,6 +16,7 @@ else | |||
| 16 | obj-y += time.o | 16 | obj-y += time.o |
| 17 | endif | 17 | endif |
| 18 | 18 | ||
| 19 | obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o | ||
| 19 | obj-$(CONFIG_FUNCTION_TRACER) += ftrace-entry.o | 20 | obj-$(CONFIG_FUNCTION_TRACER) += ftrace-entry.o |
| 20 | obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o | 21 | obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o |
| 21 | CFLAGS_REMOVE_ftrace.o = -pg | 22 | CFLAGS_REMOVE_ftrace.o = -pg |
diff --git a/arch/blackfin/kernel/ftrace-entry.S b/arch/blackfin/kernel/ftrace-entry.S index d66446b572c0..7eed00bbd26d 100644 --- a/arch/blackfin/kernel/ftrace-entry.S +++ b/arch/blackfin/kernel/ftrace-entry.S | |||
| @@ -10,6 +10,18 @@ | |||
| 10 | 10 | ||
| 11 | .text | 11 | .text |
| 12 | 12 | ||
| 13 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
| 14 | |||
| 15 | /* Simple stub so we can boot the kernel until runtime patching has | ||
| 16 | * disabled all calls to this. Then it'll be unused. | ||
| 17 | */ | ||
| 18 | ENTRY(__mcount) | ||
| 19 | # if ANOMALY_05000371 | ||
| 20 | nop; nop; nop; nop; | ||
| 21 | # endif | ||
| 22 | rts; | ||
| 23 | ENDPROC(__mcount) | ||
| 24 | |||
| 13 | /* GCC will have called us before setting up the function prologue, so we | 25 | /* GCC will have called us before setting up the function prologue, so we |
| 14 | * can clobber the normal scratch registers, but we need to make sure to | 26 | * can clobber the normal scratch registers, but we need to make sure to |
| 15 | * save/restore the registers used for argument passing (R0-R2) in case | 27 | * save/restore the registers used for argument passing (R0-R2) in case |
| @@ -20,15 +32,65 @@ | |||
| 20 | * function. And since GCC pushed the previous RETS for us, the previous | 32 | * function. And since GCC pushed the previous RETS for us, the previous |
| 21 | * function will be waiting there. mmmm pie. | 33 | * function will be waiting there. mmmm pie. |
| 22 | */ | 34 | */ |
| 35 | ENTRY(_ftrace_caller) | ||
| 36 | # ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST | ||
| 37 | /* optional micro optimization: return if stopped */ | ||
| 38 | p1.l = _function_trace_stop; | ||
| 39 | p1.h = _function_trace_stop; | ||
| 40 | r3 = [p1]; | ||
| 41 | cc = r3 == 0; | ||
| 42 | if ! cc jump _ftrace_stub (bp); | ||
| 43 | # endif | ||
| 44 | |||
| 45 | /* save first/second/third function arg and the return register */ | ||
| 46 | [--sp] = r2; | ||
| 47 | [--sp] = r0; | ||
| 48 | [--sp] = r1; | ||
| 49 | [--sp] = rets; | ||
| 50 | |||
| 51 | /* function_trace_call(unsigned long ip, unsigned long parent_ip): | ||
| 52 | * ip: this point was called by ... | ||
| 53 | * parent_ip: ... this function | ||
| 54 | * the ip itself will need adjusting for the mcount call | ||
| 55 | */ | ||
| 56 | r0 = rets; | ||
| 57 | r1 = [sp + 16]; /* skip the 4 local regs on stack */ | ||
| 58 | r0 += -MCOUNT_INSN_SIZE; | ||
| 59 | |||
| 60 | .globl _ftrace_call | ||
| 61 | _ftrace_call: | ||
| 62 | call _ftrace_stub | ||
| 63 | |||
| 64 | # ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
| 65 | .globl _ftrace_graph_call | ||
| 66 | _ftrace_graph_call: | ||
| 67 | nop; /* jump _ftrace_graph_caller; */ | ||
| 68 | # endif | ||
| 69 | |||
| 70 | /* restore state and get out of dodge */ | ||
| 71 | .Lfinish_trace: | ||
| 72 | rets = [sp++]; | ||
| 73 | r1 = [sp++]; | ||
| 74 | r0 = [sp++]; | ||
| 75 | r2 = [sp++]; | ||
| 76 | |||
| 77 | .globl _ftrace_stub | ||
| 78 | _ftrace_stub: | ||
| 79 | rts; | ||
| 80 | ENDPROC(_ftrace_caller) | ||
| 81 | |||
| 82 | #else | ||
| 83 | |||
| 84 | /* See documentation for _ftrace_caller */ | ||
| 23 | ENTRY(__mcount) | 85 | ENTRY(__mcount) |
| 24 | #ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST | 86 | # ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST |
| 25 | /* optional micro optimization: return if stopped */ | 87 | /* optional micro optimization: return if stopped */ |
| 26 | p1.l = _function_trace_stop; | 88 | p1.l = _function_trace_stop; |
| 27 | p1.h = _function_trace_stop; | 89 | p1.h = _function_trace_stop; |
| 28 | r3 = [p1]; | 90 | r3 = [p1]; |
| 29 | cc = r3 == 0; | 91 | cc = r3 == 0; |
| 30 | if ! cc jump _ftrace_stub (bp); | 92 | if ! cc jump _ftrace_stub (bp); |
| 31 | #endif | 93 | # endif |
| 32 | 94 | ||
| 33 | /* save third function arg early so we can do testing below */ | 95 | /* save third function arg early so we can do testing below */ |
| 34 | [--sp] = r2; | 96 | [--sp] = r2; |
| @@ -44,7 +106,7 @@ ENTRY(__mcount) | |||
| 44 | cc = r2 == r3; | 106 | cc = r2 == r3; |
| 45 | if ! cc jump .Ldo_trace; | 107 | if ! cc jump .Ldo_trace; |
| 46 | 108 | ||
| 47 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 109 | # ifdef CONFIG_FUNCTION_GRAPH_TRACER |
| 48 | /* if the ftrace_graph_return function pointer is not set to | 110 | /* if the ftrace_graph_return function pointer is not set to |
| 49 | * the ftrace_stub entry, call prepare_ftrace_return(). | 111 | * the ftrace_stub entry, call prepare_ftrace_return(). |
| 50 | */ | 112 | */ |
| @@ -64,7 +126,7 @@ ENTRY(__mcount) | |||
| 64 | r3 = [p0]; | 126 | r3 = [p0]; |
| 65 | cc = r2 == r3; | 127 | cc = r2 == r3; |
| 66 | if ! cc jump _ftrace_graph_caller; | 128 | if ! cc jump _ftrace_graph_caller; |
| 67 | #endif | 129 | # endif |
| 68 | 130 | ||
| 69 | r2 = [sp++]; | 131 | r2 = [sp++]; |
| 70 | rts; | 132 | rts; |
| @@ -103,6 +165,8 @@ _ftrace_stub: | |||
| 103 | rts; | 165 | rts; |
| 104 | ENDPROC(__mcount) | 166 | ENDPROC(__mcount) |
| 105 | 167 | ||
| 168 | #endif | ||
| 169 | |||
| 106 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 170 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
| 107 | /* The prepare_ftrace_return() function is similar to the trace function | 171 | /* The prepare_ftrace_return() function is similar to the trace function |
| 108 | * except it takes a pointer to the location of the frompc. This is so | 172 | * except it takes a pointer to the location of the frompc. This is so |
| @@ -110,6 +174,7 @@ ENDPROC(__mcount) | |||
| 110 | * purposes. | 174 | * purposes. |
| 111 | */ | 175 | */ |
| 112 | ENTRY(_ftrace_graph_caller) | 176 | ENTRY(_ftrace_graph_caller) |
| 177 | # ifndef CONFIG_DYNAMIC_FTRACE | ||
| 113 | /* save first/second function arg and the return register */ | 178 | /* save first/second function arg and the return register */ |
| 114 | [--sp] = r0; | 179 | [--sp] = r0; |
| 115 | [--sp] = r1; | 180 | [--sp] = r1; |
| @@ -118,9 +183,13 @@ ENTRY(_ftrace_graph_caller) | |||
| 118 | /* prepare_ftrace_return(parent, self_addr, frame_pointer) */ | 183 | /* prepare_ftrace_return(parent, self_addr, frame_pointer) */ |
| 119 | r0 = sp; /* unsigned long *parent */ | 184 | r0 = sp; /* unsigned long *parent */ |
| 120 | r1 = rets; /* unsigned long self_addr */ | 185 | r1 = rets; /* unsigned long self_addr */ |
| 121 | #ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST | 186 | # else |
| 187 | r0 = sp; /* unsigned long *parent */ | ||
| 188 | r1 = [sp]; /* unsigned long self_addr */ | ||
| 189 | # endif | ||
| 190 | # ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST | ||
| 122 | r2 = fp; /* unsigned long frame_pointer */ | 191 | r2 = fp; /* unsigned long frame_pointer */ |
| 123 | #endif | 192 | # endif |
| 124 | r0 += 16; /* skip the 4 local regs on stack */ | 193 | r0 += 16; /* skip the 4 local regs on stack */ |
| 125 | r1 += -MCOUNT_INSN_SIZE; | 194 | r1 += -MCOUNT_INSN_SIZE; |
| 126 | call _prepare_ftrace_return; | 195 | call _prepare_ftrace_return; |
| @@ -139,9 +208,9 @@ ENTRY(_return_to_handler) | |||
| 139 | [--sp] = r1; | 208 | [--sp] = r1; |
| 140 | 209 | ||
| 141 | /* get original return address */ | 210 | /* get original return address */ |
| 142 | #ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST | 211 | # ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST |
| 143 | r0 = fp; /* Blackfin is sane, so omit this */ | 212 | r0 = fp; /* Blackfin is sane, so omit this */ |
| 144 | #endif | 213 | # endif |
| 145 | call _ftrace_return_to_handler; | 214 | call _ftrace_return_to_handler; |
| 146 | rets = r0; | 215 | rets = r0; |
| 147 | 216 | ||
diff --git a/arch/blackfin/kernel/ftrace.c b/arch/blackfin/kernel/ftrace.c index a61d948ea925..48808a12b427 100644 --- a/arch/blackfin/kernel/ftrace.c +++ b/arch/blackfin/kernel/ftrace.c | |||
| @@ -1,17 +1,101 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * ftrace graph code | 2 | * ftrace graph code |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2009 Analog Devices Inc. | 4 | * Copyright (C) 2009-2010 Analog Devices Inc. |
| 5 | * Licensed under the GPL-2 or later. | 5 | * Licensed under the GPL-2 or later. |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/ftrace.h> | 8 | #include <linux/ftrace.h> |
| 9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
| 10 | #include <linux/sched.h> | 10 | #include <linux/sched.h> |
| 11 | #include <linux/uaccess.h> | ||
| 11 | #include <asm/atomic.h> | 12 | #include <asm/atomic.h> |
| 13 | #include <asm/cacheflush.h> | ||
| 14 | |||
| 15 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
| 16 | |||
| 17 | static const unsigned char mnop[] = { | ||
| 18 | 0x03, 0xc0, 0x00, 0x18, /* MNOP; */ | ||
| 19 | 0x03, 0xc0, 0x00, 0x18, /* MNOP; */ | ||
| 20 | }; | ||
| 21 | |||
| 22 | static void bfin_make_pcrel24(unsigned char *insn, unsigned long src, | ||
| 23 | unsigned long dst) | ||
| 24 | { | ||
| 25 | uint32_t pcrel = (dst - src) >> 1; | ||
| 26 | insn[0] = pcrel >> 16; | ||
| 27 | insn[1] = 0xe3; | ||
| 28 | insn[2] = pcrel; | ||
| 29 | insn[3] = pcrel >> 8; | ||
| 30 | } | ||
| 31 | #define bfin_make_pcrel24(insn, src, dst) bfin_make_pcrel24(insn, src, (unsigned long)(dst)) | ||
| 32 | |||
| 33 | static int ftrace_modify_code(unsigned long ip, const unsigned char *code, | ||
| 34 | unsigned long len) | ||
| 35 | { | ||
| 36 | int ret = probe_kernel_write((void *)ip, (void *)code, len); | ||
| 37 | flush_icache_range(ip, ip + len); | ||
| 38 | return ret; | ||
| 39 | } | ||
| 40 | |||
| 41 | int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, | ||
| 42 | unsigned long addr) | ||
| 43 | { | ||
| 44 | /* Turn the mcount call site into two MNOPs as those are 32bit insns */ | ||
| 45 | return ftrace_modify_code(rec->ip, mnop, sizeof(mnop)); | ||
| 46 | } | ||
| 47 | |||
| 48 | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | ||
| 49 | { | ||
| 50 | /* Restore the mcount call site */ | ||
| 51 | unsigned char call[8]; | ||
| 52 | call[0] = 0x67; /* [--SP] = RETS; */ | ||
| 53 | call[1] = 0x01; | ||
| 54 | bfin_make_pcrel24(&call[2], rec->ip + 2, addr); | ||
| 55 | call[6] = 0x27; /* RETS = [SP++]; */ | ||
| 56 | call[7] = 0x01; | ||
| 57 | return ftrace_modify_code(rec->ip, call, sizeof(call)); | ||
| 58 | } | ||
| 59 | |||
| 60 | int ftrace_update_ftrace_func(ftrace_func_t func) | ||
| 61 | { | ||
| 62 | unsigned char call[4]; | ||
| 63 | unsigned long ip = (unsigned long)&ftrace_call; | ||
| 64 | bfin_make_pcrel24(call, ip, func); | ||
| 65 | return ftrace_modify_code(ip, call, sizeof(call)); | ||
| 66 | } | ||
| 67 | |||
| 68 | int __init ftrace_dyn_arch_init(void *data) | ||
| 69 | { | ||
| 70 | /* return value is done indirectly via data */ | ||
| 71 | *(unsigned long *)data = 0; | ||
| 72 | |||
| 73 | return 0; | ||
| 74 | } | ||
| 75 | |||
| 76 | #endif | ||
| 12 | 77 | ||
| 13 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 78 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
| 14 | 79 | ||
| 80 | # ifdef CONFIG_DYNAMIC_FTRACE | ||
| 81 | |||
| 82 | extern void ftrace_graph_call(void); | ||
| 83 | |||
| 84 | int ftrace_enable_ftrace_graph_caller(void) | ||
| 85 | { | ||
| 86 | unsigned long ip = (unsigned long)&ftrace_graph_call; | ||
| 87 | uint16_t jump_pcrel12 = ((unsigned long)&ftrace_graph_caller - ip) >> 1; | ||
| 88 | jump_pcrel12 |= 0x2000; | ||
| 89 | return ftrace_modify_code(ip, (void *)&jump_pcrel12, sizeof(jump_pcrel12)); | ||
| 90 | } | ||
| 91 | |||
| 92 | int ftrace_disable_ftrace_graph_caller(void) | ||
| 93 | { | ||
| 94 | return ftrace_modify_code((unsigned long)&ftrace_graph_call, empty_zero_page, 2); | ||
| 95 | } | ||
| 96 | |||
| 97 | # endif | ||
| 98 | |||
| 15 | /* | 99 | /* |
| 16 | * Hook the return address and push it in the stack of return addrs | 100 | * Hook the return address and push it in the stack of return addrs |
| 17 | * in current thread info. | 101 | * in current thread info. |
