diff options
author | David A. Long <dave.long@linaro.org> | 2014-03-05 21:40:12 -0500 |
---|---|---|
committer | David A. Long <dave.long@linaro.org> | 2014-03-18 16:39:39 -0400 |
commit | 602cd2609eee92d338a83e400774e97c60535ba2 (patch) | |
tree | 43a6513e831fbfccca7889d897af7b70ed27812a | |
parent | 47e190fafde49ff8ca732fa137e39cb2b8baba8c (diff) |
ARM: Add an emulate flag to the kprobes/uprobes instruction decode functions
Add an emulate flag into the instruction interpreter, primarily for uprobes
support.
Signed-off-by: David A. Long <dave.long@linaro.org>
Acked-by: Jon Medhurst <tixy@linaro.org>
-rw-r--r-- | arch/arm/kernel/kprobes.c | 2 | ||||
-rw-r--r-- | arch/arm/kernel/kprobes.h | 1 | ||||
-rw-r--r-- | arch/arm/kernel/probes-arm.c | 4 | ||||
-rw-r--r-- | arch/arm/kernel/probes-arm.h | 2 | ||||
-rw-r--r-- | arch/arm/kernel/probes-thumb.c | 8 | ||||
-rw-r--r-- | arch/arm/kernel/probes-thumb.h | 4 | ||||
-rw-r--r-- | arch/arm/kernel/probes.c | 18 | ||||
-rw-r--r-- | arch/arm/kernel/probes.h | 2 |
8 files changed, 25 insertions, 16 deletions
diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c index 468d4a980c6c..8795f9f819d5 100644 --- a/arch/arm/kernel/kprobes.c +++ b/arch/arm/kernel/kprobes.c | |||
@@ -89,7 +89,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) | |||
89 | p->opcode = insn; | 89 | p->opcode = insn; |
90 | p->ainsn.insn = tmp_insn; | 90 | p->ainsn.insn = tmp_insn; |
91 | 91 | ||
92 | switch ((*decode_insn)(insn, &p->ainsn, actions)) { | 92 | switch ((*decode_insn)(insn, &p->ainsn, true, actions)) { |
93 | case INSN_REJECTED: /* not supported */ | 93 | case INSN_REJECTED: /* not supported */ |
94 | return -EINVAL; | 94 | return -EINVAL; |
95 | 95 | ||
diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h index eee8089b1b93..d0a24b73bcfa 100644 --- a/arch/arm/kernel/kprobes.h +++ b/arch/arm/kernel/kprobes.h | |||
@@ -35,6 +35,7 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi, | |||
35 | 35 | ||
36 | typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t, | 36 | typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t, |
37 | struct arch_specific_insn *, | 37 | struct arch_specific_insn *, |
38 | bool, | ||
38 | const union decode_action *); | 39 | const union decode_action *); |
39 | 40 | ||
40 | #ifdef CONFIG_THUMB2_KERNEL | 41 | #ifdef CONFIG_THUMB2_KERNEL |
diff --git a/arch/arm/kernel/probes-arm.c b/arch/arm/kernel/probes-arm.c index 738e5fc58928..8e7fde876521 100644 --- a/arch/arm/kernel/probes-arm.c +++ b/arch/arm/kernel/probes-arm.c | |||
@@ -725,10 +725,10 @@ static void __kprobes arm_singlestep(probes_opcode_t insn, | |||
725 | */ | 725 | */ |
726 | enum probes_insn __kprobes | 726 | enum probes_insn __kprobes |
727 | arm_probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, | 727 | arm_probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, |
728 | const union decode_action *actions) | 728 | bool emulate, const union decode_action *actions) |
729 | { | 729 | { |
730 | asi->insn_singlestep = arm_singlestep; | 730 | asi->insn_singlestep = arm_singlestep; |
731 | asi->insn_check_cc = probes_condition_checks[insn>>28]; | 731 | asi->insn_check_cc = probes_condition_checks[insn>>28]; |
732 | return probes_decode_insn(insn, asi, probes_decode_arm_table, false, | 732 | return probes_decode_insn(insn, asi, probes_decode_arm_table, false, |
733 | actions); | 733 | emulate, actions); |
734 | } | 734 | } |
diff --git a/arch/arm/kernel/probes-arm.h b/arch/arm/kernel/probes-arm.h index 7a5cce497a9b..ea614dc5aaa3 100644 --- a/arch/arm/kernel/probes-arm.h +++ b/arch/arm/kernel/probes-arm.h | |||
@@ -67,7 +67,7 @@ void __kprobes simulate_mov_ipsp(probes_opcode_t opcode, | |||
67 | extern const union decode_item probes_decode_arm_table[]; | 67 | extern const union decode_item probes_decode_arm_table[]; |
68 | 68 | ||
69 | enum probes_insn arm_probes_decode_insn(probes_opcode_t, | 69 | enum probes_insn arm_probes_decode_insn(probes_opcode_t, |
70 | struct arch_specific_insn *, | 70 | struct arch_specific_insn *, bool emulate, |
71 | const union decode_action *actions); | 71 | const union decode_action *actions); |
72 | 72 | ||
73 | #endif | 73 | #endif |
diff --git a/arch/arm/kernel/probes-thumb.c b/arch/arm/kernel/probes-thumb.c index eab440f6b2d4..23e2cbdb37cb 100644 --- a/arch/arm/kernel/probes-thumb.c +++ b/arch/arm/kernel/probes-thumb.c | |||
@@ -863,20 +863,20 @@ static void __kprobes thumb32_singlestep(probes_opcode_t opcode, | |||
863 | 863 | ||
864 | enum probes_insn __kprobes | 864 | enum probes_insn __kprobes |
865 | thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, | 865 | thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, |
866 | const union decode_action *actions) | 866 | bool emulate, const union decode_action *actions) |
867 | { | 867 | { |
868 | asi->insn_singlestep = thumb16_singlestep; | 868 | asi->insn_singlestep = thumb16_singlestep; |
869 | asi->insn_check_cc = thumb_check_cc; | 869 | asi->insn_check_cc = thumb_check_cc; |
870 | return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true, | 870 | return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true, |
871 | actions); | 871 | emulate, actions); |
872 | } | 872 | } |
873 | 873 | ||
874 | enum probes_insn __kprobes | 874 | enum probes_insn __kprobes |
875 | thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, | 875 | thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, |
876 | const union decode_action *actions) | 876 | bool emulate, const union decode_action *actions) |
877 | { | 877 | { |
878 | asi->insn_singlestep = thumb32_singlestep; | 878 | asi->insn_singlestep = thumb32_singlestep; |
879 | asi->insn_check_cc = thumb_check_cc; | 879 | asi->insn_check_cc = thumb_check_cc; |
880 | return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true, | 880 | return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true, |
881 | actions); | 881 | emulate, actions); |
882 | } | 882 | } |
diff --git a/arch/arm/kernel/probes-thumb.h b/arch/arm/kernel/probes-thumb.h index d6f67c1df7af..65e4250e9b78 100644 --- a/arch/arm/kernel/probes-thumb.h +++ b/arch/arm/kernel/probes-thumb.h | |||
@@ -89,9 +89,9 @@ extern const union decode_item probes_decode_thumb16_table[]; | |||
89 | 89 | ||
90 | enum probes_insn __kprobes | 90 | enum probes_insn __kprobes |
91 | thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, | 91 | thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, |
92 | const union decode_action *actions); | 92 | bool emulate, const union decode_action *actions); |
93 | enum probes_insn __kprobes | 93 | enum probes_insn __kprobes |
94 | thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, | 94 | thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, |
95 | const union decode_action *actions); | 95 | bool emulate, const union decode_action *actions); |
96 | 96 | ||
97 | #endif | 97 | #endif |
diff --git a/arch/arm/kernel/probes.c b/arch/arm/kernel/probes.c index b6d9b855273c..f9dff12cf85c 100644 --- a/arch/arm/kernel/probes.c +++ b/arch/arm/kernel/probes.c | |||
@@ -257,7 +257,7 @@ set_emulated_insn(probes_opcode_t insn, struct arch_specific_insn *asi, | |||
257 | * non-zero value, the corresponding nibble in pinsn is validated and modified | 257 | * non-zero value, the corresponding nibble in pinsn is validated and modified |
258 | * according to the type. | 258 | * according to the type. |
259 | */ | 259 | */ |
260 | static bool __kprobes decode_regs(probes_opcode_t *pinsn, u32 regs) | 260 | static bool __kprobes decode_regs(probes_opcode_t *pinsn, u32 regs, bool modify) |
261 | { | 261 | { |
262 | probes_opcode_t insn = *pinsn; | 262 | probes_opcode_t insn = *pinsn; |
263 | probes_opcode_t mask = 0xf; /* Start at least significant nibble */ | 263 | probes_opcode_t mask = 0xf; /* Start at least significant nibble */ |
@@ -323,7 +323,9 @@ static bool __kprobes decode_regs(probes_opcode_t *pinsn, u32 regs) | |||
323 | insn |= new_bits & mask; | 323 | insn |= new_bits & mask; |
324 | } | 324 | } |
325 | 325 | ||
326 | *pinsn = insn; | 326 | if (modify) |
327 | *pinsn = insn; | ||
328 | |||
327 | return true; | 329 | return true; |
328 | 330 | ||
329 | reject: | 331 | reject: |
@@ -385,13 +387,14 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = { | |||
385 | int __kprobes | 387 | int __kprobes |
386 | probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, | 388 | probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, |
387 | const union decode_item *table, bool thumb, | 389 | const union decode_item *table, bool thumb, |
388 | const union decode_action *actions) | 390 | bool emulate, const union decode_action *actions) |
389 | { | 391 | { |
390 | const struct decode_header *h = (struct decode_header *)table; | 392 | const struct decode_header *h = (struct decode_header *)table; |
391 | const struct decode_header *next; | 393 | const struct decode_header *next; |
392 | bool matched = false; | 394 | bool matched = false; |
393 | 395 | ||
394 | insn = prepare_emulated_insn(insn, asi, thumb); | 396 | if (emulate) |
397 | insn = prepare_emulated_insn(insn, asi, thumb); | ||
395 | 398 | ||
396 | for (;; h = next) { | 399 | for (;; h = next) { |
397 | enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; | 400 | enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; |
@@ -406,7 +409,7 @@ probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, | |||
406 | if (!matched && (insn & h->mask.bits) != h->value.bits) | 409 | if (!matched && (insn & h->mask.bits) != h->value.bits) |
407 | continue; | 410 | continue; |
408 | 411 | ||
409 | if (!decode_regs(&insn, regs)) | 412 | if (!decode_regs(&insn, regs, emulate)) |
410 | return INSN_REJECTED; | 413 | return INSN_REJECTED; |
411 | 414 | ||
412 | switch (type) { | 415 | switch (type) { |
@@ -430,6 +433,11 @@ probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, | |||
430 | 433 | ||
431 | case DECODE_TYPE_EMULATE: { | 434 | case DECODE_TYPE_EMULATE: { |
432 | struct decode_emulate *d = (struct decode_emulate *)h; | 435 | struct decode_emulate *d = (struct decode_emulate *)h; |
436 | |||
437 | if (!emulate) | ||
438 | return actions[d->handler.action].decoder(insn, | ||
439 | asi, h); | ||
440 | |||
433 | asi->insn_handler = actions[d->handler.action].handler; | 441 | asi->insn_handler = actions[d->handler.action].handler; |
434 | set_emulated_insn(insn, asi, thumb); | 442 | set_emulated_insn(insn, asi, thumb); |
435 | return INSN_GOOD; | 443 | return INSN_GOOD; |
diff --git a/arch/arm/kernel/probes.h b/arch/arm/kernel/probes.h index 0c72e544175d..33cc30c50cf5 100644 --- a/arch/arm/kernel/probes.h +++ b/arch/arm/kernel/probes.h | |||
@@ -401,7 +401,7 @@ probes_insn_handler_t probes_emulate_none; | |||
401 | 401 | ||
402 | int __kprobes | 402 | int __kprobes |
403 | probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, | 403 | probes_decode_insn(probes_opcode_t insn, struct arch_specific_insn *asi, |
404 | const union decode_item *table, bool thumb, | 404 | const union decode_item *table, bool thumb, bool emulate, |
405 | const union decode_action *actions); | 405 | const union decode_action *actions); |
406 | 406 | ||
407 | #endif | 407 | #endif |