KVM: x86 emulator: Hoist modrm and abs decoding into separate functions
authorAvi Kivity <avi@qumranet.com>
Thu, 1 Nov 2007 04:31:28 +0000 (06:31 +0200)
committerAvi Kivity <avi@qumranet.com>
Wed, 30 Jan 2008 15:52:59 +0000 (17:52 +0200)
Signed-off-by: Avi Kivity <avi@qumranet.com>
drivers/kvm/x86_emulate.c

index b352a6c4d4a2a20f56aaaae2e24760423c8cd33f..64888a6edc275e9011ad09d739b5ad008b9b9b26 100644 (file)
@@ -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;