VirtualBox

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

Last change on this file since 11146 was 8579, checked in by vboxsync, 16 years ago

AssertBreakVoid -> AssertBreak

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