diff options
author | Gleb Natapov <gleb@redhat.com> | 2010-04-28 12:15:26 -0400 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2010-08-01 03:35:30 -0400 |
commit | 414e6277fd148f6470261cef50a7fed0d88a2825 (patch) | |
tree | c105f8a33e9f744695d0ffd2172f6d2a9e859258 | |
parent | b8a98945ea5b735e083eaf92906aa0ff9ece92e8 (diff) |
KVM: x86 emulator: handle "far address" source operand
ljmp/lcall instruction operand contains address and segment.
It can be 10 bytes long. Currently we decode it as two different
operands. Fix it by introducing new kind of operand that can hold
entire far address.
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r-- | arch/x86/include/asm/kvm_emulate.h | 6 | ||||
-rw-r--r-- | arch/x86/kvm/emulate.c | 56 |
2 files changed, 37 insertions, 25 deletions
diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h index 288cbedcab1c..69a64a6a36f4 100644 --- a/arch/x86/include/asm/kvm_emulate.h +++ b/arch/x86/include/asm/kvm_emulate.h | |||
@@ -143,7 +143,11 @@ struct x86_emulate_ops { | |||
143 | struct operand { | 143 | struct operand { |
144 | enum { OP_REG, OP_MEM, OP_IMM, OP_NONE } type; | 144 | enum { OP_REG, OP_MEM, OP_IMM, OP_NONE } type; |
145 | unsigned int bytes; | 145 | unsigned int bytes; |
146 | unsigned long val, orig_val, *ptr; | 146 | unsigned long orig_val, *ptr; |
147 | union { | ||
148 | unsigned long val; | ||
149 | char valptr[sizeof(unsigned long) + 2]; | ||
150 | }; | ||
147 | }; | 151 | }; |
148 | 152 | ||
149 | struct fetch_cache { | 153 | struct fetch_cache { |
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 03a72912d7b9..687ea0906b79 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c | |||
@@ -67,6 +67,8 @@ | |||
67 | #define SrcImmUByte (8<<4) /* 8-bit unsigned immediate operand. */ | 67 | #define SrcImmUByte (8<<4) /* 8-bit unsigned immediate operand. */ |
68 | #define SrcImmU (9<<4) /* Immediate operand, unsigned */ | 68 | #define SrcImmU (9<<4) /* Immediate operand, unsigned */ |
69 | #define SrcSI (0xa<<4) /* Source is in the DS:RSI */ | 69 | #define SrcSI (0xa<<4) /* Source is in the DS:RSI */ |
70 | #define SrcImmFAddr (0xb<<4) /* Source is immediate far address */ | ||
71 | #define SrcMemFAddr (0xc<<4) /* Source is far address in memory */ | ||
70 | #define SrcMask (0xf<<4) | 72 | #define SrcMask (0xf<<4) |
71 | /* Generic ModRM decode. */ | 73 | /* Generic ModRM decode. */ |
72 | #define ModRM (1<<8) | 74 | #define ModRM (1<<8) |
@@ -88,10 +90,6 @@ | |||
88 | #define Src2CL (1<<29) | 90 | #define Src2CL (1<<29) |
89 | #define Src2ImmByte (2<<29) | 91 | #define Src2ImmByte (2<<29) |
90 | #define Src2One (3<<29) | 92 | #define Src2One (3<<29) |
91 | #define Src2Imm16 (4<<29) | ||
92 | #define Src2Mem16 (5<<29) /* Used for Ep encoding. First argument has to be | ||
93 | in memory and second argument is located | ||
94 | immediately after the first one in memory. */ | ||
95 | #define Src2Mask (7<<29) | 93 | #define Src2Mask (7<<29) |
96 | 94 | ||
97 | enum { | 95 | enum { |
@@ -175,7 +173,7 @@ static u32 opcode_table[256] = { | |||
175 | /* 0x90 - 0x97 */ | 173 | /* 0x90 - 0x97 */ |
176 | DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, | 174 | DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, |
177 | /* 0x98 - 0x9F */ | 175 | /* 0x98 - 0x9F */ |
178 | 0, 0, SrcImm | Src2Imm16 | No64, 0, | 176 | 0, 0, SrcImmFAddr | No64, 0, |
179 | ImplicitOps | Stack, ImplicitOps | Stack, 0, 0, | 177 | ImplicitOps | Stack, ImplicitOps | Stack, 0, 0, |
180 | /* 0xA0 - 0xA7 */ | 178 | /* 0xA0 - 0xA7 */ |
181 | ByteOp | DstReg | SrcMem | Mov | MemAbs, DstReg | SrcMem | Mov | MemAbs, | 179 | ByteOp | DstReg | SrcMem | Mov | MemAbs, DstReg | SrcMem | Mov | MemAbs, |
@@ -215,7 +213,7 @@ static u32 opcode_table[256] = { | |||
215 | ByteOp | SrcImmUByte | DstAcc, SrcImmUByte | DstAcc, | 213 | ByteOp | SrcImmUByte | DstAcc, SrcImmUByte | DstAcc, |
216 | /* 0xE8 - 0xEF */ | 214 | /* 0xE8 - 0xEF */ |
217 | SrcImm | Stack, SrcImm | ImplicitOps, | 215 | SrcImm | Stack, SrcImm | ImplicitOps, |
218 | SrcImmU | Src2Imm16 | No64, SrcImmByte | ImplicitOps, | 216 | SrcImmFAddr | No64, SrcImmByte | ImplicitOps, |
219 | SrcNone | ByteOp | DstAcc, SrcNone | DstAcc, | 217 | SrcNone | ByteOp | DstAcc, SrcNone | DstAcc, |
220 | SrcNone | ByteOp | DstAcc, SrcNone | DstAcc, | 218 | SrcNone | ByteOp | DstAcc, SrcNone | DstAcc, |
221 | /* 0xF0 - 0xF7 */ | 219 | /* 0xF0 - 0xF7 */ |
@@ -350,7 +348,7 @@ static u32 group_table[] = { | |||
350 | [Group5*8] = | 348 | [Group5*8] = |
351 | DstMem | SrcNone | ModRM, DstMem | SrcNone | ModRM, | 349 | DstMem | SrcNone | ModRM, DstMem | SrcNone | ModRM, |
352 | SrcMem | ModRM | Stack, 0, | 350 | SrcMem | ModRM | Stack, 0, |
353 | SrcMem | ModRM | Stack, SrcMem | ModRM | Src2Mem16 | ImplicitOps, | 351 | SrcMem | ModRM | Stack, SrcMemFAddr | ModRM | ImplicitOps, |
354 | SrcMem | ModRM | Stack, 0, | 352 | SrcMem | ModRM | Stack, 0, |
355 | [Group7*8] = | 353 | [Group7*8] = |
356 | 0, 0, ModRM | SrcMem | Priv, ModRM | SrcMem | Priv, | 354 | 0, 0, ModRM | SrcMem | Priv, ModRM | SrcMem | Priv, |
@@ -576,6 +574,13 @@ static u32 group2_table[] = { | |||
576 | (_type)_x; \ | 574 | (_type)_x; \ |
577 | }) | 575 | }) |
578 | 576 | ||
577 | #define insn_fetch_arr(_arr, _size, _eip) \ | ||
578 | ({ rc = do_insn_fetch(ctxt, ops, (_eip), _arr, (_size)); \ | ||
579 | if (rc != X86EMUL_CONTINUE) \ | ||
580 | goto done; \ | ||
581 | (_eip) += (_size); \ | ||
582 | }) | ||
583 | |||
579 | static inline unsigned long ad_mask(struct decode_cache *c) | 584 | static inline unsigned long ad_mask(struct decode_cache *c) |
580 | { | 585 | { |
581 | return (1UL << (c->ad_bytes << 3)) - 1; | 586 | return (1UL << (c->ad_bytes << 3)) - 1; |
@@ -1160,6 +1165,17 @@ done_prefixes: | |||
1160 | c->regs[VCPU_REGS_RSI]); | 1165 | c->regs[VCPU_REGS_RSI]); |
1161 | c->src.val = 0; | 1166 | c->src.val = 0; |
1162 | break; | 1167 | break; |
1168 | case SrcImmFAddr: | ||
1169 | c->src.type = OP_IMM; | ||
1170 | c->src.ptr = (unsigned long *)c->eip; | ||
1171 | c->src.bytes = c->op_bytes + 2; | ||
1172 | insn_fetch_arr(c->src.valptr, c->src.bytes, c->eip); | ||
1173 | break; | ||
1174 | case SrcMemFAddr: | ||
1175 | c->src.type = OP_MEM; | ||
1176 | c->src.ptr = (unsigned long *)c->modrm_ea; | ||
1177 | c->src.bytes = c->op_bytes + 2; | ||
1178 | break; | ||
1163 | } | 1179 | } |
1164 | 1180 | ||
1165 | /* | 1181 | /* |
@@ -1179,22 +1195,10 @@ done_prefixes: | |||
1179 | c->src2.bytes = 1; | 1195 | c->src2.bytes = 1; |
1180 | c->src2.val = insn_fetch(u8, 1, c->eip); | 1196 | c->src2.val = insn_fetch(u8, 1, c->eip); |
1181 | break; | 1197 | break; |
1182 | case Src2Imm16: | ||
1183 | c->src2.type = OP_IMM; | ||
1184 | c->src2.ptr = (unsigned long *)c->eip; | ||
1185 | c->src2.bytes = 2; | ||
1186 | c->src2.val = insn_fetch(u16, 2, c->eip); | ||
1187 | break; | ||
1188 | case Src2One: | 1198 | case Src2One: |
1189 | c->src2.bytes = 1; | 1199 | c->src2.bytes = 1; |
1190 | c->src2.val = 1; | 1200 | c->src2.val = 1; |
1191 | break; | 1201 | break; |
1192 | case Src2Mem16: | ||
1193 | c->src2.type = OP_MEM; | ||
1194 | c->src2.bytes = 2; | ||
1195 | c->src2.ptr = (unsigned long *)(c->modrm_ea + c->src.bytes); | ||
1196 | c->src2.val = 0; | ||
1197 | break; | ||
1198 | } | 1202 | } |
1199 | 1203 | ||
1200 | /* Decode and fetch the destination operand: register or memory. */ | 1204 | /* Decode and fetch the destination operand: register or memory. */ |
@@ -2558,7 +2562,7 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops) | |||
2558 | 2562 | ||
2559 | if (c->src.type == OP_MEM) { | 2563 | if (c->src.type == OP_MEM) { |
2560 | rc = read_emulated(ctxt, ops, (unsigned long)c->src.ptr, | 2564 | rc = read_emulated(ctxt, ops, (unsigned long)c->src.ptr, |
2561 | &c->src.val, c->src.bytes); | 2565 | c->src.valptr, c->src.bytes); |
2562 | if (rc != X86EMUL_CONTINUE) | 2566 | if (rc != X86EMUL_CONTINUE) |
2563 | goto done; | 2567 | goto done; |
2564 | c->src.orig_val = c->src.val; | 2568 | c->src.orig_val = c->src.val; |
@@ -2884,14 +2888,18 @@ special_insn: | |||
2884 | } | 2888 | } |
2885 | case 0xe9: /* jmp rel */ | 2889 | case 0xe9: /* jmp rel */ |
2886 | goto jmp; | 2890 | goto jmp; |
2887 | case 0xea: /* jmp far */ | 2891 | case 0xea: { /* jmp far */ |
2892 | unsigned short sel; | ||
2888 | jump_far: | 2893 | jump_far: |
2889 | if (load_segment_descriptor(ctxt, ops, c->src2.val, | 2894 | memcpy(&sel, c->src.valptr + c->op_bytes, 2); |
2890 | VCPU_SREG_CS)) | 2895 | |
2896 | if (load_segment_descriptor(ctxt, ops, sel, VCPU_SREG_CS)) | ||
2891 | goto done; | 2897 | goto done; |
2892 | 2898 | ||
2893 | c->eip = c->src.val; | 2899 | c->eip = 0; |
2900 | memcpy(&c->eip, c->src.valptr, c->op_bytes); | ||
2894 | break; | 2901 | break; |
2902 | } | ||
2895 | case 0xeb: | 2903 | case 0xeb: |
2896 | jmp: /* jmp rel short */ | 2904 | jmp: /* jmp rel short */ |
2897 | jmp_rel(c, c->src.val); | 2905 | jmp_rel(c, c->src.val); |