| Thomas Gleixner | d2912cb | 2019-06-04 10:11:33 +0200 | [diff] [blame^] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
| Jon Medhurst | 2437170 | 2011-04-19 17:56:58 +0100 | [diff] [blame] | 2 | /* |
| Wang Nan | fca08f3 | 2015-01-09 10:19:49 +0800 | [diff] [blame] | 3 | * arch/arm/probes/kprobes/actions-thumb.c |
| Jon Medhurst | 2437170 | 2011-04-19 17:56:58 +0100 | [diff] [blame] | 4 | * |
| 5 | * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>. |
| Jon Medhurst | 2437170 | 2011-04-19 17:56:58 +0100 | [diff] [blame] | 6 | */ |
| 7 | |
| David A. Long | 87abef6 | 2014-03-05 21:06:29 -0500 | [diff] [blame] | 8 | #include <linux/types.h> |
| Jon Medhurst | 2437170 | 2011-04-19 17:56:58 +0100 | [diff] [blame] | 9 | #include <linux/kernel.h> |
| David A. Long | 87abef6 | 2014-03-05 21:06:29 -0500 | [diff] [blame] | 10 | #include <linux/ptrace.h> |
| Jon Medhurst | 2437170 | 2011-04-19 17:56:58 +0100 | [diff] [blame] | 11 | #include <linux/kprobes.h> |
| 12 | |
| Wang Nan | fca08f3 | 2015-01-09 10:19:49 +0800 | [diff] [blame] | 13 | #include "../decode-thumb.h" |
| 14 | #include "core.h" |
| Wang Nan | 6624cf6 | 2015-01-05 19:29:21 +0800 | [diff] [blame] | 15 | #include "checkers.h" |
| Jon Medhurst | eaf4f33f | 2011-04-20 19:29:52 +0100 | [diff] [blame] | 16 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 17 | /* These emulation encodings are functionally equivalent... */ |
| 18 | #define t32_emulate_rd8rn16rm0ra12_noflags \ |
| 19 | t32_emulate_rdlo12rdhi8rn16rm0_noflags |
| 20 | |
| David A. Long | 87abef6 | 2014-03-05 21:06:29 -0500 | [diff] [blame] | 21 | /* t32 thumb actions */ |
| 22 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 23 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 24 | t32_simulate_table_branch(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 25 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | dd212bd3 | 2011-07-03 14:26:16 +0100 | [diff] [blame] | 26 | { |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 27 | unsigned long pc = regs->ARM_pc; |
| Jon Medhurst | dd212bd3 | 2011-07-03 14:26:16 +0100 | [diff] [blame] | 28 | int rn = (insn >> 16) & 0xf; |
| 29 | int rm = insn & 0xf; |
| 30 | |
| 31 | unsigned long rnv = (rn == 15) ? pc : regs->uregs[rn]; |
| 32 | unsigned long rmv = regs->uregs[rm]; |
| 33 | unsigned int halfwords; |
| 34 | |
| Jon Medhurst | ce715c7 | 2011-07-03 14:53:45 +0100 | [diff] [blame] | 35 | if (insn & 0x10) /* TBH */ |
| Jon Medhurst | dd212bd3 | 2011-07-03 14:26:16 +0100 | [diff] [blame] | 36 | halfwords = ((u16 *)rnv)[rmv]; |
| Jon Medhurst | ce715c7 | 2011-07-03 14:53:45 +0100 | [diff] [blame] | 37 | else /* TBB */ |
| Jon Medhurst | dd212bd3 | 2011-07-03 14:26:16 +0100 | [diff] [blame] | 38 | halfwords = ((u8 *)rnv)[rmv]; |
| 39 | |
| 40 | regs->ARM_pc = pc + 2 * halfwords; |
| 41 | } |
| 42 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 43 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 44 | t32_simulate_mrs(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 45 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | b06f3ee | 2011-07-03 14:52:18 +0100 | [diff] [blame] | 46 | { |
| Jon Medhurst | b06f3ee | 2011-07-03 14:52:18 +0100 | [diff] [blame] | 47 | int rd = (insn >> 8) & 0xf; |
| 48 | unsigned long mask = 0xf8ff03df; /* Mask out execution state */ |
| 49 | regs->uregs[rd] = regs->ARM_cpsr & mask; |
| 50 | } |
| 51 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 52 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 53 | t32_simulate_cond_branch(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 54 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | ce715c7 | 2011-07-03 14:53:45 +0100 | [diff] [blame] | 55 | { |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 56 | unsigned long pc = regs->ARM_pc; |
| Jon Medhurst | ce715c7 | 2011-07-03 14:53:45 +0100 | [diff] [blame] | 57 | |
| 58 | long offset = insn & 0x7ff; /* imm11 */ |
| 59 | offset += (insn & 0x003f0000) >> 5; /* imm6 */ |
| 60 | offset += (insn & 0x00002000) << 4; /* J1 */ |
| 61 | offset += (insn & 0x00000800) << 7; /* J2 */ |
| 62 | offset -= (insn & 0x04000000) >> 7; /* Apply sign bit */ |
| 63 | |
| 64 | regs->ARM_pc = pc + (offset * 2); |
| 65 | } |
| 66 | |
| David A. Long | 44a0a59 | 2014-03-05 21:23:42 -0500 | [diff] [blame] | 67 | static enum probes_insn __kprobes |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 68 | t32_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi, |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 69 | const struct decode_header *d) |
| Jon Medhurst | ce715c7 | 2011-07-03 14:53:45 +0100 | [diff] [blame] | 70 | { |
| 71 | int cc = (insn >> 22) & 0xf; |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 72 | asi->insn_check_cc = probes_condition_checks[cc]; |
| Jon Medhurst | ce715c7 | 2011-07-03 14:53:45 +0100 | [diff] [blame] | 73 | asi->insn_handler = t32_simulate_cond_branch; |
| 74 | return INSN_GOOD_NO_SLOT; |
| 75 | } |
| 76 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 77 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 78 | t32_simulate_branch(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 79 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | ce715c7 | 2011-07-03 14:53:45 +0100 | [diff] [blame] | 80 | { |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 81 | unsigned long pc = regs->ARM_pc; |
| Jon Medhurst | ce715c7 | 2011-07-03 14:53:45 +0100 | [diff] [blame] | 82 | |
| 83 | long offset = insn & 0x7ff; /* imm11 */ |
| 84 | offset += (insn & 0x03ff0000) >> 5; /* imm10 */ |
| 85 | offset += (insn & 0x00002000) << 9; /* J1 */ |
| 86 | offset += (insn & 0x00000800) << 10; /* J2 */ |
| 87 | if (insn & 0x04000000) |
| 88 | offset -= 0x00800000; /* Apply sign bit */ |
| 89 | else |
| 90 | offset ^= 0x00600000; /* Invert J1 and J2 */ |
| 91 | |
| 92 | if (insn & (1 << 14)) { |
| 93 | /* BL or BLX */ |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 94 | regs->ARM_lr = regs->ARM_pc | 1; |
| Jon Medhurst | ce715c7 | 2011-07-03 14:53:45 +0100 | [diff] [blame] | 95 | if (!(insn & (1 << 12))) { |
| 96 | /* BLX so switch to ARM mode */ |
| 97 | regs->ARM_cpsr &= ~PSR_T_BIT; |
| 98 | pc &= ~3; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | regs->ARM_pc = pc + (offset * 2); |
| 103 | } |
| 104 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 105 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 106 | t32_simulate_ldr_literal(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 107 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | d691023 | 2011-07-03 15:04:26 +0100 | [diff] [blame] | 108 | { |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 109 | unsigned long addr = regs->ARM_pc & ~3; |
| Jon Medhurst | d691023 | 2011-07-03 15:04:26 +0100 | [diff] [blame] | 110 | int rt = (insn >> 12) & 0xf; |
| 111 | unsigned long rtv; |
| 112 | |
| 113 | long offset = insn & 0xfff; |
| 114 | if (insn & 0x00800000) |
| 115 | addr += offset; |
| 116 | else |
| 117 | addr -= offset; |
| 118 | |
| 119 | if (insn & 0x00400000) { |
| 120 | /* LDR */ |
| 121 | rtv = *(unsigned long *)addr; |
| 122 | if (rt == 15) { |
| 123 | bx_write_pc(rtv, regs); |
| 124 | return; |
| 125 | } |
| 126 | } else if (insn & 0x00200000) { |
| 127 | /* LDRH */ |
| 128 | if (insn & 0x01000000) |
| 129 | rtv = *(s16 *)addr; |
| 130 | else |
| 131 | rtv = *(u16 *)addr; |
| 132 | } else { |
| 133 | /* LDRB */ |
| 134 | if (insn & 0x01000000) |
| 135 | rtv = *(s8 *)addr; |
| 136 | else |
| 137 | rtv = *(u8 *)addr; |
| 138 | } |
| 139 | |
| 140 | regs->uregs[rt] = rtv; |
| 141 | } |
| 142 | |
| David A. Long | 44a0a59 | 2014-03-05 21:23:42 -0500 | [diff] [blame] | 143 | static enum probes_insn __kprobes |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 144 | t32_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi, |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 145 | const struct decode_header *d) |
| Jon Medhurst | eaf1d06 | 2011-07-07 08:59:32 +0100 | [diff] [blame] | 146 | { |
| David A. Long | 44a0a59 | 2014-03-05 21:23:42 -0500 | [diff] [blame] | 147 | enum probes_insn ret = kprobe_decode_ldmstm(insn, asi, d); |
| Jon Medhurst | eaf1d06 | 2011-07-07 08:59:32 +0100 | [diff] [blame] | 148 | |
| 149 | /* Fixup modified instruction to have halfwords in correct order...*/ |
| Ben Dooks | 888be25 | 2013-11-08 18:29:25 +0000 | [diff] [blame] | 150 | insn = __mem_to_opcode_arm(asi->insn[0]); |
| 151 | ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn >> 16); |
| 152 | ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0xffff); |
| Jon Medhurst | eaf1d06 | 2011-07-07 08:59:32 +0100 | [diff] [blame] | 153 | |
| 154 | return ret; |
| 155 | } |
| 156 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 157 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 158 | t32_emulate_ldrdstrd(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 159 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | b48354d | 2011-07-03 14:23:21 +0100 | [diff] [blame] | 160 | { |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 161 | unsigned long pc = regs->ARM_pc & ~3; |
| Jon Medhurst | b48354d | 2011-07-03 14:23:21 +0100 | [diff] [blame] | 162 | int rt1 = (insn >> 12) & 0xf; |
| 163 | int rt2 = (insn >> 8) & 0xf; |
| 164 | int rn = (insn >> 16) & 0xf; |
| 165 | |
| 166 | register unsigned long rt1v asm("r0") = regs->uregs[rt1]; |
| 167 | register unsigned long rt2v asm("r1") = regs->uregs[rt2]; |
| 168 | register unsigned long rnv asm("r2") = (rn == 15) ? pc |
| 169 | : regs->uregs[rn]; |
| 170 | |
| 171 | __asm__ __volatile__ ( |
| 172 | "blx %[fn]" |
| 173 | : "=r" (rt1v), "=r" (rt2v), "=r" (rnv) |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 174 | : "0" (rt1v), "1" (rt2v), "2" (rnv), [fn] "r" (asi->insn_fn) |
| Jon Medhurst | b48354d | 2011-07-03 14:23:21 +0100 | [diff] [blame] | 175 | : "lr", "memory", "cc" |
| 176 | ); |
| 177 | |
| 178 | if (rn != 15) |
| 179 | regs->uregs[rn] = rnv; /* Writeback base register */ |
| 180 | regs->uregs[rt1] = rt1v; |
| 181 | regs->uregs[rt2] = rt2v; |
| 182 | } |
| 183 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 184 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 185 | t32_emulate_ldrstr(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 186 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | d691023 | 2011-07-03 15:04:26 +0100 | [diff] [blame] | 187 | { |
| Jon Medhurst | d691023 | 2011-07-03 15:04:26 +0100 | [diff] [blame] | 188 | int rt = (insn >> 12) & 0xf; |
| 189 | int rn = (insn >> 16) & 0xf; |
| 190 | int rm = insn & 0xf; |
| 191 | |
| 192 | register unsigned long rtv asm("r0") = regs->uregs[rt]; |
| 193 | register unsigned long rnv asm("r2") = regs->uregs[rn]; |
| 194 | register unsigned long rmv asm("r3") = regs->uregs[rm]; |
| 195 | |
| 196 | __asm__ __volatile__ ( |
| 197 | "blx %[fn]" |
| 198 | : "=r" (rtv), "=r" (rnv) |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 199 | : "0" (rtv), "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) |
| Jon Medhurst | d691023 | 2011-07-03 15:04:26 +0100 | [diff] [blame] | 200 | : "lr", "memory", "cc" |
| 201 | ); |
| 202 | |
| 203 | regs->uregs[rn] = rnv; /* Writeback base register */ |
| 204 | if (rt == 15) /* Can't be true for a STR as they aren't allowed */ |
| 205 | bx_write_pc(rtv, regs); |
| 206 | else |
| 207 | regs->uregs[rt] = rtv; |
| 208 | } |
| 209 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 210 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 211 | t32_emulate_rd8rn16rm0_rwflags(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 212 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 080e001 | 2011-07-03 14:31:58 +0100 | [diff] [blame] | 213 | { |
| Jon Medhurst | 080e001 | 2011-07-03 14:31:58 +0100 | [diff] [blame] | 214 | int rd = (insn >> 8) & 0xf; |
| 215 | int rn = (insn >> 16) & 0xf; |
| 216 | int rm = insn & 0xf; |
| 217 | |
| 218 | register unsigned long rdv asm("r1") = regs->uregs[rd]; |
| 219 | register unsigned long rnv asm("r2") = regs->uregs[rn]; |
| 220 | register unsigned long rmv asm("r3") = regs->uregs[rm]; |
| 221 | unsigned long cpsr = regs->ARM_cpsr; |
| 222 | |
| 223 | __asm__ __volatile__ ( |
| 224 | "msr cpsr_fs, %[cpsr] \n\t" |
| 225 | "blx %[fn] \n\t" |
| 226 | "mrs %[cpsr], cpsr \n\t" |
| 227 | : "=r" (rdv), [cpsr] "=r" (cpsr) |
| 228 | : "0" (rdv), "r" (rnv), "r" (rmv), |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 229 | "1" (cpsr), [fn] "r" (asi->insn_fn) |
| Jon Medhurst | 080e001 | 2011-07-03 14:31:58 +0100 | [diff] [blame] | 230 | : "lr", "memory", "cc" |
| 231 | ); |
| 232 | |
| 233 | regs->uregs[rd] = rdv; |
| 234 | regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); |
| 235 | } |
| 236 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 237 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 238 | t32_emulate_rd8pc16_noflags(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 239 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 7848786 | 2011-07-03 14:40:26 +0100 | [diff] [blame] | 240 | { |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 241 | unsigned long pc = regs->ARM_pc; |
| Jon Medhurst | 7848786 | 2011-07-03 14:40:26 +0100 | [diff] [blame] | 242 | int rd = (insn >> 8) & 0xf; |
| 243 | |
| 244 | register unsigned long rdv asm("r1") = regs->uregs[rd]; |
| 245 | register unsigned long rnv asm("r2") = pc & ~3; |
| 246 | |
| 247 | __asm__ __volatile__ ( |
| 248 | "blx %[fn]" |
| 249 | : "=r" (rdv) |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 250 | : "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn) |
| Jon Medhurst | 7848786 | 2011-07-03 14:40:26 +0100 | [diff] [blame] | 251 | : "lr", "memory", "cc" |
| 252 | ); |
| 253 | |
| 254 | regs->uregs[rd] = rdv; |
| 255 | } |
| 256 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 257 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 258 | t32_emulate_rd8rn16_noflags(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 259 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 7848786 | 2011-07-03 14:40:26 +0100 | [diff] [blame] | 260 | { |
| Jon Medhurst | 7848786 | 2011-07-03 14:40:26 +0100 | [diff] [blame] | 261 | int rd = (insn >> 8) & 0xf; |
| 262 | int rn = (insn >> 16) & 0xf; |
| 263 | |
| 264 | register unsigned long rdv asm("r1") = regs->uregs[rd]; |
| 265 | register unsigned long rnv asm("r2") = regs->uregs[rn]; |
| 266 | |
| 267 | __asm__ __volatile__ ( |
| 268 | "blx %[fn]" |
| 269 | : "=r" (rdv) |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 270 | : "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn) |
| Jon Medhurst | 7848786 | 2011-07-03 14:40:26 +0100 | [diff] [blame] | 271 | : "lr", "memory", "cc" |
| 272 | ); |
| 273 | |
| 274 | regs->uregs[rd] = rdv; |
| 275 | } |
| 276 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 277 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 278 | t32_emulate_rdlo12rdhi8rn16rm0_noflags(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 279 | struct arch_probes_insn *asi, |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 280 | struct pt_regs *regs) |
| Jon Medhurst | 231fb15 | 2011-07-03 15:15:11 +0100 | [diff] [blame] | 281 | { |
| Jon Medhurst | 231fb15 | 2011-07-03 15:15:11 +0100 | [diff] [blame] | 282 | int rdlo = (insn >> 12) & 0xf; |
| 283 | int rdhi = (insn >> 8) & 0xf; |
| 284 | int rn = (insn >> 16) & 0xf; |
| 285 | int rm = insn & 0xf; |
| 286 | |
| 287 | register unsigned long rdlov asm("r0") = regs->uregs[rdlo]; |
| 288 | register unsigned long rdhiv asm("r1") = regs->uregs[rdhi]; |
| 289 | register unsigned long rnv asm("r2") = regs->uregs[rn]; |
| 290 | register unsigned long rmv asm("r3") = regs->uregs[rm]; |
| 291 | |
| 292 | __asm__ __volatile__ ( |
| 293 | "blx %[fn]" |
| 294 | : "=r" (rdlov), "=r" (rdhiv) |
| 295 | : "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv), |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 296 | [fn] "r" (asi->insn_fn) |
| Jon Medhurst | 231fb15 | 2011-07-03 15:15:11 +0100 | [diff] [blame] | 297 | : "lr", "memory", "cc" |
| 298 | ); |
| 299 | |
| 300 | regs->uregs[rdlo] = rdlov; |
| 301 | regs->uregs[rdhi] = rdhiv; |
| 302 | } |
| David A. Long | 87abef6 | 2014-03-05 21:06:29 -0500 | [diff] [blame] | 303 | /* t16 thumb actions */ |
| Jon Medhurst | 231fb15 | 2011-07-03 15:15:11 +0100 | [diff] [blame] | 304 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 305 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 306 | t16_simulate_bxblx(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 307 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | a9c3c29 | 2011-07-02 15:51:03 +0100 | [diff] [blame] | 308 | { |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 309 | unsigned long pc = regs->ARM_pc + 2; |
| Jon Medhurst | a9c3c29 | 2011-07-02 15:51:03 +0100 | [diff] [blame] | 310 | int rm = (insn >> 3) & 0xf; |
| 311 | unsigned long rmv = (rm == 15) ? pc : regs->uregs[rm]; |
| 312 | |
| 313 | if (insn & (1 << 7)) /* BLX ? */ |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 314 | regs->ARM_lr = regs->ARM_pc | 1; |
| Jon Medhurst | a9c3c29 | 2011-07-02 15:51:03 +0100 | [diff] [blame] | 315 | |
| 316 | bx_write_pc(rmv, regs); |
| 317 | } |
| 318 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 319 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 320 | t16_simulate_ldr_literal(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 321 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | f869514 | 2011-07-02 16:00:09 +0100 | [diff] [blame] | 322 | { |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 323 | unsigned long *base = (unsigned long *)((regs->ARM_pc + 2) & ~3); |
| Jon Medhurst | f869514 | 2011-07-02 16:00:09 +0100 | [diff] [blame] | 324 | long index = insn & 0xff; |
| 325 | int rt = (insn >> 8) & 0x7; |
| 326 | regs->uregs[rt] = base[index]; |
| 327 | } |
| 328 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 329 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 330 | t16_simulate_ldrstr_sp_relative(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 331 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | f869514 | 2011-07-02 16:00:09 +0100 | [diff] [blame] | 332 | { |
| Jon Medhurst | f869514 | 2011-07-02 16:00:09 +0100 | [diff] [blame] | 333 | unsigned long* base = (unsigned long *)regs->ARM_sp; |
| 334 | long index = insn & 0xff; |
| 335 | int rt = (insn >> 8) & 0x7; |
| 336 | if (insn & 0x800) /* LDR */ |
| 337 | regs->uregs[rt] = base[index]; |
| 338 | else /* STR */ |
| 339 | base[index] = regs->uregs[rt]; |
| 340 | } |
| 341 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 342 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 343 | t16_simulate_reladr(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 344 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 2f33582 | 2011-07-02 16:05:53 +0100 | [diff] [blame] | 345 | { |
| Jon Medhurst | 2f33582 | 2011-07-02 16:05:53 +0100 | [diff] [blame] | 346 | unsigned long base = (insn & 0x800) ? regs->ARM_sp |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 347 | : ((regs->ARM_pc + 2) & ~3); |
| Jon Medhurst | 2f33582 | 2011-07-02 16:05:53 +0100 | [diff] [blame] | 348 | long offset = insn & 0xff; |
| 349 | int rt = (insn >> 8) & 0x7; |
| 350 | regs->uregs[rt] = base + offset * 4; |
| 351 | } |
| 352 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 353 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 354 | t16_simulate_add_sp_imm(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 355 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 2f33582 | 2011-07-02 16:05:53 +0100 | [diff] [blame] | 356 | { |
| Jon Medhurst | 2f33582 | 2011-07-02 16:05:53 +0100 | [diff] [blame] | 357 | long imm = insn & 0x7f; |
| 358 | if (insn & 0x80) /* SUB */ |
| 359 | regs->ARM_sp -= imm * 4; |
| 360 | else /* ADD */ |
| 361 | regs->ARM_sp += imm * 4; |
| 362 | } |
| 363 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 364 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 365 | t16_simulate_cbz(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 366 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 32818f3 | 2011-07-02 16:10:44 +0100 | [diff] [blame] | 367 | { |
| Jon Medhurst | 32818f3 | 2011-07-02 16:10:44 +0100 | [diff] [blame] | 368 | int rn = insn & 0x7; |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 369 | probes_opcode_t nonzero = regs->uregs[rn] ? insn : ~insn; |
| Jon Medhurst | 32818f3 | 2011-07-02 16:10:44 +0100 | [diff] [blame] | 370 | if (nonzero & 0x800) { |
| 371 | long i = insn & 0x200; |
| 372 | long imm5 = insn & 0xf8; |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 373 | unsigned long pc = regs->ARM_pc + 2; |
| Jon Medhurst | 32818f3 | 2011-07-02 16:10:44 +0100 | [diff] [blame] | 374 | regs->ARM_pc = pc + (i >> 3) + (imm5 >> 2); |
| 375 | } |
| 376 | } |
| 377 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 378 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 379 | t16_simulate_it(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 380 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 5b94faf | 2011-07-02 16:16:05 +0100 | [diff] [blame] | 381 | { |
| 382 | /* |
| 383 | * The 8 IT state bits are split into two parts in CPSR: |
| 384 | * ITSTATE<1:0> are in CPSR<26:25> |
| 385 | * ITSTATE<7:2> are in CPSR<15:10> |
| 386 | * The new IT state is in the lower byte of insn. |
| 387 | */ |
| Jon Medhurst | 5b94faf | 2011-07-02 16:16:05 +0100 | [diff] [blame] | 388 | unsigned long cpsr = regs->ARM_cpsr; |
| 389 | cpsr &= ~PSR_IT_MASK; |
| 390 | cpsr |= (insn & 0xfc) << 8; |
| 391 | cpsr |= (insn & 0x03) << 25; |
| 392 | regs->ARM_cpsr = cpsr; |
| 393 | } |
| 394 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 395 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 396 | t16_singlestep_it(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 397 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 5b94faf | 2011-07-02 16:16:05 +0100 | [diff] [blame] | 398 | { |
| 399 | regs->ARM_pc += 2; |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 400 | t16_simulate_it(insn, asi, regs); |
| Jon Medhurst | 5b94faf | 2011-07-02 16:16:05 +0100 | [diff] [blame] | 401 | } |
| 402 | |
| David A. Long | 44a0a59 | 2014-03-05 21:23:42 -0500 | [diff] [blame] | 403 | static enum probes_insn __kprobes |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 404 | t16_decode_it(probes_opcode_t insn, struct arch_probes_insn *asi, |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 405 | const struct decode_header *d) |
| Jon Medhurst | 5b94faf | 2011-07-02 16:16:05 +0100 | [diff] [blame] | 406 | { |
| 407 | asi->insn_singlestep = t16_singlestep_it; |
| 408 | return INSN_GOOD_NO_SLOT; |
| 409 | } |
| 410 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 411 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 412 | t16_simulate_cond_branch(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 413 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 396b41f | 2011-07-02 16:30:43 +0100 | [diff] [blame] | 414 | { |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 415 | unsigned long pc = regs->ARM_pc + 2; |
| Jon Medhurst | 396b41f | 2011-07-02 16:30:43 +0100 | [diff] [blame] | 416 | long offset = insn & 0x7f; |
| 417 | offset -= insn & 0x80; /* Apply sign bit */ |
| 418 | regs->ARM_pc = pc + (offset * 2); |
| 419 | } |
| 420 | |
| David A. Long | 44a0a59 | 2014-03-05 21:23:42 -0500 | [diff] [blame] | 421 | static enum probes_insn __kprobes |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 422 | t16_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi, |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 423 | const struct decode_header *d) |
| Jon Medhurst | 396b41f | 2011-07-02 16:30:43 +0100 | [diff] [blame] | 424 | { |
| 425 | int cc = (insn >> 8) & 0xf; |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 426 | asi->insn_check_cc = probes_condition_checks[cc]; |
| Jon Medhurst | 396b41f | 2011-07-02 16:30:43 +0100 | [diff] [blame] | 427 | asi->insn_handler = t16_simulate_cond_branch; |
| 428 | return INSN_GOOD_NO_SLOT; |
| 429 | } |
| 430 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 431 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 432 | t16_simulate_branch(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 433 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 396b41f | 2011-07-02 16:30:43 +0100 | [diff] [blame] | 434 | { |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 435 | unsigned long pc = regs->ARM_pc + 2; |
| Jon Medhurst | 396b41f | 2011-07-02 16:30:43 +0100 | [diff] [blame] | 436 | long offset = insn & 0x3ff; |
| 437 | offset -= insn & 0x400; /* Apply sign bit */ |
| 438 | regs->ARM_pc = pc + (offset * 2); |
| 439 | } |
| 440 | |
| Jon Medhurst | 02d194f | 2011-07-02 15:46:05 +0100 | [diff] [blame] | 441 | static unsigned long __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 442 | t16_emulate_loregs(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 443 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 02d194f | 2011-07-02 15:46:05 +0100 | [diff] [blame] | 444 | { |
| 445 | unsigned long oldcpsr = regs->ARM_cpsr; |
| 446 | unsigned long newcpsr; |
| 447 | |
| 448 | __asm__ __volatile__ ( |
| 449 | "msr cpsr_fs, %[oldcpsr] \n\t" |
| 450 | "ldmia %[regs], {r0-r7} \n\t" |
| 451 | "blx %[fn] \n\t" |
| 452 | "stmia %[regs], {r0-r7} \n\t" |
| 453 | "mrs %[newcpsr], cpsr \n\t" |
| 454 | : [newcpsr] "=r" (newcpsr) |
| 455 | : [oldcpsr] "r" (oldcpsr), [regs] "r" (regs), |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 456 | [fn] "r" (asi->insn_fn) |
| Jon Medhurst | 02d194f | 2011-07-02 15:46:05 +0100 | [diff] [blame] | 457 | : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", |
| 458 | "lr", "memory", "cc" |
| 459 | ); |
| 460 | |
| 461 | return (oldcpsr & ~APSR_MASK) | (newcpsr & APSR_MASK); |
| 462 | } |
| 463 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 464 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 465 | t16_emulate_loregs_rwflags(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 466 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 02d194f | 2011-07-02 15:46:05 +0100 | [diff] [blame] | 467 | { |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 468 | regs->ARM_cpsr = t16_emulate_loregs(insn, asi, regs); |
| Jon Medhurst | 02d194f | 2011-07-02 15:46:05 +0100 | [diff] [blame] | 469 | } |
| 470 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 471 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 472 | t16_emulate_loregs_noitrwflags(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 473 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 02d194f | 2011-07-02 15:46:05 +0100 | [diff] [blame] | 474 | { |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 475 | unsigned long cpsr = t16_emulate_loregs(insn, asi, regs); |
| Jon Medhurst | 02d194f | 2011-07-02 15:46:05 +0100 | [diff] [blame] | 476 | if (!in_it_block(cpsr)) |
| 477 | regs->ARM_cpsr = cpsr; |
| 478 | } |
| 479 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 480 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 481 | t16_emulate_hiregs(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 482 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | 3b5940e | 2011-07-02 15:54:57 +0100 | [diff] [blame] | 483 | { |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 484 | unsigned long pc = regs->ARM_pc + 2; |
| Jon Medhurst | 3b5940e | 2011-07-02 15:54:57 +0100 | [diff] [blame] | 485 | int rdn = (insn & 0x7) | ((insn & 0x80) >> 4); |
| 486 | int rm = (insn >> 3) & 0xf; |
| 487 | |
| 488 | register unsigned long rdnv asm("r1"); |
| 489 | register unsigned long rmv asm("r0"); |
| 490 | unsigned long cpsr = regs->ARM_cpsr; |
| 491 | |
| 492 | rdnv = (rdn == 15) ? pc : regs->uregs[rdn]; |
| 493 | rmv = (rm == 15) ? pc : regs->uregs[rm]; |
| 494 | |
| 495 | __asm__ __volatile__ ( |
| 496 | "msr cpsr_fs, %[cpsr] \n\t" |
| 497 | "blx %[fn] \n\t" |
| 498 | "mrs %[cpsr], cpsr \n\t" |
| 499 | : "=r" (rdnv), [cpsr] "=r" (cpsr) |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 500 | : "0" (rdnv), "r" (rmv), "1" (cpsr), [fn] "r" (asi->insn_fn) |
| Jon Medhurst | 3b5940e | 2011-07-02 15:54:57 +0100 | [diff] [blame] | 501 | : "lr", "memory", "cc" |
| 502 | ); |
| 503 | |
| 504 | if (rdn == 15) |
| 505 | rdnv &= ~1; |
| 506 | |
| 507 | regs->uregs[rdn] = rdnv; |
| 508 | regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); |
| 509 | } |
| 510 | |
| David A. Long | 44a0a59 | 2014-03-05 21:23:42 -0500 | [diff] [blame] | 511 | static enum probes_insn __kprobes |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 512 | t16_decode_hiregs(probes_opcode_t insn, struct arch_probes_insn *asi, |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 513 | const struct decode_header *d) |
| Jon Medhurst | 3b5940e | 2011-07-02 15:54:57 +0100 | [diff] [blame] | 514 | { |
| 515 | insn &= ~0x00ff; |
| 516 | insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */ |
| Ben Dooks | 888be25 | 2013-11-08 18:29:25 +0000 | [diff] [blame] | 517 | ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn); |
| Jon Medhurst | 3b5940e | 2011-07-02 15:54:57 +0100 | [diff] [blame] | 518 | asi->insn_handler = t16_emulate_hiregs; |
| 519 | return INSN_GOOD; |
| 520 | } |
| 521 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 522 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 523 | t16_emulate_push(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 524 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | fd0c8d8 | 2011-07-02 16:13:29 +0100 | [diff] [blame] | 525 | { |
| 526 | __asm__ __volatile__ ( |
| 527 | "ldr r9, [%[regs], #13*4] \n\t" |
| 528 | "ldr r8, [%[regs], #14*4] \n\t" |
| 529 | "ldmia %[regs], {r0-r7} \n\t" |
| 530 | "blx %[fn] \n\t" |
| 531 | "str r9, [%[regs], #13*4] \n\t" |
| 532 | : |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 533 | : [regs] "r" (regs), [fn] "r" (asi->insn_fn) |
| Jon Medhurst | fd0c8d8 | 2011-07-02 16:13:29 +0100 | [diff] [blame] | 534 | : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", |
| 535 | "lr", "memory", "cc" |
| 536 | ); |
| 537 | } |
| 538 | |
| David A. Long | 44a0a59 | 2014-03-05 21:23:42 -0500 | [diff] [blame] | 539 | static enum probes_insn __kprobes |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 540 | t16_decode_push(probes_opcode_t insn, struct arch_probes_insn *asi, |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 541 | const struct decode_header *d) |
| Jon Medhurst | fd0c8d8 | 2011-07-02 16:13:29 +0100 | [diff] [blame] | 542 | { |
| 543 | /* |
| 544 | * To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}" |
| 545 | * and call it with R9=SP and LR in the register list represented |
| 546 | * by R8. |
| 547 | */ |
| Ben Dooks | 888be25 | 2013-11-08 18:29:25 +0000 | [diff] [blame] | 548 | /* 1st half STMDB R9!,{} */ |
| 549 | ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe929); |
| 550 | /* 2nd half (register list) */ |
| 551 | ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff); |
| Jon Medhurst | fd0c8d8 | 2011-07-02 16:13:29 +0100 | [diff] [blame] | 552 | asi->insn_handler = t16_emulate_push; |
| 553 | return INSN_GOOD; |
| 554 | } |
| 555 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 556 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 557 | t16_emulate_pop_nopc(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 558 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | fd0c8d8 | 2011-07-02 16:13:29 +0100 | [diff] [blame] | 559 | { |
| 560 | __asm__ __volatile__ ( |
| 561 | "ldr r9, [%[regs], #13*4] \n\t" |
| 562 | "ldmia %[regs], {r0-r7} \n\t" |
| 563 | "blx %[fn] \n\t" |
| 564 | "stmia %[regs], {r0-r7} \n\t" |
| 565 | "str r9, [%[regs], #13*4] \n\t" |
| 566 | : |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 567 | : [regs] "r" (regs), [fn] "r" (asi->insn_fn) |
| Jon Medhurst | fd0c8d8 | 2011-07-02 16:13:29 +0100 | [diff] [blame] | 568 | : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9", |
| 569 | "lr", "memory", "cc" |
| 570 | ); |
| 571 | } |
| 572 | |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 573 | static void __kprobes |
| David A. Long | f145d66 | 2014-03-05 21:17:23 -0500 | [diff] [blame] | 574 | t16_emulate_pop_pc(probes_opcode_t insn, |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 575 | struct arch_probes_insn *asi, struct pt_regs *regs) |
| Jon Medhurst | fd0c8d8 | 2011-07-02 16:13:29 +0100 | [diff] [blame] | 576 | { |
| 577 | register unsigned long pc asm("r8"); |
| 578 | |
| 579 | __asm__ __volatile__ ( |
| 580 | "ldr r9, [%[regs], #13*4] \n\t" |
| 581 | "ldmia %[regs], {r0-r7} \n\t" |
| 582 | "blx %[fn] \n\t" |
| 583 | "stmia %[regs], {r0-r7} \n\t" |
| 584 | "str r9, [%[regs], #13*4] \n\t" |
| 585 | : "=r" (pc) |
| David A. Long | 7579f4b3 | 2014-03-07 11:19:32 -0500 | [diff] [blame] | 586 | : [regs] "r" (regs), [fn] "r" (asi->insn_fn) |
| Jon Medhurst | fd0c8d8 | 2011-07-02 16:13:29 +0100 | [diff] [blame] | 587 | : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9", |
| 588 | "lr", "memory", "cc" |
| 589 | ); |
| 590 | |
| 591 | bx_write_pc(pc, regs); |
| 592 | } |
| 593 | |
| David A. Long | 44a0a59 | 2014-03-05 21:23:42 -0500 | [diff] [blame] | 594 | static enum probes_insn __kprobes |
| David A. Long | b4cd605 | 2014-03-05 21:41:29 -0500 | [diff] [blame] | 595 | t16_decode_pop(probes_opcode_t insn, struct arch_probes_insn *asi, |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 596 | const struct decode_header *d) |
| Jon Medhurst | fd0c8d8 | 2011-07-02 16:13:29 +0100 | [diff] [blame] | 597 | { |
| 598 | /* |
| 599 | * To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}" |
| 600 | * and call it with R9=SP and PC in the register list represented |
| 601 | * by R8. |
| 602 | */ |
| Ben Dooks | 888be25 | 2013-11-08 18:29:25 +0000 | [diff] [blame] | 603 | /* 1st half LDMIA R9!,{} */ |
| 604 | ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe8b9); |
| 605 | /* 2nd half (register list) */ |
| 606 | ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff); |
| Jon Medhurst | fd0c8d8 | 2011-07-02 16:13:29 +0100 | [diff] [blame] | 607 | asi->insn_handler = insn & 0x100 ? t16_emulate_pop_pc |
| 608 | : t16_emulate_pop_nopc; |
| 609 | return INSN_GOOD; |
| 610 | } |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 611 | |
| 612 | const union decode_action kprobes_t16_actions[NUM_PROBES_T16_ACTIONS] = { |
| 613 | [PROBES_T16_ADD_SP] = {.handler = t16_simulate_add_sp_imm}, |
| 614 | [PROBES_T16_CBZ] = {.handler = t16_simulate_cbz}, |
| 615 | [PROBES_T16_SIGN_EXTEND] = {.handler = t16_emulate_loregs_rwflags}, |
| 616 | [PROBES_T16_PUSH] = {.decoder = t16_decode_push}, |
| 617 | [PROBES_T16_POP] = {.decoder = t16_decode_pop}, |
| David A. Long | eb73ea9 | 2014-03-05 21:20:25 -0500 | [diff] [blame] | 618 | [PROBES_T16_SEV] = {.handler = probes_emulate_none}, |
| 619 | [PROBES_T16_WFE] = {.handler = probes_simulate_nop}, |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 620 | [PROBES_T16_IT] = {.decoder = t16_decode_it}, |
| 621 | [PROBES_T16_CMP] = {.handler = t16_emulate_loregs_rwflags}, |
| 622 | [PROBES_T16_ADDSUB] = {.handler = t16_emulate_loregs_noitrwflags}, |
| 623 | [PROBES_T16_LOGICAL] = {.handler = t16_emulate_loregs_noitrwflags}, |
| 624 | [PROBES_T16_LDR_LIT] = {.handler = t16_simulate_ldr_literal}, |
| 625 | [PROBES_T16_BLX] = {.handler = t16_simulate_bxblx}, |
| 626 | [PROBES_T16_HIREGOPS] = {.decoder = t16_decode_hiregs}, |
| 627 | [PROBES_T16_LDRHSTRH] = {.handler = t16_emulate_loregs_rwflags}, |
| 628 | [PROBES_T16_LDRSTR] = {.handler = t16_simulate_ldrstr_sp_relative}, |
| 629 | [PROBES_T16_ADR] = {.handler = t16_simulate_reladr}, |
| 630 | [PROBES_T16_LDMSTM] = {.handler = t16_emulate_loregs_rwflags}, |
| 631 | [PROBES_T16_BRANCH_COND] = {.decoder = t16_decode_cond_branch}, |
| 632 | [PROBES_T16_BRANCH] = {.handler = t16_simulate_branch}, |
| 633 | }; |
| 634 | |
| 635 | const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = { |
| 636 | [PROBES_T32_LDMSTM] = {.decoder = t32_decode_ldmstm}, |
| 637 | [PROBES_T32_LDRDSTRD] = {.handler = t32_emulate_ldrdstrd}, |
| 638 | [PROBES_T32_TABLE_BRANCH] = {.handler = t32_simulate_table_branch}, |
| 639 | [PROBES_T32_TST] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, |
| 640 | [PROBES_T32_MOV] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, |
| 641 | [PROBES_T32_ADDSUB] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, |
| 642 | [PROBES_T32_LOGICAL] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, |
| 643 | [PROBES_T32_CMP] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, |
| 644 | [PROBES_T32_ADDWSUBW_PC] = {.handler = t32_emulate_rd8pc16_noflags,}, |
| 645 | [PROBES_T32_ADDWSUBW] = {.handler = t32_emulate_rd8rn16_noflags}, |
| 646 | [PROBES_T32_MOVW] = {.handler = t32_emulate_rd8rn16_noflags}, |
| 647 | [PROBES_T32_SAT] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, |
| 648 | [PROBES_T32_BITFIELD] = {.handler = t32_emulate_rd8rn16_noflags}, |
| David A. Long | eb73ea9 | 2014-03-05 21:20:25 -0500 | [diff] [blame] | 649 | [PROBES_T32_SEV] = {.handler = probes_emulate_none}, |
| 650 | [PROBES_T32_WFE] = {.handler = probes_simulate_nop}, |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 651 | [PROBES_T32_MRS] = {.handler = t32_simulate_mrs}, |
| 652 | [PROBES_T32_BRANCH_COND] = {.decoder = t32_decode_cond_branch}, |
| 653 | [PROBES_T32_BRANCH] = {.handler = t32_simulate_branch}, |
| David A. Long | eb73ea9 | 2014-03-05 21:20:25 -0500 | [diff] [blame] | 654 | [PROBES_T32_PLDI] = {.handler = probes_simulate_nop}, |
| David A. Long | 3e6cd39 | 2014-03-06 18:06:43 -0500 | [diff] [blame] | 655 | [PROBES_T32_LDR_LIT] = {.handler = t32_simulate_ldr_literal}, |
| 656 | [PROBES_T32_LDRSTR] = {.handler = t32_emulate_ldrstr}, |
| 657 | [PROBES_T32_SIGN_EXTEND] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, |
| 658 | [PROBES_T32_MEDIA] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, |
| 659 | [PROBES_T32_REVERSE] = {.handler = t32_emulate_rd8rn16_noflags}, |
| 660 | [PROBES_T32_MUL_ADD] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, |
| 661 | [PROBES_T32_MUL_ADD2] = {.handler = t32_emulate_rd8rn16rm0ra12_noflags}, |
| 662 | [PROBES_T32_MUL_ADD_LONG] = { |
| 663 | .handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags}, |
| 664 | }; |
| Wang Nan | 83803d9 | 2015-01-05 19:29:18 +0800 | [diff] [blame] | 665 | |
| Wang Nan | 6624cf6 | 2015-01-05 19:29:21 +0800 | [diff] [blame] | 666 | const struct decode_checker *kprobes_t32_checkers[] = {t32_stack_checker, NULL}; |
| 667 | const struct decode_checker *kprobes_t16_checkers[] = {t16_stack_checker, NULL}; |