/** * Initializes the Minimal provider. * * @returns VBox status code. * @param pVM Pointer to the VM. */ VMMR3_INT_DECL(int) GIMR3MinimalInit(PVM pVM) { AssertReturn(pVM, VERR_INVALID_PARAMETER); AssertReturn(pVM->gim.s.enmProviderId == GIMPROVIDERID_MINIMAL, VERR_INTERNAL_ERROR_5); /* * Enable the Hypervisor Present. */ CPUMSetGuestCpuIdFeature(pVM, CPUMCPUIDFEATURE_HVP); return VINF_SUCCESS; }
/** @interface_method_impl{PDMAPICHLPR0,pfnChangeFeature} */ static DECLCALLBACK(void) pdmR0ApicHlp_ChangeFeature(PPDMDEVINS pDevIns, PDMAPICVERSION enmVersion) { PDMDEV_ASSERT_DEVINS(pDevIns); LogFlow(("pdmR0ApicHlp_ChangeFeature: caller=%p/%d: version=%d\n", pDevIns, pDevIns->iInstance, (int)enmVersion)); switch (enmVersion) { case PDMAPICVERSION_NONE: CPUMClearGuestCpuIdFeature(pDevIns->Internal.s.pVMR0, CPUMCPUIDFEATURE_APIC); CPUMClearGuestCpuIdFeature(pDevIns->Internal.s.pVMR0, CPUMCPUIDFEATURE_X2APIC); break; case PDMAPICVERSION_APIC: CPUMSetGuestCpuIdFeature(pDevIns->Internal.s.pVMR0, CPUMCPUIDFEATURE_APIC); CPUMClearGuestCpuIdFeature(pDevIns->Internal.s.pVMR0, CPUMCPUIDFEATURE_X2APIC); break; case PDMAPICVERSION_X2APIC: CPUMSetGuestCpuIdFeature(pDevIns->Internal.s.pVMR0, CPUMCPUIDFEATURE_X2APIC); CPUMSetGuestCpuIdFeature(pDevIns->Internal.s.pVMR0, CPUMCPUIDFEATURE_APIC); break; default: AssertMsgFailed(("Unknown APIC version: %d\n", (int)enmVersion)); } }
/** * Initializes the Hyper-V 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) gimR3HvInit(PVM pVM) { AssertReturn(pVM, VERR_INVALID_PARAMETER); AssertReturn(pVM->gim.s.enmProviderId == GIMPROVIDERID_HYPERV, VERR_INTERNAL_ERROR_5); int rc; PGIMHV pHv = &pVM->gim.s.u.Hv; /* * Determine interface capabilities based on the version. */ if (!pVM->gim.s.u32Version) { /* Basic features. */ pHv->uBaseFeat = 0 //| GIM_HV_BASE_FEAT_VP_RUNTIME_MSR | GIM_HV_BASE_FEAT_PART_TIME_REF_COUNT_MSR //| GIM_HV_BASE_FEAT_BASIC_SYNTH_IC //| GIM_HV_BASE_FEAT_SYNTH_TIMER_MSRS | GIM_HV_BASE_FEAT_APIC_ACCESS_MSRS | GIM_HV_BASE_FEAT_HYPERCALL_MSRS | GIM_HV_BASE_FEAT_VP_ID_MSR | GIM_HV_BASE_FEAT_VIRT_SYS_RESET_MSR //| GIM_HV_BASE_FEAT_STAT_PAGES_MSR | GIM_HV_BASE_FEAT_PART_REF_TSC_MSR //| GIM_HV_BASE_FEAT_GUEST_IDLE_STATE_MSR | GIM_HV_BASE_FEAT_TIMER_FREQ_MSRS //| GIM_HV_BASE_FEAT_DEBUG_MSRS ; /* Miscellaneous features. */ pHv->uMiscFeat = GIM_HV_MISC_FEAT_TIMER_FREQ; /* Hypervisor recommendations to the guest. */ pHv->uHyperHints = GIM_HV_HINT_MSR_FOR_SYS_RESET | GIM_HV_HINT_RELAX_TIME_CHECKS; } /* * Populate the required fields in MMIO2 region records for registering. */ AssertCompile(GIM_HV_PAGE_SIZE == PAGE_SIZE); PGIMMMIO2REGION pRegion = &pHv->aMmio2Regions[GIM_HV_HYPERCALL_PAGE_REGION_IDX]; pRegion->iRegion = GIM_HV_HYPERCALL_PAGE_REGION_IDX; pRegion->fRCMapping = false; pRegion->cbRegion = PAGE_SIZE; pRegion->GCPhysPage = NIL_RTGCPHYS; RTStrCopy(pRegion->szDescription, sizeof(pRegion->szDescription), "Hyper-V hypercall page"); pRegion = &pHv->aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX]; pRegion->iRegion = GIM_HV_REF_TSC_PAGE_REGION_IDX; pRegion->fRCMapping = false; pRegion->cbRegion = PAGE_SIZE; pRegion->GCPhysPage = NIL_RTGCPHYS; RTStrCopy(pRegion->szDescription, sizeof(pRegion->szDescription), "Hyper-V TSC page"); /* * Make sure the CPU ID bit are in accordance to the Hyper-V * requirement and other paranoia checks. * See "Requirements for implementing the Microsoft hypervisor interface" spec. */ Assert(!(pHv->uPartFlags & ( GIM_HV_PART_FLAGS_CREATE_PART | GIM_HV_PART_FLAGS_ACCESS_MEMORY_POOL | GIM_HV_PART_FLAGS_ACCESS_PART_ID | GIM_HV_PART_FLAGS_ADJUST_MSG_BUFFERS | GIM_HV_PART_FLAGS_CREATE_PORT | GIM_HV_PART_FLAGS_ACCESS_STATS | GIM_HV_PART_FLAGS_CPU_MGMT | GIM_HV_PART_FLAGS_CPU_PROFILER))); Assert((pHv->uBaseFeat & (GIM_HV_BASE_FEAT_HYPERCALL_MSRS | GIM_HV_BASE_FEAT_VP_ID_MSR)) == (GIM_HV_BASE_FEAT_HYPERCALL_MSRS | GIM_HV_BASE_FEAT_VP_ID_MSR)); for (unsigned i = 0; i < RT_ELEMENTS(pHv->aMmio2Regions); i++) { PCGIMMMIO2REGION pcCur = &pHv->aMmio2Regions[i]; Assert(!pcCur->fRCMapping); Assert(!pcCur->fMapped); Assert(pcCur->GCPhysPage == NIL_RTGCPHYS); } /* * Expose HVP (Hypervisor Present) bit to the guest. */ CPUMSetGuestCpuIdFeature(pVM, CPUMCPUIDFEATURE_HVP); /* * Modify the standard hypervisor leaves for Hyper-V. */ CPUMCPUIDLEAF HyperLeaf; RT_ZERO(HyperLeaf); HyperLeaf.uLeaf = UINT32_C(0x40000000); HyperLeaf.uEax = UINT32_C(0x40000006); /* Minimum value for Hyper-V is 0x40000005. */ HyperLeaf.uEbx = 0x7263694D; /* 'Micr' */ HyperLeaf.uEcx = 0x666F736F; /* 'osof' */ HyperLeaf.uEdx = 0x76482074; /* 't Hv' */ rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf); AssertLogRelRCReturn(rc, rc); HyperLeaf.uLeaf = UINT32_C(0x40000001); HyperLeaf.uEax = 0x31237648; /* 'Hv#1' */ HyperLeaf.uEbx = 0; /* Reserved */ HyperLeaf.uEcx = 0; /* Reserved */ HyperLeaf.uEdx = 0; /* Reserved */ rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf); AssertLogRelRCReturn(rc, rc); /* * Add Hyper-V specific leaves. */ HyperLeaf.uLeaf = UINT32_C(0x40000002); /* MBZ until MSR_GIM_HV_GUEST_OS_ID is set by the guest. */ HyperLeaf.uEax = 0; HyperLeaf.uEbx = 0; HyperLeaf.uEcx = 0; HyperLeaf.uEdx = 0; rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf); AssertLogRelRCReturn(rc, rc); HyperLeaf.uLeaf = UINT32_C(0x40000003); HyperLeaf.uEax = pHv->uBaseFeat; HyperLeaf.uEbx = pHv->uPartFlags; HyperLeaf.uEcx = pHv->uPowMgmtFeat; HyperLeaf.uEdx = pHv->uMiscFeat; rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf); AssertLogRelRCReturn(rc, rc); HyperLeaf.uLeaf = UINT32_C(0x40000004); HyperLeaf.uEax = pHv->uHyperHints; HyperLeaf.uEbx = 0xffffffff; HyperLeaf.uEcx = 0; HyperLeaf.uEdx = 0; rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf); AssertLogRelRCReturn(rc, rc); /* * Insert all MSR ranges of Hyper-V. */ for (unsigned i = 0; i < RT_ELEMENTS(g_aMsrRanges_HyperV); i++) { rc = CPUMR3MsrRangesInsert(pVM, &g_aMsrRanges_HyperV[i]); AssertLogRelRCReturn(rc, rc); } return VINF_SUCCESS; }
/** * 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; }