Skip to contentSkip to conversation

ARM: ftrace: use canonical Thumb-2 wide instruction format

PATCH New Rabin Vincent 7 participants 27 messages Nov 21, 2011 – Jan 24, 2012

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/4 ARM: ftrace: use canonical Thumb-2 wide instruction format

As commit 592201a9f15 (ARM: Thumb-2: Support Thumb-2 in undefined instruction handler) says:

32-bit Thumb instructions are specified in the form:
((first_half << 16 ) | second_half)
which matches the layout used by the ARM ARM.

Convert the ftrace code to use the same format to avoid the usage of different formats in kernel code.

---
 arch/arm/kernel/ftrace.c |   29 ++++++++++++++++++-----------
 1 files changed, 18 insertions(+), 11 deletions(-)
arch/arm/kernel/ftrace.c +18 -11
index c0062ad..cdceb63 100644
@@ -19,7 +19,7 @@
 #include <asm/ftrace.h>
 
 #ifdef CONFIG_THUMB2_KERNEL
-#define	NOP		0xeb04f85d	/* pop.w {lr} */
+#define	NOP		0xf85deb04	/* pop.w {lr} */
 #else
 #define	NOP		0xe8bd4000	/* pop {lr} */
 #endif
@@ -88,7 +88,7 @@ static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr,
 	if (link)
 		second |= 1 << 14;
 
-	return (second << 16) | first;
+	return (first << 16) | second;
 }
 #else
 static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr,
