VirtualBox

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

Last change on this file since 14082 was 13832, checked in by vboxsync, 16 years ago

IN_GC -> IN_RC.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 24.9 KB
Line 
1/* $Id: TMAllVirtual.cpp 13832 2008-11-05 02:01:12Z 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 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 u64 = pVM->tm.s.u64VirtualSync;
511 return u64;
512}
513
514
515/**
516 * Gets the current TMCLOCK_VIRTUAL_SYNC time.
517 *
518 * @returns The timestamp.
519 * @param pVM VM handle.
520 * @thread EMT.
521 */
522VMMDECL(uint64_t) TMVirtualSyncGet(PVM pVM)
523{
524 return TMVirtualSyncGetEx(pVM, true /* check timers */);
525}
526
527
528/**
529 * Gets the current lag of the synchronous virtual clock (relative to the virtual clock).
530 *
531 * @return The current lag.
532 * @param pVM VM handle.
533 */
534VMMDECL(uint64_t) TMVirtualSyncGetLag(PVM pVM)
535{
536 return pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp;
537}
538
539
540/**
541 * Get the current catch-up percent.
542 *
543 * @return The current catch0up percent. 0 means running at the same speed as the virtual clock.
544 * @param pVM VM handle.
545 */
546VMMDECL(uint32_t) TMVirtualSyncGetCatchUpPct(PVM pVM)
547{
548 if (pVM->tm.s.fVirtualSyncCatchUp)
549 return pVM->tm.s.u32VirtualSyncCatchUpPercentage;
550 return 0;
551}
552
553
554/**
555 * Gets the current TMCLOCK_VIRTUAL frequency.
556 *
557 * @returns The freqency.
558 * @param pVM VM handle.
559 */
560VMMDECL(uint64_t) TMVirtualGetFreq(PVM pVM)
561{
562 return TMCLOCK_FREQ_VIRTUAL;
563}
564
565
566/**
567 * Resumes the virtual clock.
568 *
569 * @returns VINF_SUCCESS on success.
570 * @returns VINF_INTERNAL_ERROR and VBOX_STRICT assertion if called out of order.
571 * @param pVM VM handle.
572 */
573VMMDECL(int) TMVirtualResume(PVM pVM)
574{
575 if (!pVM->tm.s.fVirtualTicking)
576 {
577 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualResume);
578 pVM->tm.s.u64VirtualRawPrev = 0;
579 pVM->tm.s.u64VirtualWarpDriveStart = tmVirtualGetRawNanoTS(pVM);
580 pVM->tm.s.u64VirtualOffset = pVM->tm.s.u64VirtualWarpDriveStart - pVM->tm.s.u64Virtual;
581 pVM->tm.s.fVirtualTicking = true;
582 pVM->tm.s.fVirtualSyncTicking = true;
583 return VINF_SUCCESS;
584 }
585
586 AssertFailed();
587 return VERR_INTERNAL_ERROR;
588}
589
590
591/**
592 * Pauses the virtual clock.
593 *
594 * @returns VINF_SUCCESS on success.
595 * @returns VINF_INTERNAL_ERROR and VBOX_STRICT assertion if called out of order.
596 * @param pVM VM handle.
597 */
598VMMDECL(int) TMVirtualPause(PVM pVM)
599{
600 if (pVM->tm.s.fVirtualTicking)
601 {
602 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualPause);
603 pVM->tm.s.u64Virtual = tmVirtualGetRaw(pVM);
604 pVM->tm.s.fVirtualSyncTicking = false;
605 pVM->tm.s.fVirtualTicking = false;
606 return VINF_SUCCESS;
607 }
608
609 AssertFailed();
610 return VERR_INTERNAL_ERROR;
611}
612
613
614/**
615 * Gets the current warp drive percent.
616 *
617 * @returns The warp drive percent.
618 * @param pVM The VM handle.
619 */
620VMMDECL(uint32_t) TMVirtualGetWarpDrive(PVM pVM)
621{
622 return pVM->tm.s.u32VirtualWarpDrivePercentage;
623}
624
625
626/**
627 * Sets the warp drive percent of the virtual time.
628 *
629 * @returns VBox status code.
630 * @param pVM The VM handle.
631 * @param u32Percent The new percentage. 100 means normal operation.
632 */
633VMMDECL(int) TMVirtualSetWarpDrive(PVM pVM, uint32_t u32Percent)
634{
635/** @todo This isn't a feature specific to virtual time, move to TM level. (It
636 * should affect the TMR3UCTNow as well! */
637#ifdef IN_RING3
638 PVMREQ pReq;
639 int rc = VMR3ReqCall(pVM, VMREQDEST_ANY, &pReq, RT_INDEFINITE_WAIT, (PFNRT)tmVirtualSetWarpDrive, 2, pVM, u32Percent);
640 if (RT_SUCCESS(rc))
641 rc = pReq->iStatus;
642 VMR3ReqFree(pReq);
643 return rc;
644#else
645
646 return tmVirtualSetWarpDrive(pVM, u32Percent);
647#endif
648}
649
650
651/**
652 * EMT worker for tmVirtualSetWarpDrive.
653 *
654 * @returns VBox status code.
655 * @param pVM The VM handle.
656 * @param u32Percent See TMVirtualSetWarpDrive().
657 * @internal
658 */
659static DECLCALLBACK(int) tmVirtualSetWarpDrive(PVM pVM, uint32_t u32Percent)
660{
661 /*
662 * Validate it.
663 */
664 AssertMsgReturn(u32Percent >= 2 && u32Percent <= 20000,
665 ("%RX32 is not between 2 and 20000 (inclusive).\n", u32Percent),
666 VERR_INVALID_PARAMETER);
667
668 /*
669 * If the time is running we'll have to pause it before we can change
670 * the warp drive settings.
671 */
672 bool fPaused = pVM->tm.s.fVirtualTicking;
673 if (fPaused)
674 {
675 int rc = TMVirtualPause(pVM);
676 AssertRCReturn(rc, rc);
677 rc = TMCpuTickPause(pVM);
678 AssertRCReturn(rc, rc);
679 }
680
681 pVM->tm.s.u32VirtualWarpDrivePercentage = u32Percent;
682 pVM->tm.s.fVirtualWarpDrive = u32Percent != 100;
683 LogRel(("TM: u32VirtualWarpDrivePercentage=%RI32 fVirtualWarpDrive=%RTbool\n",
684 pVM->tm.s.u32VirtualWarpDrivePercentage, pVM->tm.s.fVirtualWarpDrive));
685
686 if (fPaused)
687 {
688 int rc = TMVirtualResume(pVM);
689 AssertRCReturn(rc, rc);
690 rc = TMCpuTickResume(pVM);
691 AssertRCReturn(rc, rc);
692 }
693
694 return VINF_SUCCESS;
695}
696
697
698/**
699 * Converts from virtual ticks to nanoseconds.
700 *
701 * @returns nanoseconds.
702 * @param pVM The VM handle.
703 * @param u64VirtualTicks The virtual ticks to convert.
704 * @remark There could be rounding errors here. We just do a simple integere divide
705 * without any adjustments.
706 */
707VMMDECL(uint64_t) TMVirtualToNano(PVM pVM, uint64_t u64VirtualTicks)
708{
709 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
710 return u64VirtualTicks;
711}
712
713
714/**
715 * Converts from virtual ticks to microseconds.
716 *
717 * @returns microseconds.
718 * @param pVM The VM handle.
719 * @param u64VirtualTicks The virtual ticks to convert.
720 * @remark There could be rounding errors here. We just do a simple integere divide
721 * without any adjustments.
722 */
723VMMDECL(uint64_t) TMVirtualToMicro(PVM pVM, uint64_t u64VirtualTicks)
724{
725 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
726 return u64VirtualTicks / 1000;
727}
728
729
730/**
731 * Converts from virtual ticks to milliseconds.
732 *
733 * @returns milliseconds.
734 * @param pVM The VM handle.
735 * @param u64VirtualTicks The virtual ticks to convert.
736 * @remark There could be rounding errors here. We just do a simple integere divide
737 * without any adjustments.
738 */
739VMMDECL(uint64_t) TMVirtualToMilli(PVM pVM, uint64_t u64VirtualTicks)
740{
741 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
742 return u64VirtualTicks / 1000000;
743}
744
745
746/**
747 * Converts from nanoseconds to virtual ticks.
748 *
749 * @returns virtual ticks.
750 * @param pVM The VM handle.
751 * @param u64NanoTS The nanosecond value ticks to convert.
752 * @remark There could be rounding and overflow errors here.
753 */
754VMMDECL(uint64_t) TMVirtualFromNano(PVM pVM, uint64_t u64NanoTS)
755{
756 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
757 return u64NanoTS;
758}
759
760
761/**
762 * Converts from microseconds to virtual ticks.
763 *
764 * @returns virtual ticks.
765 * @param pVM The VM handle.
766 * @param u64MicroTS The microsecond value ticks to convert.
767 * @remark There could be rounding and overflow errors here.
768 */
769VMMDECL(uint64_t) TMVirtualFromMicro(PVM pVM, uint64_t u64MicroTS)
770{
771 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
772 return u64MicroTS * 1000;
773}
774
775
776/**
777 * Converts from milliseconds to virtual ticks.
778 *
779 * @returns virtual ticks.
780 * @param pVM The VM handle.
781 * @param u64MilliTS The millisecond value ticks to convert.
782 * @remark There could be rounding and overflow errors here.
783 */
784VMMDECL(uint64_t) TMVirtualFromMilli(PVM pVM, uint64_t u64MilliTS)
785{
786 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
787 return u64MilliTS * 1000000;
788}
789
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