/** * Enables the Hyper-V TSC page. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param GCPhysTscPage Where to map the TSC page. * @param fUseThisTscSeq Whether to set the TSC sequence number to the one * specified in @a uTscSeq. * @param uTscSeq The TSC sequence value to use. Ignored if * @a fUseThisTscSeq is false. */ VMMR3_INT_DECL(int) gimR3HvEnableTscPage(PVM pVM, RTGCPHYS GCPhysTscPage, bool fUseThisTscSeq, uint32_t uTscSeq) { PPDMDEVINSR3 pDevIns = pVM->gim.s.pDevInsR3; PGIMMMIO2REGION pRegion = &pVM->gim.s.u.Hv.aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX]; AssertPtrReturn(pDevIns, VERR_GIM_DEVICE_NOT_REGISTERED); int rc; if (pRegion->fMapped) { /* * Is it already enabled at the given guest-address? */ if (pRegion->GCPhysPage == GCPhysTscPage) return VINF_SUCCESS; /* * If it's mapped at a different address, unmap the previous address. */ rc = gimR3HvDisableTscPage(pVM); AssertRC(rc); } /* * Map the TSC-page at the specified address. */ Assert(!pRegion->fMapped); rc = GIMR3Mmio2Map(pVM, pRegion, GCPhysTscPage); if (RT_SUCCESS(rc)) { Assert(pRegion->GCPhysPage == GCPhysTscPage); /* * Update the TSC scale. Windows guests expect a non-zero TSC sequence, otherwise * they fallback to using the reference count MSR which is not ideal in terms of VM-exits. * * Also, Hyper-V normalizes the time in 10 MHz, see: * http://technet.microsoft.com/it-it/sysinternals/dn553408%28v=vs.110%29 */ PGIMHVREFTSC pRefTsc = (PGIMHVREFTSC)pRegion->pvPageR3; Assert(pRefTsc); uint64_t const u64TscKHz = TMCpuTicksPerSecond(pVM) / UINT64_C(1000); uint32_t u32TscSeq = 1; if ( fUseThisTscSeq && uTscSeq < UINT32_C(0xfffffffe)) u32TscSeq = uTscSeq + 1; pRefTsc->u32TscSequence = u32TscSeq; pRefTsc->u64TscScale = ((INT64_C(10000) << 32) / u64TscKHz) << 32; pRefTsc->i64TscOffset = 0; LogRel(("GIM: HyperV: Enabled TSC page at %#RGp - u64TscScale=%#RX64 u64TscKHz=%#RX64 (%'RU64) Seq=%#RU32\n", GCPhysTscPage, pRefTsc->u64TscScale, u64TscKHz, u64TscKHz, pRefTsc->u32TscSequence)); TMR3CpuTickParavirtEnable(pVM); return VINF_SUCCESS; } else LogRelFunc(("GIMR3Mmio2Map failed. rc=%Rrc\n", rc)); return VERR_GIM_OPERATION_FAILED; }
/** * MSR write handler for Hyper-V. * * @returns Strict VBox status code like CPUMSetGuestMsr(). * @retval VINF_CPUM_R3_MSR_WRITE * @retval VERR_CPUM_RAISE_GP_0 * * @param pVCpu Pointer to the VMCPU. * @param idMsr The MSR being written. * @param pRange The range this MSR belongs to. * @param uRawValue The raw value with the ignored bits not masked. */ VMM_INT_DECL(VBOXSTRICTRC) gimHvWriteMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t uRawValue) { NOREF(pRange); PVM pVM = pVCpu->CTX_SUFF(pVM); PGIMHV pHv = &pVM->gim.s.u.Hv; switch (idMsr) { case MSR_GIM_HV_TPR: PDMApicWriteMSR(pVM, pVCpu->idCpu, 0x80, uRawValue); return VINF_SUCCESS; case MSR_GIM_HV_EOI: PDMApicWriteMSR(pVM, pVCpu->idCpu, 0x0B, uRawValue); return VINF_SUCCESS; case MSR_GIM_HV_ICR: PDMApicWriteMSR(pVM, pVCpu->idCpu, 0x30, uRawValue); return VINF_SUCCESS; case MSR_GIM_HV_GUEST_OS_ID: { #ifndef IN_RING3 return VINF_CPUM_R3_MSR_WRITE; #else /* Disable the hypercall-page if 0 is written to this MSR. */ if (!uRawValue) { gimR3HvDisableHypercallPage(pVM); pHv->u64HypercallMsr &= ~MSR_GIM_HV_HYPERCALL_ENABLE_BIT; } else { LogRel(("GIM: HyperV: Guest OS reported ID %#RX64\n", uRawValue)); LogRel(("GIM: HyperV: Open-source=%RTbool Vendor=%#x OS=%#x (%s) Major=%u Minor=%u ServicePack=%u Build=%u\n", MSR_GIM_HV_GUEST_OS_ID_IS_OPENSOURCE(uRawValue), MSR_GIM_HV_GUEST_OS_ID_VENDOR(uRawValue), MSR_GIM_HV_GUEST_OS_ID_OS_VARIANT(uRawValue), gimHvGetGuestOsIdVariantName(uRawValue), MSR_GIM_HV_GUEST_OS_ID_MAJOR_VERSION(uRawValue), MSR_GIM_HV_GUEST_OS_ID_MINOR_VERSION(uRawValue), MSR_GIM_HV_GUEST_OS_ID_SERVICE_VERSION(uRawValue), MSR_GIM_HV_GUEST_OS_ID_BUILD(uRawValue))); } pHv->u64GuestOsIdMsr = uRawValue; return VINF_SUCCESS; #endif /* IN_RING3 */ } case MSR_GIM_HV_HYPERCALL: { #ifndef IN_RING3 return VINF_CPUM_R3_MSR_WRITE; #else /* IN_RING3 */ /* * For now ignore writes to the hypercall MSR (i.e. keeps it disabled). * This is required to boot FreeBSD 10.1 (with Hyper-V enabled ofc), * see @bugref{7270#c116}. */ return VINF_SUCCESS; # if 0 /* First, update all but the hypercall enable bit. */ pHv->u64HypercallMsr = (uRawValue & ~MSR_GIM_HV_HYPERCALL_ENABLE_BIT); /* Hypercalls can only be enabled when the guest has set the Guest-OS Id Msr. */ bool fEnable = RT_BOOL(uRawValue & MSR_GIM_HV_HYPERCALL_ENABLE_BIT); if ( fEnable && !pHv->u64GuestOsIdMsr) { return VINF_SUCCESS; } /* Is the guest disabling the hypercall-page? Allow it regardless of the Guest-OS Id Msr. */ if (!fEnable) { gimR3HvDisableHypercallPage(pVM); pHv->u64HypercallMsr = uRawValue; return VINF_SUCCESS; } /* Enable the hypercall-page. */ RTGCPHYS GCPhysHypercallPage = MSR_GIM_HV_HYPERCALL_GUEST_PFN(uRawValue) << PAGE_SHIFT; int rc = gimR3HvEnableHypercallPage(pVM, GCPhysHypercallPage); if (RT_SUCCESS(rc)) { pHv->u64HypercallMsr = uRawValue; return VINF_SUCCESS; } return VERR_CPUM_RAISE_GP_0; # endif #endif /* IN_RING3 */ } case MSR_GIM_HV_REF_TSC: { #ifndef IN_RING3 return VINF_CPUM_R3_MSR_WRITE; #else /* IN_RING3 */ /* First, update all but the TSC-page enable bit. */ pHv->u64TscPageMsr = (uRawValue & ~MSR_GIM_HV_REF_TSC_ENABLE_BIT); /* Is the guest disabling the TSC-page? */ bool fEnable = RT_BOOL(uRawValue & MSR_GIM_HV_REF_TSC_ENABLE_BIT); if (!fEnable) { gimR3HvDisableTscPage(pVM); pHv->u64TscPageMsr = uRawValue; return VINF_SUCCESS; } /* Enable the TSC-page. */ RTGCPHYS GCPhysTscPage = MSR_GIM_HV_REF_TSC_GUEST_PFN(uRawValue) << PAGE_SHIFT; int rc = gimR3HvEnableTscPage(pVM, GCPhysTscPage, false /* fUseThisTscSequence */, 0 /* uTscSequence */); if (RT_SUCCESS(rc)) { pHv->u64TscPageMsr = uRawValue; return VINF_SUCCESS; } return VERR_CPUM_RAISE_GP_0; #endif /* IN_RING3 */ } case MSR_GIM_HV_RESET: { #ifndef IN_RING3 return VINF_CPUM_R3_MSR_WRITE; #else if (MSR_GIM_HV_RESET_IS_SET(uRawValue)) { LogRel(("GIM: HyperV: Reset initiated through MSR\n")); int rc = PDMDevHlpVMReset(pVM->gim.s.pDevInsR3); AssertRC(rc); } /* else: Ignore writes to other bits. */ return VINF_SUCCESS; #endif /* IN_RING3 */ } case MSR_GIM_HV_CRASH_CTL: { #ifndef IN_RING3 return VINF_CPUM_R3_MSR_WRITE; #else if (uRawValue & MSR_GIM_HV_CRASH_CTL_NOTIFY_BIT) { LogRel(("GIM: HyperV: Guest indicates a fatal condition! P0=%#RX64 P1=%#RX64 P2=%#RX64 P3=%#RX64 P4=%#RX64\n", pHv->uCrashP0, pHv->uCrashP1, pHv->uCrashP2, pHv->uCrashP3, pHv->uCrashP4)); } return VINF_SUCCESS; #endif } case MSR_GIM_HV_CRASH_P0: pHv->uCrashP0 = uRawValue; return VINF_SUCCESS; case MSR_GIM_HV_CRASH_P1: pHv->uCrashP1 = uRawValue; return VINF_SUCCESS; case MSR_GIM_HV_CRASH_P2: pHv->uCrashP2 = uRawValue; return VINF_SUCCESS; case MSR_GIM_HV_CRASH_P3: pHv->uCrashP3 = uRawValue; return VINF_SUCCESS; case MSR_GIM_HV_CRASH_P4: pHv->uCrashP4 = uRawValue; return VINF_SUCCESS; case MSR_GIM_HV_TIME_REF_COUNT: /* Read-only MSRs. */ case MSR_GIM_HV_VP_INDEX: case MSR_GIM_HV_TSC_FREQ: case MSR_GIM_HV_APIC_FREQ: LogFunc(("WrMsr on read-only MSR %#RX32 -> #GP(0)\n", idMsr)); return VERR_CPUM_RAISE_GP_0; default: { #ifdef IN_RING3 static uint32_t s_cTimes = 0; if (s_cTimes++ < 20) LogRel(("GIM: HyperV: Unknown/invalid WrMsr (%#x,%#x`%08x) -> #GP(0)\n", idMsr, uRawValue & UINT64_C(0xffffffff00000000), uRawValue & UINT64_C(0xffffffff))); #endif LogFunc(("Unknown/invalid WrMsr (%#RX32,%#RX64) -> #GP(0)\n", idMsr, uRawValue)); break; } } return VERR_CPUM_RAISE_GP_0; }