diff options
-rw-r--r-- | arch/arm/kernel/Makefile | 5 | ||||
-rw-r--r-- | arch/arm/kernel/ftrace.c | 61 | ||||
-rw-r--r-- | arch/arm/kernel/insn.c | 61 | ||||
-rw-r--r-- | arch/arm/kernel/insn.h | 19 |
4 files changed, 87 insertions, 59 deletions
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 43b740d0e374..dd3d5f16c147 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
@@ -7,6 +7,7 @@ AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) | |||
7 | 7 | ||
8 | ifdef CONFIG_FUNCTION_TRACER | 8 | ifdef CONFIG_FUNCTION_TRACER |
9 | CFLAGS_REMOVE_ftrace.o = -pg | 9 | CFLAGS_REMOVE_ftrace.o = -pg |
10 | CFLAGS_REMOVE_insn.o = -pg | ||
10 | endif | 11 | endif |
11 | 12 | ||
12 | CFLAGS_REMOVE_return_address.o = -pg | 13 | CFLAGS_REMOVE_return_address.o = -pg |
@@ -34,8 +35,8 @@ obj-$(CONFIG_HAVE_SCHED_CLOCK) += sched_clock.o | |||
34 | obj-$(CONFIG_SMP) += smp.o smp_tlb.o | 35 | obj-$(CONFIG_SMP) += smp.o smp_tlb.o |
35 | obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o | 36 | obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o |
36 | obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o | 37 | obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o |
37 | obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o | 38 | obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o |
38 | obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o | 39 | obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o |
39 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o | 40 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o |
40 | obj-$(CONFIG_KPROBES) += kprobes.o kprobes-common.o | 41 | obj-$(CONFIG_KPROBES) += kprobes.o kprobes-common.o |
41 | ifdef CONFIG_THUMB2_KERNEL | 42 | ifdef CONFIG_THUMB2_KERNEL |
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index 5c9cecfaeb21..df0bf0c8cb79 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c | |||
@@ -19,6 +19,8 @@ | |||
19 | #include <asm/opcodes.h> | 19 | #include <asm/opcodes.h> |
20 | #include <asm/ftrace.h> | 20 | #include <asm/ftrace.h> |
21 | 21 | ||
22 | #include "insn.h" | ||
23 | |||
22 | #ifdef CONFIG_THUMB2_KERNEL | 24 | #ifdef CONFIG_THUMB2_KERNEL |
23 | #define NOP 0xf85deb04 /* pop.w {lr} */ | 25 | #define NOP 0xf85deb04 /* pop.w {lr} */ |
24 | #else | 26 | #else |
@@ -61,64 +63,9 @@ static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) | |||
61 | } | 63 | } |
62 | #endif | 64 | #endif |
63 | 65 | ||
64 | #ifdef CONFIG_THUMB2_KERNEL | ||
65 | static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr, | ||
66 | bool link) | ||
67 | { | ||
68 | unsigned long s, j1, j2, i1, i2, imm10, imm11; | ||
69 | unsigned long first, second; | ||
70 | long offset; | ||
71 | |||
72 | offset = (long)addr - (long)(pc + 4); | ||
73 | if (offset < -16777216 || offset > 16777214) { | ||
74 | WARN_ON_ONCE(1); | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | s = (offset >> 24) & 0x1; | ||
79 | i1 = (offset >> 23) & 0x1; | ||
80 | i2 = (offset >> 22) & 0x1; | ||
81 | imm10 = (offset >> 12) & 0x3ff; | ||
82 | imm11 = (offset >> 1) & 0x7ff; | ||
83 | |||
84 | j1 = (!i1) ^ s; | ||
85 | j2 = (!i2) ^ s; | ||
86 | |||
87 | first = 0xf000 | (s << 10) | imm10; | ||
88 | second = 0x9000 | (j1 << 13) | (j2 << 11) | imm11; | ||
89 | if (link) | ||
90 | second |= 1 << 14; | ||
91 | |||
92 | return __opcode_thumb32_compose(first, second); | ||
93 | } | ||
94 | #else | ||
95 | static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr, | ||
96 | bool link) | ||
97 | { | ||
98 | unsigned long opcode = 0xea000000; | ||
99 | long offset; | ||
100 | |||
101 | if (link) | ||
102 | opcode |= 1 << 24; | ||
103 | |||
104 | offset = (long)addr - (long)(pc + 8); | ||
105 | if (unlikely(offset < -33554432 || offset > 33554428)) { | ||
106 | /* Can't generate branches that far (from ARM ARM). Ftrace | ||
107 | * doesn't generate branches outside of kernel text. | ||
108 | */ | ||
109 | WARN_ON_ONCE(1); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | offset = (offset >> 2) & 0x00ffffff; | ||
114 | |||
115 | return opcode | offset; | ||
116 | } | ||
117 | #endif | ||
118 | |||
119 | static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) | 66 | static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) |
120 | { | 67 | { |
121 | return ftrace_gen_branch(pc, addr, true); | 68 | return arm_gen_branch_link(pc, addr); |
122 | } | 69 | } |
123 | 70 | ||
124 | static int ftrace_modify_code(unsigned long pc, unsigned long old, | 71 | static int ftrace_modify_code(unsigned long pc, unsigned long old, |
@@ -258,7 +205,7 @@ static int __ftrace_modify_caller(unsigned long *callsite, | |||
258 | { | 205 | { |
259 | unsigned long caller_fn = (unsigned long) func; | 206 | unsigned long caller_fn = (unsigned long) func; |
260 | unsigned long pc = (unsigned long) callsite; | 207 | unsigned long pc = (unsigned long) callsite; |
261 | unsigned long branch = ftrace_gen_branch(pc, caller_fn, false); | 208 | unsigned long branch = arm_gen_branch(pc, caller_fn); |
262 | unsigned long nop = 0xe1a00000; /* mov r0, r0 */ | 209 | unsigned long nop = 0xe1a00000; /* mov r0, r0 */ |
263 | unsigned long old = enable ? nop : branch; | 210 | unsigned long old = enable ? nop : branch; |
264 | unsigned long new = enable ? branch : nop; | 211 | unsigned long new = enable ? branch : nop; |
diff --git a/arch/arm/kernel/insn.c b/arch/arm/kernel/insn.c new file mode 100644 index 000000000000..ab312e516546 --- /dev/null +++ b/arch/arm/kernel/insn.c | |||
@@ -0,0 +1,61 @@ | |||
1 | #include <linux/kernel.h> | ||
2 | #include <asm/opcodes.h> | ||
3 | |||
4 | static unsigned long | ||
5 | __arm_gen_branch_thumb2(unsigned long pc, unsigned long addr, bool link) | ||
6 | { | ||
7 | unsigned long s, j1, j2, i1, i2, imm10, imm11; | ||
8 | unsigned long first, second; | ||
9 | long offset; | ||
10 | |||
11 | offset = (long)addr - (long)(pc + 4); | ||
12 | if (offset < -16777216 || offset > 16777214) { | ||
13 | WARN_ON_ONCE(1); | ||
14 | return 0; | ||
15 | } | ||
16 | |||
17 | s = (offset >> 24) & 0x1; | ||
18 | i1 = (offset >> 23) & 0x1; | ||
19 | i2 = (offset >> 22) & 0x1; | ||
20 | imm10 = (offset >> 12) & 0x3ff; | ||
21 | imm11 = (offset >> 1) & 0x7ff; | ||
22 | |||
23 | j1 = (!i1) ^ s; | ||
24 | j2 = (!i2) ^ s; | ||
25 | |||
26 | first = 0xf000 | (s << 10) | imm10; | ||
27 | second = 0x9000 | (j1 << 13) | (j2 << 11) | imm11; | ||
28 | if (link) | ||
29 | second |= 1 << 14; | ||
30 | |||
31 | return __opcode_thumb32_compose(first, second); | ||
32 | } | ||
33 | |||
34 | static unsigned long | ||
35 | __arm_gen_branch_arm(unsigned long pc, unsigned long addr, bool link) | ||
36 | { | ||
37 | unsigned long opcode = 0xea000000; | ||
38 | long offset; | ||
39 | |||
40 | if (link) | ||
41 | opcode |= 1 << 24; | ||
42 | |||
43 | offset = (long)addr - (long)(pc + 8); | ||
44 | if (unlikely(offset < -33554432 || offset > 33554428)) { | ||
45 | WARN_ON_ONCE(1); | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | offset = (offset >> 2) & 0x00ffffff; | ||
50 | |||
51 | return opcode | offset; | ||
52 | } | ||
53 | |||
54 | unsigned long | ||
55 | __arm_gen_branch(unsigned long pc, unsigned long addr, bool link) | ||
56 | { | ||
57 | if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) | ||
58 | return __arm_gen_branch_thumb2(pc, addr, link); | ||
59 | else | ||
60 | return __arm_gen_branch_arm(pc, addr, link); | ||
61 | } | ||
diff --git a/arch/arm/kernel/insn.h b/arch/arm/kernel/insn.h new file mode 100644 index 000000000000..994d60fd3ff5 --- /dev/null +++ b/arch/arm/kernel/insn.h | |||
@@ -0,0 +1,19 @@ | |||
1 | #ifndef __ASM_ARM_INSN_H | ||
2 | #define __ASM_ARM_INSN_H | ||
3 | |||
4 | unsigned long | ||
5 | __arm_gen_branch(unsigned long pc, unsigned long addr, bool link); | ||
6 | |||
7 | static inline unsigned long | ||
8 | arm_gen_branch(unsigned long pc, unsigned long addr) | ||
9 | { | ||
10 | return __arm_gen_branch(pc, addr, false); | ||
11 | } | ||
12 | |||
13 | static inline unsigned long | ||
14 | arm_gen_branch_link(unsigned long pc, unsigned long addr) | ||
15 | { | ||
16 | return __arm_gen_branch(pc, addr, true); | ||
17 | } | ||
18 | |||
19 | #endif | ||