VirtualBox

source: vbox/trunk/src/VBox/VMM/VMEmt.cpp@ 13782

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

More SMP groundwork.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 38.1 KB
Line 
1/* $Id: VMEmt.cpp 13782 2008-11-04 12:16:30Z vboxsync $ */
2/** @file
3 * VM - Virtual Machine, The Emulation Thread.
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_VM
27#include <VBox/tm.h>
28#include <VBox/dbgf.h>
29#include <VBox/em.h>
30#include <VBox/pdmapi.h>
31#include <VBox/rem.h>
32#include <VBox/tm.h>
33#include "VMInternal.h"
34#include <VBox/vm.h>
35#include <VBox/uvm.h>
36
37#include <VBox/err.h>
38#include <VBox/log.h>
39#include <iprt/assert.h>
40#include <iprt/asm.h>
41#include <iprt/semaphore.h>
42#include <iprt/string.h>
43#include <iprt/thread.h>
44#include <iprt/time.h>
45
46
47/**
48 * The emulation thread.
49 *
50 * @returns Thread exit code.
51 * @param ThreadSelf The handle to the executing thread.
52 * @param pvArgs Pointer to the user mode VM structure (UVM).
53 */
54DECLCALLBACK(int) vmR3EmulationThread(RTTHREAD ThreadSelf, void *pvArgs)
55{
56 PUVMCPU pUVMCPU = (PUVMCPU)pvArgs;
57 PUVM pUVM = pUVMCPU->pUVM;
58 RTCPUID idCPU = pUVMCPU->idCPU;
59 int rc = VINF_SUCCESS;
60
61 AssertReleaseMsg(VALID_PTR(pUVM) && pUVM->u32Magic == UVM_MAGIC,
62 ("Invalid arguments to the emulation thread!\n"));
63
64 rc = RTTlsSet(pUVM->vm.s.idxTLS, pUVMCPU);
65 AssertReleaseMsgReturn(RT_SUCCESS(rc), ("RTTlsSet %x failed with %Rrc\n", pUVM->vm.s.idxTLS, rc), rc);
66
67 /*
68 * Init the native thread member.
69 */
70 pUVM->vm.s.NativeThreadEMT = RTThreadGetNative(ThreadSelf); /* @todo should go away */
71 pUVMCPU->vm.s.NativeThreadEMT = RTThreadGetNative(ThreadSelf);
72
73 /*
74 * The request loop.
75 */
76 volatile VMSTATE enmBefore = VMSTATE_CREATING; /* volatile because of setjmp */
77 Log(("vmR3EmulationThread: Emulation thread starting the days work... Thread=%#x pUVM=%p\n", ThreadSelf, pUVM));
78 for (;;)
79 {
80 /* Requested to exit the EMT thread out of sync? (currently only VMR3WaitForResume) */
81 if (setjmp(pUVMCPU->vm.s.emtJumpEnv) != 0)
82 {
83 rc = VINF_SUCCESS;
84 break;
85 }
86
87 /*
88 * During early init there is no pVM, so make a special path
89 * for that to keep things clearly separate.
90 */
91 if (!pUVM->pVM)
92 {
93 /*
94 * Check for termination first.
95 */
96 if (pUVM->vm.s.fTerminateEMT)
97 {
98 rc = VINF_EM_TERMINATE;
99 break;
100 }
101 if (pUVM->vm.s.pReqs)
102 {
103 /*
104 * Service execute in EMT request.
105 */
106 rc = VMR3ReqProcessU(pUVM, VMREQDEST_ANY);
107 Log(("vmR3EmulationThread: Req rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_CREATING));
108 }
109 else
110 {
111 /*
112 * Nothing important is pending, so wait for something.
113 */
114 rc = VMR3WaitU(pUVM);
115 if (VBOX_FAILURE(rc))
116 break;
117 }
118 }
119 else
120 {
121
122 /*
123 * Pending requests which needs servicing?
124 *
125 * We check for state changes in addition to status codes when
126 * servicing requests. (Look after the ifs.)
127 */
128 PVM pVM = pUVM->pVM;
129 enmBefore = pVM->enmVMState;
130 if ( VM_FF_ISSET(pVM, VM_FF_TERMINATE)
131 || pUVM->vm.s.fTerminateEMT)
132 {
133 rc = VINF_EM_TERMINATE;
134 break;
135 }
136 if (pUVM->vm.s.pReqs)
137 {
138 /*
139 * Service execute in EMT request.
140 */
141 rc = VMR3ReqProcessU(pUVM, VMREQDEST_ANY);
142 Log(("vmR3EmulationThread: Req rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
143 }
144 else if (VM_FF_ISSET(pVM, VM_FF_DBGF))
145 {
146 /*
147 * Service the debugger request.
148 */
149 rc = DBGFR3VMMForcedAction(pVM);
150 Log(("vmR3EmulationThread: Dbg rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
151 }
152 else if (VM_FF_ISSET(pVM, VM_FF_RESET))
153 {
154 /*
155 * Service a delayed reset request.
156 */
157 rc = VMR3Reset(pVM);
158 VM_FF_CLEAR(pVM, VM_FF_RESET);
159 Log(("vmR3EmulationThread: Reset rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
160 }
161 else
162 {
163 /*
164 * Nothing important is pending, so wait for something.
165 */
166 rc = VMR3WaitU(pUVM);
167 if (VBOX_FAILURE(rc))
168 break;
169 }
170
171 /*
172 * Check for termination requests, these have extremely high priority.
173 */
174 if ( rc == VINF_EM_TERMINATE
175 || VM_FF_ISSET(pVM, VM_FF_TERMINATE)
176 || pUVM->vm.s.fTerminateEMT)
177 break;
178 }
179
180 /*
181 * Some requests (both VMR3Req* and the DBGF) can potentially
182 * resume or start the VM, in that case we'll get a change in
183 * VM status indicating that we're now running.
184 */
185 if ( VBOX_SUCCESS(rc)
186 && pUVM->pVM
187 && enmBefore != pUVM->pVM->enmVMState
188 && pUVM->pVM->enmVMState == VMSTATE_RUNNING)
189 {
190 PVM pVM = pUVM->pVM;
191 rc = EMR3ExecuteVM(pVM);
192 Log(("vmR3EmulationThread: EMR3ExecuteVM() -> rc=%Vrc, enmVMState=%d\n", rc, pVM->enmVMState));
193 if ( EMGetState(pVM) == EMSTATE_GURU_MEDITATION
194 && pVM->enmVMState == VMSTATE_RUNNING)
195 vmR3SetState(pVM, VMSTATE_GURU_MEDITATION);
196 }
197
198 } /* forever */
199
200
201 /*
202 * Exiting.
203 */
204 Log(("vmR3EmulationThread: Terminating emulation thread! Thread=%#x pUVM=%p rc=%Vrc enmBefore=%d enmVMState=%d\n",
205 ThreadSelf, pUVM, rc, enmBefore, pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_TERMINATED));
206 if (pUVM->vm.s.fEMTDoesTheCleanup)
207 {
208 Log(("vmR3EmulationThread: executing delayed Destroy\n"));
209 Assert(pUVM->pVM);
210 vmR3Destroy(pUVM->pVM);
211 vmR3DestroyFinalBitFromEMT(pUVM);
212 }
213 else
214 {
215 vmR3DestroyFinalBitFromEMT(pUVM);
216
217 /* we don't reset ThreadEMT here because it's used in waiting. */
218 pUVM->vm.s.NativeThreadEMT = NIL_RTNATIVETHREAD;
219 }
220 Log(("vmR3EmulationThread: EMT is terminated.\n"));
221 return rc;
222}
223
224
225/**
226 * Wait for VM to be resumed. Handle events like vmR3EmulationThread does.
227 * In case the VM is stopped, clean up and long jump to the main EMT loop.
228 *
229 * @returns VINF_SUCCESS or doesn't return
230 * @param pVM VM handle.
231 */
232VMMR3DECL(int) VMR3WaitForResume(PVM pVM)
233{
234 /*
235 * The request loop.
236 */
237 PUVMCPU pUVMCPU;
238 PUVM pUVM = pVM->pUVM;
239 VMSTATE enmBefore;
240 int rc;
241
242 pUVMCPU = (PUVMCPU)RTTlsGet(pUVM->vm.s.idxTLS);
243 AssertReturn(pUVMCPU, VERR_INTERNAL_ERROR);
244
245 for (;;)
246 {
247
248 /*
249 * Pending requests which needs servicing?
250 *
251 * We check for state changes in addition to status codes when
252 * servicing requests. (Look after the ifs.)
253 */
254 enmBefore = pVM->enmVMState;
255 if ( VM_FF_ISSET(pVM, VM_FF_TERMINATE)
256 || pUVM->vm.s.fTerminateEMT)
257 {
258 rc = VINF_EM_TERMINATE;
259 break;
260 }
261 else if (pUVM->vm.s.pReqs)
262 {
263 /*
264 * Service execute in EMT request.
265 */
266 rc = VMR3ReqProcessU(pUVM, VMREQDEST_ANY);
267 Log(("vmR3EmulationThread: Req rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
268 }
269 else if (VM_FF_ISSET(pVM, VM_FF_DBGF))
270 {
271 /*
272 * Service the debugger request.
273 */
274 rc = DBGFR3VMMForcedAction(pVM);
275 Log(("vmR3EmulationThread: Dbg rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
276 }
277 else if (VM_FF_ISSET(pVM, VM_FF_RESET))
278 {
279 /*
280 * Service a delay reset request.
281 */
282 rc = VMR3Reset(pVM);
283 VM_FF_CLEAR(pVM, VM_FF_RESET);
284 Log(("vmR3EmulationThread: Reset rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
285 }
286 else
287 {
288 /*
289 * Nothing important is pending, so wait for something.
290 */
291 rc = VMR3WaitU(pUVM);
292 if (VBOX_FAILURE(rc))
293 break;
294 }
295
296 /*
297 * Check for termination requests, these are extremely high priority.
298 */
299 if ( rc == VINF_EM_TERMINATE
300 || VM_FF_ISSET(pVM, VM_FF_TERMINATE)
301 || pUVM->vm.s.fTerminateEMT)
302 break;
303
304 /*
305 * Some requests (both VMR3Req* and the DBGF) can potentially
306 * resume or start the VM, in that case we'll get a change in
307 * VM status indicating that we're now running.
308 */
309 if ( VBOX_SUCCESS(rc)
310 && enmBefore != pVM->enmVMState
311 && pVM->enmVMState == VMSTATE_RUNNING)
312 {
313 /* Only valid exit reason. */
314 return VINF_SUCCESS;
315 }
316
317 } /* forever */
318
319 /* Return to the main loop in vmR3EmulationThread, which will clean up for us. */
320 longjmp(pUVMCPU->vm.s.emtJumpEnv, 1);
321}
322
323
324/**
325 * Gets the name of a halt method.
326 *
327 * @returns Pointer to a read only string.
328 * @param enmMethod The method.
329 */
330static const char *vmR3GetHaltMethodName(VMHALTMETHOD enmMethod)
331{
332 switch (enmMethod)
333 {
334 case VMHALTMETHOD_BOOTSTRAP: return "bootstrap";
335 case VMHALTMETHOD_DEFAULT: return "default";
336 case VMHALTMETHOD_OLD: return "old";
337 case VMHALTMETHOD_1: return "method1";
338 //case VMHALTMETHOD_2: return "method2";
339 case VMHALTMETHOD_GLOBAL_1: return "global1";
340 default: return "unknown";
341 }
342}
343
344
345/**
346 * The old halt loop.
347 *
348 * @param pUVM Pointer to the user mode VM structure.
349 */
350static DECLCALLBACK(int) vmR3HaltOldDoHalt(PUVM pUVM, const uint32_t fMask, uint64_t /* u64Now*/)
351{
352 /*
353 * Halt loop.
354 */
355 PVM pVM = pUVM->pVM;
356 int rc = VINF_SUCCESS;
357 ASMAtomicWriteBool(&pUVM->vm.s.fWait, true);
358 //unsigned cLoops = 0;
359 for (;;)
360 {
361 /*
362 * Work the timers and check if we can exit.
363 * The poll call gives us the ticks left to the next event in
364 * addition to perhaps set an FF.
365 */
366 STAM_REL_PROFILE_START(&pUVM->vm.s.StatHaltPoll, a);
367 PDMR3Poll(pVM);
368 STAM_REL_PROFILE_STOP(&pUVM->vm.s.StatHaltPoll, a);
369 STAM_REL_PROFILE_START(&pUVM->vm.s.StatHaltTimers, b);
370 TMR3TimerQueuesDo(pVM);
371 STAM_REL_PROFILE_STOP(&pUVM->vm.s.StatHaltTimers, b);
372 if (VM_FF_ISPENDING(pVM, fMask))
373 break;
374 uint64_t u64NanoTS = TMVirtualToNano(pVM, TMTimerPoll(pVM));
375 if (VM_FF_ISPENDING(pVM, fMask))
376 break;
377
378 /*
379 * Wait for a while. Someone will wake us up or interrupt the call if
380 * anything needs our attention.
381 */
382 if (u64NanoTS < 50000)
383 {
384 //RTLogPrintf("u64NanoTS=%RI64 cLoops=%d spin\n", u64NanoTS, cLoops++);
385 /* spin */;
386 }
387 else
388 {
389 VMMR3YieldStop(pVM);
390 //uint64_t u64Start = RTTimeNanoTS();
391 if (u64NanoTS < 870000) /* this is a bit speculative... works fine on linux. */
392 {
393 //RTLogPrintf("u64NanoTS=%RI64 cLoops=%d yield", u64NanoTS, cLoops++);
394 STAM_REL_PROFILE_START(&pUVM->vm.s.StatHaltYield, a);
395 RTThreadYield(); /* this is the best we can do here */
396 STAM_REL_PROFILE_STOP(&pUVM->vm.s.StatHaltYield, a);
397 }
398 else if (u64NanoTS < 2000000)
399 {
400 //RTLogPrintf("u64NanoTS=%RI64 cLoops=%d sleep 1ms", u64NanoTS, cLoops++);
401 STAM_REL_PROFILE_START(&pUVM->vm.s.StatHaltBlock, a);
402 rc = RTSemEventWait(pUVM->vm.s.EventSemWait, 1);
403 STAM_REL_PROFILE_STOP(&pUVM->vm.s.StatHaltBlock, a);
404 }
405 else
406 {
407 //RTLogPrintf("u64NanoTS=%RI64 cLoops=%d sleep %dms", u64NanoTS, cLoops++, (uint32_t)RT_MIN((u64NanoTS - 500000) / 1000000, 15));
408 STAM_REL_PROFILE_START(&pUVM->vm.s.StatHaltBlock, a);
409 rc = RTSemEventWait(pUVM->vm.s.EventSemWait, RT_MIN((u64NanoTS - 1000000) / 1000000, 15));
410 STAM_REL_PROFILE_STOP(&pUVM->vm.s.StatHaltBlock, a);
411 }
412 //uint64_t u64Slept = RTTimeNanoTS() - u64Start;
413 //RTLogPrintf(" -> rc=%Vrc in %RU64 ns / %RI64 ns delta\n", rc, u64Slept, u64NanoTS - u64Slept);
414 }
415 if (rc == VERR_TIMEOUT)
416 rc = VINF_SUCCESS;
417 else if (VBOX_FAILURE(rc))
418 {
419 AssertRC(rc != VERR_INTERRUPTED);
420 AssertMsgFailed(("RTSemEventWait->%Vrc\n", rc));
421 ASMAtomicUoWriteBool(&pUVM->vm.s.fTerminateEMT, true);
422 VM_FF_SET(pVM, VM_FF_TERMINATE);
423 rc = VERR_INTERNAL_ERROR;
424 break;
425 }
426 }
427
428 ASMAtomicUoWriteBool(&pUVM->vm.s.fWait, false);
429 return rc;
430}
431
432
433/**
434 * Initialize the configuration of halt method 1 & 2.
435 *
436 * @return VBox status code. Failure on invalid CFGM data.
437 * @param pVM The VM handle.
438 */
439static int vmR3HaltMethod12ReadConfigU(PUVM pUVM)
440{
441 /*
442 * The defaults.
443 */
444#if 1 /* DEBUGGING STUFF - REMOVE LATER */
445 pUVM->vm.s.Halt.Method12.u32LagBlockIntervalDivisorCfg = 4;
446 pUVM->vm.s.Halt.Method12.u32MinBlockIntervalCfg = 2*1000000;
447 pUVM->vm.s.Halt.Method12.u32MaxBlockIntervalCfg = 75*1000000;
448 pUVM->vm.s.Halt.Method12.u32StartSpinningCfg = 30*1000000;
449 pUVM->vm.s.Halt.Method12.u32StopSpinningCfg = 20*1000000;
450#else
451 pUVM->vm.s.Halt.Method12.u32LagBlockIntervalDivisorCfg = 4;
452 pUVM->vm.s.Halt.Method12.u32MinBlockIntervalCfg = 5*1000000;
453 pUVM->vm.s.Halt.Method12.u32MaxBlockIntervalCfg = 200*1000000;
454 pUVM->vm.s.Halt.Method12.u32StartSpinningCfg = 20*1000000;
455 pUVM->vm.s.Halt.Method12.u32StopSpinningCfg = 2*1000000;
456#endif
457
458 /*
459 * Query overrides.
460 *
461 * I don't have time to bother with niceities such as invalid value checks
462 * here right now. sorry.
463 */
464 PCFGMNODE pCfg = CFGMR3GetChild(CFGMR3GetRoot(pUVM->pVM), "/VMM/HaltedMethod1");
465 if (pCfg)
466 {
467 uint32_t u32;
468 if (RT_SUCCESS(CFGMR3QueryU32(pCfg, "LagBlockIntervalDivisor", &u32)))
469 pUVM->vm.s.Halt.Method12.u32LagBlockIntervalDivisorCfg = u32;
470 if (RT_SUCCESS(CFGMR3QueryU32(pCfg, "MinBlockInterval", &u32)))
471 pUVM->vm.s.Halt.Method12.u32MinBlockIntervalCfg = u32;
472 if (RT_SUCCESS(CFGMR3QueryU32(pCfg, "MaxBlockInterval", &u32)))
473 pUVM->vm.s.Halt.Method12.u32MaxBlockIntervalCfg = u32;
474 if (RT_SUCCESS(CFGMR3QueryU32(pCfg, "StartSpinning", &u32)))
475 pUVM->vm.s.Halt.Method12.u32StartSpinningCfg = u32;
476 if (RT_SUCCESS(CFGMR3QueryU32(pCfg, "StopSpinning", &u32)))
477 pUVM->vm.s.Halt.Method12.u32StopSpinningCfg = u32;
478 LogRel(("HaltedMethod1 config: %d/%d/%d/%d/%d\n",
479 pUVM->vm.s.Halt.Method12.u32LagBlockIntervalDivisorCfg,
480 pUVM->vm.s.Halt.Method12.u32MinBlockIntervalCfg,
481 pUVM->vm.s.Halt.Method12.u32MaxBlockIntervalCfg,
482 pUVM->vm.s.Halt.Method12.u32StartSpinningCfg,
483 pUVM->vm.s.Halt.Method12.u32StopSpinningCfg));
484 }
485
486 return VINF_SUCCESS;
487}
488
489
490/**
491 * Initialize halt method 1.
492 *
493 * @return VBox status code.
494 * @param pUVM Pointer to the user mode VM structure.
495 */
496static DECLCALLBACK(int) vmR3HaltMethod1Init(PUVM pUVM)
497{
498 return vmR3HaltMethod12ReadConfigU(pUVM);
499}
500
501
502/**
503 * Method 1 - Block whenever possible, and when lagging behind
504 * switch to spinning for 10-30ms with occational blocking until
505 * the lag has been eliminated.
506 */
507static DECLCALLBACK(int) vmR3HaltMethod1Halt(PUVM pUVM, const uint32_t fMask, uint64_t u64Now)
508{
509 PVM pVM = pUVM->pVM;
510
511 /*
512 * To simplify things, we decide up-front whether we should switch to spinning or
513 * not. This makes some ASSUMPTIONS about the cause of the spinning (PIT/RTC/PCNet)
514 * and that it will generate interrupts or other events that will cause us to exit
515 * the halt loop.
516 */
517 bool fBlockOnce = false;
518 bool fSpinning = false;
519 uint32_t u32CatchUpPct = TMVirtualSyncGetCatchUpPct(pVM);
520 if (u32CatchUpPct /* non-zero if catching up */)
521 {
522 if (pUVM->vm.s.Halt.Method12.u64StartSpinTS)
523 {
524 fSpinning = TMVirtualSyncGetLag(pVM) >= pUVM->vm.s.Halt.Method12.u32StopSpinningCfg;
525 if (fSpinning)
526 {
527 uint64_t u64Lag = TMVirtualSyncGetLag(pVM);
528 fBlockOnce = u64Now - pUVM->vm.s.Halt.Method12.u64LastBlockTS
529 > RT_MAX(pUVM->vm.s.Halt.Method12.u32MinBlockIntervalCfg,
530 RT_MIN(u64Lag / pUVM->vm.s.Halt.Method12.u32LagBlockIntervalDivisorCfg,
531 pUVM->vm.s.Halt.Method12.u32MaxBlockIntervalCfg));
532 }
533 else
534 {
535 //RTLogRelPrintf("Stopped spinning (%u ms)\n", (u64Now - pUVM->vm.s.Halt.Method12.u64StartSpinTS) / 1000000);
536 pUVM->vm.s.Halt.Method12.u64StartSpinTS = 0;
537 }
538 }
539 else
540 {
541 fSpinning = TMVirtualSyncGetLag(pVM) >= pUVM->vm.s.Halt.Method12.u32StartSpinningCfg;
542 if (fSpinning)
543 pUVM->vm.s.Halt.Method12.u64StartSpinTS = u64Now;
544 }
545 }
546 else if (pUVM->vm.s.Halt.Method12.u64StartSpinTS)
547 {
548 //RTLogRelPrintf("Stopped spinning (%u ms)\n", (u64Now - pUVM->vm.s.Halt.Method12.u64StartSpinTS) / 1000000);
549 pUVM->vm.s.Halt.Method12.u64StartSpinTS = 0;
550 }
551
552 /*
553 * Halt loop.
554 */
555 int rc = VINF_SUCCESS;
556 ASMAtomicWriteBool(&pUVM->vm.s.fWait, true);
557 unsigned cLoops = 0;
558 for (;; cLoops++)
559 {
560 /*
561 * Work the timers and check if we can exit.
562 */
563 STAM_REL_PROFILE_START(&pUVM->vm.s.StatHaltPoll, a);
564 PDMR3Poll(pVM);
565 STAM_REL_PROFILE_STOP(&pUVM->vm.s.StatHaltPoll, a);
566 STAM_REL_PROFILE_START(&pUVM->vm.s.StatHaltTimers, b);
567 TMR3TimerQueuesDo(pVM);
568 STAM_REL_PROFILE_STOP(&pUVM->vm.s.StatHaltTimers, b);
569 if (VM_FF_ISPENDING(pVM, fMask))
570 break;
571
572 /*
573 * Estimate time left to the next event.
574 */
575 uint64_t u64NanoTS = TMVirtualToNano(pVM, TMTimerPoll(pVM));
576 if (VM_FF_ISPENDING(pVM, fMask))
577 break;
578
579 /*
580 * Block if we're not spinning and the interval isn't all that small.
581 */
582 if ( ( !fSpinning
583 || fBlockOnce)
584#if 1 /* DEBUGGING STUFF - REMOVE LATER */
585 && u64NanoTS >= 100000) /* 0.100 ms */
586#else
587 && u64NanoTS >= 250000) /* 0.250 ms */
588#endif
589 {
590 const uint64_t Start = pUVM->vm.s.Halt.Method12.u64LastBlockTS = RTTimeNanoTS();
591 VMMR3YieldStop(pVM);
592
593 uint32_t cMilliSecs = RT_MIN(u64NanoTS / 1000000, 15);
594 if (cMilliSecs <= pUVM->vm.s.Halt.Method12.cNSBlockedTooLongAvg)
595 cMilliSecs = 1;
596 else
597 cMilliSecs -= pUVM->vm.s.Halt.Method12.cNSBlockedTooLongAvg;
598 //RTLogRelPrintf("u64NanoTS=%RI64 cLoops=%3d sleep %02dms (%7RU64) ", u64NanoTS, cLoops, cMilliSecs, u64NanoTS);
599 STAM_REL_PROFILE_START(&pUVM->vm.s.StatHaltBlock, a);
600 rc = RTSemEventWait(pUVM->vm.s.EventSemWait, cMilliSecs);
601 STAM_REL_PROFILE_STOP(&pUVM->vm.s.StatHaltBlock, a);
602 if (rc == VERR_TIMEOUT)
603 rc = VINF_SUCCESS;
604 else if (VBOX_FAILURE(rc))
605 {
606 AssertRC(rc != VERR_INTERRUPTED);
607 AssertMsgFailed(("RTSemEventWait->%Vrc\n", rc));
608 ASMAtomicUoWriteBool(&pUVM->vm.s.fTerminateEMT, true);
609 VM_FF_SET(pVM, VM_FF_TERMINATE);
610 rc = VERR_INTERNAL_ERROR;
611 break;
612 }
613
614 /*
615 * Calc the statistics.
616 * Update averages every 16th time, and flush parts of the history every 64th time.
617 */
618 const uint64_t Elapsed = RTTimeNanoTS() - Start;
619 pUVM->vm.s.Halt.Method12.cNSBlocked += Elapsed;
620 if (Elapsed > u64NanoTS)
621 pUVM->vm.s.Halt.Method12.cNSBlockedTooLong += Elapsed - u64NanoTS;
622 pUVM->vm.s.Halt.Method12.cBlocks++;
623 if (!(pUVM->vm.s.Halt.Method12.cBlocks & 0xf))
624 {
625 pUVM->vm.s.Halt.Method12.cNSBlockedTooLongAvg = pUVM->vm.s.Halt.Method12.cNSBlockedTooLong / pUVM->vm.s.Halt.Method12.cBlocks;
626 if (!(pUVM->vm.s.Halt.Method12.cBlocks & 0x3f))
627 {
628 pUVM->vm.s.Halt.Method12.cNSBlockedTooLong = pUVM->vm.s.Halt.Method12.cNSBlockedTooLongAvg * 0x40;
629 pUVM->vm.s.Halt.Method12.cBlocks = 0x40;
630 }
631 }
632 //RTLogRelPrintf(" -> %7RU64 ns / %7RI64 ns delta%s\n", Elapsed, Elapsed - u64NanoTS, fBlockOnce ? " (block once)" : "");
633
634 /*
635 * Clear the block once flag if we actually blocked.
636 */
637 if ( fBlockOnce
638 && Elapsed > 100000 /* 0.1 ms */)
639 fBlockOnce = false;
640 }
641 }
642 //if (fSpinning) RTLogRelPrintf("spun for %RU64 ns %u loops; lag=%RU64 pct=%d\n", RTTimeNanoTS() - u64Now, cLoops, TMVirtualSyncGetLag(pVM), u32CatchUpPct);
643
644 ASMAtomicUoWriteBool(&pUVM->vm.s.fWait, false);
645 return rc;
646}
647
648
649/**
650 * Initialize the global 1 halt method.
651 *
652 * @return VBox status code.
653 * @param pUVM Pointer to the user mode VM structure.
654 */
655static DECLCALLBACK(int) vmR3HaltGlobal1Init(PUVM pUVM)
656{
657 return VINF_SUCCESS;
658}
659
660
661/**
662 * The global 1 halt method - Block in GMM (ring-0) and let it
663 * try take care of the global scheduling of EMT threads.
664 */
665static DECLCALLBACK(int) vmR3HaltGlobal1Halt(PUVM pUVM, const uint32_t fMask, uint64_t u64Now)
666{
667 PVM pVM = pUVM->pVM;
668
669 /*
670 * Halt loop.
671 */
672 int rc = VINF_SUCCESS;
673 ASMAtomicWriteBool(&pUVM->vm.s.fWait, true);
674 unsigned cLoops = 0;
675 for (;; cLoops++)
676 {
677 /*
678 * Work the timers and check if we can exit.
679 */
680 STAM_REL_PROFILE_START(&pUVM->vm.s.StatHaltPoll, a);
681 PDMR3Poll(pVM);
682 STAM_REL_PROFILE_STOP(&pUVM->vm.s.StatHaltPoll, a);
683 STAM_REL_PROFILE_START(&pUVM->vm.s.StatHaltTimers, b);
684 TMR3TimerQueuesDo(pVM);
685 STAM_REL_PROFILE_STOP(&pUVM->vm.s.StatHaltTimers, b);
686 if (VM_FF_ISPENDING(pVM, fMask))
687 break;
688
689 /*
690 * Estimate time left to the next event.
691 */
692 uint64_t u64Delta;
693 uint64_t u64GipTime = TMTimerPollGIP(pVM, &u64Delta);
694 if (VM_FF_ISPENDING(pVM, fMask))
695 break;
696
697 /*
698 * Block if we're not spinning and the interval isn't all that small.
699 */
700 if (u64Delta > 50000 /* 0.050ms */)
701 {
702 VMMR3YieldStop(pVM);
703 if (VM_FF_ISPENDING(pVM, fMask))
704 break;
705
706 //RTLogRelPrintf("u64NanoTS=%RI64 cLoops=%3d sleep %02dms (%7RU64) ", u64NanoTS, cLoops, cMilliSecs, u64NanoTS);
707 STAM_REL_PROFILE_START(&pUVM->vm.s.StatHaltBlock, c);
708 rc = SUPCallVMMR0Ex(pVM->pVMR0, VMMR0_DO_GVMM_SCHED_HALT, u64GipTime, NULL);
709 STAM_REL_PROFILE_STOP(&pUVM->vm.s.StatHaltBlock, c);
710 if (rc == VERR_INTERRUPTED)
711 rc = VINF_SUCCESS;
712 else if (VBOX_FAILURE(rc))
713 {
714 AssertMsgFailed(("VMMR0_DO_GVMM_SCHED_HALT->%Vrc\n", rc));
715 ASMAtomicUoWriteBool(&pUVM->vm.s.fTerminateEMT, true);
716 VM_FF_SET(pVM, VM_FF_TERMINATE);
717 rc = VERR_INTERNAL_ERROR;
718 break;
719 }
720 }
721 /*
722 * When spinning call upon the GVMM and do some wakups once
723 * in a while, it's not like we're actually busy or anything.
724 */
725 else if (!(cLoops & 0x1fff))
726 {
727 STAM_REL_PROFILE_START(&pUVM->vm.s.StatHaltYield, d);
728 rc = SUPCallVMMR0Ex(pVM->pVMR0, VMMR0_DO_GVMM_SCHED_POLL, false /* don't yield */, NULL);
729 STAM_REL_PROFILE_STOP(&pUVM->vm.s.StatHaltYield, d);
730 }
731 }
732 //if (fSpinning) RTLogRelPrintf("spun for %RU64 ns %u loops; lag=%RU64 pct=%d\n", RTTimeNanoTS() - u64Now, cLoops, TMVirtualSyncGetLag(pVM), u32CatchUpPct);
733
734 ASMAtomicUoWriteBool(&pUVM->vm.s.fWait, false);
735 return rc;
736}
737
738
739/**
740 * The global 1 halt method - VMR3Wait() worker.
741 *
742 * @returns VBox status code.
743 * @param pUVM Pointer to the user mode VM structure.
744 */
745static DECLCALLBACK(int) vmR3HaltGlobal1Wait(PUVM pUVM)
746{
747 ASMAtomicWriteBool(&pUVM->vm.s.fWait, true);
748
749 PVM pVM = pUVM->pVM;
750 int rc = VINF_SUCCESS;
751 for (;;)
752 {
753 /*
754 * Check Relevant FFs.
755 */
756 if (VM_FF_ISPENDING(pVM, VM_FF_EXTERNAL_SUSPENDED_MASK))
757 break;
758
759 /*
760 * Wait for a while. Someone will wake us up or interrupt the call if
761 * anything needs our attention.
762 */
763 rc = SUPCallVMMR0Ex(pVM->pVMR0, VMMR0_DO_GVMM_SCHED_HALT, RTTimeNanoTS() + 1000000000 /* +1s */, NULL);
764 if (rc == VERR_INTERRUPTED)
765 rc = VINF_SUCCESS;
766 else if (VBOX_FAILURE(rc))
767 {
768 AssertMsgFailed(("RTSemEventWait->%Vrc\n", rc));
769 ASMAtomicUoWriteBool(&pUVM->vm.s.fTerminateEMT, true);
770 VM_FF_SET(pVM, VM_FF_TERMINATE);
771 rc = VERR_INTERNAL_ERROR;
772 break;
773 }
774
775 }
776
777 ASMAtomicUoWriteBool(&pUVM->vm.s.fWait, false);
778 return rc;
779}
780
781
782/**
783 * The global 1 halt method - VMR3NotifyFF() worker.
784 *
785 * @param pUVM Pointer to the user mode VM structure.
786 * @param fNotifiedREM See VMR3NotifyFF().
787 */
788static DECLCALLBACK(void) vmR3HaltGlobal1NotifyFF(PUVM pUVM, bool fNotifiedREM)
789{
790 if (pUVM->vm.s.fWait)
791 {
792 int rc = SUPCallVMMR0Ex(pUVM->pVM->pVMR0, VMMR0_DO_GVMM_SCHED_WAKE_UP, 0, NULL);
793 AssertRC(rc);
794 }
795 else if (!fNotifiedREM)
796 REMR3NotifyFF(pUVM->pVM);
797}
798
799
800/**
801 * Bootstrap VMR3Wait() worker.
802 *
803 * @returns VBox status code.
804 * @param pUVM Pointer to the user mode VM structure.
805 */
806static DECLCALLBACK(int) vmR3BootstrapWait(PUVM pUVM)
807{
808 ASMAtomicWriteBool(&pUVM->vm.s.fWait, true);
809
810 int rc = VINF_SUCCESS;
811 for (;;)
812 {
813 /*
814 * Check Relevant FFs.
815 */
816 if (pUVM->vm.s.pReqs)
817 break;
818 if ( pUVM->pVM
819 && VM_FF_ISPENDING(pUVM->pVM, VM_FF_EXTERNAL_SUSPENDED_MASK))
820 break;
821 if (pUVM->vm.s.fTerminateEMT)
822 break;
823
824 /*
825 * Wait for a while. Someone will wake us up or interrupt the call if
826 * anything needs our attention.
827 */
828 rc = RTSemEventWait(pUVM->vm.s.EventSemWait, 1000);
829 if (rc == VERR_TIMEOUT)
830 rc = VINF_SUCCESS;
831 else if (VBOX_FAILURE(rc))
832 {
833 AssertMsgFailed(("RTSemEventWait->%Vrc\n", rc));
834 ASMAtomicUoWriteBool(&pUVM->vm.s.fTerminateEMT, true);
835 if (pUVM->pVM)
836 VM_FF_SET(pUVM->pVM, VM_FF_TERMINATE);
837 rc = VERR_INTERNAL_ERROR;
838 break;
839 }
840
841 }
842
843 ASMAtomicUoWriteBool(&pUVM->vm.s.fWait, false);
844 return rc;
845}
846
847
848/**
849 * Bootstrap VMR3NotifyFF() worker.
850 *
851 * @param pUVM Pointer to the user mode VM structure.
852 * @param fNotifiedREM See VMR3NotifyFF().
853 */
854static DECLCALLBACK(void) vmR3BootstrapNotifyFF(PUVM pUVM, bool fNotifiedREM)
855{
856 if (pUVM->vm.s.fWait)
857 {
858 int rc = RTSemEventSignal(pUVM->vm.s.EventSemWait);
859 AssertRC(rc);
860 }
861}
862
863
864/**
865 * Default VMR3Wait() worker.
866 *
867 * @returns VBox status code.
868 * @param pUVM Pointer to the user mode VM structure.
869 */
870static DECLCALLBACK(int) vmR3DefaultWait(PUVM pUVM)
871{
872 ASMAtomicWriteBool(&pUVM->vm.s.fWait, true);
873
874 PVM pVM = pUVM->pVM;
875 int rc = VINF_SUCCESS;
876 for (;;)
877 {
878 /*
879 * Check Relevant FFs.
880 */
881 if (VM_FF_ISPENDING(pVM, VM_FF_EXTERNAL_SUSPENDED_MASK))
882 break;
883
884 /*
885 * Wait for a while. Someone will wake us up or interrupt the call if
886 * anything needs our attention.
887 */
888 rc = RTSemEventWait(pUVM->vm.s.EventSemWait, 1000);
889 if (rc == VERR_TIMEOUT)
890 rc = VINF_SUCCESS;
891 else if (VBOX_FAILURE(rc))
892 {
893 AssertMsgFailed(("RTSemEventWait->%Vrc\n", rc));
894 ASMAtomicUoWriteBool(&pUVM->vm.s.fTerminateEMT, true);
895 VM_FF_SET(pVM, VM_FF_TERMINATE);
896 rc = VERR_INTERNAL_ERROR;
897 break;
898 }
899
900 }
901
902 ASMAtomicUoWriteBool(&pUVM->vm.s.fWait, false);
903 return rc;
904}
905
906
907/**
908 * Default VMR3NotifyFF() worker.
909 *
910 * @param pUVM Pointer to the user mode VM structure.
911 * @param fNotifiedREM See VMR3NotifyFF().
912 */
913static DECLCALLBACK(void) vmR3DefaultNotifyFF(PUVM pUVM, bool fNotifiedREM)
914{
915 if (pUVM->vm.s.fWait)
916 {
917 int rc = RTSemEventSignal(pUVM->vm.s.EventSemWait);
918 AssertRC(rc);
919 }
920 else if (!fNotifiedREM)
921 REMR3NotifyFF(pUVM->pVM);
922}
923
924
925/**
926 * Array with halt method descriptors.
927 * VMINT::iHaltMethod contains an index into this array.
928 */
929static const struct VMHALTMETHODDESC
930{
931 /** The halt method id. */
932 VMHALTMETHOD enmHaltMethod;
933 /** The init function for loading config and initialize variables. */
934 DECLR3CALLBACKMEMBER(int, pfnInit,(PUVM pUVM));
935 /** The term function. */
936 DECLR3CALLBACKMEMBER(void, pfnTerm,(PUVM pUVM));
937 /** The halt function. */
938 DECLR3CALLBACKMEMBER(int, pfnHalt,(PUVM pUVM, const uint32_t fMask, uint64_t u64Now));
939 /** The wait function. */
940 DECLR3CALLBACKMEMBER(int, pfnWait,(PUVM pUVM));
941 /** The notifyFF function. */
942 DECLR3CALLBACKMEMBER(void, pfnNotifyFF,(PUVM pUVM, bool fNotifiedREM));
943} g_aHaltMethods[] =
944{
945 { VMHALTMETHOD_BOOTSTRAP, NULL, NULL, NULL, vmR3BootstrapWait, vmR3BootstrapNotifyFF },
946 { VMHALTMETHOD_OLD, NULL, NULL, vmR3HaltOldDoHalt, vmR3DefaultWait, vmR3DefaultNotifyFF },
947 { VMHALTMETHOD_1, vmR3HaltMethod1Init, NULL, vmR3HaltMethod1Halt, vmR3DefaultWait, vmR3DefaultNotifyFF },
948 //{ VMHALTMETHOD_2, vmR3HaltMethod2Init, vmR3HaltMethod2Term, vmR3HaltMethod2DoHalt, vmR3HaltMethod2Wait, vmR3HaltMethod2NotifyFF },
949 { VMHALTMETHOD_GLOBAL_1,vmR3HaltGlobal1Init, NULL, vmR3HaltGlobal1Halt, vmR3HaltGlobal1Wait, vmR3HaltGlobal1NotifyFF },
950};
951
952
953/**
954 * Notify the emulation thread (EMT) about pending Forced Action (FF).
955 *
956 * This function is called by thread other than EMT to make
957 * sure EMT wakes up and promptly service an FF request.
958 *
959 * @param pVM VM handle.
960 * @param fNotifiedREM Set if REM have already been notified. If clear the
961 * generic REMR3NotifyFF() method is called.
962 */
963VMMR3DECL(void) VMR3NotifyFF(PVM pVM, bool fNotifiedREM)
964{
965 LogFlow(("VMR3NotifyFF:\n"));
966 PUVM pUVM = pVM->pUVM;
967 g_aHaltMethods[pUVM->vm.s.iHaltMethod].pfnNotifyFF(pUVM, fNotifiedREM);
968}
969
970
971/**
972 * Notify the emulation thread (EMT) about pending Forced Action (FF).
973 *
974 * This function is called by thread other than EMT to make
975 * sure EMT wakes up and promptly service an FF request.
976 *
977 * @param pUVM Pointer to the user mode VM structure.
978 * @param fNotifiedREM Set if REM have already been notified. If clear the
979 * generic REMR3NotifyFF() method is called.
980 */
981VMMR3DECL(void) VMR3NotifyFFU(PUVM pUVM, bool fNotifiedREM)
982{
983 LogFlow(("VMR3NotifyFF:\n"));
984 g_aHaltMethods[pUVM->vm.s.iHaltMethod].pfnNotifyFF(pUVM, fNotifiedREM);
985}
986
987
988/**
989 * Halted VM Wait.
990 * Any external event will unblock the thread.
991 *
992 * @returns VINF_SUCCESS unless a fatal error occured. In the latter
993 * case an appropriate status code is returned.
994 * @param pVM VM handle.
995 * @param fIgnoreInterrupts If set the VM_FF_INTERRUPT flags is ignored.
996 * @thread The emulation thread.
997 */
998VMMR3DECL(int) VMR3WaitHalted(PVM pVM, bool fIgnoreInterrupts)
999{
1000 LogFlow(("VMR3WaitHalted: fIgnoreInterrupts=%d\n", fIgnoreInterrupts));
1001
1002 /*
1003 * Check Relevant FFs.
1004 */
1005 const uint32_t fMask = !fIgnoreInterrupts
1006 ? VM_FF_EXTERNAL_HALTED_MASK
1007 : VM_FF_EXTERNAL_HALTED_MASK & ~(VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC);
1008 if (VM_FF_ISPENDING(pVM, fMask))
1009 {
1010 LogFlow(("VMR3WaitHalted: returns VINF_SUCCESS (FF %#x)\n", pVM->fForcedActions));
1011 return VINF_SUCCESS;
1012 }
1013
1014 /*
1015 * The yielder is suspended while we're halting, while TM might have clock(s) running
1016 * only at certain times and need to be notified..
1017 */
1018 VMMR3YieldSuspend(pVM);
1019 TMNotifyStartOfHalt(pVM);
1020
1021 /*
1022 * Record halt averages for the last second.
1023 */
1024 PUVM pUVM = pVM->pUVM;
1025 uint64_t u64Now = RTTimeNanoTS();
1026 int64_t off = u64Now - pUVM->vm.s.u64HaltsStartTS;
1027 if (off > 1000000000)
1028 {
1029 if (off > _4G || !pUVM->vm.s.cHalts)
1030 {
1031 pUVM->vm.s.HaltInterval = 1000000000 /* 1 sec */;
1032 pUVM->vm.s.HaltFrequency = 1;
1033 }
1034 else
1035 {
1036 pUVM->vm.s.HaltInterval = (uint32_t)off / pUVM->vm.s.cHalts;
1037 pUVM->vm.s.HaltFrequency = ASMMultU64ByU32DivByU32(pUVM->vm.s.cHalts, 1000000000, (uint32_t)off);
1038 }
1039 pUVM->vm.s.u64HaltsStartTS = u64Now;
1040 pUVM->vm.s.cHalts = 0;
1041 }
1042 pUVM->vm.s.cHalts++;
1043
1044 /*
1045 * Do the halt.
1046 */
1047 int rc = g_aHaltMethods[pUVM->vm.s.iHaltMethod].pfnHalt(pUVM, fMask, u64Now);
1048
1049 /*
1050 * Notify TM and resume the yielder
1051 */
1052 TMNotifyEndOfHalt(pVM);
1053 VMMR3YieldResume(pVM);
1054
1055 LogFlow(("VMR3WaitHalted: returns %Vrc (FF %#x)\n", rc, pVM->fForcedActions));
1056 return rc;
1057}
1058
1059
1060/**
1061 * Suspended VM Wait.
1062 * Only a handful of forced actions will cause the function to
1063 * return to the caller.
1064 *
1065 * @returns VINF_SUCCESS unless a fatal error occured. In the latter
1066 * case an appropriate status code is returned.
1067 * @param pUVM Pointer to the user mode VM structure.
1068 * @thread The emulation thread.
1069 */
1070VMMR3DECL(int) VMR3WaitU(PUVM pUVM)
1071{
1072 LogFlow(("VMR3WaitU:\n"));
1073
1074 /*
1075 * Check Relevant FFs.
1076 */
1077 PVM pVM = pUVM->pVM;
1078 if ( pVM
1079 && VM_FF_ISPENDING(pVM, VM_FF_EXTERNAL_SUSPENDED_MASK))
1080 {
1081 LogFlow(("VMR3Wait: returns VINF_SUCCESS (FF %#x)\n", pVM->fForcedActions));
1082 return VINF_SUCCESS;
1083 }
1084
1085 /*
1086 * Do waiting according to the halt method (so VMR3NotifyFF
1087 * doesn't have to special case anything).
1088 */
1089 int rc = g_aHaltMethods[pUVM->vm.s.iHaltMethod].pfnWait(pUVM);
1090 LogFlow(("VMR3WaitU: returns %Vrc (FF %#x)\n", rc, pVM ? pVM->fForcedActions : 0));
1091 return rc;
1092}
1093
1094
1095/**
1096 * Changes the halt method.
1097 *
1098 * @returns VBox status code.
1099 * @param pUVM Pointer to the user mode VM structure.
1100 * @param enmHaltMethod The new halt method.
1101 * @thread EMT.
1102 */
1103int vmR3SetHaltMethodU(PUVM pUVM, VMHALTMETHOD enmHaltMethod)
1104{
1105 PVM pVM = pUVM->pVM; Assert(pVM);
1106 VM_ASSERT_EMT(pVM);
1107 AssertReturn(enmHaltMethod > VMHALTMETHOD_INVALID && enmHaltMethod < VMHALTMETHOD_END, VERR_INVALID_PARAMETER);
1108
1109 /*
1110 * Resolve default (can be overridden in the configuration).
1111 */
1112 if (enmHaltMethod == VMHALTMETHOD_DEFAULT)
1113 {
1114 uint32_t u32;
1115 int rc = CFGMR3QueryU32(CFGMR3GetChild(CFGMR3GetRoot(pVM), "VM"), "HaltMethod", &u32);
1116 if (VBOX_SUCCESS(rc))
1117 {
1118 enmHaltMethod = (VMHALTMETHOD)u32;
1119 if (enmHaltMethod <= VMHALTMETHOD_INVALID || enmHaltMethod >= VMHALTMETHOD_END)
1120 return VMSetError(pVM, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("Invalid VM/HaltMethod value %d"), enmHaltMethod);
1121 }
1122 else if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_CHILD_NOT_FOUND)
1123 return VMSetError(pVM, rc, RT_SRC_POS, N_("Failed to Query VM/HaltMethod as uint32_t"));
1124 else
1125 enmHaltMethod = VMHALTMETHOD_GLOBAL_1;
1126 //enmHaltMethod = VMHALTMETHOD_1;
1127 //enmHaltMethod = VMHALTMETHOD_OLD;
1128 }
1129 LogRel(("VM: Halt method %s (%d)\n", vmR3GetHaltMethodName(enmHaltMethod), enmHaltMethod));
1130
1131 /*
1132 * Find the descriptor.
1133 */
1134 unsigned i = 0;
1135 while ( i < RT_ELEMENTS(g_aHaltMethods)
1136 && g_aHaltMethods[i].enmHaltMethod != enmHaltMethod)
1137 i++;
1138 AssertReturn(i < RT_ELEMENTS(g_aHaltMethods), VERR_INVALID_PARAMETER);
1139
1140 /*
1141 * Terminate the old one.
1142 */
1143 if ( pUVM->vm.s.enmHaltMethod != VMHALTMETHOD_INVALID
1144 && g_aHaltMethods[pUVM->vm.s.iHaltMethod].pfnTerm)
1145 {
1146 g_aHaltMethods[pUVM->vm.s.iHaltMethod].pfnTerm(pUVM);
1147 pUVM->vm.s.enmHaltMethod = VMHALTMETHOD_INVALID;
1148 }
1149
1150 /*
1151 * Init the new one.
1152 */
1153 memset(&pUVM->vm.s.Halt, 0, sizeof(pUVM->vm.s.Halt));
1154 if (g_aHaltMethods[i].pfnInit)
1155 {
1156 int rc = g_aHaltMethods[i].pfnInit(pUVM);
1157 AssertRCReturn(rc, rc);
1158 }
1159 pUVM->vm.s.enmHaltMethod = enmHaltMethod;
1160
1161 ASMAtomicWriteU32(&pUVM->vm.s.iHaltMethod, i);
1162 return VINF_SUCCESS;
1163}
1164
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette