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.