VirtualBox

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

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

Action flags breakup.
Fixed PGM saved state loading of 2.2.2 images.
Reduced hacks in PATM state loading (fixups).

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