Hello tech@,

Here is the patch discussed in the previous email. This covers emulation part.


@@ -5378,6 +5441,293 @@ vmm_get_guest_cpu_mode(struct vcpu *vcpu
 }

 /*
+ * get_vcpu_regs_id
+ *
+ * an utility for parse_instruction():
+ *     get a VCPU_REGS id from instruction bytes.
+ */
+int
+get_vcpu_regs_id(uint8_t insn, uint8_t rex, int op)
+{
+    int reg;
+    if (op == 0) {
+        /* get ModR/M's reg register */
+        reg = (((rex & 4) << 1) | (insn >> 3)) & 7;
+    } else {
+        /* get ModR/M's r/m resister */
+        reg = (((rex & 1) << 3) | insn) & 7;
+    }
+
+    switch (reg) {
+        case 0x0: return VCPU_REGS_RAX;
+        case 0x1: return VCPU_REGS_RCX;
+        case 0x2: return VCPU_REGS_RDX;
+        case 0x3: return VCPU_REGS_RBX;
+
+        case 0x4: return VCPU_REGS_RSP;
+        case 0x5: return VCPU_REGS_RBP;
+        case 0x6: return VCPU_REGS_RSI;
+        case 0x7: return VCPU_REGS_RDI;
+
+        case 0x8: return VCPU_REGS_R8;
+        case 0x9: return VCPU_REGS_R9;
+        case 0xa: return VCPU_REGS_R10;
+        case 0xb: return VCPU_REGS_R11;
+
+        case 0xc: return VCPU_REGS_R12;
+        case 0xd: return VCPU_REGS_R13;
+        case 0xe: return VCPU_REGS_R14;
+        case 0xf: return VCPU_REGS_R15;
+
+        default: return -1;
+    }
+}
+
+/*
+ * parse_instruction
+ *
+ * Finding out where the data to/from the iomem.
+ * This function assumes that one of the data destination or source would be
+ * in memory region. This assumption makes it very relaxed; it only has to
+ * find which register to store/read in many cases.
+ *
+ * Return values:
+ * 0: OK
+ * EPERM: Unsupported instruction
+ */
+int
+parse_instruction(struct vcpu *vcpu, uint8_t insn[], paddr_t iomem,
+        struct vmm_emul_instruction *info)
+{
+    int dir, rex = 0, skip, len = 0, displacement = 0;
+    uint8_t rep = 0;
+
+    /* Legacy prefixes */
+    switch (insn[0]) {
+        /* multi-length opcode. None of them are supported now */
+        case 0x0f:
+            skip = 1;
+            return (EPERM);
+        /* No MOV control register instruction for MMIO */
+        case 0x20:
+        case 0x22:
+            return (EPERM);
+        /* Lock prefix */
+        case 0xf0:
+            skip = 1;
+            break;
+        case 0x66:
+        case 0xf2:
+        case 0xf3:
+            /* mandatory prefix? If so, none of them are
+             * supported. */
+            if (insn[1] == 0x0f) {
+                return (EPERM);
+            } else { /* REP, REPNE, REPNZ, REPE, REPZ */
+                /* XXX String instruction can be MMIO.
+                 * Should be handled, but not yet. */
+                skip = 1;
+                rep = insn[0];
+            }
+        default:
+            skip = 0;
+    }
+
+    len = skip;
+    insn += skip;
+
+    /* REX prefix? */
+    if ((insn[0] >> 4) == 4 &&
+            vmm_get_guest_cpu_mode(vcpu) == VMM_CPU_MODE_LONG) {
+        rex = insn[0];
+        len ++;
+        insn ++;
+    }
+
+    len ++;
+    switch (insn[0]) {
+        case 0xf0:
+            len ++;
+            if (insn[1] == 0x38 || insn[1] == 0x3a)
+                len += 2;
+            else
+                len ++;
+            break;
+        /* a few of mov instructions go here */
+        case 0x88:
+        case 0x8a:
+            info->width = 8;
+        case 0x89:
+        case 0x8b:
+            /* use of ModRM, so increment */
+            len ++;
+            info->width = 32;
+            info->insn_type = VMM_EMUL_MOV;
+            /* direction indicated by opcode
+             * 0: r/m is the destination
+             * 1: r/m is the source
+             *
+             * XXX we must take care about SIB cases
+             */
+            dir = (insn[0] & 2) >> 1;
+            if (dir) {
+                       /* assume r/m = iomem = src */
+                info->src.gpa = iomem;
+                info->src.reg_id = -1;
+                info->dest.gpa = 0;
+                info->dest.reg_id = get_vcpu_regs_id(insn[1], rex, 0);
+            } else {
+                info->dest.gpa = iomem;
+                info->dest.reg_id = -1;
+                info->src.gpa = 0;
+                info->src.reg_id = get_vcpu_regs_id(insn[1], rex, 0);
+            }
+            switch (get_vcpu_regs_id(insn[1], rex, 1)) {
+                case VCPU_REGS_RSP: /* SIB */
+                    len++;
+                    break;
+                case VCPU_REGS_RBP: /* disp32 */
+                    displacement = 4;
+                    break;
+                default:
+                    if ((insn[1] >> 2) == 1)
+                        displacement = 1;
+                    else if ((insn[1] >> 2) == 2)
+                        displacement = 4;
+            }
+            break;
+            /* immediate can only be the source,
+             * and iomem must be the other */
+        default:
+            return (EPERM);
+    }
+    len += displacement;
+
+    info->len = len;
+    return 0;
+}
+
+/*
+ * emul_mmio
+ *
+ * Emulate the given instruction at insn_hva, which have accessed iomem.
+ *
+ * Return values:
+ * 0:      Handling finished
+ * EAGAIN: This device should be handled by vmd(8)
+ * ENODEV: Unknown device address
+ * EPERM:  Unsupported instruction or memory region
+ */
+int
+emul_mmio(struct vcpu *vcpu, paddr_t iomem, vaddr_t insn_hva,
+        struct vmm_emul_instruction * info)
+{
+    uint8_t *insn;
+    int parse;
+    uint64_t data;
+    /* for writing RSP to AMD CPU */
+    struct vmcb *vmcb = (struct vmcb *)vcpu->vc_control_va;
+
+    if (iomem < VMM_PCI_MMIO_BAR_BASE || iomem > VMM_PCI_MMIO_BAR_END)
+        return (EPERM);
+
+    /* endian aware; mask out unneeded data */
+    insn = (uint8_t *) insn_hva;
+
+    parse = parse_instruction(vcpu, insn, iomem, info);
+    if (parse) {
+        DPRINTF("%s: failted to parse the instruction "
+                "[%2x %2x %2x ...]: err=%04d\n",
+                __func__, insn[0], insn[1], insn[2], parse);
+        return (parse);
+    }
+
+    /* device emulation here */
+    if (iomem == VMM_PCI_MMIO_TPM_BASE + VMM_PCI_MMIO_TPM_CAP) {
+        /* no capable TPM; First bit zero means "disabled" */
+        if (info->src.gpa == iomem)
+            data = 0;
+    }
+
+    /* register is the destination */
+    if (info->dest.reg_id != -1) {
+        switch (info->dest.reg_id) {
+            case VCPU_REGS_RAX:
+                vcpu->vc_gueststate.vg_rax = data;
+                break;
+            case VCPU_REGS_RCX:
+                vcpu->vc_gueststate.vg_rcx = data;
+                break;
+            case VCPU_REGS_RDX:
+                vcpu->vc_gueststate.vg_rdx = data;
+                break;
+            case VCPU_REGS_RBX:
+                vcpu->vc_gueststate.vg_rbx = data;
+                break;
+
+            case VCPU_REGS_RSP:
+                if (vmm_softc->mode == VMM_MODE_VMX ||
+                        vmm_softc->mode == VMM_MODE_EPT)
+                    vmwrite(VMCS_GUEST_IA32_RSP, data);
+                else
+                    vmcb->v_rsp = data;
+                break;
+            case VCPU_REGS_RBP:
+                vcpu->vc_gueststate.vg_rbp = data;
+                break;
+            case VCPU_REGS_RSI:
+                vcpu->vc_gueststate.vg_rsi = data;
+                break;
+            case VCPU_REGS_RDI:
+                vcpu->vc_gueststate.vg_rdi = data;
+                break;
+
+            case VCPU_REGS_R8:
+                vcpu->vc_gueststate.vg_r8 = data;
+                break;
+            case VCPU_REGS_R9:
+                vcpu->vc_gueststate.vg_r9 = data;
+                break;
+            case VCPU_REGS_R10:
+                vcpu->vc_gueststate.vg_r10 = data;
+                break;
+            case VCPU_REGS_R11:
+                vcpu->vc_gueststate.vg_r11 = data;
+                break;
+
+            case VCPU_REGS_R12:
+                vcpu->vc_gueststate.vg_r12 = data;
+                break;
+            case VCPU_REGS_R13:
+                vcpu->vc_gueststate.vg_r13 = data;
+                break;
+            case VCPU_REGS_R14:
+                vcpu->vc_gueststate.vg_r14 = data;
+                break;
+            case VCPU_REGS_R15:
+                vcpu->vc_gueststate.vg_r15 = data;
+                break;
+        }
+    }
+
+#ifdef VMM_DEBUG
+    DPRINTF("%s: parsed data: [%02x %02x %02x %02x...] "
+            "len=(%d), insn_type=(%d), width=(%d), "
+            "zero_or_sign_extend=(%d) "
+            "src={gpa=0x%08lx, reg=0x%02x}, "
+            "dst={gpa=0x%08lx, reg=%02x}\n",
+            __func__,
+            insn[0], insn[1], insn[2], insn[3],
+            info->len, info->insn_type,
+            info->width, info->zero_sign,
+            info->src.gpa, info->src.reg_id,
+            info->dest.gpa, info->dest.reg_id);
+#endif /* VMM_DEBUG */
+
+    return 0;
+}
+
+/*
  * svm_handle_inout
  *
  * Exit handler for IN/OUT instructions.

Reply via email to