diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2015-07-29 16:44:53 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2015-09-03 06:08:05 -0400 |
commit | 40e084a506eba78310cd5e8ab700fd1226c6130a (patch) | |
tree | a7d19eea2c8a72c78205f2403be2ea3cde24ce63 | |
parent | e3b28831c18c6c95c51b6bb717fa116d2b658ba9 (diff) |
MIPS: Add uprobes support.
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r-- | arch/mips/Kconfig | 4 | ||||
-rw-r--r-- | arch/mips/include/asm/kdebug.h | 4 | ||||
-rw-r--r-- | arch/mips/include/asm/ptrace.h | 80 | ||||
-rw-r--r-- | arch/mips/include/asm/thread_info.h | 5 | ||||
-rw-r--r-- | arch/mips/include/asm/uprobes.h | 58 | ||||
-rw-r--r-- | arch/mips/include/uapi/asm/break.h | 2 | ||||
-rw-r--r-- | arch/mips/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/mips/kernel/ptrace.c | 88 | ||||
-rw-r--r-- | arch/mips/kernel/signal.c | 4 | ||||
-rw-r--r-- | arch/mips/kernel/traps.c | 12 | ||||
-rw-r--r-- | arch/mips/kernel/uprobes.c | 341 |
11 files changed, 597 insertions, 2 deletions
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 7e182424f119..7a9a2554fa00 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig | |||
@@ -1,6 +1,7 @@ | |||
1 | config MIPS | 1 | config MIPS |
2 | bool | 2 | bool |
3 | default y | 3 | default y |
4 | select ARCH_SUPPORTS_UPROBES | ||
4 | select ARCH_MIGHT_HAVE_PC_PARPORT | 5 | select ARCH_MIGHT_HAVE_PC_PARPORT |
5 | select ARCH_MIGHT_HAVE_PC_SERIO | 6 | select ARCH_MIGHT_HAVE_PC_SERIO |
6 | select ARCH_USE_CMPXCHG_LOCKREF if 64BIT | 7 | select ARCH_USE_CMPXCHG_LOCKREF if 64BIT |
@@ -1041,6 +1042,9 @@ config FW_CFE | |||
1041 | config ARCH_DMA_ADDR_T_64BIT | 1042 | config ARCH_DMA_ADDR_T_64BIT |
1042 | def_bool (HIGHMEM && ARCH_PHYS_ADDR_T_64BIT) || 64BIT | 1043 | def_bool (HIGHMEM && ARCH_PHYS_ADDR_T_64BIT) || 64BIT |
1043 | 1044 | ||
1045 | config ARCH_SUPPORTS_UPROBES | ||
1046 | bool | ||
1047 | |||
1044 | config DMA_MAYBE_COHERENT | 1048 | config DMA_MAYBE_COHERENT |
1045 | select DMA_NONCOHERENT | 1049 | select DMA_NONCOHERENT |
1046 | bool | 1050 | bool |
diff --git a/arch/mips/include/asm/kdebug.h b/arch/mips/include/asm/kdebug.h index cba22ab7ad4d..8e3d08e739c1 100644 --- a/arch/mips/include/asm/kdebug.h +++ b/arch/mips/include/asm/kdebug.h | |||
@@ -11,7 +11,9 @@ enum die_val { | |||
11 | DIE_PAGE_FAULT, | 11 | DIE_PAGE_FAULT, |
12 | DIE_BREAK, | 12 | DIE_BREAK, |
13 | DIE_SSTEPBP, | 13 | DIE_SSTEPBP, |
14 | DIE_MSAFP | 14 | DIE_MSAFP, |
15 | DIE_UPROBE, | ||
16 | DIE_UPROBE_XOL, | ||
15 | }; | 17 | }; |
16 | 18 | ||
17 | #endif /* _ASM_MIPS_KDEBUG_H */ | 19 | #endif /* _ASM_MIPS_KDEBUG_H */ |
diff --git a/arch/mips/include/asm/ptrace.h b/arch/mips/include/asm/ptrace.h index ffc320389f40..f6fc6aac5496 100644 --- a/arch/mips/include/asm/ptrace.h +++ b/arch/mips/include/asm/ptrace.h | |||
@@ -14,11 +14,16 @@ | |||
14 | #include <linux/linkage.h> | 14 | #include <linux/linkage.h> |
15 | #include <linux/types.h> | 15 | #include <linux/types.h> |
16 | #include <asm/isadep.h> | 16 | #include <asm/isadep.h> |
17 | #include <asm/page.h> | ||
18 | #include <asm/thread_info.h> | ||
17 | #include <uapi/asm/ptrace.h> | 19 | #include <uapi/asm/ptrace.h> |
18 | 20 | ||
19 | /* | 21 | /* |
20 | * This struct defines the way the registers are stored on the stack during a | 22 | * This struct defines the way the registers are stored on the stack during a |
21 | * system call/exception. As usual the registers k0/k1 aren't being saved. | 23 | * system call/exception. As usual the registers k0/k1 aren't being saved. |
24 | * | ||
25 | * If you add a register here, also add it to regoffset_table[] in | ||
26 | * arch/mips/kernel/ptrace.c. | ||
22 | */ | 27 | */ |
23 | struct pt_regs { | 28 | struct pt_regs { |
24 | #ifdef CONFIG_32BIT | 29 | #ifdef CONFIG_32BIT |
@@ -43,8 +48,83 @@ struct pt_regs { | |||
43 | unsigned long long mpl[6]; /* MTM{0-5} */ | 48 | unsigned long long mpl[6]; /* MTM{0-5} */ |
44 | unsigned long long mtp[6]; /* MTP{0-5} */ | 49 | unsigned long long mtp[6]; /* MTP{0-5} */ |
45 | #endif | 50 | #endif |
51 | unsigned long __last[0]; | ||
46 | } __aligned(8); | 52 | } __aligned(8); |
47 | 53 | ||
54 | static inline unsigned long kernel_stack_pointer(struct pt_regs *regs) | ||
55 | { | ||
56 | return regs->regs[31]; | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * Don't use asm-generic/ptrace.h it defines FP accessors that don't make | ||
61 | * sense on MIPS. We rather want an error if they get invoked. | ||
62 | */ | ||
63 | |||
64 | static inline void instruction_pointer_set(struct pt_regs *regs, | ||
65 | unsigned long val) | ||
66 | { | ||
67 | regs->cp0_epc = val; | ||
68 | } | ||
69 | |||
70 | /* Query offset/name of register from its name/offset */ | ||
71 | extern int regs_query_register_offset(const char *name); | ||
72 | #define MAX_REG_OFFSET (offsetof(struct pt_regs, __last)) | ||
73 | |||
74 | /** | ||
75 | * regs_get_register() - get register value from its offset | ||
76 | * @regs: pt_regs from which register value is gotten. | ||
77 | * @offset: offset number of the register. | ||
78 | * | ||
79 | * regs_get_register returns the value of a register. The @offset is the | ||
80 | * offset of the register in struct pt_regs address which specified by @regs. | ||
81 | * If @offset is bigger than MAX_REG_OFFSET, this returns 0. | ||
82 | */ | ||
83 | static inline unsigned long regs_get_register(struct pt_regs *regs, | ||
84 | unsigned int offset) | ||
85 | { | ||
86 | if (unlikely(offset > MAX_REG_OFFSET)) | ||
87 | return 0; | ||
88 | |||
89 | return *(unsigned long *)((unsigned long)regs + offset); | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * regs_within_kernel_stack() - check the address in the stack | ||
94 | * @regs: pt_regs which contains kernel stack pointer. | ||
95 | * @addr: address which is checked. | ||
96 | * | ||
97 | * regs_within_kernel_stack() checks @addr is within the kernel stack page(s). | ||
98 | * If @addr is within the kernel stack, it returns true. If not, returns false. | ||
99 | */ | ||
100 | static inline int regs_within_kernel_stack(struct pt_regs *regs, | ||
101 | unsigned long addr) | ||
102 | { | ||
103 | return ((addr & ~(THREAD_SIZE - 1)) == | ||
104 | (kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))); | ||
105 | } | ||
106 | |||
107 | /** | ||
108 | * regs_get_kernel_stack_nth() - get Nth entry of the stack | ||
109 | * @regs: pt_regs which contains kernel stack pointer. | ||
110 | * @n: stack entry number. | ||
111 | * | ||
112 | * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which | ||
113 | * is specified by @regs. If the @n th entry is NOT in the kernel stack, | ||
114 | * this returns 0. | ||
115 | */ | ||
116 | static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, | ||
117 | unsigned int n) | ||
118 | { | ||
119 | unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs); | ||
120 | |||
121 | addr += n; | ||
122 | if (regs_within_kernel_stack(regs, (unsigned long)addr)) | ||
123 | return *addr; | ||
124 | else | ||
125 | return 0; | ||
126 | } | ||
127 | |||
48 | struct task_struct; | 128 | struct task_struct; |
49 | 129 | ||
50 | extern int ptrace_getregs(struct task_struct *child, | 130 | extern int ptrace_getregs(struct task_struct *child, |
diff --git a/arch/mips/include/asm/thread_info.h b/arch/mips/include/asm/thread_info.h index 9c0014e87c17..e309d8fcb516 100644 --- a/arch/mips/include/asm/thread_info.h +++ b/arch/mips/include/asm/thread_info.h | |||
@@ -99,6 +99,7 @@ static inline struct thread_info *current_thread_info(void) | |||
99 | #define TIF_SYSCALL_AUDIT 3 /* syscall auditing active */ | 99 | #define TIF_SYSCALL_AUDIT 3 /* syscall auditing active */ |
100 | #define TIF_SECCOMP 4 /* secure computing */ | 100 | #define TIF_SECCOMP 4 /* secure computing */ |
101 | #define TIF_NOTIFY_RESUME 5 /* callback before returning to user */ | 101 | #define TIF_NOTIFY_RESUME 5 /* callback before returning to user */ |
102 | #define TIF_UPROBE 6 /* breakpointed or singlestepping */ | ||
102 | #define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal() */ | 103 | #define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal() */ |
103 | #define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */ | 104 | #define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */ |
104 | #define TIF_MEMDIE 18 /* is terminating due to OOM killer */ | 105 | #define TIF_MEMDIE 18 /* is terminating due to OOM killer */ |
@@ -122,6 +123,7 @@ static inline struct thread_info *current_thread_info(void) | |||
122 | #define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT) | 123 | #define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT) |
123 | #define _TIF_SECCOMP (1<<TIF_SECCOMP) | 124 | #define _TIF_SECCOMP (1<<TIF_SECCOMP) |
124 | #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) | 125 | #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) |
126 | #define _TIF_UPROBE (1<<TIF_UPROBE) | ||
125 | #define _TIF_USEDFPU (1<<TIF_USEDFPU) | 127 | #define _TIF_USEDFPU (1<<TIF_USEDFPU) |
126 | #define _TIF_NOHZ (1<<TIF_NOHZ) | 128 | #define _TIF_NOHZ (1<<TIF_NOHZ) |
127 | #define _TIF_FIXADE (1<<TIF_FIXADE) | 129 | #define _TIF_FIXADE (1<<TIF_FIXADE) |
@@ -146,7 +148,8 @@ static inline struct thread_info *current_thread_info(void) | |||
146 | 148 | ||
147 | /* work to do on interrupt/exception return */ | 149 | /* work to do on interrupt/exception return */ |
148 | #define _TIF_WORK_MASK \ | 150 | #define _TIF_WORK_MASK \ |
149 | (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_NOTIFY_RESUME) | 151 | (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_NOTIFY_RESUME | \ |
152 | _TIF_UPROBE) | ||
150 | /* work to do on any return to u-space */ | 153 | /* work to do on any return to u-space */ |
151 | #define _TIF_ALLWORK_MASK (_TIF_NOHZ | _TIF_WORK_MASK | \ | 154 | #define _TIF_ALLWORK_MASK (_TIF_NOHZ | _TIF_WORK_MASK | \ |
152 | _TIF_WORK_SYSCALL_EXIT | \ | 155 | _TIF_WORK_SYSCALL_EXIT | \ |
diff --git a/arch/mips/include/asm/uprobes.h b/arch/mips/include/asm/uprobes.h new file mode 100644 index 000000000000..34c325c674c4 --- /dev/null +++ b/arch/mips/include/asm/uprobes.h | |||
@@ -0,0 +1,58 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | */ | ||
6 | #ifndef __ASM_UPROBES_H | ||
7 | #define __ASM_UPROBES_H | ||
8 | |||
9 | #include <linux/notifier.h> | ||
10 | #include <linux/types.h> | ||
11 | |||
12 | #include <asm/break.h> | ||
13 | #include <asm/inst.h> | ||
14 | |||
15 | /* | ||
16 | * We want this to be defined as union mips_instruction but that makes the | ||
17 | * generic code blow up. | ||
18 | */ | ||
19 | typedef u32 uprobe_opcode_t; | ||
20 | |||
21 | /* | ||
22 | * Classic MIPS (note this implementation doesn't consider microMIPS yet) | ||
23 | * instructions are always 4 bytes but in order to deal with branches and | ||
24 | * their delay slots, we treat instructions as having 8 bytes maximum. | ||
25 | */ | ||
26 | #define MAX_UINSN_BYTES 8 | ||
27 | #define UPROBE_XOL_SLOT_BYTES 128 /* Max. cache line size */ | ||
28 | |||
29 | #define UPROBE_BRK_UPROBE 0x000d000d /* break 13 */ | ||
30 | #define UPROBE_BRK_UPROBE_XOL 0x000e000d /* break 14 */ | ||
31 | |||
32 | #define UPROBE_SWBP_INSN UPROBE_BRK_UPROBE | ||
33 | #define UPROBE_SWBP_INSN_SIZE 4 | ||
34 | |||
35 | struct arch_uprobe { | ||
36 | unsigned long resume_epc; | ||
37 | u32 insn[2]; | ||
38 | u32 ixol[2]; | ||
39 | union mips_instruction orig_inst[MAX_UINSN_BYTES / 4]; | ||
40 | }; | ||
41 | |||
42 | struct arch_uprobe_task { | ||
43 | unsigned long saved_trap_nr; | ||
44 | }; | ||
45 | |||
46 | extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, | ||
47 | struct mm_struct *mm, unsigned long addr); | ||
48 | extern int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs); | ||
49 | extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); | ||
50 | extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); | ||
51 | extern int arch_uprobe_exception_notify(struct notifier_block *self, | ||
52 | unsigned long val, void *data); | ||
53 | extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, | ||
54 | struct pt_regs *regs); | ||
55 | extern unsigned long arch_uretprobe_hijack_return_addr( | ||
56 | unsigned long trampoline_vaddr, struct pt_regs *regs); | ||
57 | |||
58 | #endif /* __ASM_UPROBES_H */ | ||
diff --git a/arch/mips/include/uapi/asm/break.h b/arch/mips/include/uapi/asm/break.h index 002c39ea20c3..9c4265cbf151 100644 --- a/arch/mips/include/uapi/asm/break.h +++ b/arch/mips/include/uapi/asm/break.h | |||
@@ -21,6 +21,8 @@ | |||
21 | #define BRK_DIVZERO 7 /* Divide by zero check */ | 21 | #define BRK_DIVZERO 7 /* Divide by zero check */ |
22 | #define BRK_RANGE 8 /* Range error check */ | 22 | #define BRK_RANGE 8 /* Range error check */ |
23 | #define BRK_BUG 12 /* Used by BUG() */ | 23 | #define BRK_BUG 12 /* Used by BUG() */ |
24 | #define BRK_UPROBE 13 /* See <asm/uprobes.h> */ | ||
25 | #define BRK_UPROBE_XOL 14 /* See <asm/uprobes.h> */ | ||
24 | #define BRK_MEMU 514 /* Used by FPU emulator */ | 26 | #define BRK_MEMU 514 /* Used by FPU emulator */ |
25 | #define BRK_KPROBE_BP 515 /* Kprobe break */ | 27 | #define BRK_KPROBE_BP 515 /* Kprobe break */ |
26 | #define BRK_KPROBE_SSTEPBP 516 /* Kprobe single step software implementation */ | 28 | #define BRK_KPROBE_SSTEPBP 516 /* Kprobe single step software implementation */ |
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 3f5cf8aff6f3..a61435b1ceb1 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile | |||
@@ -100,6 +100,7 @@ obj-$(CONFIG_PERF_EVENTS) += perf_event.o | |||
100 | obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_mipsxx.o | 100 | obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_mipsxx.o |
101 | 101 | ||
102 | obj-$(CONFIG_JUMP_LABEL) += jump_label.o | 102 | obj-$(CONFIG_JUMP_LABEL) += jump_label.o |
103 | obj-$(CONFIG_UPROBES) += uprobes.o | ||
103 | 104 | ||
104 | obj-$(CONFIG_MIPS_CM) += mips-cm.o | 105 | obj-$(CONFIG_MIPS_CM) += mips-cm.o |
105 | obj-$(CONFIG_MIPS_CPC) += mips-cpc.o | 106 | obj-$(CONFIG_MIPS_CPC) += mips-cpc.o |
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index e933a309f2ea..4f0ac78d17f1 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/regset.h> | 25 | #include <linux/regset.h> |
26 | #include <linux/smp.h> | 26 | #include <linux/smp.h> |
27 | #include <linux/security.h> | 27 | #include <linux/security.h> |
28 | #include <linux/stddef.h> | ||
28 | #include <linux/tracehook.h> | 29 | #include <linux/tracehook.h> |
29 | #include <linux/audit.h> | 30 | #include <linux/audit.h> |
30 | #include <linux/seccomp.h> | 31 | #include <linux/seccomp.h> |
@@ -490,6 +491,93 @@ enum mips_regset { | |||
490 | REGSET_FPR, | 491 | REGSET_FPR, |
491 | }; | 492 | }; |
492 | 493 | ||
494 | struct pt_regs_offset { | ||
495 | const char *name; | ||
496 | int offset; | ||
497 | }; | ||
498 | |||
499 | #define REG_OFFSET_NAME(reg, r) { \ | ||
500 | .name = #reg, \ | ||
501 | .offset = offsetof(struct pt_regs, r) \ | ||
502 | } | ||
503 | |||
504 | #define REG_OFFSET_END { \ | ||
505 | .name = NULL, \ | ||
506 | .offset = 0 \ | ||
507 | } | ||
508 | |||
509 | static const struct pt_regs_offset regoffset_table[] = { | ||
510 | REG_OFFSET_NAME(r0, regs[0]), | ||
511 | REG_OFFSET_NAME(r1, regs[1]), | ||
512 | REG_OFFSET_NAME(r2, regs[2]), | ||
513 | REG_OFFSET_NAME(r3, regs[3]), | ||
514 | REG_OFFSET_NAME(r4, regs[4]), | ||
515 | REG_OFFSET_NAME(r5, regs[5]), | ||
516 | REG_OFFSET_NAME(r6, regs[6]), | ||
517 | REG_OFFSET_NAME(r7, regs[7]), | ||
518 | REG_OFFSET_NAME(r8, regs[8]), | ||
519 | REG_OFFSET_NAME(r9, regs[9]), | ||
520 | REG_OFFSET_NAME(r10, regs[10]), | ||
521 | REG_OFFSET_NAME(r11, regs[11]), | ||
522 | REG_OFFSET_NAME(r12, regs[12]), | ||
523 | REG_OFFSET_NAME(r13, regs[13]), | ||
524 | REG_OFFSET_NAME(r14, regs[14]), | ||
525 | REG_OFFSET_NAME(r15, regs[15]), | ||
526 | REG_OFFSET_NAME(r16, regs[16]), | ||
527 | REG_OFFSET_NAME(r17, regs[17]), | ||
528 | REG_OFFSET_NAME(r18, regs[18]), | ||
529 | REG_OFFSET_NAME(r19, regs[19]), | ||
530 | REG_OFFSET_NAME(r20, regs[20]), | ||
531 | REG_OFFSET_NAME(r21, regs[21]), | ||
532 | REG_OFFSET_NAME(r22, regs[22]), | ||
533 | REG_OFFSET_NAME(r23, regs[23]), | ||
534 | REG_OFFSET_NAME(r24, regs[24]), | ||
535 | REG_OFFSET_NAME(r25, regs[25]), | ||
536 | REG_OFFSET_NAME(r26, regs[26]), | ||
537 | REG_OFFSET_NAME(r27, regs[27]), | ||
538 | REG_OFFSET_NAME(r28, regs[28]), | ||
539 | REG_OFFSET_NAME(r29, regs[29]), | ||
540 | REG_OFFSET_NAME(r30, regs[30]), | ||
541 | REG_OFFSET_NAME(r31, regs[31]), | ||
542 | REG_OFFSET_NAME(c0_status, cp0_status), | ||
543 | REG_OFFSET_NAME(hi, hi), | ||
544 | REG_OFFSET_NAME(lo, lo), | ||
545 | #ifdef CONFIG_CPU_HAS_SMARTMIPS | ||
546 | REG_OFFSET_NAME(acx, acx), | ||
547 | #endif | ||
548 | REG_OFFSET_NAME(c0_badvaddr, cp0_badvaddr), | ||
549 | REG_OFFSET_NAME(c0_cause, cp0_cause), | ||
550 | REG_OFFSET_NAME(c0_epc, cp0_epc), | ||
551 | #ifdef CONFIG_MIPS_MT_SMTC | ||
552 | REG_OFFSET_NAME(c0_tcstatus, cp0_tcstatus), | ||
553 | #endif | ||
554 | #ifdef CONFIG_CPU_CAVIUM_OCTEON | ||
555 | REG_OFFSET_NAME(mpl0, mpl[0]), | ||
556 | REG_OFFSET_NAME(mpl1, mpl[1]), | ||
557 | REG_OFFSET_NAME(mpl2, mpl[2]), | ||
558 | REG_OFFSET_NAME(mtp0, mtp[0]), | ||
559 | REG_OFFSET_NAME(mtp1, mtp[1]), | ||
560 | REG_OFFSET_NAME(mtp2, mtp[2]), | ||
561 | #endif | ||
562 | REG_OFFSET_END, | ||
563 | }; | ||
564 | |||
565 | /** | ||
566 | * regs_query_register_offset() - query register offset from its name | ||
567 | * @name: the name of a register | ||
568 | * | ||
569 | * regs_query_register_offset() returns the offset of a register in struct | ||
570 | * pt_regs from its name. If the name is invalid, this returns -EINVAL; | ||
571 | */ | ||
572 | int regs_query_register_offset(const char *name) | ||
573 | { | ||
574 | const struct pt_regs_offset *roff; | ||
575 | for (roff = regoffset_table; roff->name != NULL; roff++) | ||
576 | if (!strcmp(roff->name, name)) | ||
577 | return roff->offset; | ||
578 | return -EINVAL; | ||
579 | } | ||
580 | |||
493 | #if defined(CONFIG_32BIT) || defined(CONFIG_MIPS32_O32) | 581 | #if defined(CONFIG_32BIT) || defined(CONFIG_MIPS32_O32) |
494 | 582 | ||
495 | static const struct user_regset mips_regsets[] = { | 583 | static const struct user_regset mips_regsets[] = { |
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index fa13a52713df..2fec67bfc457 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/wait.h> | 21 | #include <linux/wait.h> |
22 | #include <linux/ptrace.h> | 22 | #include <linux/ptrace.h> |
23 | #include <linux/unistd.h> | 23 | #include <linux/unistd.h> |
24 | #include <linux/uprobes.h> | ||
24 | #include <linux/compiler.h> | 25 | #include <linux/compiler.h> |
25 | #include <linux/syscalls.h> | 26 | #include <linux/syscalls.h> |
26 | #include <linux/uaccess.h> | 27 | #include <linux/uaccess.h> |
@@ -856,6 +857,9 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused, | |||
856 | 857 | ||
857 | user_exit(); | 858 | user_exit(); |
858 | 859 | ||
860 | if (thread_info_flags & _TIF_UPROBE) | ||
861 | uprobe_notify_resume(regs); | ||
862 | |||
859 | /* deal with pending signal delivery */ | 863 | /* deal with pending signal delivery */ |
860 | if (thread_info_flags & _TIF_SIGPENDING) | 864 | if (thread_info_flags & _TIF_SIGPENDING) |
861 | do_signal(regs); | 865 | do_signal(regs); |
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index cea964daf400..fdb392b27e81 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c | |||
@@ -984,6 +984,18 @@ asmlinkage void do_bp(struct pt_regs *regs) | |||
984 | * pertain to them. | 984 | * pertain to them. |
985 | */ | 985 | */ |
986 | switch (bcode) { | 986 | switch (bcode) { |
987 | case BRK_UPROBE: | ||
988 | if (notify_die(DIE_UPROBE, "uprobe", regs, bcode, | ||
989 | current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP) | ||
990 | goto out; | ||
991 | else | ||
992 | break; | ||
993 | case BRK_UPROBE_XOL: | ||
994 | if (notify_die(DIE_UPROBE_XOL, "uprobe_xol", regs, bcode, | ||
995 | current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP) | ||
996 | goto out; | ||
997 | else | ||
998 | break; | ||
987 | case BRK_KPROBE_BP: | 999 | case BRK_KPROBE_BP: |
988 | if (notify_die(DIE_BREAK, "debug", regs, bcode, | 1000 | if (notify_die(DIE_BREAK, "debug", regs, bcode, |
989 | current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP) | 1001 | current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP) |
diff --git a/arch/mips/kernel/uprobes.c b/arch/mips/kernel/uprobes.c new file mode 100644 index 000000000000..8452d933a645 --- /dev/null +++ b/arch/mips/kernel/uprobes.c | |||
@@ -0,0 +1,341 @@ | |||
1 | #include <linux/highmem.h> | ||
2 | #include <linux/kdebug.h> | ||
3 | #include <linux/types.h> | ||
4 | #include <linux/notifier.h> | ||
5 | #include <linux/sched.h> | ||
6 | #include <linux/uprobes.h> | ||
7 | |||
8 | #include <asm/branch.h> | ||
9 | #include <asm/cpu-features.h> | ||
10 | #include <asm/ptrace.h> | ||
11 | #include <asm/inst.h> | ||
12 | |||
13 | static inline int insn_has_delay_slot(const union mips_instruction insn) | ||
14 | { | ||
15 | switch (insn.i_format.opcode) { | ||
16 | /* | ||
17 | * jr and jalr are in r_format format. | ||
18 | */ | ||
19 | case spec_op: | ||
20 | switch (insn.r_format.func) { | ||
21 | case jalr_op: | ||
22 | case jr_op: | ||
23 | return 1; | ||
24 | } | ||
25 | break; | ||
26 | |||
27 | /* | ||
28 | * This group contains: | ||
29 | * bltz_op, bgez_op, bltzl_op, bgezl_op, | ||
30 | * bltzal_op, bgezal_op, bltzall_op, bgezall_op. | ||
31 | */ | ||
32 | case bcond_op: | ||
33 | switch (insn.i_format.rt) { | ||
34 | case bltz_op: | ||
35 | case bltzl_op: | ||
36 | case bgez_op: | ||
37 | case bgezl_op: | ||
38 | case bltzal_op: | ||
39 | case bltzall_op: | ||
40 | case bgezal_op: | ||
41 | case bgezall_op: | ||
42 | case bposge32_op: | ||
43 | return 1; | ||
44 | } | ||
45 | break; | ||
46 | |||
47 | /* | ||
48 | * These are unconditional and in j_format. | ||
49 | */ | ||
50 | case jal_op: | ||
51 | case j_op: | ||
52 | case beq_op: | ||
53 | case beql_op: | ||
54 | case bne_op: | ||
55 | case bnel_op: | ||
56 | case blez_op: /* not really i_format */ | ||
57 | case blezl_op: | ||
58 | case bgtz_op: | ||
59 | case bgtzl_op: | ||
60 | return 1; | ||
61 | |||
62 | /* | ||
63 | * And now the FPA/cp1 branch instructions. | ||
64 | */ | ||
65 | case cop1_op: | ||
66 | #ifdef CONFIG_CPU_CAVIUM_OCTEON | ||
67 | case lwc2_op: /* This is bbit0 on Octeon */ | ||
68 | case ldc2_op: /* This is bbit032 on Octeon */ | ||
69 | case swc2_op: /* This is bbit1 on Octeon */ | ||
70 | case sdc2_op: /* This is bbit132 on Octeon */ | ||
71 | #endif | ||
72 | return 1; | ||
73 | } | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * arch_uprobe_analyze_insn - instruction analysis including validity and fixups. | ||
80 | * @mm: the probed address space. | ||
81 | * @arch_uprobe: the probepoint information. | ||
82 | * @addr: virtual address at which to install the probepoint | ||
83 | * Return 0 on success or a -ve number on error. | ||
84 | */ | ||
85 | int arch_uprobe_analyze_insn(struct arch_uprobe *aup, | ||
86 | struct mm_struct *mm, unsigned long addr) | ||
87 | { | ||
88 | union mips_instruction inst; | ||
89 | |||
90 | /* | ||
91 | * For the time being this also blocks attempts to use uprobes with | ||
92 | * MIPS16 and microMIPS. | ||
93 | */ | ||
94 | if (addr & 0x03) | ||
95 | return -EINVAL; | ||
96 | |||
97 | inst.word = aup->insn[0]; | ||
98 | aup->ixol[0] = aup->insn[insn_has_delay_slot(inst)]; | ||
99 | aup->ixol[1] = UPROBE_BRK_UPROBE_XOL; /* NOP */ | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * is_trap_insn - check if the instruction is a trap variant | ||
106 | * @insn: instruction to be checked. | ||
107 | * Returns true if @insn is a trap variant. | ||
108 | * | ||
109 | * This definition overrides the weak definition in kernel/events/uprobes.c. | ||
110 | * and is needed for the case where an architecture has multiple trap | ||
111 | * instructions (like PowerPC or MIPS). We treat BREAK just like the more | ||
112 | * modern conditional trap instructions. | ||
113 | */ | ||
114 | bool is_trap_insn(uprobe_opcode_t *insn) | ||
115 | { | ||
116 | union mips_instruction inst; | ||
117 | |||
118 | inst.word = *insn; | ||
119 | |||
120 | switch (inst.i_format.opcode) { | ||
121 | case spec_op: | ||
122 | switch (inst.r_format.func) { | ||
123 | case break_op: | ||
124 | case teq_op: | ||
125 | case tge_op: | ||
126 | case tgeu_op: | ||
127 | case tlt_op: | ||
128 | case tltu_op: | ||
129 | case tne_op: | ||
130 | return 1; | ||
131 | } | ||
132 | break; | ||
133 | |||
134 | case bcond_op: /* Yes, really ... */ | ||
135 | switch (inst.u_format.rt) { | ||
136 | case teqi_op: | ||
137 | case tgei_op: | ||
138 | case tgeiu_op: | ||
139 | case tlti_op: | ||
140 | case tltiu_op: | ||
141 | case tnei_op: | ||
142 | return 1; | ||
143 | } | ||
144 | break; | ||
145 | } | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | #define UPROBE_TRAP_NR ULONG_MAX | ||
151 | |||
152 | /* | ||
153 | * arch_uprobe_pre_xol - prepare to execute out of line. | ||
154 | * @auprobe: the probepoint information. | ||
155 | * @regs: reflects the saved user state of current task. | ||
156 | */ | ||
157 | int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs) | ||
158 | { | ||
159 | struct uprobe_task *utask = current->utask; | ||
160 | union mips_instruction insn; | ||
161 | |||
162 | /* | ||
163 | * Now find the EPC where to resume after the breakpoint has been | ||
164 | * dealt with. This may require emulation of a branch. | ||
165 | */ | ||
166 | aup->resume_epc = regs->cp0_epc + 4; | ||
167 | if (insn_has_delay_slot((union mips_instruction) aup->insn[0])) { | ||
168 | unsigned long epc; | ||
169 | |||
170 | epc = regs->cp0_epc; | ||
171 | __compute_return_epc_for_insn(regs, insn); | ||
172 | aup->resume_epc = regs->cp0_epc; | ||
173 | } | ||
174 | |||
175 | utask->autask.saved_trap_nr = current->thread.trap_nr; | ||
176 | current->thread.trap_nr = UPROBE_TRAP_NR; | ||
177 | regs->cp0_epc = current->utask->xol_vaddr; | ||
178 | |||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs) | ||
183 | { | ||
184 | struct uprobe_task *utask = current->utask; | ||
185 | |||
186 | current->thread.trap_nr = utask->autask.saved_trap_nr; | ||
187 | regs->cp0_epc = aup->resume_epc; | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | /* | ||
193 | * If xol insn itself traps and generates a signal(Say, | ||
194 | * SIGILL/SIGSEGV/etc), then detect the case where a singlestepped | ||
195 | * instruction jumps back to its own address. It is assumed that anything | ||
196 | * like do_page_fault/do_trap/etc sets thread.trap_nr != -1. | ||
197 | * | ||
198 | * arch_uprobe_pre_xol/arch_uprobe_post_xol save/restore thread.trap_nr, | ||
199 | * arch_uprobe_xol_was_trapped() simply checks that ->trap_nr is not equal to | ||
200 | * UPROBE_TRAP_NR == -1 set by arch_uprobe_pre_xol(). | ||
201 | */ | ||
202 | bool arch_uprobe_xol_was_trapped(struct task_struct *tsk) | ||
203 | { | ||
204 | if (tsk->thread.trap_nr != UPROBE_TRAP_NR) | ||
205 | return true; | ||
206 | |||
207 | return false; | ||
208 | } | ||
209 | |||
210 | int arch_uprobe_exception_notify(struct notifier_block *self, | ||
211 | unsigned long val, void *data) | ||
212 | { | ||
213 | struct die_args *args = data; | ||
214 | struct pt_regs *regs = args->regs; | ||
215 | |||
216 | /* regs == NULL is a kernel bug */ | ||
217 | if (WARN_ON(!regs)) | ||
218 | return NOTIFY_DONE; | ||
219 | |||
220 | /* We are only interested in userspace traps */ | ||
221 | if (!user_mode(regs)) | ||
222 | return NOTIFY_DONE; | ||
223 | |||
224 | switch (val) { | ||
225 | case DIE_BREAK: | ||
226 | if (uprobe_pre_sstep_notifier(regs)) | ||
227 | return NOTIFY_STOP; | ||
228 | break; | ||
229 | case DIE_UPROBE_XOL: | ||
230 | if (uprobe_post_sstep_notifier(regs)) | ||
231 | return NOTIFY_STOP; | ||
232 | default: | ||
233 | break; | ||
234 | } | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * This function gets called when XOL instruction either gets trapped or | ||
241 | * the thread has a fatal signal. Reset the instruction pointer to its | ||
242 | * probed address for the potential restart or for post mortem analysis. | ||
243 | */ | ||
244 | void arch_uprobe_abort_xol(struct arch_uprobe *aup, | ||
245 | struct pt_regs *regs) | ||
246 | { | ||
247 | struct uprobe_task *utask = current->utask; | ||
248 | |||
249 | instruction_pointer_set(regs, utask->vaddr); | ||
250 | } | ||
251 | |||
252 | unsigned long arch_uretprobe_hijack_return_addr( | ||
253 | unsigned long trampoline_vaddr, struct pt_regs *regs) | ||
254 | { | ||
255 | unsigned long ra; | ||
256 | |||
257 | ra = regs->regs[31]; | ||
258 | |||
259 | /* Replace the return address with the trampoline address */ | ||
260 | regs->regs[31] = ra; | ||
261 | |||
262 | return ra; | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * set_swbp - store breakpoint at a given address. | ||
267 | * @auprobe: arch specific probepoint information. | ||
268 | * @mm: the probed process address space. | ||
269 | * @vaddr: the virtual address to insert the opcode. | ||
270 | * | ||
271 | * For mm @mm, store the breakpoint instruction at @vaddr. | ||
272 | * Return 0 (success) or a negative errno. | ||
273 | * | ||
274 | * This version overrides the weak version in kernel/events/uprobes.c. | ||
275 | * It is required to handle MIPS16 and microMIPS. | ||
276 | */ | ||
277 | int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, | ||
278 | unsigned long vaddr) | ||
279 | { | ||
280 | return uprobe_write_opcode(mm, vaddr, UPROBE_SWBP_INSN); | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * set_orig_insn - Restore the original instruction. | ||
285 | * @mm: the probed process address space. | ||
286 | * @auprobe: arch specific probepoint information. | ||
287 | * @vaddr: the virtual address to insert the opcode. | ||
288 | * | ||
289 | * For mm @mm, restore the original opcode (opcode) at @vaddr. | ||
290 | * Return 0 (success) or a negative errno. | ||
291 | * | ||
292 | * This overrides the weak version in kernel/events/uprobes.c. | ||
293 | */ | ||
294 | int set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, | ||
295 | unsigned long vaddr) | ||
296 | { | ||
297 | return uprobe_write_opcode(mm, vaddr, | ||
298 | *(uprobe_opcode_t *)&auprobe->orig_inst[0].word); | ||
299 | } | ||
300 | |||
301 | void __weak arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, | ||
302 | void *src, unsigned long len) | ||
303 | { | ||
304 | void *kaddr; | ||
305 | |||
306 | /* Initialize the slot */ | ||
307 | kaddr = kmap_atomic(page); | ||
308 | memcpy(kaddr + (vaddr & ~PAGE_MASK), src, len); | ||
309 | kunmap_atomic(kaddr); | ||
310 | |||
311 | /* | ||
312 | * The MIPS version of flush_icache_range will operate safely on | ||
313 | * user space addresses and more importantly, it doesn't require a | ||
314 | * VMA argument. | ||
315 | */ | ||
316 | flush_icache_range(vaddr, vaddr + len); | ||
317 | } | ||
318 | |||
319 | /** | ||
320 | * uprobe_get_swbp_addr - compute address of swbp given post-swbp regs | ||
321 | * @regs: Reflects the saved state of the task after it has hit a breakpoint | ||
322 | * instruction. | ||
323 | * Return the address of the breakpoint instruction. | ||
324 | * | ||
325 | * This overrides the weak version in kernel/events/uprobes.c. | ||
326 | */ | ||
327 | unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) | ||
328 | { | ||
329 | return instruction_pointer(regs); | ||
330 | } | ||
331 | |||
332 | /* | ||
333 | * See if the instruction can be emulated. | ||
334 | * Returns true if instruction was emulated, false otherwise. | ||
335 | * | ||
336 | * For now we always emulate so this function just returns 0. | ||
337 | */ | ||
338 | bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
339 | { | ||
340 | return 0; | ||
341 | } | ||