Commit | Line | Data |
---|---|---|
c7edc9e3 DL |
1 | /* |
2 | * Copyright (C) 2012 Rabin Vincent <rabin at rab.in> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/kernel.h> | |
10 | #include <linux/types.h> | |
11 | #include <linux/stddef.h> | |
12 | #include <linux/wait.h> | |
13 | #include <linux/uprobes.h> | |
14 | #include <linux/module.h> | |
15 | ||
16 | #include "probes.h" | |
17 | #include "probes-arm.h" | |
18 | #include "uprobes.h" | |
19 | ||
20 | static int uprobes_substitute_pc(unsigned long *pinsn, u32 oregs) | |
21 | { | |
22 | probes_opcode_t insn = __mem_to_opcode_arm(*pinsn); | |
23 | probes_opcode_t temp; | |
24 | probes_opcode_t mask; | |
25 | int freereg; | |
26 | u32 free = 0xffff; | |
27 | u32 regs; | |
28 | ||
29 | for (regs = oregs; regs; regs >>= 4, insn >>= 4) { | |
30 | if ((regs & 0xf) == REG_TYPE_NONE) | |
31 | continue; | |
32 | ||
33 | free &= ~(1 << (insn & 0xf)); | |
34 | } | |
35 | ||
36 | /* No PC, no problem */ | |
37 | if (free & (1 << 15)) | |
38 | return 15; | |
39 | ||
40 | if (!free) | |
41 | return -1; | |
42 | ||
43 | /* | |
44 | * fls instead of ffs ensures that for "ldrd r0, r1, [pc]" we would | |
45 | * pick LR instead of R1. | |
46 | */ | |
47 | freereg = free = fls(free) - 1; | |
48 | ||
49 | temp = __mem_to_opcode_arm(*pinsn); | |
50 | insn = temp; | |
51 | regs = oregs; | |
52 | mask = 0xf; | |
53 | ||
54 | for (; regs; regs >>= 4, mask <<= 4, free <<= 4, temp >>= 4) { | |
55 | if ((regs & 0xf) == REG_TYPE_NONE) | |
56 | continue; | |
57 | ||
58 | if ((temp & 0xf) != 15) | |
59 | continue; | |
60 | ||
61 | insn &= ~mask; | |
62 | insn |= free & mask; | |
63 | } | |
64 | ||
65 | *pinsn = __opcode_to_mem_arm(insn); | |
66 | return freereg; | |
67 | } | |
68 | ||
69 | static void uprobe_set_pc(struct arch_uprobe *auprobe, | |
70 | struct arch_uprobe_task *autask, | |
71 | struct pt_regs *regs) | |
72 | { | |
73 | u32 pcreg = auprobe->pcreg; | |
74 | ||
75 | autask->backup = regs->uregs[pcreg]; | |
76 | regs->uregs[pcreg] = regs->ARM_pc + 8; | |
77 | } | |
78 | ||
79 | static void uprobe_unset_pc(struct arch_uprobe *auprobe, | |
80 | struct arch_uprobe_task *autask, | |
81 | struct pt_regs *regs) | |
82 | { | |
83 | /* PC will be taken care of by common code */ | |
84 | regs->uregs[auprobe->pcreg] = autask->backup; | |
85 | } | |
86 | ||
87 | static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe, | |
88 | struct arch_uprobe_task *autask, | |
89 | struct pt_regs *regs) | |
90 | { | |
91 | u32 pcreg = auprobe->pcreg; | |
92 | ||
93 | alu_write_pc(regs->uregs[pcreg], regs); | |
94 | regs->uregs[pcreg] = autask->backup; | |
95 | } | |
96 | ||
97 | static void uprobe_write_pc(struct arch_uprobe *auprobe, | |
98 | struct arch_uprobe_task *autask, | |
99 | struct pt_regs *regs) | |
100 | { | |
101 | u32 pcreg = auprobe->pcreg; | |
102 | ||
103 | load_write_pc(regs->uregs[pcreg], regs); | |
104 | regs->uregs[pcreg] = autask->backup; | |
105 | } | |
106 | ||
107 | enum probes_insn | |
108 | decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi, | |
109 | const struct decode_header *d) | |
110 | { | |
111 | struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, | |
112 | asi); | |
113 | struct decode_emulate *decode = (struct decode_emulate *) d; | |
114 | u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS; | |
115 | int reg; | |
116 | ||
117 | reg = uprobes_substitute_pc(&auprobe->ixol[0], regs); | |
118 | if (reg == 15) | |
119 | return INSN_GOOD; | |
120 | ||
121 | if (reg == -1) | |
122 | return INSN_REJECTED; | |
123 | ||
124 | auprobe->pcreg = reg; | |
125 | auprobe->prehandler = uprobe_set_pc; | |
126 | auprobe->posthandler = uprobe_unset_pc; | |
127 | ||
128 | return INSN_GOOD; | |
129 | } | |
130 | ||
131 | enum probes_insn | |
132 | decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi, | |
133 | const struct decode_header *d, bool alu) | |
134 | { | |
135 | struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, | |
136 | asi); | |
137 | enum probes_insn ret = decode_pc_ro(insn, asi, d); | |
138 | ||
139 | if (((insn >> 12) & 0xf) == 15) | |
140 | auprobe->posthandler = alu ? uprobe_aluwrite_pc | |
141 | : uprobe_write_pc; | |
142 | ||
143 | return ret; | |
144 | } | |
145 | ||
146 | enum probes_insn | |
147 | decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, | |
148 | struct arch_probes_insn *asi, | |
149 | const struct decode_header *d) | |
150 | { | |
151 | return decode_wb_pc(insn, asi, d, true); | |
152 | } | |
153 | ||
154 | enum probes_insn | |
155 | decode_ldr(probes_opcode_t insn, struct arch_probes_insn *asi, | |
156 | const struct decode_header *d) | |
157 | { | |
158 | return decode_wb_pc(insn, asi, d, false); | |
159 | } | |
160 | ||
161 | enum probes_insn | |
162 | uprobe_decode_ldmstm(probes_opcode_t insn, | |
163 | struct arch_probes_insn *asi, | |
164 | const struct decode_header *d) | |
165 | { | |
166 | struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, | |
167 | asi); | |
168 | unsigned reglist = insn & 0xffff; | |
169 | int rn = (insn >> 16) & 0xf; | |
170 | int lbit = insn & (1 << 20); | |
171 | unsigned used = reglist | (1 << rn); | |
172 | ||
173 | if (rn == 15) | |
174 | return INSN_REJECTED; | |
175 | ||
176 | if (!(used & (1 << 15))) | |
177 | return INSN_GOOD; | |
178 | ||
179 | if (used & (1 << 14)) | |
180 | return INSN_REJECTED; | |
181 | ||
182 | /* Use LR instead of PC */ | |
183 | insn ^= 0xc000; | |
184 | ||
185 | auprobe->pcreg = 14; | |
186 | auprobe->ixol[0] = __opcode_to_mem_arm(insn); | |
187 | ||
188 | auprobe->prehandler = uprobe_set_pc; | |
189 | if (lbit) | |
190 | auprobe->posthandler = uprobe_write_pc; | |
191 | else | |
192 | auprobe->posthandler = uprobe_unset_pc; | |
193 | ||
194 | return INSN_GOOD; | |
195 | } | |
196 | ||
197 | const union decode_action uprobes_probes_actions[] = { | |
198 | [PROBES_EMULATE_NONE] = {.handler = probes_simulate_nop}, | |
199 | [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, | |
200 | [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, | |
201 | [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, | |
202 | [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, | |
203 | [PROBES_MRS] = {.handler = simulate_mrs}, | |
204 | [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx}, | |
205 | [PROBES_CLZ] = {.handler = probes_simulate_nop}, | |
206 | [PROBES_SATURATING_ARITHMETIC] = {.handler = probes_simulate_nop}, | |
207 | [PROBES_MUL1] = {.handler = probes_simulate_nop}, | |
208 | [PROBES_MUL2] = {.handler = probes_simulate_nop}, | |
209 | [PROBES_SWP] = {.handler = probes_simulate_nop}, | |
210 | [PROBES_LDRSTRD] = {.decoder = decode_pc_ro}, | |
211 | [PROBES_LOAD_EXTRA] = {.decoder = decode_pc_ro}, | |
212 | [PROBES_LOAD] = {.decoder = decode_ldr}, | |
213 | [PROBES_STORE_EXTRA] = {.decoder = decode_pc_ro}, | |
214 | [PROBES_STORE] = {.decoder = decode_pc_ro}, | |
215 | [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp}, | |
216 | [PROBES_DATA_PROCESSING_REG] = { | |
217 | .decoder = decode_rd12rn16rm0rs8_rwflags}, | |
218 | [PROBES_DATA_PROCESSING_IMM] = { | |
219 | .decoder = decode_rd12rn16rm0rs8_rwflags}, | |
220 | [PROBES_MOV_HALFWORD] = {.handler = probes_simulate_nop}, | |
221 | [PROBES_SEV] = {.handler = probes_simulate_nop}, | |
222 | [PROBES_WFE] = {.handler = probes_simulate_nop}, | |
223 | [PROBES_SATURATE] = {.handler = probes_simulate_nop}, | |
224 | [PROBES_REV] = {.handler = probes_simulate_nop}, | |
225 | [PROBES_MMI] = {.handler = probes_simulate_nop}, | |
226 | [PROBES_PACK] = {.handler = probes_simulate_nop}, | |
227 | [PROBES_EXTEND] = {.handler = probes_simulate_nop}, | |
228 | [PROBES_EXTEND_ADD] = {.handler = probes_simulate_nop}, | |
229 | [PROBES_MUL_ADD_LONG] = {.handler = probes_simulate_nop}, | |
230 | [PROBES_MUL_ADD] = {.handler = probes_simulate_nop}, | |
231 | [PROBES_BITFIELD] = {.handler = probes_simulate_nop}, | |
232 | [PROBES_BRANCH] = {.handler = simulate_bbl}, | |
233 | [PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm} | |
234 | }; |