diff options
| author | David A. Long <dave.long@linaro.org> | 2014-03-07 11:23:04 -0500 |
|---|---|---|
| committer | David A. Long <dave.long@linaro.org> | 2014-03-18 16:39:40 -0400 |
| commit | c7edc9e326d53ca5ef9bed82de0740c6b107d55b (patch) | |
| tree | 262d901b5e4d61930d5bc8ff68b9ddd807e3f956 | |
| parent | b4cd605ca92d9a8a2f71355cb45dd943ebcb0c97 (diff) | |
ARM: add uprobes support
Using Rabin Vincent's ARM uprobes patches as a base, enable uprobes
support on ARM.
Caveats:
- Thumb is not supported
Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: David A. Long <dave.long@linaro.org>
| -rw-r--r-- | arch/arm/Kconfig | 3 | ||||
| -rw-r--r-- | arch/arm/include/asm/ptrace.h | 6 | ||||
| -rw-r--r-- | arch/arm/include/asm/thread_info.h | 5 | ||||
| -rw-r--r-- | arch/arm/include/asm/uprobes.h | 45 | ||||
| -rw-r--r-- | arch/arm/kernel/Makefile | 1 | ||||
| -rw-r--r-- | arch/arm/kernel/signal.c | 4 | ||||
| -rw-r--r-- | arch/arm/kernel/uprobes-arm.c | 234 | ||||
| -rw-r--r-- | arch/arm/kernel/uprobes.c | 210 | ||||
| -rw-r--r-- | arch/arm/kernel/uprobes.h | 35 |
9 files changed, 542 insertions, 1 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e25419817791..4d05bb93714a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
| @@ -207,6 +207,9 @@ config ZONE_DMA | |||
| 207 | config NEED_DMA_MAP_STATE | 207 | config NEED_DMA_MAP_STATE |
| 208 | def_bool y | 208 | def_bool y |
| 209 | 209 | ||
| 210 | config ARCH_SUPPORTS_UPROBES | ||
| 211 | def_bool y | ||
| 212 | |||
| 210 | config ARCH_HAS_DMA_SET_COHERENT_MASK | 213 | config ARCH_HAS_DMA_SET_COHERENT_MASK |
| 211 | bool | 214 | bool |
| 212 | 215 | ||
diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h index 04c99f36ff7f..ee688b0a13c3 100644 --- a/arch/arm/include/asm/ptrace.h +++ b/arch/arm/include/asm/ptrace.h | |||
| @@ -80,6 +80,12 @@ static inline long regs_return_value(struct pt_regs *regs) | |||
| 80 | 80 | ||
| 81 | #define instruction_pointer(regs) (regs)->ARM_pc | 81 | #define instruction_pointer(regs) (regs)->ARM_pc |
| 82 | 82 | ||
| 83 | static inline void instruction_pointer_set(struct pt_regs *regs, | ||
| 84 | unsigned long val) | ||
| 85 | { | ||
| 86 | instruction_pointer(regs) = val; | ||
| 87 | } | ||
| 88 | |||
| 83 | #ifdef CONFIG_SMP | 89 | #ifdef CONFIG_SMP |
| 84 | extern unsigned long profile_pc(struct pt_regs *regs); | 90 | extern unsigned long profile_pc(struct pt_regs *regs); |
| 85 | #else | 91 | #else |
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index 71a06b293489..f989d7c22dc5 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h | |||
| @@ -153,6 +153,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *, | |||
| 153 | #define TIF_SIGPENDING 0 | 153 | #define TIF_SIGPENDING 0 |
| 154 | #define TIF_NEED_RESCHED 1 | 154 | #define TIF_NEED_RESCHED 1 |
| 155 | #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ | 155 | #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ |
| 156 | #define TIF_UPROBE 7 | ||
| 156 | #define TIF_SYSCALL_TRACE 8 | 157 | #define TIF_SYSCALL_TRACE 8 |
| 157 | #define TIF_SYSCALL_AUDIT 9 | 158 | #define TIF_SYSCALL_AUDIT 9 |
| 158 | #define TIF_SYSCALL_TRACEPOINT 10 | 159 | #define TIF_SYSCALL_TRACEPOINT 10 |
| @@ -165,6 +166,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *, | |||
| 165 | #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) | 166 | #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) |
| 166 | #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) | 167 | #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) |
| 167 | #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) | 168 | #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) |
| 169 | #define _TIF_UPROBE (1 << TIF_UPROBE) | ||
| 168 | #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) | 170 | #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) |
| 169 | #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) | 171 | #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) |
| 170 | #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) | 172 | #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) |
| @@ -178,7 +180,8 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *, | |||
| 178 | /* | 180 | /* |
| 179 | * Change these and you break ASM code in entry-common.S | 181 | * Change these and you break ASM code in entry-common.S |
| 180 | */ | 182 | */ |
| 181 | #define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_RESUME) | 183 | #define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \ |
| 184 | _TIF_NOTIFY_RESUME | _TIF_UPROBE) | ||
| 182 | 185 | ||
| 183 | #endif /* __KERNEL__ */ | 186 | #endif /* __KERNEL__ */ |
| 184 | #endif /* __ASM_ARM_THREAD_INFO_H */ | 187 | #endif /* __ASM_ARM_THREAD_INFO_H */ |
diff --git a/arch/arm/include/asm/uprobes.h b/arch/arm/include/asm/uprobes.h new file mode 100644 index 000000000000..9472c20b7d49 --- /dev/null +++ b/arch/arm/include/asm/uprobes.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Rabin Vincent <rabin at rab.in> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef _ASM_UPROBES_H | ||
| 10 | #define _ASM_UPROBES_H | ||
| 11 | |||
| 12 | #include <asm/probes.h> | ||
| 13 | #include <asm/opcodes.h> | ||
| 14 | |||
| 15 | typedef u32 uprobe_opcode_t; | ||
| 16 | |||
| 17 | #define MAX_UINSN_BYTES 4 | ||
| 18 | #define UPROBE_XOL_SLOT_BYTES 64 | ||
| 19 | |||
| 20 | #define UPROBE_SWBP_ARM_INSN 0xe7f001f9 | ||
| 21 | #define UPROBE_SS_ARM_INSN 0xe7f001fa | ||
| 22 | #define UPROBE_SWBP_INSN __opcode_to_mem_arm(UPROBE_SWBP_ARM_INSN) | ||
| 23 | #define UPROBE_SWBP_INSN_SIZE 4 | ||
| 24 | |||
| 25 | struct arch_uprobe_task { | ||
| 26 | u32 backup; | ||
| 27 | unsigned long saved_trap_no; | ||
| 28 | }; | ||
| 29 | |||
| 30 | struct arch_uprobe { | ||
| 31 | u8 insn[MAX_UINSN_BYTES]; | ||
| 32 | unsigned long ixol[2]; | ||
| 33 | uprobe_opcode_t bpinsn; | ||
| 34 | bool simulate; | ||
| 35 | u32 pcreg; | ||
| 36 | void (*prehandler)(struct arch_uprobe *auprobe, | ||
| 37 | struct arch_uprobe_task *autask, | ||
| 38 | struct pt_regs *regs); | ||
| 39 | void (*posthandler)(struct arch_uprobe *auprobe, | ||
| 40 | struct arch_uprobe_task *autask, | ||
| 41 | struct pt_regs *regs); | ||
| 42 | struct arch_probes_insn asi; | ||
| 43 | }; | ||
| 44 | |||
| 45 | #endif | ||
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index bb739f28dd80..a766bcbaf8ad 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
| @@ -50,6 +50,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o | |||
| 50 | obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o | 50 | obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o |
| 51 | obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o | 51 | obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o |
| 52 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o | 52 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o |
| 53 | obj-$(CONFIG_UPROBES) += probes.o probes-arm.o uprobes.o uprobes-arm.o | ||
| 53 | obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o | 54 | obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o |
| 54 | ifdef CONFIG_THUMB2_KERNEL | 55 | ifdef CONFIG_THUMB2_KERNEL |
| 55 | obj-$(CONFIG_KPROBES) += kprobes-thumb.o probes-thumb.o | 56 | obj-$(CONFIG_KPROBES) += kprobes-thumb.o probes-thumb.o |
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 04d63880037f..bd1983437205 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <linux/personality.h> | 13 | #include <linux/personality.h> |
| 14 | #include <linux/uaccess.h> | 14 | #include <linux/uaccess.h> |
| 15 | #include <linux/tracehook.h> | 15 | #include <linux/tracehook.h> |
| 16 | #include <linux/uprobes.h> | ||
| 16 | 17 | ||
| 17 | #include <asm/elf.h> | 18 | #include <asm/elf.h> |
| 18 | #include <asm/cacheflush.h> | 19 | #include <asm/cacheflush.h> |
| @@ -590,6 +591,9 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) | |||
| 590 | return restart; | 591 | return restart; |
| 591 | } | 592 | } |
| 592 | syscall = 0; | 593 | syscall = 0; |
| 594 | } else if (thread_flags & _TIF_UPROBE) { | ||
| 595 | clear_thread_flag(TIF_UPROBE); | ||
| 596 | uprobe_notify_resume(regs); | ||
| 593 | } else { | 597 | } else { |
| 594 | clear_thread_flag(TIF_NOTIFY_RESUME); | 598 | clear_thread_flag(TIF_NOTIFY_RESUME); |
| 595 | tracehook_notify_resume(regs); | 599 | tracehook_notify_resume(regs); |
diff --git a/arch/arm/kernel/uprobes-arm.c b/arch/arm/kernel/uprobes-arm.c new file mode 100644 index 000000000000..d3b655ff17da --- /dev/null +++ b/arch/arm/kernel/uprobes-arm.c | |||
| @@ -0,0 +1,234 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Rabin Vincent <rabin at rab.in> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/kernel.h> | ||
| 10 | #include <linux/types.h> | ||
| 11 | #include <linux/stddef.h> | ||
| 12 | #include <linux/wait.h> | ||
| 13 | #include <linux/uprobes.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | |||
| 16 | #include "probes.h" | ||
| 17 | #include "probes-arm.h" | ||
| 18 | #include "uprobes.h" | ||
| 19 | |||
| 20 | static int uprobes_substitute_pc(unsigned long *pinsn, u32 oregs) | ||
| 21 | { | ||
| 22 | probes_opcode_t insn = __mem_to_opcode_arm(*pinsn); | ||
| 23 | probes_opcode_t temp; | ||
| 24 | probes_opcode_t mask; | ||
| 25 | int freereg; | ||
| 26 | u32 free = 0xffff; | ||
| 27 | u32 regs; | ||
