KVM: x86 emulator: Decode memory operands directly into a 'struct operand'

Since modrm operand can be either register or memory, decoding it into
a 'struct operand', which can represent both, is simpler.

Signed-off-by: Avi Kivity <avi@redhat.com>
This commit is contained in:
Avi Kivity 2010-08-01 15:40:19 +03:00
parent 1f6f05800e
commit 2dbd0dd711
2 changed files with 57 additions and 71 deletions

View File

@ -203,9 +203,6 @@ struct decode_cache {
u8 modrm_rm; u8 modrm_rm;
u8 modrm_seg; u8 modrm_seg;
bool rip_relative; bool rip_relative;
unsigned long modrm_ea;
void *modrm_ptr;
unsigned long modrm_val;
struct fetch_cache fetch; struct fetch_cache fetch;
struct read_cache io_read; struct read_cache io_read;
struct read_cache mem_read; struct read_cache mem_read;

View File

@ -581,12 +581,14 @@ static void decode_register_operand(struct operand *op,
} }
static int decode_modrm(struct x86_emulate_ctxt *ctxt, static int decode_modrm(struct x86_emulate_ctxt *ctxt,
struct x86_emulate_ops *ops) struct x86_emulate_ops *ops,
struct operand *op)
{ {
struct decode_cache *c = &ctxt->decode; struct decode_cache *c = &ctxt->decode;
u8 sib; u8 sib;
int index_reg = 0, base_reg = 0, scale; int index_reg = 0, base_reg = 0, scale;
int rc = X86EMUL_CONTINUE; int rc = X86EMUL_CONTINUE;
ulong modrm_ea = 0;
if (c->rex_prefix) { if (c->rex_prefix) {
c->modrm_reg = (c->rex_prefix & 4) << 1; /* REX.R */ c->modrm_reg = (c->rex_prefix & 4) << 1; /* REX.R */
@ -598,16 +600,19 @@ static int decode_modrm(struct x86_emulate_ctxt *ctxt,
c->modrm_mod |= (c->modrm & 0xc0) >> 6; c->modrm_mod |= (c->modrm & 0xc0) >> 6;
c->modrm_reg |= (c->modrm & 0x38) >> 3; c->modrm_reg |= (c->modrm & 0x38) >> 3;
c->modrm_rm |= (c->modrm & 0x07); c->modrm_rm |= (c->modrm & 0x07);
c->modrm_ea = 0;
c->modrm_seg = VCPU_SREG_DS; c->modrm_seg = VCPU_SREG_DS;
if (c->modrm_mod == 3) { if (c->modrm_mod == 3) {
c->modrm_ptr = decode_register(c->modrm_rm, op->type = OP_REG;
op->bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
op->addr.reg = decode_register(c->modrm_rm,
c->regs, c->d & ByteOp); c->regs, c->d & ByteOp);
c->modrm_val = *(unsigned long *)c->modrm_ptr; fetch_register_operand(op);
return rc; return rc;
} }
op->type = OP_MEM;
if (c->ad_bytes == 2) { if (c->ad_bytes == 2) {
unsigned bx = c->regs[VCPU_REGS_RBX]; unsigned bx = c->regs[VCPU_REGS_RBX];
unsigned bp = c->regs[VCPU_REGS_RBP]; unsigned bp = c->regs[VCPU_REGS_RBP];
@ -618,46 +623,46 @@ static int decode_modrm(struct x86_emulate_ctxt *ctxt,
switch (c->modrm_mod) { switch (c->modrm_mod) {
case 0: case 0:
if (c->modrm_rm == 6) if (c->modrm_rm == 6)
c->modrm_ea += insn_fetch(u16, 2, c->eip); modrm_ea += insn_fetch(u16, 2, c->eip);
break; break;
case 1: case 1:
c->modrm_ea += insn_fetch(s8, 1, c->eip); modrm_ea += insn_fetch(s8, 1, c->eip);
break; break;
case 2: case 2:
c->modrm_ea += insn_fetch(u16, 2, c->eip); modrm_ea += insn_fetch(u16, 2, c->eip);
break; break;
} }
switch (c->modrm_rm) { switch (c->modrm_rm) {
case 0: case 0:
c->modrm_ea += bx + si; modrm_ea += bx + si;
break; break;
case 1: case 1:
c->modrm_ea += bx + di; modrm_ea += bx + di;
break; break;
case 2: case 2:
c->modrm_ea += bp + si; modrm_ea += bp + si;
break; break;
case 3: case 3:
c->modrm_ea += bp + di; modrm_ea += bp + di;
break; break;
case 4: case 4:
c->modrm_ea += si; modrm_ea += si;
break; break;
case 5: case 5:
c->modrm_ea += di; modrm_ea += di;
break; break;
case 6: case 6:
if (c->modrm_mod != 0) if (c->modrm_mod != 0)
c->modrm_ea += bp; modrm_ea += bp;
break; break;
case 7: case 7:
c->modrm_ea += bx; modrm_ea += bx;
break; break;
} }
if (c->modrm_rm == 2 || c->modrm_rm == 3 || if (c->modrm_rm == 2 || c->modrm_rm == 3 ||
(c->modrm_rm == 6 && c->modrm_mod != 0)) (c->modrm_rm == 6 && c->modrm_mod != 0))
c->modrm_seg = VCPU_SREG_SS; c->modrm_seg = VCPU_SREG_SS;
c->modrm_ea = (u16)c->modrm_ea; modrm_ea = (u16)modrm_ea;
} else { } else {
/* 32/64-bit ModR/M decode. */ /* 32/64-bit ModR/M decode. */
if ((c->modrm_rm & 7) == 4) { if ((c->modrm_rm & 7) == 4) {
@ -667,48 +672,51 @@ static int decode_modrm(struct x86_emulate_ctxt *ctxt,
scale = sib >> 6; scale = sib >> 6;
if ((base_reg & 7) == 5 && c->modrm_mod == 0) if ((base_reg & 7) == 5 && c->modrm_mod == 0)
c->modrm_ea += insn_fetch(s32, 4, c->eip); modrm_ea += insn_fetch(s32, 4, c->eip);
else else
c->modrm_ea += c->regs[base_reg]; modrm_ea += c->regs[base_reg];
if (index_reg != 4) if (index_reg != 4)
c->modrm_ea += c->regs[index_reg] << scale; modrm_ea += c->regs[index_reg] << scale;
} else if ((c->modrm_rm & 7) == 5 && c->modrm_mod == 0) { } else if ((c->modrm_rm & 7) == 5 && c->modrm_mod == 0) {
if (ctxt->mode == X86EMUL_MODE_PROT64) if (ctxt->mode == X86EMUL_MODE_PROT64)
c->rip_relative = 1; c->rip_relative = 1;
} else } else
c->modrm_ea += c->regs[c->modrm_rm]; modrm_ea += c->regs[c->modrm_rm];
switch (c->modrm_mod) { switch (c->modrm_mod) {
case 0: case 0:
if (c->modrm_rm == 5) if (c->modrm_rm == 5)
c->modrm_ea += insn_fetch(s32, 4, c->eip); modrm_ea += insn_fetch(s32, 4, c->eip);
break; break;
case 1: case 1:
c->modrm_ea += insn_fetch(s8, 1, c->eip); modrm_ea += insn_fetch(s8, 1, c->eip);
break; break;
case 2: case 2:
c->modrm_ea += insn_fetch(s32, 4, c->eip); modrm_ea += insn_fetch(s32, 4, c->eip);
break; break;
} }
} }
op->addr.mem = modrm_ea;
done: done:
return rc; return rc;
} }
static int decode_abs(struct x86_emulate_ctxt *ctxt, static int decode_abs(struct x86_emulate_ctxt *ctxt,
struct x86_emulate_ops *ops) struct x86_emulate_ops *ops,
struct operand *op)
{ {
struct decode_cache *c = &ctxt->decode; struct decode_cache *c = &ctxt->decode;
int rc = X86EMUL_CONTINUE; int rc = X86EMUL_CONTINUE;
op->type = OP_MEM;
switch (c->ad_bytes) { switch (c->ad_bytes) {
case 2: case 2:
c->modrm_ea = insn_fetch(u16, 2, c->eip); op->addr.mem = insn_fetch(u16, 2, c->eip);
break; break;
case 4: case 4:
c->modrm_ea = insn_fetch(u32, 4, c->eip); op->addr.mem = insn_fetch(u32, 4, c->eip);
break; break;
case 8: case 8:
c->modrm_ea = insn_fetch(u64, 8, c->eip); op->addr.mem = insn_fetch(u64, 8, c->eip);
break; break;
} }
done: done:
@ -2280,6 +2288,7 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt)
int mode = ctxt->mode; int mode = ctxt->mode;
int def_op_bytes, def_ad_bytes, dual, goffset; int def_op_bytes, def_ad_bytes, dual, goffset;
struct opcode opcode, *g_mod012, *g_mod3; struct opcode opcode, *g_mod012, *g_mod3;
struct operand memop = { .type = OP_NONE };
/* we cannot decode insn before we complete previous rep insn */ /* we cannot decode insn before we complete previous rep insn */
WARN_ON(ctxt->restart); WARN_ON(ctxt->restart);
@ -2418,25 +2427,25 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt)
/* ModRM and SIB bytes. */ /* ModRM and SIB bytes. */
if (c->d & ModRM) { if (c->d & ModRM) {
rc = decode_modrm(ctxt, ops); rc = decode_modrm(ctxt, ops, &memop);
if (!c->has_seg_override) if (!c->has_seg_override)
set_seg_override(c, c->modrm_seg); set_seg_override(c, c->modrm_seg);
} else if (c->d & MemAbs) } else if (c->d & MemAbs)
rc = decode_abs(ctxt, ops); rc = decode_abs(ctxt, ops, &memop);
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
goto done; goto done;
if (!c->has_seg_override) if (!c->has_seg_override)
set_seg_override(c, VCPU_SREG_DS); set_seg_override(c, VCPU_SREG_DS);
if (!(!c->twobyte && c->b == 0x8d)) if (memop.type == OP_MEM && !(!c->twobyte && c->b == 0x8d))
c->modrm_ea += seg_override_base(ctxt, ops, c); memop.addr.mem += seg_override_base(ctxt, ops, c);
if (c->ad_bytes != 8) if (memop.type == OP_MEM && c->ad_bytes != 8)
c->modrm_ea = (u32)c->modrm_ea; memop.addr.mem = (u32)memop.addr.mem;
if (c->rip_relative) if (memop.type == OP_MEM && c->rip_relative)
c->modrm_ea += c->eip; memop.addr.mem += c->eip;
/* /*
* Decode and fetch the source operand: register, memory * Decode and fetch the source operand: register, memory
@ -2449,31 +2458,16 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt)
decode_register_operand(&c->src, c, 0); decode_register_operand(&c->src, c, 0);
break; break;
case SrcMem16: case SrcMem16:
c->src.bytes = 2; memop.bytes = 2;
goto srcmem_common; goto srcmem_common;
case SrcMem32: case SrcMem32:
c->src.bytes = 4; memop.bytes = 4;
goto srcmem_common; goto srcmem_common;
case SrcMem: case SrcMem:
c->src.bytes = (c->d & ByteOp) ? 1 : memop.bytes = (c->d & ByteOp) ? 1 :
c->op_bytes; c->op_bytes;
/* Don't fetch the address for invlpg: it could be unmapped. */
if (c->d & NoAccess)
break;
srcmem_common: srcmem_common:
/* c->src = memop;
* For instructions with a ModR/M byte, switch to register
* access if Mod = 3.
*/
if ((c->d & ModRM) && c->modrm_mod == 3) {
c->src.type = OP_REG;
c->src.val = c->modrm_val;
c->src.addr.reg = c->modrm_ptr;
break;
}
c->src.type = OP_MEM;
c->src.addr.mem = c->modrm_ea;
c->src.val = 0;
break; break;
case SrcImm: case SrcImm:
case SrcImmU: case SrcImmU:
@ -2543,9 +2537,8 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt)
insn_fetch_arr(c->src.valptr, c->src.bytes, c->eip); insn_fetch_arr(c->src.valptr, c->src.bytes, c->eip);
break; break;
case SrcMemFAddr: case SrcMemFAddr:
c->src.type = OP_MEM; memop.bytes = c->op_bytes + 2;
c->src.addr.mem = c->modrm_ea; goto srcmem_common;
c->src.bytes = c->op_bytes + 2;
break; break;
} }
@ -2583,26 +2576,18 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt)
break; break;
case DstMem: case DstMem:
case DstMem64: case DstMem64:
if ((c->d & ModRM) && c->modrm_mod == 3) { c->dst = memop;
c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
c->dst.type = OP_REG;
c->dst.val = c->dst.orig_val = c->modrm_val;
c->dst.addr.reg = c->modrm_ptr;
break;
}
c->dst.type = OP_MEM;
c->dst.addr.mem = c->modrm_ea;
if ((c->d & DstMask) == DstMem64) if ((c->d & DstMask) == DstMem64)
c->dst.bytes = 8; c->dst.bytes = 8;
else else
c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes; c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
c->dst.val = 0; if (c->dst.type == OP_MEM && (c->d & BitOp)) {
if (c->d & BitOp) {
unsigned long mask = ~(c->dst.bytes * 8 - 1); unsigned long mask = ~(c->dst.bytes * 8 - 1);
c->dst.addr.mem = c->dst.addr.mem + c->dst.addr.mem = c->dst.addr.mem +
(c->src.val & mask) / 8; (c->src.val & mask) / 8;
} }
c->dst.orig_val = c->dst.val;
break; break;
case DstAcc: case DstAcc:
c->dst.type = OP_REG; c->dst.type = OP_REG;
@ -2682,11 +2667,15 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
} }
if (c->src.type == OP_MEM) { if (c->src.type == OP_MEM) {
if (c->d & NoAccess)
goto no_fetch;
rc = read_emulated(ctxt, ops, c->src.addr.mem, rc = read_emulated(ctxt, ops, c->src.addr.mem,
c->src.valptr, c->src.bytes); c->src.valptr, c->src.bytes);
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
goto done; goto done;
c->src.orig_val64 = c->src.val64; c->src.orig_val64 = c->src.val64;
no_fetch:
;
} }
if (c->src2.type == OP_MEM) { if (c->src2.type == OP_MEM) {