aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2008-05-14 23:49:44 -0400
committerThomas Gleixner <tglx@linutronix.de>2008-05-23 16:43:11 -0400
commit4e491d14f2506b218d678935c25a7027b79178b1 (patch)
treec61c3ca40fe85f8556c579e50e0cb563ffcd06da /arch/powerpc/kernel
parente0eca07badc023a675a61906020b397da20f07c3 (diff)
ftrace: support for PowerPC
This patch adds full support for ftrace for PowerPC (both 64 and 32 bit). This includes dynamic tracing and function filtering. Signed-off-by: Steven Rostedt <srostedt@redhat.com> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r--arch/powerpc/kernel/Makefile14
-rw-r--r--arch/powerpc/kernel/entry_32.S130
-rw-r--r--arch/powerpc/kernel/entry_64.S62
-rw-r--r--arch/powerpc/kernel/ftrace.c165
-rw-r--r--arch/powerpc/kernel/io.c3
-rw-r--r--arch/powerpc/kernel/irq.c6
-rw-r--r--arch/powerpc/kernel/setup_32.c11
-rw-r--r--arch/powerpc/kernel/setup_64.c5
8 files changed, 389 insertions, 7 deletions
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 2346d271fbfd..f3f5e2641432 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -12,6 +12,18 @@ CFLAGS_prom_init.o += -fPIC
12CFLAGS_btext.o += -fPIC 12CFLAGS_btext.o += -fPIC
13endif 13endif
14 14
15ifdef CONFIG_FTRACE
16# Do not trace early boot code
17CFLAGS_REMOVE_cputable.o = -pg
18CFLAGS_REMOVE_prom_init.o = -pg
19
20ifdef CONFIG_DYNAMIC_FTRACE
21# dynamic ftrace setup.
22CFLAGS_REMOVE_ftrace.o = -pg
23endif
24
25endif
26
15obj-y := cputable.o ptrace.o syscalls.o \ 27obj-y := cputable.o ptrace.o syscalls.o \
16 irq.o align.o signal_32.o pmc.o vdso.o \ 28 irq.o align.o signal_32.o pmc.o vdso.o \
17 init_task.o process.o systbl.o idle.o \ 29 init_task.o process.o systbl.o idle.o \
@@ -78,6 +90,8 @@ obj-$(CONFIG_KEXEC) += machine_kexec.o crash.o \
78obj-$(CONFIG_AUDIT) += audit.o 90obj-$(CONFIG_AUDIT) += audit.o
79obj64-$(CONFIG_AUDIT) += compat_audit.o 91obj64-$(CONFIG_AUDIT) += compat_audit.o
80 92
93obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
94
81obj-$(CONFIG_8XX_MINIMAL_FPEMU) += softemu8xx.o 95obj-$(CONFIG_8XX_MINIMAL_FPEMU) += softemu8xx.o
82 96
83ifneq ($(CONFIG_PPC_INDIRECT_IO),y) 97ifneq ($(CONFIG_PPC_INDIRECT_IO),y)
diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S
index 0c8614d9875c..0e6221889ca9 100644
--- a/arch/powerpc/kernel/entry_32.S
+++ b/arch/powerpc/kernel/entry_32.S
@@ -1035,3 +1035,133 @@ machine_check_in_rtas:
1035 /* XXX load up BATs and panic */ 1035 /* XXX load up BATs and panic */
1036 1036
1037#endif /* CONFIG_PPC_RTAS */ 1037#endif /* CONFIG_PPC_RTAS */
1038
1039#ifdef CONFIG_FTRACE
1040#ifdef CONFIG_DYNAMIC_FTRACE
1041_GLOBAL(mcount)
1042_GLOBAL(_mcount)
1043 stwu r1,-48(r1)
1044 stw r3, 12(r1)
1045 stw r4, 16(r1)
1046 stw r5, 20(r1)
1047 stw r6, 24(r1)
1048 mflr r3
1049 stw r7, 28(r1)
1050 mfcr r5
1051 stw r8, 32(r1)
1052 stw r9, 36(r1)
1053 stw r10,40(r1)
1054 stw r3, 44(r1)
1055 stw r5, 8(r1)
1056 .globl mcount_call
1057mcount_call:
1058 bl ftrace_stub
1059 nop
1060 lwz r6, 8(r1)
1061 lwz r0, 44(r1)
1062 lwz r3, 12(r1)
1063 mtctr r0
1064 lwz r4, 16(r1)
1065 mtcr r6
1066 lwz r5, 20(r1)
1067 lwz r6, 24(r1)
1068 lwz r0, 52(r1)
1069 lwz r7, 28(r1)
1070 lwz r8, 32(r1)
1071 mtlr r0
1072 lwz r9, 36(r1)
1073 lwz r10,40(r1)
1074 addi r1, r1, 48
1075 bctr
1076
1077_GLOBAL(ftrace_caller)
1078 /* Based off of objdump optput from glibc */
1079 stwu r1,-48(r1)
1080 stw r3, 12(r1)
1081 stw r4, 16(r1)
1082 stw r5, 20(r1)
1083 stw r6, 24(r1)
1084 mflr r3
1085 lwz r4, 52(r1)
1086 mfcr r5
1087 stw r7, 28(r1)
1088 stw r8, 32(r1)
1089 stw r9, 36(r1)
1090 stw r10,40(r1)
1091 stw r3, 44(r1)
1092 stw r5, 8(r1)
1093.globl ftrace_call
1094ftrace_call:
1095 bl ftrace_stub
1096 nop
1097 lwz r6, 8(r1)
1098 lwz r0, 44(r1)
1099 lwz r3, 12(r1)
1100 mtctr r0
1101 lwz r4, 16(r1)
1102 mtcr r6
1103 lwz r5, 20(r1)
1104 lwz r6, 24(r1)
1105 lwz r0, 52(r1)
1106 lwz r7, 28(r1)
1107 lwz r8, 32(r1)
1108 mtlr r0
1109 lwz r9, 36(r1)
1110 lwz r10,40(r1)
1111 addi r1, r1, 48
1112 bctr
1113#else
1114_GLOBAL(mcount)
1115_GLOBAL(_mcount)
1116 stwu r1,-48(r1)
1117 stw r3, 12(r1)
1118 stw r4, 16(r1)
1119 stw r5, 20(r1)
1120 stw r6, 24(r1)
1121 mflr r3
1122 lwz r4, 52(r1)
1123 mfcr r5
1124 stw r7, 28(r1)
1125 stw r8, 32(r1)
1126 stw r9, 36(r1)
1127 stw r10,40(r1)
1128 stw r3, 44(r1)
1129 stw r5, 8(r1)
1130
1131 LOAD_REG_ADDR(r5, ftrace_trace_function)
1132#if 0
1133 mtctr r3
1134 mr r1, r5
1135 bctrl
1136#endif
1137 lwz r5,0(r5)
1138#if 1
1139 mtctr r5
1140 bctrl
1141#else
1142 bl ftrace_stub
1143#endif
1144 nop
1145
1146 lwz r6, 8(r1)
1147 lwz r0, 44(r1)
1148 lwz r3, 12(r1)
1149 mtctr r0
1150 lwz r4, 16(r1)
1151 mtcr r6
1152 lwz r5, 20(r1)
1153 lwz r6, 24(r1)
1154 lwz r0, 52(r1)
1155 lwz r7, 28(r1)
1156 lwz r8, 32(r1)
1157 mtlr r0
1158 lwz r9, 36(r1)
1159 lwz r10,40(r1)
1160 addi r1, r1, 48
1161 bctr
1162#endif
1163
1164_GLOBAL(ftrace_stub)
1165 blr
1166
1167#endif /* CONFIG_MCOUNT */
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index c0db5b769e55..2c4d9e056ead 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -870,3 +870,65 @@ _GLOBAL(enter_prom)
870 ld r0,16(r1) 870 ld r0,16(r1)
871 mtlr r0 871 mtlr r0
872 blr 872 blr
873
874#ifdef CONFIG_FTRACE
875#ifdef CONFIG_DYNAMIC_FTRACE
876_GLOBAL(mcount)
877_GLOBAL(_mcount)
878 /* Taken from output of objdump from lib64/glibc */
879 mflr r3
880 stdu r1, -112(r1)
881 std r3, 128(r1)
882 .globl mcount_call
883mcount_call:
884 bl ftrace_stub
885 nop
886 ld r0, 128(r1)
887 mtlr r0
888 addi r1, r1, 112
889 blr
890
891_GLOBAL(ftrace_caller)
892 /* Taken from output of objdump from lib64/glibc */
893 mflr r3
894 ld r11, 0(r1)
895 stdu r1, -112(r1)
896 std r3, 128(r1)
897 ld r4, 16(r11)
898.globl ftrace_call
899ftrace_call:
900 bl ftrace_stub
901 nop
902 ld r0, 128(r1)
903 mtlr r0
904 addi r1, r1, 112
905_GLOBAL(ftrace_stub)
906 blr
907#else
908_GLOBAL(mcount)
909 blr
910
911_GLOBAL(_mcount)
912 /* Taken from output of objdump from lib64/glibc */
913 mflr r3
914 ld r11, 0(r1)
915 stdu r1, -112(r1)
916 std r3, 128(r1)
917 ld r4, 16(r11)
918
919
920 LOAD_REG_ADDR(r5,ftrace_trace_function)
921 ld r5,0(r5)
922 ld r5,0(r5)
923 mtctr r5
924 bctrl
925
926 nop
927 ld r0, 128(r1)
928 mtlr r0
929 addi r1, r1, 112
930_GLOBAL(ftrace_stub)
931 blr
932
933#endif
934#endif
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c
new file mode 100644
index 000000000000..5a4993fefa45
--- /dev/null
+++ b/arch/powerpc/kernel/ftrace.c
@@ -0,0 +1,165 @@
1/*
2 * Code for replacing ftrace calls with jumps.
3 *
4 * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
5 *
6 * Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box.
7 *
8 */
9
10#include <linux/spinlock.h>
11#include <linux/hardirq.h>
12#include <linux/ftrace.h>
13#include <linux/percpu.h>
14#include <linux/init.h>
15#include <linux/list.h>
16
17#include <asm/cacheflush.h>
18
19#define CALL_BACK 4
20
21static unsigned int ftrace_nop = 0x60000000;
22
23#ifdef CONFIG_PPC32
24# define GET_ADDR(addr) addr
25#else
26/* PowerPC64's functions are data that points to the functions */
27# define GET_ADDR(addr) *(unsigned long *)addr
28#endif
29
30notrace int ftrace_ip_converted(unsigned long ip)
31{
32 unsigned int save;
33
34 ip -= CALL_BACK;
35 save = *(unsigned int *)ip;
36
37 return save == ftrace_nop;
38}
39
40static unsigned int notrace ftrace_calc_offset(long ip, long addr)
41{
42 return (int)((addr + CALL_BACK) - ip);
43}
44
45notrace unsigned char *ftrace_nop_replace(void)
46{
47 return (char *)&ftrace_nop;
48}
49
50notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
51{
52 static unsigned int op;
53
54 addr = GET_ADDR(addr);
55
56 /* Set to "bl addr" */
57 op = 0x48000001 | (ftrace_calc_offset(ip, addr) & 0x03fffffe);
58
59 /*
60 * No locking needed, this must be called via kstop_machine
61 * which in essence is like running on a uniprocessor machine.
62 */
63 return (unsigned char *)&op;
64}
65
66#ifdef CONFIG_PPC64
67# define _ASM_ALIGN " .align 3 "
68# define _ASM_PTR " .llong "
69#else
70# define _ASM_ALIGN " .align 2 "
71# define _ASM_PTR " .long "
72#endif
73
74notrace int
75ftrace_modify_code(unsigned long ip, unsigned char *old_code,
76 unsigned char *new_code)
77{
78 unsigned replaced;
79 unsigned old = *(unsigned *)old_code;
80 unsigned new = *(unsigned *)new_code;
81 int faulted = 0;
82
83 /* move the IP back to the start of the call */
84 ip -= CALL_BACK;
85
86 /*
87 * Note: Due to modules and __init, code can
88 * disappear and change, we need to protect against faulting
89 * as well as code changing.
90 *
91 * No real locking needed, this code is run through
92 * kstop_machine.
93 */
94 asm volatile (
95 "1: lwz %1, 0(%2)\n"
96 " cmpw %1, %5\n"
97 " bne 2f\n"
98 " stwu %3, 0(%2)\n"
99 "2:\n"
100 ".section .fixup, \"ax\"\n"
101 "3: li %0, 1\n"
102 " b 2b\n"
103 ".previous\n"
104 ".section __ex_table,\"a\"\n"
105 _ASM_ALIGN "\n"
106 _ASM_PTR "1b, 3b\n"
107 ".previous"
108 : "=r"(faulted), "=r"(replaced)
109 : "r"(ip), "r"(new),
110 "0"(faulted), "r"(old)
111 : "memory");
112
113 if (replaced != old && replaced != new)
114 faulted = 2;
115
116 if (!faulted)
117 flush_icache_range(ip, ip + 8);
118
119 return faulted;
120}
121
122notrace int ftrace_update_ftrace_func(ftrace_func_t func)
123{
124 unsigned long ip = (unsigned long)(&ftrace_call);
125 unsigned char old[4], *new;
126 int ret;
127
128 ip += CALL_BACK;
129
130 memcpy(old, &ftrace_call, 4);
131 new = ftrace_call_replace(ip, (unsigned long)func);
132 ret = ftrace_modify_code(ip, old, new);
133
134 return ret;
135}
136
137notrace int ftrace_mcount_set(unsigned long *data)
138{
139 unsigned long ip = (long)(&mcount_call);
140 unsigned long *addr = data;
141 unsigned char old[4], *new;
142
143 /* ip is at the location, but modify code will subtact this */
144 ip += CALL_BACK;
145
146 /*
147 * Replace the mcount stub with a pointer to the
148 * ip recorder function.
149 */
150 memcpy(old, &mcount_call, 4);
151 new = ftrace_call_replace(ip, *addr);
152 *addr = ftrace_modify_code(ip, old, new);
153
154 return 0;
155}
156
157int __init ftrace_dyn_arch_init(void *data)
158{
159 /* This is running in kstop_machine */
160
161 ftrace_mcount_set(data);
162
163 return 0;
164}
165
diff --git a/arch/powerpc/kernel/io.c b/arch/powerpc/kernel/io.c
index e31aca9208eb..1882bf419fa6 100644
--- a/arch/powerpc/kernel/io.c
+++ b/arch/powerpc/kernel/io.c
@@ -120,7 +120,8 @@ EXPORT_SYMBOL(_outsl_ns);
120 120
121#define IO_CHECK_ALIGN(v,a) ((((unsigned long)(v)) & ((a) - 1)) == 0) 121#define IO_CHECK_ALIGN(v,a) ((((unsigned long)(v)) & ((a) - 1)) == 0)
122 122
123void _memset_io(volatile void __iomem *addr, int c, unsigned long n) 123notrace void
124_memset_io(volatile void __iomem *addr, int c, unsigned long n)
124{ 125{
125 void *p = (void __force *)addr; 126 void *p = (void __force *)addr;
126 u32 lc = c; 127 u32 lc = c;
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index 2f73f705d564..6e01eb0a3315 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -98,7 +98,7 @@ EXPORT_SYMBOL(irq_desc);
98 98
99int distribute_irqs = 1; 99int distribute_irqs = 1;
100 100
101static inline unsigned long get_hard_enabled(void) 101static inline notrace unsigned long get_hard_enabled(void)
102{ 102{
103 unsigned long enabled; 103 unsigned long enabled;
104 104
@@ -108,13 +108,13 @@ static inline unsigned long get_hard_enabled(void)
108 return enabled; 108 return enabled;
109} 109}
110 110
111static inline void set_soft_enabled(unsigned long enable) 111static inline notrace void set_soft_enabled(unsigned long enable)
112{ 112{
113 __asm__ __volatile__("stb %0,%1(13)" 113 __asm__ __volatile__("stb %0,%1(13)"
114 : : "r" (enable), "i" (offsetof(struct paca_struct, soft_enabled))); 114 : : "r" (enable), "i" (offsetof(struct paca_struct, soft_enabled)));
115} 115}
116 116
117void raw_local_irq_restore(unsigned long en) 117notrace void raw_local_irq_restore(unsigned long en)
118{ 118{
119 /* 119 /*
120 * get_paca()->soft_enabled = en; 120 * get_paca()->soft_enabled = en;
diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
index 5112a4aa801d..22f8e2bacd32 100644
--- a/arch/powerpc/kernel/setup_32.c
+++ b/arch/powerpc/kernel/setup_32.c
@@ -47,6 +47,11 @@
47#include <asm/kgdb.h> 47#include <asm/kgdb.h>
48#endif 48#endif
49 49
50#ifdef CONFIG_FTRACE
51extern void _mcount(void);
52EXPORT_SYMBOL(_mcount);
53#endif
54
50extern void bootx_init(unsigned long r4, unsigned long phys); 55extern void bootx_init(unsigned long r4, unsigned long phys);
51 56
52int boot_cpuid; 57int boot_cpuid;
@@ -81,7 +86,7 @@ int ucache_bsize;
81 * from the address that it was linked at, so we must use RELOC/PTRRELOC 86 * from the address that it was linked at, so we must use RELOC/PTRRELOC
82 * to access static data (including strings). -- paulus 87 * to access static data (including strings). -- paulus
83 */ 88 */
84unsigned long __init early_init(unsigned long dt_ptr) 89notrace unsigned long __init early_init(unsigned long dt_ptr)
85{ 90{
86 unsigned long offset = reloc_offset(); 91 unsigned long offset = reloc_offset();
87 struct cpu_spec *spec; 92 struct cpu_spec *spec;
@@ -111,7 +116,7 @@ unsigned long __init early_init(unsigned long dt_ptr)
111 * This is called very early on the boot process, after a minimal 116 * This is called very early on the boot process, after a minimal
112 * MMU environment has been set up but before MMU_init is called. 117 * MMU environment has been set up but before MMU_init is called.
113 */ 118 */
114void __init machine_init(unsigned long dt_ptr, unsigned long phys) 119notrace void __init machine_init(unsigned long dt_ptr, unsigned long phys)
115{ 120{
116 /* Enable early debugging if any specified (see udbg.h) */ 121 /* Enable early debugging if any specified (see udbg.h) */
117 udbg_early_init(); 122 udbg_early_init();
@@ -133,7 +138,7 @@ void __init machine_init(unsigned long dt_ptr, unsigned long phys)
133 138
134#ifdef CONFIG_BOOKE_WDT 139#ifdef CONFIG_BOOKE_WDT
135/* Checks wdt=x and wdt_period=xx command-line option */ 140/* Checks wdt=x and wdt_period=xx command-line option */
136int __init early_parse_wdt(char *p) 141notrace int __init early_parse_wdt(char *p)
137{ 142{
138 if (p && strncmp(p, "0", 1) != 0) 143 if (p && strncmp(p, "0", 1) != 0)
139 booke_wdt_enabled = 1; 144 booke_wdt_enabled = 1;
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index 098fd96a394a..277bf18cbbcc 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -85,6 +85,11 @@ struct ppc64_caches ppc64_caches = {
85}; 85};
86EXPORT_SYMBOL_GPL(ppc64_caches); 86EXPORT_SYMBOL_GPL(ppc64_caches);
87 87
88#ifdef CONFIG_FTRACE
89extern void _mcount(void);
90EXPORT_SYMBOL(_mcount);
91#endif
92
88/* 93/*
89 * These are used in binfmt_elf.c to put aux entries on the stack 94 * These are used in binfmt_elf.c to put aux entries on the stack
90 * for each elf executable being started. 95 * for each elf executable being started.