VirtualBox

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

Last change on this file since 5070 was 4592, checked in by vboxsync, 17 years ago

Give up earlier, esp. if we're not the EMT - just had EMT in a HLT loop and the timer thread racing each other.

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