/** * Enables the Hyper-V Hypercall page. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param GCPhysHypercallPage Where to map the hypercall page. */ VMMR3_INT_DECL(int) gimR3HvEnableHypercallPage(PVM pVM, RTGCPHYS GCPhysHypercallPage) { PPDMDEVINSR3 pDevIns = pVM->gim.s.pDevInsR3; PGIMMMIO2REGION pRegion = &pVM->gim.s.u.Hv.aMmio2Regions[GIM_HV_HYPERCALL_PAGE_REGION_IDX]; AssertPtrReturn(pDevIns, VERR_GIM_DEVICE_NOT_REGISTERED); if (pRegion->fMapped) { /* * Is it already enabled at the given guest-address? */ if (pRegion->GCPhysPage == GCPhysHypercallPage) return VINF_SUCCESS; /* * If it's mapped at a different address, unmap the previous address. */ int rc2 = gimR3HvDisableHypercallPage(pVM); AssertRC(rc2); } /* * Map the hypercall-page at the specified address. */ Assert(!pRegion->fMapped); int rc = GIMR3Mmio2Map(pVM, pRegion, GCPhysHypercallPage); if (RT_SUCCESS(rc)) { Assert(pRegion->GCPhysPage == GCPhysHypercallPage); /* * Patch the hypercall-page. */ size_t cbWritten = 0; rc = VMMPatchHypercall(pVM, pRegion->pvPageR3, PAGE_SIZE, &cbWritten); if ( RT_SUCCESS(rc) && cbWritten < PAGE_SIZE) { uint8_t *pbLast = (uint8_t *)pRegion->pvPageR3 + cbWritten; *pbLast = 0xc3; /* RET */ /* * Notify VMM that hypercalls are now enabled for all VCPUs. */ for (VMCPUID i = 0; i < pVM->cCpus; i++) VMMHypercallsEnable(&pVM->aCpus[i]); LogRel(("GIM: HyperV: Enabled hypercalls at %#RGp\n", GCPhysHypercallPage)); return VINF_SUCCESS; } else { if (rc == VINF_SUCCESS) rc = VERR_GIM_OPERATION_FAILED; LogRel(("GIM: HyperV: VMMPatchHypercall failed. rc=%Rrc cbWritten=%u\n", rc, cbWritten)); } GIMR3Mmio2Unmap(pVM, pRegion); } LogRel(("GIM: HyperV: GIMR3Mmio2Map failed. rc=%Rrc\n", rc)); return rc; }
/** * 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; }