aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAvi Kivity2007-11-01 06:31:28 +0200
committerAvi Kivity2008-01-30 17:52:59 +0200
commit1c73ef66502311d9aff7fed7d7f970288329c6cb (patch)
tree1bafc99b5c6e0786670d9198fef2caf113fa820a /drivers
parent3b6fff198c2ae3ca87ee8c9103d52ac9e8f40dd5 (diff)
KVM: x86 emulator: Hoist modrm and abs decoding into separate functions
Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/kvm/x86_emulate.c337
1 files changed, 177 insertions, 160 deletions
diff --git a/drivers/kvm/x86_emulate.c b/drivers/kvm/x86_emulate.c
index b352a6c4d4a2..64888a6edc27 100644
--- a/drivers/kvm/x86_emulate.c
+++ b/drivers/kvm/x86_emulate.c
@@ -548,14 +548,182 @@ static void decode_register_operand(struct operand *op,
op->orig_val = op->val;
}
+static int decode_modrm(struct x86_emulate_ctxt *ctxt,
+ struct x86_emulate_ops *ops)
+{
+ struct decode_cache *c = &ctxt->decode;
+ u8 sib;
+ int index_reg = 0, base_reg = 0, scale, rip_relative = 0;
+ int rc = 0;
+
+ if (c->rex_prefix) {
+ 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 */
+ }
+
+ c->modrm = insn_fetch(u8, 1, c->eip);
+ c->modrm_mod |= (c->modrm & 0xc0) >> 6;
+ c->modrm_reg |= (c->modrm & 0x38) >> 3;
+ c->modrm_rm |= (c->modrm & 0x07);
+ c->modrm_ea = 0;
+ c->use_modrm_ea = 1;
+
+ if (c->modrm_mod == 3) {
+ c->modrm_val = *(unsigned long *)
+ decode_register(c->modrm_rm, c->regs, c->d & ByteOp);
+ return rc;
+ }
+
+ if (c->ad_bytes == 2) {
+ unsigned bx = c->regs[VCPU_REGS_RBX];
+ unsigned bp = c->regs[VCPU_REGS_RBP];
+ unsigned si = c->regs[VCPU_REGS_RSI];
+ unsigned di = c->regs[VCPU_REGS_RDI];
+
+ /* 16-bit ModR/M decode. */
+ switch (c->modrm_mod) {
+ case 0:
+ if (c->modrm_rm == 6)
+ c->modrm_ea += insn_fetch(u16, 2, c->eip);
+ break;
+ case 1:
+ c->modrm_ea += insn_fetch(s8, 1, c->eip);
+ break;
+ case 2:
+ c->modrm_ea += insn_fetch(u16, 2, c->eip);
+ break;
+ }
+ switch (c->modrm_rm) {
+ case 0:
+ c->modrm_ea += bx + si;
+ break;
+ case 1:
+ c->modrm_ea += bx + di;
+ break;
+ case 2:
+ c->modrm_ea += bp + si;
+ break;
+ case 3:
+ c->modrm_ea += bp + di;
+ break;
+ case 4:
+ c->modrm_ea += si;
+ break;
+ case 5:
+ c->modrm_ea += di;
+ break;
+ case 6:
+ if (c->modrm_mod != 0)
+ c->modrm_ea += bp;
+ break;
+ case 7:
+ c->modrm_ea += bx;
+ break;
+ }
+ if (c->modrm_rm == 2 || c->modrm_rm == 3 ||
+ (c->modrm_rm == 6 && c->modrm_mod != 0))
+ if (!c->override_base)
+ c->override_base = &ctxt->ss_base;
+ c->modrm_ea = (u16)c->modrm_ea;
+ } else {
+ /* 32/64-bit ModR/M decode. */
+ switch (c->modrm_rm) {
+ case 4:
+ case 12:
+ sib = insn_fetch(u8, 1, c->eip);
+ index_reg |= (sib >> 3) & 7;
+ base_reg |= sib & 7;
+ scale = sib >> 6;
+
+ switch (base_reg) {
+ case 5:
+ if (c->modrm_mod != 0)
+ c->modrm_ea += c->regs[base_reg];
+ else
+ c->modrm_ea +=
+ insn_fetch(s32, 4, c->eip);
+ break;
+ default:
+ c->modrm_ea += c->regs[base_reg];
+ }
+ switch (index_reg) {
+ case 4:
+ break;
+ default:
+ 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 (ctxt->mode == X86EMUL_MODE_PROT64)
+ rip_relative = 1;
+ break;
+ default:
+ c->modrm_ea += c->regs[c->modrm_rm];
+ break;
+ }
+ switch (c->modrm_mod) {
+ case 0:
+ if (c->modrm_rm == 5)
+ c->modrm_ea += insn_fetch(s32, 4, c->eip);
+ break;
+ case 1:
+ c->modrm_ea += insn_fetch(s8, 1, c->eip);
+ break;
+ case 2:
+ c->modrm_ea += insn_fetch(s32, 4, c->eip);
+ break;
+ }
+ }
+ if (rip_relative) {
+ c->modrm_ea += c->eip;
+ switch (c->d & SrcMask) {
+ case SrcImmByte:
+ c->modrm_ea += 1;
+ break;
+ case SrcImm:
+ if (c->d & ByteOp)
+ c->modrm_ea += 1;
+ else
+ if (c->op_bytes == 8)
+ c->modrm_ea += 4;
+ else
+ c->modrm_ea += c->op_bytes;
+ }
+ }
+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);
+ break;
+ case 4:
+ c->modrm_ea = insn_fetch(u32, 4, c->eip);
+ break;
+ case 8:
+ 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;
- u8 sib;
int rc = 0;
int mode = ctxt->mode;
- int index_reg = 0, base_reg = 0, scale, rip_relative = 0;
/* Shadow copy of register state. Committed on successful emulation. */
@@ -637,13 +805,9 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
done_prefixes:
/* REX prefix. */
- if (c->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];
@@ -663,159 +827,12 @@ done_prefixes:
}
/* 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;
- c->modrm_rm |= (c->modrm & 0x07);
- c->modrm_ea = 0;
- c->use_modrm_ea = 1;
-
- if (c->modrm_mod == 3) {
- c->modrm_val = *(unsigned long *)
- decode_register(c->modrm_rm, c->regs, c->d & ByteOp);
- goto modrm_done;
- }
-
- if (c->ad_bytes == 2) {
- unsigned bx = c->regs[VCPU_REGS_RBX];
- unsigned bp = c->regs[VCPU_REGS_RBP];
- unsigned si = c->regs[VCPU_REGS_RSI];
- unsigned di = c->regs[VCPU_REGS_RDI];
-
- /* 16-bit ModR/M decode. */
- switch (c->modrm_mod) {
- case 0:
- if (c->modrm_rm == 6)
- c->modrm_ea +=
- insn_fetch(u16, 2, c->eip);
- break;
- case 1:
- c->modrm_ea += insn_fetch(s8, 1, c->eip);
- break;
- case 2:
- c->modrm_ea += insn_fetch(u16, 2, c->eip);
- break;
- }
- switch (c->modrm_rm) {
- case 0:
- c->modrm_ea += bx + si;
- break;
- case 1:
- c->modrm_ea += bx + di;
- break;
- case 2:
- c->modrm_ea += bp + si;
- break;
- case 3:
- c->modrm_ea += bp + di;
- break;
- case 4:
- c->modrm_ea += si;
- break;
- case 5:
- c->modrm_ea += di;
- break;
- case 6:
- if (c->modrm_mod != 0)
- c->modrm_ea += bp;
- break;
- case 7:
- c->modrm_ea += bx;
- break;
- }
- if (c->modrm_rm == 2 || c->modrm_rm == 3 ||
- (c->modrm_rm == 6 && c->modrm_mod != 0))
- if (!c->override_base)
- c->override_base = &ctxt->ss_base;
- c->modrm_ea = (u16)c->modrm_ea;
- } else {
- /* 32/64-bit ModR/M decode. */
- switch (c->modrm_rm) {
- case 4:
- case 12:
- sib = insn_fetch(u8, 1, c->eip);
- index_reg |= (sib >> 3) & 7;
- base_reg |= sib & 7;
- scale = sib >> 6;
-
- switch (base_reg) {
- case 5:
- if (c->modrm_mod != 0)
- c->modrm_ea +=
- c->regs[base_reg];
- else
- c->modrm_ea +=
- insn_fetch(s32, 4, c->eip);
- break;
- default:
- c->modrm_ea += c->regs[base_reg];
- }
- switch (index_reg) {
- case 4:
- break;
- default:
- 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)
- rip_relative = 1;
- break;
- default:
- c->modrm_ea += c->regs[c->modrm_rm];
- break;
- }
- switch (c->modrm_mod) {
- case 0:
- if (c->modrm_rm == 5)
- c->modrm_ea +=
- insn_fetch(s32, 4, c->eip);
- break;
- case 1:
- c->modrm_ea += insn_fetch(s8, 1, c->eip);
- break;
- case 2:
- c->modrm_ea += insn_fetch(s32, 4, c->eip);
- break;
- }
- }
- if (rip_relative) {
- c->modrm_ea += c->eip;
- switch (c->d & SrcMask) {
- case SrcImmByte:
- c->modrm_ea += 1;
- break;
- case SrcImm:
- if (c->d & ByteOp)
- c->modrm_ea += 1;
- else
- if (c->op_bytes == 8)
- c->modrm_ea += 4;
- else
- c->modrm_ea += c->op_bytes;
- }
- }
-modrm_done:
- ;
- } else if (c->d & MemAbs) {
- switch (c->ad_bytes) {
- case 2:
- c->modrm_ea = insn_fetch(u16, 2, c->eip);
- break;
- case 4:
- c->modrm_ea = insn_fetch(u32, 4, c->eip);
- break;
- case 8:
- c->modrm_ea = insn_fetch(u64, 8, c->eip);
- break;
- }
-
- }
+ 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;