Changeset 86121 in vbox for trunk/src/VBox/VMM/VMMR3
- Timestamp:
- Sep 14, 2020 4:56:09 PM (5 years ago)
- svn:sync-xref-src-repo-rev:
- 140355
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/VMM/VMMR3/GIMKvm.cpp
r84139 r86121 86 86 87 87 /** 88 * Updates the KVM VCPU system-time structure in guest memory. 89 * 90 * @returns VBox status code. 91 * @param pVM The cross context VM structure. 92 * @param pVCpu The cross context virtual CPU structure. 93 * 94 * @remarks This must be called after the system time MSR value has been updated. 95 */ 96 static int gimR3KvmUpdateSystemTime(PVM pVM, PVMCPU pVCpu) 97 { 98 PGIMKVM pKvm = &pVM->gim.s.u.Kvm; 99 PGIMKVMCPU pKvmCpu = &pVCpu->gim.s.u.KvmCpu; 100 101 /* 102 * Validate the MSR has the enable bit and the guest's system time struct. address. 103 */ 104 MSR_GIM_KVM_SYSTEM_TIME_IS_ENABLED(pKvmCpu->u64SystemTimeMsr); 105 if (!PGMPhysIsGCPhysNormal(pVM, pKvmCpu->GCPhysSystemTime)) 106 { 107 LogRel(("GIM: KVM: VCPU%3d: Invalid physical addr requested for mapping system-time struct. GCPhysSystemTime=%#RGp\n", 108 pVCpu->idCpu, pKvmCpu->GCPhysSystemTime)); 109 return VERR_GIM_OPERATION_FAILED; 110 } 111 112 VMSTATE const enmVMState = pVM->enmVMState; 113 bool const fRunning = VMSTATE_IS_RUNNING(enmVMState); 114 Assert(!(pKvmCpu->u32SystemTimeVersion & UINT32_C(1))); 115 116 /* 117 * Construct a system-time struct. 118 */ 119 GIMKVMSYSTEMTIME SystemTime; 120 RT_ZERO(SystemTime); 121 SystemTime.u32Version = pKvmCpu->u32SystemTimeVersion + !!fRunning; 122 SystemTime.u64NanoTS = pKvmCpu->uVirtNanoTS; 123 SystemTime.u64Tsc = pKvmCpu->uTsc; 124 SystemTime.fFlags = pKvmCpu->fSystemTimeFlags | GIM_KVM_SYSTEM_TIME_FLAGS_TSC_STABLE; 125 126 /* 127 * How the guest calculates the system time (nanoseconds): 128 * 129 * tsc = rdtsc - SysTime.u64Tsc 130 * if (SysTime.i8TscShift >= 0) 131 * tsc <<= i8TscShift; 132 * else 133 * tsc >>= -i8TscShift; 134 * time = ((tsc * SysTime.u32TscScale) >> 32) + SysTime.u64NanoTS 135 */ 136 uint64_t u64TscFreq = pKvm->cTscTicksPerSecond; 137 SystemTime.i8TscShift = 0; 138 while (u64TscFreq > 2 * RT_NS_1SEC_64) 139 { 140 u64TscFreq >>= 1; 141 SystemTime.i8TscShift--; 142 } 143 uint32_t uTscFreqLo = (uint32_t)u64TscFreq; 144 while (uTscFreqLo <= RT_NS_1SEC) 145 { 146 uTscFreqLo <<= 1; 147 SystemTime.i8TscShift++; 148 } 149 SystemTime.u32TscScale = ASMDivU64ByU32RetU32(RT_NS_1SEC_64 << 32, uTscFreqLo); 150 151 /* 152 * For informational purposes, back-calculate the exact TSC frequency the guest will see. 153 * Note that the frequency is in kHz, not Hz, since that's what Linux uses. 154 */ 155 uint64_t uTscKHz = (RT_NS_1MS_64 << 32) / SystemTime.u32TscScale; 156 if (SystemTime.i8TscShift < 0) 157 uTscKHz <<= -SystemTime.i8TscShift; 158 else 159 uTscKHz >>= SystemTime.i8TscShift; 160 161 /* 162 * Update guest memory with the system-time struct. 163 * 164 * We update the struct with an incremented, odd version field to indicate to the guest 165 * that the memory is being updated concurrently by the host and it should discard any 166 * data from this struct when it reads an odd version. 167 * 168 * When the VM is not running, we don't need to do this two step update for obvious 169 * reasons and so we skip it. 170 */ 171 if (fRunning) 172 Assert(SystemTime.u32Version & UINT32_C(1)); 173 else 174 Assert(!(SystemTime.u32Version & UINT32_C(1))); 175 176 int rc = PGMPhysSimpleWriteGCPhys(pVM, pKvmCpu->GCPhysSystemTime, &SystemTime, sizeof(GIMKVMSYSTEMTIME)); 177 if (RT_SUCCESS(rc)) 178 { 179 LogRel(("GIM: KVM: VCPU%3d: Enabled system-time struct. at %#RGp - u32TscScale=%#RX32 i8TscShift=%d uVersion=%#RU32 " 180 "fFlags=%#x uTsc=%#RX64 uVirtNanoTS=%#RX64 TscKHz=%RU64\n", pVCpu->idCpu, pKvmCpu->GCPhysSystemTime, 181 SystemTime.u32TscScale, SystemTime.i8TscShift, SystemTime.u32Version + !!fRunning, SystemTime.fFlags, 182 pKvmCpu->uTsc, pKvmCpu->uVirtNanoTS, uTscKHz)); 183 TMR3CpuTickParavirtEnable(pVM); 184 } 185 else 186 { 187 LogRel(("GIM: KVM: VCPU%3d: Failed to write system-time struct. at %#RGp. rc=%Rrc\n", pVCpu->idCpu, 188 pKvmCpu->GCPhysSystemTime, rc)); 189 } 190 191 if (fRunning) 192 { 193 ++SystemTime.u32Version; 194 Assert(!(SystemTime.u32Version & UINT32_C(1))); 195 rc = PGMPhysSimpleWriteGCPhys(pVM, pKvmCpu->GCPhysSystemTime + RT_UOFFSETOF(GIMKVMSYSTEMTIME, u32Version), 196 &SystemTime.u32Version, sizeof(SystemTime.u32Version)); 197 if (RT_FAILURE(rc)) 198 { 199 LogRel(("GIM: KVM: VCPU%3d: Failed to write system-time struct. while updating version field at %#RGp. rc=%Rrc\n", 200 pVCpu->idCpu, pKvmCpu->GCPhysSystemTime, rc)); 201 return rc; 202 } 203 204 /* Update the version so our next write will start with an even value. */ 205 pKvmCpu->u32SystemTimeVersion += 2; 206 } 207 208 return rc; 209 } 210 211 212 /** 88 213 * Initializes the KVM GIM provider. 89 214 * … … 340 465 Assert(!TMVirtualIsTicking(pVM)); /* paranoia. */ 341 466 Assert(!TMCpuTickIsTicking(pVCpu)); 342 gimR3Kvm EnableSystemTime(pVM, pVCpu);467 gimR3KvmUpdateSystemTime(pVM, pVCpu); 343 468 } 344 469 } … … 352 477 353 478 return VINF_SUCCESS; 354 }355 356 357 /**358 * Enables the KVM VCPU system-time structure.359 *360 * @returns VBox status code.361 * @param pVM The cross context VM structure.362 * @param pVCpu The cross context virtual CPU structure.363 *364 * @remarks Don't do any release assertions here, these can be triggered by365 * guest R0 code.366 */367 VMMR3_INT_DECL(int) gimR3KvmEnableSystemTime(PVM pVM, PVMCPU pVCpu)368 {369 PGIMKVM pKvm = &pVM->gim.s.u.Kvm;370 PGIMKVMCPU pKvmCpu = &pVCpu->gim.s.u.KvmCpu;371 372 /*373 * Validate the mapping address first.374 */375 if (!PGMPhysIsGCPhysNormal(pVM, pKvmCpu->GCPhysSystemTime))376 {377 LogRel(("GIM: KVM: VCPU%3d: Invalid physical addr requested for mapping system-time struct. GCPhysSystemTime=%#RGp\n",378 pVCpu->idCpu, pKvmCpu->GCPhysSystemTime));379 return VERR_GIM_OPERATION_FAILED;380 }381 382 /*383 * Construct the system-time struct.384 */385 GIMKVMSYSTEMTIME SystemTime;386 RT_ZERO(SystemTime);387 SystemTime.u32Version = pKvmCpu->u32SystemTimeVersion;388 SystemTime.u64NanoTS = pKvmCpu->uVirtNanoTS;389 SystemTime.u64Tsc = pKvmCpu->uTsc;390 SystemTime.fFlags = pKvmCpu->fSystemTimeFlags | GIM_KVM_SYSTEM_TIME_FLAGS_TSC_STABLE;391 392 /*393 * How the guest calculates the system time (nanoseconds):394 *395 * tsc = rdtsc - SysTime.u64Tsc396 * if (SysTime.i8TscShift >= 0)397 * tsc <<= i8TscShift;398 * else399 * tsc >>= -i8TscShift;400 * time = ((tsc * SysTime.u32TscScale) >> 32) + SysTime.u64NanoTS401 */402 uint64_t u64TscFreq = pKvm->cTscTicksPerSecond;403 SystemTime.i8TscShift = 0;404 while (u64TscFreq > 2 * RT_NS_1SEC_64)405 {406 u64TscFreq >>= 1;407 SystemTime.i8TscShift--;408 }409 uint32_t uTscFreqLo = (uint32_t)u64TscFreq;410 while (uTscFreqLo <= RT_NS_1SEC)411 {412 uTscFreqLo <<= 1;413 SystemTime.i8TscShift++;414 }415 SystemTime.u32TscScale = ASMDivU64ByU32RetU32(RT_NS_1SEC_64 << 32, uTscFreqLo);416 417 /* For informational purposes, back-calculate the exact TSC frequency the guest will see.418 * Note that the frequency is in kHz, not Hz, since that's what Linux uses.419 */420 uint64_t uTscKHz = (RT_NS_1MS_64 << 32) / SystemTime.u32TscScale;421 if (SystemTime.i8TscShift < 0)422 uTscKHz <<= -SystemTime.i8TscShift;423 else424 uTscKHz >>= SystemTime.i8TscShift;425 426 /*427 * Update guest memory with the system-time struct. Technically we are cheating428 * by only writing the struct once with the version incremented by two; in reality,429 * the system-time struct is enabled once for each CPU at boot and not concurrently430 * read by other VCPUs at the same time.431 */432 Assert(!(SystemTime.u32Version & UINT32_C(1)));433 int rc = PGMPhysSimpleWriteGCPhys(pVM, pKvmCpu->GCPhysSystemTime, &SystemTime, sizeof(GIMKVMSYSTEMTIME));434 if (RT_SUCCESS(rc))435 {436 LogRel(("GIM: KVM: VCPU%3d: Enabled system-time struct. at %#RGp - u32TscScale=%#RX32 i8TscShift=%d uVersion=%#RU32 "437 "fFlags=%#x uTsc=%#RX64 uVirtNanoTS=%#RX64 TscKHz=%RU64\n", pVCpu->idCpu, pKvmCpu->GCPhysSystemTime, SystemTime.u32TscScale,438 SystemTime.i8TscShift, SystemTime.u32Version, SystemTime.fFlags, pKvmCpu->uTsc, pKvmCpu->uVirtNanoTS, uTscKHz));439 TMR3CpuTickParavirtEnable(pVM);440 }441 else442 LogRel(("GIM: KVM: VCPU%3d: Failed to write system-time struct. at %#RGp. rc=%Rrc\n",443 pVCpu->idCpu, pKvmCpu->GCPhysSystemTime, rc));444 445 return rc;446 479 } 447 480 … … 546 579 } 547 580 581 582 /** 583 * Enables the KVM system time structure. 584 * 585 * This can be done concurrently because the guest memory being updated is per-VCPU 586 * and the struct even has a "version" field which needs to be incremented 587 * before/after altering guest memory to allow concurrent updates from the host. 588 * Hence this is not being done in an EMT rendezvous. It -is- done in ring-3 since 589 * we call into ring-3 only TM code in the end. 590 * 591 * @returns VBox status code. 592 * @param pVM The cross context VM structure. 593 * @param pVCpu The cross context virtual CPU structure. 594 * @param uMsrSystemTime The system time MSR value being written. 595 */ 596 VMMR3_INT_DECL(int) gimR3KvmEnableSystemTime(PVMCC pVM, PVMCPUCC pVCpu, uint64_t uMsrSystemTime) 597 { 598 Assert(uMsrSystemTime & MSR_GIM_KVM_SYSTEM_TIME_ENABLE_BIT); 599 PGIMKVM pKvm = &pVM->gim.s.u.Kvm; 600 PGIMKVMCPU pKvmCpu = &pVCpu->gim.s.u.KvmCpu; 601 602 /* 603 * Update the system-time struct. 604 * The system-time structs are usually placed at a different guest address for each VCPU. 605 * 606 * The rendezvous ensures that we don't have other VCPUs that might potentially be 607 * reading the guest memory which we're about to update. 608 */ 609 pKvmCpu->uTsc = TMCpuTickGetNoCheck(pVCpu); 610 pKvmCpu->uVirtNanoTS = ASMMultU64ByU32DivByU32(pKvmCpu->uTsc, RT_NS_1SEC, pKvm->cTscTicksPerSecond); 611 pKvmCpu->u64SystemTimeMsr = uMsrSystemTime; 612 pKvmCpu->GCPhysSystemTime = MSR_GIM_KVM_SYSTEM_TIME_GUEST_GPA(uMsrSystemTime); 613 614 int rc = gimR3KvmUpdateSystemTime(pVM, pVCpu); 615 if (RT_FAILURE(rc)) 616 { 617 pKvmCpu->u64SystemTimeMsr = 0; 618 /* We shouldn't throw a #GP(0) here for buggy guests (neither does KVM apparently), see @bugref{8627}. */ 619 } 620 621 return rc; 622 } 623
Note:
See TracChangeset
for help on using the changeset viewer.