VirtualBox

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

Last change on this file since 108968 was 108968, checked in by vboxsync, 3 weeks ago

VMM,Main,Devices: Respect VBOX_VMM_TARGET_ARMV8 correctly on amd64 hosts (for IEM debugging purposes). jiraref:VBP-1598

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