Commit 1c73ef66 authored by Avi Kivity's avatar Avi Kivity

KVM: x86 emulator: Hoist modrm and abs decoding into separate functions

Signed-off-by: default avatarAvi Kivity <avi@qumranet.com>
parent 3b6fff19
......@@ -548,122 +548,20 @@ static void decode_register_operand(struct operand *op,
op->orig_val = op->val;
}
int
x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
static int decode_modrm(struct x86_emulate_ctxt *ctxt,
struct x86_emulate_ops *ops)
{
struct decode_cache *c = &ctxt->decode;
u8 sib;
int rc = 0;
int mode = ctxt->mode;
int index_reg = 0, base_reg = 0, scale, rip_relative = 0;
int rc = 0;
/* Shadow copy of register state. Committed on successful emulation. */
memset(c, 0, sizeof(struct decode_cache));
c->eip = ctxt->vcpu->rip;
memcpy(c->regs, ctxt->vcpu->regs, sizeof c->regs);
switch (mode) {
case X86EMUL_MODE_REAL:
case X86EMUL_MODE_PROT16:
c->op_bytes = c->ad_bytes = 2;
break;
case X86EMUL_MODE_PROT32:
c->op_bytes = c->ad_bytes = 4;
break;
#ifdef CONFIG_X86_64
case X86EMUL_MODE_PROT64:
c->op_bytes = 4;
c->ad_bytes = 8;
break;
#endif
default:
return -1;
}
/* Legacy prefixes. */
for (;;) {
switch (c->b = insn_fetch(u8, 1, c->eip)) {
case 0x66: /* operand-size override */
c->op_bytes ^= 6; /* switch between 2/4 bytes */
break;
case 0x67: /* address-size override */
if (mode == X86EMUL_MODE_PROT64)
/* switch between 4/8 bytes */
c->ad_bytes ^= 12;
else
/* switch between 2/4 bytes */
c->ad_bytes ^= 6;
break;
case 0x2e: /* CS override */
c->override_base = &ctxt->cs_base;
break;
case 0x3e: /* DS override */
c->override_base = &ctxt->ds_base;
break;
case 0x26: /* ES override */
c->override_base = &ctxt->es_base;
break;
case 0x64: /* FS override */
c->override_base = &ctxt->fs_base;
break;
case 0x65: /* GS override */
c->override_base = &ctxt->gs_base;
break;
case 0x36: /* SS override */
c->override_base = &ctxt->ss_base;
break;
case 0x40 ... 0x4f: /* REX */
if (mode != X86EMUL_MODE_PROT64)
goto done_prefixes;
c->rex_prefix = c->b;
continue;
case 0xf0: /* LOCK */
c->lock_prefix = 1;
break;
case 0xf2: /* REPNE/REPNZ */
case 0xf3: /* REP/REPE/REPZ */
c->rep_prefix = 1;
break;
default:
goto done_prefixes;
}
/* Any legacy prefix after a REX prefix nullifies its effect. */
c->rex_prefix = 0;
}
done_prefixes:
/* REX prefix. */
if (c->rex_prefix) {
if (c->rex_prefix & 8)
c->op_bytes = 8; /* REX.W */
c->modrm_reg = (c->rex_prefix & 4) << 1; /* REX.R */
index_reg = (c->rex_prefix & 2) << 2; /* REX.X */
c->modrm_rm = base_reg = (c->rex_prefix & 1) << 3; /* REG.B */
}
/* Opcode byte(s). */
c->d = opcode_table[c->b];
if (c->d == 0) {
/* Two-byte opcode? */
if (c->b == 0x0f) {
c->twobyte = 1;
c->b = insn_fetch(u8, 1, c->eip);
c->d = twobyte_table[c->b];
}
/* Unrecognised? */
if (c->d == 0) {
DPRINTF("Cannot emulate %02x\n", c->b);
return -1;
}
}
/* ModRM and SIB bytes. */
if (c->d & ModRM) {
c->modrm = insn_fetch(u8, 1, c->eip);
c->modrm_mod |= (c->modrm & 0xc0) >> 6;
c->modrm_reg |= (c->modrm & 0x38) >> 3;
......@@ -674,7 +572,7 @@ done_prefixes:
if (c->modrm_mod == 3) {
c->modrm_val = *(unsigned long *)
decode_register(c->modrm_rm, c->regs, c->d & ByteOp);
goto modrm_done;
return rc;
}
if (c->ad_bytes == 2) {
......@@ -687,8 +585,7 @@ done_prefixes:
switch (c->modrm_mod) {
case 0:
if (c->modrm_rm == 6)
c->modrm_ea +=
insn_fetch(u16, 2, c->eip);
c->modrm_ea += insn_fetch(u16, 2, c->eip);
break;
case 1:
c->modrm_ea += insn_fetch(s8, 1, c->eip);
......@@ -742,8 +639,7 @@ done_prefixes:
switch (base_reg) {
case 5:
if (c->modrm_mod != 0)
c->modrm_ea +=
c->regs[base_reg];
c->modrm_ea += c->regs[base_reg];
else
c->modrm_ea +=
insn_fetch(s32, 4, c->eip);
......@@ -755,15 +651,13 @@ done_prefixes:
case 4:
break;
default:
c->modrm_ea +=
c->regs[index_reg] << scale;
c->modrm_ea += c->regs[index_reg] << scale;
}
break;
case 5:
if (c->modrm_mod != 0)
c->modrm_ea += c->regs[c->modrm_rm];
else if (mode == X86EMUL_MODE_PROT64)
else if (ctxt->mode == X86EMUL_MODE_PROT64)
rip_relative = 1;
break;
default:
......@@ -773,8 +667,7 @@ done_prefixes:
switch (c->modrm_mod) {
case 0:
if (c->modrm_rm == 5)
c->modrm_ea +=
insn_fetch(s32, 4, c->eip);
c->modrm_ea += insn_fetch(s32, 4, c->eip);
break;
case 1:
c->modrm_ea += insn_fetch(s8, 1, c->eip);
......@@ -800,9 +693,16 @@ done_prefixes:
c->modrm_ea += c->op_bytes;
}
}
modrm_done:
;
} else if (c->d & MemAbs) {
done:
return rc;
}
static int decode_abs(struct x86_emulate_ctxt *ctxt,
struct x86_emulate_ops *ops)
{
struct decode_cache *c = &ctxt->decode;
int rc = 0;
switch (c->ad_bytes) {
case 2:
c->modrm_ea = insn_fetch(u16, 2, c->eip);
......@@ -814,9 +714,126 @@ modrm_done:
c->modrm_ea = insn_fetch(u64, 8, c->eip);
break;
}
done:
return rc;
}
int
x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
{
struct decode_cache *c = &ctxt->decode;
int rc = 0;
int mode = ctxt->mode;
/* Shadow copy of register state. Committed on successful emulation. */
memset(c, 0, sizeof(struct decode_cache));
c->eip = ctxt->vcpu->rip;
memcpy(c->regs, ctxt->vcpu->regs, sizeof c->regs);
switch (mode) {
case X86EMUL_MODE_REAL:
case X86EMUL_MODE_PROT16:
c->op_bytes = c->ad_bytes = 2;
break;
case X86EMUL_MODE_PROT32:
c->op_bytes = c->ad_bytes = 4;
break;
#ifdef CONFIG_X86_64
case X86EMUL_MODE_PROT64:
c->op_bytes = 4;
c->ad_bytes = 8;
break;
#endif
default:
return -1;
}
/* Legacy prefixes. */
for (;;) {
switch (c->b = insn_fetch(u8, 1, c->eip)) {
case 0x66: /* operand-size override */
c->op_bytes ^= 6; /* switch between 2/4 bytes */
break;
case 0x67: /* address-size override */
if (mode == X86EMUL_MODE_PROT64)
/* switch between 4/8 bytes */
c->ad_bytes ^= 12;
else
/* switch between 2/4 bytes */
c->ad_bytes ^= 6;
break;
case 0x2e: /* CS override */
c->override_base = &ctxt->cs_base;
break;
case 0x3e: /* DS override */
c->override_base = &ctxt->ds_base;
break;
case 0x26: /* ES override */
c->override_base = &ctxt->es_base;
break;
case 0x64: /* FS override */
c->override_base = &ctxt->fs_base;
break;
case 0x65: /* GS override */
c->override_base = &ctxt->gs_base;
break;
case 0x36: /* SS override */
c->override_base = &ctxt->ss_base;
break;
case 0x40 ... 0x4f: /* REX */
if (mode != X86EMUL_MODE_PROT64)
goto done_prefixes;
c->rex_prefix = c->b;
continue;
case 0xf0: /* LOCK */
c->lock_prefix = 1;
break;
case 0xf2: /* REPNE/REPNZ */
case 0xf3: /* REP/REPE/REPZ */
c->rep_prefix = 1;
break;
default:
goto done_prefixes;
}
/* Any legacy prefix after a REX prefix nullifies its effect. */
c->rex_prefix = 0;
}
done_prefixes:
/* REX prefix. */
if (c->rex_prefix)
if (c->rex_prefix & 8)
c->op_bytes = 8; /* REX.W */
/* Opcode byte(s). */
c->d = opcode_table[c->b];
if (c->d == 0) {
/* Two-byte opcode? */
if (c->b == 0x0f) {
c->twobyte = 1;
c->b = insn_fetch(u8, 1, c->eip);
c->d = twobyte_table[c->b];
}
/* Unrecognised? */
if (c->d == 0) {
DPRINTF("Cannot emulate %02x\n", c->b);
return -1;
}
}
/* ModRM and SIB bytes. */
if (c->d & ModRM)
rc = decode_modrm(ctxt, ops);
else if (c->d & MemAbs)
rc = decode_abs(ctxt, ops);
if (rc)
goto done;
if (!c->override_base)
c->override_base = &ctxt->ds_base;
if (mode == X86EMUL_MODE_PROT64 &&
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment