[REFACTOR] decompose setjmp_pre_handler()
[kernel/swap-modules.git] / uprobe / arch / asm-arm / swap_uprobes.c
1 #include <dbi_kprobes.h>
2 #include <asm/dbi_kprobes.h>
3 #include <asm/traps.h>
4
5 // FIXME:
6 #include <dbi_kdebug.h>
7 extern struct hlist_head uprobe_insn_pages;
8 kprobe_opcode_t *get_insn_slot(struct task_struct *task, struct hlist_head *page_list, int atomic);
9 int arch_check_insn_arm(struct arch_specific_insn *ainsn);
10 int prep_pc_dep_insn_execbuf(kprobe_opcode_t *insns, kprobe_opcode_t insn, int uregs);
11 void free_insn_slot(struct hlist_head *page_list, struct task_struct *task, kprobe_opcode_t *slot);
12 void pc_dep_insn_execbuf(void);
13 void gen_insn_execbuf(void);
14 void gen_insn_execbuf_thumb(void);
15 void pc_dep_insn_execbuf_thumb(void);
16 int kprobe_trap_handler(struct pt_regs *regs, unsigned int instr);
17
18
19 #define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
20 #define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
21
22 static kprobe_opcode_t get_addr_b(kprobe_opcode_t insn, kprobe_opcode_t *addr)
23 {
24         // real position less then PC by 8
25         return (kprobe_opcode_t)((long)addr + 8 + branch_displacement(insn));
26 }
27
28 /* is instruction Thumb2 and NOT a branch, etc... */
29 static int is_thumb2(kprobe_opcode_t insn)
30 {
31         return ((insn & 0xf800) == 0xe800 ||
32                 (insn & 0xf800) == 0xf000 ||
33                 (insn & 0xf800) == 0xf800);
34 }
35
36 static int arch_copy_trampoline_arm_uprobe(struct kprobe *p, struct task_struct *task, int atomic)
37 {
38         kprobe_opcode_t insns[UPROBES_TRAMP_LEN];
39         int uregs, pc_dep;
40         kprobe_opcode_t insn[MAX_INSN_SIZE];
41         struct arch_specific_insn ainsn;
42
43         p->safe_arm = -1;
44         if ((unsigned long)p->addr & 0x01) {
45                 printk("Error in %s at %d: attempt to register kprobe at an unaligned address\n", __FILE__, __LINE__);
46                 return -EINVAL;
47         }
48
49         insn[0] = p->opcode;
50         ainsn.insn_arm = insn;
51         if (!arch_check_insn_arm(&ainsn)) {
52                 p->safe_arm = 0;
53         }
54
55         uregs = pc_dep = 0;
56         // Rn, Rm ,Rd
57         if (ARM_INSN_MATCH(DPIS, insn[0]) || ARM_INSN_MATCH(LRO, insn[0]) ||
58             ARM_INSN_MATCH(SRO, insn[0])) {
59                 uregs = 0xb;
60                 if ((ARM_INSN_REG_RN(insn[0]) == 15) || (ARM_INSN_REG_RM(insn[0]) == 15) ||
61                     (ARM_INSN_MATCH(SRO, insn[0]) && (ARM_INSN_REG_RD(insn[0]) == 15))) {
62                         DBPRINTF("Unboostable insn %lx, DPIS/LRO/SRO\n", insn[0]);
63                         pc_dep = 1;
64                 }
65
66         // Rn ,Rd
67         } else if (ARM_INSN_MATCH(DPI, insn[0]) || ARM_INSN_MATCH(LIO, insn[0]) ||
68                    ARM_INSN_MATCH (SIO, insn[0])) {
69                 uregs = 0x3;
70                 if ((ARM_INSN_REG_RN(insn[0]) == 15) || (ARM_INSN_MATCH(SIO, insn[0]) &&
71                     (ARM_INSN_REG_RD(insn[0]) == 15))) {
72                         pc_dep = 1;
73                         DBPRINTF("Unboostable insn %lx/%p, DPI/LIO/SIO\n", insn[0], p);
74                 }
75
76         // Rn, Rm, Rs
77         } else if (ARM_INSN_MATCH(DPRS, insn[0])) {
78                 uregs = 0xd;
79                 if ((ARM_INSN_REG_RN(insn[0]) == 15) || (ARM_INSN_REG_RM(insn[0]) == 15) ||
80                     (ARM_INSN_REG_RS(insn[0]) == 15)) {
81                         pc_dep = 1;
82                         DBPRINTF("Unboostable insn %lx, DPRS\n", insn[0]);
83                 }
84
85         // register list
86         } else if (ARM_INSN_MATCH(SM, insn[0])) {
87                 uregs = 0x10;
88                 if (ARM_INSN_REG_MR (insn[0], 15))
89                 {
90                         DBPRINTF ("Unboostable insn %lx, SM\n", insn[0]);
91                         pc_dep = 1;
92                 }
93         }
94
95         // check instructions that can write result to SP andu uses PC
96         if (pc_dep  && (ARM_INSN_REG_RD (ainsn.insn_arm[0]) == 13)) {
97                 printk("Error in %s at %d: instruction check failed (arm)\n", __FILE__, __LINE__);
98                 p->safe_arm = -1;
99                 // TODO: move free to later phase
100                 //free_insn_slot (&uprobe_insn_pages, task, p->ainsn.insn_arm, 0);
101                 //ret = -EFAULT;
102         }
103
104         if (unlikely(uregs && pc_dep)) {
105                 memcpy(insns, pc_dep_insn_execbuf, sizeof(insns));
106                 if (prep_pc_dep_insn_execbuf(insns, insn[0], uregs) != 0) {
107                         printk("Error in %s at %d: failed to prepare exec buffer for insn %lx!",
108                                __FILE__, __LINE__, insn[0]);
109                         p->safe_arm = -1;
110                         // TODO: move free to later phase
111                         //free_insn_slot (&uprobe_insn_pages, task, p->ainsn.insn_arm, 0);
112                         //return -EINVAL;
113                 }
114
115                 insns[6] = (kprobe_opcode_t) (p->addr + 2);
116         } else {
117                 memcpy(insns, gen_insn_execbuf, sizeof(insns));
118                 insns[UPROBES_TRAMP_INSN_IDX] = insn[0];
119         }
120
121         insns[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
122         insns[7] = (kprobe_opcode_t) (p->addr + 1);
123
124         // B
125         if(ARM_INSN_MATCH(B, ainsn.insn_arm[0])) {
126                 memcpy(insns, pc_dep_insn_execbuf, sizeof(insns));
127                 insns[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
128                 insns[6] = (kprobe_opcode_t)(p->addr + 2);
129                 insns[7] = get_addr_b(p->opcode, p->addr);
130         }
131
132         DBPRINTF("arch_prepare_uprobe: to %p - %lx %lx %lx %lx %lx %lx %lx %lx %lx",
133                  p->ainsn.insn_arm, insns[0], insns[1], insns[2], insns[3], insns[4],
134                  insns[5], insns[6], insns[7], insns[8]);
135         if (!write_proc_vm_atomic(task, (unsigned long)p->ainsn.insn_arm, insns, sizeof(insns))) {
136                 panic("failed to write memory %p!\n", p->ainsn.insn_arm);
137                 // Mr_Nobody: we have to panic, really??...
138                 //free_insn_slot (&uprobe_insn_pages, task, p->ainsn.insn_arm, 0);
139                 //return -EINVAL;
140         }
141
142         return 0;
143 }
144
145 static int arch_check_insn_thumb(struct arch_specific_insn *ainsn)
146 {
147         int ret = 0;
148
149         // check instructions that can change PC
150         if (THUMB_INSN_MATCH(UNDEF, ainsn->insn_thumb[0]) ||
151             THUMB_INSN_MATCH(SWI, ainsn->insn_thumb[0]) ||
152             THUMB_INSN_MATCH(BREAK, ainsn->insn_thumb[0]) ||
153             THUMB2_INSN_MATCH(BL, ainsn->insn_thumb[0]) ||
154             THUMB_INSN_MATCH(B1, ainsn->insn_thumb[0]) ||
155             THUMB_INSN_MATCH(B2, ainsn->insn_thumb[0]) ||
156             THUMB_INSN_MATCH(CBZ, ainsn->insn_thumb[0]) ||
157             THUMB2_INSN_MATCH(B1, ainsn->insn_thumb[0]) ||
158             THUMB2_INSN_MATCH(B2, ainsn->insn_thumb[0]) ||
159             THUMB2_INSN_MATCH(BLX1, ainsn->insn_thumb[0]) ||
160             THUMB_INSN_MATCH(BLX2, ainsn->insn_thumb[0]) ||
161             THUMB_INSN_MATCH(BX, ainsn->insn_thumb[0]) ||
162             THUMB2_INSN_MATCH(BXJ, ainsn->insn_thumb[0]) ||
163             (THUMB2_INSN_MATCH(ADR, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RD(ainsn->insn_thumb[0]) == 15) ||
164             (THUMB2_INSN_MATCH(LDRW, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RT(ainsn->insn_thumb[0]) == 15) ||
165             (THUMB2_INSN_MATCH(LDRW1, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RT(ainsn->insn_thumb[0]) == 15) ||
166             (THUMB2_INSN_MATCH(LDRHW, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RT(ainsn->insn_thumb[0]) == 15) ||
167             (THUMB2_INSN_MATCH(LDRHW1, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RT(ainsn->insn_thumb[0]) == 15) ||
168             (THUMB2_INSN_MATCH(LDRWL, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RT(ainsn->insn_thumb[0]) == 15) ||
169             THUMB2_INSN_MATCH(LDMIA, ainsn->insn_thumb[0]) ||
170             THUMB2_INSN_MATCH(LDMDB, ainsn->insn_thumb[0]) ||
171             (THUMB2_INSN_MATCH(DP, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RD(ainsn->insn_thumb[0]) == 15) ||
172             (THUMB2_INSN_MATCH(RSBW, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RD(ainsn->insn_thumb[0]) == 15) ||
173             (THUMB2_INSN_MATCH(RORW, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RD(ainsn->insn_thumb[0]) == 15) ||
174             (THUMB2_INSN_MATCH(ROR, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RD(ainsn->insn_thumb[0]) == 15) ||
175             (THUMB2_INSN_MATCH(LSLW1, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RD(ainsn->insn_thumb[0]) == 15) ||
176             (THUMB2_INSN_MATCH(LSLW2, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RD(ainsn->insn_thumb[0]) == 15) ||
177             (THUMB2_INSN_MATCH(LSRW1, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RD(ainsn->insn_thumb[0]) == 15) ||
178             (THUMB2_INSN_MATCH(LSRW2, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RD(ainsn->insn_thumb[0]) == 15) ||
179             /* skip PC, #-imm12 -> SP, #-imm8 and Tegra-hanging instructions */
180             (THUMB2_INSN_MATCH(STRW1, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RN(ainsn->insn_thumb[0]) == 15) ||
181             (THUMB2_INSN_MATCH(STRBW1, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RN(ainsn->insn_thumb[0]) == 15) ||
182             (THUMB2_INSN_MATCH(STRHW1, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RN(ainsn->insn_thumb[0]) == 15) ||
183             (THUMB2_INSN_MATCH(STRW, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RN(ainsn->insn_thumb[0]) == 15) ||
184             (THUMB2_INSN_MATCH(STRHW, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RN(ainsn->insn_thumb[0]) == 15) ||
185             (THUMB2_INSN_MATCH(LDRW, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RN(ainsn->insn_thumb[0]) == 15) ||
186             (THUMB2_INSN_MATCH(LDRBW, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RN(ainsn->insn_thumb[0]) == 15) ||
187             (THUMB2_INSN_MATCH(LDRHW, ainsn->insn_thumb[0]) && THUMB2_INSN_REG_RN(ainsn->insn_thumb[0]) == 15) ||
188             /* skip STRDx/LDRDx Rt, Rt2, [Rd, ...] */
189             (THUMB2_INSN_MATCH(LDRD, ainsn->insn_thumb[0]) || THUMB2_INSN_MATCH(LDRD1, ainsn->insn_thumb[0]) || THUMB2_INSN_MATCH(STRD, ainsn->insn_thumb[0])) ) {
190                 DBPRINTF("Bad insn arch_check_insn_thumb: %lx\n", ainsn->insn_thumb[0]);
191                 ret = -EFAULT;
192         }
193
194         return ret;
195 }
196
197 static int prep_pc_dep_insn_execbuf_thumb(kprobe_opcode_t * insns, kprobe_opcode_t insn, int uregs)
198 {
199         unsigned char mreg = 0;
200         unsigned char reg = 0;
201
202         if (THUMB_INSN_MATCH(APC, insn) || THUMB_INSN_MATCH(LRO3, insn)) {
203                 reg = ((insn & 0xffff) & uregs) >> 8;
204         } else {
205                 if (THUMB_INSN_MATCH(MOV3, insn)) {
206                         if (((((unsigned char) insn) & 0xff) >> 3) == 15) {
207                                 reg = (insn & 0xffff) & uregs;
208                         } else {
209                                 return 0;
210                         }
211                 } else {
212                         if (THUMB2_INSN_MATCH(ADR, insn)) {
213                                 reg = ((insn >> 16) & uregs) >> 8;
214                                 if (reg == 15) {
215                                         return 0;
216                                 }
217                         } else {
218                                 if (THUMB2_INSN_MATCH(LDRW, insn) || THUMB2_INSN_MATCH(LDRW1, insn) ||
219                                     THUMB2_INSN_MATCH(LDRHW, insn) || THUMB2_INSN_MATCH(LDRHW1, insn) ||
220                                     THUMB2_INSN_MATCH(LDRWL, insn)) {
221                                         reg = ((insn >> 16) & uregs) >> 12;
222                                         if (reg == 15) {
223                                                 return 0;
224                                         }
225                                 } else {
226                                         // LDRB.W PC, [PC, #immed] => PLD [PC, #immed], so Rt == PC is skipped
227                                         if (THUMB2_INSN_MATCH(LDRBW, insn) || THUMB2_INSN_MATCH(LDRBW1, insn) ||
228                                             THUMB2_INSN_MATCH(LDREX, insn)) {
229                                                 reg = ((insn >> 16) & uregs) >> 12;
230                                         } else {
231                                                 if (THUMB2_INSN_MATCH(DP, insn)) {
232                                                         reg = ((insn >> 16) & uregs) >> 12;
233                                                         if (reg == 15) {
234                                                                 return 0;
235                                                         }
236                                                 } else {
237                                                         if (THUMB2_INSN_MATCH(RSBW, insn)) {
238                                                                 reg = ((insn >> 12) & uregs) >> 8;
239                                                                 if (reg == 15){
240                                                                         return 0;
241                                                                 }
242                                                         } else {
243                                                                 if (THUMB2_INSN_MATCH(RORW, insn)) {
244                                                                         reg = ((insn >> 12) & uregs) >> 8;
245                                                                         if (reg == 15) {
246                                                                                 return 0;
247                                                                         }
248                                                                 } else {
249                                                                         if (THUMB2_INSN_MATCH(ROR, insn) || THUMB2_INSN_MATCH(LSLW1, insn) ||
250                                                                             THUMB2_INSN_MATCH(LSLW2, insn) || THUMB2_INSN_MATCH(LSRW1, insn) ||
251                                                                             THUMB2_INSN_MATCH(LSRW2, insn)) {
252                                                                                 reg = ((insn >> 12) & uregs) >> 8;
253                                                                                 if (reg == 15) {
254                                                                                         return 0;
255                                                                                 }
256                                                                         } else {
257                                                                                 if (THUMB2_INSN_MATCH(TEQ1, insn) || THUMB2_INSN_MATCH(TST1, insn)) {
258                                                                                         reg = 15;
259                                                                                 } else {
260                                                                                         if (THUMB2_INSN_MATCH(TEQ2, insn) || THUMB2_INSN_MATCH(TST2, insn)) {
261                                                                                                 reg = THUMB2_INSN_REG_RM(insn);
262                                                                                         }
263                                                                                 }
264                                                                         }
265                                                                 }
266                                                         }
267                                                 }
268                                         }
269                                 }
270                         }
271                 }
272         }
273
274         if ((THUMB2_INSN_MATCH(STRW, insn) || THUMB2_INSN_MATCH(STRBW, insn) ||
275              THUMB2_INSN_MATCH(STRD, insn) || THUMB2_INSN_MATCH(STRHT, insn) ||
276              THUMB2_INSN_MATCH(STRT, insn) || THUMB2_INSN_MATCH(STRHW1, insn) ||
277              THUMB2_INSN_MATCH(STRHW, insn)) && THUMB2_INSN_REG_RT(insn) == 15) {
278                 reg = THUMB2_INSN_REG_RT(insn);
279         }
280
281         if (reg == 6 || reg == 7) {
282                 *((unsigned short*)insns + 0) = (*((unsigned short*)insns + 0) & 0x00ff) | ((1 << mreg) | (1 << (mreg + 1)));
283                 *((unsigned short*)insns + 1) = (*((unsigned short*)insns + 1) & 0xf8ff) | (mreg << 8);
284                 *((unsigned short*)insns + 2) = (*((unsigned short*)insns + 2) & 0xfff8) | (mreg + 1);
285                 *((unsigned short*)insns + 3) = (*((unsigned short*)insns + 3) & 0xffc7) | (mreg << 3);
286                 *((unsigned short*)insns + 7) = (*((unsigned short*)insns + 7) & 0xf8ff) | (mreg << 8);
287                 *((unsigned short*)insns + 8) = (*((unsigned short*)insns + 8) & 0xffc7) | (mreg << 3);
288                 *((unsigned short*)insns + 9) = (*((unsigned short*)insns + 9) & 0xffc7) | ((mreg + 1) << 3);
289                 *((unsigned short*)insns + 10) = (*((unsigned short*)insns + 10) & 0x00ff) | (( 1 << mreg) | (1 << (mreg + 1)));
290         }
291
292         if (THUMB_INSN_MATCH(APC, insn)) {
293                 // ADD Rd, PC, #immed_8*4 -> ADD Rd, SP, #immed_8*4
294                 *((unsigned short*)insns + 4) = ((insn & 0xffff) | 0x800);                              // ADD Rd, SP, #immed_8*4
295         } else {
296                 if (THUMB_INSN_MATCH(LRO3, insn)) {
297                         // LDR Rd, [PC, #immed_8*4] -> LDR Rd, [SP, #immed_8*4]
298                         *((unsigned short*)insns + 4) = ((insn & 0xffff) + 0x5000);                     // LDR Rd, [SP, #immed_8*4]
299                 } else {
300                         if (THUMB_INSN_MATCH(MOV3, insn)) {
301                                 // MOV Rd, PC -> MOV Rd, SP
302                                 *((unsigned short*)insns + 4) = ((insn & 0xffff) ^ 0x10);               // MOV Rd, SP
303                         } else {
304                                 if (THUMB2_INSN_MATCH(ADR, insn)) {
305                                         // ADDW Rd, PC, #imm -> ADDW Rd, SP, #imm
306                                         insns[2] = (insn & 0xfffffff0) | 0x0d;                          // ADDW Rd, SP, #imm
307                                 } else {
308                                         if (THUMB2_INSN_MATCH(LDRW, insn) || THUMB2_INSN_MATCH(LDRBW, insn) ||
309                                             THUMB2_INSN_MATCH(LDRHW, insn)) {
310                                                 // LDR.W Rt, [PC, #-<imm_12>] -> LDR.W Rt, [SP, #-<imm_8>]
311                                                 // !!!!!!!!!!!!!!!!!!!!!!!!
312                                                 // !!! imm_12 vs. imm_8 !!!
313                                                 // !!!!!!!!!!!!!!!!!!!!!!!!
314                                                 insns[2] = (insn & 0xf0fffff0) | 0x0c00000d;            // LDR.W Rt, [SP, #-<imm_8>]
315                                         } else {
316                                                 if (THUMB2_INSN_MATCH(LDRW1, insn) || THUMB2_INSN_MATCH(LDRBW1, insn) ||
317                                                     THUMB2_INSN_MATCH(LDRHW1, insn) || THUMB2_INSN_MATCH(LDRD, insn) ||
318                                                     THUMB2_INSN_MATCH(LDRD1, insn) || THUMB2_INSN_MATCH(LDREX, insn)) {
319                                                         // LDRx.W Rt, [PC, #+<imm_12>] -> LDRx.W Rt, [SP, #+<imm_12>] (+/-imm_8 for LDRD Rt, Rt2, [PC, #<imm_8>]
320                                                         insns[2] = (insn & 0xfffffff0) | 0xd;                                                                                                   // LDRx.W Rt, [SP, #+<imm_12>]
321                                                 } else {
322                                                         if (THUMB2_INSN_MATCH(MUL, insn)) {
323                                                                 insns[2] = (insn & 0xfff0ffff) | 0x000d0000;                                                                                    // MUL Rd, Rn, SP
324                                                         } else {
325                                                                 if (THUMB2_INSN_MATCH(DP, insn)) {
326                                                                         if (THUMB2_INSN_REG_RM(insn) == 15) {
327                                                                                 insns[2] = (insn & 0xfff0ffff) | 0x000d0000;                                                                    // DP Rd, Rn, PC
328                                                                         } else if (THUMB2_INSN_REG_RN(insn) == 15) {
329                                                                                 insns[2] = (insn & 0xfffffff0) | 0xd;                                                                           // DP Rd, PC, Rm
330                                                                         }
331                                                                 } else {
332                                                                         if (THUMB2_INSN_MATCH(LDRWL, insn)) {
333                                                                                 // LDRx.W Rt, [PC, #<imm_12>] -> LDRx.W Rt, [SP, #+<imm_12>] (+/-imm_8 for LDRD Rt, Rt2, [PC, #<imm_8>]
334                                                                                 insns[2] = (insn & 0xfffffff0) | 0xd;                                                                           // LDRx.W Rt, [SP, #+<imm_12>]
335                                                                         } else {
336                                                                                 if (THUMB2_INSN_MATCH(RSBW, insn)) {
337                                                                                         insns[2] = (insn & 0xfffffff0) | 0xd;                                                                   // RSB{S}.W Rd, PC, #<const> -> RSB{S}.W Rd, SP, #<const>
338                                                                                 } else {
339                                                                                         if (THUMB2_INSN_MATCH(RORW, insn) || THUMB2_INSN_MATCH(LSLW1, insn) || THUMB2_INSN_MATCH(LSRW1, insn)) {
340                                                                                                 if ((THUMB2_INSN_REG_RM(insn) == 15) && (THUMB2_INSN_REG_RN(insn) == 15)) {
341                                                                                                         insns[2] = (insn & 0xfffdfffd);                                                         // ROR.W Rd, PC, PC
342                                                                                                 } else if (THUMB2_INSN_REG_RM(insn) == 15) {
343                                                                                                         insns[2] = (insn & 0xfff0ffff) | 0xd0000;                                               // ROR.W Rd, Rn, PC
344                                                                                                 } else if (THUMB2_INSN_REG_RN(insn) == 15) {
345                                                                                                         insns[2] = (insn & 0xfffffff0) | 0xd;                                                   // ROR.W Rd, PC, Rm
346                                                                                                 }
347                                                                                         } else {
348                                                                                                 if (THUMB2_INSN_MATCH(ROR, insn) || THUMB2_INSN_MATCH(LSLW2, insn) || THUMB2_INSN_MATCH(LSRW2, insn)) {
349                                                                                                         insns[2] = (insn & 0xfff0ffff) | 0xd0000;                                               // ROR{S} Rd, PC, #<const> -> ROR{S} Rd, SP, #<const>
350                                                                                                 }
351                                                                                         }
352                                                                                 }
353                                                                         }
354                                                                 }
355                                                         }
356                                                 }
357                                         }
358                                 }
359                         }
360                 }
361         }
362
363         if (THUMB2_INSN_MATCH(STRW, insn) || THUMB2_INSN_MATCH(STRBW, insn)) {
364                 insns[2] = (insn & 0xfff0ffff) | 0x000d0000;                                                            // STRx.W Rt, [Rn, SP]
365         } else {
366                 if (THUMB2_INSN_MATCH(STRD, insn) || THUMB2_INSN_MATCH(STRHT, insn) ||
367                     THUMB2_INSN_MATCH(STRT, insn) || THUMB2_INSN_MATCH(STRHW1, insn)) {
368                         if (THUMB2_INSN_REG_RN(insn) == 15) {
369                                 insns[2] = (insn & 0xfffffff0) | 0xd;                                                   // STRD/T/HT{.W} Rt, [SP, ...]
370                         } else {
371                                 insns[2] = insn;
372                         }
373                 } else {
374                         if (THUMB2_INSN_MATCH(STRHW, insn) && (THUMB2_INSN_REG_RN(insn) == 15)) {
375                                 if (THUMB2_INSN_REG_RN(insn) == 15) {
376                                         insns[2] = (insn & 0xf0fffff0) | 0x0c00000d;                                    // STRH.W Rt, [SP, #-<imm_8>]
377                                 } else {
378                                         insns[2] = insn;
379                                 }
380                         }
381                 }
382         }
383
384         // STRx PC, xxx
385         if ((reg == 15) && (THUMB2_INSN_MATCH(STRW, insn)   ||
386                             THUMB2_INSN_MATCH(STRBW, insn)  ||
387                             THUMB2_INSN_MATCH(STRD, insn)   ||
388                             THUMB2_INSN_MATCH(STRHT, insn)  ||
389                             THUMB2_INSN_MATCH(STRT, insn)   ||
390                             THUMB2_INSN_MATCH(STRHW1, insn) ||
391                             THUMB2_INSN_MATCH(STRHW, insn) )) {
392                 insns[2] = (insns[2] & 0x0fffffff) | 0xd0000000;
393         }
394
395         if (THUMB2_INSN_MATCH(TEQ1, insn) || THUMB2_INSN_MATCH(TST1, insn)) {
396                 insns[2] = (insn & 0xfffffff0) | 0xd;                                                                   // TEQ SP, #<const>
397         } else {
398                 if (THUMB2_INSN_MATCH(TEQ2, insn) || THUMB2_INSN_MATCH(TST2, insn)) {
399                         if ((THUMB2_INSN_REG_RN(insn) == 15) && (THUMB2_INSN_REG_RM(insn) == 15)) {
400                                 insns[2] = (insn & 0xfffdfffd);                                                         // TEQ/TST PC, PC
401                         } else if (THUMB2_INSN_REG_RM(insn) == 15) {
402                                 insns[2] = (insn & 0xfff0ffff) | 0xd0000;                                               // TEQ/TST Rn, PC
403                         } else if (THUMB2_INSN_REG_RN(insn) == 15) {
404                                 insns[2] = (insn & 0xfffffff0) | 0xd;                                                   // TEQ/TST PC, Rm
405                         }
406                 }
407         }
408
409         return 0;
410 }
411
412 static int arch_copy_trampoline_thumb_uprobe(struct kprobe *p, struct task_struct *task, int atomic)
413 {
414         int uregs, pc_dep;
415         unsigned int addr;
416         kprobe_opcode_t insn[MAX_INSN_SIZE];
417         struct arch_specific_insn ainsn;
418         kprobe_opcode_t insns[UPROBES_TRAMP_LEN * 2];
419
420         p->safe_thumb = -1;
421         if ((unsigned long)p->addr & 0x01) {
422                 printk("Error in %s at %d: attempt to register kprobe at an unaligned address\n", __FILE__, __LINE__);
423                 return -EINVAL;
424         }
425
426         insn[0] = p->opcode;
427         ainsn.insn_thumb = insn;
428         if (!arch_check_insn_thumb(&ainsn)) {
429                 p->safe_thumb = 0;
430         }
431
432         uregs = 0;
433         pc_dep = 0;
434
435         if (THUMB_INSN_MATCH(APC, insn[0]) || THUMB_INSN_MATCH(LRO3, insn[0])) {
436                 uregs = 0x0700;         // 8-10
437                 pc_dep = 1;
438         } else if (THUMB_INSN_MATCH(MOV3, insn[0]) && (((((unsigned char)insn[0]) & 0xff) >> 3) == 15)) {
439                 // MOV Rd, PC
440                 uregs = 0x07;
441                 pc_dep = 1;
442         } else if THUMB2_INSN_MATCH(ADR, insn[0]) {
443                 uregs = 0x0f00;         // Rd 8-11
444                 pc_dep = 1;
445         } else if (((THUMB2_INSN_MATCH(LDRW, insn[0]) || THUMB2_INSN_MATCH(LDRW1, insn[0]) ||
446                      THUMB2_INSN_MATCH(LDRBW, insn[0]) || THUMB2_INSN_MATCH(LDRBW1, insn[0]) ||
447                      THUMB2_INSN_MATCH(LDRHW, insn[0]) || THUMB2_INSN_MATCH(LDRHW1, insn[0]) ||
448                      THUMB2_INSN_MATCH(LDRWL, insn[0])) && THUMB2_INSN_REG_RN(insn[0]) == 15) ||
449                      THUMB2_INSN_MATCH(LDREX, insn[0]) ||
450                      ((THUMB2_INSN_MATCH(STRW, insn[0]) || THUMB2_INSN_MATCH(STRBW, insn[0]) ||
451                        THUMB2_INSN_MATCH(STRHW, insn[0]) || THUMB2_INSN_MATCH(STRHW1, insn[0])) &&
452                       (THUMB2_INSN_REG_RN(insn[0]) == 15 || THUMB2_INSN_REG_RT(insn[0]) == 15)) ||
453                      ((THUMB2_INSN_MATCH(STRT, insn[0]) || THUMB2_INSN_MATCH(STRHT, insn[0])) &&
454                        (THUMB2_INSN_REG_RN(insn[0]) == 15 || THUMB2_INSN_REG_RT(insn[0]) == 15))) {
455                 uregs = 0xf000;         // Rt 12-15
456                 pc_dep = 1;
457         } else if ((THUMB2_INSN_MATCH(LDRD, insn[0]) || THUMB2_INSN_MATCH(LDRD1, insn[0])) && (THUMB2_INSN_REG_RN(insn[0]) == 15)) {
458                 uregs = 0xff00;         // Rt 12-15, Rt2 8-11
459                 pc_dep = 1;
460         } else if (THUMB2_INSN_MATCH(MUL, insn[0]) && THUMB2_INSN_REG_RM(insn[0]) == 15) {
461                 uregs = 0xf;
462                 pc_dep = 1;
463         } else if (THUMB2_INSN_MATCH(DP, insn[0]) && (THUMB2_INSN_REG_RN(insn[0]) == 15 || THUMB2_INSN_REG_RM(insn[0]) == 15)) {
464                 uregs = 0xf000;         // Rd 12-15
465                 pc_dep = 1;
466         } else if (THUMB2_INSN_MATCH(STRD, insn[0]) && ((THUMB2_INSN_REG_RN(insn[0]) == 15) || (THUMB2_INSN_REG_RT(insn[0]) == 15) || THUMB2_INSN_REG_RT2(insn[0]) == 15)) {
467                 uregs = 0xff00;         // Rt 12-15, Rt2 8-11
468                 pc_dep = 1;
469         } else if (THUMB2_INSN_MATCH(RSBW, insn[0]) && THUMB2_INSN_REG_RN(insn[0]) == 15) {
470                 uregs = 0x0f00;         // Rd 8-11
471                 pc_dep = 1;
472         } else if (THUMB2_INSN_MATCH (RORW, insn[0]) && (THUMB2_INSN_REG_RN(insn[0]) == 15 || THUMB2_INSN_REG_RM(insn[0]) == 15)) {
473                 uregs = 0x0f00;
474                 pc_dep = 1;
475         } else if ((THUMB2_INSN_MATCH(ROR, insn[0]) || THUMB2_INSN_MATCH(LSLW2, insn[0]) || THUMB2_INSN_MATCH(LSRW2, insn[0])) && THUMB2_INSN_REG_RM(insn[0]) == 15) {
476                 uregs = 0x0f00;         // Rd 8-11
477                 pc_dep = 1;
478         } else if ((THUMB2_INSN_MATCH(LSLW1, insn[0]) || THUMB2_INSN_MATCH(LSRW1, insn[0])) && (THUMB2_INSN_REG_RN(insn[0]) == 15 || THUMB2_INSN_REG_RM(insn[0]) == 15)) {
479                 uregs = 0x0f00;         // Rd 8-11
480                 pc_dep = 1;
481         } else if ((THUMB2_INSN_MATCH(TEQ1, insn[0]) || THUMB2_INSN_MATCH(TST1, insn[0])) && THUMB2_INSN_REG_RN(insn[0]) == 15) {
482                 uregs = 0xf0000;        //Rn 0-3 (16-19)
483                 pc_dep = 1;
484         } else if ((THUMB2_INSN_MATCH(TEQ2, insn[0]) || THUMB2_INSN_MATCH(TST2, insn[0])) &&
485                    (THUMB2_INSN_REG_RN(insn[0]) == 15 || THUMB2_INSN_REG_RM(insn[0]) == 15)) {
486                 uregs = 0xf0000;        //Rn 0-3 (16-19)
487                 pc_dep = 1;
488         }
489
490         if (unlikely(uregs && pc_dep)) {
491                 memcpy(insns, pc_dep_insn_execbuf_thumb, 18 * 2);
492                 if (prep_pc_dep_insn_execbuf_thumb(insns, insn[0], uregs) != 0) {
493                         printk("Error in %s at %d: failed to prepare exec buffer for insn %lx!",
494                                __FILE__, __LINE__, insn[0]);
495                         p->safe_thumb = -1;
496                         //free_insn_slot (&uprobe_insn_pages, task, p->ainsn.insn_thumb, 0);
497                         //return -EINVAL;
498                 }
499
500                 addr = ((unsigned int)p->addr) + 4;
501                 *((unsigned short*)insns + 13) = 0xdeff;
502                 *((unsigned short*)insns + 14) = addr & 0x0000ffff;
503                 *((unsigned short*)insns + 15) = addr >> 16;
504                 if (!is_thumb2(insn[0])) {
505                         addr = ((unsigned int)p->addr) + 2;
506                         *((unsigned short*)insns + 16) = (addr & 0x0000ffff) | 0x1;
507                         *((unsigned short*)insns + 17) = addr >> 16;
508                 } else {
509                         addr = ((unsigned int)p->addr) + 4;
510                         *((unsigned short*)insns + 16) = (addr & 0x0000ffff) | 0x1;
511                         *((unsigned short*)insns + 17) = addr >> 16;
512                 }
513         } else {
514                 memcpy(insns, gen_insn_execbuf_thumb, 18 * 2);
515                 *((unsigned short*)insns + 13) = 0xdeff;
516                 if (!is_thumb2(insn[0])) {
517                         addr = ((unsigned int)p->addr) + 2;
518                         *((unsigned short*)insns + 2) = insn[0];
519                         *((unsigned short*)insns + 16) = (addr & 0x0000ffff) | 0x1;
520                         *((unsigned short*)insns + 17) = addr >> 16;
521                 } else {
522                         addr = ((unsigned int)p->addr) + 4;
523                         insns[1] = insn[0];
524                         *((unsigned short*)insns + 16) = (addr & 0x0000ffff) | 0x1;
525                         *((unsigned short*)insns + 17) = addr >> 16;
526                 }
527         }
528
529         if (!write_proc_vm_atomic (task, (unsigned long)p->ainsn.insn_thumb, insns, 18 * 2)) {
530                 panic("failed to write memory %p!\n", p->ainsn.insn_thumb);
531                 // Mr_Nobody: we have to panic, really??...
532                 //free_insn_slot (&uprobe_insn_pages, task, p->ainsn.insn_thumb, 0);
533                 //return -EINVAL;
534         }
535
536         return 0;
537 }
538
539 int arch_prepare_uprobe(struct kprobe *p, struct task_struct *task, int atomic)
540 {
541         int ret = 0;
542         kprobe_opcode_t insn[MAX_INSN_SIZE];
543
544         if ((unsigned long)p->addr & 0x01) {
545                 printk("Error in %s at %d: attempt to register kprobe at an unaligned address\n", __FILE__, __LINE__);
546                 return -EINVAL;
547         }
548
549         if (!read_proc_vm_atomic(task, (unsigned long)p->addr, &insn, MAX_INSN_SIZE * sizeof(kprobe_opcode_t))) {
550                 panic("Failed to read memory task[tgid=%u, comm=%s] %p!\n", task->tgid, task->comm, p->addr);
551         }
552
553         p->opcode = insn[0];
554         p->ainsn.insn_arm = get_insn_slot(task, &uprobe_insn_pages, atomic);
555         if (!p->ainsn.insn_arm) {
556                 printk("Error in %s at %d: kprobe slot allocation error (arm)\n", __FILE__, __LINE__);
557                 return -ENOMEM;
558         }
559
560         ret = arch_copy_trampoline_arm_uprobe(p, task, 1);
561         if (ret) {
562                 free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_arm);
563                 return -EFAULT;
564         }
565
566         p->ainsn.insn_thumb = get_insn_slot(task, &uprobe_insn_pages, atomic);
567         if (!p->ainsn.insn_thumb) {
568                 printk("Error in %s at %d: kprobe slot allocation error (thumb)\n", __FILE__, __LINE__);
569                 return -ENOMEM;
570         }
571
572         ret = arch_copy_trampoline_thumb_uprobe(p, task, 1);
573         if (ret) {
574                 free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_arm);
575                 free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_thumb);
576                 return -EFAULT;
577         }
578
579         if ((p->safe_arm == -1) && (p->safe_thumb == -1)) {
580                 printk("Error in %s at %d: failed arch_copy_trampoline_*_uprobe() (both) [tgid=%u, addr=%lx, data=%lx]\n",
581                        __FILE__, __LINE__, task->tgid, (unsigned long)p->addr, (unsigned long)p->opcode);
582                 if (!write_proc_vm_atomic(task, (unsigned long)p->addr, &p->opcode, sizeof(p->opcode))) {
583                         panic("Failed to write memory %p!\n", p->addr);
584                 }
585
586                 free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_arm);
587                 free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_thumb);
588
589                 return -EFAULT;
590         }
591
592         return ret;
593 }
594
595 int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs)
596 {
597         struct jprobe *jp = container_of(p, struct jprobe, kp);
598         kprobe_pre_entry_handler_t pre_entry = (kprobe_pre_entry_handler_t)jp->pre_entry;
599         entry_point_t entry = (entry_point_t)jp->entry;
600
601         if (pre_entry) {
602                 p->ss_addr = (kprobe_opcode_t *)pre_entry(jp->priv_arg, regs);
603         }
604
605         if (entry) {
606                 entry(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2,
607                       regs->ARM_r3, regs->ARM_r4, regs->ARM_r5);
608         } else {
609                 dbi_arch_uprobe_return();
610         }
611
612         prepare_singlestep(p, regs);
613
614         return 1;
615 }
616
617 static int check_validity_insn(struct kprobe *p, struct pt_regs *regs, struct task_struct *task)
618 {
619         struct kprobe *kp;
620
621         if (unlikely(thumb_mode(regs))) {
622                 if (p->safe_thumb != -1) {
623                         p->ainsn.insn = p->ainsn.insn_thumb;
624                         list_for_each_entry_rcu(kp, &p->list, list) {
625                                 kp->ainsn.insn = p->ainsn.insn_thumb;
626                         }
627                 } else {
628                         printk("Error in %s at %d: we are in thumb mode (!) and check instruction was fail \
629                                 (%0lX instruction at %p address)!\n", __FILE__, __LINE__, p->opcode, p->addr);
630                         // Test case when we do our actions on already running application
631                         arch_disarm_uprobe(p, task);
632                         return -1;
633                 }
634         } else {
635                 if (p->safe_arm != -1) {
636                         p->ainsn.insn = p->ainsn.insn_arm;
637                         list_for_each_entry_rcu(kp, &p->list, list) {
638                                 kp->ainsn.insn = p->ainsn.insn_arm;
639                         }
640                 } else {
641                         printk("Error in %s at %d: we are in arm mode (!) and check instruction was fail \
642                                 (%0lX instruction at %p address)!\n", __FILE__, __LINE__, p->opcode, p->addr);
643                         // Test case when we do our actions on already running application
644                         arch_disarm_uprobe(p, task);
645                         return -1;
646                 }
647         }
648
649         return 0;
650 }
651
652 static int uprobe_handler(struct pt_regs *regs)
653 {
654         int err_out = 0;
655         char *msg_out = NULL;
656         struct task_struct *task = current;
657         pid_t tgid = task->tgid;
658         kprobe_opcode_t *addr = (kprobe_opcode_t *)(regs->ARM_pc);
659         struct kprobe *p = NULL;
660         int ret = 0, retprobe = 0;
661         struct kprobe_ctlblk *kcb;
662
663 #ifdef SUPRESS_BUG_MESSAGES
664         int swap_oops_in_progress;
665         // oops_in_progress used to avoid BUG() messages that slow down kprobe_handler() execution
666         swap_oops_in_progress = oops_in_progress;
667         oops_in_progress = 1;
668 #endif
669
670         p = get_kprobe(addr, tgid);
671
672         if (p && (check_validity_insn(p, regs, task) != 0)) {
673                 goto no_uprobe_live;
674         }
675
676         /* We're in an interrupt, but this is clear and BUG()-safe. */
677         kcb = get_kprobe_ctlblk();
678
679         if (p == NULL) {
680                 p = get_kprobe_by_insn_slot(addr, tgid, regs);
681                 if (p == NULL) {
682                         /* Not one of ours: let kernel handle it */
683                         goto no_uprobe;
684                 }
685
686                 retprobe = 1;
687         }
688
689         /* restore opcode for thumb app */
690         if (thumb_mode(regs)) {
691                 if (!is_thumb2(p->opcode)) {
692                         unsigned long tmp = p->opcode >> 16;
693                         write_proc_vm_atomic(task, (unsigned long)((unsigned short*)p->addr + 1), &tmp, 2);
694
695                         // "2*sizeof(kprobe_opcode_t)" - strange. Should be "sizeof(kprobe_opcode_t)", need to test
696                         flush_icache_range((unsigned int)p->addr, ((unsigned int)p->addr) + (2 * sizeof(kprobe_opcode_t)));
697                 }
698         }
699
700         set_current_kprobe(p, NULL, NULL);
701         kcb->kprobe_status = KPROBE_HIT_ACTIVE;
702
703         if (retprobe) {
704                 ret = trampoline_probe_handler(p, regs);
705         } else if (p->pre_handler) {
706                 ret = p->pre_handler(p, regs);
707                 if(p->pre_handler != trampoline_probe_handler) {
708                         reset_current_kprobe();
709                 }
710         }
711
712         if (ret) {
713                 /* handler has already set things up, so skip ss setup */
714                 err_out = 0;
715                 goto out;
716         }
717
718 no_uprobe:
719         msg_out = "no_uprobe\n";
720         err_out = 1;            // return with death
721         goto out;
722
723 no_uprobe_live:
724         msg_out = "no_uprobe live\n";
725         err_out = 0;            // ok - life is life
726         goto out;
727
728 out:
729 #ifdef SUPRESS_BUG_MESSAGES
730         oops_in_progress = swap_oops_in_progress;
731 #endif
732
733         if(msg_out) {
734                 printk(msg_out);
735         }
736
737         return err_out;
738 }
739
740 int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
741 {
742         int ret;
743         unsigned long flags;
744         local_irq_save(flags);
745
746         preempt_disable();
747         ret = uprobe_handler(regs);
748         preempt_enable_no_resched();
749
750         local_irq_restore(flags);
751         return ret;
752 }
753
754 /* userspace probes hook (arm) */
755 static struct undef_hook undef_hook_for_us_arm = {
756         .instr_mask     = 0xffffffff,
757         .instr_val      = BREAKPOINT_INSTRUCTION,
758         .cpsr_mask      = MODE_MASK,
759         .cpsr_val       = USR_MODE,
760         .fn             = uprobe_trap_handler
761 };
762
763 /* userspace probes hook (thumb) */
764 static struct undef_hook undef_hook_for_us_thumb = {
765         .instr_mask     = 0xffffffff,
766         .instr_val      = BREAKPOINT_INSTRUCTION & 0x0000ffff,
767         .cpsr_mask      = MODE_MASK,
768         .cpsr_val       = USR_MODE,
769         .fn             = uprobe_trap_handler
770 };
771
772 int swap_arch_init_uprobes(void)
773 {
774         swap_register_undef_hook(&undef_hook_for_us_arm);
775         swap_register_undef_hook(&undef_hook_for_us_thumb);
776
777         return 0;
778 }
779
780 void swap_arch_exit_uprobes(void)
781 {
782         swap_unregister_undef_hook(&undef_hook_for_us_thumb);
783         swap_unregister_undef_hook(&undef_hook_for_us_arm);
784 }