Vegard Nossum | dfec072 | 2008-04-04 00:51:41 +0200 | [diff] [blame] | 1 | #include <linux/types.h> |
| 2 | |
| 3 | #include "opcode.h" |
| 4 | |
| 5 | static bool opcode_is_prefix(uint8_t b) |
| 6 | { |
| 7 | return |
| 8 | /* Group 1 */ |
| 9 | b == 0xf0 || b == 0xf2 || b == 0xf3 |
| 10 | /* Group 2 */ |
| 11 | || b == 0x2e || b == 0x36 || b == 0x3e || b == 0x26 |
| 12 | || b == 0x64 || b == 0x65 || b == 0x2e || b == 0x3e |
| 13 | /* Group 3 */ |
| 14 | || b == 0x66 |
| 15 | /* Group 4 */ |
| 16 | || b == 0x67; |
| 17 | } |
| 18 | |
Pekka Enberg | 6d9609c | 2009-02-26 11:23:52 +0200 | [diff] [blame] | 19 | #ifdef CONFIG_X86_64 |
Vegard Nossum | dfec072 | 2008-04-04 00:51:41 +0200 | [diff] [blame] | 20 | static bool opcode_is_rex_prefix(uint8_t b) |
| 21 | { |
| 22 | return (b & 0xf0) == 0x40; |
| 23 | } |
Pekka Enberg | 6d9609c | 2009-02-26 11:23:52 +0200 | [diff] [blame] | 24 | #else |
| 25 | static bool opcode_is_rex_prefix(uint8_t b) |
| 26 | { |
| 27 | return false; |
| 28 | } |
| 29 | #endif |
Vegard Nossum | dfec072 | 2008-04-04 00:51:41 +0200 | [diff] [blame] | 30 | |
| 31 | #define REX_W (1 << 3) |
| 32 | |
| 33 | /* |
| 34 | * This is a VERY crude opcode decoder. We only need to find the size of the |
| 35 | * load/store that caused our #PF and this should work for all the opcodes |
| 36 | * that we care about. Moreover, the ones who invented this instruction set |
| 37 | * should be shot. |
| 38 | */ |
| 39 | void kmemcheck_opcode_decode(const uint8_t *op, unsigned int *size) |
| 40 | { |
| 41 | /* Default operand size */ |
| 42 | int operand_size_override = 4; |
| 43 | |
| 44 | /* prefixes */ |
| 45 | for (; opcode_is_prefix(*op); ++op) { |
| 46 | if (*op == 0x66) |
| 47 | operand_size_override = 2; |
| 48 | } |
| 49 | |
Vegard Nossum | dfec072 | 2008-04-04 00:51:41 +0200 | [diff] [blame] | 50 | /* REX prefix */ |
| 51 | if (opcode_is_rex_prefix(*op)) { |
| 52 | uint8_t rex = *op; |
| 53 | |
| 54 | ++op; |
| 55 | if (rex & REX_W) { |
| 56 | switch (*op) { |
| 57 | case 0x63: |
| 58 | *size = 4; |
| 59 | return; |
| 60 | case 0x0f: |
| 61 | ++op; |
| 62 | |
| 63 | switch (*op) { |
| 64 | case 0xb6: |
| 65 | case 0xbe: |
| 66 | *size = 1; |
| 67 | return; |
| 68 | case 0xb7: |
| 69 | case 0xbf: |
| 70 | *size = 2; |
| 71 | return; |
| 72 | } |
| 73 | |
| 74 | break; |
| 75 | } |
| 76 | |
| 77 | *size = 8; |
| 78 | return; |
| 79 | } |
| 80 | } |
Vegard Nossum | dfec072 | 2008-04-04 00:51:41 +0200 | [diff] [blame] | 81 | |
| 82 | /* escape opcode */ |
| 83 | if (*op == 0x0f) { |
| 84 | ++op; |
| 85 | |
| 86 | /* |
| 87 | * This is move with zero-extend and sign-extend, respectively; |
| 88 | * we don't have to think about 0xb6/0xbe, because this is |
| 89 | * already handled in the conditional below. |
| 90 | */ |
| 91 | if (*op == 0xb7 || *op == 0xbf) |
| 92 | operand_size_override = 2; |
| 93 | } |
| 94 | |
| 95 | *size = (*op & 1) ? operand_size_override : 1; |
| 96 | } |
| 97 | |
| 98 | const uint8_t *kmemcheck_opcode_get_primary(const uint8_t *op) |
| 99 | { |
| 100 | /* skip prefixes */ |
| 101 | while (opcode_is_prefix(*op)) |
| 102 | ++op; |
| 103 | if (opcode_is_rex_prefix(*op)) |
| 104 | ++op; |
| 105 | return op; |
| 106 | } |