/** * KVM state-load operation, final pass. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param pSSM Pointer to the SSM handle. * @param uSSMVersion The GIM saved-state version. */ VMMR3_INT_DECL(int) gimR3KvmLoad(PVM pVM, PSSMHANDLE pSSM, uint32_t uSSMVersion) { /* * Load the KVM SSM version first. */ uint32_t uKvmSavedStatVersion; int rc = SSMR3GetU32(pSSM, &uKvmSavedStatVersion); AssertRCReturn(rc, rc); if (uKvmSavedStatVersion != GIM_KVM_SAVED_STATE_VERSION) return SSMR3SetLoadError(pSSM, VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION, RT_SRC_POS, N_("Unsupported KVM saved-state version %u (expected %u)."), uKvmSavedStatVersion, GIM_KVM_SAVED_STATE_VERSION); /* * Update the TSC frequency from TM. */ PGIMKVM pKvm = &pVM->gim.s.u.Kvm; pKvm->cTscTicksPerSecond = TMCpuTicksPerSecond(pVM); /* * Load per-VCPU data. */ for (uint32_t i = 0; i < pVM->cCpus; i++) { PVMCPU pVCpu = &pVM->aCpus[i]; PGIMKVMCPU pKvmCpu = &pVCpu->gim.s.u.KvmCpu; uint8_t fSystemTimeFlags = 0; SSMR3GetU64(pSSM, &pKvmCpu->u64SystemTimeMsr); SSMR3GetU64(pSSM, &pKvmCpu->uTsc); SSMR3GetU64(pSSM, &pKvmCpu->uVirtNanoTS); SSMR3GetGCPhys(pSSM, &pKvmCpu->GCPhysSystemTime); SSMR3GetU32(pSSM, &pKvmCpu->u32SystemTimeVersion); rc = SSMR3GetU8(pSSM, &pKvmCpu->fSystemTimeFlags); AssertRCReturn(rc, rc); /* Enable the system-time struct. if necessary. */ /** @todo update guest struct only if cTscTicksPerSecond doesn't match host * anymore. */ if (MSR_GIM_KVM_SYSTEM_TIME_IS_ENABLED(pKvmCpu->u64SystemTimeMsr)) { Assert(!TMVirtualIsTicking(pVM)); /* paranoia. */ Assert(!TMCpuTickIsTicking(pVCpu)); rc = gimR3KvmEnableSystemTime(pVM, pVCpu); AssertRCReturn(rc, rc); } } /* * Load per-VM data. */ SSMR3GetU64(pSSM, &pKvm->u64WallClockMsr); rc = SSMR3GetU32(pSSM, &pKvm->uBaseFeat); AssertRCReturn(rc, rc); return VINF_SUCCESS; }
/** * Returns whether the guest has configured and enabled the use of KVM's * paravirtualized TSC. * * @returns true if paravirt. TSC is enabled, false otherwise. * @param pVM Pointer to the VM. */ VMM_INT_DECL(bool) gimKvmIsParavirtTscEnabled(PVM pVM) { uint32_t cCpus = pVM->cCpus; for (uint32_t i = 0; i < cCpus; i++) { PVMCPU pVCpu = &pVM->aCpus[i]; PGIMKVMCPU pGimKvmCpu = &pVCpu->gim.s.u.KvmCpu; if (MSR_GIM_KVM_SYSTEM_TIME_IS_ENABLED(pGimKvmCpu->u64SystemTimeMsr)) return true; } return false; }
/** * KVM state-save operation. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param pSSM Pointer to the SSM handle. */ VMMR3_INT_DECL(int) gimR3KvmSave(PVM pVM, PSSMHANDLE pSSM) { PCGIMKVM pcKvm = &pVM->gim.s.u.Kvm; /* * Save the KVM SSM version. */ SSMR3PutU32(pSSM, GIM_KVM_SAVED_STATE_VERSION); /* * Save per-VCPU data. */ for (uint32_t i = 0; i < pVM->cCpus; i++) { PCGIMKVMCPU pcKvmCpu = &pVM->aCpus[i].gim.s.u.KvmCpu; /* Guest may alter flags (namely GIM_KVM_SYSTEM_TIME_FLAGS_GUEST_PAUSED bit). So re-read them from guest-memory. */ GIMKVMSYSTEMTIME SystemTime; RT_ZERO(SystemTime); if (MSR_GIM_KVM_SYSTEM_TIME_IS_ENABLED(pcKvmCpu->u64SystemTimeMsr)) { int rc = PGMPhysSimpleReadGCPhys(pVM, &SystemTime, pcKvmCpu->GCPhysSystemTime, sizeof(GIMKVMSYSTEMTIME)); AssertRCReturn(rc, rc); } SSMR3PutU64(pSSM, pcKvmCpu->u64SystemTimeMsr); SSMR3PutU64(pSSM, pcKvmCpu->uTsc); SSMR3PutU64(pSSM, pcKvmCpu->uVirtNanoTS); SSMR3PutGCPhys(pSSM, pcKvmCpu->GCPhysSystemTime); SSMR3PutU32(pSSM, pcKvmCpu->u32SystemTimeVersion); SSMR3PutU8(pSSM, SystemTime.fFlags); } /* * Save per-VM data. */ SSMR3PutU64(pSSM, pcKvm->u64WallClockMsr); return SSMR3PutU32(pSSM, pcKvm->uBaseFeat); }
/** * MSR write handler for KVM. * * @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) gimKvmWriteMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t uRawValue) { NOREF(pRange); PVM pVM = pVCpu->CTX_SUFF(pVM); PGIMKVM pKvm = &pVM->gim.s.u.Kvm; PGIMKVMCPU pKvmCpu = &pVCpu->gim.s.u.KvmCpu; switch (idMsr) { case MSR_GIM_KVM_SYSTEM_TIME: case MSR_GIM_KVM_SYSTEM_TIME_OLD: { bool fEnable = RT_BOOL(uRawValue & MSR_GIM_KVM_SYSTEM_TIME_ENABLE_BIT); #ifdef IN_RING0 gimR0KvmUpdateSystemTime(pVM, pVCpu); return VINF_CPUM_R3_MSR_WRITE; #elif defined(IN_RC) Assert(pVM->cCpus == 1); if (fEnable) { RTCCUINTREG fEFlags = ASMIntDisableFlags(); pKvmCpu->uTsc = TMCpuTickGetNoCheck(pVCpu) | UINT64_C(1); pKvmCpu->uVirtNanoTS = TMVirtualGetNoCheck(pVM) | UINT64_C(1); ASMSetFlags(fEFlags); } return VINF_CPUM_R3_MSR_WRITE; #else /* IN_RING3 */ if (!fEnable) { gimR3KvmDisableSystemTime(pVM); pKvmCpu->u64SystemTimeMsr = uRawValue; return VINF_SUCCESS; } /* Is the system-time struct. already enabled? If so, get flags that need preserving. */ uint8_t fFlags = 0; GIMKVMSYSTEMTIME SystemTime; RT_ZERO(SystemTime); if ( MSR_GIM_KVM_SYSTEM_TIME_IS_ENABLED(pKvmCpu->u64SystemTimeMsr) && MSR_GIM_KVM_SYSTEM_TIME_GUEST_GPA(uRawValue) == pKvmCpu->GCPhysSystemTime) { int rc2 = PGMPhysSimpleReadGCPhys(pVM, &SystemTime, pKvmCpu->GCPhysSystemTime, sizeof(GIMKVMSYSTEMTIME)); if (RT_SUCCESS(rc2)) pKvmCpu->fSystemTimeFlags = (SystemTime.fFlags & GIM_KVM_SYSTEM_TIME_FLAGS_GUEST_PAUSED); } /* Enable and populate the system-time struct. */ pKvmCpu->u64SystemTimeMsr = uRawValue; pKvmCpu->GCPhysSystemTime = MSR_GIM_KVM_SYSTEM_TIME_GUEST_GPA(uRawValue); pKvmCpu->u32SystemTimeVersion += 2; int rc = gimR3KvmEnableSystemTime(pVM, pVCpu); if (RT_FAILURE(rc)) { pKvmCpu->u64SystemTimeMsr = 0; return VERR_CPUM_RAISE_GP_0; } return VINF_SUCCESS; #endif } case MSR_GIM_KVM_WALL_CLOCK: case MSR_GIM_KVM_WALL_CLOCK_OLD: { #ifndef IN_RING3 return VINF_CPUM_R3_MSR_WRITE; #else /* Enable the wall-clock struct. */ RTGCPHYS GCPhysWallClock = MSR_GIM_KVM_WALL_CLOCK_GUEST_GPA(uRawValue); if (RT_LIKELY(RT_ALIGN_64(GCPhysWallClock, 4) == GCPhysWallClock)) { int rc = gimR3KvmEnableWallClock(pVM, GCPhysWallClock); if (RT_SUCCESS(rc)) { pKvm->u64WallClockMsr = uRawValue; return VINF_SUCCESS; } } return VERR_CPUM_RAISE_GP_0; #endif /* IN_RING3 */ } default: { #ifdef IN_RING3 static uint32_t s_cTimes = 0; if (s_cTimes++ < 20) LogRel(("GIM: KVM: 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; }