diff options
author | Jon Medhurst <tixy@yxit.co.uk> | 2011-04-26 10:15:56 -0400 |
---|---|---|
committer | Tixy <tixy@medhuaa1.miniserver.com> | 2011-07-13 13:32:43 -0400 |
commit | 0d1a095aa1e6e2a233bfb1729e15233e77f69d54 (patch) | |
tree | eb4d2415cf599c42bd425edf7557611abbd0bd4e /arch/arm/kernel/kprobes-common.c | |
parent | e2960317d4581689bf80dbad4d75e7a59f11a3f7 (diff) |
ARM: kprobes: Infrastructure for table driven decoding of CPU instructions
The existing ARM instruction decoding functions are a mass of if/else
code. Rather than follow this pattern for Thumb instruction decoding
this patch implements an infrastructure for a new table driven scheme.
This has several advantages:
- Reduces the kernel size by approx 2kB. (The ARM instruction decoding
will eventually have -3.1kB code, +1.3kB data; with similar or better
estimated savings for Thumb decoding.)
- Allows programmatic checking of decoding consistency and test case
coverage.
- Provides more uniform source code and is therefore, arguably, clearer.
For a detailed explanation of how decoding tables work see the in-source
documentation in kprobes.h, and also for kprobe_decode_insn().
Signed-off-by: Jon Medhurst <tixy@yxit.co.uk>
Acked-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Diffstat (limited to 'arch/arm/kernel/kprobes-common.c')
-rw-r--r-- | arch/arm/kernel/kprobes-common.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c index 87e91ce4d495..1cb6a82a5e24 100644 --- a/arch/arm/kernel/kprobes-common.c +++ b/arch/arm/kernel/kprobes-common.c | |||
@@ -140,3 +140,261 @@ kprobe_check_cc * const kprobe_condition_checks[16] = { | |||
140 | &__check_hi, &__check_ls, &__check_ge, &__check_lt, | 140 | &__check_hi, &__check_ls, &__check_ge, &__check_lt, |
141 | &__check_gt, &__check_le, &__check_al, &__check_al | 141 | &__check_gt, &__check_le, &__check_al, &__check_al |
142 | }; | 142 | }; |
143 | |||
144 | |||
145 | /* | ||
146 | * Prepare an instruction slot to receive an instruction for emulating. | ||
147 | * This is done by placing a subroutine return after the location where the | ||
148 | * instruction will be placed. We also modify ARM instructions to be | ||
149 | * unconditional as the condition code will already be checked before any | ||
150 | * emulation handler is called. | ||
151 | */ | ||
152 | static kprobe_opcode_t __kprobes | ||
153 | prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi, | ||
154 | bool thumb) | ||
155 | { | ||
156 | #ifdef CONFIG_THUMB2_KERNEL | ||
157 | if (thumb) { | ||
158 | u16 *thumb_insn = (u16 *)asi->insn; | ||
159 | thumb_insn[1] = 0x4770; /* Thumb bx lr */ | ||
160 | thumb_insn[2] = 0x4770; /* Thumb bx lr */ | ||
161 | return insn; | ||
162 | } | ||
163 | asi->insn[1] = 0xe12fff1e; /* ARM bx lr */ | ||
164 | #else | ||
165 | asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */ | ||
166 | #endif | ||
167 | /* Make an ARM instruction unconditional */ | ||
168 | if (insn < 0xe0000000) | ||
169 | insn = (insn | 0xe0000000) & ~0x10000000; | ||
170 | return insn; | ||
171 | } | ||
172 | |||
173 | /* | ||
174 | * Write a (probably modified) instruction into the slot previously prepared by | ||
175 | * prepare_emulated_insn | ||
176 | */ | ||
177 | static void __kprobes | ||
178 | set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi, | ||
179 | bool thumb) | ||
180 | { | ||
181 | #ifdef CONFIG_THUMB2_KERNEL | ||
182 | if (thumb) { | ||
183 | u16 *ip = (u16 *)asi->insn; | ||
184 | if (is_wide_instruction(insn)) | ||
185 | *ip++ = insn >> 16; | ||
186 | *ip++ = insn; | ||
187 | return; | ||
188 | } | ||
189 | #endif | ||
190 | asi->insn[0] = insn; | ||
191 | } | ||
192 | |||
193 | /* | ||
194 | * When we modify the register numbers encoded in an instruction to be emulated, | ||
195 | * the new values come from this define. For ARM and 32-bit Thumb instructions | ||
196 | * this gives... | ||
197 | * | ||
198 | * bit position 16 12 8 4 0 | ||
199 | * ---------------+---+---+---+---+---+ | ||
200 | * register r2 r0 r1 -- r3 | ||
201 | */ | ||
202 | #define INSN_NEW_BITS 0x00020103 | ||
203 | |||
204 | /* Each nibble has same value as that at INSN_NEW_BITS bit 16 */ | ||
205 | #define INSN_SAMEAS16_BITS 0x22222222 | ||
206 | |||
207 | /* | ||
208 | * Validate and modify each of the registers encoded in an instruction. | ||
209 | * | ||
210 | * Each nibble in regs contains a value from enum decode_reg_type. For each | ||
211 | * non-zero value, the corresponding nibble in pinsn is validated and modified | ||
212 | * according to the type. | ||
213 | */ | ||
214 | static bool __kprobes decode_regs(kprobe_opcode_t* pinsn, u32 regs) | ||
215 | { | ||
216 | kprobe_opcode_t insn = *pinsn; | ||
217 | kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */ | ||
218 | |||
219 | for (; regs != 0; regs >>= 4, mask <<= 4) { | ||
220 | |||
221 | kprobe_opcode_t new_bits = INSN_NEW_BITS; | ||
222 | |||
223 | switch (regs & 0xf) { | ||
224 | |||
225 | case REG_TYPE_NONE: | ||
226 | /* Nibble not a register, skip to next */ | ||
227 | continue; | ||
228 | |||
229 | case REG_TYPE_ANY: | ||
230 | /* Any register is allowed */ | ||
231 | break; | ||
232 | |||
233 | case REG_TYPE_SAMEAS16: | ||
234 | /* Replace register with same as at bit position 16 */ | ||
235 | new_bits = INSN_SAMEAS16_BITS; | ||
236 | break; | ||
237 | |||
238 | case REG_TYPE_SP: | ||
239 | /* Only allow SP (R13) */ | ||
240 | if ((insn ^ 0xdddddddd) & mask) | ||
241 | goto reject; | ||
242 | break; | ||
243 | |||
244 | case REG_TYPE_PC: | ||
245 | /* Only allow PC (R15) */ | ||
246 | if ((insn ^ 0xffffffff) & mask) | ||
247 | goto reject; | ||
248 | break; | ||
249 | |||
250 | case REG_TYPE_NOSP: | ||
251 | /* Reject SP (R13) */ | ||
252 | if (((insn ^ 0xdddddddd) & mask) == 0) | ||
253 | goto reject; | ||
254 | break; | ||
255 | |||
256 | case REG_TYPE_NOSPPC: | ||
257 | case REG_TYPE_NOSPPCX: | ||
258 | /* Reject SP and PC (R13 and R15) */ | ||
259 | if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0) | ||
260 | goto reject; | ||
261 | break; | ||
262 | |||
263 | case REG_TYPE_NOPCWB: | ||
264 | if (!is_writeback(insn)) | ||
265 | break; /* No writeback, so any register is OK */ | ||
266 | /* fall through... */ | ||
267 | case REG_TYPE_NOPC: | ||
268 | case REG_TYPE_NOPCX: | ||
269 | /* Reject PC (R15) */ | ||
270 | if (((insn ^ 0xffffffff) & mask) == 0) | ||
271 | goto reject; | ||
272 | break; | ||
273 | } | ||
274 | |||
275 | /* Replace value of nibble with new register number... */ | ||
276 | insn &= ~mask; | ||
277 | insn |= new_bits & mask; | ||
278 | } | ||
279 | |||
280 | *pinsn = insn; | ||
281 | return true; | ||
282 | |||
283 | reject: | ||
284 | return false; | ||
285 | } | ||
286 | |||
287 | static const int decode_struct_sizes[NUM_DECODE_TYPES] = { | ||
288 | [DECODE_TYPE_TABLE] = sizeof(struct decode_table), | ||
289 | [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom), | ||
290 | [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate), | ||
291 | [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate), | ||
292 | [DECODE_TYPE_OR] = sizeof(struct decode_or), | ||
293 | [DECODE_TYPE_REJECT] = sizeof(struct decode_reject) | ||
294 | }; | ||
295 | |||
296 | /* | ||
297 | * kprobe_decode_insn operates on data tables in order to decode an ARM | ||
298 | * architecture instruction onto which a kprobe has been placed. | ||
299 | * | ||
300 | * These instruction decoding tables are a concatenation of entries each | ||
301 | * of which consist of one of the following structs: | ||
302 | * | ||
303 | * decode_table | ||
304 | * decode_custom | ||
305 | * decode_simulate | ||
306 | * decode_emulate | ||
307 | * decode_or | ||
308 | * decode_reject | ||
309 | * | ||
310 | * Each of these starts with a struct decode_header which has the following | ||
311 | * fields: | ||
312 | * | ||
313 | * type_regs | ||
314 | * mask | ||
315 | * value | ||
316 | * | ||
317 | * The least significant DECODE_TYPE_BITS of type_regs contains a value | ||
318 | * from enum decode_type, this indicates which of the decode_* structs | ||
319 | * the entry contains. The value DECODE_TYPE_END indicates the end of the | ||
320 | * table. | ||
321 | * | ||
322 | * When the table is parsed, each entry is checked in turn to see if it | ||
323 | * matches the instruction to be decoded using the test: | ||
324 | * | ||
325 | * (insn & mask) == value | ||
326 | * | ||
327 | * If no match is found before the end of the table is reached then decoding | ||
328 | * fails with INSN_REJECTED. | ||
329 | * | ||
330 | * When a match is found, decode_regs() is called to validate and modify each | ||
331 | * of the registers encoded in the instruction; the data it uses to do this | ||
332 | * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding | ||
333 | * to fail with INSN_REJECTED. | ||
334 | * | ||
335 | * Once the instruction has passed the above tests, further processing | ||
336 | * depends on the type of the table entry's decode struct. | ||
337 | * | ||
338 | */ | ||
339 | int __kprobes | ||
340 | kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi, | ||
341 | const union decode_item *table, bool thumb) | ||
342 | { | ||
343 | const struct decode_header *h = (struct decode_header *)table; | ||
344 | const struct decode_header *next; | ||
345 | bool matched = false; | ||
346 | |||
347 | insn = prepare_emulated_insn(insn, asi, thumb); | ||
348 | |||
349 | for (;; h = next) { | ||
350 | enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; | ||
351 | u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS; | ||
352 | |||
353 | if (type == DECODE_TYPE_END) | ||
354 | return INSN_REJECTED; | ||
355 | |||
356 | next = (struct decode_header *) | ||
357 | ((uintptr_t)h + decode_struct_sizes[type]); | ||
358 | |||
359 | if (!matched && (insn & h->mask.bits) != h->value.bits) | ||
360 | continue; | ||
361 | |||
362 | if (!decode_regs(&insn, regs)) | ||
363 | return INSN_REJECTED; | ||
364 | |||
365 | switch (type) { | ||
366 | |||
367 | case DECODE_TYPE_TABLE: { | ||
368 | struct decode_table *d = (struct decode_table *)h; | ||
369 | next = (struct decode_header *)d->table.table; | ||
370 | break; | ||
371 | } | ||
372 | |||
373 | case DECODE_TYPE_CUSTOM: { | ||
374 | struct decode_custom *d = (struct decode_custom *)h; | ||
375 | return (*d->decoder.decoder)(insn, asi); | ||
376 | } | ||
377 | |||
378 | case DECODE_TYPE_SIMULATE: { | ||
379 | struct decode_simulate *d = (struct decode_simulate *)h; | ||
380 | asi->insn_handler = d->handler.handler; | ||
381 | return INSN_GOOD_NO_SLOT; | ||
382 | } | ||
383 | |||
384 | case DECODE_TYPE_EMULATE: { | ||
385 | struct decode_emulate *d = (struct decode_emulate *)h; | ||
386 | asi->insn_handler = d->handler.handler; | ||
387 | set_emulated_insn(insn, asi, thumb); | ||
388 | return INSN_GOOD; | ||
389 | } | ||
390 | |||
391 | case DECODE_TYPE_OR: | ||
392 | matched = true; | ||
393 | break; | ||
394 | |||
395 | case DECODE_TYPE_REJECT: | ||
396 | default: | ||
397 | return INSN_REJECTED; | ||
398 | } | ||
399 | } | ||
400 | } | ||