VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR0/VMMR0.cpp@ 90997

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

VMM,PDM,PGM: Restrict the VMSetError and VMSetRuntimeError APIs to ring-3, these never worked properly in ring-0 or raw-mode. A PAEmode runtime error was the only place any of these were used, but given that the VMSetRuntimeError codepath starts with an assertion, it can't have been used/tested. The PAEmode runtime error shouldn't necessarily be triggered by PGM anyway, but IEM. Removed VMMCALLRING3_VM_SET_ERROR and VMMCALLRING3_VM_SET_RUNTIME_ERROR. bugref:10093

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 141.4 KB
Line 
1/* $Id: VMMR0.cpp 90997 2021-08-30 14:04:48Z vboxsync $ */
2/** @file
3 * VMM - Host Context Ring 0.
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_VMM
23#include <VBox/vmm/vmm.h>
24#include <VBox/sup.h>
25#include <VBox/vmm/iom.h>
26#include <VBox/vmm/trpm.h>
27#include <VBox/vmm/cpum.h>
28#include <VBox/vmm/pdmapi.h>
29#include <VBox/vmm/pgm.h>
30#ifdef VBOX_WITH_NEM_R0
31# include <VBox/vmm/nem.h>
32#endif
33#include <VBox/vmm/em.h>
34#include <VBox/vmm/stam.h>
35#include <VBox/vmm/tm.h>
36#include "VMMInternal.h"
37#include <VBox/vmm/vmcc.h>
38#include <VBox/vmm/gvm.h>
39#ifdef VBOX_WITH_PCI_PASSTHROUGH
40# include <VBox/vmm/pdmpci.h>
41#endif
42#include <VBox/vmm/apic.h>
43
44#include <VBox/vmm/gvmm.h>
45#include <VBox/vmm/gmm.h>
46#include <VBox/vmm/gim.h>
47#include <VBox/intnet.h>
48#include <VBox/vmm/hm.h>
49#include <VBox/param.h>
50#include <VBox/err.h>
51#include <VBox/version.h>
52#include <VBox/log.h>
53
54#include <iprt/asm-amd64-x86.h>
55#include <iprt/assert.h>
56#include <iprt/crc.h>
57#include <iprt/mem.h>
58#include <iprt/memobj.h>
59#include <iprt/mp.h>
60#include <iprt/once.h>
61#include <iprt/semaphore.h>
62#include <iprt/spinlock.h>
63#include <iprt/stdarg.h>
64#include <iprt/string.h>
65#include <iprt/thread.h>
66#include <iprt/timer.h>
67#include <iprt/time.h>
68
69#include "dtrace/VBoxVMM.h"
70
71
72#if defined(_MSC_VER) && defined(RT_ARCH_AMD64) /** @todo check this with with VC7! */
73# pragma intrinsic(_AddressOfReturnAddress)
74#endif
75
76#if defined(RT_OS_DARWIN) && ARCH_BITS == 32
77# error "32-bit darwin is no longer supported. Go back to 4.3 or earlier!"
78#endif
79
80
81
82/*********************************************************************************************************************************
83* Defined Constants And Macros *
84*********************************************************************************************************************************/
85/** @def VMM_CHECK_SMAP_SETUP
86 * SMAP check setup. */
87/** @def VMM_CHECK_SMAP_CHECK
88 * Checks that the AC flag is set if SMAP is enabled. If AC is not set,
89 * it will be logged and @a a_BadExpr is executed. */
90/** @def VMM_CHECK_SMAP_CHECK2
91 * Checks that the AC flag is set if SMAP is enabled. If AC is not set, it will
92 * be logged, written to the VMs assertion text buffer, and @a a_BadExpr is
93 * executed. */
94#if (defined(VBOX_STRICT) || 1) && !defined(VBOX_WITH_RAM_IN_KERNEL)
95# define VMM_CHECK_SMAP_SETUP() uint32_t const fKernelFeatures = SUPR0GetKernelFeatures()
96# define VMM_CHECK_SMAP_CHECK(a_BadExpr) \
97 do { \
98 if (fKernelFeatures & SUPKERNELFEATURES_SMAP) \
99 { \
100 RTCCUINTREG fEflCheck = ASMGetFlags(); \
101 if (RT_LIKELY(fEflCheck & X86_EFL_AC)) \
102 { /* likely */ } \
103 else \
104 { \
105 SUPR0Printf("%s, line %d: EFLAGS.AC is clear! (%#x)\n", __FUNCTION__, __LINE__, (uint32_t)fEflCheck); \
106 a_BadExpr; \
107 } \
108 } \
109 } while (0)
110# define VMM_CHECK_SMAP_CHECK2(a_pGVM, a_BadExpr) \
111 do { \
112 if (fKernelFeatures & SUPKERNELFEATURES_SMAP) \
113 { \
114 RTCCUINTREG fEflCheck = ASMGetFlags(); \
115 if (RT_LIKELY(fEflCheck & X86_EFL_AC)) \
116 { /* likely */ } \
117 else if (a_pGVM) \
118 { \
119 SUPR0BadContext((a_pGVM)->pSession, __FILE__, __LINE__, "EFLAGS.AC is zero!"); \
120 RTStrPrintf((a_pGVM)->vmm.s.szRing0AssertMsg1, sizeof((a_pGVM)->vmm.s.szRing0AssertMsg1), \
121 "%s, line %d: EFLAGS.AC is clear! (%#x)\n", __FUNCTION__, __LINE__, (uint32_t)fEflCheck); \
122 a_BadExpr; \
123 } \
124 else \
125 { \
126 SUPR0Printf("%s, line %d: EFLAGS.AC is clear! (%#x)\n", __FUNCTION__, __LINE__, (uint32_t)fEflCheck); \
127 a_BadExpr; \
128 } \
129 } \
130 } while (0)
131#else
132# define VMM_CHECK_SMAP_SETUP() uint32_t const fKernelFeatures = 0
133# define VMM_CHECK_SMAP_CHECK(a_BadExpr) NOREF(fKernelFeatures)
134# define VMM_CHECK_SMAP_CHECK2(a_pGVM, a_BadExpr) NOREF(fKernelFeatures)
135#endif
136
137
138/*********************************************************************************************************************************
139* Internal Functions *
140*********************************************************************************************************************************/
141RT_C_DECLS_BEGIN
142#if defined(RT_ARCH_X86) && (defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD))
143extern uint64_t __udivdi3(uint64_t, uint64_t);
144extern uint64_t __umoddi3(uint64_t, uint64_t);
145#endif
146RT_C_DECLS_END
147static int vmmR0UpdateLoggers(PGVM pGVM, VMCPUID idCpu, PVMMR0UPDATELOGGERSREQ pReq, size_t idxLogger);
148static int vmmR0LogFlusher(PGVM pGVM);
149static int vmmR0LogWaitFlushed(PGVM pGVM, VMCPUID idCpu, size_t idxLogger);
150static int vmmR0InitLoggers(PGVM pGVM);
151static void vmmR0CleanupLoggers(PGVM pGVM);
152
153
154/*********************************************************************************************************************************
155* Global Variables *
156*********************************************************************************************************************************/
157/** Drag in necessary library bits.
158 * The runtime lives here (in VMMR0.r0) and VBoxDD*R0.r0 links against us. */
159struct CLANG11WEIRDNOTHROW { PFNRT pfn; } g_VMMR0Deps[] =
160{
161 { (PFNRT)RTCrc32 },
162 { (PFNRT)RTOnce },
163#if defined(RT_ARCH_X86) && (defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD))
164 { (PFNRT)__udivdi3 },
165 { (PFNRT)__umoddi3 },
166#endif
167 { NULL }
168};
169
170#ifdef RT_OS_SOLARIS
171/* Dependency information for the native solaris loader. */
172extern "C" { char _depends_on[] = "vboxdrv"; }
173#endif
174
175
176/**
177 * Initialize the module.
178 * This is called when we're first loaded.
179 *
180 * @returns 0 on success.
181 * @returns VBox status on failure.
182 * @param hMod Image handle for use in APIs.
183 */
184DECLEXPORT(int) ModuleInit(void *hMod)
185{
186 VMM_CHECK_SMAP_SETUP();
187 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
188
189#ifdef VBOX_WITH_DTRACE_R0
190 /*
191 * The first thing to do is register the static tracepoints.
192 * (Deregistration is automatic.)
193 */
194 int rc2 = SUPR0TracerRegisterModule(hMod, &g_VTGObjHeader);
195 if (RT_FAILURE(rc2))
196 return rc2;
197#endif
198 LogFlow(("ModuleInit:\n"));
199
200#ifdef VBOX_WITH_64ON32_CMOS_DEBUG
201 /*
202 * Display the CMOS debug code.
203 */
204 ASMOutU8(0x72, 0x03);
205 uint8_t bDebugCode = ASMInU8(0x73);
206 LogRel(("CMOS Debug Code: %#x (%d)\n", bDebugCode, bDebugCode));
207 RTLogComPrintf("CMOS Debug Code: %#x (%d)\n", bDebugCode, bDebugCode);
208#endif
209
210 /*
211 * Initialize the VMM, GVMM, GMM, HM, PGM (Darwin) and INTNET.
212 */
213 int rc = vmmInitFormatTypes();
214 if (RT_SUCCESS(rc))
215 {
216 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
217 rc = GVMMR0Init();
218 if (RT_SUCCESS(rc))
219 {
220 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
221 rc = GMMR0Init();
222 if (RT_SUCCESS(rc))
223 {
224 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
225 rc = HMR0Init();
226 if (RT_SUCCESS(rc))
227 {
228 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
229
230 PDMR0Init(hMod);
231 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
232
233 rc = PGMRegisterStringFormatTypes();
234 if (RT_SUCCESS(rc))
235 {
236 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
237#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE
238 rc = PGMR0DynMapInit();
239#endif
240 if (RT_SUCCESS(rc))
241 {
242 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
243 rc = IntNetR0Init();
244 if (RT_SUCCESS(rc))
245 {
246#ifdef VBOX_WITH_PCI_PASSTHROUGH
247 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
248 rc = PciRawR0Init();
249#endif
250 if (RT_SUCCESS(rc))
251 {
252 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
253 rc = CPUMR0ModuleInit();
254 if (RT_SUCCESS(rc))
255 {
256#ifdef VBOX_WITH_TRIPLE_FAULT_HACK
257 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
258 rc = vmmR0TripleFaultHackInit();
259 if (RT_SUCCESS(rc))
260#endif
261 {
262 VMM_CHECK_SMAP_CHECK(rc = VERR_VMM_SMAP_BUT_AC_CLEAR);
263 if (RT_SUCCESS(rc))
264 {
265 LogFlow(("ModuleInit: returns success\n"));
266 return VINF_SUCCESS;
267 }
268 }
269
270 /*
271 * Bail out.
272 */
273#ifdef VBOX_WITH_TRIPLE_FAULT_HACK
274 vmmR0TripleFaultHackTerm();
275#endif
276 }
277 else
278 LogRel(("ModuleInit: CPUMR0ModuleInit -> %Rrc\n", rc));
279#ifdef VBOX_WITH_PCI_PASSTHROUGH
280 PciRawR0Term();
281#endif
282 }
283 else
284 LogRel(("ModuleInit: PciRawR0Init -> %Rrc\n", rc));
285 IntNetR0Term();
286 }
287 else
288 LogRel(("ModuleInit: IntNetR0Init -> %Rrc\n", rc));
289#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE
290 PGMR0DynMapTerm();
291#endif
292 }
293 else
294 LogRel(("ModuleInit: PGMR0DynMapInit -> %Rrc\n", rc));
295 PGMDeregisterStringFormatTypes();
296 }
297 else
298 LogRel(("ModuleInit: PGMRegisterStringFormatTypes -> %Rrc\n", rc));
299 HMR0Term();
300 }
301 else
302 LogRel(("ModuleInit: HMR0Init -> %Rrc\n", rc));
303 GMMR0Term();
304 }
305 else
306 LogRel(("ModuleInit: GMMR0Init -> %Rrc\n", rc));
307 GVMMR0Term();
308 }
309 else
310 LogRel(("ModuleInit: GVMMR0Init -> %Rrc\n", rc));
311 vmmTermFormatTypes();
312 }
313 else
314 LogRel(("ModuleInit: vmmInitFormatTypes -> %Rrc\n", rc));
315
316 LogFlow(("ModuleInit: failed %Rrc\n", rc));
317 return rc;
318}
319
320
321/**
322 * Terminate the module.
323 * This is called when we're finally unloaded.
324 *
325 * @param hMod Image handle for use in APIs.
326 */
327DECLEXPORT(void) ModuleTerm(void *hMod)
328{
329 NOREF(hMod);
330 LogFlow(("ModuleTerm:\n"));
331
332 /*
333 * Terminate the CPUM module (Local APIC cleanup).
334 */
335 CPUMR0ModuleTerm();
336
337 /*
338 * Terminate the internal network service.
339 */
340 IntNetR0Term();
341
342 /*
343 * PGM (Darwin), HM and PciRaw global cleanup.
344 */
345#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE
346 PGMR0DynMapTerm();
347#endif
348#ifdef VBOX_WITH_PCI_PASSTHROUGH
349 PciRawR0Term();
350#endif
351 PGMDeregisterStringFormatTypes();
352 HMR0Term();
353#ifdef VBOX_WITH_TRIPLE_FAULT_HACK
354 vmmR0TripleFaultHackTerm();
355#endif
356
357 /*
358 * Destroy the GMM and GVMM instances.
359 */
360 GMMR0Term();
361 GVMMR0Term();
362
363 vmmTermFormatTypes();
364
365 LogFlow(("ModuleTerm: returns\n"));
366}
367
368
369/**
370 * Initializes VMM specific members when the GVM structure is created,
371 * allocating loggers and stuff.
372 *
373 * The loggers are allocated here so that we can update their settings before
374 * doing VMMR0_DO_VMMR0_INIT and have correct logging at that time.
375 *
376 * @returns VBox status code.
377 * @param pGVM The global (ring-0) VM structure.
378 */
379VMMR0_INT_DECL(int) VMMR0InitPerVMData(PGVM pGVM)
380{
381 AssertCompile(sizeof(pGVM->vmmr0.s) <= sizeof(pGVM->vmmr0.padding));
382
383 /*
384 * Initialize all members first.
385 */
386 pGVM->vmmr0.s.fCalledInitVm = false;
387 pGVM->vmmr0.s.hMemObjLogger = NIL_RTR0MEMOBJ;
388 pGVM->vmmr0.s.hMapObjLogger = NIL_RTR0MEMOBJ;
389 pGVM->vmmr0.s.hMemObjReleaseLogger = NIL_RTR0MEMOBJ;
390 pGVM->vmmr0.s.hMapObjReleaseLogger = NIL_RTR0MEMOBJ;
391 pGVM->vmmr0.s.LogFlusher.hSpinlock = NIL_RTSPINLOCK;
392 pGVM->vmmr0.s.LogFlusher.hThread = NIL_RTNATIVETHREAD;
393 pGVM->vmmr0.s.LogFlusher.hEvent = NIL_RTSEMEVENT;
394 pGVM->vmmr0.s.LogFlusher.idxRingHead = 0;
395 pGVM->vmmr0.s.LogFlusher.idxRingTail = 0;
396 pGVM->vmmr0.s.LogFlusher.fThreadWaiting = false;
397
398 for (VMCPUID idCpu = 0; idCpu < pGVM->cCpus; idCpu++)
399 {
400 PGVMCPU pGVCpu = &pGVM->aCpus[idCpu];
401 Assert(pGVCpu->idHostCpu == NIL_RTCPUID);
402 Assert(pGVCpu->iHostCpuSet == UINT32_MAX);
403 pGVCpu->vmmr0.s.pPreemptState = NULL;
404 pGVCpu->vmmr0.s.hCtxHook = NIL_RTTHREADCTXHOOK;
405 for (size_t iLogger = 0; iLogger < RT_ELEMENTS(pGVCpu->vmmr0.s.u.aLoggers); iLogger++)
406 pGVCpu->vmmr0.s.u.aLoggers[iLogger].hEventFlushWait = NIL_RTSEMEVENT;
407 }
408
409 /*
410 * Create the loggers.
411 */
412 return vmmR0InitLoggers(pGVM);
413}
414
415
416/**
417 * Initiates the R0 driver for a particular VM instance.
418 *
419 * @returns VBox status code.
420 *
421 * @param pGVM The global (ring-0) VM structure.
422 * @param uSvnRev The SVN revision of the ring-3 part.
423 * @param uBuildType Build type indicator.
424 * @thread EMT(0)
425 */
426static int vmmR0InitVM(PGVM pGVM, uint32_t uSvnRev, uint32_t uBuildType)
427{
428 VMM_CHECK_SMAP_SETUP();
429 VMM_CHECK_SMAP_CHECK(return VERR_VMM_SMAP_BUT_AC_CLEAR);
430
431 /*
432 * Match the SVN revisions and build type.
433 */
434 if (uSvnRev != VMMGetSvnRev())
435 {
436 LogRel(("VMMR0InitVM: Revision mismatch, r3=%d r0=%d\n", uSvnRev, VMMGetSvnRev()));
437 SUPR0Printf("VMMR0InitVM: Revision mismatch, r3=%d r0=%d\n", uSvnRev, VMMGetSvnRev());
438 return VERR_VMM_R0_VERSION_MISMATCH;
439 }
440 if (uBuildType != vmmGetBuildType())
441 {
442 LogRel(("VMMR0InitVM: Build type mismatch, r3=%#x r0=%#x\n", uBuildType, vmmGetBuildType()));
443 SUPR0Printf("VMMR0InitVM: Build type mismatch, r3=%#x r0=%#x\n", uBuildType, vmmGetBuildType());
444 return VERR_VMM_R0_VERSION_MISMATCH;
445 }
446
447 int rc = GVMMR0ValidateGVMandEMT(pGVM, 0 /*idCpu*/);
448 if (RT_FAILURE(rc))
449 return rc;
450
451 /* Don't allow this to be called more than once. */
452 if (!pGVM->vmmr0.s.fCalledInitVm)
453 pGVM->vmmr0.s.fCalledInitVm = true;
454 else
455 return VERR_ALREADY_INITIALIZED;
456
457#ifdef LOG_ENABLED
458
459 /*
460 * Register the EMT R0 logger instance for VCPU 0.
461 */
462 PVMCPUCC pVCpu = VMCC_GET_CPU_0(pGVM);
463 if (pVCpu->vmmr0.s.u.s.Logger.pLogger)
464 {
465# if 0 /* testing of the logger. */
466 LogCom(("vmmR0InitVM: before %p\n", RTLogDefaultInstance()));
467 LogCom(("vmmR0InitVM: pfnFlush=%p actual=%p\n", pR0Logger->Logger.pfnFlush, vmmR0LoggerFlush));
468 LogCom(("vmmR0InitVM: pfnLogger=%p actual=%p\n", pR0Logger->Logger.pfnLogger, vmmR0LoggerWrapper));
469 LogCom(("vmmR0InitVM: offScratch=%d fFlags=%#x fDestFlags=%#x\n", pR0Logger->Logger.offScratch, pR0Logger->Logger.fFlags, pR0Logger->Logger.fDestFlags));
470
471 RTLogSetDefaultInstanceThread(&pR0Logger->Logger, (uintptr_t)pGVM->pSession);
472 LogCom(("vmmR0InitVM: after %p reg\n", RTLogDefaultInstance()));
473 RTLogSetDefaultInstanceThread(NULL, pGVM->pSession);
474 LogCom(("vmmR0InitVM: after %p dereg\n", RTLogDefaultInstance()));
475
476 pR0Logger->Logger.pfnLogger("hello ring-0 logger\n");
477 LogCom(("vmmR0InitVM: returned successfully from direct logger call.\n"));
478 pR0Logger->Logger.pfnFlush(&pR0Logger->Logger);
479 LogCom(("vmmR0InitVM: returned successfully from direct flush call.\n"));
480
481 RTLogSetDefaultInstanceThread(&pR0Logger->Logger, (uintptr_t)pGVM->pSession);
482 LogCom(("vmmR0InitVM: after %p reg2\n", RTLogDefaultInstance()));
483 pR0Logger->Logger.pfnLogger("hello ring-0 logger\n");
484 LogCom(("vmmR0InitVM: returned successfully from direct logger call (2). offScratch=%d\n", pR0Logger->Logger.offScratch));
485 RTLogSetDefaultInstanceThread(NULL, pGVM->pSession);
486 LogCom(("vmmR0InitVM: after %p dereg2\n", RTLogDefaultInstance()));
487
488 RTLogLoggerEx(&pR0Logger->Logger, 0, ~0U, "hello ring-0 logger (RTLogLoggerEx)\n");
489 LogCom(("vmmR0InitVM: RTLogLoggerEx returned fine offScratch=%d\n", pR0Logger->Logger.offScratch));
490
491 RTLogSetDefaultInstanceThread(&pR0Logger->Logger, (uintptr_t)pGVM->pSession);
492 RTLogPrintf("hello ring-0 logger (RTLogPrintf)\n");
493 LogCom(("vmmR0InitVM: RTLogPrintf returned fine offScratch=%d\n", pR0Logger->Logger.offScratch));
494# endif
495# ifdef VBOX_WITH_R0_LOGGING
496 Log(("Switching to per-thread logging instance %p (key=%p)\n", pVCpu->vmmr0.s.u.s.Logger.pLogger, pGVM->pSession));
497 RTLogSetDefaultInstanceThread(pVCpu->vmmr0.s.u.s.Logger.pLogger, (uintptr_t)pGVM->pSession);
498 pVCpu->vmmr0.s.u.s.Logger.fRegistered = true;
499# endif
500 }
501#endif /* LOG_ENABLED */
502
503 /*
504 * Check if the host supports high resolution timers or not.
505 */
506 if ( pGVM->vmm.s.fUsePeriodicPreemptionTimers
507 && !RTTimerCanDoHighResolution())
508 pGVM->vmm.s.fUsePeriodicPreemptionTimers = false;
509
510 /*
511 * Initialize the per VM data for GVMM and GMM.
512 */
513 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
514 rc = GVMMR0InitVM(pGVM);
515 if (RT_SUCCESS(rc))
516 {
517 /*
518 * Init HM, CPUM and PGM (Darwin only).
519 */
520 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
521 rc = HMR0InitVM(pGVM);
522 if (RT_SUCCESS(rc))
523 VMM_CHECK_SMAP_CHECK2(pGVM, rc = VERR_VMM_RING0_ASSERTION); /* CPUR0InitVM will otherwise panic the host */
524 if (RT_SUCCESS(rc))
525 {
526 rc = CPUMR0InitVM(pGVM);
527 if (RT_SUCCESS(rc))
528 {
529 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
530 rc = PGMR0InitVM(pGVM);
531 if (RT_SUCCESS(rc))
532 {
533 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
534 rc = EMR0InitVM(pGVM);
535 if (RT_SUCCESS(rc))
536 {
537 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
538#ifdef VBOX_WITH_PCI_PASSTHROUGH
539 rc = PciRawR0InitVM(pGVM);
540#endif
541 if (RT_SUCCESS(rc))
542 {
543 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
544 rc = GIMR0InitVM(pGVM);
545 if (RT_SUCCESS(rc))
546 {
547 VMM_CHECK_SMAP_CHECK2(pGVM, rc = VERR_VMM_RING0_ASSERTION);
548 if (RT_SUCCESS(rc))
549 {
550 GVMMR0DoneInitVM(pGVM);
551
552 /*
553 * Collect a bit of info for the VM release log.
554 */
555 pGVM->vmm.s.fIsPreemptPendingApiTrusty = RTThreadPreemptIsPendingTrusty();
556 pGVM->vmm.s.fIsPreemptPossible = RTThreadPreemptIsPossible();;
557
558 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
559 return rc;
560 }
561
562 /* bail out*/
563 GIMR0TermVM(pGVM);
564 }
565#ifdef VBOX_WITH_PCI_PASSTHROUGH
566 PciRawR0TermVM(pGVM);
567#endif
568 }
569 }
570 }
571 }
572 HMR0TermVM(pGVM);
573 }
574 }
575
576 RTLogSetDefaultInstanceThread(NULL, (uintptr_t)pGVM->pSession);
577 return rc;
578}
579
580
581/**
582 * Does EMT specific VM initialization.
583 *
584 * @returns VBox status code.
585 * @param pGVM The ring-0 VM structure.
586 * @param idCpu The EMT that's calling.
587 */
588static int vmmR0InitVMEmt(PGVM pGVM, VMCPUID idCpu)
589{
590 /* Paranoia (caller checked these already). */
591 AssertReturn(idCpu < pGVM->cCpus, VERR_INVALID_CPU_ID);
592 AssertReturn(pGVM->aCpus[idCpu].hEMT == RTThreadNativeSelf(), VERR_INVALID_CPU_ID);
593
594#if defined(LOG_ENABLED) && defined(VBOX_WITH_R0_LOGGING)
595 /*
596 * Registration of ring 0 loggers.
597 */
598 PVMCPUCC pVCpu = &pGVM->aCpus[idCpu];
599 if ( pVCpu->vmmr0.s.u.s.Logger.pLogger
600 && !pVCpu->vmmr0.s.u.s.Logger.fRegistered)
601 {
602 RTLogSetDefaultInstanceThread(pVCpu->vmmr0.s.u.s.Logger.pLogger, (uintptr_t)pGVM->pSession);
603 pVCpu->vmmr0.s.u.s.Logger.fRegistered = true;
604 }
605#endif
606
607 return VINF_SUCCESS;
608}
609
610
611
612/**
613 * Terminates the R0 bits for a particular VM instance.
614 *
615 * This is normally called by ring-3 as part of the VM termination process, but
616 * may alternatively be called during the support driver session cleanup when
617 * the VM object is destroyed (see GVMM).
618 *
619 * @returns VBox status code.
620 *
621 * @param pGVM The global (ring-0) VM structure.
622 * @param idCpu Set to 0 if EMT(0) or NIL_VMCPUID if session cleanup
623 * thread.
624 * @thread EMT(0) or session clean up thread.
625 */
626VMMR0_INT_DECL(int) VMMR0TermVM(PGVM pGVM, VMCPUID idCpu)
627{
628 /*
629 * Check EMT(0) claim if we're called from userland.
630 */
631 if (idCpu != NIL_VMCPUID)
632 {
633 AssertReturn(idCpu == 0, VERR_INVALID_CPU_ID);
634 int rc = GVMMR0ValidateGVMandEMT(pGVM, idCpu);
635 if (RT_FAILURE(rc))
636 return rc;
637 }
638
639#ifdef VBOX_WITH_PCI_PASSTHROUGH
640 PciRawR0TermVM(pGVM);
641#endif
642
643 /*
644 * Tell GVMM what we're up to and check that we only do this once.
645 */
646 if (GVMMR0DoingTermVM(pGVM))
647 {
648 GIMR0TermVM(pGVM);
649
650 /** @todo I wish to call PGMR0PhysFlushHandyPages(pGVM, &pGVM->aCpus[idCpu])
651 * here to make sure we don't leak any shared pages if we crash... */
652#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE
653 PGMR0DynMapTermVM(pGVM);
654#endif
655 HMR0TermVM(pGVM);
656 }
657
658 /*
659 * Deregister the logger for this EMT.
660 */
661 RTLogSetDefaultInstanceThread(NULL, (uintptr_t)pGVM->pSession);
662
663 /*
664 * Start log flusher thread termination.
665 */
666 ASMAtomicWriteBool(&pGVM->vmmr0.s.LogFlusher.fThreadShutdown, true);
667 if (pGVM->vmmr0.s.LogFlusher.hEvent != NIL_RTSEMEVENT)
668 RTSemEventSignal(pGVM->vmmr0.s.LogFlusher.hEvent);
669
670 return VINF_SUCCESS;
671}
672
673
674/**
675 * This is called at the end of gvmmR0CleanupVM().
676 *
677 * @param pGVM The global (ring-0) VM structure.
678 */
679VMMR0_INT_DECL(void) VMMR0CleanupVM(PGVM pGVM)
680{
681 AssertCompile(NIL_RTTHREADCTXHOOK == (RTTHREADCTXHOOK)0); /* Depends on zero initialized memory working for NIL at the moment. */
682 for (VMCPUID idCpu = 0; idCpu < pGVM->cCpus; idCpu++)
683 {
684 PGVMCPU pGVCpu = &pGVM->aCpus[idCpu];
685
686 /** @todo Can we busy wait here for all thread-context hooks to be
687 * deregistered before releasing (destroying) it? Only until we find a
688 * solution for not deregistering hooks everytime we're leaving HMR0
689 * context. */
690 VMMR0ThreadCtxHookDestroyForEmt(pGVCpu);
691 }
692
693 vmmR0CleanupLoggers(pGVM);
694}
695
696
697/**
698 * An interrupt or unhalt force flag is set, deal with it.
699 *
700 * @returns VINF_SUCCESS (or VINF_EM_HALT).
701 * @param pVCpu The cross context virtual CPU structure.
702 * @param uMWait Result from EMMonitorWaitIsActive().
703 * @param enmInterruptibility Guest CPU interruptbility level.
704 */
705static int vmmR0DoHaltInterrupt(PVMCPUCC pVCpu, unsigned uMWait, CPUMINTERRUPTIBILITY enmInterruptibility)
706{
707 Assert(!TRPMHasTrap(pVCpu));
708 Assert( enmInterruptibility > CPUMINTERRUPTIBILITY_INVALID
709 && enmInterruptibility < CPUMINTERRUPTIBILITY_END);
710
711 /*
712 * Pending interrupts w/o any SMIs or NMIs? That the usual case.
713 */
714 if ( VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC)
715 && !VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_SMI | VMCPU_FF_INTERRUPT_NMI))
716 {
717 if (enmInterruptibility <= CPUMINTERRUPTIBILITY_UNRESTRAINED)
718 {
719 uint8_t u8Interrupt = 0;
720 int rc = PDMGetInterrupt(pVCpu, &u8Interrupt);
721 Log(("vmmR0DoHaltInterrupt: CPU%d u8Interrupt=%d (%#x) rc=%Rrc\n", pVCpu->idCpu, u8Interrupt, u8Interrupt, rc));
722 if (RT_SUCCESS(rc))
723 {
724 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_UNHALT);
725
726 rc = TRPMAssertTrap(pVCpu, u8Interrupt, TRPM_HARDWARE_INT);
727 AssertRCSuccess(rc);
728 STAM_REL_COUNTER_INC(&pVCpu->vmm.s.StatR0HaltExec);
729 return rc;
730 }
731 }
732 }
733 /*
734 * SMI is not implemented yet, at least not here.
735 */
736 else if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_SMI))
737 {
738 Log12(("vmmR0DoHaltInterrupt: CPU%d failed #3\n", pVCpu->idCpu));
739 STAM_REL_COUNTER_INC(&pVCpu->vmm.s.StatR0HaltToR3);
740 return VINF_EM_HALT;
741 }
742 /*
743 * NMI.
744 */
745 else if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_NMI))
746 {
747 if (enmInterruptibility < CPUMINTERRUPTIBILITY_NMI_INHIBIT)
748 {
749 /** @todo later. */
750 Log12(("vmmR0DoHaltInterrupt: CPU%d failed #2 (uMWait=%u enmInt=%d)\n", pVCpu->idCpu, uMWait, enmInterruptibility));
751 STAM_REL_COUNTER_INC(&pVCpu->vmm.s.StatR0HaltToR3);
752 return VINF_EM_HALT;
753 }
754 }
755 /*
756 * Nested-guest virtual interrupt.
757 */
758 else if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_NESTED_GUEST))
759 {
760 if (enmInterruptibility < CPUMINTERRUPTIBILITY_VIRT_INT_DISABLED)
761 {
762 /** @todo NSTVMX: NSTSVM: Remember, we might have to check and perform VM-exits
763 * here before injecting the virtual interrupt. See emR3ForcedActions
764 * for details. */
765 Log12(("vmmR0DoHaltInterrupt: CPU%d failed #1 (uMWait=%u enmInt=%d)\n", pVCpu->idCpu, uMWait, enmInterruptibility));
766 STAM_REL_COUNTER_INC(&pVCpu->vmm.s.StatR0HaltToR3);
767 return VINF_EM_HALT;
768 }
769 }
770
771 if (VMCPU_FF_TEST_AND_CLEAR(pVCpu, VMCPU_FF_UNHALT))
772 {
773 STAM_REL_COUNTER_INC(&pVCpu->vmm.s.StatR0HaltExec);
774 Log11(("vmmR0DoHaltInterrupt: CPU%d success VINF_SUCCESS (UNHALT)\n", pVCpu->idCpu));
775 return VINF_SUCCESS;
776 }
777 if (uMWait > 1)
778 {
779 STAM_REL_COUNTER_INC(&pVCpu->vmm.s.StatR0HaltExec);
780 Log11(("vmmR0DoHaltInterrupt: CPU%d success VINF_SUCCESS (uMWait=%u > 1)\n", pVCpu->idCpu, uMWait));
781 return VINF_SUCCESS;
782 }
783
784 Log12(("vmmR0DoHaltInterrupt: CPU%d failed #0 (uMWait=%u enmInt=%d)\n", pVCpu->idCpu, uMWait, enmInterruptibility));
785 STAM_REL_COUNTER_INC(&pVCpu->vmm.s.StatR0HaltToR3);
786 return VINF_EM_HALT;
787}
788
789
790/**
791 * This does one round of vmR3HaltGlobal1Halt().
792 *
793 * The rational here is that we'll reduce latency in interrupt situations if we
794 * don't go to ring-3 immediately on a VINF_EM_HALT (guest executed HLT or
795 * MWAIT), but do one round of blocking here instead and hope the interrupt is
796 * raised in the meanwhile.
797 *
798 * If we go to ring-3 we'll quit the inner HM/NEM loop in EM and end up in the
799 * outer loop, which will then call VMR3WaitHalted() and that in turn will do a
800 * ring-0 call (unless we're too close to a timer event). When the interrupt
801 * wakes us up, we'll return from ring-0 and EM will by instinct do a
802 * rescheduling (because of raw-mode) before it resumes the HM/NEM loop and gets
803 * back to VMMR0EntryFast().
804 *
805 * @returns VINF_SUCCESS or VINF_EM_HALT.
806 * @param pGVM The ring-0 VM structure.
807 * @param pGVCpu The ring-0 virtual CPU structure.
808 *
809 * @todo r=bird: All the blocking/waiting and EMT managment should move out of
810 * the VM module, probably to VMM. Then this would be more weird wrt
811 * parameters and statistics.
812 */
813static int vmmR0DoHalt(PGVM pGVM, PGVMCPU pGVCpu)
814{
815 /*
816 * Do spin stat historization.
817 */
818 if (++pGVCpu->vmm.s.cR0Halts & 0xff)
819 { /* likely */ }
820 else if (pGVCpu->vmm.s.cR0HaltsSucceeded > pGVCpu->vmm.s.cR0HaltsToRing3)
821 {
822 pGVCpu->vmm.s.cR0HaltsSucceeded = 2;
823 pGVCpu->vmm.s.cR0HaltsToRing3 = 0;
824 }
825 else
826 {
827 pGVCpu->vmm.s.cR0HaltsSucceeded = 0;
828 pGVCpu->vmm.s.cR0HaltsToRing3 = 2;
829 }
830
831 /*
832 * Flags that makes us go to ring-3.
833 */
834 uint32_t const fVmFFs = VM_FF_TM_VIRTUAL_SYNC | VM_FF_PDM_QUEUES | VM_FF_PDM_DMA
835 | VM_FF_DBGF | VM_FF_REQUEST | VM_FF_CHECK_VM_STATE
836 | VM_FF_RESET | VM_FF_EMT_RENDEZVOUS | VM_FF_PGM_NEED_HANDY_PAGES
837 | VM_FF_PGM_NO_MEMORY | VM_FF_DEBUG_SUSPEND;
838 uint64_t const fCpuFFs = VMCPU_FF_TIMER | VMCPU_FF_PDM_CRITSECT | VMCPU_FF_IEM
839 | VMCPU_FF_REQUEST | VMCPU_FF_DBGF | VMCPU_FF_HM_UPDATE_CR3
840 | VMCPU_FF_HM_UPDATE_PAE_PDPES | VMCPU_FF_PGM_SYNC_CR3 | VMCPU_FF_PGM_SYNC_CR3_NON_GLOBAL
841 | VMCPU_FF_TO_R3 | VMCPU_FF_IOM;
842
843 /*
844 * Check preconditions.
845 */
846 unsigned const uMWait = EMMonitorWaitIsActive(pGVCpu);
847 CPUMINTERRUPTIBILITY const enmInterruptibility = CPUMGetGuestInterruptibility(pGVCpu);
848 if ( pGVCpu->vmm.s.fMayHaltInRing0
849 && !TRPMHasTrap(pGVCpu)
850 && ( enmInterruptibility == CPUMINTERRUPTIBILITY_UNRESTRAINED
851 || uMWait > 1))
852 {
853 if ( !VM_FF_IS_ANY_SET(pGVM, fVmFFs)
854 && !VMCPU_FF_IS_ANY_SET(pGVCpu, fCpuFFs))
855 {
856 /*
857 * Interrupts pending already?
858 */
859 if (VMCPU_FF_TEST_AND_CLEAR(pGVCpu, VMCPU_FF_UPDATE_APIC))
860 APICUpdatePendingInterrupts(pGVCpu);
861
862 /*
863 * Flags that wake up from the halted state.
864 */
865 uint64_t const fIntMask = VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_INTERRUPT_NESTED_GUEST
866 | VMCPU_FF_INTERRUPT_NMI | VMCPU_FF_INTERRUPT_SMI | VMCPU_FF_UNHALT;
867
868 if (VMCPU_FF_IS_ANY_SET(pGVCpu, fIntMask))
869 return vmmR0DoHaltInterrupt(pGVCpu, uMWait, enmInterruptibility);
870 ASMNopPause();
871
872 /*
873 * Check out how long till the next timer event.
874 */
875 uint64_t u64Delta;
876 uint64_t u64GipTime = TMTimerPollGIP(pGVM, pGVCpu, &u64Delta);
877
878 if ( !VM_FF_IS_ANY_SET(pGVM, fVmFFs)
879 && !VMCPU_FF_IS_ANY_SET(pGVCpu, fCpuFFs))
880 {
881 if (VMCPU_FF_TEST_AND_CLEAR(pGVCpu, VMCPU_FF_UPDATE_APIC))
882 APICUpdatePendingInterrupts(pGVCpu);
883
884 if (VMCPU_FF_IS_ANY_SET(pGVCpu, fIntMask))
885 return vmmR0DoHaltInterrupt(pGVCpu, uMWait, enmInterruptibility);
886
887 /*
888 * Wait if there is enough time to the next timer event.
889 */
890 if (u64Delta >= pGVCpu->vmm.s.cNsSpinBlockThreshold)
891 {
892 /* If there are few other CPU cores around, we will procrastinate a
893 little before going to sleep, hoping for some device raising an
894 interrupt or similar. Though, the best thing here would be to
895 dynamically adjust the spin count according to its usfulness or
896 something... */
897 if ( pGVCpu->vmm.s.cR0HaltsSucceeded > pGVCpu->vmm.s.cR0HaltsToRing3
898 && RTMpGetOnlineCount() >= 4)
899 {
900 /** @todo Figure out how we can skip this if it hasn't help recently...
901 * @bugref{9172#c12} */
902 uint32_t cSpinLoops = 42;
903 while (cSpinLoops-- > 0)
904 {
905 ASMNopPause();
906 if (VMCPU_FF_TEST_AND_CLEAR(pGVCpu, VMCPU_FF_UPDATE_APIC))
907 APICUpdatePendingInterrupts(pGVCpu);
908 ASMNopPause();
909 if (VM_FF_IS_ANY_SET(pGVM, fVmFFs))
910 {
911 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3FromSpin);
912 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3);
913 return VINF_EM_HALT;
914 }
915 ASMNopPause();
916 if (VMCPU_FF_IS_ANY_SET(pGVCpu, fCpuFFs))
917 {
918 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3FromSpin);
919 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3);
920 return VINF_EM_HALT;
921 }
922 ASMNopPause();
923 if (VMCPU_FF_IS_ANY_SET(pGVCpu, fIntMask))
924 {
925 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltExecFromSpin);
926 return vmmR0DoHaltInterrupt(pGVCpu, uMWait, enmInterruptibility);
927 }
928 ASMNopPause();
929 }
930 }
931
932 /*
933 * We have to set the state to VMCPUSTATE_STARTED_HALTED here so ring-3
934 * knows when to notify us (cannot access VMINTUSERPERVMCPU::fWait from here).
935 * After changing the state we must recheck the force flags of course.
936 */
937 if (VMCPU_CMPXCHG_STATE(pGVCpu, VMCPUSTATE_STARTED_HALTED, VMCPUSTATE_STARTED))
938 {
939 if ( !VM_FF_IS_ANY_SET(pGVM, fVmFFs)
940 && !VMCPU_FF_IS_ANY_SET(pGVCpu, fCpuFFs))
941 {
942 if (VMCPU_FF_TEST_AND_CLEAR(pGVCpu, VMCPU_FF_UPDATE_APIC))
943 APICUpdatePendingInterrupts(pGVCpu);
944
945 if (VMCPU_FF_IS_ANY_SET(pGVCpu, fIntMask))
946 {
947 VMCPU_CMPXCHG_STATE(pGVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_HALTED);
948 return vmmR0DoHaltInterrupt(pGVCpu, uMWait, enmInterruptibility);
949 }
950
951 /* Okay, block! */
952 uint64_t const u64StartSchedHalt = RTTimeNanoTS();
953 int rc = GVMMR0SchedHalt(pGVM, pGVCpu, u64GipTime);
954 uint64_t const u64EndSchedHalt = RTTimeNanoTS();
955 uint64_t const cNsElapsedSchedHalt = u64EndSchedHalt - u64StartSchedHalt;
956 Log10(("vmmR0DoHalt: CPU%d: halted %llu ns\n", pGVCpu->idCpu, cNsElapsedSchedHalt));
957
958 VMCPU_CMPXCHG_STATE(pGVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_HALTED);
959 STAM_REL_PROFILE_ADD_PERIOD(&pGVCpu->vmm.s.StatR0HaltBlock, cNsElapsedSchedHalt);
960 if ( rc == VINF_SUCCESS
961 || rc == VERR_INTERRUPTED)
962 {
963 /* Keep some stats like ring-3 does. */
964 int64_t const cNsOverslept = u64EndSchedHalt - u64GipTime;
965 if (cNsOverslept > 50000)
966 STAM_REL_PROFILE_ADD_PERIOD(&pGVCpu->vmm.s.StatR0HaltBlockOverslept, cNsOverslept);
967 else if (cNsOverslept < -50000)
968 STAM_REL_PROFILE_ADD_PERIOD(&pGVCpu->vmm.s.StatR0HaltBlockInsomnia, cNsElapsedSchedHalt);
969 else
970 STAM_REL_PROFILE_ADD_PERIOD(&pGVCpu->vmm.s.StatR0HaltBlockOnTime, cNsElapsedSchedHalt);
971
972 /*
973 * Recheck whether we can resume execution or have to go to ring-3.
974 */
975 if ( !VM_FF_IS_ANY_SET(pGVM, fVmFFs)
976 && !VMCPU_FF_IS_ANY_SET(pGVCpu, fCpuFFs))
977 {
978 if (VMCPU_FF_TEST_AND_CLEAR(pGVCpu, VMCPU_FF_UPDATE_APIC))
979 APICUpdatePendingInterrupts(pGVCpu);
980 if (VMCPU_FF_IS_ANY_SET(pGVCpu, fIntMask))
981 {
982 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltExecFromBlock);
983 return vmmR0DoHaltInterrupt(pGVCpu, uMWait, enmInterruptibility);
984 }
985 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3PostNoInt);
986 Log12(("vmmR0DoHalt: CPU%d post #2 - No pending interrupt\n", pGVCpu->idCpu));
987 }
988 else
989 {
990 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3PostPendingFF);
991 Log12(("vmmR0DoHalt: CPU%d post #1 - Pending FF\n", pGVCpu->idCpu));
992 }
993 }
994 else
995 {
996 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3Other);
997 Log12(("vmmR0DoHalt: CPU%d GVMMR0SchedHalt failed: %Rrc\n", pGVCpu->idCpu, rc));
998 }
999 }
1000 else
1001 {
1002 VMCPU_CMPXCHG_STATE(pGVCpu, VMCPUSTATE_STARTED, VMCPUSTATE_STARTED_HALTED);
1003 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3PendingFF);
1004 Log12(("vmmR0DoHalt: CPU%d failed #5 - Pending FF\n", pGVCpu->idCpu));
1005 }
1006 }
1007 else
1008 {
1009 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3Other);
1010 Log12(("vmmR0DoHalt: CPU%d failed #4 - enmState=%d\n", pGVCpu->idCpu, VMCPU_GET_STATE(pGVCpu)));
1011 }
1012 }
1013 else
1014 {
1015 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3SmallDelta);
1016 Log12(("vmmR0DoHalt: CPU%d failed #3 - delta too small: %RU64\n", pGVCpu->idCpu, u64Delta));
1017 }
1018 }
1019 else
1020 {
1021 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3PendingFF);
1022 Log12(("vmmR0DoHalt: CPU%d failed #2 - Pending FF\n", pGVCpu->idCpu));
1023 }
1024 }
1025 else
1026 {
1027 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3PendingFF);
1028 Log12(("vmmR0DoHalt: CPU%d failed #1 - Pending FF\n", pGVCpu->idCpu));
1029 }
1030 }
1031 else
1032 {
1033 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3Other);
1034 Log12(("vmmR0DoHalt: CPU%d failed #0 - fMayHaltInRing0=%d TRPMHasTrap=%d enmInt=%d uMWait=%u\n",
1035 pGVCpu->idCpu, pGVCpu->vmm.s.fMayHaltInRing0, TRPMHasTrap(pGVCpu), enmInterruptibility, uMWait));
1036 }
1037
1038 STAM_REL_COUNTER_INC(&pGVCpu->vmm.s.StatR0HaltToR3);
1039 return VINF_EM_HALT;
1040}
1041
1042
1043/**
1044 * VMM ring-0 thread-context callback.
1045 *
1046 * This does common HM state updating and calls the HM-specific thread-context
1047 * callback.
1048 *
1049 * This is used together with RTThreadCtxHookCreate() on platforms which
1050 * supports it, and directly from VMMR0EmtPrepareForBlocking() and
1051 * VMMR0EmtResumeAfterBlocking() on platforms which don't.
1052 *
1053 * @param enmEvent The thread-context event.
1054 * @param pvUser Opaque pointer to the VMCPU.
1055 *
1056 * @thread EMT(pvUser)
1057 */
1058static DECLCALLBACK(void) vmmR0ThreadCtxCallback(RTTHREADCTXEVENT enmEvent, void *pvUser)
1059{
1060 PVMCPUCC pVCpu = (PVMCPUCC)pvUser;
1061
1062 switch (enmEvent)
1063 {
1064 case RTTHREADCTXEVENT_IN:
1065 {
1066 /*
1067 * Linux may call us with preemption enabled (really!) but technically we
1068 * cannot get preempted here, otherwise we end up in an infinite recursion
1069 * scenario (i.e. preempted in resume hook -> preempt hook -> resume hook...
1070 * ad infinitum). Let's just disable preemption for now...
1071 */
1072 /** @todo r=bird: I don't believe the above. The linux code is clearly enabling
1073 * preemption after doing the callout (one or two functions up the
1074 * call chain). */
1075 /** @todo r=ramshankar: See @bugref{5313#c30}. */
1076 RTTHREADPREEMPTSTATE ParanoidPreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
1077 RTThreadPreemptDisable(&ParanoidPreemptState);
1078
1079 /* We need to update the VCPU <-> host CPU mapping. */
1080 RTCPUID idHostCpu;
1081 uint32_t iHostCpuSet = RTMpCurSetIndexAndId(&idHostCpu);
1082 pVCpu->iHostCpuSet = iHostCpuSet;
1083 ASMAtomicWriteU32(&pVCpu->idHostCpu, idHostCpu);
1084
1085 /* In the very unlikely event that the GIP delta for the CPU we're
1086 rescheduled needs calculating, try force a return to ring-3.
1087 We unfortunately cannot do the measurements right here. */
1088 if (RT_LIKELY(!SUPIsTscDeltaAvailableForCpuSetIndex(iHostCpuSet)))
1089 { /* likely */ }
1090 else
1091 VMCPU_FF_SET(pVCpu, VMCPU_FF_TO_R3);
1092
1093 /* Invoke the HM-specific thread-context callback. */
1094 HMR0ThreadCtxCallback(enmEvent, pvUser);
1095
1096 /* Restore preemption. */
1097 RTThreadPreemptRestore(&ParanoidPreemptState);
1098 break;
1099 }
1100
1101 case RTTHREADCTXEVENT_OUT:
1102 {
1103 /* Invoke the HM-specific thread-context callback. */
1104 HMR0ThreadCtxCallback(enmEvent, pvUser);
1105
1106 /*
1107 * Sigh. See VMMGetCpu() used by VMCPU_ASSERT_EMT(). We cannot let several VCPUs
1108 * have the same host CPU associated with it.
1109 */
1110 pVCpu->iHostCpuSet = UINT32_MAX;
1111 ASMAtomicWriteU32(&pVCpu->idHostCpu, NIL_RTCPUID);
1112 break;
1113 }
1114
1115 default:
1116 /* Invoke the HM-specific thread-context callback. */
1117 HMR0ThreadCtxCallback(enmEvent, pvUser);
1118 break;
1119 }
1120}
1121
1122
1123/**
1124 * Creates thread switching hook for the current EMT thread.
1125 *
1126 * This is called by GVMMR0CreateVM and GVMMR0RegisterVCpu. If the host
1127 * platform does not implement switcher hooks, no hooks will be create and the
1128 * member set to NIL_RTTHREADCTXHOOK.
1129 *
1130 * @returns VBox status code.
1131 * @param pVCpu The cross context virtual CPU structure.
1132 * @thread EMT(pVCpu)
1133 */
1134VMMR0_INT_DECL(int) VMMR0ThreadCtxHookCreateForEmt(PVMCPUCC pVCpu)
1135{
1136 VMCPU_ASSERT_EMT(pVCpu);
1137 Assert(pVCpu->vmmr0.s.hCtxHook == NIL_RTTHREADCTXHOOK);
1138
1139#if 1 /* To disable this stuff change to zero. */
1140 int rc = RTThreadCtxHookCreate(&pVCpu->vmmr0.s.hCtxHook, 0, vmmR0ThreadCtxCallback, pVCpu);
1141 if (RT_SUCCESS(rc))
1142 {
1143 pVCpu->pGVM->vmm.s.fIsUsingContextHooks = true;
1144 return rc;
1145 }
1146#else
1147 RT_NOREF(vmmR0ThreadCtxCallback);
1148 int rc = VERR_NOT_SUPPORTED;
1149#endif
1150
1151 pVCpu->vmmr0.s.hCtxHook = NIL_RTTHREADCTXHOOK;
1152 pVCpu->pGVM->vmm.s.fIsUsingContextHooks = false;
1153 if (rc == VERR_NOT_SUPPORTED)
1154 return VINF_SUCCESS;
1155
1156 LogRelMax(32, ("RTThreadCtxHookCreate failed! rc=%Rrc pVCpu=%p idCpu=%RU32\n", rc, pVCpu, pVCpu->idCpu));
1157 return VINF_SUCCESS; /* Just ignore it, we can live without context hooks. */
1158}
1159
1160
1161/**
1162 * Destroys the thread switching hook for the specified VCPU.
1163 *
1164 * @param pVCpu The cross context virtual CPU structure.
1165 * @remarks Can be called from any thread.
1166 */
1167VMMR0_INT_DECL(void) VMMR0ThreadCtxHookDestroyForEmt(PVMCPUCC pVCpu)
1168{
1169 int rc = RTThreadCtxHookDestroy(pVCpu->vmmr0.s.hCtxHook);
1170 AssertRC(rc);
1171 pVCpu->vmmr0.s.hCtxHook = NIL_RTTHREADCTXHOOK;
1172}
1173
1174
1175/**
1176 * Disables the thread switching hook for this VCPU (if we got one).
1177 *
1178 * @param pVCpu The cross context virtual CPU structure.
1179 * @thread EMT(pVCpu)
1180 *
1181 * @remarks This also clears GVMCPU::idHostCpu, so the mapping is invalid after
1182 * this call. This means you have to be careful with what you do!
1183 */
1184VMMR0_INT_DECL(void) VMMR0ThreadCtxHookDisable(PVMCPUCC pVCpu)
1185{
1186 /*
1187 * Clear the VCPU <-> host CPU mapping as we've left HM context.
1188 * @bugref{7726#c19} explains the need for this trick:
1189 *
1190 * VMXR0CallRing3Callback/SVMR0CallRing3Callback &
1191 * hmR0VmxLeaveSession/hmR0SvmLeaveSession disables context hooks during
1192 * longjmp & normal return to ring-3, which opens a window where we may be
1193 * rescheduled without changing GVMCPUID::idHostCpu and cause confusion if
1194 * the CPU starts executing a different EMT. Both functions first disables
1195 * preemption and then calls HMR0LeaveCpu which invalids idHostCpu, leaving
1196 * an opening for getting preempted.
1197 */
1198 /** @todo Make HM not need this API! Then we could leave the hooks enabled
1199 * all the time. */
1200
1201 /*
1202 * Disable the context hook, if we got one.
1203 */
1204 if (pVCpu->vmmr0.s.hCtxHook != NIL_RTTHREADCTXHOOK)
1205 {
1206 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
1207 ASMAtomicWriteU32(&pVCpu->idHostCpu, NIL_RTCPUID);
1208 int rc = RTThreadCtxHookDisable(pVCpu->vmmr0.s.hCtxHook);
1209 AssertRC(rc);
1210 }
1211}
1212
1213
1214/**
1215 * Internal version of VMMR0ThreadCtxHooksAreRegistered.
1216 *
1217 * @returns true if registered, false otherwise.
1218 * @param pVCpu The cross context virtual CPU structure.
1219 */
1220DECLINLINE(bool) vmmR0ThreadCtxHookIsEnabled(PVMCPUCC pVCpu)
1221{
1222 return RTThreadCtxHookIsEnabled(pVCpu->vmmr0.s.hCtxHook);
1223}
1224
1225
1226/**
1227 * Whether thread-context hooks are registered for this VCPU.
1228 *
1229 * @returns true if registered, false otherwise.
1230 * @param pVCpu The cross context virtual CPU structure.
1231 */
1232VMMR0_INT_DECL(bool) VMMR0ThreadCtxHookIsEnabled(PVMCPUCC pVCpu)
1233{
1234 return vmmR0ThreadCtxHookIsEnabled(pVCpu);
1235}
1236
1237
1238/**
1239 * Returns the ring-0 release logger instance.
1240 *
1241 * @returns Pointer to release logger, NULL if not configured.
1242 * @param pVCpu The cross context virtual CPU structure of the caller.
1243 * @thread EMT(pVCpu)
1244 */
1245VMMR0_INT_DECL(PRTLOGGER) VMMR0GetReleaseLogger(PVMCPUCC pVCpu)
1246{
1247 return pVCpu->vmmr0.s.u.s.RelLogger.pLogger;
1248}
1249
1250
1251#ifdef VBOX_WITH_STATISTICS
1252/**
1253 * Record return code statistics
1254 * @param pVM The cross context VM structure.
1255 * @param pVCpu The cross context virtual CPU structure.
1256 * @param rc The status code.
1257 */
1258static void vmmR0RecordRC(PVMCC pVM, PVMCPUCC pVCpu, int rc)
1259{
1260 /*
1261 * Collect statistics.
1262 */
1263 switch (rc)
1264 {
1265 case VINF_SUCCESS:
1266 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetNormal);
1267 break;
1268 case VINF_EM_RAW_INTERRUPT:
1269 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetInterrupt);
1270 break;
1271 case VINF_EM_RAW_INTERRUPT_HYPER:
1272 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetInterruptHyper);
1273 break;
1274 case VINF_EM_RAW_GUEST_TRAP:
1275 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetGuestTrap);
1276 break;
1277 case VINF_EM_RAW_RING_SWITCH:
1278 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetRingSwitch);
1279 break;
1280 case VINF_EM_RAW_RING_SWITCH_INT:
1281 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetRingSwitchInt);
1282 break;
1283 case VINF_EM_RAW_STALE_SELECTOR:
1284 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetStaleSelector);
1285 break;
1286 case VINF_EM_RAW_IRET_TRAP:
1287 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetIRETTrap);
1288 break;
1289 case VINF_IOM_R3_IOPORT_READ:
1290 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetIORead);
1291 break;
1292 case VINF_IOM_R3_IOPORT_WRITE:
1293 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetIOWrite);
1294 break;
1295 case VINF_IOM_R3_IOPORT_COMMIT_WRITE:
1296 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetIOCommitWrite);
1297 break;
1298 case VINF_IOM_R3_MMIO_READ:
1299 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetMMIORead);
1300 break;
1301 case VINF_IOM_R3_MMIO_WRITE:
1302 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetMMIOWrite);
1303 break;
1304 case VINF_IOM_R3_MMIO_COMMIT_WRITE:
1305 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetMMIOCommitWrite);
1306 break;
1307 case VINF_IOM_R3_MMIO_READ_WRITE:
1308 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetMMIOReadWrite);
1309 break;
1310 case VINF_PATM_HC_MMIO_PATCH_READ:
1311 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetMMIOPatchRead);
1312 break;
1313 case VINF_PATM_HC_MMIO_PATCH_WRITE:
1314 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetMMIOPatchWrite);
1315 break;
1316 case VINF_CPUM_R3_MSR_READ:
1317 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetMSRRead);
1318 break;
1319 case VINF_CPUM_R3_MSR_WRITE:
1320 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetMSRWrite);
1321 break;
1322 case VINF_EM_RAW_EMULATE_INSTR:
1323 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetEmulate);
1324 break;
1325 case VINF_PATCH_EMULATE_INSTR:
1326 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetPatchEmulate);
1327 break;
1328 case VINF_EM_RAW_EMULATE_INSTR_LDT_FAULT:
1329 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetLDTFault);
1330 break;
1331 case VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT:
1332 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetGDTFault);
1333 break;
1334 case VINF_EM_RAW_EMULATE_INSTR_IDT_FAULT:
1335 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetIDTFault);
1336 break;
1337 case VINF_EM_RAW_EMULATE_INSTR_TSS_FAULT:
1338 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetTSSFault);
1339 break;
1340 case VINF_CSAM_PENDING_ACTION:
1341 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetCSAMTask);
1342 break;
1343 case VINF_PGM_SYNC_CR3:
1344 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetSyncCR3);
1345 break;
1346 case VINF_PATM_PATCH_INT3:
1347 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetPatchInt3);
1348 break;
1349 case VINF_PATM_PATCH_TRAP_PF:
1350 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetPatchPF);
1351 break;
1352 case VINF_PATM_PATCH_TRAP_GP:
1353 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetPatchGP);
1354 break;
1355 case VINF_PATM_PENDING_IRQ_AFTER_IRET:
1356 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetPatchIretIRQ);
1357 break;
1358 case VINF_EM_RESCHEDULE_REM:
1359 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetRescheduleREM);
1360 break;
1361 case VINF_EM_RAW_TO_R3:
1362 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetToR3Total);
1363 if (VM_FF_IS_SET(pVM, VM_FF_TM_VIRTUAL_SYNC))
1364 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetToR3TMVirt);
1365 else if (VM_FF_IS_SET(pVM, VM_FF_PGM_NEED_HANDY_PAGES))
1366 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetToR3HandyPages);
1367 else if (VM_FF_IS_SET(pVM, VM_FF_PDM_QUEUES))
1368 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetToR3PDMQueues);
1369 else if (VM_FF_IS_SET(pVM, VM_FF_EMT_RENDEZVOUS))
1370 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetToR3Rendezvous);
1371 else if (VM_FF_IS_SET(pVM, VM_FF_PDM_DMA))
1372 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetToR3DMA);
1373 else if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_TIMER))
1374 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetToR3Timer);
1375 else if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_PDM_CRITSECT))
1376 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetToR3CritSect);
1377 else if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_TO_R3))
1378 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetToR3FF);
1379 else if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_IEM))
1380 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetToR3Iem);
1381 else if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_IOM))
1382 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetToR3Iom);
1383 else
1384 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetToR3Unknown);
1385 break;
1386
1387 case VINF_EM_RAW_TIMER_PENDING:
1388 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetTimerPending);
1389 break;
1390 case VINF_EM_RAW_INTERRUPT_PENDING:
1391 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetInterruptPending);
1392 break;
1393 case VINF_VMM_CALL_HOST:
1394 switch (pVCpu->vmm.s.enmCallRing3Operation)
1395 {
1396 case VMMCALLRING3_PGM_POOL_GROW:
1397 STAM_COUNTER_INC(&pVM->vmm.s.StatRZCallPGMPoolGrow);
1398 break;
1399 case VMMCALLRING3_PGM_LOCK:
1400 STAM_COUNTER_INC(&pVM->vmm.s.StatRZCallPGMLock);
1401 break;
1402 case VMMCALLRING3_PGM_MAP_CHUNK:
1403 STAM_COUNTER_INC(&pVM->vmm.s.StatRZCallPGMMapChunk);
1404 break;
1405 case VMMCALLRING3_PGM_ALLOCATE_HANDY_PAGES:
1406 STAM_COUNTER_INC(&pVM->vmm.s.StatRZCallPGMAllocHandy);
1407 break;
1408 case VMMCALLRING3_VM_R0_ASSERTION:
1409 default:
1410 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetCallRing3);
1411 break;
1412 }
1413 break;
1414 case VINF_PATM_DUPLICATE_FUNCTION:
1415 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetPATMDuplicateFn);
1416 break;
1417 case VINF_PGM_CHANGE_MODE:
1418 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetPGMChangeMode);
1419 break;
1420 case VINF_PGM_POOL_FLUSH_PENDING:
1421 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetPGMFlushPending);
1422 break;
1423 case VINF_EM_PENDING_REQUEST:
1424 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetPendingRequest);
1425 break;
1426 case VINF_EM_HM_PATCH_TPR_INSTR:
1427 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetPatchTPR);
1428 break;
1429 default:
1430 STAM_COUNTER_INC(&pVM->vmm.s.StatRZRetMisc);
1431 break;
1432 }
1433}
1434#endif /* VBOX_WITH_STATISTICS */
1435
1436
1437/**
1438 * The Ring 0 entry point, called by the fast-ioctl path.
1439 *
1440 * @param pGVM The global (ring-0) VM structure.
1441 * @param pVMIgnored The cross context VM structure. The return code is
1442 * stored in pVM->vmm.s.iLastGZRc.
1443 * @param idCpu The Virtual CPU ID of the calling EMT.
1444 * @param enmOperation Which operation to execute.
1445 * @remarks Assume called with interrupts _enabled_.
1446 */
1447VMMR0DECL(void) VMMR0EntryFast(PGVM pGVM, PVMCC pVMIgnored, VMCPUID idCpu, VMMR0OPERATION enmOperation)
1448{
1449 RT_NOREF(pVMIgnored);
1450
1451 /*
1452 * Validation.
1453 */
1454 if ( idCpu < pGVM->cCpus
1455 && pGVM->cCpus == pGVM->cCpusUnsafe)
1456 { /*likely*/ }
1457 else
1458 {
1459 SUPR0Printf("VMMR0EntryFast: Bad idCpu=%#x cCpus=%#x cCpusUnsafe=%#x\n", idCpu, pGVM->cCpus, pGVM->cCpusUnsafe);
1460 return;
1461 }
1462
1463 PGVMCPU pGVCpu = &pGVM->aCpus[idCpu];
1464 RTNATIVETHREAD const hNativeThread = RTThreadNativeSelf();
1465 if (RT_LIKELY( pGVCpu->hEMT == hNativeThread
1466 && pGVCpu->hNativeThreadR0 == hNativeThread))
1467 { /* likely */ }
1468 else
1469 {
1470 SUPR0Printf("VMMR0EntryFast: Bad thread idCpu=%#x hNativeSelf=%p pGVCpu->hEmt=%p pGVCpu->hNativeThreadR0=%p\n",
1471 idCpu, hNativeThread, pGVCpu->hEMT, pGVCpu->hNativeThreadR0);
1472 return;
1473 }
1474
1475 /*
1476 * SMAP fun.
1477 */
1478 VMM_CHECK_SMAP_SETUP();
1479 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1480
1481 /*
1482 * Perform requested operation.
1483 */
1484 switch (enmOperation)
1485 {
1486 /*
1487 * Run guest code using the available hardware acceleration technology.
1488 */
1489 case VMMR0_DO_HM_RUN:
1490 {
1491 for (;;) /* hlt loop */
1492 {
1493 /*
1494 * Disable ring-3 calls & blocking till we've successfully entered HM.
1495 * Otherwise we sometimes end up blocking at the finall Log4 statement
1496 * in VMXR0Enter, while still in a somewhat inbetween state.
1497 */
1498 VMMRZCallRing3Disable(pGVCpu);
1499
1500 /*
1501 * Disable preemption.
1502 */
1503 Assert(!vmmR0ThreadCtxHookIsEnabled(pGVCpu));
1504 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
1505 RTThreadPreemptDisable(&PreemptState);
1506 pGVCpu->vmmr0.s.pPreemptState = &PreemptState;
1507
1508 /*
1509 * Get the host CPU identifiers, make sure they are valid and that
1510 * we've got a TSC delta for the CPU.
1511 */
1512 RTCPUID idHostCpu;
1513 uint32_t iHostCpuSet = RTMpCurSetIndexAndId(&idHostCpu);
1514 if (RT_LIKELY( iHostCpuSet < RTCPUSET_MAX_CPUS
1515 && SUPIsTscDeltaAvailableForCpuSetIndex(iHostCpuSet)))
1516 {
1517 pGVCpu->iHostCpuSet = iHostCpuSet;
1518 ASMAtomicWriteU32(&pGVCpu->idHostCpu, idHostCpu);
1519
1520 /*
1521 * Update the periodic preemption timer if it's active.
1522 */
1523 if (pGVM->vmm.s.fUsePeriodicPreemptionTimers)
1524 GVMMR0SchedUpdatePeriodicPreemptionTimer(pGVM, pGVCpu->idHostCpu, TMCalcHostTimerFrequency(pGVM, pGVCpu));
1525 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1526
1527#ifdef VMM_R0_TOUCH_FPU
1528 /*
1529 * Make sure we've got the FPU state loaded so and we don't need to clear
1530 * CR0.TS and get out of sync with the host kernel when loading the guest
1531 * FPU state. @ref sec_cpum_fpu (CPUM.cpp) and @bugref{4053}.
1532 */
1533 CPUMR0TouchHostFpu();
1534#endif
1535 int rc;
1536 bool fPreemptRestored = false;
1537 if (!HMR0SuspendPending())
1538 {
1539 /*
1540 * Enable the context switching hook.
1541 */
1542 if (pGVCpu->vmmr0.s.hCtxHook != NIL_RTTHREADCTXHOOK)
1543 {
1544 Assert(!RTThreadCtxHookIsEnabled(pGVCpu->vmmr0.s.hCtxHook));
1545 int rc2 = RTThreadCtxHookEnable(pGVCpu->vmmr0.s.hCtxHook); AssertRC(rc2);
1546 }
1547
1548 /*
1549 * Enter HM context.
1550 */
1551 rc = HMR0Enter(pGVCpu);
1552 if (RT_SUCCESS(rc))
1553 {
1554 VMCPU_SET_STATE(pGVCpu, VMCPUSTATE_STARTED_HM);
1555
1556 /*
1557 * When preemption hooks are in place, enable preemption now that
1558 * we're in HM context.
1559 */
1560 if (vmmR0ThreadCtxHookIsEnabled(pGVCpu))
1561 {
1562 fPreemptRestored = true;
1563 pGVCpu->vmmr0.s.pPreemptState = NULL;
1564 RTThreadPreemptRestore(&PreemptState);
1565 }
1566 VMMRZCallRing3Enable(pGVCpu);
1567
1568 /*
1569 * Setup the longjmp machinery and execute guest code (calls HMR0RunGuestCode).
1570 */
1571 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1572 rc = vmmR0CallRing3SetJmp(&pGVCpu->vmm.s.CallRing3JmpBufR0, HMR0RunGuestCode, pGVM, pGVCpu);
1573 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1574
1575 /*
1576 * Assert sanity on the way out. Using manual assertions code here as normal
1577 * assertions are going to panic the host since we're outside the setjmp/longjmp zone.
1578 */
1579 if (RT_UNLIKELY( VMCPU_GET_STATE(pGVCpu) != VMCPUSTATE_STARTED_HM
1580 && RT_SUCCESS_NP(rc)
1581 && rc != VINF_VMM_CALL_HOST ))
1582 {
1583 pGVM->vmm.s.szRing0AssertMsg1[0] = '\0';
1584 RTStrPrintf(pGVM->vmm.s.szRing0AssertMsg2, sizeof(pGVM->vmm.s.szRing0AssertMsg2),
1585 "Got VMCPU state %d expected %d.\n", VMCPU_GET_STATE(pGVCpu), VMCPUSTATE_STARTED_HM);
1586 rc = VERR_VMM_WRONG_HM_VMCPU_STATE;
1587 }
1588#if 0
1589 /** @todo Get rid of this. HM shouldn't disable the context hook. */
1590 else if (RT_UNLIKELY(vmmR0ThreadCtxHookIsEnabled(pGVCpu)))
1591 {
1592 pGVM->vmm.s.szRing0AssertMsg1[0] = '\0';
1593 RTStrPrintf(pGVM->vmm.s.szRing0AssertMsg2, sizeof(pGVM->vmm.s.szRing0AssertMsg2),
1594 "Thread-context hooks still enabled! VCPU=%p Id=%u rc=%d.\n", pGVCpu, pGVCpu->idCpu, rc);
1595 rc = VERR_VMM_CONTEXT_HOOK_STILL_ENABLED;
1596 }
1597#endif
1598
1599 VMMRZCallRing3Disable(pGVCpu); /* Lazy bird: Simpler just disabling it again... */
1600 VMCPU_SET_STATE(pGVCpu, VMCPUSTATE_STARTED);
1601 }
1602 STAM_COUNTER_INC(&pGVM->vmm.s.StatRunGC);
1603
1604 /*
1605 * Invalidate the host CPU identifiers before we disable the context
1606 * hook / restore preemption.
1607 */
1608 pGVCpu->iHostCpuSet = UINT32_MAX;
1609 ASMAtomicWriteU32(&pGVCpu->idHostCpu, NIL_RTCPUID);
1610
1611 /*
1612 * Disable context hooks. Due to unresolved cleanup issues, we
1613 * cannot leave the hooks enabled when we return to ring-3.
1614 *
1615 * Note! At the moment HM may also have disabled the hook
1616 * when we get here, but the IPRT API handles that.
1617 */
1618 if (pGVCpu->vmmr0.s.hCtxHook != NIL_RTTHREADCTXHOOK)
1619 RTThreadCtxHookDisable(pGVCpu->vmmr0.s.hCtxHook);
1620 }
1621 /*
1622 * The system is about to go into suspend mode; go back to ring 3.
1623 */
1624 else
1625 {
1626 pGVCpu->iHostCpuSet = UINT32_MAX;
1627 ASMAtomicWriteU32(&pGVCpu->idHostCpu, NIL_RTCPUID);
1628 rc = VINF_EM_RAW_INTERRUPT;
1629 }
1630
1631 /** @todo When HM stops messing with the context hook state, we'll disable
1632 * preemption again before the RTThreadCtxHookDisable call. */
1633 if (!fPreemptRestored)
1634 {
1635 pGVCpu->vmmr0.s.pPreemptState = NULL;
1636 RTThreadPreemptRestore(&PreemptState);
1637 }
1638
1639 pGVCpu->vmm.s.iLastGZRc = rc;
1640
1641 /* Fire dtrace probe and collect statistics. */
1642 VBOXVMM_R0_VMM_RETURN_TO_RING3_HM(pGVCpu, CPUMQueryGuestCtxPtr(pGVCpu), rc);
1643#ifdef VBOX_WITH_STATISTICS
1644 vmmR0RecordRC(pGVM, pGVCpu, rc);
1645#endif
1646 VMMRZCallRing3Enable(pGVCpu);
1647
1648 /*
1649 * If this is a halt.
1650 */
1651 if (rc != VINF_EM_HALT)
1652 { /* we're not in a hurry for a HLT, so prefer this path */ }
1653 else
1654 {
1655 pGVCpu->vmm.s.iLastGZRc = rc = vmmR0DoHalt(pGVM, pGVCpu);
1656 if (rc == VINF_SUCCESS)
1657 {
1658 pGVCpu->vmm.s.cR0HaltsSucceeded++;
1659 continue;
1660 }
1661 pGVCpu->vmm.s.cR0HaltsToRing3++;
1662 }
1663 }
1664 /*
1665 * Invalid CPU set index or TSC delta in need of measuring.
1666 */
1667 else
1668 {
1669 pGVCpu->vmmr0.s.pPreemptState = NULL;
1670 pGVCpu->iHostCpuSet = UINT32_MAX;
1671 ASMAtomicWriteU32(&pGVCpu->idHostCpu, NIL_RTCPUID);
1672 RTThreadPreemptRestore(&PreemptState);
1673
1674 VMMRZCallRing3Enable(pGVCpu);
1675
1676 if (iHostCpuSet < RTCPUSET_MAX_CPUS)
1677 {
1678 int rc = SUPR0TscDeltaMeasureBySetIndex(pGVM->pSession, iHostCpuSet, 0 /*fFlags*/,
1679 2 /*cMsWaitRetry*/, 5*RT_MS_1SEC /*cMsWaitThread*/,
1680 0 /*default cTries*/);
1681 if (RT_SUCCESS(rc) || rc == VERR_CPU_OFFLINE)
1682 pGVCpu->vmm.s.iLastGZRc = VINF_EM_RAW_TO_R3;
1683 else
1684 pGVCpu->vmm.s.iLastGZRc = rc;
1685 }
1686 else
1687 pGVCpu->vmm.s.iLastGZRc = VERR_INVALID_CPU_INDEX;
1688 }
1689 break;
1690 } /* halt loop. */
1691 break;
1692 }
1693
1694#ifdef VBOX_WITH_NEM_R0
1695# if defined(RT_ARCH_AMD64) && defined(RT_OS_WINDOWS)
1696 case VMMR0_DO_NEM_RUN:
1697 {
1698 /*
1699 * Setup the longjmp machinery and execute guest code (calls NEMR0RunGuestCode).
1700 */
1701 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1702# ifdef VBOXSTRICTRC_STRICT_ENABLED
1703 int rc = vmmR0CallRing3SetJmp2(&pGVCpu->vmm.s.CallRing3JmpBufR0, (PFNVMMR0SETJMP2)NEMR0RunGuestCode, pGVM, idCpu);
1704# else
1705 int rc = vmmR0CallRing3SetJmp2(&pGVCpu->vmm.s.CallRing3JmpBufR0, NEMR0RunGuestCode, pGVM, idCpu);
1706# endif
1707 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1708 STAM_COUNTER_INC(&pGVM->vmm.s.StatRunGC);
1709
1710 pGVCpu->vmm.s.iLastGZRc = rc;
1711
1712 /*
1713 * Fire dtrace probe and collect statistics.
1714 */
1715 VBOXVMM_R0_VMM_RETURN_TO_RING3_NEM(pGVCpu, CPUMQueryGuestCtxPtr(pGVCpu), rc);
1716# ifdef VBOX_WITH_STATISTICS
1717 vmmR0RecordRC(pGVM, pGVCpu, rc);
1718# endif
1719 break;
1720 }
1721# endif
1722#endif
1723
1724 /*
1725 * For profiling.
1726 */
1727 case VMMR0_DO_NOP:
1728 pGVCpu->vmm.s.iLastGZRc = VINF_SUCCESS;
1729 break;
1730
1731 /*
1732 * Shouldn't happen.
1733 */
1734 default:
1735 AssertMsgFailed(("%#x\n", enmOperation));
1736 pGVCpu->vmm.s.iLastGZRc = VERR_NOT_SUPPORTED;
1737 break;
1738 }
1739 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1740}
1741
1742
1743/**
1744 * Validates a session or VM session argument.
1745 *
1746 * @returns true / false accordingly.
1747 * @param pGVM The global (ring-0) VM structure.
1748 * @param pClaimedSession The session claim to validate.
1749 * @param pSession The session argument.
1750 */
1751DECLINLINE(bool) vmmR0IsValidSession(PGVM pGVM, PSUPDRVSESSION pClaimedSession, PSUPDRVSESSION pSession)
1752{
1753 /* This must be set! */
1754 if (!pSession)
1755 return false;
1756
1757 /* Only one out of the two. */
1758 if (pGVM && pClaimedSession)
1759 return false;
1760 if (pGVM)
1761 pClaimedSession = pGVM->pSession;
1762 return pClaimedSession == pSession;
1763}
1764
1765
1766/**
1767 * VMMR0EntryEx worker function, either called directly or when ever possible
1768 * called thru a longjmp so we can exit safely on failure.
1769 *
1770 * @returns VBox status code.
1771 * @param pGVM The global (ring-0) VM structure.
1772 * @param idCpu Virtual CPU ID argument. Must be NIL_VMCPUID if pVM
1773 * is NIL_RTR0PTR, and may be NIL_VMCPUID if it isn't
1774 * @param enmOperation Which operation to execute.
1775 * @param pReqHdr This points to a SUPVMMR0REQHDR packet. Optional.
1776 * The support driver validates this if it's present.
1777 * @param u64Arg Some simple constant argument.
1778 * @param pSession The session of the caller.
1779 *
1780 * @remarks Assume called with interrupts _enabled_.
1781 */
1782DECL_NO_INLINE(static, int) vmmR0EntryExWorker(PGVM pGVM, VMCPUID idCpu, VMMR0OPERATION enmOperation,
1783 PSUPVMMR0REQHDR pReqHdr, uint64_t u64Arg, PSUPDRVSESSION pSession)
1784{
1785 /*
1786 * Validate pGVM and idCpu for consistency and validity.
1787 */
1788 if (pGVM != NULL)
1789 {
1790 if (RT_LIKELY(((uintptr_t)pGVM & PAGE_OFFSET_MASK) == 0))
1791 { /* likely */ }
1792 else
1793 {
1794 SUPR0Printf("vmmR0EntryExWorker: Invalid pGVM=%p! (op=%d)\n", pGVM, enmOperation);
1795 return VERR_INVALID_POINTER;
1796 }
1797
1798 if (RT_LIKELY(idCpu == NIL_VMCPUID || idCpu < pGVM->cCpus))
1799 { /* likely */ }
1800 else
1801 {
1802 SUPR0Printf("vmmR0EntryExWorker: Invalid idCpu %#x (cCpus=%#x)\n", idCpu, pGVM->cCpus);
1803 return VERR_INVALID_PARAMETER;
1804 }
1805
1806 if (RT_LIKELY( pGVM->enmVMState >= VMSTATE_CREATING
1807 && pGVM->enmVMState <= VMSTATE_TERMINATED
1808 && pGVM->pSession == pSession
1809 && pGVM->pSelf == pGVM))
1810 { /* likely */ }
1811 else
1812 {
1813 SUPR0Printf("vmmR0EntryExWorker: Invalid pGVM=%p:{.enmVMState=%d, .cCpus=%#x, .pSession=%p(==%p), .pSelf=%p(==%p)}! (op=%d)\n",
1814 pGVM, pGVM->enmVMState, pGVM->cCpus, pGVM->pSession, pSession, pGVM->pSelf, pGVM, enmOperation);
1815 return VERR_INVALID_POINTER;
1816 }
1817 }
1818 else if (RT_LIKELY(idCpu == NIL_VMCPUID))
1819 { /* likely */ }
1820 else
1821 {
1822 SUPR0Printf("vmmR0EntryExWorker: Invalid idCpu=%u\n", idCpu);
1823 return VERR_INVALID_PARAMETER;
1824 }
1825
1826 /*
1827 * SMAP fun.
1828 */
1829 VMM_CHECK_SMAP_SETUP();
1830 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
1831
1832 /*
1833 * Process the request.
1834 */
1835 int rc;
1836 switch (enmOperation)
1837 {
1838 /*
1839 * GVM requests
1840 */
1841 case VMMR0_DO_GVMM_CREATE_VM:
1842 if (pGVM == NULL && u64Arg == 0 && idCpu == NIL_VMCPUID)
1843 rc = GVMMR0CreateVMReq((PGVMMCREATEVMREQ)pReqHdr, pSession);
1844 else
1845 rc = VERR_INVALID_PARAMETER;
1846 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
1847 break;
1848
1849 case VMMR0_DO_GVMM_DESTROY_VM:
1850 if (pReqHdr == NULL && u64Arg == 0)
1851 rc = GVMMR0DestroyVM(pGVM);
1852 else
1853 rc = VERR_INVALID_PARAMETER;
1854 VMM_CHECK_SMAP_CHECK(RT_NOTHING);
1855 break;
1856
1857 case VMMR0_DO_GVMM_REGISTER_VMCPU:
1858 if (pGVM != NULL)
1859 rc = GVMMR0RegisterVCpu(pGVM, idCpu);
1860 else
1861 rc = VERR_INVALID_PARAMETER;
1862 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1863 break;
1864
1865 case VMMR0_DO_GVMM_DEREGISTER_VMCPU:
1866 if (pGVM != NULL)
1867 rc = GVMMR0DeregisterVCpu(pGVM, idCpu);
1868 else
1869 rc = VERR_INVALID_PARAMETER;
1870 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1871 break;
1872
1873 case VMMR0_DO_GVMM_SCHED_HALT:
1874 if (pReqHdr)
1875 return VERR_INVALID_PARAMETER;
1876 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1877 rc = GVMMR0SchedHaltReq(pGVM, idCpu, u64Arg);
1878 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1879 break;
1880
1881 case VMMR0_DO_GVMM_SCHED_WAKE_UP:
1882 if (pReqHdr || u64Arg)
1883 return VERR_INVALID_PARAMETER;
1884 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1885 rc = GVMMR0SchedWakeUp(pGVM, idCpu);
1886 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1887 break;
1888
1889 case VMMR0_DO_GVMM_SCHED_POKE:
1890 if (pReqHdr || u64Arg)
1891 return VERR_INVALID_PARAMETER;
1892 rc = GVMMR0SchedPoke(pGVM, idCpu);
1893 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1894 break;
1895
1896 case VMMR0_DO_GVMM_SCHED_WAKE_UP_AND_POKE_CPUS:
1897 if (u64Arg)
1898 return VERR_INVALID_PARAMETER;
1899 rc = GVMMR0SchedWakeUpAndPokeCpusReq(pGVM, (PGVMMSCHEDWAKEUPANDPOKECPUSREQ)pReqHdr);
1900 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1901 break;
1902
1903 case VMMR0_DO_GVMM_SCHED_POLL:
1904 if (pReqHdr || u64Arg > 1)
1905 return VERR_INVALID_PARAMETER;
1906 rc = GVMMR0SchedPoll(pGVM, idCpu, !!u64Arg);
1907 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1908 break;
1909
1910 case VMMR0_DO_GVMM_QUERY_STATISTICS:
1911 if (u64Arg)
1912 return VERR_INVALID_PARAMETER;
1913 rc = GVMMR0QueryStatisticsReq(pGVM, (PGVMMQUERYSTATISTICSSREQ)pReqHdr, pSession);
1914 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1915 break;
1916
1917 case VMMR0_DO_GVMM_RESET_STATISTICS:
1918 if (u64Arg)
1919 return VERR_INVALID_PARAMETER;
1920 rc = GVMMR0ResetStatisticsReq(pGVM, (PGVMMRESETSTATISTICSSREQ)pReqHdr, pSession);
1921 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1922 break;
1923
1924 /*
1925 * Initialize the R0 part of a VM instance.
1926 */
1927 case VMMR0_DO_VMMR0_INIT:
1928 rc = vmmR0InitVM(pGVM, RT_LODWORD(u64Arg), RT_HIDWORD(u64Arg));
1929 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1930 break;
1931
1932 /*
1933 * Does EMT specific ring-0 init.
1934 */
1935 case VMMR0_DO_VMMR0_INIT_EMT:
1936 rc = vmmR0InitVMEmt(pGVM, idCpu);
1937 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1938 break;
1939
1940 /*
1941 * Terminate the R0 part of a VM instance.
1942 */
1943 case VMMR0_DO_VMMR0_TERM:
1944 rc = VMMR0TermVM(pGVM, 0 /*idCpu*/);
1945 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1946 break;
1947
1948 /*
1949 * Update release or debug logger instances.
1950 */
1951 case VMMR0_DO_VMMR0_UPDATE_LOGGERS:
1952 if (idCpu == NIL_VMCPUID)
1953 return VERR_INVALID_CPU_ID;
1954 if (u64Arg < VMMLOGGER_IDX_MAX && pReqHdr != NULL)
1955 rc = vmmR0UpdateLoggers(pGVM, idCpu /*idCpu*/, (PVMMR0UPDATELOGGERSREQ)pReqHdr, (size_t)u64Arg);
1956 else
1957 return VERR_INVALID_PARAMETER;
1958 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1959 break;
1960
1961 /*
1962 * Log flusher thread.
1963 */
1964 case VMMR0_DO_VMMR0_LOG_FLUSHER:
1965 if (idCpu != NIL_VMCPUID)
1966 return VERR_INVALID_CPU_ID;
1967 if (pReqHdr == NULL)
1968 rc = vmmR0LogFlusher(pGVM);
1969 else
1970 return VERR_INVALID_PARAMETER;
1971 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1972 break;
1973
1974 /*
1975 * Wait for the flush to finish with all the buffers for the given logger.
1976 */
1977 case VMMR0_DO_VMMR0_LOG_WAIT_FLUSHED:
1978 if (idCpu == NIL_VMCPUID)
1979 return VERR_INVALID_CPU_ID;
1980 if (u64Arg < VMMLOGGER_IDX_MAX && pReqHdr == NULL)
1981 rc = vmmR0LogWaitFlushed(pGVM, idCpu /*idCpu*/, (size_t)u64Arg);
1982 else
1983 return VERR_INVALID_PARAMETER;
1984 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1985 break;
1986
1987 /*
1988 * Attempt to enable hm mode and check the current setting.
1989 */
1990 case VMMR0_DO_HM_ENABLE:
1991 rc = HMR0EnableAllCpus(pGVM);
1992 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
1993 break;
1994
1995 /*
1996 * Setup the hardware accelerated session.
1997 */
1998 case VMMR0_DO_HM_SETUP_VM:
1999 rc = HMR0SetupVM(pGVM);
2000 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2001 break;
2002
2003 /*
2004 * PGM wrappers.
2005 */
2006 case VMMR0_DO_PGM_ALLOCATE_HANDY_PAGES:
2007 if (idCpu == NIL_VMCPUID)
2008 return VERR_INVALID_CPU_ID;
2009 rc = PGMR0PhysAllocateHandyPages(pGVM, idCpu);
2010 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2011 break;
2012
2013 case VMMR0_DO_PGM_FLUSH_HANDY_PAGES:
2014 if (idCpu == NIL_VMCPUID)
2015 return VERR_INVALID_CPU_ID;
2016 rc = PGMR0PhysFlushHandyPages(pGVM, idCpu);
2017 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2018 break;
2019
2020 case VMMR0_DO_PGM_ALLOCATE_LARGE_HANDY_PAGE:
2021 if (idCpu == NIL_VMCPUID)
2022 return VERR_INVALID_CPU_ID;
2023 rc = PGMR0PhysAllocateLargeHandyPage(pGVM, idCpu);
2024 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2025 break;
2026
2027 case VMMR0_DO_PGM_PHYS_SETUP_IOMMU:
2028 if (idCpu != 0)
2029 return VERR_INVALID_CPU_ID;
2030 rc = PGMR0PhysSetupIoMmu(pGVM);
2031 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2032 break;
2033
2034 case VMMR0_DO_PGM_POOL_GROW:
2035 if (idCpu == NIL_VMCPUID)
2036 return VERR_INVALID_CPU_ID;
2037 rc = PGMR0PoolGrow(pGVM);
2038 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2039 break;
2040
2041 /*
2042 * GMM wrappers.
2043 */
2044 case VMMR0_DO_GMM_INITIAL_RESERVATION:
2045 if (u64Arg)
2046 return VERR_INVALID_PARAMETER;
2047 rc = GMMR0InitialReservationReq(pGVM, idCpu, (PGMMINITIALRESERVATIONREQ)pReqHdr);
2048 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2049 break;
2050
2051 case VMMR0_DO_GMM_UPDATE_RESERVATION:
2052 if (u64Arg)
2053 return VERR_INVALID_PARAMETER;
2054 rc = GMMR0UpdateReservationReq(pGVM, idCpu, (PGMMUPDATERESERVATIONREQ)pReqHdr);
2055 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2056 break;
2057
2058 case VMMR0_DO_GMM_ALLOCATE_PAGES:
2059 if (u64Arg)
2060 return VERR_INVALID_PARAMETER;
2061 rc = GMMR0AllocatePagesReq(pGVM, idCpu, (PGMMALLOCATEPAGESREQ)pReqHdr);
2062 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2063 break;
2064
2065 case VMMR0_DO_GMM_FREE_PAGES:
2066 if (u64Arg)
2067 return VERR_INVALID_PARAMETER;
2068 rc = GMMR0FreePagesReq(pGVM, idCpu, (PGMMFREEPAGESREQ)pReqHdr);
2069 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2070 break;
2071
2072 case VMMR0_DO_GMM_FREE_LARGE_PAGE:
2073 if (u64Arg)
2074 return VERR_INVALID_PARAMETER;
2075 rc = GMMR0FreeLargePageReq(pGVM, idCpu, (PGMMFREELARGEPAGEREQ)pReqHdr);
2076 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2077 break;
2078
2079 case VMMR0_DO_GMM_QUERY_HYPERVISOR_MEM_STATS:
2080 if (u64Arg)
2081 return VERR_INVALID_PARAMETER;
2082 rc = GMMR0QueryHypervisorMemoryStatsReq((PGMMMEMSTATSREQ)pReqHdr);
2083 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2084 break;
2085
2086 case VMMR0_DO_GMM_QUERY_MEM_STATS:
2087 if (idCpu == NIL_VMCPUID)
2088 return VERR_INVALID_CPU_ID;
2089 if (u64Arg)
2090 return VERR_INVALID_PARAMETER;
2091 rc = GMMR0QueryMemoryStatsReq(pGVM, idCpu, (PGMMMEMSTATSREQ)pReqHdr);
2092 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2093 break;
2094
2095 case VMMR0_DO_GMM_BALLOONED_PAGES:
2096 if (u64Arg)
2097 return VERR_INVALID_PARAMETER;
2098 rc = GMMR0BalloonedPagesReq(pGVM, idCpu, (PGMMBALLOONEDPAGESREQ)pReqHdr);
2099 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2100 break;
2101
2102 case VMMR0_DO_GMM_MAP_UNMAP_CHUNK:
2103 if (u64Arg)
2104 return VERR_INVALID_PARAMETER;
2105 rc = GMMR0MapUnmapChunkReq(pGVM, (PGMMMAPUNMAPCHUNKREQ)pReqHdr);
2106 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2107 break;
2108
2109 case VMMR0_DO_GMM_SEED_CHUNK:
2110 if (pReqHdr)
2111 return VERR_INVALID_PARAMETER;
2112 rc = GMMR0SeedChunk(pGVM, idCpu, (RTR3PTR)u64Arg);
2113 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2114 break;
2115
2116 case VMMR0_DO_GMM_REGISTER_SHARED_MODULE:
2117 if (idCpu == NIL_VMCPUID)
2118 return VERR_INVALID_CPU_ID;
2119 if (u64Arg)
2120 return VERR_INVALID_PARAMETER;
2121 rc = GMMR0RegisterSharedModuleReq(pGVM, idCpu, (PGMMREGISTERSHAREDMODULEREQ)pReqHdr);
2122 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2123 break;
2124
2125 case VMMR0_DO_GMM_UNREGISTER_SHARED_MODULE:
2126 if (idCpu == NIL_VMCPUID)
2127 return VERR_INVALID_CPU_ID;
2128 if (u64Arg)
2129 return VERR_INVALID_PARAMETER;
2130 rc = GMMR0UnregisterSharedModuleReq(pGVM, idCpu, (PGMMUNREGISTERSHAREDMODULEREQ)pReqHdr);
2131 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2132 break;
2133
2134 case VMMR0_DO_GMM_RESET_SHARED_MODULES:
2135 if (idCpu == NIL_VMCPUID)
2136 return VERR_INVALID_CPU_ID;
2137 if ( u64Arg
2138 || pReqHdr)
2139 return VERR_INVALID_PARAMETER;
2140 rc = GMMR0ResetSharedModules(pGVM, idCpu);
2141 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2142 break;
2143
2144#ifdef VBOX_WITH_PAGE_SHARING
2145 case VMMR0_DO_GMM_CHECK_SHARED_MODULES:
2146 {
2147 if (idCpu == NIL_VMCPUID)
2148 return VERR_INVALID_CPU_ID;
2149 if ( u64Arg
2150 || pReqHdr)
2151 return VERR_INVALID_PARAMETER;
2152 rc = GMMR0CheckSharedModules(pGVM, idCpu);
2153 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2154 break;
2155 }
2156#endif
2157
2158#if defined(VBOX_STRICT) && HC_ARCH_BITS == 64
2159 case VMMR0_DO_GMM_FIND_DUPLICATE_PAGE:
2160 if (u64Arg)
2161 return VERR_INVALID_PARAMETER;
2162 rc = GMMR0FindDuplicatePageReq(pGVM, (PGMMFINDDUPLICATEPAGEREQ)pReqHdr);
2163 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2164 break;
2165#endif
2166
2167 case VMMR0_DO_GMM_QUERY_STATISTICS:
2168 if (u64Arg)
2169 return VERR_INVALID_PARAMETER;
2170 rc = GMMR0QueryStatisticsReq(pGVM, (PGMMQUERYSTATISTICSSREQ)pReqHdr);
2171 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2172 break;
2173
2174 case VMMR0_DO_GMM_RESET_STATISTICS:
2175 if (u64Arg)
2176 return VERR_INVALID_PARAMETER;
2177 rc = GMMR0ResetStatisticsReq(pGVM, (PGMMRESETSTATISTICSSREQ)pReqHdr);
2178 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2179 break;
2180
2181 /*
2182 * A quick GCFGM mock-up.
2183 */
2184 /** @todo GCFGM with proper access control, ring-3 management interface and all that. */
2185 case VMMR0_DO_GCFGM_SET_VALUE:
2186 case VMMR0_DO_GCFGM_QUERY_VALUE:
2187 {
2188 if (pGVM || !pReqHdr || u64Arg || idCpu != NIL_VMCPUID)
2189 return VERR_INVALID_PARAMETER;
2190 PGCFGMVALUEREQ pReq = (PGCFGMVALUEREQ)pReqHdr;
2191 if (pReq->Hdr.cbReq != sizeof(*pReq))
2192 return VERR_INVALID_PARAMETER;
2193 if (enmOperation == VMMR0_DO_GCFGM_SET_VALUE)
2194 {
2195 rc = GVMMR0SetConfig(pReq->pSession, &pReq->szName[0], pReq->u64Value);
2196 //if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2197 // rc = GMMR0SetConfig(pReq->pSession, &pReq->szName[0], pReq->u64Value);
2198 }
2199 else
2200 {
2201 rc = GVMMR0QueryConfig(pReq->pSession, &pReq->szName[0], &pReq->u64Value);
2202 //if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2203 // rc = GMMR0QueryConfig(pReq->pSession, &pReq->szName[0], &pReq->u64Value);
2204 }
2205 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2206 break;
2207 }
2208
2209 /*
2210 * PDM Wrappers.
2211 */
2212 case VMMR0_DO_PDM_DRIVER_CALL_REQ_HANDLER:
2213 {
2214 if (!pReqHdr || u64Arg || idCpu != NIL_VMCPUID)
2215 return VERR_INVALID_PARAMETER;
2216 rc = PDMR0DriverCallReqHandler(pGVM, (PPDMDRIVERCALLREQHANDLERREQ)pReqHdr);
2217 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2218 break;
2219 }
2220
2221 case VMMR0_DO_PDM_DEVICE_CREATE:
2222 {
2223 if (!pReqHdr || u64Arg || idCpu != 0)
2224 return VERR_INVALID_PARAMETER;
2225 rc = PDMR0DeviceCreateReqHandler(pGVM, (PPDMDEVICECREATEREQ)pReqHdr);
2226 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2227 break;
2228 }
2229
2230 case VMMR0_DO_PDM_DEVICE_GEN_CALL:
2231 {
2232 if (!pReqHdr || u64Arg)
2233 return VERR_INVALID_PARAMETER;
2234 rc = PDMR0DeviceGenCallReqHandler(pGVM, (PPDMDEVICEGENCALLREQ)pReqHdr, idCpu);
2235 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2236 break;
2237 }
2238
2239 /** @todo Remove the once all devices has been converted to new style! @bugref{9218} */
2240 case VMMR0_DO_PDM_DEVICE_COMPAT_SET_CRITSECT:
2241 {
2242 if (!pReqHdr || u64Arg || idCpu != 0)
2243 return VERR_INVALID_PARAMETER;
2244 rc = PDMR0DeviceCompatSetCritSectReqHandler(pGVM, (PPDMDEVICECOMPATSETCRITSECTREQ)pReqHdr);
2245 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2246 break;
2247 }
2248
2249 /*
2250 * Requests to the internal networking service.
2251 */
2252 case VMMR0_DO_INTNET_OPEN:
2253 {
2254 PINTNETOPENREQ pReq = (PINTNETOPENREQ)pReqHdr;
2255 if (u64Arg || !pReq || !vmmR0IsValidSession(pGVM, pReq->pSession, pSession) || idCpu != NIL_VMCPUID)
2256 return VERR_INVALID_PARAMETER;
2257 rc = IntNetR0OpenReq(pSession, pReq);
2258 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2259 break;
2260 }
2261
2262 case VMMR0_DO_INTNET_IF_CLOSE:
2263 if (u64Arg || !pReqHdr || !vmmR0IsValidSession(pGVM, ((PINTNETIFCLOSEREQ)pReqHdr)->pSession, pSession) || idCpu != NIL_VMCPUID)
2264 return VERR_INVALID_PARAMETER;
2265 rc = IntNetR0IfCloseReq(pSession, (PINTNETIFCLOSEREQ)pReqHdr);
2266 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2267 break;
2268
2269
2270 case VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS:
2271 if (u64Arg || !pReqHdr || !vmmR0IsValidSession(pGVM, ((PINTNETIFGETBUFFERPTRSREQ)pReqHdr)->pSession, pSession) || idCpu != NIL_VMCPUID)
2272 return VERR_INVALID_PARAMETER;
2273 rc = IntNetR0IfGetBufferPtrsReq(pSession, (PINTNETIFGETBUFFERPTRSREQ)pReqHdr);
2274 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2275 break;
2276
2277 case VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE:
2278 if (u64Arg || !pReqHdr || !vmmR0IsValidSession(pGVM, ((PINTNETIFSETPROMISCUOUSMODEREQ)pReqHdr)->pSession, pSession) || idCpu != NIL_VMCPUID)
2279 return VERR_INVALID_PARAMETER;
2280 rc = IntNetR0IfSetPromiscuousModeReq(pSession, (PINTNETIFSETPROMISCUOUSMODEREQ)pReqHdr);
2281 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2282 break;
2283
2284 case VMMR0_DO_INTNET_IF_SET_MAC_ADDRESS:
2285 if (u64Arg || !pReqHdr || !vmmR0IsValidSession(pGVM, ((PINTNETIFSETMACADDRESSREQ)pReqHdr)->pSession, pSession) || idCpu != NIL_VMCPUID)
2286 return VERR_INVALID_PARAMETER;
2287 rc = IntNetR0IfSetMacAddressReq(pSession, (PINTNETIFSETMACADDRESSREQ)pReqHdr);
2288 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2289 break;
2290
2291 case VMMR0_DO_INTNET_IF_SET_ACTIVE:
2292 if (u64Arg || !pReqHdr || !vmmR0IsValidSession(pGVM, ((PINTNETIFSETACTIVEREQ)pReqHdr)->pSession, pSession) || idCpu != NIL_VMCPUID)
2293 return VERR_INVALID_PARAMETER;
2294 rc = IntNetR0IfSetActiveReq(pSession, (PINTNETIFSETACTIVEREQ)pReqHdr);
2295 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2296 break;
2297
2298 case VMMR0_DO_INTNET_IF_SEND:
2299 if (u64Arg || !pReqHdr || !vmmR0IsValidSession(pGVM, ((PINTNETIFSENDREQ)pReqHdr)->pSession, pSession) || idCpu != NIL_VMCPUID)
2300 return VERR_INVALID_PARAMETER;
2301 rc = IntNetR0IfSendReq(pSession, (PINTNETIFSENDREQ)pReqHdr);
2302 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2303 break;
2304
2305 case VMMR0_DO_INTNET_IF_WAIT:
2306 if (u64Arg || !pReqHdr || !vmmR0IsValidSession(pGVM, ((PINTNETIFWAITREQ)pReqHdr)->pSession, pSession) || idCpu != NIL_VMCPUID)
2307 return VERR_INVALID_PARAMETER;
2308 rc = IntNetR0IfWaitReq(pSession, (PINTNETIFWAITREQ)pReqHdr);
2309 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2310 break;
2311
2312 case VMMR0_DO_INTNET_IF_ABORT_WAIT:
2313 if (u64Arg || !pReqHdr || !vmmR0IsValidSession(pGVM, ((PINTNETIFWAITREQ)pReqHdr)->pSession, pSession) || idCpu != NIL_VMCPUID)
2314 return VERR_INVALID_PARAMETER;
2315 rc = IntNetR0IfAbortWaitReq(pSession, (PINTNETIFABORTWAITREQ)pReqHdr);
2316 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2317 break;
2318
2319#if 0 //def VBOX_WITH_PCI_PASSTHROUGH
2320 /*
2321 * Requests to host PCI driver service.
2322 */
2323 case VMMR0_DO_PCIRAW_REQ:
2324 if (u64Arg || !pReqHdr || !vmmR0IsValidSession(pGVM, ((PPCIRAWSENDREQ)pReqHdr)->pSession, pSession) || idCpu != NIL_VMCPUID)
2325 return VERR_INVALID_PARAMETER;
2326 rc = PciRawR0ProcessReq(pGVM, pSession, (PPCIRAWSENDREQ)pReqHdr);
2327 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2328 break;
2329#endif
2330
2331 /*
2332 * NEM requests.
2333 */
2334#ifdef VBOX_WITH_NEM_R0
2335# if defined(RT_ARCH_AMD64) && defined(RT_OS_WINDOWS)
2336 case VMMR0_DO_NEM_INIT_VM:
2337 if (u64Arg || pReqHdr || idCpu != 0)
2338 return VERR_INVALID_PARAMETER;
2339 rc = NEMR0InitVM(pGVM);
2340 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2341 break;
2342
2343 case VMMR0_DO_NEM_INIT_VM_PART_2:
2344 if (u64Arg || pReqHdr || idCpu != 0)
2345 return VERR_INVALID_PARAMETER;
2346 rc = NEMR0InitVMPart2(pGVM);
2347 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2348 break;
2349
2350 case VMMR0_DO_NEM_MAP_PAGES:
2351 if (u64Arg || pReqHdr || idCpu == NIL_VMCPUID)
2352 return VERR_INVALID_PARAMETER;
2353 rc = NEMR0MapPages(pGVM, idCpu);
2354 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2355 break;
2356
2357 case VMMR0_DO_NEM_UNMAP_PAGES:
2358 if (u64Arg || pReqHdr || idCpu == NIL_VMCPUID)
2359 return VERR_INVALID_PARAMETER;
2360 rc = NEMR0UnmapPages(pGVM, idCpu);
2361 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2362 break;
2363
2364 case VMMR0_DO_NEM_EXPORT_STATE:
2365 if (u64Arg || pReqHdr || idCpu == NIL_VMCPUID)
2366 return VERR_INVALID_PARAMETER;
2367 rc = NEMR0ExportState(pGVM, idCpu);
2368 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2369 break;
2370
2371 case VMMR0_DO_NEM_IMPORT_STATE:
2372 if (pReqHdr || idCpu == NIL_VMCPUID)
2373 return VERR_INVALID_PARAMETER;
2374 rc = NEMR0ImportState(pGVM, idCpu, u64Arg);
2375 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2376 break;
2377
2378 case VMMR0_DO_NEM_QUERY_CPU_TICK:
2379 if (u64Arg || pReqHdr || idCpu == NIL_VMCPUID)
2380 return VERR_INVALID_PARAMETER;
2381 rc = NEMR0QueryCpuTick(pGVM, idCpu);
2382 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2383 break;
2384
2385 case VMMR0_DO_NEM_RESUME_CPU_TICK_ON_ALL:
2386 if (pReqHdr || idCpu == NIL_VMCPUID)
2387 return VERR_INVALID_PARAMETER;
2388 rc = NEMR0ResumeCpuTickOnAll(pGVM, idCpu, u64Arg);
2389 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2390 break;
2391
2392 case VMMR0_DO_NEM_UPDATE_STATISTICS:
2393 if (u64Arg || pReqHdr)
2394 return VERR_INVALID_PARAMETER;
2395 rc = NEMR0UpdateStatistics(pGVM, idCpu);
2396 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2397 break;
2398
2399# if 1 && defined(DEBUG_bird)
2400 case VMMR0_DO_NEM_EXPERIMENT:
2401 if (pReqHdr)
2402 return VERR_INVALID_PARAMETER;
2403 rc = NEMR0DoExperiment(pGVM, idCpu, u64Arg);
2404 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2405 break;
2406# endif
2407# endif
2408#endif
2409
2410 /*
2411 * IOM requests.
2412 */
2413 case VMMR0_DO_IOM_GROW_IO_PORTS:
2414 {
2415 if (pReqHdr || idCpu != 0)
2416 return VERR_INVALID_PARAMETER;
2417 rc = IOMR0IoPortGrowRegistrationTables(pGVM, u64Arg);
2418 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2419 break;
2420 }
2421
2422 case VMMR0_DO_IOM_GROW_IO_PORT_STATS:
2423 {
2424 if (pReqHdr || idCpu != 0)
2425 return VERR_INVALID_PARAMETER;
2426 rc = IOMR0IoPortGrowStatisticsTable(pGVM, u64Arg);
2427 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2428 break;
2429 }
2430
2431 case VMMR0_DO_IOM_GROW_MMIO_REGS:
2432 {
2433 if (pReqHdr || idCpu != 0)
2434 return VERR_INVALID_PARAMETER;
2435 rc = IOMR0MmioGrowRegistrationTables(pGVM, u64Arg);
2436 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2437 break;
2438 }
2439
2440 case VMMR0_DO_IOM_GROW_MMIO_STATS:
2441 {
2442 if (pReqHdr || idCpu != 0)
2443 return VERR_INVALID_PARAMETER;
2444 rc = IOMR0MmioGrowStatisticsTable(pGVM, u64Arg);
2445 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2446 break;
2447 }
2448
2449 case VMMR0_DO_IOM_SYNC_STATS_INDICES:
2450 {
2451 if (pReqHdr || idCpu != 0)
2452 return VERR_INVALID_PARAMETER;
2453 rc = IOMR0IoPortSyncStatisticsIndices(pGVM);
2454 if (RT_SUCCESS(rc))
2455 rc = IOMR0MmioSyncStatisticsIndices(pGVM);
2456 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2457 break;
2458 }
2459
2460 /*
2461 * DBGF requests.
2462 */
2463#ifdef VBOX_WITH_DBGF_TRACING
2464 case VMMR0_DO_DBGF_TRACER_CREATE:
2465 {
2466 if (!pReqHdr || u64Arg || idCpu != 0)
2467 return VERR_INVALID_PARAMETER;
2468 rc = DBGFR0TracerCreateReqHandler(pGVM, (PDBGFTRACERCREATEREQ)pReqHdr);
2469 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2470 break;
2471 }
2472
2473 case VMMR0_DO_DBGF_TRACER_CALL_REQ_HANDLER:
2474 {
2475 if (!pReqHdr || u64Arg)
2476 return VERR_INVALID_PARAMETER;
2477# if 0 /** @todo */
2478 rc = DBGFR0TracerGenCallReqHandler(pGVM, (PDBGFTRACERGENCALLREQ)pReqHdr, idCpu);
2479# else
2480 rc = VERR_NOT_IMPLEMENTED;
2481# endif
2482 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2483 break;
2484 }
2485#endif
2486
2487 case VMMR0_DO_DBGF_BP_INIT:
2488 {
2489 if (!pReqHdr || u64Arg || idCpu != 0)
2490 return VERR_INVALID_PARAMETER;
2491 rc = DBGFR0BpInitReqHandler(pGVM, (PDBGFBPINITREQ)pReqHdr);
2492 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2493 break;
2494 }
2495
2496 case VMMR0_DO_DBGF_BP_CHUNK_ALLOC:
2497 {
2498 if (!pReqHdr || u64Arg || idCpu != 0)
2499 return VERR_INVALID_PARAMETER;
2500 rc = DBGFR0BpChunkAllocReqHandler(pGVM, (PDBGFBPCHUNKALLOCREQ)pReqHdr);
2501 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2502 break;
2503 }
2504
2505 case VMMR0_DO_DBGF_BP_L2_TBL_CHUNK_ALLOC:
2506 {
2507 if (!pReqHdr || u64Arg || idCpu != 0)
2508 return VERR_INVALID_PARAMETER;
2509 rc = DBGFR0BpL2TblChunkAllocReqHandler(pGVM, (PDBGFBPL2TBLCHUNKALLOCREQ)pReqHdr);
2510 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2511 break;
2512 }
2513
2514 case VMMR0_DO_DBGF_BP_OWNER_INIT:
2515 {
2516 if (!pReqHdr || u64Arg || idCpu != 0)
2517 return VERR_INVALID_PARAMETER;
2518 rc = DBGFR0BpOwnerInitReqHandler(pGVM, (PDBGFBPOWNERINITREQ)pReqHdr);
2519 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2520 break;
2521 }
2522
2523 case VMMR0_DO_DBGF_BP_PORTIO_INIT:
2524 {
2525 if (!pReqHdr || u64Arg || idCpu != 0)
2526 return VERR_INVALID_PARAMETER;
2527 rc = DBGFR0BpPortIoInitReqHandler(pGVM, (PDBGFBPINITREQ)pReqHdr);
2528 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2529 break;
2530 }
2531
2532
2533 /*
2534 * TM requests.
2535 */
2536 case VMMR0_DO_TM_GROW_TIMER_QUEUE:
2537 {
2538 if (pReqHdr || idCpu == NIL_VMCPUID)
2539 return VERR_INVALID_PARAMETER;
2540 rc = TMR0TimerQueueGrow(pGVM, RT_HI_U32(u64Arg), RT_LO_U32(u64Arg));
2541 VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
2542 break;
2543 }
2544
2545 /*
2546 * For profiling.
2547 */
2548 case VMMR0_DO_NOP:
2549 case VMMR0_DO_SLOW_NOP:
2550 return VINF_SUCCESS;
2551
2552 /*
2553 * For testing Ring-0 APIs invoked in this environment.
2554 */
2555 case VMMR0_DO_TESTS:
2556 /** @todo make new test */
2557 return VINF_SUCCESS;
2558
2559 default:
2560 /*
2561 * We're returning VERR_NOT_SUPPORT here so we've got something else
2562 * than -1 which the interrupt gate glue code might return.
2563 */
2564 Log(("operation %#x is not supported\n", enmOperation));
2565 return VERR_NOT_SUPPORTED;
2566 }
2567 return rc;
2568}
2569
2570
2571/**
2572 * This is just a longjmp wrapper function for VMMR0EntryEx calls.
2573 *
2574 * @returns VBox status code.
2575 * @param pvArgs The argument package
2576 */
2577static DECLCALLBACK(int) vmmR0EntryExWrapper(void *pvArgs)
2578{
2579 PGVMCPU pGVCpu = (PGVMCPU)pvArgs;
2580 return vmmR0EntryExWorker(pGVCpu->vmmr0.s.pGVM,
2581 pGVCpu->vmmr0.s.idCpu,
2582 pGVCpu->vmmr0.s.enmOperation,
2583 pGVCpu->vmmr0.s.pReq,
2584 pGVCpu->vmmr0.s.u64Arg,
2585 pGVCpu->vmmr0.s.pSession);
2586}
2587
2588
2589/**
2590 * The Ring 0 entry point, called by the support library (SUP).
2591 *
2592 * @returns VBox status code.
2593 * @param pGVM The global (ring-0) VM structure.
2594 * @param pVM The cross context VM structure.
2595 * @param idCpu Virtual CPU ID argument. Must be NIL_VMCPUID if pVM
2596 * is NIL_RTR0PTR, and may be NIL_VMCPUID if it isn't
2597 * @param enmOperation Which operation to execute.
2598 * @param pReq Pointer to the SUPVMMR0REQHDR packet. Optional.
2599 * @param u64Arg Some simple constant argument.
2600 * @param pSession The session of the caller.
2601 * @remarks Assume called with interrupts _enabled_.
2602 */
2603VMMR0DECL(int) VMMR0EntryEx(PGVM pGVM, PVMCC pVM, VMCPUID idCpu, VMMR0OPERATION enmOperation,
2604 PSUPVMMR0REQHDR pReq, uint64_t u64Arg, PSUPDRVSESSION pSession)
2605{
2606 /*
2607 * Requests that should only happen on the EMT thread will be
2608 * wrapped in a setjmp so we can assert without causing trouble.
2609 */
2610 if ( pVM != NULL
2611 && pGVM != NULL
2612 && pVM == pGVM /** @todo drop pVM or pGVM */
2613 && idCpu < pGVM->cCpus
2614 && pGVM->pSession == pSession
2615 && pGVM->pSelf == pVM)
2616 {
2617 switch (enmOperation)
2618 {
2619 /* These might/will be called before VMMR3Init. */
2620 case VMMR0_DO_GMM_INITIAL_RESERVATION:
2621 case VMMR0_DO_GMM_UPDATE_RESERVATION:
2622 case VMMR0_DO_GMM_ALLOCATE_PAGES:
2623 case VMMR0_DO_GMM_FREE_PAGES:
2624 case VMMR0_DO_GMM_BALLOONED_PAGES:
2625 /* On the mac we might not have a valid jmp buf, so check these as well. */
2626 case VMMR0_DO_VMMR0_INIT:
2627 case VMMR0_DO_VMMR0_TERM:
2628
2629 case VMMR0_DO_PDM_DEVICE_CREATE:
2630 case VMMR0_DO_PDM_DEVICE_GEN_CALL:
2631 case VMMR0_DO_IOM_GROW_IO_PORTS:
2632 case VMMR0_DO_IOM_GROW_IO_PORT_STATS:
2633 case VMMR0_DO_DBGF_BP_INIT:
2634 case VMMR0_DO_DBGF_BP_CHUNK_ALLOC:
2635 case VMMR0_DO_DBGF_BP_L2_TBL_CHUNK_ALLOC:
2636 {
2637 PGVMCPU pGVCpu = &pGVM->aCpus[idCpu];
2638 RTNATIVETHREAD hNativeThread = RTThreadNativeSelf();
2639 if (RT_LIKELY( pGVCpu->hEMT == hNativeThread
2640 && pGVCpu->hNativeThreadR0 == hNativeThread))
2641 {
2642 if (!pGVCpu->vmm.s.CallRing3JmpBufR0.pvSavedStack)
2643 break;
2644
2645 pGVCpu->vmmr0.s.pGVM = pGVM;
2646 pGVCpu->vmmr0.s.idCpu = idCpu;
2647 pGVCpu->vmmr0.s.enmOperation = enmOperation;
2648 pGVCpu->vmmr0.s.pReq = pReq;
2649 pGVCpu->vmmr0.s.u64Arg = u64Arg;
2650 pGVCpu->vmmr0.s.pSession = pSession;
2651 return vmmR0CallRing3SetJmpEx(&pGVCpu->vmm.s.CallRing3JmpBufR0, vmmR0EntryExWrapper, pGVCpu,
2652 ((uintptr_t)u64Arg << 16) | (uintptr_t)enmOperation);
2653 }
2654 return VERR_VM_THREAD_NOT_EMT;
2655 }
2656
2657 default:
2658 case VMMR0_DO_PGM_POOL_GROW:
2659 break;
2660 }
2661 }
2662 return vmmR0EntryExWorker(pGVM, idCpu, enmOperation, pReq, u64Arg, pSession);
2663}
2664
2665
2666/*********************************************************************************************************************************
2667* EMT Blocking *
2668*********************************************************************************************************************************/
2669
2670/**
2671 * Checks whether we've armed the ring-0 long jump machinery.
2672 *
2673 * @returns @c true / @c false
2674 * @param pVCpu The cross context virtual CPU structure.
2675 * @thread EMT
2676 * @sa VMMIsLongJumpArmed
2677 */
2678VMMR0_INT_DECL(bool) VMMR0IsLongJumpArmed(PVMCPUCC pVCpu)
2679{
2680#ifdef RT_ARCH_X86
2681 return pVCpu->vmm.s.CallRing3JmpBufR0.eip
2682 && !pVCpu->vmm.s.CallRing3JmpBufR0.fInRing3Call;
2683#else
2684 return pVCpu->vmm.s.CallRing3JmpBufR0.rip
2685 && !pVCpu->vmm.s.CallRing3JmpBufR0.fInRing3Call;
2686#endif
2687}
2688
2689
2690/**
2691 * Checks whether we've done a ring-3 long jump.
2692 *
2693 * @returns @c true / @c false
2694 * @param pVCpu The cross context virtual CPU structure.
2695 * @thread EMT
2696 */
2697VMMR0_INT_DECL(bool) VMMR0IsInRing3LongJump(PVMCPUCC pVCpu)
2698{
2699 return pVCpu->vmm.s.CallRing3JmpBufR0.fInRing3Call;
2700}
2701
2702
2703/**
2704 * Locking helper that deals with HM context and checks if the thread can block.
2705 *
2706 * @returns VINF_SUCCESS if we can block. Returns @a rcBusy or
2707 * VERR_VMM_CANNOT_BLOCK if not able to block.
2708 * @param pVCpu The cross context virtual CPU structure of the calling
2709 * thread.
2710 * @param rcBusy What to return in case of a blocking problem. Will IPE
2711 * if VINF_SUCCESS and we cannot block.
2712 * @param pszCaller The caller (for logging problems).
2713 * @param pvLock The lock address (for logging problems).
2714 * @param pCtx Where to return context info for the resume call.
2715 * @thread EMT(pVCpu)
2716 */
2717VMMR0_INT_DECL(int) VMMR0EmtPrepareToBlock(PVMCPUCC pVCpu, int rcBusy, const char *pszCaller, void *pvLock,
2718 PVMMR0EMTBLOCKCTX pCtx)
2719{
2720 const char *pszMsg;
2721
2722 /*
2723 * Check that we are allowed to block.
2724 */
2725 if (RT_LIKELY(VMMRZCallRing3IsEnabled(pVCpu)))
2726 {
2727 /*
2728 * Are we in HM context and w/o a context hook? If so work the context hook.
2729 */
2730 if (pVCpu->idHostCpu != NIL_RTCPUID)
2731 {
2732 Assert(pVCpu->iHostCpuSet != UINT32_MAX);
2733
2734 if (pVCpu->vmmr0.s.hCtxHook == NIL_RTTHREADCTXHOOK)
2735 {
2736 vmmR0ThreadCtxCallback(RTTHREADCTXEVENT_OUT, pVCpu);
2737 if (pVCpu->vmmr0.s.pPreemptState)
2738 RTThreadPreemptRestore(pVCpu->vmmr0.s.pPreemptState);
2739
2740 pCtx->uMagic = VMMR0EMTBLOCKCTX_MAGIC;
2741 pCtx->fWasInHmContext = true;
2742 return VINF_SUCCESS;
2743 }
2744 }
2745
2746 if (RT_LIKELY(!pVCpu->vmmr0.s.pPreemptState))
2747 {
2748 /*
2749 * Not in HM context or we've got hooks, so just check that preemption
2750 * is enabled.
2751 */
2752 if (RT_LIKELY(RTThreadPreemptIsEnabled(NIL_RTTHREAD)))
2753 {
2754 pCtx->uMagic = VMMR0EMTBLOCKCTX_MAGIC;
2755 pCtx->fWasInHmContext = false;
2756 return VINF_SUCCESS;
2757 }
2758 pszMsg = "Preemption is disabled!";
2759 }
2760 else
2761 pszMsg = "Preemption state w/o HM state!";
2762 }
2763 else
2764 pszMsg = "Ring-3 calls are disabled!";
2765
2766 static uint32_t volatile s_cWarnings = 0;
2767 if (++s_cWarnings < 50)
2768 SUPR0Printf("VMMR0EmtPrepareToBlock: %s pvLock=%p pszCaller=%s rcBusy=%p\n", pszMsg, pvLock, pszCaller, rcBusy);
2769 pCtx->uMagic = VMMR0EMTBLOCKCTX_MAGIC_DEAD;
2770 pCtx->fWasInHmContext = false;
2771 return rcBusy != VINF_SUCCESS ? rcBusy : VERR_VMM_CANNOT_BLOCK;
2772}
2773
2774
2775/**
2776 * Counterpart to VMMR0EmtPrepareToBlock.
2777 *
2778 * @param pVCpu The cross context virtual CPU structure of the calling
2779 * thread.
2780 * @param pCtx The context structure used with VMMR0EmtPrepareToBlock.
2781 * @thread EMT(pVCpu)
2782 */
2783VMMR0_INT_DECL(void) VMMR0EmtResumeAfterBlocking(PVMCPUCC pVCpu, PVMMR0EMTBLOCKCTX pCtx)
2784{
2785 AssertReturnVoid(pCtx->uMagic == VMMR0EMTBLOCKCTX_MAGIC);
2786 if (pCtx->fWasInHmContext)
2787 {
2788 if (pVCpu->vmmr0.s.pPreemptState)
2789 RTThreadPreemptDisable(pVCpu->vmmr0.s.pPreemptState);
2790
2791 pCtx->fWasInHmContext = false;
2792 vmmR0ThreadCtxCallback(RTTHREADCTXEVENT_IN, pVCpu);
2793 }
2794 pCtx->uMagic = VMMR0EMTBLOCKCTX_MAGIC_DEAD;
2795}
2796
2797/** @name VMMR0EMTWAIT_F_XXX - flags for VMMR0EmtWaitEventInner and friends.
2798 * @{ */
2799/** Try suppress VERR_INTERRUPTED for a little while (~10 sec). */
2800#define VMMR0EMTWAIT_F_TRY_SUPPRESS_INTERRUPTED RT_BIT_32(0)
2801/** @} */
2802
2803/**
2804 * Helper for waiting on an RTSEMEVENT, caller did VMMR0EmtPrepareToBlock.
2805 *
2806 * @returns
2807 * @retval VERR_THREAD_IS_TERMINATING
2808 * @retval VERR_TIMEOUT if we ended up waiting too long, either according to
2809 * @a cMsTimeout or to maximum wait values.
2810 *
2811 * @param pGVCpu The ring-0 virtual CPU structure.
2812 * @param fFlags VMMR0EMTWAIT_F_XXX.
2813 * @param hEvent The event to wait on.
2814 * @param cMsTimeout The timeout or RT_INDEFINITE_WAIT.
2815 */
2816VMMR0DECL(int) VMMR0EmtWaitEventInner(PGVMCPU pGVCpu, uint32_t fFlags, RTSEMEVENT hEvent, RTMSINTERVAL cMsTimeout)
2817{
2818 AssertReturn(pGVCpu->hEMT == RTThreadNativeSelf(), VERR_VM_THREAD_NOT_EMT);
2819
2820 /*
2821 * Note! Similar code is found in the PDM critical sections too.
2822 */
2823 uint64_t const nsStart = RTTimeNanoTS();
2824 uint64_t cNsMaxTotal = cMsTimeout == RT_INDEFINITE_WAIT
2825 ? RT_NS_5MIN : RT_MIN(RT_NS_5MIN, RT_NS_1MS_64 * cMsTimeout);
2826 uint32_t cMsMaxOne = RT_MS_5SEC;
2827 bool fNonInterruptible = false;
2828 for (;;)
2829 {
2830 /* Wait. */
2831 int rcWait = !fNonInterruptible
2832 ? RTSemEventWaitNoResume(hEvent, cMsMaxOne)
2833 : RTSemEventWait(hEvent, cMsMaxOne);
2834 if (RT_SUCCESS(rcWait))
2835 return rcWait;
2836
2837 if (rcWait == VERR_TIMEOUT || rcWait == VERR_INTERRUPTED)
2838 {
2839 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
2840
2841 /*
2842 * Check the thread termination status.
2843 */
2844 int const rcTerm = RTThreadQueryTerminationStatus(NIL_RTTHREAD);
2845 AssertMsg(rcTerm == VINF_SUCCESS || rcTerm == VERR_NOT_SUPPORTED || rcTerm == VINF_THREAD_IS_TERMINATING,
2846 ("rcTerm=%Rrc\n", rcTerm));
2847 if ( rcTerm == VERR_NOT_SUPPORTED
2848 && !fNonInterruptible
2849 && cNsMaxTotal > RT_NS_1MIN)
2850 cNsMaxTotal = RT_NS_1MIN;
2851
2852 /* We return immediately if it looks like the thread is terminating. */
2853 if (rcTerm == VINF_THREAD_IS_TERMINATING)
2854 return VERR_THREAD_IS_TERMINATING;
2855
2856 /* We may suppress VERR_INTERRUPTED if VMMR0EMTWAIT_F_TRY_SUPPRESS_INTERRUPTED was
2857 specified, otherwise we'll just return it. */
2858 if (rcWait == VERR_INTERRUPTED)
2859 {
2860 if (!(fFlags & VMMR0EMTWAIT_F_TRY_SUPPRESS_INTERRUPTED))
2861 return VERR_INTERRUPTED;
2862 if (!fNonInterruptible)
2863 {
2864 /* First time: Adjust down the wait parameters and make sure we get at least
2865 one non-interruptible wait before timing out. */
2866 fNonInterruptible = true;
2867 cMsMaxOne = 32;
2868 uint64_t const cNsLeft = cNsMaxTotal - cNsElapsed;
2869 if (cNsLeft > RT_NS_10SEC)
2870 cNsMaxTotal = cNsElapsed + RT_NS_10SEC;
2871 continue;
2872 }
2873 }
2874
2875 /* Check for timeout. */
2876 if (cNsElapsed > cNsMaxTotal)
2877 return VERR_TIMEOUT;
2878 }
2879 else
2880 return rcWait;
2881 }
2882 /* not reached */
2883}
2884
2885
2886/*********************************************************************************************************************************
2887* Logging. *
2888*********************************************************************************************************************************/
2889
2890/**
2891 * VMMR0_DO_VMMR0_UPDATE_LOGGERS: Updates the EMT loggers for the VM.
2892 *
2893 * @returns VBox status code.
2894 * @param pGVM The global (ring-0) VM structure.
2895 * @param idCpu The ID of the calling EMT.
2896 * @param pReq The request data.
2897 * @param idxLogger Which logger set to update.
2898 * @thread EMT(idCpu)
2899 */
2900static int vmmR0UpdateLoggers(PGVM pGVM, VMCPUID idCpu, PVMMR0UPDATELOGGERSREQ pReq, size_t idxLogger)
2901{
2902 /*
2903 * Check sanity. First we require EMT to be calling us.
2904 */
2905 AssertReturn(idCpu < pGVM->cCpus, VERR_INVALID_CPU_ID);
2906 AssertReturn(pGVM->aCpus[idCpu].hEMT == RTThreadNativeSelf(), VERR_INVALID_CPU_ID);
2907
2908 AssertReturn(pReq->Hdr.cbReq >= RT_UOFFSETOF_DYN(VMMR0UPDATELOGGERSREQ, afGroups[0]), VERR_INVALID_PARAMETER);
2909 AssertReturn(pReq->cGroups < _8K, VERR_INVALID_PARAMETER);
2910 AssertReturn(pReq->Hdr.cbReq == RT_UOFFSETOF_DYN(VMMR0UPDATELOGGERSREQ, afGroups[pReq->cGroups]), VERR_INVALID_PARAMETER);
2911
2912 AssertReturn(idxLogger < VMMLOGGER_IDX_MAX, VERR_OUT_OF_RANGE);
2913
2914 /*
2915 * Adjust flags.
2916 */
2917 /* Always buffered: */
2918 pReq->fFlags |= RTLOGFLAGS_BUFFERED;
2919 /* These doesn't make sense at present: */
2920 pReq->fFlags &= ~(RTLOGFLAGS_FLUSH | RTLOGFLAGS_WRITE_THROUGH);
2921 /* We've traditionally skipped the group restrictions. */
2922 pReq->fFlags &= ~RTLOGFLAGS_RESTRICT_GROUPS;
2923
2924 /*
2925 * Do the updating.
2926 */
2927 int rc = VINF_SUCCESS;
2928 for (idCpu = 0; idCpu < pGVM->cCpus; idCpu++)
2929 {
2930 PGVMCPU pGVCpu = &pGVM->aCpus[idCpu];
2931 PRTLOGGER pLogger = pGVCpu->vmmr0.s.u.aLoggers[idxLogger].pLogger;
2932 if (pLogger)
2933 {
2934 RTLogSetR0ProgramStart(pLogger, pGVM->vmm.s.nsProgramStart);
2935 rc = RTLogBulkUpdate(pLogger, pReq->fFlags, pReq->uGroupCrc32, pReq->cGroups, pReq->afGroups);
2936 }
2937 }
2938
2939 return rc;
2940}
2941
2942
2943/**
2944 * VMMR0_DO_VMMR0_LOG_FLUSHER: Get the next log flushing job.
2945 *
2946 * The job info is copied into VMM::LogFlusherItem.
2947 *
2948 * @returns VBox status code.
2949 * @retval VERR_OBJECT_DESTROYED if we're shutting down.
2950 * @retval VERR_NOT_OWNER if the calling thread is not the flusher thread.
2951 * @param pGVM The global (ring-0) VM structure.
2952 * @thread The log flusher thread (first caller automatically becomes the log
2953 * flusher).
2954 */
2955static int vmmR0LogFlusher(PGVM pGVM)
2956{
2957 /*
2958 * Check that this really is the flusher thread.
2959 */
2960 RTNATIVETHREAD const hNativeSelf = RTThreadNativeSelf();
2961 AssertReturn(hNativeSelf != NIL_RTNATIVETHREAD, VERR_INTERNAL_ERROR_3);
2962 if (RT_LIKELY(pGVM->vmmr0.s.LogFlusher.hThread == hNativeSelf))
2963 { /* likely */ }
2964 else
2965 {
2966 /* The first caller becomes the flusher thread. */
2967 bool fOk;
2968 ASMAtomicCmpXchgHandle(&pGVM->vmmr0.s.LogFlusher.hThread, hNativeSelf, NIL_RTNATIVETHREAD, fOk);
2969 if (!fOk)
2970 return VERR_NOT_OWNER;
2971 pGVM->vmmr0.s.LogFlusher.fThreadRunning = true;
2972 }
2973
2974 /*
2975 * Acknowledge flush, waking up waiting EMT.
2976 */
2977 RTSpinlockAcquire(pGVM->vmmr0.s.LogFlusher.hSpinlock);
2978
2979 uint32_t idxTail = pGVM->vmmr0.s.LogFlusher.idxRingTail % RT_ELEMENTS(pGVM->vmmr0.s.LogFlusher.aRing);
2980 uint32_t idxHead = pGVM->vmmr0.s.LogFlusher.idxRingHead % RT_ELEMENTS(pGVM->vmmr0.s.LogFlusher.aRing);
2981 if ( idxTail != idxHead
2982 && pGVM->vmmr0.s.LogFlusher.aRing[idxHead].s.fProcessing)
2983 {
2984 /* Pop the head off the ring buffer. */
2985 uint32_t const idCpu = pGVM->vmmr0.s.LogFlusher.aRing[idxHead].s.idCpu;
2986 uint32_t const idxLogger = pGVM->vmmr0.s.LogFlusher.aRing[idxHead].s.idxLogger;
2987 uint32_t const idxBuffer = pGVM->vmmr0.s.LogFlusher.aRing[idxHead].s.idxBuffer;
2988
2989 pGVM->vmmr0.s.LogFlusher.aRing[idxHead].u32 = UINT32_MAX >> 1; /* invalidate the entry */
2990 pGVM->vmmr0.s.LogFlusher.idxRingHead = (idxHead + 1) % RT_ELEMENTS(pGVM->vmmr0.s.LogFlusher.aRing);
2991
2992 /* Validate content. */
2993 if ( idCpu < pGVM->cCpus
2994 && idxLogger < VMMLOGGER_IDX_MAX
2995 && idxBuffer < VMMLOGGER_BUFFER_COUNT)
2996 {
2997 PGVMCPU pGVCpu = &pGVM->aCpus[idCpu];
2998 PVMMR0PERVCPULOGGER pR0Log = &pGVCpu->vmmr0.s.u.aLoggers[idxLogger];
2999 PVMMR3CPULOGGER pShared = &pGVCpu->vmm.s.u.aLoggers[idxLogger];
3000
3001 /*
3002 * Accounting.
3003 */
3004 uint32_t cFlushing = pR0Log->cFlushing - 1;
3005 if (RT_LIKELY(cFlushing < VMMLOGGER_BUFFER_COUNT))
3006 { /*likely*/ }
3007 else
3008 cFlushing = 0;
3009 pR0Log->cFlushing = cFlushing;
3010 ASMAtomicWriteU32(&pShared->cFlushing, cFlushing);
3011
3012 /*
3013 * Wake up the EMT if it's waiting.
3014 */
3015 if (!pR0Log->fEmtWaiting)
3016 RTSpinlockRelease(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3017 else
3018 {
3019 pR0Log->fEmtWaiting = false;
3020 RTSpinlockRelease(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3021
3022 int rc = RTSemEventSignal(pR0Log->hEventFlushWait);
3023 if (RT_FAILURE(rc))
3024 LogRelMax(64, ("vmmR0LogFlusher: RTSemEventSignal failed ACKing entry #%u (%u/%u/%u): %Rrc!\n",
3025 idxHead, idCpu, idxLogger, idxBuffer, rc));
3026 }
3027 }
3028 else
3029 {
3030 RTSpinlockRelease(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3031 LogRelMax(64, ("vmmR0LogFlusher: Bad ACK entry #%u: %u/%u/%u!\n", idxHead, idCpu, idxLogger, idxBuffer));
3032 }
3033
3034 RTSpinlockAcquire(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3035 }
3036
3037 /*
3038 * The wait loop.
3039 */
3040 int rc;
3041 for (;;)
3042 {
3043 /*
3044 * Work pending?
3045 */
3046 idxTail = pGVM->vmmr0.s.LogFlusher.idxRingTail % RT_ELEMENTS(pGVM->vmmr0.s.LogFlusher.aRing);
3047 idxHead = pGVM->vmmr0.s.LogFlusher.idxRingHead % RT_ELEMENTS(pGVM->vmmr0.s.LogFlusher.aRing);
3048 if (idxTail != idxHead)
3049 {
3050 pGVM->vmmr0.s.LogFlusher.aRing[idxHead].s.fProcessing = true;
3051 pGVM->vmm.s.LogFlusherItem.u32 = pGVM->vmmr0.s.LogFlusher.aRing[idxHead].u32;
3052
3053 RTSpinlockRelease(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3054 return VINF_SUCCESS;
3055 }
3056
3057 /*
3058 * Nothing to do, so, check for termination and go to sleep.
3059 */
3060 if (!pGVM->vmmr0.s.LogFlusher.fThreadShutdown)
3061 { /* likely */ }
3062 else
3063 {
3064 rc = VERR_OBJECT_DESTROYED;
3065 break;
3066 }
3067
3068 pGVM->vmmr0.s.LogFlusher.fThreadWaiting = true;
3069 RTSpinlockRelease(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3070
3071 rc = RTSemEventWaitNoResume(pGVM->vmmr0.s.LogFlusher.hEvent, RT_MS_5MIN);
3072
3073 RTSpinlockAcquire(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3074 pGVM->vmmr0.s.LogFlusher.fThreadWaiting = false;
3075
3076 if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT)
3077 { /* likely */ }
3078 else if (rc == VERR_INTERRUPTED)
3079 {
3080 RTSpinlockRelease(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3081 return rc;
3082 }
3083 else if (rc == VERR_SEM_DESTROYED || rc == VERR_INVALID_HANDLE)
3084 break;
3085 else
3086 {
3087 LogRel(("vmmR0LogFlusher: RTSemEventWaitNoResume returned unexpected status %Rrc\n", rc));
3088 break;
3089 }
3090 }
3091
3092 /*
3093 * Terminating - prevent further calls and indicate to the EMTs that we're no longer around.
3094 */
3095 pGVM->vmmr0.s.LogFlusher.hThread = ~pGVM->vmmr0.s.LogFlusher.hThread; /* (should be reasonably safe) */
3096 pGVM->vmmr0.s.LogFlusher.fThreadRunning = false;
3097
3098 RTSpinlockRelease(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3099 return rc;
3100}
3101
3102
3103/**
3104 * VMMR0_DO_VMMR0_LOG_WAIT_FLUSHED: Waits for the flusher thread to finish all
3105 * buffers for logger @a idxLogger.
3106 *
3107 * @returns VBox status code.
3108 * @param pGVM The global (ring-0) VM structure.
3109 * @param idCpu The ID of the calling EMT.
3110 * @param idxLogger Which logger to wait on.
3111 * @thread EMT(idCpu)
3112 */
3113static int vmmR0LogWaitFlushed(PGVM pGVM, VMCPUID idCpu, size_t idxLogger)
3114{
3115 /*
3116 * Check sanity. First we require EMT to be calling us.
3117 */
3118 AssertReturn(idCpu < pGVM->cCpus, VERR_INVALID_CPU_ID);
3119 PGVMCPU pGVCpu = &pGVM->aCpus[idCpu];
3120 AssertReturn(pGVCpu->hEMT == RTThreadNativeSelf(), VERR_INVALID_CPU_ID);
3121 AssertReturn(idxLogger < VMMLOGGER_IDX_MAX, VERR_OUT_OF_RANGE);
3122 PVMMR0PERVCPULOGGER const pR0Log = &pGVCpu->vmmr0.s.u.aLoggers[idxLogger];
3123
3124 /*
3125 * Do the waiting.
3126 */
3127 int rc = VINF_SUCCESS;
3128 RTSpinlockAcquire(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3129 uint32_t cFlushing = pR0Log->cFlushing;
3130 while (cFlushing > 0)
3131 {
3132 pR0Log->fEmtWaiting = true;
3133 RTSpinlockRelease(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3134
3135 rc = RTSemEventWaitNoResume(pR0Log->hEventFlushWait, RT_MS_5MIN);
3136
3137 RTSpinlockAcquire(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3138 pR0Log->fEmtWaiting = false;
3139 if (RT_SUCCESS(rc))
3140 {
3141 /* Read the new count, make sure it decreased before looping. That
3142 way we can guarentee that we will only wait more than 5 min * buffers. */
3143 uint32_t const cPrevFlushing = cFlushing;
3144 cFlushing = pR0Log->cFlushing;
3145 if (cFlushing < cPrevFlushing)
3146 continue;
3147 rc = VERR_INTERNAL_ERROR_3;
3148 }
3149 break;
3150 }
3151 RTSpinlockRelease(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3152 return rc;
3153}
3154
3155
3156/**
3157 * Inner worker for vmmR0LoggerFlushCommon.
3158 */
3159static bool vmmR0LoggerFlushInner(PGVM pGVM, PGVMCPU pGVCpu, uint32_t idxLogger, size_t idxBuffer, uint32_t cbToFlush)
3160{
3161 PVMMR0PERVCPULOGGER const pR0Log = &pGVCpu->vmmr0.s.u.aLoggers[idxLogger];
3162 PVMMR3CPULOGGER const pShared = &pGVCpu->vmm.s.u.aLoggers[idxLogger];
3163
3164 /*
3165 * Figure out what we need to do and whether we can.
3166 */
3167 enum { kJustSignal, kPrepAndSignal, kPrepSignalAndWait } enmAction;
3168#if VMMLOGGER_BUFFER_COUNT >= 2
3169 if (pR0Log->cFlushing < VMMLOGGER_BUFFER_COUNT - 1)
3170 {
3171 if (RTSemEventIsSignalSafe())
3172 enmAction = kJustSignal;
3173 else if (VMMRZCallRing3IsEnabled(pGVCpu))
3174 enmAction = kPrepAndSignal;
3175 else
3176 {
3177 /** @todo This is a bit simplistic. We could introduce a FF to signal the
3178 * thread or similar. */
3179 STAM_REL_COUNTER_INC(&pShared->StatCannotBlock);
3180# if defined(RT_OS_LINUX)
3181 SUP_DPRINTF(("vmmR0LoggerFlush: Signalling not safe and EMT blocking disabled! (%u bytes)\n", cbToFlush));
3182# endif
3183 pShared->cbDropped += cbToFlush;
3184 return true;
3185 }
3186 }
3187 else
3188#endif
3189 if (VMMRZCallRing3IsEnabled(pGVCpu))
3190 enmAction = kPrepSignalAndWait;
3191 else
3192 {
3193 STAM_REL_COUNTER_INC(&pShared->StatCannotBlock);
3194# if defined(RT_OS_LINUX)
3195 SUP_DPRINTF(("vmmR0LoggerFlush: EMT blocking disabled! (%u bytes)\n", cbToFlush));
3196# endif
3197 pShared->cbDropped += cbToFlush;
3198 return true;
3199 }
3200
3201 /*
3202 * Prepare for blocking if necessary.
3203 */
3204 VMMR0EMTBLOCKCTX Ctx;
3205 if (enmAction != kJustSignal)
3206 {
3207 int rc = VMMR0EmtPrepareToBlock(pGVCpu, VINF_SUCCESS, "vmmR0LoggerFlushInner", pR0Log->hEventFlushWait, &Ctx);
3208 if (RT_SUCCESS(rc))
3209 { /* likely */ }
3210 else
3211 {
3212 STAM_REL_COUNTER_INC(&pShared->StatCannotBlock);
3213 SUP_DPRINTF(("vmmR0LoggerFlush: VMMR0EmtPrepareToBlock failed! rc=%d\n", rc));
3214 return false;
3215 }
3216 }
3217
3218 /*
3219 * Queue the flush job.
3220 */
3221 bool fFlushedBuffer;
3222 RTSpinlockAcquire(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3223 if (pGVM->vmmr0.s.LogFlusher.fThreadRunning)
3224 {
3225 uint32_t const idxHead = pGVM->vmmr0.s.LogFlusher.idxRingHead % RT_ELEMENTS(pGVM->vmmr0.s.LogFlusher.aRing);
3226 uint32_t const idxTail = pGVM->vmmr0.s.LogFlusher.idxRingTail % RT_ELEMENTS(pGVM->vmmr0.s.LogFlusher.aRing);
3227 uint32_t const idxNewTail = (idxTail + 1) % RT_ELEMENTS(pGVM->vmmr0.s.LogFlusher.aRing);
3228 if (idxNewTail != idxHead)
3229 {
3230 /* Queue it. */
3231 pGVM->vmmr0.s.LogFlusher.aRing[idxTail].s.idCpu = pGVCpu->idCpu;
3232 pGVM->vmmr0.s.LogFlusher.aRing[idxTail].s.idxLogger = idxLogger;
3233 pGVM->vmmr0.s.LogFlusher.aRing[idxTail].s.idxBuffer = (uint32_t)idxBuffer;
3234 pGVM->vmmr0.s.LogFlusher.aRing[idxTail].s.fProcessing = 0;
3235 pGVM->vmmr0.s.LogFlusher.idxRingTail = idxNewTail;
3236
3237 /* Update the number of buffers currently being flushed. */
3238 uint32_t cFlushing = pR0Log->cFlushing;
3239 cFlushing = RT_MIN(cFlushing + 1, VMMLOGGER_BUFFER_COUNT);
3240 pShared->cFlushing = pR0Log->cFlushing = cFlushing;
3241
3242 /* We must wait if all buffers are currently being flushed. */
3243 bool const fEmtWaiting = cFlushing >= VMMLOGGER_BUFFER_COUNT && enmAction != kJustSignal /* paranoia */;
3244 pR0Log->fEmtWaiting = fEmtWaiting;
3245
3246 /* Stats. */
3247 STAM_REL_COUNTER_INC(&pShared->StatFlushes);
3248 STAM_REL_COUNTER_INC(&pGVM->vmm.s.StatLogFlusherFlushes);
3249
3250 /* Signal the worker thread. */
3251 if (pGVM->vmmr0.s.LogFlusher.fThreadWaiting)
3252 {
3253 RTSpinlockRelease(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3254 RTSemEventSignal(pGVM->vmmr0.s.LogFlusher.hEvent);
3255 }
3256 else
3257 {
3258 STAM_REL_COUNTER_INC(&pGVM->vmm.s.StatLogFlusherNoWakeUp);
3259 RTSpinlockRelease(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3260 }
3261
3262 /*
3263 * Wait for a buffer to finish flushing.
3264 *
3265 * Note! Lazy bird is ignoring the status code here. The result is
3266 * that we might end up with an extra even signalling and the
3267 * next time we need to wait we won't and end up with some log
3268 * corruption. However, it's too much hazzle right now for
3269 * a scenario which would most likely end the process rather
3270 * than causing log corruption.
3271 */
3272 if (fEmtWaiting)
3273 {
3274 STAM_REL_PROFILE_START(&pShared->StatWait, a);
3275 VMMR0EmtWaitEventInner(pGVCpu, VMMR0EMTWAIT_F_TRY_SUPPRESS_INTERRUPTED,
3276 pR0Log->hEventFlushWait, RT_INDEFINITE_WAIT);
3277 STAM_REL_PROFILE_STOP(&pShared->StatWait, a);
3278 }
3279
3280 /*
3281 * We always switch buffer if we have more than one.
3282 */
3283#if VMMLOGGER_BUFFER_COUNT == 1
3284 fFlushedBuffer = true;
3285#else
3286 AssertCompile(VMMLOGGER_BUFFER_COUNT >= 1);
3287 pShared->idxBuf = (idxBuffer + 1) % VMMLOGGER_BUFFER_COUNT;
3288 fFlushedBuffer = false;
3289#endif
3290 }
3291 else
3292 {
3293 RTSpinlockRelease(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3294 SUP_DPRINTF(("vmmR0LoggerFlush: ring buffer is full!\n"));
3295 fFlushedBuffer = true;
3296 }
3297 }
3298 else
3299 {
3300 RTSpinlockRelease(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3301 SUP_DPRINTF(("vmmR0LoggerFlush: flusher not active - dropping %u bytes\n", cbToFlush));
3302 fFlushedBuffer = true;
3303 }
3304
3305 /*
3306 * Restore the HM context.
3307 */
3308 if (enmAction != kJustSignal)
3309 VMMR0EmtResumeAfterBlocking(pGVCpu, &Ctx);
3310
3311 return fFlushedBuffer;
3312}
3313
3314
3315/**
3316 * Common worker for vmmR0LogFlush and vmmR0LogRelFlush.
3317 */
3318static bool vmmR0LoggerFlushCommon(PRTLOGGER pLogger, PRTLOGBUFFERDESC pBufDesc, uint32_t idxLogger)
3319{
3320 /*
3321 * Convert the pLogger into a GVMCPU handle and 'call' back to Ring-3.
3322 * (This is a bit paranoid code.)
3323 */
3324 if (RT_VALID_PTR(pLogger))
3325 {
3326 if ( pLogger->u32Magic == RTLOGGER_MAGIC
3327 && (pLogger->u32UserValue1 & VMMR0_LOGGER_FLAGS_MAGIC_MASK) == VMMR0_LOGGER_FLAGS_MAGIC_VALUE
3328 && pLogger->u64UserValue2 == pLogger->u64UserValue3)
3329 {
3330 PGVMCPU const pGVCpu = (PGVMCPU)(uintptr_t)pLogger->u64UserValue2;
3331 if ( RT_VALID_PTR(pGVCpu)
3332 && ((uintptr_t)pGVCpu & PAGE_OFFSET_MASK) == 0)
3333 {
3334 RTNATIVETHREAD const hNativeSelf = RTThreadNativeSelf();
3335 PGVM const pGVM = pGVCpu->pGVM;
3336 if ( hNativeSelf == pGVCpu->hEMT
3337 && RT_VALID_PTR(pGVM))
3338 {
3339 PVMMR0PERVCPULOGGER const pR0Log = &pGVCpu->vmmr0.s.u.aLoggers[idxLogger];
3340 size_t const idxBuffer = pBufDesc - &pR0Log->aBufDescs[0];
3341 if (idxBuffer < VMMLOGGER_BUFFER_COUNT)
3342 {
3343 /*
3344 * Make sure we don't recurse forever here should something in the
3345 * following code trigger logging or an assertion. Do the rest in
3346 * an inner work to avoid hitting the right margin too hard.
3347 */
3348 if (!pR0Log->fFlushing)
3349 {
3350 pR0Log->fFlushing = true;
3351 bool fFlushed = vmmR0LoggerFlushInner(pGVM, pGVCpu, idxLogger, idxBuffer, pBufDesc->offBuf);
3352 pR0Log->fFlushing = false;
3353 return fFlushed;
3354 }
3355
3356 SUP_DPRINTF(("vmmR0LoggerFlush: Recursive flushing!\n"));
3357 }
3358 else
3359 SUP_DPRINTF(("vmmR0LoggerFlush: pLogger=%p pGVCpu=%p: idxBuffer=%#zx\n", pLogger, pGVCpu, idxBuffer));
3360 }
3361 else
3362 SUP_DPRINTF(("vmmR0LoggerFlush: pLogger=%p pGVCpu=%p hEMT=%p hNativeSelf=%p!\n",
3363 pLogger, pGVCpu, pGVCpu->hEMT, hNativeSelf));
3364 }
3365 else
3366 SUP_DPRINTF(("vmmR0LoggerFlush: pLogger=%p pGVCpu=%p!\n", pLogger, pGVCpu));
3367 }
3368 else
3369 SUP_DPRINTF(("vmmR0LoggerFlush: pLogger=%p u32Magic=%#x u32UserValue1=%#x u64UserValue2=%#RX64 u64UserValue3=%#RX64!\n",
3370 pLogger, pLogger->u32Magic, pLogger->u32UserValue1, pLogger->u64UserValue2, pLogger->u64UserValue3));
3371 }
3372 else
3373 SUP_DPRINTF(("vmmR0LoggerFlush: pLogger=%p!\n", pLogger));
3374 return true;
3375}
3376
3377
3378/**
3379 * @callback_method_impl{FNRTLOGFLUSH, Release logger buffer flush callback.}
3380 */
3381static DECLCALLBACK(bool) vmmR0LogRelFlush(PRTLOGGER pLogger, PRTLOGBUFFERDESC pBufDesc)
3382{
3383 return vmmR0LoggerFlushCommon(pLogger, pBufDesc, VMMLOGGER_IDX_RELEASE);
3384}
3385
3386
3387/**
3388 * @callback_method_impl{FNRTLOGFLUSH, Logger (debug) buffer flush callback.}
3389 */
3390static DECLCALLBACK(bool) vmmR0LogFlush(PRTLOGGER pLogger, PRTLOGBUFFERDESC pBufDesc)
3391{
3392#ifdef LOG_ENABLED
3393 return vmmR0LoggerFlushCommon(pLogger, pBufDesc, VMMLOGGER_IDX_REGULAR);
3394#else
3395 RT_NOREF(pLogger, pBufDesc);
3396 return true;
3397#endif
3398}
3399
3400
3401/*
3402 * Override RTLogDefaultInstanceEx so we can do logging from EMTs in ring-0.
3403 */
3404DECLEXPORT(PRTLOGGER) RTLogDefaultInstanceEx(uint32_t fFlagsAndGroup)
3405{
3406#ifdef LOG_ENABLED
3407 PGVMCPU pGVCpu = GVMMR0GetGVCpuByEMT(NIL_RTNATIVETHREAD);
3408 if (pGVCpu)
3409 {
3410 PRTLOGGER pLogger = pGVCpu->vmmr0.s.u.s.Logger.pLogger;
3411 if (RT_VALID_PTR(pLogger))
3412 {
3413 if ( pLogger->u64UserValue2 == (uintptr_t)pGVCpu
3414 && pLogger->u64UserValue3 == (uintptr_t)pGVCpu)
3415 {
3416 if (!pGVCpu->vmmr0.s.u.s.Logger.fFlushing)
3417 {
3418 if (!(pGVCpu->vmmr0.s.fLogFlushingDisabled))
3419 return RTLogCheckGroupFlags(pLogger, fFlagsAndGroup);
3420 return NULL;
3421 }
3422
3423 /*
3424 * When we're flushing we _must_ return NULL here to suppress any
3425 * attempts at using the logger while in vmmR0LoggerFlushCommon.
3426 * The VMMR0EmtPrepareToBlock code may trigger logging in HM,
3427 * which will reset the buffer content before we even get to queue
3428 * the flush request. (Only an issue when VBOX_WITH_R0_LOGGING
3429 * is enabled.)
3430 */
3431 return NULL;
3432 }
3433 }
3434 }
3435#endif
3436 return SUPR0DefaultLogInstanceEx(fFlagsAndGroup);
3437}
3438
3439
3440/*
3441 * Override RTLogRelGetDefaultInstanceEx so we can do LogRel to VBox.log from EMTs in ring-0.
3442 */
3443DECLEXPORT(PRTLOGGER) RTLogRelGetDefaultInstanceEx(uint32_t fFlagsAndGroup)
3444{
3445 PGVMCPU pGVCpu = GVMMR0GetGVCpuByEMT(NIL_RTNATIVETHREAD);
3446 if (pGVCpu)
3447 {
3448 PRTLOGGER pLogger = pGVCpu->vmmr0.s.u.s.RelLogger.pLogger;
3449 if (RT_VALID_PTR(pLogger))
3450 {
3451 if ( pLogger->u64UserValue2 == (uintptr_t)pGVCpu
3452 && pLogger->u64UserValue3 == (uintptr_t)pGVCpu)
3453 {
3454 if (!pGVCpu->vmmr0.s.u.s.RelLogger.fFlushing)
3455 {
3456 if (!(pGVCpu->vmmr0.s.fLogFlushingDisabled))
3457 return RTLogCheckGroupFlags(pLogger, fFlagsAndGroup);
3458 return NULL;
3459 }
3460 }
3461 }
3462 }
3463 return SUPR0GetDefaultLogRelInstanceEx(fFlagsAndGroup);
3464}
3465
3466
3467/**
3468 * Helper for vmmR0InitLoggerSet
3469 */
3470static int vmmR0InitLoggerOne(PGVMCPU pGVCpu, bool fRelease, PVMMR0PERVCPULOGGER pR0Log, PVMMR3CPULOGGER pShared,
3471 uint32_t cbBuf, char *pchBuf, RTR3PTR pchBufR3)
3472{
3473 /*
3474 * Create and configure the logger.
3475 */
3476 for (size_t i = 0; i < VMMLOGGER_BUFFER_COUNT; i++)
3477 {
3478 pR0Log->aBufDescs[i].u32Magic = RTLOGBUFFERDESC_MAGIC;
3479 pR0Log->aBufDescs[i].uReserved = 0;
3480 pR0Log->aBufDescs[i].cbBuf = cbBuf;
3481 pR0Log->aBufDescs[i].offBuf = 0;
3482 pR0Log->aBufDescs[i].pchBuf = pchBuf + i * cbBuf;
3483 pR0Log->aBufDescs[i].pAux = &pShared->aBufs[i].AuxDesc;
3484
3485 pShared->aBufs[i].AuxDesc.fFlushedIndicator = false;
3486 pShared->aBufs[i].AuxDesc.afPadding[0] = 0;
3487 pShared->aBufs[i].AuxDesc.afPadding[1] = 0;
3488 pShared->aBufs[i].AuxDesc.afPadding[2] = 0;
3489 pShared->aBufs[i].AuxDesc.offBuf = 0;
3490 pShared->aBufs[i].pchBufR3 = pchBufR3 + i * cbBuf;
3491 }
3492 pShared->cbBuf = cbBuf;
3493
3494 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
3495 int rc = RTLogCreateEx(&pR0Log->pLogger, fRelease ? "VBOX_RELEASE_LOG" : "VBOX_LOG", RTLOG_F_NO_LOCKING | RTLOGFLAGS_BUFFERED,
3496 "all", RT_ELEMENTS(s_apszGroups), s_apszGroups, UINT32_MAX,
3497 VMMLOGGER_BUFFER_COUNT, pR0Log->aBufDescs, RTLOGDEST_DUMMY,
3498 NULL /*pfnPhase*/, 0 /*cHistory*/, 0 /*cbHistoryFileMax*/, 0 /*cSecsHistoryTimeSlot*/,
3499 NULL /*pErrInfo*/, NULL /*pszFilenameFmt*/);
3500 if (RT_SUCCESS(rc))
3501 {
3502 PRTLOGGER pLogger = pR0Log->pLogger;
3503 pLogger->u32UserValue1 = VMMR0_LOGGER_FLAGS_MAGIC_VALUE;
3504 pLogger->u64UserValue2 = (uintptr_t)pGVCpu;
3505 pLogger->u64UserValue3 = (uintptr_t)pGVCpu;
3506
3507 rc = RTLogSetFlushCallback(pLogger, fRelease ? vmmR0LogRelFlush : vmmR0LogFlush);
3508 if (RT_SUCCESS(rc))
3509 {
3510 RTLogSetR0ThreadNameF(pLogger, "EMT-%u-R0", pGVCpu->idCpu);
3511
3512 /*
3513 * Create the event sem the EMT waits on while flushing is happening.
3514 */
3515 rc = RTSemEventCreate(&pR0Log->hEventFlushWait);
3516 if (RT_SUCCESS(rc))
3517 return VINF_SUCCESS;
3518 pR0Log->hEventFlushWait = NIL_RTSEMEVENT;
3519 }
3520 RTLogDestroy(pLogger);
3521 }
3522 pR0Log->pLogger = NULL;
3523 return rc;
3524}
3525
3526
3527/**
3528 * Worker for VMMR0CleanupVM and vmmR0InitLoggerSet that destroys one logger.
3529 */
3530static void vmmR0TermLoggerOne(PVMMR0PERVCPULOGGER pR0Log, PVMMR3CPULOGGER pShared)
3531{
3532 RTLogDestroy(pR0Log->pLogger);
3533 pR0Log->pLogger = NULL;
3534
3535 for (size_t i = 0; i < VMMLOGGER_BUFFER_COUNT; i++)
3536 pShared->aBufs[i].pchBufR3 = NIL_RTR3PTR;
3537
3538 RTSemEventDestroy(pR0Log->hEventFlushWait);
3539 pR0Log->hEventFlushWait = NIL_RTSEMEVENT;
3540}
3541
3542
3543/**
3544 * Initializes one type of loggers for each EMT.
3545 */
3546static int vmmR0InitLoggerSet(PGVM pGVM, uint8_t idxLogger, uint32_t cbBuf, PRTR0MEMOBJ phMemObj, PRTR0MEMOBJ phMapObj)
3547{
3548 /* Allocate buffers first. */
3549 int rc = RTR0MemObjAllocPage(phMemObj, cbBuf * pGVM->cCpus * VMMLOGGER_BUFFER_COUNT, false /*fExecutable*/);
3550 if (RT_SUCCESS(rc))
3551 {
3552 rc = RTR0MemObjMapUser(phMapObj, *phMemObj, (RTR3PTR)-1, 0 /*uAlignment*/, RTMEM_PROT_READ, NIL_RTR0PROCESS);
3553 if (RT_SUCCESS(rc))
3554 {
3555 char * const pchBuf = (char *)RTR0MemObjAddress(*phMemObj);
3556 AssertPtrReturn(pchBuf, VERR_INTERNAL_ERROR_2);
3557
3558 RTR3PTR const pchBufR3 = RTR0MemObjAddressR3(*phMapObj);
3559 AssertReturn(pchBufR3 != NIL_RTR3PTR, VERR_INTERNAL_ERROR_3);
3560
3561 /* Initialize the per-CPU loggers. */
3562 for (uint32_t i = 0; i < pGVM->cCpus; i++)
3563 {
3564 PGVMCPU pGVCpu = &pGVM->aCpus[i];
3565 PVMMR0PERVCPULOGGER pR0Log = &pGVCpu->vmmr0.s.u.aLoggers[idxLogger];
3566 PVMMR3CPULOGGER pShared = &pGVCpu->vmm.s.u.aLoggers[idxLogger];
3567 rc = vmmR0InitLoggerOne(pGVCpu, idxLogger == VMMLOGGER_IDX_RELEASE, pR0Log, pShared, cbBuf,
3568 pchBuf + i * cbBuf * VMMLOGGER_BUFFER_COUNT,
3569 pchBufR3 + i * cbBuf * VMMLOGGER_BUFFER_COUNT);
3570 if (RT_FAILURE(rc))
3571 {
3572 vmmR0TermLoggerOne(pR0Log, pShared);
3573 while (i-- > 0)
3574 {
3575 pGVCpu = &pGVM->aCpus[i];
3576 vmmR0TermLoggerOne(&pGVCpu->vmmr0.s.u.aLoggers[idxLogger], &pGVCpu->vmm.s.u.aLoggers[idxLogger]);
3577 }
3578 break;
3579 }
3580 }
3581 if (RT_SUCCESS(rc))
3582 return VINF_SUCCESS;
3583
3584 /* Bail out. */
3585 RTR0MemObjFree(*phMapObj, false /*fFreeMappings*/);
3586 *phMapObj = NIL_RTR0MEMOBJ;
3587 }
3588 RTR0MemObjFree(*phMemObj, true /*fFreeMappings*/);
3589 *phMemObj = NIL_RTR0MEMOBJ;
3590 }
3591 return rc;
3592}
3593
3594
3595/**
3596 * Worker for VMMR0InitPerVMData that initializes all the logging related stuff.
3597 *
3598 * @returns VBox status code.
3599 * @param pGVM The global (ring-0) VM structure.
3600 */
3601static int vmmR0InitLoggers(PGVM pGVM)
3602{
3603 /*
3604 * Invalidate the ring buffer (not really necessary).
3605 */
3606 for (size_t idx = 0; idx < RT_ELEMENTS(pGVM->vmmr0.s.LogFlusher.aRing); idx++)
3607 pGVM->vmmr0.s.LogFlusher.aRing[idx].u32 = UINT32_MAX >> 1; /* (all bits except fProcessing set) */
3608
3609 /*
3610 * Create the spinlock and flusher event semaphore.
3611 */
3612 int rc = RTSpinlockCreate(&pGVM->vmmr0.s.LogFlusher.hSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VM-Log-Flusher");
3613 if (RT_SUCCESS(rc))
3614 {
3615 rc = RTSemEventCreate(&pGVM->vmmr0.s.LogFlusher.hEvent);
3616 if (RT_SUCCESS(rc))
3617 {
3618 /*
3619 * Create the ring-0 release loggers.
3620 */
3621 rc = vmmR0InitLoggerSet(pGVM, VMMLOGGER_IDX_RELEASE, _4K,
3622 &pGVM->vmmr0.s.hMemObjReleaseLogger, &pGVM->vmmr0.s.hMapObjReleaseLogger);
3623#ifdef LOG_ENABLED
3624 if (RT_SUCCESS(rc))
3625 {
3626 /*
3627 * Create debug loggers.
3628 */
3629 rc = vmmR0InitLoggerSet(pGVM, VMMLOGGER_IDX_REGULAR, _64K,
3630 &pGVM->vmmr0.s.hMemObjLogger, &pGVM->vmmr0.s.hMapObjLogger);
3631 }
3632#endif
3633 }
3634 }
3635 return rc;
3636}
3637
3638
3639/**
3640 * Worker for VMMR0InitPerVMData that initializes all the logging related stuff.
3641 *
3642 * @param pGVM The global (ring-0) VM structure.
3643 */
3644static void vmmR0CleanupLoggers(PGVM pGVM)
3645{
3646 for (VMCPUID idCpu = 0; idCpu < pGVM->cCpus; idCpu++)
3647 {
3648 PGVMCPU pGVCpu = &pGVM->aCpus[idCpu];
3649 for (size_t iLogger = 0; iLogger < RT_ELEMENTS(pGVCpu->vmmr0.s.u.aLoggers); iLogger++)
3650 vmmR0TermLoggerOne(&pGVCpu->vmmr0.s.u.aLoggers[iLogger], &pGVCpu->vmm.s.u.aLoggers[iLogger]);
3651 }
3652
3653 /*
3654 * Free logger buffer memory.
3655 */
3656 RTR0MemObjFree(pGVM->vmmr0.s.hMapObjReleaseLogger, false /*fFreeMappings*/);
3657 pGVM->vmmr0.s.hMapObjReleaseLogger = NIL_RTR0MEMOBJ;
3658 RTR0MemObjFree(pGVM->vmmr0.s.hMemObjReleaseLogger, true /*fFreeMappings*/);
3659 pGVM->vmmr0.s.hMemObjReleaseLogger = NIL_RTR0MEMOBJ;
3660
3661 RTR0MemObjFree(pGVM->vmmr0.s.hMapObjLogger, false /*fFreeMappings*/);
3662 pGVM->vmmr0.s.hMapObjLogger = NIL_RTR0MEMOBJ;
3663 RTR0MemObjFree(pGVM->vmmr0.s.hMemObjLogger, true /*fFreeMappings*/);
3664 pGVM->vmmr0.s.hMemObjLogger = NIL_RTR0MEMOBJ;
3665
3666 /*
3667 * Free log flusher related stuff.
3668 */
3669 RTSpinlockDestroy(pGVM->vmmr0.s.LogFlusher.hSpinlock);
3670 pGVM->vmmr0.s.LogFlusher.hSpinlock = NIL_RTSPINLOCK;
3671 RTSemEventDestroy(pGVM->vmmr0.s.LogFlusher.hEvent);
3672 pGVM->vmmr0.s.LogFlusher.hEvent = NIL_RTSEMEVENT;
3673}
3674
3675
3676/*********************************************************************************************************************************
3677* Assertions *
3678*********************************************************************************************************************************/
3679
3680/*
3681 * Jump back to ring-3 if we're the EMT and the longjmp is armed.
3682 *
3683 * @returns true if the breakpoint should be hit, false if it should be ignored.
3684 */
3685DECLEXPORT(bool) RTCALL RTAssertShouldPanic(void)
3686{
3687#if 0
3688 return true;
3689#else
3690 PVMCC pVM = GVMMR0GetVMByEMT(NIL_RTNATIVETHREAD);
3691 if (pVM)
3692 {
3693 PVMCPUCC pVCpu = VMMGetCpu(pVM);
3694
3695 if (pVCpu)
3696 {
3697# ifdef RT_ARCH_X86
3698 if ( pVCpu->vmm.s.CallRing3JmpBufR0.eip
3699 && !pVCpu->vmm.s.CallRing3JmpBufR0.fInRing3Call)
3700# else
3701 if ( pVCpu->vmm.s.CallRing3JmpBufR0.rip
3702 && !pVCpu->vmm.s.CallRing3JmpBufR0.fInRing3Call)
3703# endif
3704 {
3705 int rc = VMMRZCallRing3(pVM, pVCpu, VMMCALLRING3_VM_R0_ASSERTION, 0);
3706 return RT_FAILURE_NP(rc);
3707 }
3708 }
3709 }
3710# ifdef RT_OS_LINUX
3711 return true;
3712# else
3713 return false;
3714# endif
3715#endif
3716}
3717
3718
3719/*
3720 * Override this so we can push it up to ring-3.
3721 */
3722DECLEXPORT(void) RTCALL RTAssertMsg1Weak(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
3723{
3724 /*
3725 * To host kernel log/whatever.
3726 */
3727 SUPR0Printf("!!R0-Assertion Failed!!\n"
3728 "Expression: %s\n"
3729 "Location : %s(%d) %s\n",
3730 pszExpr, pszFile, uLine, pszFunction);
3731
3732 /*
3733 * To the log.
3734 */
3735 LogAlways(("\n!!R0-Assertion Failed!!\n"
3736 "Expression: %s\n"
3737 "Location : %s(%d) %s\n",
3738 pszExpr, pszFile, uLine, pszFunction));
3739
3740 /*
3741 * To the global VMM buffer.
3742 */
3743 PVMCC pVM = GVMMR0GetVMByEMT(NIL_RTNATIVETHREAD);
3744 if (pVM)
3745 RTStrPrintf(pVM->vmm.s.szRing0AssertMsg1, sizeof(pVM->vmm.s.szRing0AssertMsg1),
3746 "\n!!R0-Assertion Failed!!\n"
3747 "Expression: %.*s\n"
3748 "Location : %s(%d) %s\n",
3749 sizeof(pVM->vmm.s.szRing0AssertMsg1) / 4 * 3, pszExpr,
3750 pszFile, uLine, pszFunction);
3751
3752 /*
3753 * Continue the normal way.
3754 */
3755 RTAssertMsg1(pszExpr, uLine, pszFile, pszFunction);
3756}
3757
3758
3759/**
3760 * Callback for RTLogFormatV which writes to the ring-3 log port.
3761 * See PFNLOGOUTPUT() for details.
3762 */
3763static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars)
3764{
3765 for (size_t i = 0; i < cbChars; i++)
3766 {
3767 LogAlways(("%c", pachChars[i])); NOREF(pachChars);
3768 }
3769
3770 NOREF(pv);
3771 return cbChars;
3772}
3773
3774
3775/*
3776 * Override this so we can push it up to ring-3.
3777 */
3778DECLEXPORT(void) RTCALL RTAssertMsg2WeakV(const char *pszFormat, va_list va)
3779{
3780 va_list vaCopy;
3781
3782 /*
3783 * Push the message to the loggers.
3784 */
3785 PRTLOGGER pLog = RTLogRelGetDefaultInstance();
3786 if (pLog)
3787 {
3788 va_copy(vaCopy, va);
3789 RTLogFormatV(rtLogOutput, pLog, pszFormat, vaCopy);
3790 va_end(vaCopy);
3791 }
3792 pLog = RTLogGetDefaultInstance(); /* Don't initialize it here... */
3793 if (pLog)
3794 {
3795 va_copy(vaCopy, va);
3796 RTLogFormatV(rtLogOutput, pLog, pszFormat, vaCopy);
3797 va_end(vaCopy);
3798 }
3799
3800 /*
3801 * Push it to the global VMM buffer.
3802 */
3803 PVMCC pVM = GVMMR0GetVMByEMT(NIL_RTNATIVETHREAD);
3804 if (pVM)
3805 {
3806 va_copy(vaCopy, va);
3807 RTStrPrintfV(pVM->vmm.s.szRing0AssertMsg2, sizeof(pVM->vmm.s.szRing0AssertMsg2), pszFormat, vaCopy);
3808 va_end(vaCopy);
3809 }
3810
3811 /*
3812 * Continue the normal way.
3813 */
3814 RTAssertMsg2V(pszFormat, va);
3815}
3816
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette