VirtualBox

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

Last change on this file since 84044 was 82968, checked in by vboxsync, 4 years ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use