VirtualBox

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

Last change on this file since 69111 was 69111, checked in by vboxsync, 7 years ago

(C) year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 49.3 KB
Line 
1/* $Id: VMEmt.cpp 69111 2017-10-17 14:26:02Z vboxsync $ */
2/** @file
3 * VM - Virtual Machine, The Emulation Thread.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_VM
23#include <VBox/vmm/tm.h>
24#include <VBox/vmm/dbgf.h>
25#include <VBox/vmm/em.h>
26#include <VBox/vmm/pdmapi.h>
27#ifdef VBOX_WITH_REM
28# include <VBox/vmm/rem.h>
29#endif
30#include <VBox/vmm/tm.h>
31#include "VMInternal.h"
32#include <VBox/vmm/vm.h>
33#include <VBox/vmm/uvm.h>
34
35#include <VBox/err.h>
36#include <VBox/log.h>
37#include <iprt/assert.h>
38#include <iprt/asm.h>
39#include <iprt/asm-math.h>
40#include <iprt/semaphore.h>
41#include <iprt/string.h>
42#include <iprt/thread.h>
43#include <iprt/time.h>
44
45
46/*********************************************************************************************************************************
47* Internal Functions *
48*********************************************************************************************************************************/
49int vmR3EmulationThreadWithId(RTTHREAD hThreadSelf, PUVMCPU pUVCpu, VMCPUID idCpu);
50
51
52/**
53 * The emulation thread main function.
54 *
55 * @returns Thread exit code.
56 * @param hThreadSelf The handle to the executing thread.
57 * @param pvArgs Pointer to the user mode per-VCpu structure (UVMPCU).
58 */
59DECLCALLBACK(int) vmR3EmulationThread(RTTHREAD hThreadSelf, void *pvArgs)
60{
61 PUVMCPU pUVCpu = (PUVMCPU)pvArgs;
62 return vmR3EmulationThreadWithId(hThreadSelf, pUVCpu, pUVCpu->idCpu);
63}
64
65
66/**
67 * The emulation thread main function, with Virtual CPU ID for debugging.
68 *
69 * @returns Thread exit code.
70 * @param hThreadSelf The handle to the executing thread.
71 * @param pUVCpu Pointer to the user mode per-VCpu structure.
72 * @param idCpu The virtual CPU ID, for backtrace purposes.
73 */
74int vmR3EmulationThreadWithId(RTTHREAD hThreadSelf, PUVMCPU pUVCpu, VMCPUID idCpu)
75{
76 PUVM pUVM = pUVCpu->pUVM;
77 int rc;
78 RT_NOREF_PV(hThreadSelf);
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 if ( pUVM->pVmm2UserMethods
87 && pUVM->pVmm2UserMethods->pfnNotifyEmtInit)
88 pUVM->pVmm2UserMethods->pfnNotifyEmtInit(pUVM->pVmm2UserMethods, pUVM, pUVCpu);
89
90 /*
91 * The request loop.
92 */
93 rc = VINF_SUCCESS;
94 Log(("vmR3EmulationThread: Emulation thread starting the days work... Thread=%#x pUVM=%p\n", hThreadSelf, pUVM));
95 VMSTATE enmBefore = VMSTATE_CREATED; /* (only used for logging atm.) */
96 ASMAtomicIncU32(&pUVM->vm.s.cActiveEmts);
97 for (;;)
98 {
99 /*
100 * During early init there is no pVM and/or pVCpu, so make a special path
101 * for that to keep things clearly separate.
102 */
103 PVM pVM = pUVM->pVM;
104 PVMCPU pVCpu = pUVCpu->pVCpu;
105 if (!pVCpu || !pVM)
106 {
107 /*
108 * Check for termination first.
109 */
110 if (pUVM->vm.s.fTerminateEMT)
111 {
112 rc = VINF_EM_TERMINATE;
113 break;
114 }
115
116 /*
117 * Only the first VCPU may initialize the VM during early init
118 * and must therefore service all VMCPUID_ANY requests.
119 * See also VMR3Create
120 */
121 if ( (pUVM->vm.s.pNormalReqs || pUVM->vm.s.pPriorityReqs)
122 && pUVCpu->idCpu == 0)
123 {
124 /*
125 * Service execute in any EMT request.
126 */
127 rc = VMR3ReqProcessU(pUVM, VMCPUID_ANY, false /*fPriorityOnly*/);
128 Log(("vmR3EmulationThread: Req rc=%Rrc, VM state %s -> %s\n", rc, VMR3GetStateName(enmBefore), pUVM->pVM ? VMR3GetStateName(pUVM->pVM->enmVMState) : "CREATING"));
129 }
130 else if (pUVCpu->vm.s.pNormalReqs || pUVCpu->vm.s.pPriorityReqs)
131 {
132 /*
133 * Service execute in specific EMT request.
134 */
135 rc = VMR3ReqProcessU(pUVM, pUVCpu->idCpu, false /*fPriorityOnly*/);
136 Log(("vmR3EmulationThread: Req (cpu=%u) rc=%Rrc, VM state %s -> %s\n", pUVCpu->idCpu, rc, VMR3GetStateName(enmBefore), pUVM->pVM ? VMR3GetStateName(pUVM->pVM->enmVMState) : "CREATING"));
137 }
138 else
139 {
140 /*
141 * Nothing important is pending, so wait for something.
142 */
143 rc = VMR3WaitU(pUVCpu);
144 if (RT_FAILURE(rc))
145 {
146 AssertLogRelMsgFailed(("VMR3WaitU failed with %Rrc\n", rc));
147 break;
148 }
149 }
150 }
151 else
152 {
153 /*
154 * Pending requests which needs servicing?
155 *
156 * We check for state changes in addition to status codes when
157 * servicing requests. (Look after the ifs.)
158 */
159 enmBefore = pVM->enmVMState;
160 if (pUVM->vm.s.fTerminateEMT)
161 {
162 rc = VINF_EM_TERMINATE;
163 break;
164 }
165
166 if (VM_FF_IS_PENDING(pVM, VM_FF_EMT_RENDEZVOUS))
167 {
168 rc = VMMR3EmtRendezvousFF(pVM, &pVM->aCpus[idCpu]);
169 Log(("vmR3EmulationThread: Rendezvous rc=%Rrc, VM state %s -> %s\n", rc, VMR3GetStateName(enmBefore), VMR3GetStateName(pVM->enmVMState)));
170 }
171 else if (pUVM->vm.s.pNormalReqs || pUVM->vm.s.pPriorityReqs)
172 {
173 /*
174 * Service execute in any EMT request.
175 */
176 rc = VMR3ReqProcessU(pUVM, VMCPUID_ANY, false /*fPriorityOnly*/);
177 Log(("vmR3EmulationThread: Req rc=%Rrc, VM state %s -> %s\n", rc, VMR3GetStateName(enmBefore), VMR3GetStateName(pVM->enmVMState)));
178 }
179 else if (pUVCpu->vm.s.pNormalReqs || pUVCpu->vm.s.pPriorityReqs)
180 {
181 /*
182 * Service execute in specific EMT request.
183 */
184 rc = VMR3ReqProcessU(pUVM, pUVCpu->idCpu, false /*fPriorityOnly*/);
185 Log(("vmR3EmulationThread: Req (cpu=%u) rc=%Rrc, VM state %s -> %s\n", pUVCpu->idCpu, rc, VMR3GetStateName(enmBefore), VMR3GetStateName(pVM->enmVMState)));
186 }
187 else if ( VM_FF_IS_SET(pVM, VM_FF_DBGF)
188 || VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_DBGF))
189 {
190 /*
191 * Service the debugger request.
192 */
193 rc = DBGFR3VMMForcedAction(pVM, pVCpu);
194 Log(("vmR3EmulationThread: Dbg rc=%Rrc, VM state %s -> %s\n", rc, VMR3GetStateName(enmBefore), VMR3GetStateName(pVM->enmVMState)));
195 }
196 else if (VM_FF_TEST_AND_CLEAR(pVM, VM_FF_RESET))
197 {
198 /*
199 * Service a delayed reset request.
200 */
201 rc = VBOXSTRICTRC_VAL(VMR3ResetFF(pVM));
202 VM_FF_CLEAR(pVM, VM_FF_RESET);
203 Log(("vmR3EmulationThread: Reset rc=%Rrc, VM state %s -> %s\n", rc, VMR3GetStateName(enmBefore), VMR3GetStateName(pVM->enmVMState)));
204 }
205 else
206 {
207 /*
208 * Nothing important is pending, so wait for something.
209 */
210 rc = VMR3WaitU(pUVCpu);
211 if (RT_FAILURE(rc))
212 {
213 AssertLogRelMsgFailed(("VMR3WaitU failed with %Rrc\n", rc));
214 break;
215 }
216 }
217
218 /*
219 * Check for termination requests, these have extremely high priority.
220 */
221 if ( rc == VINF_EM_TERMINATE
222 || pUVM->vm.s.fTerminateEMT)
223 break;
224 }
225
226 /*
227 * Some requests (both VMR3Req* and the DBGF) can potentially resume
228 * or start the VM, in that case we'll get a change in VM status
229 * indicating that we're now running.
230 */
231 if (RT_SUCCESS(rc))
232 {
233 pVM = pUVM->pVM;
234 if (pVM)
235 {
236 pVCpu = &pVM->aCpus[idCpu];
237 if ( pVM->enmVMState == VMSTATE_RUNNING
238 && VMCPUSTATE_IS_STARTED(VMCPU_GET_STATE(pVCpu)))
239 {
240 rc = EMR3ExecuteVM(pVM, pVCpu);
241 Log(("vmR3EmulationThread: EMR3ExecuteVM() -> rc=%Rrc, enmVMState=%d\n", rc, pVM->enmVMState));
242 }
243 }
244 }
245
246 } /* forever */
247
248
249 /*
250 * Decrement the active EMT count if we haven't done it yet in vmR3Destroy.
251 */
252 if (!pUVCpu->vm.s.fBeenThruVmDestroy)
253 ASMAtomicDecU32(&pUVM->vm.s.cActiveEmts);
254
255
256 /*
257 * Cleanup and exit.
258 * EMT0 does the VM destruction after all other EMTs have deregistered and terminated.
259 */
260 Log(("vmR3EmulationThread: Terminating emulation thread! Thread=%#x pUVM=%p rc=%Rrc enmBefore=%d enmVMState=%d\n",
261 hThreadSelf, pUVM, rc, enmBefore, pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_TERMINATED));
262 PVM pVM;
263 if ( idCpu == 0
264 && (pVM = pUVM->pVM) != NULL)
265 {
266 /* Wait for any other EMTs to terminate before we destroy the VM (see vmR3DestroyVM). */
267 for (VMCPUID iCpu = 1; iCpu < pUVM->cCpus; iCpu++)
268 {
269 RTTHREAD hThread;
270 ASMAtomicXchgHandle(&pUVM->aCpus[iCpu].vm.s.ThreadEMT, NIL_RTTHREAD, &hThread);
271 if (hThread != NIL_RTTHREAD)
272 {
273 int rc2 = RTThreadWait(hThread, 5 * RT_MS_1SEC, NULL);
274 AssertLogRelMsgRC(rc2, ("iCpu=%u rc=%Rrc\n", iCpu, rc2));
275 if (RT_FAILURE(rc2))
276 pUVM->aCpus[iCpu].vm.s.ThreadEMT = hThread;
277 }
278 }
279
280 /* Switch to the terminated state, clearing the VM pointer and finally destroy the VM. */
281 vmR3SetTerminated(pVM);
282
283 pUVM->pVM = NULL;
284
285 int rc2 = SUPR3CallVMMR0Ex(pVM->pVMR0, 0 /*idCpu*/, VMMR0_DO_GVMM_DESTROY_VM, 0, NULL);
286 AssertLogRelRC(rc2);
287 }
288 /* Deregister the EMT with VMMR0. */
289 else if ( idCpu != 0
290 && (pVM = pUVM->pVM) != NULL)
291 {
292 int rc2 = SUPR3CallVMMR0Ex(pVM->pVMR0, idCpu, VMMR0_DO_GVMM_DEREGISTER_VMCPU, 0, NULL);
293 AssertLogRelRC(rc2);
294 }
295
296 if ( pUVM->pVmm2UserMethods
297 && pUVM->pVmm2UserMethods->pfnNotifyEmtTerm)
298 pUVM->pVmm2UserMethods->pfnNotifyEmtTerm(pUVM->pVmm2UserMethods, pUVM, pUVCpu);
299
300 pUVCpu->vm.s.NativeThreadEMT = NIL_RTNATIVETHREAD;
301 Log(("vmR3EmulationThread: EMT is terminated.\n"));
302 return rc;
303}
304
305
306/**
307 * Gets the name of a halt method.
308 *
309 * @returns Pointer to a read only string.
310 * @param enmMethod The method.
311 */
312static const char *vmR3GetHaltMethodName(VMHALTMETHOD enmMethod)
313{
314 switch (enmMethod)
315 {
316 case VMHALTMETHOD_BOOTSTRAP: return "bootstrap";
317 case VMHALTMETHOD_DEFAULT: return "default";
318 case VMHALTMETHOD_OLD: return "old";
319 case VMHALTMETHOD_1: return "method1";
320 //case VMHALTMETHOD_2: return "method2";
321 case VMHALTMETHOD_GLOBAL_1: return "global1";
322 default: return "unknown";
323 }
324}
325
326
327/**
328 * Signal a fatal wait error.
329 *
330 * @returns Fatal error code to be propagated up the call stack.
331 * @param pUVCpu The user mode per CPU structure of the calling
332 * EMT.
333 * @param pszFmt The error format with a single %Rrc in it.
334 * @param rcFmt The status code to format.
335 */
336static int vmR3FatalWaitError(PUVMCPU pUVCpu, const char *pszFmt, int rcFmt)
337{
338 /** @todo This is wrong ... raise a fatal error / guru meditation
339 * instead. */
340 AssertLogRelMsgFailed((pszFmt, rcFmt));
341 ASMAtomicUoWriteBool(&pUVCpu->pUVM->vm.s.fTerminateEMT, true);
342 if (pUVCpu->pVM)
343 VM_FF_SET(pUVCpu->pVM, VM_FF_CHECK_VM_STATE);
344 return VERR_VM_FATAL_WAIT_ERROR;
345}
346
347
348/**
349 * The old halt loop.
350 */
351static DECLCALLBACK(int) vmR3HaltOldDoHalt(PUVMCPU pUVCpu, const uint32_t fMask, uint64_t /* u64Now*/)
352{
353 /*
354 * Halt loop.
355 */
356 PVM pVM = pUVCpu->pVM;
357 PVMCPU pVCpu = pUVCpu->pVCpu;
358
359 int rc = VINF_SUCCESS;
360 ASMAtomicWriteBool(&pUVCpu->vm.s.fWait, true);
361 //unsigned cLoops = 0;
362 for (;;)
363 {
364 /*
365 * Work the timers and check if we can exit.
366 * The poll call gives us the ticks left to the next event in
367 * addition to perhaps set an FF.
368 */
369 uint64_t const u64StartTimers = RTTimeNanoTS();
370 TMR3TimerQueuesDo(pVM);
371 uint64_t const cNsElapsedTimers = RTTimeNanoTS() - u64StartTimers;
372 STAM_REL_PROFILE_ADD_PERIOD(&pUVCpu->vm.s.StatHaltTimers, cNsElapsedTimers);
373 if ( VM_FF_IS_PENDING(pVM, VM_FF_EXTERNAL_HALTED_MASK)
374 || VMCPU_FF_IS_PENDING(pVCpu, fMask))
375 break;
376 uint64_t u64NanoTS;
377 TMTimerPollGIP(pVM, pVCpu, &u64NanoTS);
378 if ( VM_FF_IS_PENDING(pVM, VM_FF_EXTERNAL_HALTED_MASK)
379 || VMCPU_FF_IS_PENDING(pVCpu, fMask))
380 break;
381
382 /*
383 * Wait for a while. Someone will wake us up or interrupt the call if
384 * anything needs our attention.
385 */
386 if (u64NanoTS < 50000)
387 {
388 //RTLogPrintf("u64NanoTS=%RI64 cLoops=%d spin\n", u64NanoTS, cLoops++);
389 /* spin */;
390 }
391 else
392 {
393 VMMR3YieldStop(pVM);
394 //uint64_t u64Start = RTTimeNanoTS();
395 if (u64NanoTS < 870000) /* this is a bit speculative... works fine on linux. */
396 {
397 //RTLogPrintf("u64NanoTS=%RI64 cLoops=%d yield", u64NanoTS, cLoops++);
398 uint64_t const u64StartSchedYield = RTTimeNanoTS();
399 RTThreadYield(); /* this is the best we can do here */
400 uint64_t const cNsElapsedSchedYield = RTTimeNanoTS() - u64StartSchedYield;
401 STAM_REL_PROFILE_ADD_PERIOD(&pUVCpu->vm.s.StatHaltYield, cNsElapsedSchedYield);
402 }
403 else if (u64NanoTS < 2000000)
404 {
405 //RTLogPrintf("u64NanoTS=%RI64 cLoops=%d sleep 1ms", u64NanoTS, cLoops++);
406 uint64_t const u64StartSchedHalt = RTTimeNanoTS();
407 rc = RTSemEventWait(pUVCpu->vm.s.EventSemWait, 1);
408 uint64_t const cNsElapsedSchedHalt = RTTimeNanoTS() - u64StartSchedHalt;
409 STAM_REL_PROFILE_ADD_PERIOD(&pUVCpu->vm.s.StatHaltBlock, cNsElapsedSchedHalt);
410 }
411 else
412 {
413 //RTLogPrintf("u64NanoTS=%RI64 cLoops=%d sleep %dms", u64NanoTS, cLoops++, (uint32_t)RT_MIN((u64NanoTS - 500000) / 1000000, 15));
414 uint64_t const u64StartSchedHalt = RTTimeNanoTS();
415 rc = RTSemEventWait(pUVCpu->vm.s.EventSemWait, RT_MIN((u64NanoTS - 1000000) / 1000000, 15));
416 uint64_t const cNsElapsedSchedHalt = RTTimeNanoTS() - u64StartSchedHalt;
417 STAM_REL_PROFILE_ADD_PERIOD(&pUVCpu->vm.s.StatHaltBlock, cNsElapsedSchedHalt);
418 }
419 //uint64_t u64Slept = RTTimeNanoTS() - u64Start;
420 //RTLogPrintf(" -> rc=%Rrc in %RU64 ns / %RI64 ns delta\n", rc, u64Slept, u64NanoTS - u64Slept);
421 }
422 if (rc == VERR_TIMEOUT)
423 rc = VINF_SUCCESS;
424 else if (RT_FAILURE(rc))
425 {
426 rc = vmR3FatalWaitError(pUVCpu, "RTSemEventWait->%Rrc\n", rc);
427 break;
428 }
429 }
430
431 ASMAtomicUoWriteBool(&pUVCpu->vm.s.fWait, false);
432 return rc;
433}
434
435
436/**
437 * Initialize the configuration of halt method 1 & 2.
438 *
439 * @return VBox status code. Failure on invalid CFGM data.
440 * @param pUVM The user mode VM structure.
441 */
442static int vmR3HaltMethod12ReadConfigU(PUVM pUVM)
443{
444 /*
445 * The defaults.
446 */
447#if 1 /* DEBUGGING STUFF - REMOVE LATER */
448 pUVM->vm.s.Halt.Method12.u32LagBlockIntervalDivisorCfg = 4;
449 pUVM->vm.s.Halt.Method12.u32MinBlockIntervalCfg = 2*1000000;
450 pUVM->vm.s.Halt.Method12.u32MaxBlockIntervalCfg = 75*1000000;
451 pUVM->vm.s.Halt.Method12.u32StartSpinningCfg = 30*1000000;
452 pUVM->vm.s.Halt.Method12.u32StopSpinningCfg = 20*1000000;
453#else
454 pUVM->vm.s.Halt.Method12.u32LagBlockIntervalDivisorCfg = 4;
455 pUVM->vm.s.Halt.Method12.u32MinBlockIntervalCfg = 5*1000000;
456 pUVM->vm.s.Halt.Method12.u32MaxBlockIntervalCfg = 200*1000000;
457 pUVM->vm.s.Halt.Method12.u32StartSpinningCfg = 20*1000000;
458 pUVM->vm.s.Halt.Method12.u32StopSpinningCfg = 2*1000000;
459#endif
460
461 /*
462 * Query overrides.
463 *
464 * I don't have time to bother with niceties such as invalid value checks
465 * here right now. sorry.
466 */
467 PCFGMNODE pCfg = CFGMR3GetChild(CFGMR3GetRoot(pUVM->pVM), "/VMM/HaltedMethod1");
468 if (pCfg)
469 {
470 uint32_t u32;
471 if (RT_SUCCESS(CFGMR3QueryU32(pCfg, "LagBlockIntervalDivisor", &u32)))
472 pUVM->vm.s.Halt.Method12.u32LagBlockIntervalDivisorCfg = u32;
473 if (RT_SUCCESS(CFGMR3QueryU32(pCfg, "MinBlockInterval", &u32)))
474 pUVM->vm.s.Halt.Method12.u32MinBlockIntervalCfg = u32;
475 if (RT_SUCCESS(CFGMR3QueryU32(pCfg, "MaxBlockInterval", &u32)))
476 pUVM->vm.s.Halt.Method12.u32MaxBlockIntervalCfg = u32;
477 if (RT_SUCCESS(CFGMR3QueryU32(pCfg, "StartSpinning", &u32)))
478 pUVM->vm.s.Halt.Method12.u32StartSpinningCfg = u32;
479 if (RT_SUCCESS(CFGMR3QueryU32(pCfg, "StopSpinning", &u32)))
480 pUVM->vm.s.Halt.Method12.u32StopSpinningCfg = u32;
481 LogRel(("VMEmt: HaltedMethod1 config: %d/%d/%d/%d/%d\n",
482 pUVM->vm.s.Halt.Method12.u32LagBlockIntervalDivisorCfg,
483 pUVM->vm.s.Halt.Method12.u32MinBlockIntervalCfg,
484 pUVM->vm.s.Halt.Method12.u32MaxBlockIntervalCfg,
485 pUVM->vm.s.Halt.Method12.u32StartSpinningCfg,
486 pUVM->vm.s.Halt.Method12.u32StopSpinningCfg));
487 }
488
489 return VINF_SUCCESS;
490}
491
492
493/**
494 * Initialize halt method 1.
495 *
496 * @return VBox status code.
497 * @param pUVM Pointer to the user mode VM structure.
498 */
499static DECLCALLBACK(int) vmR3HaltMethod1Init(PUVM pUVM)
500{
501 return vmR3HaltMethod12ReadConfigU(pUVM);
502}
503
504
505/**
506 * Method 1 - Block whenever possible, and when lagging behind
507 * switch to spinning for 10-30ms with occasional blocking until
508 * the lag has been eliminated.
509 */
510static DECLCALLBACK(int) vmR3HaltMethod1Halt(PUVMCPU pUVCpu, const uint32_t fMask, uint64_t u64Now)
511{
512 PUVM pUVM = pUVCpu->pUVM;
513 PVMCPU pVCpu = pUVCpu->pVCpu;
514 PVM pVM = pUVCpu->pVM;
515
516 /*
517 * To simplify things, we decide up-front whether we should switch to spinning or
518 * not. This makes some ASSUMPTIONS about the cause of the spinning (PIT/RTC/PCNet)
519 * and that it will generate interrupts or other events that will cause us to exit
520 * the halt loop.
521 */
522 bool fBlockOnce = false;
523 bool fSpinning = false;
524 uint32_t u32CatchUpPct = TMVirtualSyncGetCatchUpPct(pVM);
525 if (u32CatchUpPct /* non-zero if catching up */)
526 {
527 if (pUVCpu->vm.s.Halt.Method12.u64StartSpinTS)
528 {
529 fSpinning = TMVirtualSyncGetLag(pVM) >= pUVM->vm.s.Halt.Method12.u32StopSpinningCfg;
530 if (fSpinning)
531 {
532 uint64_t u64Lag = TMVirtualSyncGetLag(pVM);
533 fBlockOnce = u64Now - pUVCpu->vm.s.Halt.Method12.u64LastBlockTS
534 > RT_MAX(pUVM->vm.s.Halt.Method12.u32MinBlockIntervalCfg,
535 RT_MIN(u64Lag / pUVM->vm.s.Halt.Method12.u32LagBlockIntervalDivisorCfg,
536 pUVM->vm.s.Halt.Method12.u32MaxBlockIntervalCfg));
537 }
538 else
539 {
540 //RTLogRelPrintf("Stopped spinning (%u ms)\n", (u64Now - pUVCpu->vm.s.Halt.Method12.u64StartSpinTS) / 1000000);
541 pUVCpu->vm.s.Halt.Method12.u64StartSpinTS = 0;
542 }
543 }
544 else
545 {
546 fSpinning = TMVirtualSyncGetLag(pVM) >= pUVM->vm.s.Halt.Method12.u32StartSpinningCfg;
547 if (fSpinning)
548 pUVCpu->vm.s.Halt.Method12.u64StartSpinTS = u64Now;
549 }
550 }
551 else if (pUVCpu->vm.s.Halt.Method12.u64StartSpinTS)
552 {
553 //RTLogRelPrintf("Stopped spinning (%u ms)\n", (u64Now - pUVCpu->vm.s.Halt.Method12.u64StartSpinTS) / 1000000);
554 pUVCpu->vm.s.Halt.Method12.u64StartSpinTS = 0;
555 }
556
557 /*
558 * Halt loop.
559 */
560 int rc = VINF_SUCCESS;
561 ASMAtomicWriteBool(&pUVCpu->vm.s.fWait, true);
562 unsigned cLoops = 0;
563 for (;; cLoops++)
564 {
565 /*
566 * Work the timers and check if we can exit.
567 */
568 uint64_t const u64StartTimers = RTTimeNanoTS();
569 TMR3TimerQueuesDo(pVM);
570 uint64_t const cNsElapsedTimers = RTTimeNanoTS() - u64StartTimers;
571 STAM_REL_PROFILE_ADD_PERIOD(&pUVCpu->vm.s.StatHaltTimers, cNsElapsedTimers);
572 if ( VM_FF_IS_PENDING(pVM, VM_FF_EXTERNAL_HALTED_MASK)
573 || VMCPU_FF_IS_PENDING(pVCpu, fMask))
574 break;
575
576 /*
577 * Estimate time left to the next event.
578 */
579 uint64_t u64NanoTS;
580 TMTimerPollGIP(pVM, pVCpu, &u64NanoTS);
581 if ( VM_FF_IS_PENDING(pVM, VM_FF_EXTERNAL_HALTED_MASK)
582 || VMCPU_FF_IS_PENDING(pVCpu, fMask))
583 break;
584
585 /*
586 * Block if we're not spinning and the interval isn't all that small.
587 */
588 if ( ( !fSpinning
589 || fBlockOnce)
590#if 1 /* DEBUGGING STUFF - REMOVE LATER */
591 && u64NanoTS >= 100000) /* 0.100 ms */
592#else
593 && u64NanoTS >= 250000) /* 0.250 ms */
594#endif
595 {
596 const uint64_t Start = pUVCpu->vm.s.Halt.Method12.u64LastBlockTS = RTTimeNanoTS();
597 VMMR3YieldStop(pVM);
598
599 uint32_t cMilliSecs = RT_MIN(u64NanoTS / 1000000, 15);
600 if (cMilliSecs <= pUVCpu->vm.s.Halt.Method12.cNSBlockedTooLongAvg)
601 cMilliSecs = 1;
602 else
603 cMilliSecs -= pUVCpu->vm.s.Halt.Method12.cNSBlockedTooLongAvg;
604
605 //RTLogRelPrintf("u64NanoTS=%RI64 cLoops=%3d sleep %02dms (%7RU64) ", u64NanoTS, cLoops, cMilliSecs, u64NanoTS);
606 uint64_t const u64StartSchedHalt = RTTimeNanoTS();
607 rc = RTSemEventWait(pUVCpu->vm.s.EventSemWait, cMilliSecs);
608 uint64_t const cNsElapsedSchedHalt = RTTimeNanoTS() - u64StartSchedHalt;
609 STAM_REL_PROFILE_ADD_PERIOD(&pUVCpu->vm.s.StatHaltBlock, cNsElapsedSchedHalt);
610
611 if (rc == VERR_TIMEOUT)
612 rc = VINF_SUCCESS;
613 else if (RT_FAILURE(rc))
614 {
615 rc = vmR3FatalWaitError(pUVCpu, "RTSemEventWait->%Rrc\n", rc);
616 break;
617 }
618
619 /*
620 * Calc the statistics.
621 * Update averages every 16th time, and flush parts of the history every 64th time.
622 */
623 const uint64_t Elapsed = RTTimeNanoTS() - Start;
624 pUVCpu->vm.s.Halt.Method12.cNSBlocked += Elapsed;
625 if (Elapsed > u64NanoTS)
626 pUVCpu->vm.s.Halt.Method12.cNSBlockedTooLong += Elapsed - u64NanoTS;
627 pUVCpu->vm.s.Halt.Method12.cBlocks++;
628 if (!(pUVCpu->vm.s.Halt.Method12.cBlocks & 0xf))
629 {
630 pUVCpu->vm.s.Halt.Method12.cNSBlockedTooLongAvg = pUVCpu->vm.s.Halt.Method12.cNSBlockedTooLong / pUVCpu->vm.s.Halt.Method12.cBlocks;
631 if (!(pUVCpu->vm.s.Halt.Method12.cBlocks & 0x3f))
632 {
633 pUVCpu->vm.s.Halt.Method12.cNSBlockedTooLong = pUVCpu->vm.s.Halt.Method12.cNSBlockedTooLongAvg * 0x40;
634 pUVCpu->vm.s.Halt.Method12.cBlocks = 0x40;
635 }
636 }
637 //RTLogRelPrintf(" -> %7RU64 ns / %7RI64 ns delta%s\n", Elapsed, Elapsed - u64NanoTS, fBlockOnce ? " (block once)" : "");
638
639 /*
640 * Clear the block once flag if we actually blocked.
641 */
642 if ( fBlockOnce
643 && Elapsed > 100000 /* 0.1 ms */)
644 fBlockOnce = false;
645 }
646 }
647 //if (fSpinning) RTLogRelPrintf("spun for %RU64 ns %u loops; lag=%RU64 pct=%d\n", RTTimeNanoTS() - u64Now, cLoops, TMVirtualSyncGetLag(pVM), u32CatchUpPct);
648
649 ASMAtomicUoWriteBool(&pUVCpu->vm.s.fWait, false);
650 return rc;
651}
652
653
654/**
655 * Initialize the global 1 halt method.
656 *
657 * @return VBox status code.
658 * @param pUVM Pointer to the user mode VM structure.
659 */
660static DECLCALLBACK(int) vmR3HaltGlobal1Init(PUVM pUVM)
661{
662 /*
663 * The defaults.
664 */
665 uint32_t cNsResolution = SUPSemEventMultiGetResolution(pUVM->vm.s.pSession);
666 if (cNsResolution > 5*RT_NS_100US)
667 pUVM->vm.s.Halt.Global1.cNsSpinBlockThresholdCfg = 50000;
668 else if (cNsResolution > RT_NS_100US)
669 pUVM->vm.s.Halt.Global1.cNsSpinBlockThresholdCfg = cNsResolution / 4;
670 else
671 pUVM->vm.s.Halt.Global1.cNsSpinBlockThresholdCfg = 2000;
672
673 /*
674 * Query overrides.
675 *
676 * I don't have time to bother with niceties such as invalid value checks
677 * here right now. sorry.
678 */
679 PCFGMNODE pCfg = CFGMR3GetChild(CFGMR3GetRoot(pUVM->pVM), "/VMM/HaltedGlobal1");
680 if (pCfg)
681 {
682 uint32_t u32;
683 if (RT_SUCCESS(CFGMR3QueryU32(pCfg, "SpinBlockThreshold", &u32)))
684 pUVM->vm.s.Halt.Global1.cNsSpinBlockThresholdCfg = u32;
685 }
686 LogRel(("VMEmt: HaltedGlobal1 config: cNsSpinBlockThresholdCfg=%u\n",
687 pUVM->vm.s.Halt.Global1.cNsSpinBlockThresholdCfg));
688 return VINF_SUCCESS;
689}
690
691
692/**
693 * The global 1 halt method - Block in GMM (ring-0) and let it
694 * try take care of the global scheduling of EMT threads.
695 */
696static DECLCALLBACK(int) vmR3HaltGlobal1Halt(PUVMCPU pUVCpu, const uint32_t fMask, uint64_t u64Now)
697{
698 PUVM pUVM = pUVCpu->pUVM;
699 PVMCPU pVCpu = pUVCpu->pVCpu;
700 PVM pVM = pUVCpu->pVM;
701 Assert(VMMGetCpu(pVM) == pVCpu);
702 NOREF(u64Now);
703
704 /*
705 * Halt loop.
706 */
707 //uint64_t u64NowLog, u64Start;
708 //u64Start = u64NowLog = RTTimeNanoTS();
709 int rc = VINF_SUCCESS;
710 ASMAtomicWriteBool(&pUVCpu->vm.s.fWait, true);
711 unsigned cLoops = 0;
712 for (;; cLoops++)
713 {
714 /*
715 * Work the timers and check if we can exit.
716 */
717 uint64_t const u64StartTimers = RTTimeNanoTS();
718 TMR3TimerQueuesDo(pVM);
719 uint64_t const cNsElapsedTimers = RTTimeNanoTS() - u64StartTimers;
720 STAM_REL_PROFILE_ADD_PERIOD(&pUVCpu->vm.s.StatHaltTimers, cNsElapsedTimers);
721 if ( VM_FF_IS_PENDING(pVM, VM_FF_EXTERNAL_HALTED_MASK)
722 || VMCPU_FF_IS_PENDING(pVCpu, fMask))
723 break;
724
725 /*
726 * Estimate time left to the next event.
727 */
728 //u64NowLog = RTTimeNanoTS();
729 uint64_t u64Delta;
730 uint64_t u64GipTime = TMTimerPollGIP(pVM, pVCpu, &u64Delta);
731 if ( VM_FF_IS_PENDING(pVM, VM_FF_EXTERNAL_HALTED_MASK)
732 || VMCPU_FF_IS_PENDING(pVCpu, fMask))
733 break;
734
735 /*
736 * Block if we're not spinning and the interval isn't all that small.
737 */
738 if (u64Delta >= pUVM->vm.s.Halt.Global1.cNsSpinBlockThresholdCfg)
739 {
740 VMMR3YieldStop(pVM);
741 if ( VM_FF_IS_PENDING(pVM, VM_FF_EXTERNAL_HALTED_MASK)
742 || VMCPU_FF_IS_PENDING(pVCpu, fMask))
743 break;
744
745 //RTLogPrintf("loop=%-3d u64GipTime=%'llu / %'llu now=%'llu / %'llu\n", cLoops, u64GipTime, u64Delta, u64NowLog, u64GipTime - u64NowLog);
746 uint64_t const u64StartSchedHalt = RTTimeNanoTS();
747 rc = SUPR3CallVMMR0Ex(pVM->pVMR0, pVCpu->idCpu, VMMR0_DO_GVMM_SCHED_HALT, u64GipTime, NULL);
748 uint64_t const u64EndSchedHalt = RTTimeNanoTS();
749 uint64_t const cNsElapsedSchedHalt = u64EndSchedHalt - u64StartSchedHalt;
750 STAM_REL_PROFILE_ADD_PERIOD(&pUVCpu->vm.s.StatHaltBlock, cNsElapsedSchedHalt);
751
752 if (rc == VERR_INTERRUPTED)
753 rc = VINF_SUCCESS;
754 else if (RT_FAILURE(rc))
755 {
756 rc = vmR3FatalWaitError(pUVCpu, "vmR3HaltGlobal1Halt: VMMR0_DO_GVMM_SCHED_HALT->%Rrc\n", rc);
757 break;
758 }
759 else
760 {
761 int64_t const cNsOverslept = u64EndSchedHalt - u64GipTime;
762 if (cNsOverslept > 50000)
763 STAM_PROFILE_ADD_PERIOD(&pUVCpu->vm.s.StatHaltBlockOverslept, cNsOverslept);
764 else if (cNsOverslept < -50000)
765 STAM_PROFILE_ADD_PERIOD(&pUVCpu->vm.s.StatHaltBlockInsomnia, cNsElapsedSchedHalt);
766 else
767 STAM_PROFILE_ADD_PERIOD(&pUVCpu->vm.s.StatHaltBlockOnTime, cNsElapsedSchedHalt);
768 }
769 }
770 /*
771 * When spinning call upon the GVMM and do some wakups once
772 * in a while, it's not like we're actually busy or anything.
773 */
774 else if (!(cLoops & 0x1fff))
775 {
776 uint64_t const u64StartSchedYield = RTTimeNanoTS();
777 rc = SUPR3CallVMMR0Ex(pVM->pVMR0, pVCpu->idCpu, VMMR0_DO_GVMM_SCHED_POLL, false /* don't yield */, NULL);
778 uint64_t const cNsElapsedSchedYield = RTTimeNanoTS() - u64StartSchedYield;
779 STAM_REL_PROFILE_ADD_PERIOD(&pUVCpu->vm.s.StatHaltYield, cNsElapsedSchedYield);
780 }
781 }
782 //RTLogPrintf("*** %u loops %'llu; lag=%RU64\n", cLoops, u64NowLog - u64Start, TMVirtualSyncGetLag(pVM));
783
784 ASMAtomicUoWriteBool(&pUVCpu->vm.s.fWait, false);
785 return rc;
786}
787
788
789/**
790 * The global 1 halt method - VMR3Wait() worker.
791 *
792 * @returns VBox status code.
793 * @param pUVCpu Pointer to the user mode VMCPU structure.
794 */
795static DECLCALLBACK(int) vmR3HaltGlobal1Wait(PUVMCPU pUVCpu)
796{
797 ASMAtomicWriteBool(&pUVCpu->vm.s.fWait, true);
798
799 PVM pVM = pUVCpu->pUVM->pVM;
800 PVMCPU pVCpu = VMMGetCpu(pVM);
801 Assert(pVCpu->idCpu == pUVCpu->idCpu);
802
803 int rc = VINF_SUCCESS;
804 for (;;)
805 {
806 /*
807 * Check Relevant FFs.
808 */
809 if ( VM_FF_IS_PENDING(pVM, VM_FF_EXTERNAL_SUSPENDED_MASK)
810 || VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_EXTERNAL_SUSPENDED_MASK))
811 break;
812
813 /*
814 * Wait for a while. Someone will wake us up or interrupt the call if
815 * anything needs our attention.
816 */
817 rc = SUPR3CallVMMR0Ex(pVM->pVMR0, pVCpu->idCpu, VMMR0_DO_GVMM_SCHED_HALT, RTTimeNanoTS() + 1000000000 /* +1s */, NULL);
818 if (rc == VERR_INTERRUPTED)
819 rc = VINF_SUCCESS;
820 else if (RT_FAILURE(rc))
821 {
822 rc = vmR3FatalWaitError(pUVCpu, "vmR3HaltGlobal1Wait: VMMR0_DO_GVMM_SCHED_HALT->%Rrc\n", rc);
823 break;
824 }
825 }
826
827 ASMAtomicUoWriteBool(&pUVCpu->vm.s.fWait, false);
828 return rc;
829}
830
831
832/**
833 * The global 1 halt method - VMR3NotifyFF() worker.
834 *
835 * @param pUVCpu Pointer to the user mode VMCPU structure.
836 * @param fFlags Notification flags, VMNOTIFYFF_FLAGS_*.
837 */
838static DECLCALLBACK(void) vmR3HaltGlobal1NotifyCpuFF(PUVMCPU pUVCpu, uint32_t fFlags)
839{
840 if (pUVCpu->vm.s.fWait)
841 {
842 int rc = SUPR3CallVMMR0Ex(pUVCpu->pVM->pVMR0, pUVCpu->idCpu, VMMR0_DO_GVMM_SCHED_WAKE_UP, 0, NULL);
843 AssertRC(rc);
844 }
845 else if ( ( (fFlags & VMNOTIFYFF_FLAGS_POKE)
846 || !(fFlags & VMNOTIFYFF_FLAGS_DONE_REM))
847 && pUVCpu->pVCpu)
848 {
849 VMCPUSTATE enmState = VMCPU_GET_STATE(pUVCpu->pVCpu);
850 if (enmState == VMCPUSTATE_STARTED_EXEC)
851 {
852 if (fFlags & VMNOTIFYFF_FLAGS_POKE)
853 {
854 int rc = SUPR3CallVMMR0Ex(pUVCpu->pVM->pVMR0, pUVCpu->idCpu, VMMR0_DO_GVMM_SCHED_POKE, 0, NULL);
855 AssertRC(rc);
856 }
857 }
858#ifdef VBOX_WITH_REM
859 else if (enmState == VMCPUSTATE_STARTED_EXEC_REM)
860 {
861 if (!(fFlags & VMNOTIFYFF_FLAGS_DONE_REM))
862 REMR3NotifyFF(pUVCpu->pVM);
863 }
864#endif
865 }
866}
867
868
869/**
870 * Bootstrap VMR3Wait() worker.
871 *
872 * @returns VBox status code.
873 * @param pUVCpu Pointer to the user mode VMCPU structure.
874 */
875static DECLCALLBACK(int) vmR3BootstrapWait(PUVMCPU pUVCpu)
876{
877 PUVM pUVM = pUVCpu->pUVM;
878
879 ASMAtomicWriteBool(&pUVCpu->vm.s.fWait, true);
880
881 int rc = VINF_SUCCESS;
882 for (;;)
883 {
884 /*
885 * Check Relevant FFs.
886 */
887 if (pUVM->vm.s.pNormalReqs || pUVM->vm.s.pPriorityReqs) /* global requests pending? */
888 break;
889 if (pUVCpu->vm.s.pNormalReqs || pUVCpu->vm.s.pPriorityReqs) /* local requests pending? */
890 break;
891
892 if ( pUVCpu->pVM
893 && ( VM_FF_IS_PENDING(pUVCpu->pVM, VM_FF_EXTERNAL_SUSPENDED_MASK)
894 || VMCPU_FF_IS_PENDING(VMMGetCpu(pUVCpu->pVM), VMCPU_FF_EXTERNAL_SUSPENDED_MASK)
895 )
896 )
897 break;
898 if (pUVM->vm.s.fTerminateEMT)
899 break;
900
901 /*
902 * Wait for a while. Someone will wake us up or interrupt the call if
903 * anything needs our attention.
904 */
905 rc = RTSemEventWait(pUVCpu->vm.s.EventSemWait, 1000);
906 if (rc == VERR_TIMEOUT)
907 rc = VINF_SUCCESS;
908 else if (RT_FAILURE(rc))
909 {
910 rc = vmR3FatalWaitError(pUVCpu, "RTSemEventWait->%Rrc\n", rc);
911 break;
912 }
913 }
914
915 ASMAtomicUoWriteBool(&pUVCpu->vm.s.fWait, false);
916 return rc;
917}
918
919
920/**
921 * Bootstrap VMR3NotifyFF() worker.
922 *
923 * @param pUVCpu Pointer to the user mode VMCPU structure.
924 * @param fFlags Notification flags, VMNOTIFYFF_FLAGS_*.
925 */
926static DECLCALLBACK(void) vmR3BootstrapNotifyCpuFF(PUVMCPU pUVCpu, uint32_t fFlags)
927{
928 if (pUVCpu->vm.s.fWait)
929 {
930 int rc = RTSemEventSignal(pUVCpu->vm.s.EventSemWait);
931 AssertRC(rc);
932 }
933 NOREF(fFlags);
934}
935
936
937/**
938 * Default VMR3Wait() worker.
939 *
940 * @returns VBox status code.
941 * @param pUVCpu Pointer to the user mode VMCPU structure.
942 */
943static DECLCALLBACK(int) vmR3DefaultWait(PUVMCPU pUVCpu)
944{
945 ASMAtomicWriteBool(&pUVCpu->vm.s.fWait, true);
946
947 PVM pVM = pUVCpu->pVM;
948 PVMCPU pVCpu = pUVCpu->pVCpu;
949 int rc = VINF_SUCCESS;
950 for (;;)
951 {
952 /*
953 * Check Relevant FFs.
954 */
955 if ( VM_FF_IS_PENDING(pVM, VM_FF_EXTERNAL_SUSPENDED_MASK)
956 || VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_EXTERNAL_SUSPENDED_MASK))
957 break;
958
959 /*
960 * Wait for a while. Someone will wake us up or interrupt the call if
961 * anything needs our attention.
962 */
963 rc = RTSemEventWait(pUVCpu->vm.s.EventSemWait, 1000);
964 if (rc == VERR_TIMEOUT)
965 rc = VINF_SUCCESS;
966 else if (RT_FAILURE(rc))
967 {
968 rc = vmR3FatalWaitError(pUVCpu, "RTSemEventWait->%Rrc", rc);
969 break;
970 }
971 }
972
973 ASMAtomicUoWriteBool(&pUVCpu->vm.s.fWait, false);
974 return rc;
975}
976
977
978/**
979 * Default VMR3NotifyFF() worker.
980 *
981 * @param pUVCpu Pointer to the user mode VMCPU structure.
982 * @param fFlags Notification flags, VMNOTIFYFF_FLAGS_*.
983 */
984static DECLCALLBACK(void) vmR3DefaultNotifyCpuFF(PUVMCPU pUVCpu, uint32_t fFlags)
985{
986 if (pUVCpu->vm.s.fWait)
987 {
988 int rc = RTSemEventSignal(pUVCpu->vm.s.EventSemWait);
989 AssertRC(rc);
990 }
991#ifdef VBOX_WITH_REM
992 else if ( !(fFlags & VMNOTIFYFF_FLAGS_DONE_REM)
993 && pUVCpu->pVCpu
994 && pUVCpu->pVCpu->enmState == VMCPUSTATE_STARTED_EXEC_REM)
995 REMR3NotifyFF(pUVCpu->pVM);
996#else
997 RT_NOREF(fFlags);
998#endif
999}
1000
1001
1002/**
1003 * Array with halt method descriptors.
1004 * VMINT::iHaltMethod contains an index into this array.
1005 */
1006static const struct VMHALTMETHODDESC
1007{
1008 /** The halt method id. */
1009 VMHALTMETHOD enmHaltMethod;
1010 /** The init function for loading config and initialize variables. */
1011 DECLR3CALLBACKMEMBER(int, pfnInit,(PUVM pUVM));
1012 /** The term function. */
1013 DECLR3CALLBACKMEMBER(void, pfnTerm,(PUVM pUVM));
1014 /** The VMR3WaitHaltedU function. */
1015 DECLR3CALLBACKMEMBER(int, pfnHalt,(PUVMCPU pUVCpu, const uint32_t fMask, uint64_t u64Now));
1016 /** The VMR3WaitU function. */
1017 DECLR3CALLBACKMEMBER(int, pfnWait,(PUVMCPU pUVCpu));
1018 /** The VMR3NotifyCpuFFU function. */
1019 DECLR3CALLBACKMEMBER(void, pfnNotifyCpuFF,(PUVMCPU pUVCpu, uint32_t fFlags));
1020 /** The VMR3NotifyGlobalFFU function. */
1021 DECLR3CALLBACKMEMBER(void, pfnNotifyGlobalFF,(PUVM pUVM, uint32_t fFlags));
1022} g_aHaltMethods[] =
1023{
1024 { VMHALTMETHOD_BOOTSTRAP, NULL, NULL, NULL, vmR3BootstrapWait, vmR3BootstrapNotifyCpuFF, NULL },
1025 { VMHALTMETHOD_OLD, NULL, NULL, vmR3HaltOldDoHalt, vmR3DefaultWait, vmR3DefaultNotifyCpuFF, NULL },
1026 { VMHALTMETHOD_1, vmR3HaltMethod1Init, NULL, vmR3HaltMethod1Halt, vmR3DefaultWait, vmR3DefaultNotifyCpuFF, NULL },
1027 { VMHALTMETHOD_GLOBAL_1, vmR3HaltGlobal1Init, NULL, vmR3HaltGlobal1Halt, vmR3HaltGlobal1Wait, vmR3HaltGlobal1NotifyCpuFF, NULL },
1028};
1029
1030
1031/**
1032 * Notify the emulation thread (EMT) about pending Forced Action (FF).
1033 *
1034 * This function is called by thread other than EMT to make
1035 * sure EMT wakes up and promptly service an FF request.
1036 *
1037 * @param pUVM Pointer to the user mode VM structure.
1038 * @param fFlags Notification flags, VMNOTIFYFF_FLAGS_*.
1039 * @internal
1040 */
1041VMMR3_INT_DECL(void) VMR3NotifyGlobalFFU(PUVM pUVM, uint32_t fFlags)
1042{
1043 LogFlow(("VMR3NotifyGlobalFFU:\n"));
1044 uint32_t iHaldMethod = pUVM->vm.s.iHaltMethod;
1045
1046 if (g_aHaltMethods[iHaldMethod].pfnNotifyGlobalFF) /** @todo make mandatory. */
1047 g_aHaltMethods[iHaldMethod].pfnNotifyGlobalFF(pUVM, fFlags);
1048 else
1049 for (VMCPUID iCpu = 0; iCpu < pUVM->cCpus; iCpu++)
1050 g_aHaltMethods[iHaldMethod].pfnNotifyCpuFF(&pUVM->aCpus[iCpu], fFlags);
1051}
1052
1053
1054/**
1055 * Notify the emulation thread (EMT) about pending Forced Action (FF).
1056 *
1057 * This function is called by thread other than EMT to make
1058 * sure EMT wakes up and promptly service an FF request.
1059 *
1060 * @param pUVCpu Pointer to the user mode per CPU VM structure.
1061 * @param fFlags Notification flags, VMNOTIFYFF_FLAGS_*.
1062 * @internal
1063 */
1064VMMR3_INT_DECL(void) VMR3NotifyCpuFFU(PUVMCPU pUVCpu, uint32_t fFlags)
1065{
1066 PUVM pUVM = pUVCpu->pUVM;
1067
1068 LogFlow(("VMR3NotifyCpuFFU:\n"));
1069 g_aHaltMethods[pUVM->vm.s.iHaltMethod].pfnNotifyCpuFF(pUVCpu, fFlags);
1070}
1071
1072
1073/**
1074 * Halted VM Wait.
1075 * Any external event will unblock the thread.
1076 *
1077 * @returns VINF_SUCCESS unless a fatal error occurred. In the latter
1078 * case an appropriate status code is returned.
1079 * @param pVM The cross context VM structure.
1080 * @param pVCpu The cross context virtual CPU structure.
1081 * @param fIgnoreInterrupts If set the VM_FF_INTERRUPT flags is ignored.
1082 * @thread The emulation thread.
1083 * @remarks Made visible for implementing vmsvga sync register.
1084 * @internal
1085 */
1086VMMR3_INT_DECL(int) VMR3WaitHalted(PVM pVM, PVMCPU pVCpu, bool fIgnoreInterrupts)
1087{
1088 LogFlow(("VMR3WaitHalted: fIgnoreInterrupts=%d\n", fIgnoreInterrupts));
1089
1090 /*
1091 * Check Relevant FFs.
1092 */
1093 const uint32_t fMask = !fIgnoreInterrupts
1094 ? VMCPU_FF_EXTERNAL_HALTED_MASK
1095 : VMCPU_FF_EXTERNAL_HALTED_MASK & ~(VMCPU_FF_UPDATE_APIC | VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC);
1096 if ( VM_FF_IS_PENDING(pVM, VM_FF_EXTERNAL_HALTED_MASK)
1097 || VMCPU_FF_IS_PENDING(pVCpu, fMask))
1098 {
1099 LogFlow(("VMR3WaitHalted: returns VINF_SUCCESS (FF %#x FFCPU %#x)\n", pVM->fGlobalForcedActions, pVCpu->fLocalForcedActions));
1100 return VINF_SUCCESS;
1101 }
1102
1103 /*
1104 * The yielder is suspended while we're halting, while TM might have clock(s) running
1105 * only at certain times and need to be notified..
1106 */
1107 if (pVCpu->idCpu == 0)
1108 VMMR3YieldSuspend(pVM);
1109 TMNotifyStartOfHalt(pVCpu);
1110
1111 /*
1112 * Record halt averages for the last second.
1113 */
1114 PUVMCPU pUVCpu = pVCpu->pUVCpu;
1115 uint64_t u64Now = RTTimeNanoTS();
1116 int64_t off = u64Now - pUVCpu->vm.s.u64HaltsStartTS;
1117 if (off > 1000000000)
1118 {
1119 if (off > _4G || !pUVCpu->vm.s.cHalts)
1120 {
1121 pUVCpu->vm.s.HaltInterval = 1000000000 /* 1 sec */;
1122 pUVCpu->vm.s.HaltFrequency = 1;
1123 }
1124 else
1125 {
1126 pUVCpu->vm.s.HaltInterval = (uint32_t)off / pUVCpu->vm.s.cHalts;
1127 pUVCpu->vm.s.HaltFrequency = ASMMultU64ByU32DivByU32(pUVCpu->vm.s.cHalts, 1000000000, (uint32_t)off);
1128 }
1129 pUVCpu->vm.s.u64HaltsStartTS = u64Now;
1130 pUVCpu->vm.s.cHalts = 0;
1131 }
1132 pUVCpu->vm.s.cHalts++;
1133
1134 /*
1135 * Do the halt.
1136 */
1137 VMCPU_ASSERT_STATE(pVCpu, VMCPUSTATE_STARTED);
1138 VMCPU_SET_STATE(pVCpu, VMCPUSTATE_STARTED_HALTED);
1139 PUVM pUVM = pUVCpu->pUVM;
1140 int rc = g_aHaltMethods[pUVM->vm.s.iHaltMethod].pfnHalt(pUVCpu, fMask, u64Now);
1141 VMCPU_SET_STATE(pVCpu, VMCPUSTATE_STARTED);
1142
1143 /*
1144 * Notify TM and resume the yielder
1145 */
1146 TMNotifyEndOfHalt(pVCpu);
1147 if (pVCpu->idCpu == 0)
1148 VMMR3YieldResume(pVM);
1149
1150 LogFlow(("VMR3WaitHalted: returns %Rrc (FF %#x)\n", rc, pVM->fGlobalForcedActions));
1151 return rc;
1152}
1153
1154
1155/**
1156 * Suspended VM Wait.
1157 * Only a handful of forced actions will cause the function to
1158 * return to the caller.
1159 *
1160 * @returns VINF_SUCCESS unless a fatal error occurred. In the latter
1161 * case an appropriate status code is returned.
1162 * @param pUVCpu Pointer to the user mode VMCPU structure.
1163 * @thread The emulation thread.
1164 * @internal
1165 */
1166VMMR3_INT_DECL(int) VMR3WaitU(PUVMCPU pUVCpu)
1167{
1168 LogFlow(("VMR3WaitU:\n"));
1169
1170 /*
1171 * Check Relevant FFs.
1172 */
1173 PVM pVM = pUVCpu->pVM;
1174 PVMCPU pVCpu = pUVCpu->pVCpu;
1175
1176 if ( pVM
1177 && ( VM_FF_IS_PENDING(pVM, VM_FF_EXTERNAL_SUSPENDED_MASK)
1178 || VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_EXTERNAL_SUSPENDED_MASK)
1179 )
1180 )
1181 {
1182 LogFlow(("VMR3Wait: returns VINF_SUCCESS (FF %#x)\n", pVM->fGlobalForcedActions));
1183 return VINF_SUCCESS;
1184 }
1185
1186 /*
1187 * Do waiting according to the halt method (so VMR3NotifyFF
1188 * doesn't have to special case anything).
1189 */
1190 PUVM pUVM = pUVCpu->pUVM;
1191 int rc = g_aHaltMethods[pUVM->vm.s.iHaltMethod].pfnWait(pUVCpu);
1192 LogFlow(("VMR3WaitU: returns %Rrc (FF %#x)\n", rc, pUVM->pVM ? pUVM->pVM->fGlobalForcedActions : 0));
1193 return rc;
1194}
1195
1196
1197/**
1198 * Interface that PDMR3Suspend, PDMR3PowerOff and PDMR3Reset uses when they wait
1199 * for the handling of asynchronous notifications to complete.
1200 *
1201 * @returns VINF_SUCCESS unless a fatal error occurred. In the latter
1202 * case an appropriate status code is returned.
1203 * @param pUVCpu Pointer to the user mode VMCPU structure.
1204 * @thread The emulation thread.
1205 */
1206VMMR3_INT_DECL(int) VMR3AsyncPdmNotificationWaitU(PUVMCPU pUVCpu)
1207{
1208 LogFlow(("VMR3AsyncPdmNotificationWaitU:\n"));
1209 return VMR3WaitU(pUVCpu);
1210}
1211
1212
1213/**
1214 * Interface that PDM the helper asynchronous notification completed methods
1215 * uses for EMT0 when it is waiting inside VMR3AsyncPdmNotificationWaitU().
1216 *
1217 * @param pUVM Pointer to the user mode VM structure.
1218 */
1219VMMR3_INT_DECL(void) VMR3AsyncPdmNotificationWakeupU(PUVM pUVM)
1220{
1221 LogFlow(("VMR3AsyncPdmNotificationWakeupU:\n"));
1222 VM_FF_SET(pUVM->pVM, VM_FF_REQUEST); /* this will have to do for now. */
1223 g_aHaltMethods[pUVM->vm.s.iHaltMethod].pfnNotifyCpuFF(&pUVM->aCpus[0], 0 /*fFlags*/);
1224}
1225
1226
1227/**
1228 * Rendezvous callback that will be called once.
1229 *
1230 * @returns VBox strict status code.
1231 * @param pVM The cross context VM structure.
1232 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
1233 * @param pvUser The new g_aHaltMethods index.
1234 */
1235static DECLCALLBACK(VBOXSTRICTRC) vmR3SetHaltMethodCallback(PVM pVM, PVMCPU pVCpu, void *pvUser)
1236{
1237 PUVM pUVM = pVM->pUVM;
1238 uintptr_t i = (uintptr_t)pvUser;
1239 Assert(i < RT_ELEMENTS(g_aHaltMethods));
1240 NOREF(pVCpu);
1241
1242 /*
1243 * Terminate the old one.
1244 */
1245 if ( pUVM->vm.s.enmHaltMethod != VMHALTMETHOD_INVALID
1246 && g_aHaltMethods[pUVM->vm.s.iHaltMethod].pfnTerm)
1247 {
1248 g_aHaltMethods[pUVM->vm.s.iHaltMethod].pfnTerm(pUVM);
1249 pUVM->vm.s.enmHaltMethod = VMHALTMETHOD_INVALID;
1250 }
1251
1252 /* Assert that the failure fallback is where we expect. */
1253 Assert(g_aHaltMethods[0].enmHaltMethod == VMHALTMETHOD_BOOTSTRAP);
1254 Assert(!g_aHaltMethods[0].pfnTerm && !g_aHaltMethods[0].pfnInit);
1255
1256 /*
1257 * Init the new one.
1258 */
1259 int rc = VINF_SUCCESS;
1260 memset(&pUVM->vm.s.Halt, 0, sizeof(pUVM->vm.s.Halt));
1261 if (g_aHaltMethods[i].pfnInit)
1262 {
1263 rc = g_aHaltMethods[i].pfnInit(pUVM);
1264 if (RT_FAILURE(rc))
1265 {
1266 /* Fall back on the bootstrap method. This requires no
1267 init/term (see assertion above), and will always work. */
1268 AssertLogRelRC(rc);
1269 i = 0;
1270 }
1271 }
1272
1273 /*
1274 * Commit it.
1275 */
1276 pUVM->vm.s.enmHaltMethod = g_aHaltMethods[i].enmHaltMethod;
1277 ASMAtomicWriteU32(&pUVM->vm.s.iHaltMethod, i);
1278
1279 return rc;
1280}
1281
1282
1283/**
1284 * Changes the halt method.
1285 *
1286 * @returns VBox status code.
1287 * @param pUVM Pointer to the user mode VM structure.
1288 * @param enmHaltMethod The new halt method.
1289 * @thread EMT.
1290 */
1291int vmR3SetHaltMethodU(PUVM pUVM, VMHALTMETHOD enmHaltMethod)
1292{
1293 PVM pVM = pUVM->pVM; Assert(pVM);
1294 VM_ASSERT_EMT(pVM);
1295 AssertReturn(enmHaltMethod > VMHALTMETHOD_INVALID && enmHaltMethod < VMHALTMETHOD_END, VERR_INVALID_PARAMETER);
1296
1297 /*
1298 * Resolve default (can be overridden in the configuration).
1299 */
1300 if (enmHaltMethod == VMHALTMETHOD_DEFAULT)
1301 {
1302 uint32_t u32;
1303 int rc = CFGMR3QueryU32(CFGMR3GetChild(CFGMR3GetRoot(pVM), "VM"), "HaltMethod", &u32);
1304 if (RT_SUCCESS(rc))
1305 {
1306 enmHaltMethod = (VMHALTMETHOD)u32;
1307 if (enmHaltMethod <= VMHALTMETHOD_INVALID || enmHaltMethod >= VMHALTMETHOD_END)
1308 return VMSetError(pVM, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("Invalid VM/HaltMethod value %d"), enmHaltMethod);
1309 }
1310 else if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_CHILD_NOT_FOUND)
1311 return VMSetError(pVM, rc, RT_SRC_POS, N_("Failed to Query VM/HaltMethod as uint32_t"));
1312 else
1313 enmHaltMethod = VMHALTMETHOD_GLOBAL_1;
1314 //enmHaltMethod = VMHALTMETHOD_1;
1315 //enmHaltMethod = VMHALTMETHOD_OLD;
1316 }
1317 LogRel(("VMEmt: Halt method %s (%d)\n", vmR3GetHaltMethodName(enmHaltMethod), enmHaltMethod));
1318
1319 /*
1320 * Find the descriptor.
1321 */
1322 unsigned i = 0;
1323 while ( i < RT_ELEMENTS(g_aHaltMethods)
1324 && g_aHaltMethods[i].enmHaltMethod != enmHaltMethod)
1325 i++;
1326 AssertReturn(i < RT_ELEMENTS(g_aHaltMethods), VERR_INVALID_PARAMETER);
1327
1328 /*
1329 * This needs to be done while the other EMTs are not sleeping or otherwise messing around.
1330 */
1331 return VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ONCE, vmR3SetHaltMethodCallback, (void *)(uintptr_t)i);
1332}
1333
1334
1335/**
1336 * Special interface for implementing a HLT-like port on a device.
1337 *
1338 * This can be called directly from device code, provide the device is trusted
1339 * to access the VMM directly. Since we may not have an accurate register set
1340 * and the caller certainly shouldn't (device code does not access CPU
1341 * registers), this function will return when interrupts are pending regardless
1342 * of the actual EFLAGS.IF state.
1343 *
1344 * @returns VBox error status (never informational statuses).
1345 * @param pVM The cross context VM structure.
1346 * @param idCpu The id of the calling EMT.
1347 */
1348VMMR3DECL(int) VMR3WaitForDeviceReady(PVM pVM, VMCPUID idCpu)
1349{
1350 /*
1351 * Validate caller and resolve the CPU ID.
1352 */
1353 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
1354 AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID);
1355 PVMCPU pVCpu = &pVM->aCpus[idCpu];
1356 VMCPU_ASSERT_EMT_RETURN(pVCpu, VERR_VM_THREAD_NOT_EMT);
1357
1358 /*
1359 * Tag along with the HLT mechanics for now.
1360 */
1361 int rc = VMR3WaitHalted(pVM, pVCpu, false /*fIgnoreInterrupts*/);
1362 if (RT_SUCCESS(rc))
1363 return VINF_SUCCESS;
1364 return rc;
1365}
1366
1367
1368/**
1369 * Wakes up a CPU that has called VMR3WaitForDeviceReady.
1370 *
1371 * @returns VBox error status (never informational statuses).
1372 * @param pVM The cross context VM structure.
1373 * @param idCpu The id of the calling EMT.
1374 */
1375VMMR3DECL(int) VMR3NotifyCpuDeviceReady(PVM pVM, VMCPUID idCpu)
1376{
1377 /*
1378 * Validate caller and resolve the CPU ID.
1379 */
1380 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
1381 AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID);
1382 PVMCPU pVCpu = &pVM->aCpus[idCpu];
1383
1384 /*
1385 * Pretend it was an FF that got set since we've got logic for that already.
1386 */
1387 VMR3NotifyCpuFFU(pVCpu->pUVCpu, VMNOTIFYFF_FLAGS_DONE_REM);
1388 return VINF_SUCCESS;
1389}
1390
1391
1392/**
1393 * Returns the number of active EMTs.
1394 *
1395 * This is used by the rendezvous code during VM destruction to avoid waiting
1396 * for EMTs that aren't around any more.
1397 *
1398 * @returns Number of active EMTs. 0 if invalid parameter.
1399 * @param pUVM The user mode VM structure.
1400 */
1401VMMR3_INT_DECL(uint32_t) VMR3GetActiveEmts(PUVM pUVM)
1402{
1403 UVM_ASSERT_VALID_EXT_RETURN(pUVM, 0);
1404 return pUVM->vm.s.cActiveEmts;
1405}
1406
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