VirtualBox

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

Last change on this file since 60682 was 60404, checked in by vboxsync, 9 years ago

VMM,Devices,Main: Implemented soft/warm reset for shutdown status codes 05h, 09h and 0Ah.

This is a shot at adjusting our VM reset handling to handle the ancient way of
getting a 286 out of protected mode and back to real mode. Our exiting reset
code (XXXR3Reset, PDMDEVREG::pfnReset, and so on) is doing a cold reset of the
system and then some additional device & memory initialization that the firmware
is usually responsible for doing. When the guest triggers a reset via the
keyboard controller, system control port A, CPU triple fault, and possibly ACPI,
only the CPU is supposed to be reset. The BIOS would then decide whether memory
and devices needed resetting as well, or if the resetter justed wanted to get out
protected mode and resume executing some real mode code pointed to by 467h.

  • New states SOFT_RESETTING and SOFT_RESETTING_LS. The latter returns to RUNNING_LS, not SUSPENDED_LS like for hard reset.
  • Added a firmware interface so the VMM/PDM can ask it whether we're supposed to do a hard reset or a soft(/warm) one.
  • Implemented firmware interface for the PC BIOS (but not EFI). It indicates soft(/warm) reset when CMOS[0xf] is 5, 9 or 10.
  • Moved the CMOS[0xf] resetting from the RTC device to the PC BIOS since it's firmware thing, not RTC.
  • Added a flag parameter to PDMDevHlpVMReset for specifying the source of the reset operation. One class of sources (GIM) will always trigger hard resets, whereas the others will check with the firmware first.
  • Added PDMR3GetResetInfo for query the flags passed to PDMDevHlpVMReset and for asking the firmware whether it's a hard or soft reset. The latter, however, is only done if only CPU 0 is active. Systems with more than one CPU in a state other than EMSTATE_WAIT_SIPI status will always be hard reset.
  • Added internal VMR3ResetFF and VMR3ResetTripleFault APIs for handling the VM_FF_RESET and VINF_EM_TRIPLE_FAULT conditions.
  • Added PMDR3ResetSoft and had it call pfnSoftReset (which is now defined).

Warning! Major PDM_DEVHLPR3_VERSION change, minor PDM_DEVREG_VERSION change.

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