diff options
author | Masami Hiramatsu <hiramatu@sdl.hitachi.co.jp> | 2006-06-26 03:25:19 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-26 12:58:22 -0400 |
commit | 585deacaca3e7bfc63580623f0344d1fa9c47f11 (patch) | |
tree | f0006846255aa002c74e97a2c32be6063f8de990 /arch/i386/kernel/kprobes.c | |
parent | 6ae7440e6b7a46f4c119c60580aa9384c1b463f7 (diff) |
[PATCH] kprobe: boost 2byte-opcodes on i386
Previous kprobe-booster patch has not handled any 2byte opcodes and
prefixes. I checked whole IA32 opcode map and classified it.
This patch enables kprobe to boost those 2byte opcodes and prefixes.
Signed-off-by: Masami Hiramatsu <hiramatu@sdl.hitachi.co.jp>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: "Keshavamurthy, Anil S" <anil.s.keshavamurthy@intel.com>
Cc: Prasanna S Panchamukhi <prasanna@in.ibm.com>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: Yumiko Sugita <sugita@sdl.hitachi.co.jp>
Cc: Satoshi Oshima <soshima@redhat.com>
Cc: Hideo Aoki <haoki@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386/kernel/kprobes.c')
-rw-r--r-- | arch/i386/kernel/kprobes.c | 79 |
1 files changed, 65 insertions, 14 deletions
diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c index 395a9a6dff88..37f86234bdd7 100644 --- a/arch/i386/kernel/kprobes.c +++ b/arch/i386/kernel/kprobes.c | |||
@@ -57,34 +57,85 @@ static __always_inline void set_jmp_op(void *from, void *to) | |||
57 | /* | 57 | /* |
58 | * returns non-zero if opcodes can be boosted. | 58 | * returns non-zero if opcodes can be boosted. |
59 | */ | 59 | */ |
60 | static __always_inline int can_boost(kprobe_opcode_t opcode) | 60 | static __always_inline int can_boost(kprobe_opcode_t *opcodes) |
61 | { | 61 | { |
62 | switch (opcode & 0xf0 ) { | 62 | #define W(row,b0,b1,b2,b3,b4,b5,b6,b7,b8,b9,ba,bb,bc,bd,be,bf) \ |
63 | (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \ | ||
64 | (b4##UL << 0x4)|(b5##UL << 0x5)|(b6##UL << 0x6)|(b7##UL << 0x7) | \ | ||
65 | (b8##UL << 0x8)|(b9##UL << 0x9)|(ba##UL << 0xa)|(bb##UL << 0xb) | \ | ||
66 | (bc##UL << 0xc)|(bd##UL << 0xd)|(be##UL << 0xe)|(bf##UL << 0xf)) \ | ||
67 | << (row % 32)) | ||
68 | /* | ||
69 | * Undefined/reserved opcodes, conditional jump, Opcode Extension | ||
70 | * Groups, and some special opcodes can not be boost. | ||
71 | */ | ||
72 | static const unsigned long twobyte_is_boostable[256 / 32] = { | ||
73 | /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ | ||
74 | /* ------------------------------- */ | ||
75 | W(0x00, 0,0,1,1,0,0,1,0,1,1,0,0,0,0,0,0)| /* 00 */ | ||
76 | W(0x10, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), /* 10 */ | ||
77 | W(0x20, 1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0)| /* 20 */ | ||
78 | W(0x30, 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0), /* 30 */ | ||
79 | W(0x40, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 40 */ | ||
80 | W(0x50, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), /* 50 */ | ||
81 | W(0x60, 1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1)| /* 60 */ | ||
82 | W(0x70, 0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,1), /* 70 */ | ||
83 | W(0x80, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)| /* 80 */ | ||
84 | W(0x90, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1), /* 90 */ | ||
85 | W(0xa0, 1,1,0,1,1,1,0,0,1,1,0,1,1,1,0,1)| /* a0 */ | ||
86 | W(0xb0, 1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1), /* b0 */ | ||
87 | W(0xc0, 1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1)| /* c0 */ | ||
88 | W(0xd0, 0,1,1,1,0,1,0,0,1,1,0,1,1,1,0,1), /* d0 */ | ||
89 | W(0xe0, 0,1,1,0,0,1,0,0,1,1,0,1,1,1,0,1)| /* e0 */ | ||
90 | W(0xf0, 0,1,1,1,0,1,0,0,1,1,1,0,1,1,1,0) /* f0 */ | ||
91 | /* ------------------------------- */ | ||
92 | /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ | ||
93 | }; | ||
94 | #undef W | ||
95 | kprobe_opcode_t opcode; | ||
96 | kprobe_opcode_t *orig_opcodes = opcodes; | ||
97 | retry: | ||
98 | if (opcodes - orig_opcodes > MAX_INSN_SIZE - 1) | ||
99 | return 0; | ||
100 | opcode = *(opcodes++); | ||
101 | |||
102 | /* 2nd-byte opcode */ | ||
103 | if (opcode == 0x0f) { | ||
104 | if (opcodes - orig_opcodes > MAX_INSN_SIZE - 1) | ||
105 | return 0; | ||
106 | return test_bit(*opcodes, twobyte_is_boostable); | ||
107 | } | ||
108 | |||
109 | switch (opcode & 0xf0) { | ||
110 | case 0x60: | ||
111 | if (0x63 < opcode && opcode < 0x67) | ||
112 | goto retry; /* prefixes */ | ||
113 | /* can't boost Address-size override and bound */ | ||
114 | return (opcode != 0x62 && opcode != 0x67); | ||
63 | case 0x70: | 115 | case 0x70: |
64 | return 0; /* can't boost conditional jump */ | 116 | return 0; /* can't boost conditional jump */ |
65 | case 0x90: | ||
66 | /* can't boost call and pushf */ | ||
67 | return opcode != 0x9a && opcode != 0x9c; | ||
68 | case 0xc0: | 117 | case 0xc0: |
69 | /* can't boost undefined opcodes and soft-interruptions */ | 118 | /* can't boost software-interruptions */ |
70 | return (0xc1 < opcode && opcode < 0xc6) || | 119 | return (0xc1 < opcode && opcode < 0xcc) || opcode == 0xcf; |
71 | (0xc7 < opcode && opcode < 0xcc) || opcode == 0xcf; | ||
72 | case 0xd0: | 120 | case 0xd0: |
73 | /* can boost AA* and XLAT */ | 121 | /* can boost AA* and XLAT */ |
74 | return (opcode == 0xd4 || opcode == 0xd5 || opcode == 0xd7); | 122 | return (opcode == 0xd4 || opcode == 0xd5 || opcode == 0xd7); |
75 | case 0xe0: | 123 | case 0xe0: |
76 | /* can boost in/out and (may be) jmps */ | 124 | /* can boost in/out and absolute jmps */ |
77 | return (0xe3 < opcode && opcode != 0xe8); | 125 | return ((opcode & 0x04) || opcode == 0xea); |
78 | case 0xf0: | 126 | case 0xf0: |
127 | if ((opcode & 0x0c) == 0 && opcode != 0xf1) | ||
128 | goto retry; /* lock/rep(ne) prefix */ | ||
79 | /* clear and set flags can be boost */ | 129 | /* clear and set flags can be boost */ |
80 | return (opcode == 0xf5 || (0xf7 < opcode && opcode < 0xfe)); | 130 | return (opcode == 0xf5 || (0xf7 < opcode && opcode < 0xfe)); |
81 | default: | 131 | default: |
82 | /* currently, can't boost 2 bytes opcodes */ | 132 | if (opcode == 0x26 || opcode == 0x36 || opcode == 0x3e) |
83 | return opcode != 0x0f; | 133 | goto retry; /* prefixes */ |
134 | /* can't boost CS override and call */ | ||
135 | return (opcode != 0x2e && opcode != 0x9a); | ||
84 | } | 136 | } |
85 | } | 137 | } |
86 | 138 | ||
87 | |||
88 | /* | 139 | /* |
89 | * returns non-zero if opcode modifies the interrupt flag. | 140 | * returns non-zero if opcode modifies the interrupt flag. |
90 | */ | 141 | */ |
@@ -109,7 +160,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) | |||
109 | 160 | ||
110 | memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); | 161 | memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); |
111 | p->opcode = *p->addr; | 162 | p->opcode = *p->addr; |
112 | if (can_boost(p->opcode)) { | 163 | if (can_boost(p->addr)) { |
113 | p->ainsn.boostable = 0; | 164 | p->ainsn.boostable = 0; |
114 | } else { | 165 | } else { |
115 | p->ainsn.boostable = -1; | 166 | p->ainsn.boostable = -1; |