aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/metag/Kconfig5
-rw-r--r--arch/metag/include/asm/Kbuild1
-rw-r--r--arch/metag/include/asm/ftrace.h23
-rw-r--r--arch/metag/kernel/ftrace.c126
-rw-r--r--arch/metag/kernel/ftrace_stub.S76
-rw-r--r--arch/metag/kernel/metag_ksyms.c5
-rw-r--r--scripts/recordmcount.c13
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
11generic-y += exec.h 11generic-y += exec.h
12generic-y += fb.h 12generic-y += fb.h
13generic-y += fcntl.h 13generic-y += fcntl.h
14generic-y += ftrace.h
15generic-y += futex.h 14generic-y += futex.h
16generic-y += hardirq.h 15generic-y += hardirq.h
17generic-y += hw_irq.h 16generic-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__
8extern void mcount_wrapper(void);
9#define MCOUNT_ADDR ((long)(mcount_wrapper))
10
11static inline unsigned long ftrace_call_adjust(unsigned long addr)
12{
13 return addr;
14}
15
16struct 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
19static const unsigned long NOP[2] = {0xa0fffffe, 0xa0fffffe};
20static unsigned long movt_and_call_insn[2];
21
22static unsigned char *ftrace_nop_replace(void)
23{
24 return (char *)&NOP[0];
25}
26
27static 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
51static 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
82int 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
96int 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
108int 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 */
120int __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);
73EXPORT_SYMBOL(memcpy); 74EXPORT_SYMBOL(memcpy);
74EXPORT_SYMBOL(memset); 75EXPORT_SYMBOL(memset);
75EXPORT_SYMBOL(memmove); 76EXPORT_SYMBOL(memmove);
77
78#ifdef CONFIG_FUNCTION_TRACER
79EXPORT_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
36static int fd_map; /* File descriptor for file being modified. */ 43static int fd_map; /* File descriptor for file being modified. */
37static int mmap_failed; /* Boolean flag. */ 44static int mmap_failed; /* Boolean flag. */
38static void *ehdr_curr; /* current ElfXX_Ehdr * for resource cleanup */ 45static 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;