Merge branch 'async-scsi-resume' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / arch / arm / kernel / uprobes.c
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/stddef.h>
11 #include <linux/errno.h>
12 #include <linux/highmem.h>
13 #include <linux/sched.h>
14 #include <linux/uprobes.h>
15 #include <linux/notifier.h>
16
17 #include <asm/opcodes.h>
18 #include <asm/traps.h>
19
20 #include "probes.h"
21 #include "probes-arm.h"
22 #include "uprobes.h"
23
24 #define UPROBE_TRAP_NR  UINT_MAX
25
26 bool is_swbp_insn(uprobe_opcode_t *insn)
27 {
28         return (__mem_to_opcode_arm(*insn) & 0x0fffffff) ==
29                 (UPROBE_SWBP_ARM_INSN & 0x0fffffff);
30 }
31
32 int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,
33              unsigned long vaddr)
34 {
35         return uprobe_write_opcode(mm, vaddr,
36                    __opcode_to_mem_arm(auprobe->bpinsn));
37 }
38
39 bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
40 {
41         if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) {
42                 regs->ARM_pc += 4;
43                 return true;
44         }
45
46         return false;
47 }
48
49 bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
50 {
51         probes_opcode_t opcode;
52
53         if (!auprobe->simulate)
54                 return false;
55
56         opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn);
57
58         auprobe->asi.insn_singlestep(opcode, &auprobe->asi, regs);
59
60         return true;
61 }
62
63 unsigned long
64 arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
65                                   struct pt_regs *regs)
66 {
67         unsigned long orig_ret_vaddr;
68
69         orig_ret_vaddr = regs->ARM_lr;
70         /* Replace the return addr with trampoline addr */
71         regs->ARM_lr = trampoline_vaddr;
72         return orig_ret_vaddr;
73 }
74
75 int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
76                              unsigned long addr)
77 {
78         unsigned int insn;
79         unsigned int bpinsn;
80         enum probes_insn ret;
81
82         /* Thumb not yet support */
83         if (addr & 0x3)
84                 return -EINVAL;
85
86         insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn);
87         auprobe->ixol[0] = __opcode_to_mem_arm(insn);
88         auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN);
89
90         ret = arm_probes_decode_insn(insn, &auprobe->asi, false,
91                                      uprobes_probes_actions);
92         switch (ret) {
93         case INSN_REJECTED:
94                 return -EINVAL;
95
96         case INSN_GOOD_NO_SLOT:
97                 auprobe->simulate = true;
98                 break;
99
100         case INSN_GOOD:
101         default:
102                 break;
103         }
104
105         bpinsn = UPROBE_SWBP_ARM_INSN & 0x0fffffff;
106         if (insn >= 0xe0000000)
107                 bpinsn |= 0xe0000000;  /* Unconditional instruction */
108         else
109                 bpinsn |= insn & 0xf0000000;  /* Copy condition from insn */
110
111         auprobe->bpinsn = bpinsn;
112
113         return 0;
114 }
115
116 int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
117 {
118         struct uprobe_task *utask = current->utask;
119
120         if (auprobe->prehandler)
121                 auprobe->prehandler(auprobe, &utask->autask, regs);
122
123         utask->autask.saved_trap_no = current->thread.trap_no;
124         current->thread.trap_no = UPROBE_TRAP_NR;
125         regs->ARM_pc = utask->xol_vaddr;
126
127         return 0;
128 }
129
130 int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
131 {
132         struct uprobe_task *utask = current->utask;
133
134         WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR);
135
136         current->thread.trap_no = utask->autask.saved_trap_no;
137         regs->ARM_pc = utask->vaddr + 4;
138
139         if (auprobe->posthandler)
140                 auprobe->posthandler(auprobe, &utask->autask, regs);
141
142         return 0;
143 }
144
145 bool arch_uprobe_xol_was_trapped(struct task_struct *t)
146 {
147         if (t->thread.trap_no != UPROBE_TRAP_NR)
148                 return true;
149
150         return false;
151 }
152
153 void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
154 {
155         struct uprobe_task *utask = current->utask;
156
157         current->thread.trap_no = utask->autask.saved_trap_no;
158         instruction_pointer_set(regs, utask->vaddr);
159 }
160
161 int arch_uprobe_exception_notify(struct notifier_block *self,
162                                  unsigned long val, void *data)
163 {
164         return NOTIFY_DONE;
165 }
166
167 static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
168 {
169         unsigned long flags;
170
171         local_irq_save(flags);
172         instr &= 0x0fffffff;
173         if (instr == (UPROBE_SWBP_ARM_INSN & 0x0fffffff))
174                 uprobe_pre_sstep_notifier(regs);
175         else if (instr == (UPROBE_SS_ARM_INSN & 0x0fffffff))
176                 uprobe_post_sstep_notifier(regs);
177         local_irq_restore(flags);
178
179         return 0;
180 }
181
182 unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
183 {
184         return instruction_pointer(regs);
185 }
186
187 static struct undef_hook uprobes_arm_break_hook = {
188         .instr_mask     = 0x0fffffff,
189         .instr_val      = (UPROBE_SWBP_ARM_INSN & 0x0fffffff),
190         .cpsr_mask      = MODE_MASK,
191         .cpsr_val       = USR_MODE,
192         .fn             = uprobe_trap_handler,
193 };
194
195 static struct undef_hook uprobes_arm_ss_hook = {
196         .instr_mask     = 0x0fffffff,
197         .instr_val      = (UPROBE_SS_ARM_INSN & 0x0fffffff),
198         .cpsr_mask      = MODE_MASK,
199         .cpsr_val       = USR_MODE,
200         .fn             = uprobe_trap_handler,
201 };
202
203 static int arch_uprobes_init(void)
204 {
205         register_undef_hook(&uprobes_arm_break_hook);
206         register_undef_hook(&uprobes_arm_ss_hook);
207
208         return 0;
209 }
210 device_initcall(arch_uprobes_init);