@@ -125,11 +125,20 @@ static int ftrace_modify_code(unsigned long pc, unsigned long old,
 {
 	unsigned long replaced;
 
-	if (probe_kernel_read(&replaced, (void *)pc, MCOUNT_INSN_SIZE))
-		return -EFAULT;
+#ifndef __ARMEB__
+	if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
+		old = (old >> 16) | (old << 16);
+		new = (new >> 16) | (new << 16);
+	}
+#endif
 
-	if (replaced != old)
-		return -EINVAL;
+	if (old) {
+		if (probe_kernel_read(&replaced, (void *)pc, MCOUNT_INSN_SIZE))
+			return -EFAULT;
+
+		if (replaced != old)
+			return -EINVAL;
+	}
 
 	if (probe_kernel_write((void *)pc, &new, MCOUNT_INSN_SIZE))
 		return -EPERM;
@@ -141,23 +150,21 @@ static int ftrace_modify_code(unsigned long pc, unsigned long old,
 
 int ftrace_update_ftrace_func(ftrace_func_t func)
 {
-	unsigned long pc, old;
+	unsigned long pc;
 	unsigned long new;
 	int ret;
 
 	pc = (unsigned long)&ftrace_call;
-	memcpy(&old, &ftrace_call, MCOUNT_INSN_SIZE);
 	new = ftrace_call_replace(pc, (unsigned long)func);
 
-	ret = ftrace_modify_code(pc, old, new);
+	ret = ftrace_modify_code(pc, 0, new);
 
 #ifdef CONFIG_OLD_MCOUNT
 	if (!ret) {
 		pc = (unsigned long)&ftrace_call_old;
-		memcpy(&old, &ftrace_call_old, MCOUNT_INSN_SIZE);
 		new = ftrace_call_replace(pc, (unsigned long)func);
 
-		ret = ftrace_modify_code(pc, old, new);
+		ret = ftrace_modify_code(pc, 0, new);
 	}
 #endif
 
Signature · 1 line
1.7.7.3
#1 lore raw

PATCH 2/4 ARM: extract out insn generation code from ftrace

Extract out the instruction generation code so that it can be used for jump labels too.

---
 arch/arm/kernel/Makefile |    5 ++-
 arch/arm/kernel/ftrace.c |   61 +++-------------------------------------------
 arch/arm/kernel/insn.c   |   60 +++++++++++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/insn.h   |   19 ++++++++++++++
 4 files changed, 86 insertions(+), 59 deletions(-)
 create mode 100644 arch/arm/kernel/insn.c
 create mode 100644 arch/arm/kernel/insn.h
arch/arm/kernel/Makefile +3 -2
index 16eed6a..a2f34a3 100644
@@ -7,6 +7,7 @@ AFLAGS_head.o        := -DTEXT_OFFSET=$(TEXT_OFFSET)
 
 ifdef CONFIG_FUNCTION_TRACER
 CFLAGS_REMOVE_ftrace.o = -pg
+CFLAGS_REMOVE_insn.o = -pg
 endif
 
 CFLAGS_REMOVE_return_address.o = -pg
@@ -34,8 +35,8 @@ obj-$(CONFIG_HAVE_SCHED_CLOCK)	+= sched_clock.o
 obj-$(CONFIG_SMP)		+= smp.o smp_tlb.o
 obj-$(CONFIG_HAVE_ARM_SCU)	+= smp_scu.o
 obj-$(CONFIG_HAVE_ARM_TWD)	+= smp_twd.o
-obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o
-obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o
+obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o insn.o
+obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o insn.o
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
 obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-common.o
 ifdef CONFIG_THUMB2_KERNEL
arch/arm/kernel/ftrace.c +4 -57

81 lines not on this page · raw message

arch/arm/kernel/insn.c new file +60 -0

60 lines not on this page · raw message

arch/arm/kernel/insn.h new file +19 -0
new file mode 100644
index 0000000..994d60f
@@ -0,0 +1,19 @@
+#ifndef __ASM_ARM_INSN_H
+#define __ASM_ARM_INSN_H
+
+unsigned long
+__arm_gen_branch(unsigned long pc, unsigned long addr, bool link);
+
+static inline unsigned long
+arm_gen_branch(unsigned long pc, unsigned long addr)
+{
+	return __arm_gen_branch(pc, addr, false);
+}
+
+static inline unsigned long
+arm_gen_branch_link(unsigned long pc, unsigned long addr)
+{
+	return __arm_gen_branch(pc, addr, true);
+}
+
+#endif
Signature · 1 line
1.7.7.3
#2 lore raw

PATCH 2/4 ARM: extract out insn generation code from ftrace

Quoted 14 lines · Rabin Vincent

Extract out the instruction generation code so that it can be used for jump labels too.

---
 arch/arm/kernel/Makefile |    5 ++-
 arch/arm/kernel/ftrace.c |   61 +++-------------------------------------------
 arch/arm/kernel/insn.c   |   60 +++++++++++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/insn.h   |   19 ++++++++++++++
 4 files changed, 86 insertions(+), 59 deletions(-)
 create mode 100644 arch/arm/kernel/insn.c
 create mode 100644 arch/arm/kernel/insn.h
arch/arm/kernel/Makefile +0 -0

[...]

arch/arm/kernel/Makefile static int __ftrace_modify_caller(unsigned long *callsite, · line 207

Quoted 8 lines

static int ftrace_modify_code(unsigned long pc, unsigned long old,

arch/arm/kernel/Makefile +1 -1
@@ -256,7 +203,7 @@ static int __ftrace_modify_caller(unsigned long *callsite,
 {
 	unsigned long caller_fn = (unsigned long) func;
 	unsigned long pc = (unsigned long) callsite;
-	unsigned long branch = ftrace_gen_branch(pc, caller_fn, false);
+	unsigned long branch = arm_gen_branch(pc, caller_fn);
 	unsigned long nop = 0xe1a00000;	/* mov r0, r0 */

Does this code get used in Thumb-2 kernels? So far as I can tell, this code is used... but it looks like it shouldn't work, due to the ARM specifics.

Cheers
---Dave

#3 lore raw

PATCH 2/4 ARM: extract out insn generation code from ftrace

Quoted 13 lines · Dave Martin
Quoted 8 lines · Rabin Vincent

?static int ftrace_modify_code(unsigned long pc, unsigned long old,

(quoted diff) +1 -1
@@ -256,7 +203,7 @@ static int __ftrace_modify_caller(unsigned long *callsite,
 ?{
 ? ? ? unsigned long caller_fn = (unsigned long) func;
 ? ? ? unsigned long pc = (unsigned long) callsite;
- ? ? unsigned long branch = ftrace_gen_branch(pc, caller_fn, false);
+ ? ? unsigned long branch = arm_gen_branch(pc, caller_fn);

? ? ? unsigned long nop = 0xe1a00000; /* mov r0, r0 */

Does this code get used in Thumb-2 kernels? ?So far as I can tell, this code is used... but it looks like it shouldn't work, due to the ARM specifics.

It's not used on Thumb-2. This is only used by the function graph tracer and that depends on !THUMB2_KERNEL.

#4 lore raw

PATCH 2/4 ARM: extract out insn generation code from ftrace

Quoted 15 lines · Rabin Vincent
Quoted 13 lines · Dave Martin
Quoted 8 lines · Rabin Vincent

?static int ftrace_modify_code(unsigned long pc, unsigned long old,

(quoted diff) +1 -1
@@ -256,7 +203,7 @@ static int __ftrace_modify_caller(unsigned long *callsite,
 ?{
 ? ? ? unsigned long caller_fn = (unsigned long) func;
 ? ? ? unsigned long pc = (unsigned long) callsite;
- ? ? unsigned long branch = ftrace_gen_branch(pc, caller_fn, false);
+ ? ? unsigned long branch = arm_gen_branch(pc, caller_fn);

? ? ? unsigned long nop = 0xe1a00000; /* mov r0, r0 */

Does this code get used in Thumb-2 kernels? ?So far as I can tell, this code is used... but it looks like it shouldn't work, due to the ARM specifics.

Quoted 2 lines

It's not used on Thumb-2. This is only used by the function graph tracer and that depends on !THUMB2_KERNEL.

I assume there's no special reason why this doesn't work with a Thumb-2 kernel, other than that it simply hasn't implemented yet?

Are there any particular problems blocking this?

Cheers
---Dave

#5 lore raw

PATCH 2/4 ARM: extract out insn generation code from ftrace

Quoted 6 lines · Dave Martin
Quoted 2 lines · Rabin Vincent

It's not used on Thumb-2. ?This is only used by the function graph tracer and that depends on !THUMB2_KERNEL.

I assume there's no special reason why this doesn't work with a Thumb-2 kernel, other than that it simply hasn't implemented yet?

The function graph tracer expects to get a pointer to the saved return address of the function (on the stack). It changes this to its own return_hooker function where it records the exit from the function and jumps to the real return address.

We do this for ARM (the instruction set) by building with frame pointers, and the function prologues always save the registers in the expected order and the epilogues restore them out from the saved locations.

However, with Thumb-2, we don't get frame pointers even if we ask for them. So to support Thumb-2 we'd probably need a way to find and modify the LR without it being at a fixed location (MIPS does something similar IIRC). I haven't really looked into it.

#6 lore raw

PATCH 2/4 ARM: extract out insn generation code from ftrace

Quoted 13 lines · Rabin Vincent
Quoted 6 lines · Dave Martin
Quoted 2 lines · Rabin Vincent

It's not used on Thumb-2. ?This is only used by the function graph tracer and that depends on !THUMB2_KERNEL.

I assume there's no special reason why this doesn't work with a Thumb-2 kernel, other than that it simply hasn't implemented yet?

The function graph tracer expects to get a pointer to the saved return address of the function (on the stack). It changes this to its own return_hooker function where it records the exit from the function and jumps to the real return address.

Quoted 8 lines

We do this for ARM (the instruction set) by building with frame pointers, and the function prologues always save the registers in the expected order and the epilogues restore them out from the saved locations.

However, with Thumb-2, we don't get frame pointers even if we ask for them. So to support Thumb-2 we'd probably need a way to find and modify the LR without it being at a fixed location (MIPS does something similar IIRC). I haven't really looked into it.

The unwind tables contain the information required to find the link register. This is how the backtracer works in Thumb-2 kernels or with
CONFIG_ARM_UNWIND. This adds some complexity, but it also has some
advantages such as elimiating the runtime stack and instruction overhead associated with maintaining a framepointer chain.

Really, "find/change frame's stored return address" is a generic operation which is closely related to stack unwinding, and would best be implemented along with the unwinder code. Ideally, ftrace should not need to know or care _how_ this is accomplished. Indeed, at this level the operation isn't even arch-specific.

I will take a look at the ARM unwind/backtrace code and think about whether this could be factored out or exposed in a helpful way. Certainly we shouldn't implement a second unwind table parser in ftrace...

Are there any other dependencies on framepointers, or is that the only thing?

Cheers
---Dave

#7 lore raw

PATCH 2/4 ARM: extract out insn generation code from ftrace

Quoted 2 lines · Dave Martin

Are there any other dependencies on framepointers, or is that the only thing?

That's the only thing.

#8 lore raw

PATCH 3/4 ARM: extract out code patch function from kprobes

In reply to Rabin Vincent

Extract out the code patching code from kprobes so that it can be used from the jump label code. Additionally, the separated code:

- Uses the IS_ENABLED() macros instead of the #ifdefs for THUMB2 support

- Unifies the two separate functions in kprobes, providing one function that uses stop_machine() internally, and one that can be called from stop_machine() directly

- Patches the text on all CPUs only on processors requiring software broadcasting of cache operations

---
 arch/arm/kernel/Makefile  |    3 +-
 arch/arm/kernel/kprobes.c |   86 ++++++++++++--------------------------------
 arch/arm/kernel/patch.c   |   72 +++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/patch.h   |    7 ++++
 4 files changed, 105 insertions(+), 63 deletions(-)
 create mode 100644 arch/arm/kernel/patch.c
 create mode 100644 arch/arm/kernel/patch.h
arch/arm/kernel/Makefile +2 -1
index a2f34a3..adf02ed 100644
@@ -8,6 +8,7 @@ AFLAGS_head.o        := -DTEXT_OFFSET=$(TEXT_OFFSET)
 ifdef CONFIG_FUNCTION_TRACER
 CFLAGS_REMOVE_ftrace.o = -pg
 CFLAGS_REMOVE_insn.o = -pg
+CFLAGS_REMOVE_patch.o = -pg
 endif
 
 CFLAGS_REMOVE_return_address.o = -pg
@@ -38,7 +39,7 @@ obj-$(CONFIG_HAVE_ARM_TWD)	+= smp_twd.o
 obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o insn.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o insn.o
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
-obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-common.o
+obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-common.o patch.o
 ifdef CONFIG_THUMB2_KERNEL
 obj-$(CONFIG_KPROBES)		+= kprobes-thumb.o
 else
arch/arm/kernel/kprobes.c +24 -62

112 lines not on this page · raw message

arch/arm/kernel/patch.c new file +72 -0

72 lines not on this page · raw message

arch/arm/kernel/patch.h new file +7 -0
new file mode 100644
index 0000000..b4731f2
@@ -0,0 +1,7 @@
+#ifndef _ARM_KERNEL_PATCH_H
+#define _ARM_KERNEL_PATCH_H
+
+void patch_text(void *addr, unsigned int insn);
+void __patch_text(void *addr, unsigned int insn);
+
+#endif
Signature · 1 line
1.7.7.3
#9 lore raw

PATCH 3/4 ARM: extract out code patch function from kprobes

Quoted 10 lines · Rabin Vincent

Extract out the code patching code from kprobes so that it can be used from the jump label code. Additionally, the separated code:

- Uses the IS_ENABLED() macros instead of the #ifdefs for THUMB2 support

- Unifies the two separate functions in kprobes, providing one function that uses stop_machine() internally, and one that can be called from stop_machine() directly

Quoted 6 lines

- Patches the text on all CPUs only on processors requiring software broadcasting of cache operations

---

Looks good. I've a couple of minor inline comments below.

Quoted 167 lines
 arch/arm/kernel/Makefile  |    3 +-
 arch/arm/kernel/kprobes.c |   86 ++++++++++++--------------------------------
 arch/arm/kernel/patch.c   |   72 +++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/patch.h   |    7 ++++
 4 files changed, 105 insertions(+), 63 deletions(-)
 create mode 100644 arch/arm/kernel/patch.c
 create mode 100644 arch/arm/kernel/patch.h
arch/arm/kernel/Makefile +2 -1
index a2f34a3..adf02ed 100644
@@ -8,6 +8,7 @@ AFLAGS_head.o        := -DTEXT_OFFSET=$(TEXT_OFFSET)
 ifdef CONFIG_FUNCTION_TRACER
 CFLAGS_REMOVE_ftrace.o = -pg
 CFLAGS_REMOVE_insn.o = -pg
+CFLAGS_REMOVE_patch.o = -pg
 endif
 
 CFLAGS_REMOVE_return_address.o = -pg
@@ -38,7 +39,7 @@ obj-$(CONFIG_HAVE_ARM_TWD)	+= smp_twd.o
 obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o insn.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o insn.o
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
-obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-common.o
+obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-common.o patch.o
 ifdef CONFIG_THUMB2_KERNEL
 obj-$(CONFIG_KPROBES)		+= kprobes-thumb.o
 else
arch/arm/kernel/kprobes.c +24 -62
index 129c116..ab1869d 100644
@@ -29,6 +29,7 @@
 #include <asm/cacheflush.h>
 
 #include "kprobes.h"
+#include "patch.h"
 
 #define MIN_STACK_SIZE(addr) 				\
 	min((unsigned long)MAX_STACK_SIZE,		\
@@ -103,57 +104,33 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
 	return 0;
 }
 
-#ifdef CONFIG_THUMB2_KERNEL
-
-/*
- * For a 32-bit Thumb breakpoint spanning two memory words we need to take
- * special precautions to insert the breakpoint atomically, especially on SMP
- * systems. This is achieved by calling this arming function using stop_machine.
- */
-static int __kprobes set_t32_breakpoint(void *addr)
-{
-	((u16 *)addr)[0] = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION >> 16;
-	((u16 *)addr)[1] = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION & 0xffff;
-	flush_insns(addr, 2*sizeof(u16));
-	return 0;
-}
-
 void __kprobes arch_arm_kprobe(struct kprobe *p)
 {
-	uintptr_t addr = (uintptr_t)p->addr & ~1; /* Remove any Thumb flag */
-
-	if (!is_wide_instruction(p->opcode)) {
-		*(u16 *)addr = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION;
-		flush_insns(addr, sizeof(u16));
-	} else if (addr & 2) {
-		/* A 32-bit instruction spanning two words needs special care */
-		stop_machine(set_t32_breakpoint, (void *)addr, &cpu_online_map);
+	unsigned int brkp;
+	void *addr;
+
+	if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
+		/* Remove any Thumb flag */
+		addr = (void *)((uintptr_t)p->addr & ~1);
+
+		if (is_wide_instruction(p->opcode))
+			brkp = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION;
+		else
+			brkp = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION;
 	} else {
-		/* Word aligned 32-bit instruction can be written atomically */
-		u32 bkp = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION;
-#ifndef __ARMEB__ /* Swap halfwords for little-endian */
-		bkp = (bkp >> 16) | (bkp << 16);
-#endif
-		*(u32 *)addr = bkp;
-		flush_insns(addr, sizeof(u32));
-	}
-}
+		kprobe_opcode_t insn = p->opcode;
 
-#else /* !CONFIG_THUMB2_KERNEL */
+		addr = p->addr;
+		brkp = KPROBE_ARM_BREAKPOINT_INSTRUCTION;
 
-void __kprobes arch_arm_kprobe(struct kprobe *p)
-{
-	kprobe_opcode_t insn = p->opcode;
-	kprobe_opcode_t brkp = KPROBE_ARM_BREAKPOINT_INSTRUCTION;
-	if (insn >= 0xe0000000)
-		brkp |= 0xe0000000;  /* Unconditional instruction */
-	else
-		brkp |= insn & 0xf0000000;  /* Copy condition from insn */
-	*p->addr = brkp;
-	flush_insns(p->addr, sizeof(p->addr[0]));
-}
+		if (insn >= 0xe0000000)
+			brkp |= 0xe0000000;  /* Unconditional instruction */
+		else
+			brkp |= insn & 0xf0000000;  /* Copy condition from insn */
+	}
 
-#endif /* !CONFIG_THUMB2_KERNEL */
+	patch_text(addr, brkp);
+}
 
 /*
  * The actual disarming is done here on each CPU and synchronized using
@@ -166,25 +143,10 @@ void __kprobes arch_arm_kprobe(struct kprobe *p)
 int __kprobes __arch_disarm_kprobe(void *p)
 {
 	struct kprobe *kp = p;
-#ifdef CONFIG_THUMB2_KERNEL
-	u16 *addr = (u16 *)((uintptr_t)kp->addr & ~1);
-	kprobe_opcode_t insn = kp->opcode;
-	unsigned int len;
+	void *addr = (void *)((uintptr_t)kp->addr & ~1);
 
-	if (is_wide_instruction(insn)) {
-		((u16 *)addr)[0] = insn>>16;
-		((u16 *)addr)[1] = insn;
-		len = 2*sizeof(u16);
-	} else {
-		((u16 *)addr)[0] = insn;
-		len = sizeof(u16);
-	}
-	flush_insns(addr, len);
+	__patch_text(addr, kp->opcode);
 
-#else /* !CONFIG_THUMB2_KERNEL */
-	*kp->addr = kp->opcode;
-	flush_insns(kp->addr, sizeof(kp->addr[0]));
-#endif
 	return 0;
 }
 
arch/arm/kernel/patch.c new file +13 -0
new file mode 100644
index 0000000..3f93190
@@ -0,0 +1,72 @@
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/stop_machine.h>
+
+#include <asm/cacheflush.h>
+#include <asm/smp_plat.h>
+
+#include "patch.h"
+
+struct patch {
+	void *addr;
+	unsigned int insn;
+};

arch/arm/kernel/patch.c line 23

Quoted 10 lines
arch/arm/kernel/patch.c new file +10 -0
+
+void __kprobes __patch_text(void *addr, unsigned int insn)
+{
+	bool thumb2 = IS_ENABLED(CONFIG_THUMB2_KERNEL);
+	int size;
+
+	if (thumb2 && !is_wide_instruction(insn)) {
+		*(u16 *)addr = insn;
+		size = sizeof(u16);
+	} else if (thumb2 && ((unsigned long)addr & 2)) {

For pointer arithmetic, is casting to (uintptr_t) preferred to (unsigned long)? (There are several other places in this file where this comment applies.)

arch/arm/kernel/patch.c line 24

Quoted 1 line
arch/arm/kernel/patch.c new file +1 -0
+		u16 *addrw = addr;

Here's some very pedantic nit-picking...
I guess the 'w' in 'addrw' comes from X86 land meaning 'word'? 'h' for 'half-word' would be more appropriate for ARM.

Quoted 38 lines
arch/arm/kernel/patch.c new file +38 -0
+
+		addrw[0] = insn >> 16;
+		addrw[1] = insn & 0xffff;
+
+		size = sizeof(u32);
+	} else {
+#ifndef __ARMEB__
+		if (thumb2)
+			insn = (insn >> 16) | (insn << 16);
+#endif
+
+		*(u32 *)addr = insn;
+		size = sizeof(u32);
+	}
+
+	flush_icache_range((unsigned long)(addr),
+			   (unsigned long)(addr) + size);
+}
+
+static int __kprobes patch_text_stop_machine(void *data)
+{
+	struct patch *patch = data;
+
+	__patch_text(patch->addr, patch->insn);
+
+	return 0;
+}
+
+void __kprobes patch_text(void *addr, unsigned int insn)
+{
+	bool stopmachine;
+	struct patch patch = {
+		.addr = addr,
+		.insn = insn,
+	};
+
+	stopmachine = cache_ops_need_broadcast() ||
+		      (IS_ENABLED(CONFIG_THUMB2_KERNEL)

arch/arm/kernel/patch.c line 72

Quoted 10 lines
arch/arm/kernel/patch.c new file +10 -0
+		       && is_wide_instruction(insn)
+		       && ((unsigned long)addr & 2));
+
+	if (!stopmachine)
+		__patch_text(addr, insn);
+	else if (cache_ops_need_broadcast())
+		stop_machine(patch_text_stop_machine, &patch, cpu_online_mask);
+	else
+		stop_machine(patch_text_stop_machine, &patch, NULL);
+}

This inlines the cache_ops_need_broadcast() code twice. We could instead either cache the result in a local variable, or rewrite if/else clause to something like...

	if (cache_ops_need_broadcast())
		stop_machine(patch_text_stop_machine, &patch, cpu_online_mask);
	else {
		bool straddles_word = IS_ENABLED(CONFIG_THUMB2_KERNEL)
					&& is_wide_instruction(insn)
					&& ((unsigned long)addr & 2))
		if (straddles_word)
			stop_machine(patch_text_stop_machine, &patch, NULL);
		else
			__patch_text(addr, insn);
	}
Quoted 13 lines
arch/arm/kernel/patch.h new file +7 -0
new file mode 100644
index 0000000..b4731f2
@@ -0,0 +1,7 @@
+#ifndef _ARM_KERNEL_PATCH_H
+#define _ARM_KERNEL_PATCH_H
+
+void patch_text(void *addr, unsigned int insn);
+void __patch_text(void *addr, unsigned int insn);
+
+#endif
#10 lore raw

PATCH 3/4 ARM: extract out code patch function from kprobes

Quoted 11 lines · Tixy
Quoted 10 lines · Rabin Vincent

Extract out the code patching code from kprobes so that it can be used from the jump label code. ?Additionally, the separated code:

?- Uses the IS_ENABLED() macros instead of the #ifdefs for THUMB2 ? ?support

?- Unifies the two separate functions in kprobes, providing one function ? ?that uses stop_machine() internally, and one that can be called from ? ?stop_machine() directly

Quoted 8 lines
Quoted 6 lines

?- Patches the text on all CPUs only on processors requiring software ? ?broadcasting of cache operations

---

Looks good. I've a couple of minor inline comments below.

Thank you for the review. I'll address all your comments in v2.

#11 lore raw

PATCH 4/4 ARM: add jump label support

In reply to Rabin Vincent

Add the arch-specific code to support jump labels for ARM and Thumb-2.

Note that to build succesfully it requires (what will be) GCC 4.7.0 because of incomplete support for the '%c' specifier in earlier versions:

	http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48637
---
 arch/arm/Kconfig                  |    1 +
 arch/arm/include/asm/jump_label.h |   41 +++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/Makefile          |    1 +
 arch/arm/kernel/insn.h            |   12 ++++++++++
 arch/arm/kernel/jump_label.c      |   35 +++++++++++++++++++++++++++++++
 5 files changed, 90 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/jump_label.h
 create mode 100644 arch/arm/kernel/jump_label.c
arch/arm/Kconfig +1 -0
index 44789ef..ae6d31c 100644
@@ -9,6 +9,7 @@ config ARM
 	select SYS_SUPPORTS_APM_EMULATION
 	select GENERIC_ATOMIC64 if (CPU_V6 || !CPU_32v6K || !AEABI)
 	select HAVE_OPROFILE if (HAVE_PERF_EVENTS)
+	select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL
 	select HAVE_ARCH_KGDB
 	select HAVE_KPROBES if !XIP_KERNEL
 	select HAVE_KRETPROBES if (HAVE_KPROBES)
arch/arm/include/asm/jump_label.h new file +41 -0

41 lines not on this page · raw message

arch/arm/kernel/Makefile +1 -0
index adf02ed..be200ad 100644
@@ -38,6 +38,7 @@ obj-$(CONFIG_HAVE_ARM_SCU)	+= smp_scu.o
 obj-$(CONFIG_HAVE_ARM_TWD)	+= smp_twd.o
 obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o insn.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o insn.o
+obj-$(CONFIG_JUMP_LABEL)	+= jump_label.o insn.o patch.o
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
 obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-common.o patch.o
 ifdef CONFIG_THUMB2_KERNEL
arch/arm/kernel/insn.h +12 -0
index 994d60f..c32d271 100644
@@ -1,6 +1,18 @@
 #ifndef __ASM_ARM_INSN_H
 #define __ASM_ARM_INSN_H
 
+static inline unsigned long
+arm_gen_nop(void)
+{
+#ifdef CONFIG_THUMB2_KERNEL
+	return 0xf3af8000; /* nop.w */
+#elif defined(CONFIG_CPU_32v6K)
+	return 0xe320f000; /* nop */
+#else
+	return 0xe1a00000; /* mov r0, r0 */
+#endif
+}
+
 unsigned long
 __arm_gen_branch(unsigned long pc, unsigned long addr, bool link);
 
arch/arm/kernel/jump_label.c new file +35 -0
new file mode 100644
index 0000000..7900914
@@ -0,0 +1,35 @@
+#include <linux/kernel.h>
+#include <linux/jump_label.h>
+
+#include "insn.h"
+#include "patch.h"
+
+static void __arch_jump_label_transform(struct jump_entry *entry,
+					enum jump_label_type type,
+					bool is_static)
+{
+	void *addr = (void *)entry->code;
+	unsigned int insn;
+
+	if (type == JUMP_LABEL_ENABLE)
+		insn = arm_gen_branch(entry->code, entry->target);
+	else
+		insn = arm_gen_nop();
+
+	if (is_static)
+		__patch_text(addr, insn);
+	else
+		patch_text(addr, insn);
+}
+
+void arch_jump_label_transform(struct jump_entry *entry,
+			       enum jump_label_type type)
+{
+	__arch_jump_label_transform(entry, type, false);
+}
+
+void arch_jump_label_transform_static(struct jump_entry *entry,
+				      enum jump_label_type type)
+{
+	__arch_jump_label_transform(entry, type, true);
+}
Signature · 1 line
1.7.7.3
#12 lore raw

PATCH 4/4 ARM: add jump label support

Quoted 10 lines · Rabin Vincent

Add the arch-specific code to support jump labels for ARM and Thumb-2.

Note that to build succesfully it requires (what will be) GCC 4.7.0 because of incomplete support for the '%c' specifier in earlier versions:

	http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48637

This appears to imply that with this patch, we're upping the minimum gcc version for successfully building kernels to 4.7.0. That's _way_ too early (I'm using 4.3.5 here and don't have plans to update.)

arch/arm/kernel/insn.h line 12

Quoted 9 lines
arch/arm/kernel/insn.h +9 -0
+static inline unsigned long
+arm_gen_nop(void)
+{
+#ifdef CONFIG_THUMB2_KERNEL
+	return 0xf3af8000; /* nop.w */
+#elif defined(CONFIG_CPU_32v6K)
+	return 0xe320f000; /* nop */
+#else
+	return 0xe1a00000; /* mov r0, r0 */

There really is no point making the distinction between the new nop and the old nop instructions. The difference between them is that the new nop is a true 'no operation' whereas the old nop causes exactly what the instruction says to happen - which in effect is a no-op.

Obviously, doing a true no-operation may require in less power, but if you're using this code, you're debugging, so power usage isn't really a concern. So lets keep the code simple and just use the old nop here. It won't go away.

#13 lore raw

PATCH 4/4 ARM: add jump label support

Quoted 7 lines · Russell King - ARM Linux
Quoted 6 lines · Rabin Vincent

Add the arch-specific code to support jump labels for ARM and Thumb-2.

Note that to build succesfully it requires (what will be) GCC 4.7.0 because of incomplete support for the '%c' specifier in earlier versions:

Quoted 7 lines
Quoted 4 lines
	http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48637

This appears to imply that with this patch, we're upping the minimum gcc version for successfully building kernels to 4.7.0. That's _way_ too early (I'm using 4.3.5 here and don't have plans to update.)

Is there a way to detect that the compiler doesn't support %c? Perhaps we can do that in the same spirit of how we detect asm goto support for jump labels.

Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
#14 lore raw

PATCH 4/4 ARM: add jump label support

Quoted 12 lines · Stephen Boyd
Quoted 11 lines · Russell King - ARM Linux
Quoted 10 lines · Rabin Vincent

Add the arch-specific code to support jump labels for ARM and Thumb-2.

Note that to build succesfully it requires (what will be) GCC 4.7.0 because of incomplete support for the '%c' specifier in earlier versions:

	http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48637
Quoted 8 lines
Quoted 3 lines

This appears to imply that with this patch, we're upping the minimum gcc version for successfully building kernels to 4.7.0. That's _way_ too early (I'm using 4.3.5 here and don't have plans to update.)

Is there a way to detect that the compiler doesn't support %c? Perhaps we can do that in the same spirit of how we detect asm goto support for jump labels.

Right, for jump labels we have a script at: scripts/gcc-goto.sh, which basically just checks if we can compile 'asm goto' without error, so for %c, you could probably have some simple 'asm()' that uses %c, and see if it compiles. Although, I'm not sure if there's a arch specific dir, for such a thing?

thanks,

-Jason

#15 lore raw

PATCH 4/4 ARM: add jump label support

Quoted 9 lines · Jason Baron

Right, for jump labels we have a script at: scripts/gcc-goto.sh, which basically just checks if we can compile 'asm goto' without error, so for %c, you could probably have some simple 'asm()' that uses %c, and see if it compiles. Although, I'm not sure if there's a arch specific dir, for such a thing?

thanks,

-Jason

hmm...actually we could add it to the top level scripts/gcc-goto.sh, that would be fine, I can take a look at that...

-Jason

#16 lore raw

Re: [PATCH 4/4] ARM: add jump label support

Quoted 7 lines · Jason Baron
Quoted 6 lines · Jason Baron

Right, for jump labels we have a script at: scripts/gcc-goto.sh, which basically just checks if we can compile 'asm goto' without error, so for %c, you could probably have some simple 'asm()' that uses %c, and see if it compiles. Although, I'm not sure if there's a arch specific dir, for such a thing?

Quoted 6 lines
Quoted 3 lines

thanks,

-Jason

hmm...actually we could add it to the top level scripts/gcc-goto.sh, that would be fine, I can take a look at that...

Here's a patch. Atleast on MIPS %c means something else so I had to ifdef it for ARM:

8<----------------------------

Quoted 1 line

From ea211ce77e0ea9c5b8cd32b99bfb24e0140f0068 Mon Sep 17 00:00:00 2001

From: Rabin Vincent <[email protected]>
Date: Wed, 23 Nov 2011 20:05:09 +0530
Subject: [PATCH] jump label: detect %c support for ARM

Some versions of ARM GCC don't support the %c specifier fully, but do support asm goto. Since we need the %c to support jump labels on ARM, detect that too in the asm goto detection script to avoid build errors with these versions.

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48637

---
 scripts/gcc-goto.sh |   18 +++++++++++++++++-
 1 files changed, 17 insertions(+), 1 deletions(-)
scripts/gcc-goto.sh +17 -1
index 98cffcb..9b744de 100644
@@ -2,4 +2,20 @@
 # Test for gcc 'asm goto' support
 # Copyright (C) 2010, Jason Baron <[email protected]>
 
-echo "int main(void) { entry: asm goto (\"\"::::entry); return 0; }" | $@ -x c - -c -o /dev/null >/dev/null 2>&1 && echo "y"
+cat << "END" | $@ -x c - -c -o /dev/null >/dev/null 2>&1 && echo "y"
+int main(void)
+{
+#ifdef __arm__
+	/*
+	 * Not related to asm goto, but used by jump label
+	 * and broken on some ARM GCC versions (see GCC Bug 48637).
+	 */
+	static struct { int dummy; int state; } tp;
+	asm ("@ %c0" :: "i" (&tp.state));
+#endif
+
+entry:
+	asm goto ("" :::: entry);
+	return 0;
+}
+END
Signature · 1 line
1.7.7.3
#17 lore raw

Re: [PATCH 4/4] ARM: add jump label support

Quoted 37 lines · Rabin Vincent

Here's a patch. Atleast on MIPS %c means something else so I had to ifdef it for ARM:

8<----------------------------

Quoted 1 line

From ea211ce77e0ea9c5b8cd32b99bfb24e0140f0068 Mon Sep 17 00:00:00 2001

From: Rabin Vincent <[email protected]>
Date: Wed, 23 Nov 2011 20:05:09 +0530
Subject: [PATCH] jump label: detect %c support for ARM

Some versions of ARM GCC don't support the %c specifier fully, but do support asm goto. Since we need the %c to support jump labels on ARM, detect that too in the asm goto detection script to avoid build errors with these versions.

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48637

---
 scripts/gcc-goto.sh |   18 +++++++++++++++++-
 1 files changed, 17 insertions(+), 1 deletions(-)
scripts/gcc-goto.sh +7 -1
index 98cffcb..9b744de 100644
@@ -2,4 +2,20 @@
 # Test for gcc 'asm goto' support
 # Copyright (C) 2010, Jason Baron <[email protected]>
 
-echo "int main(void) { entry: asm goto (\"\"::::entry); return 0; }" | $@ -x c - -c -o /dev/null >/dev/null 2>&1 && echo "y"
+cat << "END" | $@ -x c - -c -o /dev/null >/dev/null 2>&1 && echo "y"
+int main(void)
+{
+#ifdef __arm__
+	/*
+	 * Not related to asm goto, but used by jump label
+	 * and broken on some ARM GCC versions (see GCC Bug 48637).

scripts/gcc-goto.sh line 21

Quoted 10 lines
scripts/gcc-goto.sh +10 -0
+	 */
+	static struct { int dummy; int state; } tp;
+	asm ("@ %c0" :: "i" (&tp.state));
+#endif
+
+entry:
+	asm goto ("" :::: entry);
+	return 0;
+}
+END

Is the presence of asm goto and %c something we want to test together or separately? I suspect it's something we want to do separately.

#18 lore raw

Re: [PATCH 4/4] ARM: add jump label support

Quoted 13 lines · Russell King - ARM Linux
Quoted 12 lines · Rabin Vincent
scripts/gcc-goto.sh +12 -0
+cat << "END" | $@ -x c - -c -o /dev/null >/dev/null 2>&1 && echo "y"
+int main(void)
+{
+#ifdef __arm__
+ ? ? /*
+ ? ? ?* Not related to asm goto, but used by jump label
+ ? ? ?* and broken on some ARM GCC versions (see GCC Bug 48637).
+ ? ? ?*/
+ ? ? static struct { int dummy; int state; } tp;
+ ? ? asm ("@ %c0" :: "i" (&tp.state));
+#endif
+
Quoted 8 lines

scripts/gcc-goto.sh line 21

Quoted 5 lines
scripts/gcc-goto.sh +5 -0
+entry:
+ ? ? asm goto ("" :::: entry);
+ ? ? return 0;
+}
+END

Is the presence of asm goto and %c something we want to test together or separately? ?I suspect it's something we want to do separately.

Why would we want to do it separately?

The only reason I see is because the results of this test are assigned to a variable called CC_HAVE_ASM_GOTO and that is not an entirely accurate name (on ARM) after the addition of this test. If that's the concern, perhaps this variable could just be renamed?

#19 lore raw

PATCH 4/4 ARM: add jump label support

In reply to Russell King - ARM Linux
Quoted 16 lines · Russell King - ARM Linux

arch/arm/kernel/insn.h line 12

Quoted 9 lines · Rabin Vincent
arch/arm/kernel/insn.h +9 -0
+static inline unsigned long
+arm_gen_nop(void)
+{
+#ifdef CONFIG_THUMB2_KERNEL
+	return 0xf3af8000; /* nop.w */
+#elif defined(CONFIG_CPU_32v6K)
+	return 0xe320f000; /* nop */
+#else
+	return 0xe1a00000; /* mov r0, r0 */

There really is no point making the distinction between the new nop and the old nop instructions. The difference between them is that the new nop is a true 'no operation' whereas the old nop causes exactly what the instruction says to happen - which in effect is a no-op.

Quoted 4 lines

Obviously, doing a true no-operation may require in less power, but if you're using this code, you're debugging, so power usage isn't really a concern. So lets keep the code simple and just use the old nop here. It won't go away.

Is "if you're using this code, you're debugging" really correct here? Because the the purpose of jump labels is to reduce the overhead of tracepoints when they aren't activated, i.e. when you're not actively debugging.

#20 lore raw

PATCH 4/4 ARM: add jump label support

Quoted 22 lines · Rabin Vincent
Quoted 16 lines · Russell King - ARM Linux

arch/arm/kernel/insn.h line 12

Quoted 9 lines · Rabin Vincent
arch/arm/kernel/insn.h +9 -0
+static inline unsigned long
+arm_gen_nop(void)
+{
+#ifdef CONFIG_THUMB2_KERNEL
+	return 0xf3af8000; /* nop.w */
+#elif defined(CONFIG_CPU_32v6K)
+	return 0xe320f000; /* nop */
+#else
+	return 0xe1a00000; /* mov r0, r0 */

There really is no point making the distinction between the new nop and the old nop instructions. The difference between them is that the new nop is a true 'no operation' whereas the old nop causes exactly what the instruction says to happen - which in effect is a no-op.

Quoted 4 lines

Obviously, doing a true no-operation may require in less power, but if you're using this code, you're debugging, so power usage isn't really a concern. So lets keep the code simple and just use the old nop here. It won't go away.

Quoted 4 lines

Is "if you're using this code, you're debugging" really correct here? Because the the purpose of jump labels is to reduce the overhead of tracepoints when they aren't activated, i.e. when you're not actively debugging.

Let me repeat: the difference is power. Nothing more nothing less. 'nop' was invented to allow some vendors to 'optimize' nop from a power management point of view. Nothing more, nothing less.

'nop' will be executed as a true 'no operation' where as 'mov r0, r0' will tell the CPU to load the value of r0 and store it back into the register - which involves doing an operation.

There's no difference between "nop" vs "mov r0, r0" in terms of performance. It's purely a power thing.

If you have tracepoints enabled, then you're taking a performance hit anyway by having all those sites inserted. So, the only time you'd sanely enable them is if you're doing development work. You wouldn't enable them for production, unless you had some desire to waste CPU cycles needlessly in your end users products (maybe you're trying to go for the worlds slowest device?)

#21 lore raw

PATCH 4/4 ARM: add jump label support

Quoted 11 lines · Russell King - ARM Linux

Let me repeat: the difference is power. Nothing more nothing less. 'nop' was invented to allow some vendors to 'optimize' nop from a power management point of view. Nothing more, nothing less.

'nop' will be executed as a true 'no operation' where as 'mov r0, r0' will tell the CPU to load the value of r0 and store it back into the register - which involves doing an operation.

There's no difference between "nop" vs "mov r0, r0" in terms of performance. It's purely a power thing.

Quoted 6 lines

If you have tracepoints enabled, then you're taking a performance hit anyway by having all those sites inserted. So, the only time you'd sanely enable them is if you're doing development work. You wouldn't enable them for production, unless you had some desire to waste CPU cycles needlessly in your end users products (maybe you're trying to go for the worlds slowest device?)

I was thinking not of end user products but of distro kernels. For example Debian does have tracepoints built in into the standard kernels (at least on x86).

I will drop the "real nop" usage from v2 of this patchset as you suggested since the power impact of these few nops is vanishingly negligible.

#22 lore raw

PATCH 1/4 ARM: ftrace: use canonical Thumb-2 wide instruction format

In reply to Rabin Vincent
Quoted 38 lines · Rabin Vincent

As commit 592201a9f15 (ARM: Thumb-2: Support Thumb-2 in undefined instruction handler) says:

32-bit Thumb instructions are specified in the form:
((first_half << 16 ) | second_half)
which matches the layout used by the ARM ARM.

Convert the ftrace code to use the same format to avoid the usage of different formats in kernel code.

---
 arch/arm/kernel/ftrace.c |   29 ++++++++++++++++++-----------
 1 files changed, 18 insertions(+), 11 deletions(-)
arch/arm/kernel/ftrace.c +2 -2
index c0062ad..cdceb63 100644
@@ -19,7 +19,7 @@
 #include <asm/ftrace.h>
 
 #ifdef CONFIG_THUMB2_KERNEL
-#define	NOP		0xeb04f85d	/* pop.w {lr} */
+#define	NOP		0xf85deb04	/* pop.w {lr} */
 #else
 #define	NOP		0xe8bd4000	/* pop {lr} */
 #endif
@@ -88,7 +88,7 @@ static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr,
 	if (link)
 		second |= 1 << 14;
 
-	return (second << 16) | first;
+	return (first << 16) | second;
 }
 #else
 static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr,

arch/arm/kernel/ftrace.c static int ftrace_modify_code(unsigned long pc, unsigned long old, · line 131

Quoted 9 lines
arch/arm/kernel/ftrace.c +4 -2
@@ -125,11 +125,20 @@ static int ftrace_modify_code(unsigned long pc, unsigned long old,
 {
 	unsigned long replaced;
 
-	if (probe_kernel_read(&replaced, (void *)pc, MCOUNT_INSN_SIZE))
-		return -EFAULT;
+#ifndef __ARMEB__
+	if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
+		old = (old >> 16) | (old << 16);
+		new = (new >> 16) | (new << 16);

I think swahw32() in <linux/swab.h> can be used for this operation.

Really though, we need a common set of "load and store instruction" macros, rather than duplicating this knowledge everywhere.

In particular, we really want those macros to encapsulate the #ifdef __ARMEB__ stuff.

Quoted 31 lines
arch/arm/kernel/ftrace.c +11 -5
+	}
+#endif
 
-	if (replaced != old)
-		return -EINVAL;
+	if (old) {
+		if (probe_kernel_read(&replaced, (void *)pc, MCOUNT_INSN_SIZE))
+			return -EFAULT;
+
+		if (replaced != old)
+			return -EINVAL;
+	}
 
 	if (probe_kernel_write((void *)pc, &new, MCOUNT_INSN_SIZE))
 		return -EPERM;
@@ -141,23 +150,21 @@ static int ftrace_modify_code(unsigned long pc, unsigned long old,
 
 int ftrace_update_ftrace_func(ftrace_func_t func)
 {
-	unsigned long pc, old;
+	unsigned long pc;
 	unsigned long new;
 	int ret;
 
 	pc = (unsigned long)&ftrace_call;
-	memcpy(&old, &ftrace_call, MCOUNT_INSN_SIZE);
 	new = ftrace_call_replace(pc, (unsigned long)func);
 
-	ret = ftrace_modify_code(pc, old, new);
+	ret = ftrace_modify_code(pc, 0, new);
 

arch/arm/kernel/ftrace.c static int ftrace_modify_code(unsigned long pc, unsigned long old, · line 169

Quoted 10 lines
arch/arm/kernel/ftrace.c +1 -2
 #ifdef CONFIG_OLD_MCOUNT
 	if (!ret) {
 		pc = (unsigned long)&ftrace_call_old;
-		memcpy(&old, &ftrace_call_old, MCOUNT_INSN_SIZE);
 		new = ftrace_call_replace(pc, (unsigned long)func);
 
-		ret = ftrace_modify_code(pc, old, new);
+		ret = ftrace_modify_code(pc, 0, new);
 	}
 #endif

Why don't we check the old value any more?

It would be good to see something in comments or the commit log clarifying this.

To what extent can this be unified with the kprobes functionality? (if you've already done that, ignore this comment -- I confess I don't understand all the details of these patches...)

Cheers
---Dave

#23 lore raw

PATCH 1/4 ARM: ftrace: use canonical Thumb-2 wide instruction format

Quoted 7 lines · Dave Martin
Quoted 4 lines · Rabin Vincent

+#ifndef __ARMEB__
+ ? ? if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
+ ? ? ? ? ? ? old = (old >> 16) | (old << 16);
+ ? ? ? ? ? ? new = (new >> 16) | (new << 16);

I think swahw32() in <linux/swab.h> can be used for this operation.

Yes.

Quoted 2 lines

Really though, we need a common set of "load and store instruction" macros, rather than duplicating this knowledge everywhere.

Yes, though this ftrace code may not be able to use them directly since it uses probe_kernel_read()/write().

Quoted 2 lines

In particular, we really want those macros to encapsulate the #ifdef __ARMEB__ stuff.

The ifdef for ARMEB is a bit jarring, especially since now we can even get rid of ifdefs for CONFIG_THUMB2_KERNEL. An arm_is_bigendian() or somesuch macro in a header would be preferable for this, I think.

Quoted 39 lines
Quoted 39 lines

+ ? ? }

(quoted diff) +8 -2
+#endif
 
- ? ? if (replaced != old)
- ? ? ? ? ? ? return -EINVAL;
+ ? ? if (old) {
+ ? ? ? ? ? ? if (probe_kernel_read(&replaced, (void *)pc, MCOUNT_INSN_SIZE))
+ ? ? ? ? ? ? ? ? ? ? return -EFAULT;
+
+ ? ? ? ? ? ? if (replaced != old)
+ ? ? ? ? ? ? ? ? ? ? return -EINVAL;
+ ? ? }

? ? ? if (probe_kernel_write((void *)pc, &new, MCOUNT_INSN_SIZE)) ? ? ? ? ? ? ? return -EPERM;

(quoted diff) +2 -3
@@ -141,23 +150,21 @@ static int ftrace_modify_code(unsigned long pc, unsigned long old,
 
 ?int ftrace_update_ftrace_func(ftrace_func_t func)
 ?{
- ? ? unsigned long pc, old;
+ ? ? unsigned long pc;
 ? ? ? unsigned long new;
 ? ? ? int ret;
 
 ? ? ? pc = (unsigned long)&ftrace_call;
- ? ? memcpy(&old, &ftrace_call, MCOUNT_INSN_SIZE);
 ? ? ? new = ftrace_call_replace(pc, (unsigned long)func);
 
- ? ? ret = ftrace_modify_code(pc, old, new);
+ ? ? ret = ftrace_modify_code(pc, 0, new);
 

?#ifdef CONFIG_OLD_MCOUNT
? ? ? if (!ret) {
? ? ? ? ? ? ? pc = (unsigned long)&ftrace_call_old;
- ? ? ? ? ? ? memcpy(&old, &ftrace_call_old, MCOUNT_INSN_SIZE); ? ? ? ? ? ? ? new = ftrace_call_replace(pc, (unsigned long)func);

Quoted 6 lines
Quoted 4 lines

- ? ? ? ? ? ? ret = ftrace_modify_code(pc, old, new);
+ ? ? ? ? ? ? ret = ftrace_modify_code(pc, 0, new); ? ? ? }
?#endif

Why don't we check the old value any more?

The ftrace_modify_code() checks the location its modifying to ensure that the old value was what it expected it to be -- this is useful for example if we have problems with the recordmcount code that makes it record wrong locations.

In this particular case however, what we're modifying is not code at an mcount location but some of the code in entry-common.S which is at the location of a known symbol. The safety check for the old value was quite useless in this case because we were just memcpy()ing the content of the location and passing that in as the old value.

The reason it's done in this patch is that because of converting to the canonical format, we'd need an additional ifdef-ARMEB/swahw32() here after doing the memcpy(), for the memcmp() in ftrace_modify_code() to succeed. Instead, we just avoid the comparison for this case.

Quoted 2 lines

It would be good to see something in comments or the commit log clarifying this.

Agreed. Perhaps it should even be done in a separate patch.

Quoted 4 lines

To what extent can this be unified with the kprobes functionality? (if you've already done that, ignore this comment -- I confess I don't understand all the details of these patches...)

With this patch series:
- kprobes and jump labels use the common text patching code
- jump labels and ftrace use the common instruction generation code

kprobes and ftrace, however, don't share anything. We could make ftrace use __patch_text(), although that would only save half-a-dozen lines of code and also remove the use of probe_kernel_write(), which all the other arch ftrace implementations use.

#24 lore raw

PATCH 1/4 ARM: ftrace: use canonical Thumb-2 wide instruction format

Quoted 9 lines · Rabin Vincent
Quoted 7 lines · Dave Martin
Quoted 4 lines · Rabin Vincent

+#ifndef __ARMEB__
+ ? ? if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
+ ? ? ? ? ? ? old = (old >> 16) | (old << 16);
+ ? ? ? ? ? ? new = (new >> 16) | (new << 16);

I think swahw32() in <linux/swab.h> can be used for this operation.

Quoted 7 lines

Yes.

Quoted 2 lines

Really though, we need a common set of "load and store instruction" macros, rather than duplicating this knowledge everywhere.

Yes, though this ftrace code may not be able to use them directly since it uses probe_kernel_read()/write().

In that case what we would want are macros to convert instructions between integer and in-memory representation, rather than conflating all that together in some memory accessor macros.

How about:

#include <linux/swab.h>

#ifdef __ARMEB__
#define insn_to_mem_arm(insn) swab32(insn)
#define insn_to_mem_thumb16(insn) swab16(insn)
#define insn_to_mem_thumb32(insn) swab16(insn)
#else
#define insn_to_mem_arm(insn) (insn)
#define insn_to_mem_thumb16(insn) (insn)
#define insn_to_mem_thumb32(insn) shahw32(insn)
#endif

#define mem_to_insn_arm(insn) insn_to_mem_asm(insn) #define mem_to_insn_thumb16(insn) insn_to_mem_thumb16(insn) #define mem_to_insn_thumb32(insn) insn_to_mem_thumb32(insn)

Because these are all straightforward swap operations, we don't really need separate forwards and backwards operations, but having the separate names may make calling code easier to read.

I'm still not sure exactly which header this belongs in, but it's a core ARM thing; not local to kprobes or ftrace, etc.

If this looks sensible, it would also make sense for Junxiao Bi to build his big-endian fixes on it.

Cheers
---Dave

#25 lore raw

PATCH 1/4 ARM: ftrace: use canonical Thumb-2 wide instruction format

Quoted 44 lines · Dave Martin
Quoted 11 lines · Rabin Vincent
Quoted 9 lines · Dave Martin

arch/arm/kernel/ftrace.c static int ftrace_modify_code(unsigned long pc, unsigned long old,

Quoted 5 lines · Rabin Vincent
arch/arm/kernel/ftrace.c +4 -0
+#ifndef __ARMEB__
+     if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
+             old = (old>>  16) | (old<<  16);
+             new = (new>>  16) | (new<<  16);

I think swahw32() in<linux/swab.h> can be used for this operation.

Quoted 9 lines

Yes.

Quoted 3 lines

Really though, we need a common set of "load and store instruction" macros, rather than duplicating this knowledge everywhere.

Yes, though this ftrace code may not be able to use them directly since it uses probe_kernel_read()/write().

In that case what we would want are macros to convert instructions between integer and in-memory representation, rather than conflating all that together in some memory accessor macros.

How about:

#include<linux/swab.h>

#ifdef __ARMEB__
#define insn_to_mem_arm(insn) swab32(insn)
#define insn_to_mem_thumb16(insn) swab16(insn)
#define insn_to_mem_thumb32(insn) swab16(insn)
#else
#define insn_to_mem_arm(insn) (insn)
#define insn_to_mem_thumb16(insn) (insn)
#define insn_to_mem_thumb32(insn) shahw32(insn)
#endif

#define mem_to_insn_arm(insn) insn_to_mem_asm(insn) #define mem_to_insn_thumb16(insn) insn_to_mem_thumb16(insn) #define mem_to_insn_thumb32(insn) insn_to_mem_thumb32(insn)

Quoted 8 lines

Because these are all straightforward swap operations, we don't really need separate forwards and backwards operations, but having the separate names may make calling code easier to read.

I'm still not sure exactly which header this belongs in, but it's a core ARM thing; not local to kprobes or ftrace, etc.

Yes, these macros are useful for be8 support. Do they deserve a separated header file as there is no appropriate one for them now?

Quoted 7 lines

If this looks sensible, it would also make sense for Junxiao Bi to build his big-endian fixes on it.

Cheers
---Dave

#26 lore raw

PATCH 1/4 ARM: ftrace: use canonical Thumb-2 wide instruction format

Quoted 41 lines · Bi Junxiao
Quoted 40 lines · Dave Martin
Quoted 6 lines · Rabin Vincent
Quoted 5 lines · Dave Martin
Quoted 4 lines · Rabin Vincent
arch/arm/kernel/ftrace.c +4 -0
+#ifndef __ARMEB__
+     if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
+             old = (old>>  16) | (old<<  16);
+             new = (new>>  16) | (new<<  16);
Quoted 7 lines
Quoted 1 line

I think swahw32() in<linux/swab.h> can be used for this operation.

Yes.

Quoted 2 lines

Really though, we need a common set of "load and store instruction" macros, rather than duplicating this knowledge everywhere.

Yes, though this ftrace code may not be able to use them directly since it uses probe_kernel_read()/write().

In that case what we would want are macros to convert instructions between integer and in-memory representation, rather than conflating all that together in some memory accessor macros.

How about:

#include<linux/swab.h>

#ifdef __ARMEB__
#define insn_to_mem_arm(insn) swab32(insn)
#define insn_to_mem_thumb16(insn) swab16(insn)
#define insn_to_mem_thumb32(insn) swab16(insn)
#else
#define insn_to_mem_arm(insn) (insn)
#define insn_to_mem_thumb16(insn) (insn)
#define insn_to_mem_thumb32(insn) shahw32(insn)
#endif

#define mem_to_insn_arm(insn) insn_to_mem_asm(insn) #define mem_to_insn_thumb16(insn) insn_to_mem_thumb16(insn) #define mem_to_insn_thumb32(insn) insn_to_mem_thumb32(insn)

Because these are all straightforward swap operations, we don't really need separate forwards and backwards operations, but having the separate names may make calling code easier to read.

Quoted 7 lines
Quoted 3 lines

I'm still not sure exactly which header this belongs in, but it's a core ARM thing; not local to kprobes or ftrace, etc.

Yes, these macros are useful for be8 support. Do they deserve a separated header file as there is no appropriate one for them now?

Quoted 2 lines

If this looks sensible, it would also make sense for Junxiao Bi to build his big-endian fixes on it.

I don't really see a header where this stuff fits.

I'll post an RFC.

Cheers
---Dave