/** * Patches the instructions necessary for making a hypercall to the hypervisor. * Used by GIM. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param pvBuf The buffer in the hypercall page(s) to be patched. * @param cbBuf The size of the buffer. * @param pcbWritten Where to store the number of bytes patched. This * is reliably updated only when this function returns * VINF_SUCCESS. */ VMM_INT_DECL(int) VMMPatchHypercall(PVM pVM, void *pvBuf, size_t cbBuf, size_t *pcbWritten) { AssertReturn(pvBuf, VERR_INVALID_POINTER); AssertReturn(pcbWritten, VERR_INVALID_POINTER); if (ASMIsAmdCpu()) { uint8_t abHypercall[] = { 0x0F, 0x01, 0xD9 }; /* VMMCALL */ if (RT_LIKELY(cbBuf >= sizeof(abHypercall))) { memcpy(pvBuf, abHypercall, sizeof(abHypercall)); *pcbWritten = sizeof(abHypercall); return VINF_SUCCESS; } return VERR_BUFFER_OVERFLOW; } else { AssertReturn(ASMIsIntelCpu() || ASMIsViaCentaurCpu(), VERR_UNSUPPORTED_CPU); uint8_t abHypercall[] = { 0x0F, 0x01, 0xC1 }; /* VMCALL */ if (RT_LIKELY(cbBuf >= sizeof(abHypercall))) { memcpy(pvBuf, abHypercall, sizeof(abHypercall)); *pcbWritten = sizeof(abHypercall); return VINF_SUCCESS; } return VERR_BUFFER_OVERFLOW; } }
/** * Initializes the KVM GIM provider. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param uVersion The interface version this VM should use. */ VMMR3_INT_DECL(int) gimR3KvmInit(PVM pVM) { AssertReturn(pVM, VERR_INVALID_PARAMETER); AssertReturn(pVM->gim.s.enmProviderId == GIMPROVIDERID_KVM, VERR_INTERNAL_ERROR_5); int rc; PGIMKVM pKvm = &pVM->gim.s.u.Kvm; /* * Determine interface capabilities based on the version. */ if (!pVM->gim.s.u32Version) { /* Basic features. */ pKvm->uBaseFeat = 0 | GIM_KVM_BASE_FEAT_CLOCK_OLD //| GIM_KVM_BASE_FEAT_NOP_IO_DELAY //| GIM_KVM_BASE_FEAT_MMU_OP | GIM_KVM_BASE_FEAT_CLOCK //| GIM_KVM_BASE_FEAT_ASYNC_PF //| GIM_KVM_BASE_FEAT_STEAL_TIME //| GIM_KVM_BASE_FEAT_PV_EOI | GIM_KVM_BASE_FEAT_PV_UNHALT ; /* Rest of the features are determined in gimR3KvmInitCompleted(). */ } /* * Expose HVP (Hypervisor Present) bit to the guest. */ CPUMSetGuestCpuIdFeature(pVM, CPUMCPUIDFEATURE_HVP); /* * Modify the standard hypervisor leaves for KVM. */ CPUMCPUIDLEAF HyperLeaf; RT_ZERO(HyperLeaf); HyperLeaf.uLeaf = UINT32_C(0x40000000); HyperLeaf.uEax = UINT32_C(0x40000001); /* Minimum value for KVM is 0x40000001. */ HyperLeaf.uEbx = 0x4B4D564B; /* 'KVMK' */ HyperLeaf.uEcx = 0x564B4D56; /* 'VMKV' */ HyperLeaf.uEdx = 0x0000004D; /* 'M000' */ rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf); AssertLogRelRCReturn(rc, rc); /* * Add KVM specific leaves. */ HyperLeaf.uLeaf = UINT32_C(0x40000001); HyperLeaf.uEax = pKvm->uBaseFeat; HyperLeaf.uEbx = 0; /* Reserved */ HyperLeaf.uEcx = 0; /* Reserved */ HyperLeaf.uEdx = 0; /* Reserved */ rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf); AssertLogRelRCReturn(rc, rc); /* * Insert all MSR ranges of KVM. */ for (unsigned i = 0; i < RT_ELEMENTS(g_aMsrRanges_Kvm); i++) { rc = CPUMR3MsrRangesInsert(pVM, &g_aMsrRanges_Kvm[i]); AssertLogRelRCReturn(rc, rc); } /* * Setup hypercall and #UD handling. */ for (VMCPUID i = 0; i < pVM->cCpus; i++) VMMHypercallsEnable(&pVM->aCpus[i]); if (ASMIsAmdCpu()) { pKvm->fTrapXcptUD = true; pKvm->uOpCodeNative = OP_VMMCALL; } else { Assert(ASMIsIntelCpu() || ASMIsViaCentaurCpu()); pKvm->fTrapXcptUD = false; pKvm->uOpCodeNative = OP_VMCALL; } /* We always need to trap VMCALL/VMMCALL hypercall using #UDs for raw-mode VMs. */ if (!HMIsEnabled(pVM)) pKvm->fTrapXcptUD = true; return VINF_SUCCESS; }