VirtualBox

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

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

Queue suspend and power off calls from the VM for SMP guests (deadlock prevention).

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