VirtualBox

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

Last change on this file since 6537 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

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