VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/TMAllVirtual.cpp@ 17119

Last change on this file since 17119 was 14299, checked in by vboxsync, 16 years ago

Corrected grammos in comments. No code changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 25.7 KB
Line 
1/* $Id: TMAllVirtual.cpp 14299 2008-11-18 13:25:40Z vboxsync $ */
2/** @file
3 * TM - Timeout Manager, Virtual Time, All Contexts.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_TM
27#include <VBox/tm.h>
28#ifdef IN_RING3
29# include <VBox/rem.h>
30# include <iprt/thread.h>
31#endif
32#include "TMInternal.h"
33#include <VBox/vm.h>
34#include <VBox/err.h>
35#include <VBox/log.h>
36#include <VBox/sup.h>
37
38#include <iprt/time.h>
39#include <iprt/assert.h>
40#include <iprt/asm.h>
41
42
43/*******************************************************************************
44* Internal Functions *
45*******************************************************************************/
46static DECLCALLBACK(int) tmVirtualSetWarpDrive(PVM pVM, uint32_t u32Percent);
47
48
49/**
50 * Helper function that's used by the assembly routines when something goes bust.
51 *
52 * @param pData Pointer to the data structure.
53 * @param u64NanoTS The calculated nano ts.
54 * @param u64DeltaPrev The delta relative to the previously returned timestamp.
55 * @param u64PrevNanoTS The previously returned timestamp (as it was read it).
56 */
57DECLEXPORT(void) tmVirtualNanoTSBad(PRTTIMENANOTSDATA pData, uint64_t u64NanoTS, uint64_t u64DeltaPrev, uint64_t u64PrevNanoTS)
58{
59 //PVM pVM = (PVM)((uint8_t *)pData - RT_OFFSETOF(VM, CTXALLSUFF(s.tm.VirtualGetRawData)));
60 pData->cBadPrev++;
61 if ((int64_t)u64DeltaPrev < 0)
62 LogRel(("TM: u64DeltaPrev=%RI64 u64PrevNanoTS=0x%016RX64 u64NanoTS=0x%016RX64\n",
63 u64DeltaPrev, u64PrevNanoTS, u64NanoTS));
64 else
65 Log(("TM: u64DeltaPrev=%RI64 u64PrevNanoTS=0x%016RX64 u64NanoTS=0x%016RX64 (debugging?)\n",
66 u64DeltaPrev, u64PrevNanoTS, u64NanoTS));
67}
68
69
70/**
71 * Called the first time somebody asks for the time or when the GIP
72 * is mapped/unmapped.
73 *
74 * This should never ever happen.
75 */
76DECLEXPORT(uint64_t) tmVirtualNanoTSRediscover(PRTTIMENANOTSDATA pData)
77{
78 //PVM pVM = (PVM)((uint8_t *)pData - RT_OFFSETOF(VM, CTXALLSUFF(s.tm.VirtualGetRawData)));
79 PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
80 AssertFatalMsgFailed(("pGip=%p u32Magic=%#x\n", pGip, VALID_PTR(pGip) ? pGip->u32Magic : 0));
81}
82
83
84#if 1
85
86/**
87 * Wrapper around the IPRT GIP time methods.
88 */
89DECLINLINE(uint64_t) tmVirtualGetRawNanoTS(PVM pVM)
90{
91#ifdef IN_RING3
92 return CTXALLSUFF(pVM->tm.s.pfnVirtualGetRaw)(&CTXALLSUFF(pVM->tm.s.VirtualGetRawData));
93# else /* !IN_RING3 */
94 uint32_t cPrevSteps = pVM->tm.s.CTX_SUFF(VirtualGetRawData).c1nsSteps;
95 uint64_t u64 = pVM->tm.s.CTX_SUFF(pfnVirtualGetRaw)(&pVM->tm.s.CTX_SUFF(VirtualGetRawData));
96 if (cPrevSteps != pVM->tm.s.CTX_SUFF(VirtualGetRawData).c1nsSteps)
97 VM_FF_SET(pVM, VM_FF_TO_R3); /* S10 hack */
98 return u64;
99# endif /* !IN_RING3 */
100}
101
102#else
103
104/**
105 * This is (mostly) the same as rtTimeNanoTSInternal() except
106 * for the two globals which live in TM.
107 *
108 * @returns Nanosecond timestamp.
109 * @param pVM The VM handle.
110 */
111static uint64_t tmVirtualGetRawNanoTS(PVM pVM)
112{
113 uint64_t u64Delta;
114 uint32_t u32NanoTSFactor0;
115 uint64_t u64TSC;
116 uint64_t u64NanoTS;
117 uint32_t u32UpdateIntervalTSC;
118 uint64_t u64PrevNanoTS;
119
120 /*
121 * Read the GIP data and the previous value.
122 */
123 for (;;)
124 {
125 uint32_t u32TransactionId;
126 PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
127#ifdef IN_RING3
128 if (RT_UNLIKELY(!pGip || pGip->u32Magic != SUPGLOBALINFOPAGE_MAGIC))
129 return RTTimeSystemNanoTS();
130#endif
131
132 if (pGip->u32Mode != SUPGIPMODE_ASYNC_TSC)
133 {
134 u32TransactionId = pGip->aCPUs[0].u32TransactionId;
135#ifdef RT_OS_L4
136 Assert((u32TransactionId & 1) == 0);
137#endif
138 u32UpdateIntervalTSC = pGip->aCPUs[0].u32UpdateIntervalTSC;
139 u64NanoTS = pGip->aCPUs[0].u64NanoTS;
140 u64TSC = pGip->aCPUs[0].u64TSC;
141 u32NanoTSFactor0 = pGip->u32UpdateIntervalNS;
142 u64Delta = ASMReadTSC();
143 u64PrevNanoTS = ASMAtomicReadU64(&pVM->tm.s.u64VirtualRawPrev);
144 if (RT_UNLIKELY( pGip->aCPUs[0].u32TransactionId != u32TransactionId
145 || (u32TransactionId & 1)))
146 continue;
147 }
148 else
149 {
150 /* SUPGIPMODE_ASYNC_TSC */
151 PSUPGIPCPU pGipCpu;
152
153 uint8_t u8ApicId = ASMGetApicId();
154 if (RT_LIKELY(u8ApicId < RT_ELEMENTS(pGip->aCPUs)))
155 pGipCpu = &pGip->aCPUs[u8ApicId];
156 else
157 {
158 AssertMsgFailed(("%x\n", u8ApicId));
159 pGipCpu = &pGip->aCPUs[0];
160 }
161
162 u32TransactionId = pGipCpu->u32TransactionId;
163#ifdef RT_OS_L4
164 Assert((u32TransactionId & 1) == 0);
165#endif
166 u32UpdateIntervalTSC = pGipCpu->u32UpdateIntervalTSC;
167 u64NanoTS = pGipCpu->u64NanoTS;
168 u64TSC = pGipCpu->u64TSC;
169 u32NanoTSFactor0 = pGip->u32UpdateIntervalNS;
170 u64Delta = ASMReadTSC();
171 u64PrevNanoTS = ASMAtomicReadU64(&pVM->tm.s.u64VirtualRawPrev);
172#ifdef IN_RC
173 Assert(!(ASMGetFlags() & X86_EFL_IF));
174#else
175 if (RT_UNLIKELY(u8ApicId != ASMGetApicId()))
176 continue;
177 if (RT_UNLIKELY( pGipCpu->u32TransactionId != u32TransactionId
178 || (u32TransactionId & 1)))
179 continue;
180#endif
181 }
182 break;
183 }
184
185 /*
186 * Calc NanoTS delta.
187 */
188 u64Delta -= u64TSC;
189 if (u64Delta > u32UpdateIntervalTSC)
190 {
191 /*
192 * We've expired the interval, cap it. If we're here for the 2nd
193 * time without any GIP update inbetween, the checks against
194 * pVM->tm.s.u64VirtualRawPrev below will force 1ns stepping.
195 */
196 u64Delta = u32UpdateIntervalTSC;
197 }
198#if !defined(_MSC_VER) || defined(RT_ARCH_AMD64) /* GCC makes very pretty code from these two inline calls, while MSC cannot. */
199 u64Delta = ASMMult2xU32RetU64((uint32_t)u64Delta, u32NanoTSFactor0);
200 u64Delta = ASMDivU64ByU32RetU32(u64Delta, u32UpdateIntervalTSC);
201#else
202 __asm
203 {
204 mov eax, dword ptr [u64Delta]
205 mul dword ptr [u32NanoTSFactor0]
206 div dword ptr [u32UpdateIntervalTSC]
207 mov dword ptr [u64Delta], eax
208 xor edx, edx
209 mov dword ptr [u64Delta + 4], edx
210 }
211#endif
212
213 /*
214 * Calculate the time and compare it with the previously returned value.
215 *
216 * Since this function is called *very* frequently when the VM is running
217 * and then mostly on EMT, we can restrict the valid range of the delta
218 * (-1s to 2*GipUpdates) and simplify/optimize the default path.
219 */
220 u64NanoTS += u64Delta;
221 uint64_t u64DeltaPrev = u64NanoTS - u64PrevNanoTS;
222 if (RT_LIKELY(u64DeltaPrev < 1000000000 /* 1s */))
223 /* frequent - less than 1s since last call. */;
224 else if ( (int64_t)u64DeltaPrev < 0
225 && (int64_t)u64DeltaPrev + u32NanoTSFactor0 * 2 > 0)
226 {
227 /* occasional - u64NanoTS is in the 'past' relative to previous returns. */
228 ASMAtomicIncU32(&pVM->tm.s.CTX_SUFF(VirtualGetRawData).c1nsSteps);
229 u64NanoTS = u64PrevNanoTS + 1;
230#ifndef IN_RING3
231 VM_FF_SET(pVM, VM_FF_TO_R3); /* S10 hack */
232#endif
233 }
234 else if (u64PrevNanoTS)
235 {
236 /* Something has gone bust, if negative offset it's real bad. */
237 ASMAtomicIncU32(&pVM->tm.s.CTX_SUFF(VirtualGetRawData).cBadPrev);
238 if ((int64_t)u64DeltaPrev < 0)
239 LogRel(("TM: u64DeltaPrev=%RI64 u64PrevNanoTS=0x%016RX64 u64NanoTS=0x%016RX64 u64Delta=%#RX64\n",
240 u64DeltaPrev, u64PrevNanoTS, u64NanoTS, u64Delta));
241 else
242 Log(("TM: u64DeltaPrev=%RI64 u64PrevNanoTS=0x%016RX64 u64NanoTS=0x%016RX64 u64Delta=%#RX64 (debugging?)\n",
243 u64DeltaPrev, u64PrevNanoTS, u64NanoTS, u64Delta));
244#ifdef DEBUG_bird
245 /** @todo there are some hickups during boot and reset that can cause 2-5 seconds delays. Investigate... */
246 AssertMsg(u64PrevNanoTS > UINT64_C(100000000000) /* 100s */,
247 ("u64DeltaPrev=%RI64 u64PrevNanoTS=0x%016RX64 u64NanoTS=0x%016RX64 u64Delta=%#RX64\n",
248 u64DeltaPrev, u64PrevNanoTS, u64NanoTS, u64Delta));
249#endif
250 }
251 /* else: We're resuming (see TMVirtualResume). */
252 if (RT_LIKELY(ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualRawPrev, u64NanoTS, u64PrevNanoTS)))
253 return u64NanoTS;
254
255 /*
256 * Attempt updating the previous value, provided we're still ahead of it.
257 *
258 * There is no point in recalculating u64NanoTS because we got preemted or if
259 * we raced somebody while the GIP was updated, since these are events
260 * that might occure at any point in the return path as well.
261 */
262 for (int cTries = 50;;)
263 {
264 u64PrevNanoTS = ASMAtomicReadU64(&pVM->tm.s.u64VirtualRawPrev);
265 if (u64PrevNanoTS >= u64NanoTS)
266 break;
267 if (ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualRawPrev, u64NanoTS, u64PrevNanoTS))
268 break;
269 AssertBreak(--cTries <= 0);
270 if (cTries < 25 && !VM_IS_EMT(pVM)) /* give up early */
271 break;
272 }
273
274 return u64NanoTS;
275}
276
277#endif
278
279
280/**
281 * Get the time when we're not running at 100%
282 *
283 * @returns The timestamp.
284 * @param pVM The VM handle.
285 */
286static uint64_t tmVirtualGetRawNonNormal(PVM pVM)
287{
288 /*
289 * Recalculate the RTTimeNanoTS() value for the period where
290 * warp drive has been enabled.
291 */
292 uint64_t u64 = tmVirtualGetRawNanoTS(pVM);
293 u64 -= pVM->tm.s.u64VirtualWarpDriveStart;
294 u64 *= pVM->tm.s.u32VirtualWarpDrivePercentage;
295 u64 /= 100;
296 u64 += pVM->tm.s.u64VirtualWarpDriveStart;
297
298 /*
299 * Now we apply the virtual time offset.
300 * (Which is the negated tmVirtualGetRawNanoTS() value for when the virtual
301 * machine started if it had been running continuously without any suspends.)
302 */
303 u64 -= pVM->tm.s.u64VirtualOffset;
304 return u64;
305}
306
307
308/**
309 * Get the raw virtual time.
310 *
311 * @returns The current time stamp.
312 * @param pVM The VM handle.
313 */
314DECLINLINE(uint64_t) tmVirtualGetRaw(PVM pVM)
315{
316 if (RT_LIKELY(!pVM->tm.s.fVirtualWarpDrive))
317 return tmVirtualGetRawNanoTS(pVM) - pVM->tm.s.u64VirtualOffset;
318 return tmVirtualGetRawNonNormal(pVM);
319}
320
321
322/**
323 * Inlined version of tmVirtualGetEx.
324 */
325DECLINLINE(uint64_t) tmVirtualGet(PVM pVM, bool fCheckTimers)
326{
327 uint64_t u64;
328 if (RT_LIKELY(pVM->tm.s.fVirtualTicking))
329 {
330 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGet);
331 u64 = tmVirtualGetRaw(pVM);
332
333 /*
334 * Use the chance to check for expired timers.
335 */
336 if ( fCheckTimers
337 && !VM_FF_ISSET(pVM, VM_FF_TIMER)
338 && ( pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire <= u64
339 || ( pVM->tm.s.fVirtualSyncTicking
340 && pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire <= u64 - pVM->tm.s.offVirtualSync
341 )
342 )
343 )
344 {
345 VM_FF_SET(pVM, VM_FF_TIMER);
346 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGetSetFF);
347#ifdef IN_RING3
348 REMR3NotifyTimerPending(pVM);
349 VMR3NotifyFF(pVM, true);
350#endif
351 }
352 }
353 else
354 u64 = pVM->tm.s.u64Virtual;
355 return u64;
356}
357
358
359/**
360 * Gets the current TMCLOCK_VIRTUAL time
361 *
362 * @returns The timestamp.
363 * @param pVM VM handle.
364 *
365 * @remark While the flow of time will never go backwards, the speed of the
366 * progress varies due to inaccurate RTTimeNanoTS and TSC. The latter can be
367 * influenced by power saving (SpeedStep, PowerNow!), while the former
368 * makes use of TSC and kernel timers.
369 */
370VMMDECL(uint64_t) TMVirtualGet(PVM pVM)
371{
372 return TMVirtualGetEx(pVM, true /* check timers */);
373}
374
375
376/**
377 * Gets the current TMCLOCK_VIRTUAL time
378 *
379 * @returns The timestamp.
380 * @param pVM VM handle.
381 * @param fCheckTimers Check timers or not
382 *
383 * @remark While the flow of time will never go backwards, the speed of the
384 * progress varies due to inaccurate RTTimeNanoTS and TSC. The latter can be
385 * influenced by power saving (SpeedStep, PowerNow!), while the former
386 * makes use of TSC and kernel timers.
387 */
388VMMDECL(uint64_t) TMVirtualGetEx(PVM pVM, bool fCheckTimers)
389{
390 return tmVirtualGet(pVM, fCheckTimers);
391}
392
393
394/**
395 * Gets the current TMCLOCK_VIRTUAL_SYNC time.
396 *
397 * @returns The timestamp.
398 * @param pVM VM handle.
399 * @param fCheckTimers Check timers or not
400 * @thread EMT.
401 */
402VMMDECL(uint64_t) TMVirtualSyncGetEx(PVM pVM, bool fCheckTimers)
403{
404 VM_ASSERT_EMT(pVM);
405
406 uint64_t u64;
407 if (pVM->tm.s.fVirtualSyncTicking)
408 {
409 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGetSync);
410
411 /*
412 * Query the virtual clock and do the usual expired timer check.
413 */
414 Assert(pVM->tm.s.fVirtualTicking);
415 u64 = tmVirtualGetRaw(pVM);
416 if ( fCheckTimers
417 && !VM_FF_ISSET(pVM, VM_FF_TIMER)
418 && pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire <= u64)
419 {
420 VM_FF_SET(pVM, VM_FF_TIMER);
421#ifdef IN_RING3
422 REMR3NotifyTimerPending(pVM);
423 VMR3NotifyFF(pVM, true);
424#endif
425 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGetSyncSetFF);
426 }
427
428 /*
429 * Read the offset and adjust if we're playing catch-up.
430 *
431 * The catch-up adjusting work by us decrementing the offset by a percentage of
432 * the time elapsed since the previous TMVirtualGetSync call.
433 *
434 * It's possible to get a very long or even negative interval between two read
435 * for the following reasons:
436 * - Someone might have suspended the process execution, frequently the case when
437 * debugging the process.
438 * - We might be on a different CPU which TSC isn't quite in sync with the
439 * other CPUs in the system.
440 * - Another thread is racing us and we might have been preemnted while inside
441 * this function.
442 *
443 * Assuming nano second virtual time, we can simply ignore any intervals which has
444 * any of the upper 32 bits set.
445 */
446 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
447 uint64_t off = pVM->tm.s.offVirtualSync;
448 if (pVM->tm.s.fVirtualSyncCatchUp)
449 {
450 const uint64_t u64Prev = pVM->tm.s.u64VirtualSyncCatchUpPrev;
451 uint64_t u64Delta = u64 - u64Prev;
452 if (RT_LIKELY(!(u64Delta >> 32)))
453 {
454 uint64_t u64Sub = ASMMultU64ByU32DivByU32(u64Delta, pVM->tm.s.u32VirtualSyncCatchUpPercentage, 100);
455 if (off > u64Sub + pVM->tm.s.offVirtualSyncGivenUp)
456 {
457 off -= u64Sub;
458 ASMAtomicXchgU64(&pVM->tm.s.offVirtualSync, off);
459 pVM->tm.s.u64VirtualSyncCatchUpPrev = u64;
460 Log4(("TM: %RU64/%RU64: sub %RU32\n", u64 - off, pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp, u64Sub));
461 }
462 else
463 {
464 /* we've completely caught up. */
465 STAM_PROFILE_ADV_STOP(&pVM->tm.s.StatVirtualSyncCatchup, c);
466 off = pVM->tm.s.offVirtualSyncGivenUp;
467 ASMAtomicXchgU64(&pVM->tm.s.offVirtualSync, off);
468 ASMAtomicXchgBool(&pVM->tm.s.fVirtualSyncCatchUp, false);
469 pVM->tm.s.u64VirtualSyncCatchUpPrev = u64;
470 Log4(("TM: %RU64/0: caught up\n", u64));
471 }
472 }
473 else
474 {
475 /* More than 4 seconds since last time (or negative), ignore it. */
476 if (!(u64Delta & RT_BIT_64(63)))
477 pVM->tm.s.u64VirtualSyncCatchUpPrev = u64;
478 Log(("TMVirtualGetSync: u64Delta=%RX64\n", u64Delta));
479 }
480 }
481
482 /*
483 * Complete the calculation of the current TMCLOCK_VIRTUAL_SYNC time. The current
484 * approach is to never pass the head timer. So, when we do stop the clock and
485 * set the timer pending flag.
486 */
487 u64 -= off;
488 const uint64_t u64Expire = pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire;
489 if (u64 >= u64Expire)
490 {
491 u64 = u64Expire;
492 ASMAtomicXchgU64(&pVM->tm.s.u64VirtualSync, u64);
493 ASMAtomicXchgBool(&pVM->tm.s.fVirtualSyncTicking, false);
494 if ( fCheckTimers
495 && !VM_FF_ISSET(pVM, VM_FF_TIMER))
496 {
497 VM_FF_SET(pVM, VM_FF_TIMER);
498#ifdef IN_RING3
499 REMR3NotifyTimerPending(pVM);
500 VMR3NotifyFF(pVM, true);
501#endif
502 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGetSyncSetFF);
503 Log4(("TM: %RU64/%RU64: exp tmr=>ff\n", u64, pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp));
504 }
505 else
506 Log4(("TM: %RU64/%RU64: exp tmr\n", u64, pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp));
507 }
508 }
509 else
510 {
511 u64 = pVM->tm.s.u64VirtualSync;
512
513 /*
514 * If it looks like a halt caused by pending timers, make sure the FF is raised.
515 * This is a safeguard against timer queue runner leaving the virtual sync clock stopped.
516 */
517 if ( fCheckTimers
518 && pVM->tm.s.fVirtualTicking
519 && !VM_FF_ISSET(pVM, VM_FF_TIMER))
520 {
521 const uint64_t u64Expire = pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire;
522 if (u64 >= u64Expire)
523 {
524 VM_FF_SET(pVM, VM_FF_TIMER);
525#ifdef IN_RING3
526 REMR3NotifyTimerPending(pVM);
527 VMR3NotifyFF(pVM, true);
528#endif
529 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGetSyncSetFF);
530 Log4(("TM: %RU64/%RU64: exp tmr=>ff (!)\n", u64, pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp));
531 }
532 }
533 }
534 return u64;
535}
536
537
538/**
539 * Gets the current TMCLOCK_VIRTUAL_SYNC time.
540 *
541 * @returns The timestamp.
542 * @param pVM VM handle.
543 * @thread EMT.
544 */
545VMMDECL(uint64_t) TMVirtualSyncGet(PVM pVM)
546{
547 return TMVirtualSyncGetEx(pVM, true /* check timers */);
548}
549
550
551/**
552 * Gets the current lag of the synchronous virtual clock (relative to the virtual clock).
553 *
554 * @return The current lag.
555 * @param pVM VM handle.
556 */
557VMMDECL(uint64_t) TMVirtualSyncGetLag(PVM pVM)
558{
559 return pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp;
560}
561
562
563/**
564 * Get the current catch-up percent.
565 *
566 * @return The current catch0up percent. 0 means running at the same speed as the virtual clock.
567 * @param pVM VM handle.
568 */
569VMMDECL(uint32_t) TMVirtualSyncGetCatchUpPct(PVM pVM)
570{
571 if (pVM->tm.s.fVirtualSyncCatchUp)
572 return pVM->tm.s.u32VirtualSyncCatchUpPercentage;
573 return 0;
574}
575
576
577/**
578 * Gets the current TMCLOCK_VIRTUAL frequency.
579 *
580 * @returns The freqency.
581 * @param pVM VM handle.
582 */
583VMMDECL(uint64_t) TMVirtualGetFreq(PVM pVM)
584{
585 return TMCLOCK_FREQ_VIRTUAL;
586}
587
588
589/**
590 * Resumes the virtual clock.
591 *
592 * @returns VINF_SUCCESS on success.
593 * @returns VINF_INTERNAL_ERROR and VBOX_STRICT assertion if called out of order.
594 * @param pVM VM handle.
595 */
596VMMDECL(int) TMVirtualResume(PVM pVM)
597{
598 if (!pVM->tm.s.fVirtualTicking)
599 {
600 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualResume);
601 pVM->tm.s.u64VirtualRawPrev = 0;
602 pVM->tm.s.u64VirtualWarpDriveStart = tmVirtualGetRawNanoTS(pVM);
603 pVM->tm.s.u64VirtualOffset = pVM->tm.s.u64VirtualWarpDriveStart - pVM->tm.s.u64Virtual;
604 pVM->tm.s.fVirtualTicking = true;
605 pVM->tm.s.fVirtualSyncTicking = true;
606 return VINF_SUCCESS;
607 }
608
609 AssertFailed();
610 return VERR_INTERNAL_ERROR;
611}
612
613
614/**
615 * Pauses the virtual clock.
616 *
617 * @returns VINF_SUCCESS on success.
618 * @returns VINF_INTERNAL_ERROR and VBOX_STRICT assertion if called out of order.
619 * @param pVM VM handle.
620 */
621VMMDECL(int) TMVirtualPause(PVM pVM)
622{
623 if (pVM->tm.s.fVirtualTicking)
624 {
625 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualPause);
626 pVM->tm.s.u64Virtual = tmVirtualGetRaw(pVM);
627 pVM->tm.s.fVirtualSyncTicking = false;
628 pVM->tm.s.fVirtualTicking = false;
629 return VINF_SUCCESS;
630 }
631
632 AssertFailed();
633 return VERR_INTERNAL_ERROR;
634}
635
636
637/**
638 * Gets the current warp drive percent.
639 *
640 * @returns The warp drive percent.
641 * @param pVM The VM handle.
642 */
643VMMDECL(uint32_t) TMVirtualGetWarpDrive(PVM pVM)
644{
645 return pVM->tm.s.u32VirtualWarpDrivePercentage;
646}
647
648
649/**
650 * Sets the warp drive percent of the virtual time.
651 *
652 * @returns VBox status code.
653 * @param pVM The VM handle.
654 * @param u32Percent The new percentage. 100 means normal operation.
655 */
656VMMDECL(int) TMVirtualSetWarpDrive(PVM pVM, uint32_t u32Percent)
657{
658/** @todo This isn't a feature specific to virtual time, move to TM level. (It
659 * should affect the TMR3UCTNow as well! */
660#ifdef IN_RING3
661 PVMREQ pReq;
662 int rc = VMR3ReqCall(pVM, VMREQDEST_ANY, &pReq, RT_INDEFINITE_WAIT, (PFNRT)tmVirtualSetWarpDrive, 2, pVM, u32Percent);
663 if (RT_SUCCESS(rc))
664 rc = pReq->iStatus;
665 VMR3ReqFree(pReq);
666 return rc;
667#else
668
669 return tmVirtualSetWarpDrive(pVM, u32Percent);
670#endif
671}
672
673
674/**
675 * EMT worker for tmVirtualSetWarpDrive.
676 *
677 * @returns VBox status code.
678 * @param pVM The VM handle.
679 * @param u32Percent See TMVirtualSetWarpDrive().
680 * @internal
681 */
682static DECLCALLBACK(int) tmVirtualSetWarpDrive(PVM pVM, uint32_t u32Percent)
683{
684 /*
685 * Validate it.
686 */
687 AssertMsgReturn(u32Percent >= 2 && u32Percent <= 20000,
688 ("%RX32 is not between 2 and 20000 (inclusive).\n", u32Percent),
689 VERR_INVALID_PARAMETER);
690
691 /*
692 * If the time is running we'll have to pause it before we can change
693 * the warp drive settings.
694 */
695 bool fPaused = pVM->tm.s.fVirtualTicking;
696 if (fPaused)
697 {
698 int rc = TMVirtualPause(pVM);
699 AssertRCReturn(rc, rc);
700 rc = TMCpuTickPause(pVM);
701 AssertRCReturn(rc, rc);
702 }
703
704 pVM->tm.s.u32VirtualWarpDrivePercentage = u32Percent;
705 pVM->tm.s.fVirtualWarpDrive = u32Percent != 100;
706 LogRel(("TM: u32VirtualWarpDrivePercentage=%RI32 fVirtualWarpDrive=%RTbool\n",
707 pVM->tm.s.u32VirtualWarpDrivePercentage, pVM->tm.s.fVirtualWarpDrive));
708
709 if (fPaused)
710 {
711 int rc = TMVirtualResume(pVM);
712 AssertRCReturn(rc, rc);
713 rc = TMCpuTickResume(pVM);
714 AssertRCReturn(rc, rc);
715 }
716
717 return VINF_SUCCESS;
718}
719
720
721/**
722 * Converts from virtual ticks to nanoseconds.
723 *
724 * @returns nanoseconds.
725 * @param pVM The VM handle.
726 * @param u64VirtualTicks The virtual ticks to convert.
727 * @remark There could be rounding errors here. We just do a simple integere divide
728 * without any adjustments.
729 */
730VMMDECL(uint64_t) TMVirtualToNano(PVM pVM, uint64_t u64VirtualTicks)
731{
732 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
733 return u64VirtualTicks;
734}
735
736
737/**
738 * Converts from virtual ticks to microseconds.
739 *
740 * @returns microseconds.
741 * @param pVM The VM handle.
742 * @param u64VirtualTicks The virtual ticks to convert.
743 * @remark There could be rounding errors here. We just do a simple integere divide
744 * without any adjustments.
745 */
746VMMDECL(uint64_t) TMVirtualToMicro(PVM pVM, uint64_t u64VirtualTicks)
747{
748 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
749 return u64VirtualTicks / 1000;
750}
751
752
753/**
754 * Converts from virtual ticks to milliseconds.
755 *
756 * @returns milliseconds.
757 * @param pVM The VM handle.
758 * @param u64VirtualTicks The virtual ticks to convert.
759 * @remark There could be rounding errors here. We just do a simple integere divide
760 * without any adjustments.
761 */
762VMMDECL(uint64_t) TMVirtualToMilli(PVM pVM, uint64_t u64VirtualTicks)
763{
764 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
765 return u64VirtualTicks / 1000000;
766}
767
768
769/**
770 * Converts from nanoseconds to virtual ticks.
771 *
772 * @returns virtual ticks.
773 * @param pVM The VM handle.
774 * @param u64NanoTS The nanosecond value ticks to convert.
775 * @remark There could be rounding and overflow errors here.
776 */
777VMMDECL(uint64_t) TMVirtualFromNano(PVM pVM, uint64_t u64NanoTS)
778{
779 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
780 return u64NanoTS;
781}
782
783
784/**
785 * Converts from microseconds to virtual ticks.
786 *
787 * @returns virtual ticks.
788 * @param pVM The VM handle.
789 * @param u64MicroTS The microsecond value ticks to convert.
790 * @remark There could be rounding and overflow errors here.
791 */
792VMMDECL(uint64_t) TMVirtualFromMicro(PVM pVM, uint64_t u64MicroTS)
793{
794 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
795 return u64MicroTS * 1000;
796}
797
798
799/**
800 * Converts from milliseconds to virtual ticks.
801 *
802 * @returns virtual ticks.
803 * @param pVM The VM handle.
804 * @param u64MilliTS The millisecond value ticks to convert.
805 * @remark There could be rounding and overflow errors here.
806 */
807VMMDECL(uint64_t) TMVirtualFromMilli(PVM pVM, uint64_t u64MilliTS)
808{
809 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
810 return u64MilliTS * 1000000;
811}
812
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