diff options
-rw-r--r-- | arch/metag/Kconfig | 5 | ||||
-rw-r--r-- | arch/metag/include/asm/Kbuild | 1 | ||||
-rw-r--r-- | arch/metag/include/asm/ftrace.h | 23 | ||||
-rw-r--r-- | arch/metag/kernel/ftrace.c | 126 | ||||
-rw-r--r-- | arch/metag/kernel/ftrace_stub.S | 76 | ||||
-rw-r--r-- | arch/metag/kernel/metag_ksyms.c | 5 | ||||
-rw-r--r-- | scripts/recordmcount.c | 13 |
7 files changed, 248 insertions, 1 deletions
diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig index 47972025818f..f6846ad5d3a8 100644 --- a/arch/metag/Kconfig +++ b/arch/metag/Kconfig | |||
@@ -12,7 +12,12 @@ config METAG | |||
12 | select GENERIC_SMP_IDLE_THREAD | 12 | select GENERIC_SMP_IDLE_THREAD |
13 | select HAVE_64BIT_ALIGNED_ACCESS | 13 | select HAVE_64BIT_ALIGNED_ACCESS |
14 | select HAVE_ARCH_TRACEHOOK | 14 | select HAVE_ARCH_TRACEHOOK |
15 | select HAVE_C_RECORDMCOUNT | ||
15 | select HAVE_DEBUG_KMEMLEAK | 16 | select HAVE_DEBUG_KMEMLEAK |
17 | select HAVE_DYNAMIC_FTRACE | ||
18 | select HAVE_FTRACE_MCOUNT_RECORD | ||
19 | select HAVE_FUNCTION_TRACER | ||
20 | select HAVE_FUNCTION_TRACE_MCOUNT_TEST | ||
16 | select HAVE_GENERIC_HARDIRQS | 21 | select HAVE_GENERIC_HARDIRQS |
17 | select HAVE_IRQ_WORK | 22 | select HAVE_IRQ_WORK |
18 | select HAVE_KERNEL_BZIP2 | 23 | select HAVE_KERNEL_BZIP2 |
diff --git a/arch/metag/include/asm/Kbuild b/arch/metag/include/asm/Kbuild index 3a139810559e..6ae0ccb632cb 100644 --- a/arch/metag/include/asm/Kbuild +++ b/arch/metag/include/asm/Kbuild | |||
@@ -11,7 +11,6 @@ generic-y += errno.h | |||
11 | generic-y += exec.h | 11 | generic-y += exec.h |
12 | generic-y += fb.h | 12 | generic-y += fb.h |
13 | generic-y += fcntl.h | 13 | generic-y += fcntl.h |
14 | generic-y += ftrace.h | ||
15 | generic-y += futex.h | 14 | generic-y += futex.h |
16 | generic-y += hardirq.h | 15 | generic-y += hardirq.h |
17 | generic-y += hw_irq.h | 16 | generic-y += hw_irq.h |
diff --git a/arch/metag/include/asm/ftrace.h b/arch/metag/include/asm/ftrace.h new file mode 100644 index 000000000000..2901f0f7d944 --- /dev/null +++ b/arch/metag/include/asm/ftrace.h | |||
@@ -0,0 +1,23 @@ | |||
1 | #ifndef _ASM_METAG_FTRACE | ||
2 | #define _ASM_METAG_FTRACE | ||
3 | |||
4 | #ifdef CONFIG_FUNCTION_TRACER | ||
5 | #define MCOUNT_INSN_SIZE 8 /* sizeof mcount call */ | ||
6 | |||
7 | #ifndef __ASSEMBLY__ | ||
8 | extern void mcount_wrapper(void); | ||
9 | #define MCOUNT_ADDR ((long)(mcount_wrapper)) | ||
10 | |||
11 | static inline unsigned long ftrace_call_adjust(unsigned long addr) | ||
12 | { | ||
13 | return addr; | ||
14 | } | ||
15 | |||
16 | struct dyn_arch_ftrace { | ||
17 | /* No extra data needed on metag */ | ||
18 | }; | ||
19 | #endif /* __ASSEMBLY__ */ | ||
20 | |||
21 | #endif /* CONFIG_FUNCTION_TRACER */ | ||
22 | |||
23 | #endif /* _ASM_METAG_FTRACE */ | ||
diff --git a/arch/metag/kernel/ftrace.c b/arch/metag/kernel/ftrace.c new file mode 100644 index 000000000000..a774f321643f --- /dev/null +++ b/arch/metag/kernel/ftrace.c | |||
@@ -0,0 +1,126 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Imagination Technologies Ltd. | ||
3 | * Licensed under the GPL | ||
4 | * | ||
5 | * Dynamic ftrace support. | ||
6 | */ | ||
7 | |||
8 | #include <linux/ftrace.h> | ||
9 | #include <linux/io.h> | ||
10 | #include <linux/uaccess.h> | ||
11 | |||
12 | #include <asm/cacheflush.h> | ||
13 | |||
14 | #define D04_MOVT_TEMPLATE 0x02200005 | ||
15 | #define D04_CALL_TEMPLATE 0xAC200005 | ||
16 | #define D1RTP_MOVT_TEMPLATE 0x03200005 | ||
17 | #define D1RTP_CALL_TEMPLATE 0xAC200006 | ||
18 | |||
19 | static const unsigned long NOP[2] = {0xa0fffffe, 0xa0fffffe}; | ||
20 | static unsigned long movt_and_call_insn[2]; | ||
21 | |||
22 | static unsigned char *ftrace_nop_replace(void) | ||
23 | { | ||
24 | return (char *)&NOP[0]; | ||
25 | } | ||
26 | |||
27 | static unsigned char *ftrace_call_replace(unsigned long pc, unsigned long addr) | ||
28 | { | ||
29 | unsigned long hi16, low16; | ||
30 | |||
31 | hi16 = (addr & 0xffff0000) >> 13; | ||
32 | low16 = (addr & 0x0000ffff) << 3; | ||
33 | |||
34 | /* | ||
35 | * The compiler makes the call to mcount_wrapper() | ||
36 | * (Meta's wrapper around mcount()) through the register | ||
37 | * D0.4. So whenever we're patching one of those compiler-generated | ||
38 | * calls we also need to go through D0.4. Otherwise use D1RtP. | ||
39 | */ | ||
40 | if (pc == (unsigned long)&ftrace_call) { | ||
41 | writel(D1RTP_MOVT_TEMPLATE | hi16, &movt_and_call_insn[0]); | ||
42 | writel(D1RTP_CALL_TEMPLATE | low16, &movt_and_call_insn[1]); | ||
43 | } else { | ||
44 | writel(D04_MOVT_TEMPLATE | hi16, &movt_and_call_insn[0]); | ||
45 | writel(D04_CALL_TEMPLATE | low16, &movt_and_call_insn[1]); | ||
46 | } | ||
47 | |||
48 | return (unsigned char *)&movt_and_call_insn[0]; | ||
49 | } | ||
50 | |||
51 | static int ftrace_modify_code(unsigned long pc, unsigned char *old_code, | ||
52 | unsigned char *new_code) | ||
53 | { | ||
54 | unsigned char replaced[MCOUNT_INSN_SIZE]; | ||
55 | |||
56 | /* | ||
57 | * Note: Due to modules and __init, code can | ||
58 | * disappear and change, we need to protect against faulting | ||
59 | * as well as code changing. | ||
60 | * | ||
61 | * No real locking needed, this code is run through | ||
62 | * kstop_machine. | ||
63 | */ | ||
64 | |||
65 | /* read the text we want to modify */ | ||
66 | if (probe_kernel_read(replaced, (void *)pc, MCOUNT_INSN_SIZE)) | ||
67 | return -EFAULT; | ||
68 | |||
69 | /* Make sure it is what we expect it to be */ | ||
70 | if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0) | ||
71 | return -EINVAL; | ||
72 | |||
73 | /* replace the text with the new text */ | ||
74 | if (probe_kernel_write((void *)pc, new_code, MCOUNT_INSN_SIZE)) | ||
75 | return -EPERM; | ||
76 | |||
77 | flush_icache_range(pc, pc + MCOUNT_INSN_SIZE); | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | int ftrace_update_ftrace_func(ftrace_func_t func) | ||
83 | { | ||
84 | int ret; | ||
85 | unsigned long pc; | ||
86 | unsigned char old[MCOUNT_INSN_SIZE], *new; | ||
87 | |||
88 | pc = (unsigned long)&ftrace_call; | ||
89 | memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); | ||
90 | new = ftrace_call_replace(pc, (unsigned long)func); | ||
91 | ret = ftrace_modify_code(pc, old, new); | ||
92 | |||
93 | return ret; | ||
94 | } | ||
95 | |||
96 | int ftrace_make_nop(struct module *mod, | ||
97 | struct dyn_ftrace *rec, unsigned long addr) | ||
98 | { | ||
99 | unsigned char *new, *old; | ||
100 | unsigned long ip = rec->ip; | ||
101 | |||
102 | old = ftrace_call_replace(ip, addr); | ||
103 | new = ftrace_nop_replace(); | ||
104 | |||
105 | return ftrace_modify_code(ip, old, new); | ||
106 | } | ||
107 | |||
108 | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | ||
109 | { | ||
110 | unsigned char *new, *old; | ||
111 | unsigned long ip = rec->ip; | ||
112 | |||
113 | old = ftrace_nop_replace(); | ||
114 | new = ftrace_call_replace(ip, addr); | ||
115 | |||
116 | return ftrace_modify_code(ip, old, new); | ||
117 | } | ||
118 | |||
119 | /* run from kstop_machine */ | ||
120 | int __init ftrace_dyn_arch_init(void *data) | ||
121 | { | ||
122 | /* The return code is returned via data */ | ||
123 | writel(0, data); | ||
124 | |||
125 | return 0; | ||
126 | } | ||
diff --git a/arch/metag/kernel/ftrace_stub.S b/arch/metag/kernel/ftrace_stub.S new file mode 100644 index 000000000000..e70bff745bdd --- /dev/null +++ b/arch/metag/kernel/ftrace_stub.S | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Imagination Technologies Ltd. | ||
3 | * Licensed under the GPL | ||
4 | * | ||
5 | */ | ||
6 | |||
7 | #include <asm/ftrace.h> | ||
8 | |||
9 | .text | ||
10 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
11 | .global _mcount_wrapper | ||
12 | .type _mcount_wrapper,function | ||
13 | _mcount_wrapper: | ||
14 | MOV PC,D0.4 | ||
15 | |||
16 | .global _ftrace_caller | ||
17 | .type _ftrace_caller,function | ||
18 | _ftrace_caller: | ||
19 | MOVT D0Re0,#HI(_function_trace_stop) | ||
20 | ADD D0Re0,D0Re0,#LO(_function_trace_stop) | ||
21 | GETD D0Re0,[D0Re0] | ||
22 | CMP D0Re0,#0 | ||
23 | BEQ $Lcall_stub | ||
24 | MOV PC,D0.4 | ||
25 | $Lcall_stub: | ||
26 | MSETL [A0StP], D0Ar6, D0Ar4, D0Ar2, D0.4 | ||
27 | MOV D1Ar1, D0.4 | ||
28 | MOV D0Ar2, D1RtP | ||
29 | SUB D1Ar1,D1Ar1,#MCOUNT_INSN_SIZE | ||
30 | |||
31 | .global _ftrace_call | ||
32 | _ftrace_call: | ||
33 | MOVT D1RtP,#HI(_ftrace_stub) | ||
34 | CALL D1RtP,#LO(_ftrace_stub) | ||
35 | GETL D0.4, D1RtP, [A0StP++#(-8)] | ||
36 | GETL D0Ar2, D1Ar1, [A0StP++#(-8)] | ||
37 | GETL D0Ar4, D1Ar3, [A0StP++#(-8)] | ||
38 | GETL D0Ar6, D1Ar5, [A0StP++#(-8)] | ||
39 | MOV PC, D0.4 | ||
40 | #else | ||
41 | |||
42 | .global _mcount_wrapper | ||
43 | .type _mcount_wrapper,function | ||
44 | _mcount_wrapper: | ||
45 | MOVT D0Re0,#HI(_function_trace_stop) | ||
46 | ADD D0Re0,D0Re0,#LO(_function_trace_stop) | ||
47 | GETD D0Re0,[D0Re0] | ||
48 | CMP D0Re0,#0 | ||
49 | BEQ $Lcall_mcount | ||
50 | MOV PC,D0.4 | ||
51 | $Lcall_mcount: | ||
52 | MSETL [A0StP], D0Ar6, D0Ar4, D0Ar2, D0.4 | ||
53 | MOV D1Ar1, D0.4 | ||
54 | MOV D0Ar2, D1RtP | ||
55 | MOVT D0Re0,#HI(_ftrace_trace_function) | ||
56 | ADD D0Re0,D0Re0,#LO(_ftrace_trace_function) | ||
57 | GET D1Ar3,[D0Re0] | ||
58 | MOVT D1Re0,#HI(_ftrace_stub) | ||
59 | ADD D1Re0,D1Re0,#LO(_ftrace_stub) | ||
60 | CMP D1Ar3,D1Re0 | ||
61 | BEQ $Ltrace_exit | ||
62 | MOV D1RtP,D1Ar3 | ||
63 | SUB D1Ar1,D1Ar1,#MCOUNT_INSN_SIZE | ||
64 | SWAP PC,D1RtP | ||
65 | $Ltrace_exit: | ||
66 | GETL D0.4, D1RtP, [A0StP++#(-8)] | ||
67 | GETL D0Ar2, D1Ar1, [A0StP++#(-8)] | ||
68 | GETL D0Ar4, D1Ar3, [A0StP++#(-8)] | ||
69 | GETL D0Ar6, D1Ar5, [A0StP++#(-8)] | ||
70 | MOV PC, D0.4 | ||
71 | |||
72 | #endif /* CONFIG_DYNAMIC_FTRACE */ | ||
73 | |||
74 | .global _ftrace_stub | ||
75 | _ftrace_stub: | ||
76 | MOV PC,D1RtP | ||
diff --git a/arch/metag/kernel/metag_ksyms.c b/arch/metag/kernel/metag_ksyms.c index c73ebd3db417..3cc4d1d4b322 100644 --- a/arch/metag/kernel/metag_ksyms.c +++ b/arch/metag/kernel/metag_ksyms.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <asm/checksum.h> | 10 | #include <asm/checksum.h> |
11 | #include <asm/uaccess.h> | 11 | #include <asm/uaccess.h> |
12 | #include <asm/traps.h> | 12 | #include <asm/traps.h> |
13 | #include <asm/ftrace.h> | ||
13 | #include <asm/tbx.h> | 14 | #include <asm/tbx.h> |
14 | 15 | ||
15 | /* uaccess symbols */ | 16 | /* uaccess symbols */ |
@@ -73,3 +74,7 @@ EXPORT_SYMBOL(div_s64); | |||
73 | EXPORT_SYMBOL(memcpy); | 74 | EXPORT_SYMBOL(memcpy); |
74 | EXPORT_SYMBOL(memset); | 75 | EXPORT_SYMBOL(memset); |
75 | EXPORT_SYMBOL(memmove); | 76 | EXPORT_SYMBOL(memmove); |
77 | |||
78 | #ifdef CONFIG_FUNCTION_TRACER | ||
79 | EXPORT_SYMBOL(mcount_wrapper); | ||
80 | #endif | ||
diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index ee52cb8e17ad..9c22317778eb 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c | |||
@@ -33,6 +33,13 @@ | |||
33 | #include <string.h> | 33 | #include <string.h> |
34 | #include <unistd.h> | 34 | #include <unistd.h> |
35 | 35 | ||
36 | #ifndef EM_METAG | ||
37 | /* Remove this when these make it to the standard system elf.h. */ | ||
38 | #define EM_METAG 174 | ||
39 | #define R_METAG_ADDR32 2 | ||
40 | #define R_METAG_NONE 3 | ||
41 | #endif | ||
42 | |||
36 | static int fd_map; /* File descriptor for file being modified. */ | 43 | static int fd_map; /* File descriptor for file being modified. */ |
37 | static int mmap_failed; /* Boolean flag. */ | 44 | static int mmap_failed; /* Boolean flag. */ |
38 | static void *ehdr_curr; /* current ElfXX_Ehdr * for resource cleanup */ | 45 | static void *ehdr_curr; /* current ElfXX_Ehdr * for resource cleanup */ |
@@ -341,6 +348,12 @@ do_file(char const *const fname) | |||
341 | altmcount = "__gnu_mcount_nc"; | 348 | altmcount = "__gnu_mcount_nc"; |
342 | break; | 349 | break; |
343 | case EM_IA_64: reltype = R_IA64_IMM64; gpfx = '_'; break; | 350 | case EM_IA_64: reltype = R_IA64_IMM64; gpfx = '_'; break; |
351 | case EM_METAG: reltype = R_METAG_ADDR32; | ||
352 | altmcount = "_mcount_wrapper"; | ||
353 | rel_type_nop = R_METAG_NONE; | ||
354 | /* We happen to have the same requirement as MIPS */ | ||
355 | is_fake_mcount32 = MIPS32_is_fake_mcount; | ||
356 | break; | ||
344 | case EM_MIPS: /* reltype: e_class */ gpfx = '_'; break; | 357 | case EM_MIPS: /* reltype: e_class */ gpfx = '_'; break; |
345 | case EM_PPC: reltype = R_PPC_ADDR32; gpfx = '_'; break; | 358 | case EM_PPC: reltype = R_PPC_ADDR32; gpfx = '_'; break; |
346 | case EM_PPC64: reltype = R_PPC64_ADDR64; gpfx = '_'; break; | 359 | case EM_PPC64: reltype = R_PPC64_ADDR64; gpfx = '_'; break; |