VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/GIMKvm.cpp@ 54845

Last change on this file since 54845 was 54845, checked in by vboxsync, 10 years ago

VMM/GIM,TM: paranoia assertions.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/* $Id: GIMKvm.cpp 54845 2015-03-19 10:55:06Z vboxsync $ */
2/** @file
3 * GIM - Guest Interface Manager, KVM implementation.
4 */
5
6/*
7 * Copyright (C) 2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_GIM
22#include "GIMInternal.h"
23
24#include <iprt/asm-math.h>
25#include <iprt/assert.h>
26#include <iprt/err.h>
27#include <iprt/string.h>
28#include <iprt/mem.h>
29#include <iprt/spinlock.h>
30
31#include <VBox/vmm/cpum.h>
32#include <VBox/vmm/ssm.h>
33#include <VBox/vmm/vm.h>
34#include <VBox/vmm/hm.h>
35#include <VBox/vmm/pdmapi.h>
36#include <VBox/version.h>
37
38
39/*******************************************************************************
40* Defined Constants And Macros *
41*******************************************************************************/
42
43/**
44 * GIM KVM saved-state version.
45 */
46#define GIM_KVM_SAVED_STATE_VERSION UINT32_C(1)
47
48
49/*******************************************************************************
50* Global Variables *
51*******************************************************************************/
52#ifdef VBOX_WITH_STATISTICS
53# define GIMKVM_MSRRANGE(a_uFirst, a_uLast, a_szName) \
54 { (a_uFirst), (a_uLast), kCpumMsrRdFn_Gim, kCpumMsrWrFn_Gim, 0, 0, 0, 0, 0, a_szName, { 0 }, { 0 }, { 0 }, { 0 } }
55#else
56# define GIMKVM_MSRRANGE(a_uFirst, a_uLast, a_szName) \
57 { (a_uFirst), (a_uLast), kCpumMsrRdFn_Gim, kCpumMsrWrFn_Gim, 0, 0, 0, 0, 0, a_szName }
58#endif
59
60/**
61 * Array of MSR ranges supported by KVM.
62 */
63static CPUMMSRRANGE const g_aMsrRanges_Kvm[] =
64{
65 GIMKVM_MSRRANGE(MSR_GIM_KVM_RANGE0_START, MSR_GIM_KVM_RANGE0_END, "KVM range 0"),
66 GIMKVM_MSRRANGE(MSR_GIM_KVM_RANGE1_START, MSR_GIM_KVM_RANGE1_END, "KVM range 1")
67};
68#undef GIMKVM_MSRRANGE
69
70
71/**
72 * Initializes the KVM GIM provider.
73 *
74 * @returns VBox status code.
75 * @param pVM Pointer to the VM.
76 * @param uVersion The interface version this VM should use.
77 */
78VMMR3_INT_DECL(int) gimR3KvmInit(PVM pVM)
79{
80 AssertReturn(pVM, VERR_INVALID_PARAMETER);
81 AssertReturn(pVM->gim.s.enmProviderId == GIMPROVIDERID_KVM, VERR_INTERNAL_ERROR_5);
82
83 int rc;
84 PGIMKVM pKvm = &pVM->gim.s.u.Kvm;
85
86 /*
87 * Determine interface capabilities based on the version.
88 */
89 if (!pVM->gim.s.u32Version)
90 {
91 /* Basic features. */
92 pKvm->uBaseFeat = 0
93 | GIM_KVM_BASE_FEAT_CLOCK_OLD
94 //| GIM_KVM_BASE_FEAT_NOP_IO_DELAY
95 //| GIM_KVM_BASE_FEAT_MMU_OP
96 | GIM_KVM_BASE_FEAT_CLOCK
97 //| GIM_KVM_BASE_FEAT_ASYNC_PF
98 //| GIM_KVM_BASE_FEAT_STEAL_TIME
99 //| GIM_KVM_BASE_FEAT_PV_EOI
100 //| GIM_KVM_BASE_FEAT_UNHALT
101 ;
102 /* Rest of the features are determined in gimR3KvmInitCompleted(). */
103 }
104
105 /*
106 * Expose HVP (Hypervisor Present) bit to the guest.
107 */
108 CPUMSetGuestCpuIdFeature(pVM, CPUMCPUIDFEATURE_HVP);
109
110 /*
111 * Modify the standard hypervisor leaves for KVM.
112 */
113 CPUMCPUIDLEAF HyperLeaf;
114 RT_ZERO(HyperLeaf);
115 HyperLeaf.uLeaf = UINT32_C(0x40000000);
116 HyperLeaf.uEax = UINT32_C(0x40000001); /* Minimum value for KVM is 0x40000001. */
117 HyperLeaf.uEbx = 0x4B4D564B; /* 'KVMK' */
118 HyperLeaf.uEcx = 0x564B4D56; /* 'VMKV' */
119 HyperLeaf.uEdx = 0x0000004D; /* 'M000' */
120 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
121 AssertLogRelRCReturn(rc, rc);
122
123 /*
124 * Add KVM specific leaves.
125 */
126 HyperLeaf.uLeaf = UINT32_C(0x40000001);
127 HyperLeaf.uEax = pKvm->uBaseFeat;
128 HyperLeaf.uEbx = 0; /* Reserved */
129 HyperLeaf.uEcx = 0; /* Reserved */
130 HyperLeaf.uEdx = 0; /* Reserved */
131 rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
132 AssertLogRelRCReturn(rc, rc);
133
134 /*
135 * Insert all MSR ranges of KVM.
136 */
137 for (unsigned i = 0; i < RT_ELEMENTS(g_aMsrRanges_Kvm); i++)
138 {
139 rc = CPUMR3MsrRangesInsert(pVM, &g_aMsrRanges_Kvm[i]);
140 AssertLogRelRCReturn(rc, rc);
141 }
142
143 return VINF_SUCCESS;
144}
145
146
147/**
148 * Initializes remaining bits of the KVM provider.
149 *
150 * This is called after initializing HM and almost all other VMM components.
151 *
152 * @returns VBox status code.
153 * @param pVM Pointer to the VM.
154 */
155VMMR3_INT_DECL(int) gimR3KvmInitCompleted(PVM pVM)
156{
157 if (TMR3CpuTickIsFixedRateMonotonic(pVM, true /* fWithParavirtEnabled */))
158 {
159 PGIMKVM pKvm = &pVM->gim.s.u.Kvm;
160 pKvm->uBaseFeat |= GIM_KVM_BASE_FEAT_TSC_STABLE;
161
162 CPUMCPUIDLEAF HyperLeaf;
163 RT_ZERO(HyperLeaf);
164 HyperLeaf.uLeaf = UINT32_C(0x40000001);
165 HyperLeaf.uEax = pKvm->uBaseFeat;
166 HyperLeaf.uEbx = 0;
167 HyperLeaf.uEcx = 0;
168 HyperLeaf.uEdx = 0;
169 int rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf);
170 AssertLogRelRCReturn(rc, rc);
171 }
172
173 return VINF_SUCCESS;
174}
175
176
177/**
178 * Terminates the KVM GIM provider.
179 *
180 * @returns VBox status code.
181 * @param pVM Pointer to the VM.
182 */
183VMMR3_INT_DECL(int) gimR3KvmTerm(PVM pVM)
184{
185 gimR3KvmReset(pVM);
186 return VINF_SUCCESS;
187}
188
189
190/**
191 * Applies relocations to data and code managed by this component.
192 *
193 * This function will be called at init and whenever the VMM need to relocate
194 * itself inside the GC.
195 *
196 * @param pVM Pointer to the VM.
197 * @param offDelta Relocation delta relative to old location.
198 */
199VMMR3_INT_DECL(void) gimR3KvmRelocate(PVM pVM, RTGCINTPTR offDelta)
200{
201 NOREF(pVM); NOREF(offDelta);
202}
203
204
205/**
206 * This resets KVM provider MSRs and unmaps whatever KVM regions that
207 * the guest may have mapped.
208 *
209 * This is called when the VM is being reset.
210 *
211 * @param pVM Pointer to the VM.
212 * @thread EMT(0).
213 */
214VMMR3_INT_DECL(void) gimR3KvmReset(PVM pVM)
215{
216 VM_ASSERT_EMT0(pVM);
217
218 /*
219 * Reset MSRs.
220 */
221 PGIMKVM pKvm = &pVM->gim.s.u.Kvm;
222 pKvm->u64WallClockMsr = 0;
223 for (VMCPUID iCpu = 0; iCpu < pVM->cCpus; iCpu++)
224 {
225 PGIMKVMCPU pKvmCpu = &pVM->aCpus[iCpu].gim.s.u.KvmCpu;
226 pKvmCpu->u64SystemTimeMsr = 0;
227 }
228}
229
230
231/**
232 * KVM state-save operation.
233 *
234 * @returns VBox status code.
235 * @param pVM Pointer to the VM.
236 * @param pSSM Pointer to the SSM handle.
237 */
238VMMR3_INT_DECL(int) gimR3KvmSave(PVM pVM, PSSMHANDLE pSSM)
239{
240 PCGIMKVM pcKvm = &pVM->gim.s.u.Kvm;
241
242 /*
243 * Save the KVM SSM version.
244 */
245 SSMR3PutU32(pSSM, GIM_KVM_SAVED_STATE_VERSION);
246
247 /*
248 * Save per-VCPU data.
249 */
250 for (uint32_t i = 0; i < pVM->cCpus; i++)
251 {
252 PCGIMKVMCPU pcKvmCpu = &pVM->aCpus[i].gim.s.u.KvmCpu;
253
254 /* Guest may alter flags (namely GIM_KVM_SYSTEM_TIME_FLAGS_GUEST_PAUSED bit). So re-read them from guest-memory. */
255 GIMKVMSYSTEMTIME SystemTime;
256 RT_ZERO(SystemTime);
257 if (MSR_GIM_KVM_SYSTEM_TIME_IS_ENABLED(pcKvmCpu->u64SystemTimeMsr))
258 {
259 int rc = PGMPhysSimpleReadGCPhys(pVM, &SystemTime, pcKvmCpu->GCPhysSystemTime, sizeof(GIMKVMSYSTEMTIME));
260 AssertRCReturn(rc, rc);
261 }
262
263 SSMR3PutU64(pSSM, pcKvmCpu->u64SystemTimeMsr);
264 SSMR3PutU64(pSSM, pcKvmCpu->uTsc);
265 SSMR3PutU64(pSSM, pcKvmCpu->uVirtNanoTS);
266 SSMR3PutGCPhys(pSSM, pcKvmCpu->GCPhysSystemTime);
267 SSMR3PutU32(pSSM, pcKvmCpu->u32SystemTimeVersion);
268 SSMR3PutU8(pSSM, SystemTime.fFlags);
269 }
270
271 /*
272 * Save per-VM data.
273 */
274 SSMR3PutU64(pSSM, pcKvm->u64WallClockMsr);
275 return SSMR3PutU32(pSSM, pcKvm->uBaseFeat);
276}
277
278
279/**
280 * KVM state-load operation, final pass.
281 *
282 * @returns VBox status code.
283 * @param pVM Pointer to the VM.
284 * @param pSSM Pointer to the SSM handle.
285 * @param uSSMVersion The GIM saved-state version.
286 */
287VMMR3_INT_DECL(int) gimR3KvmLoad(PVM pVM, PSSMHANDLE pSSM, uint32_t uSSMVersion)
288{
289 /*
290 * Load the KVM SSM version first.
291 */
292 uint32_t uKvmSavedStatVersion;
293 int rc = SSMR3GetU32(pSSM, &uKvmSavedStatVersion);
294 AssertRCReturn(rc, rc);
295 if (uKvmSavedStatVersion != GIM_KVM_SAVED_STATE_VERSION)
296 return SSMR3SetLoadError(pSSM, VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION, RT_SRC_POS,
297 N_("Unsupported KVM saved-state version %u (expected %u)."), uKvmSavedStatVersion,
298 GIM_KVM_SAVED_STATE_VERSION);
299
300 /*
301 * Load per-VCPU data.
302 */
303 for (uint32_t i = 0; i < pVM->cCpus; i++)
304 {
305 PVMCPU pVCpu = &pVM->aCpus[i];
306 PGIMKVMCPU pKvmCpu = &pVCpu->gim.s.u.KvmCpu;
307
308 uint8_t fSystemTimeFlags = 0;
309 SSMR3GetU64(pSSM, &pKvmCpu->u64SystemTimeMsr);
310 SSMR3GetU64(pSSM, &pKvmCpu->uTsc);
311 SSMR3GetU64(pSSM, &pKvmCpu->uVirtNanoTS);
312 SSMR3GetGCPhys(pSSM, &pKvmCpu->GCPhysSystemTime);
313 SSMR3GetU32(pSSM, &pKvmCpu->u32SystemTimeVersion);
314 SSMR3GetU8(pSSM, &fSystemTimeFlags);
315
316 /* Enable the system-time struct. if necessary. */
317 if (MSR_GIM_KVM_SYSTEM_TIME_IS_ENABLED(pKvmCpu->u64SystemTimeMsr))
318 {
319 Assert(!TMVirtualIsTicking(pVM)); /* paranoia. */
320 Assert(!TMCpuTickIsTicking(pVCpu));
321 rc = gimR3KvmEnableSystemTime(pVM, pVCpu, pKvmCpu, fSystemTimeFlags);
322 AssertRCReturn(rc, rc);
323 }
324 }
325
326 /*
327 * Load per-VM data.
328 */
329 PGIMKVM pKvm = &pVM->gim.s.u.Kvm;
330 SSMR3GetU64(pSSM, &pKvm->u64WallClockMsr);
331 rc = SSMR3GetU32(pSSM, &pKvm->uBaseFeat);
332 AssertRCReturn(rc, rc);
333
334 return VINF_SUCCESS;
335}
336
337
338/**
339 * Enables the KVM VCPU system-time structure.
340 *
341 * @returns VBox status code.
342 * @param pVM Pointer to the VM.
343 * @param pVCpu Pointer to the VMCPU.
344 * @param pKvmCpu Pointer to the GIMKVMCPU with all fields
345 * populated by the caller.
346 * @param fFlags The system-time struct. flags.
347 *
348 * @remarks Don't do any release assertions here, these can be triggered by
349 * guest R0 code.
350 */
351VMMR3_INT_DECL(int) gimR3KvmEnableSystemTime(PVM pVM, PVMCPU pVCpu, PGIMKVMCPU pKvmCpu, uint8_t fFlags)
352{
353 GIMKVMSYSTEMTIME SystemTime;
354 RT_ZERO(SystemTime);
355 SystemTime.u32Version = pKvmCpu->u32SystemTimeVersion;
356 SystemTime.u64NanoTS = pKvmCpu->uVirtNanoTS;
357 SystemTime.u64Tsc = pKvmCpu->uTsc;
358 SystemTime.fFlags = fFlags | GIM_KVM_SYSTEM_TIME_FLAGS_TSC_STABLE;
359
360 /*
361 * How the guest calculates the system time (nanoseconds):
362 *
363 * tsc = rdtsc - SysTime.u64Tsc
364 * if (SysTime.i8TscShift >= 0)
365 * tsc <<= i8TscShift;
366 * else
367 * tsc >>= -i8TscShift;
368 * time = ((tsc * SysTime.u32TscScale) >> 32) + SysTime.u64NanoTS
369 */
370 uint64_t u64TscFreq = TMCpuTicksPerSecond(pVM);
371 SystemTime.i8TscShift = 0;
372 while (u64TscFreq > 2 * RT_NS_1SEC_64)
373 {
374 u64TscFreq >>= 1;
375 SystemTime.i8TscShift--;
376 }
377 uint32_t uTscFreqLo = (uint32_t)u64TscFreq;
378 while (uTscFreqLo <= RT_NS_1SEC)
379 {
380 uTscFreqLo <<= 1;
381 SystemTime.i8TscShift++;
382 }
383 SystemTime.u32TscScale = ASMDivU64ByU32RetU32(RT_NS_1SEC_64 << 32, uTscFreqLo);
384
385 Assert(!(SystemTime.u32Version & UINT32_C(1)));
386 Assert(PGMPhysIsGCPhysNormal(pVM, pKvmCpu->GCPhysSystemTime));
387 int rc = PGMPhysSimpleWriteGCPhys(pVM, pKvmCpu->GCPhysSystemTime, &SystemTime, sizeof(GIMKVMSYSTEMTIME));
388 if (RT_SUCCESS(rc))
389 {
390 LogRel(("GIM: KVM: VCPU%3d: Enabled system-time struct. at %#RGp - u32TscScale=%#RX32 i8TscShift=%d uVersion=%#RU32 "
391 "fFlags=%#x uTsc=%#RX64 uVirtNanoTS=%#RX64\n", pVCpu->idCpu, pKvmCpu->GCPhysSystemTime, SystemTime.u32TscScale,
392 SystemTime.i8TscShift, SystemTime.u32Version, SystemTime.fFlags, pKvmCpu->uTsc, pKvmCpu->uVirtNanoTS));
393 TMR3CpuTickParavirtEnable(pVM);
394 }
395 else
396 LogRel(("GIM: KVM: VCPU%3d: Failed to write system-time struct. at %#RGp. rc=%Rrc\n", pKvmCpu->GCPhysSystemTime, rc));
397
398 return rc;
399}
400
401
402/**
403 * Disables the KVM system-time struct.
404 *
405 * @returns VBox status code.
406 * @param pVM Pointer to the VM.
407 */
408VMMR3_INT_DECL(int) gimR3KvmDisableSystemTime(PVM pVM)
409{
410 TMR3CpuTickParavirtDisable(pVM);
411 return VINF_SUCCESS;
412}
413
414
415/**
416 * Enables the KVM wall-clock structure.
417 *
418 * @returns VBox status code.
419 * @param pVM Pointer to the VM.
420 * @param GCPhysWallClock Where the guest wall-clock structure is located.
421 * @param uVersion The version (sequence number) value to use.
422 *
423 * @remarks Don't do any release assertions here, these can be triggered by
424 * guest R0 code.
425 */
426VMMR3_INT_DECL(int) gimR3KvmEnableWallClock(PVM pVM, RTGCPHYS GCPhysWallClock, uint32_t uVersion)
427{
428 RTTIMESPEC TimeSpec;
429 int32_t iSec;
430 int32_t iNano;
431
432 TMR3UtcNow(pVM, &TimeSpec);
433 RTTimeSpecGetSecondsAndNano(&TimeSpec, &iSec, &iNano);
434
435 GIMKVMWALLCLOCK WallClock;
436 RT_ZERO(WallClock);
437 WallClock.u32Version = uVersion;
438 WallClock.u32Sec = iSec;
439 WallClock.u32Nano = iNano;
440
441 Assert(PGMPhysIsGCPhysNormal(pVM, GCPhysWallClock));
442 Assert(!(WallClock.u32Version & UINT32_C(1)));
443 int rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysWallClock, &WallClock, sizeof(GIMKVMWALLCLOCK));
444 if (RT_SUCCESS(rc))
445 {
446 LogRel(("GIM: KVM: Enabled wall-clock struct. at %#RGp - u32Sec=%u u32Nano=%u uVersion=%#RU32\n", GCPhysWallClock,
447 WallClock.u32Sec, WallClock.u32Nano, WallClock.u32Version));
448 }
449 else
450 LogRel(("GIM: KVM: Failed to write wall-clock struct. at %#RGp. rc=%Rrc\n", GCPhysWallClock, rc));
451
452 return rc;
453}
454
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette