aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAbhishek Sagar <sagar.abhishek@gmail.com>2008-05-31 04:53:50 -0400
committerIngo Molnar <mingo@elte.hu>2008-06-02 05:32:20 -0400
commit014c257cce65e9d1cd2d28ec1c89a37c536b151d (patch)
treec7a28fbf6f295538964539d5b8c552ea786f7072
parentb1829d2705daa7cb72eb1e08bdc8b7e9fad34266 (diff)
ftrace: core support for ARM
Core ftrace support for the ARM architecture, which includes support for dynamic function tracing. Signed-off-by: Abhishek Sagar <sagar.abhishek@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--arch/arm/Kconfig2
-rw-r--r--arch/arm/boot/compressed/Makefile6
-rw-r--r--arch/arm/kernel/Makefile5
-rw-r--r--arch/arm/kernel/armksyms.c5
-rw-r--r--arch/arm/kernel/entry-common.S47
-rw-r--r--arch/arm/kernel/ftrace.c128
6 files changed, 193 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index b786e68914d4..3845e5c8a34f 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -14,6 +14,8 @@ config ARM
14 select HAVE_OPROFILE 14 select HAVE_OPROFILE
15 select HAVE_KPROBES if (!XIP_KERNEL) 15 select HAVE_KPROBES if (!XIP_KERNEL)
16 select HAVE_KRETPROBES if (HAVE_KPROBES) 16 select HAVE_KRETPROBES if (HAVE_KPROBES)
17 select HAVE_FTRACE if (!XIP_KERNEL)
18 select HAVE_DYNAMIC_FTRACE if (HAVE_FTRACE)
17 help 19 help
18 The ARM series is a line of low-power-consumption RISC chip designs 20 The ARM series is a line of low-power-consumption RISC chip designs
19 licensed by ARM Ltd and targeted at embedded applications and 21 licensed by ARM Ltd and targeted at embedded applications and
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index de9d9ee50958..95baac4939e0 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -69,6 +69,12 @@ SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/
69 69
70targets := vmlinux vmlinux.lds piggy.gz piggy.o font.o font.c \ 70targets := vmlinux vmlinux.lds piggy.gz piggy.o font.o font.c \
71 head.o misc.o $(OBJS) 71 head.o misc.o $(OBJS)
72
73ifeq ($(CONFIG_FTRACE),y)
74ORIG_CFLAGS := $(KBUILD_CFLAGS)
75KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
76endif
77
72EXTRA_CFLAGS := -fpic -fno-builtin 78EXTRA_CFLAGS := -fpic -fno-builtin
73EXTRA_AFLAGS := 79EXTRA_AFLAGS :=
74 80
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index ad455ff5aebe..eb9092ca8008 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -4,6 +4,10 @@
4 4
5AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) 5AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
6 6
7ifdef CONFIG_DYNAMIC_FTRACE
8CFLAGS_REMOVE_ftrace.o = -pg
9endif
10
7# Object file lists. 11# Object file lists.
8 12
9obj-y := compat.o entry-armv.o entry-common.o irq.o \ 13obj-y := compat.o entry-armv.o entry-common.o irq.o \
@@ -18,6 +22,7 @@ obj-$(CONFIG_ARTHUR) += arthur.o
18obj-$(CONFIG_ISA_DMA) += dma-isa.o 22obj-$(CONFIG_ISA_DMA) += dma-isa.o
19obj-$(CONFIG_PCI) += bios32.o isa.o 23obj-$(CONFIG_PCI) += bios32.o isa.o
20obj-$(CONFIG_SMP) += smp.o 24obj-$(CONFIG_SMP) += smp.o
25obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
21obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o 26obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
22obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o 27obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o
23obj-$(CONFIG_ATAGS_PROC) += atags.o 28obj-$(CONFIG_ATAGS_PROC) += atags.o
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index 688b7b1ee416..3b132215cbf8 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -48,6 +48,11 @@ extern void __aeabi_ulcmp(void);
48extern void fpundefinstr(void); 48extern void fpundefinstr(void);
49extern void fp_enter(void); 49extern void fp_enter(void);
50 50
51#ifdef CONFIG_FTRACE
52extern void mcount(void);
53EXPORT_SYMBOL(mcount);
54#endif
55
51/* 56/*
52 * This has a special calling convention; it doesn't 57 * This has a special calling convention; it doesn't
53 * modify any of the usual registers, except for LR. 58 * modify any of the usual registers, except for LR.
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 597ed00a08d8..8f79a4789ed4 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -99,6 +99,53 @@ ENTRY(ret_from_fork)
99#undef CALL 99#undef CALL
100#define CALL(x) .long x 100#define CALL(x) .long x
101 101
102#ifdef CONFIG_FTRACE
103#ifdef CONFIG_DYNAMIC_FTRACE
104ENTRY(mcount)
105 stmdb sp!, {r0-r3, lr}
106 mov r0, lr
107
108 .globl mcount_call
109mcount_call:
110 bl ftrace_stub
111 ldmia sp!, {r0-r3, pc}
112
113ENTRY(ftrace_caller)
114 stmdb sp!, {r0-r3, lr}
115 ldr r1, [fp, #-4]
116 mov r0, lr
117
118 .globl ftrace_call
119ftrace_call:
120 bl ftrace_stub
121 ldmia sp!, {r0-r3, pc}
122
123#else
124
125ENTRY(mcount)
126 stmdb sp!, {r0-r3, lr}
127 ldr r0, =ftrace_trace_function
128 ldr r2, [r0]
129 adr r0, ftrace_stub
130 cmp r0, r2
131 bne trace
132 ldmia sp!, {r0-r3, pc}
133
134trace:
135 ldr r1, [fp, #-4]
136 mov r0, lr
137 mov lr, pc
138 mov pc, r2
139 ldmia sp!, {r0-r3, pc}
140
141#endif /* CONFIG_DYNAMIC_FTRACE */
142
143 .globl ftrace_stub
144ftrace_stub:
145 mov pc, lr
146
147#endif /* CONFIG_FTRACE */
148
102/*============================================================================= 149/*=============================================================================
103 * SWI handler 150 * SWI handler
104 *----------------------------------------------------------------------------- 151 *-----------------------------------------------------------------------------
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
new file mode 100644
index 000000000000..f4cb4cc3fa0c
--- /dev/null
+++ b/arch/arm/kernel/ftrace.c
@@ -0,0 +1,128 @@
1/*
2 * Dynamic function tracing support.
3 *
4 * Copyright (C) 2008 Abhishek Sagar <sagar.abhishek@gmail.com>
5 *
6 * For licencing details, see COPYING.
7 *
8 * Defines low-level handling of mcount calls when the kernel
9 * is compiled with the -pg flag. When using dynamic ftrace, the
10 * mcount call-sites get patched lazily with NOP till they are
11 * enabled. All code mutation routines here take effect atomically.
12 */
13
14#include <linux/ftrace.h>
15#include <asm/cacheflush.h>
16
17#define INSN_SIZE 4
18#define PC_OFFSET 8
19#define BL_OPCODE 0xeb000000
20#define BL_OFFSET_MASK 0x00ffffff
21
22static unsigned long bl_insn;
23static const unsigned long NOP = 0xe1a00000; /* mov r0, r0 */
24
25/* return true if mcount call site is already patched/no-op'ed */
26int ftrace_ip_converted(unsigned long pc)
27{
28 unsigned long save;
29
30 pc -= INSN_SIZE;
31 save = *(unsigned long *)pc;
32 return save == NOP;
33}
34
35unsigned char *ftrace_nop_replace(void)
36{
37 return (char *)&NOP;
38}
39
40/* construct a branch (BL) instruction to addr */
41unsigned char *ftrace_call_replace(unsigned long pc, unsigned long addr)
42{
43 long offset;
44
45 offset = (long)addr - (long)(pc - INSN_SIZE + PC_OFFSET);
46 if (unlikely(offset < -33554432 || offset > 33554428)) {
47 /* Can't generate branches that far (from ARM ARM). Ftrace
48 * doesn't generate branches outside of core kernel text.
49 */
50 WARN_ON_ONCE(1);
51 return NULL;
52 }
53 offset = (offset >> 2) & BL_OFFSET_MASK;
54 bl_insn = BL_OPCODE | offset;
55 return (unsigned char *)&bl_insn;
56}
57
58int ftrace_modify_code(unsigned long pc, unsigned char *old_code,
59 unsigned char *new_code)
60{
61 unsigned long err = 0, replaced = 0, old, new;
62
63 old = *(unsigned long *)old_code;
64 new = *(unsigned long *)new_code;
65 pc -= INSN_SIZE;
66
67 __asm__ __volatile__ (
68 "1: ldr %1, [%2] \n"
69 " cmp %1, %4 \n"
70 "2: streq %3, [%2] \n"
71 " cmpne %1, %3 \n"
72 " movne %0, #2 \n"
73 "3:\n"
74
75 ".section .fixup, \"ax\"\n"
76 "4: mov %0, #1 \n"
77 " b 3b \n"
78 ".previous\n"
79
80 ".section __ex_table, \"a\"\n"
81 " .long 1b, 4b \n"
82 " .long 2b, 4b \n"
83 ".previous\n"
84
85 : "=r"(err), "=r"(replaced)
86 : "r"(pc), "r"(new), "r"(old), "0"(err), "1"(replaced)
87 : "memory");
88
89 if (!err && (replaced == old))
90 flush_icache_range(pc, pc + INSN_SIZE);
91
92 return err;
93}
94
95int ftrace_update_ftrace_func(ftrace_func_t func)
96{
97 int ret;
98 unsigned long pc, old;
99 unsigned char *new;
100
101 pc = (unsigned long)&ftrace_call;
102 pc += INSN_SIZE;
103 memcpy(&old, &ftrace_call, INSN_SIZE);
104 new = ftrace_call_replace(pc, (unsigned long)func);
105 ret = ftrace_modify_code(pc, (unsigned char *)&old, new);
106 return ret;
107}
108
109int ftrace_mcount_set(unsigned long *data)
110{
111 unsigned long pc, old;
112 unsigned long *addr = data;
113 unsigned char *new;
114
115 pc = (unsigned long)&mcount_call;
116 pc += INSN_SIZE;
117 memcpy(&old, &mcount_call, INSN_SIZE);
118 new = ftrace_call_replace(pc, *addr);
119 *addr = ftrace_modify_code(pc, (unsigned char *)&old, new);
120 return 0;
121}
122
123/* run from kstop_machine */
124int __init ftrace_dyn_arch_init(void *data)
125{
126 ftrace_mcount_set(data);
127 return 0;
128}