VirtualBox

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

Last change on this file since 7034 was 7034, checked in by vboxsync, 17 years ago

Fixed unlinking bug in the object cleanup routine that would screw up the linked list (GVMM:iUsedHead) if the VM wasn't at the head of it. (inverted loop condition)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 53.7 KB
Line 
1/* $Id: GVMMR0.cpp 7034 2008-02-20 13:29:17Z vboxsync $ */
2/** @file
3 * GVMM - Global VM Manager.
4 */
5
6/*
7 * Copyright (C) 2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/** @page pg_GVMM GVMM - The Global VM Manager
20 *
21 * The Global VM Manager lives in ring-0. It's main function at the moment
22 * is to manage a list of all running VMs, keep a ring-0 only structure (GVM)
23 * for each of them, and assign them unique identifiers (so GMM can track
24 * page owners). The idea for the future is to add an idle priority kernel
25 * thread that can take care of tasks like page sharing.
26 *
27 * The GVMM will create a ring-0 object for each VM when it's registered,
28 * this is both for session cleanup purposes and for having a point where
29 * it's possible to implement usage polices later (in SUPR0ObjRegister).
30 */
31
32
33/*******************************************************************************
34* Header Files *
35*******************************************************************************/
36#define LOG_GROUP LOG_GROUP_GVMM
37#include <VBox/gvmm.h>
38#include "GVMMR0Internal.h"
39#include <VBox/gvm.h>
40#include <VBox/vm.h>
41#include <VBox/err.h>
42#include <iprt/alloc.h>
43#include <iprt/semaphore.h>
44#include <iprt/time.h>
45#include <VBox/log.h>
46#include <iprt/thread.h>
47#include <iprt/param.h>
48#include <iprt/string.h>
49#include <iprt/assert.h>
50#include <iprt/mem.h>
51#include <iprt/memobj.h>
52
53
54/*******************************************************************************
55* Structures and Typedefs *
56*******************************************************************************/
57
58/**
59 * Global VM handle.
60 */
61typedef struct GVMHANDLE
62{
63 /** The index of the next handle in the list (free or used). (0 is nil.) */
64 uint16_t volatile iNext;
65 /** Our own index / handle value. */
66 uint16_t iSelf;
67 /** The pointer to the ring-0 only (aka global) VM structure. */
68 PGVM pGVM;
69 /** The ring-0 mapping of the shared VM instance data. */
70 PVM pVM;
71 /** The virtual machine object. */
72 void *pvObj;
73 /** The session this VM is associated with. */
74 PSUPDRVSESSION pSession;
75 /** The ring-0 handle of the EMT thread.
76 * This is used for assertions and similar cases where we need to find the VM handle. */
77 RTNATIVETHREAD hEMT;
78} GVMHANDLE;
79/** Pointer to a global VM handle. */
80typedef GVMHANDLE *PGVMHANDLE;
81
82/**
83 * The GVMM instance data.
84 */
85typedef struct GVMM
86{
87 /** Eyecatcher / magic. */
88 uint32_t u32Magic;
89 /** The index of the head of the free handle chain. (0 is nil.) */
90 uint16_t volatile iFreeHead;
91 /** The index of the head of the active handle chain. (0 is nil.) */
92 uint16_t volatile iUsedHead;
93 /** The number of VMs. */
94 uint16_t volatile cVMs;
95// /** The number of halted EMT threads. */
96// uint16_t volatile cHaltedEMTs;
97 /** The lock used to serialize VM creation, destruction and associated events that
98 * isn't performance critical. Owners may acquire the list lock. */
99 RTSEMFASTMUTEX CreateDestroyLock;
100 /** The lock used to serialize used list updates and accesses.
101 * This indirectly includes scheduling since the scheduler will have to walk the
102 * used list to examin running VMs. Owners may not acquire any other locks. */
103 RTSEMFASTMUTEX UsedLock;
104 /** The handle array.
105 * The size of this array defines the maximum number of currently running VMs.
106 * The first entry is unused as it represents the NIL handle. */
107 GVMHANDLE aHandles[128];
108
109 /** @gcfgm{/GVMM/cVMsMeansCompany, 32-bit, 0, UINT32_MAX, 1}
110 * The number of VMs that means we no longer consider ourselves alone on a CPU/Core.
111 */
112 uint32_t cVMsMeansCompany;
113 /** @gcfgm{/GVMM/MinSleepAlone,32-bit, 0, 100000000, 750000, ns}
114 * The minimum sleep time for when we're alone, in nano seconds.
115 */
116 uint32_t nsMinSleepAlone;
117 /** @gcfgm{/GVMM/MinSleepCompany,32-bit,0, 100000000, 15000, ns}
118 * The minimum sleep time for when we've got company, in nano seconds.
119 */
120 uint32_t nsMinSleepCompany;
121 /** @gcfgm{/GVMM/EarlyWakeUp1, 32-bit, 0, 100000000, 25000, ns}
122 * The limit for the first round of early wakeups, given in nano seconds.
123 */
124 uint32_t nsEarlyWakeUp1;
125 /** @gcfgm{/GVMM/EarlyWakeUp2, 32-bit, 0, 100000000, 50000, ns}
126 * The limit for the second round of early wakeups, given in nano seconds.
127 */
128 uint32_t nsEarlyWakeUp2;
129} GVMM;
130/** Pointer to the GVMM instance data. */
131typedef GVMM *PGVMM;
132
133/** The GVMM::u32Magic value (Charlie Haden). */
134#define GVMM_MAGIC 0x19370806
135
136
137
138/*******************************************************************************
139* Global Variables *
140*******************************************************************************/
141/** Pointer to the GVMM instance data.
142 * (Just my general dislike for global variables.) */
143static PGVMM g_pGVMM = NULL;
144
145/** Macro for obtaining and validating the g_pGVMM pointer.
146 * On failure it will return from the invoking function with the specified return value.
147 *
148 * @param pGVMM The name of the pGVMM variable.
149 * @param rc The return value on failure. Use VERR_INTERNAL_ERROR for
150 * VBox status codes.
151 */
152#define GVMM_GET_VALID_INSTANCE(pGVMM, rc) \
153 do { \
154 (pGVMM) = g_pGVMM;\
155 AssertPtrReturn((pGVMM), (rc)); \
156 AssertMsgReturn((pGVMM)->u32Magic == GVMM_MAGIC, ("%p - %#x\n", (pGVMM), (pGVMM)->u32Magic), (rc)); \
157 } while (0)
158
159/** Macro for obtaining and validating the g_pGVMM pointer, void function variant.
160 * On failure it will return from the invoking function.
161 *
162 * @param pGVMM The name of the pGVMM variable.
163 */
164#define GVMM_GET_VALID_INSTANCE_VOID(pGVMM) \
165 do { \
166 (pGVMM) = g_pGVMM;\
167 AssertPtrReturnVoid((pGVMM)); \
168 AssertMsgReturnVoid((pGVMM)->u32Magic == GVMM_MAGIC, ("%p - %#x\n", (pGVMM), (pGVMM)->u32Magic)); \
169 } while (0)
170
171
172/*******************************************************************************
173* Internal Functions *
174*******************************************************************************/
175static void gvmmR0InitPerVMData(PGVM pGVM);
176static DECLCALLBACK(void) gvmmR0HandleObjDestructor(void *pvObj, void *pvGVMM, void *pvHandle);
177static int gvmmR0ByVM(PVM pVM, PGVM *ppGVM, PGVMM *ppGVMM, bool fTakeUsedLock);
178static int gvmmR0ByVMAndEMT(PVM pVM, PGVM *ppGVM, PGVMM *ppGVMM);
179
180
181/**
182 * Initializes the GVMM.
183 *
184 * This is called while owninng the loader sempahore (see supdrvIOCtl_LdrLoad()).
185 *
186 * @returns VBox status code.
187 */
188GVMMR0DECL(int) GVMMR0Init(void)
189{
190 LogFlow(("GVMMR0Init:\n"));
191
192 /*
193 * Allocate and initialize the instance data.
194 */
195 PGVMM pGVMM = (PGVMM)RTMemAllocZ(sizeof(*pGVMM));
196 if (!pGVMM)
197 return VERR_NO_MEMORY;
198 int rc = RTSemFastMutexCreate(&pGVMM->CreateDestroyLock);
199 if (RT_SUCCESS(rc))
200 {
201 rc = RTSemFastMutexCreate(&pGVMM->UsedLock);
202 if (RT_SUCCESS(rc))
203 {
204 pGVMM->u32Magic = GVMM_MAGIC;
205 pGVMM->iUsedHead = 0;
206 pGVMM->iFreeHead = 1;
207
208 /* the nil handle */
209 pGVMM->aHandles[0].iSelf = 0;
210 pGVMM->aHandles[0].iNext = 0;
211
212 /* the tail */
213 unsigned i = RT_ELEMENTS(pGVMM->aHandles) - 1;
214 pGVMM->aHandles[i].iSelf = i;
215 pGVMM->aHandles[i].iNext = 0; /* nil */
216
217 /* the rest */
218 while (i-- > 1)
219 {
220 pGVMM->aHandles[i].iSelf = i;
221 pGVMM->aHandles[i].iNext = i + 1;
222 }
223
224 /* The default configuration values. */
225 pGVMM->cVMsMeansCompany = 1; /** @todo should be adjusted to relative to the cpu count or something... */
226 pGVMM->nsMinSleepAlone = 750000 /* ns (0.750 ms) */; /** @todo this should be adjusted to be 75% (or something) of the scheduler granularity... */
227 pGVMM->nsMinSleepCompany = 15000 /* ns (0.015 ms) */;
228 pGVMM->nsEarlyWakeUp1 = 25000 /* ns (0.025 ms) */;
229 pGVMM->nsEarlyWakeUp2 = 50000 /* ns (0.050 ms) */;
230
231 g_pGVMM = pGVMM;
232 LogFlow(("GVMMR0Init: pGVMM=%p\n", pGVMM));
233 return VINF_SUCCESS;
234 }
235
236 RTSemFastMutexDestroy(pGVMM->CreateDestroyLock);
237 }
238
239 RTMemFree(pGVMM);
240 return rc;
241}
242
243
244/**
245 * Terminates the GVM.
246 *
247 * This is called while owning the loader semaphore (see supdrvLdrFree()).
248 * And unless something is wrong, there should be absolutely no VMs
249 * registered at this point.
250 */
251GVMMR0DECL(void) GVMMR0Term(void)
252{
253 LogFlow(("GVMMR0Term:\n"));
254
255 PGVMM pGVMM = g_pGVMM;
256 g_pGVMM = NULL;
257 if (RT_UNLIKELY(!VALID_PTR(pGVMM)))
258 {
259 SUPR0Printf("GVMMR0Term: pGVMM=%p\n", pGVMM);
260 return;
261 }
262
263 pGVMM->u32Magic++;
264
265 RTSemFastMutexDestroy(pGVMM->UsedLock);
266 pGVMM->UsedLock = NIL_RTSEMFASTMUTEX;
267 RTSemFastMutexDestroy(pGVMM->CreateDestroyLock);
268 pGVMM->CreateDestroyLock = NIL_RTSEMFASTMUTEX;
269
270 pGVMM->iFreeHead = 0;
271 if (pGVMM->iUsedHead)
272 {
273 SUPR0Printf("GVMMR0Term: iUsedHead=%#x! (cVMs=%#x)\n", pGVMM->iUsedHead, pGVMM->cVMs);
274 pGVMM->iUsedHead = 0;
275 }
276
277 RTMemFree(pGVMM);
278}
279
280
281/**
282 * A quick hack for setting global config values.
283 *
284 * @returns VBox status code.
285 *
286 * @param pSession The session handle. Used for authentication.
287 * @param pszName The variable name.
288 * @param u64Value The new value.
289 */
290GVMMR0DECL(int) GVMMR0SetConfig(PSUPDRVSESSION pSession, const char *pszName, uint64_t u64Value)
291{
292 /*
293 * Validate input.
294 */
295 PGVMM pGVMM;
296 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
297 AssertPtrReturn(pSession, VERR_INVALID_HANDLE);
298 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
299
300 /*
301 * String switch time!
302 */
303 if (strncmp(pszName, "/GVMM/", sizeof("/GVMM/") - 1))
304 return VERR_CFGM_VALUE_NOT_FOUND; /* borrow status codes from CFGM... */
305 int rc = VINF_SUCCESS;
306 pszName += sizeof("/GVMM/") - 1;
307 if (!strcmp(pszName, "cVMsMeansCompany"))
308 {
309 if (u64Value <= UINT32_MAX)
310 pGVMM->cVMsMeansCompany = u64Value;
311 else
312 rc = VERR_OUT_OF_RANGE;
313 }
314 else if (!strcmp(pszName, "MinSleepAlone"))
315 {
316 if (u64Value <= 100000000)
317 pGVMM->nsMinSleepAlone = u64Value;
318 else
319 rc = VERR_OUT_OF_RANGE;
320 }
321 else if (!strcmp(pszName, "MinSleepCompany"))
322 {
323 if (u64Value <= 100000000)
324 pGVMM->nsMinSleepCompany = u64Value;
325 else
326 rc = VERR_OUT_OF_RANGE;
327 }
328 else if (!strcmp(pszName, "EarlyWakeUp1"))
329 {
330 if (u64Value <= 100000000)
331 pGVMM->nsEarlyWakeUp1 = u64Value;
332 else
333 rc = VERR_OUT_OF_RANGE;
334 }
335 else if (!strcmp(pszName, "EarlyWakeUp2"))
336 {
337 if (u64Value <= 100000000)
338 pGVMM->nsEarlyWakeUp2 = u64Value;
339 else
340 rc = VERR_OUT_OF_RANGE;
341 }
342 else
343 rc = VERR_CFGM_VALUE_NOT_FOUND;
344 return rc;
345}
346
347
348/**
349 * A quick hack for getting global config values.
350 *
351 * @returns VBox status code.
352 *
353 * @param pSession The session handle. Used for authentication.
354 * @param pszName The variable name.
355 * @param u64Value The new value.
356 */
357GVMMR0DECL(int) GVMMR0QueryConfig(PSUPDRVSESSION pSession, const char *pszName, uint64_t *pu64Value)
358{
359 /*
360 * Validate input.
361 */
362 PGVMM pGVMM;
363 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
364 AssertPtrReturn(pSession, VERR_INVALID_HANDLE);
365 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
366 AssertPtrReturn(pu64Value, VERR_INVALID_POINTER);
367
368 /*
369 * String switch time!
370 */
371 if (strncmp(pszName, "/GVMM/", sizeof("/GVMM/") - 1))
372 return VERR_CFGM_VALUE_NOT_FOUND; /* borrow status codes from CFGM... */
373 int rc = VINF_SUCCESS;
374 pszName += sizeof("/GVMM/") - 1;
375 if (!strcmp(pszName, "cVMsMeansCompany"))
376 *pu64Value = pGVMM->cVMsMeansCompany;
377 else if (!strcmp(pszName, "MinSleepAlone"))
378 *pu64Value = pGVMM->nsMinSleepAlone;
379 else if (!strcmp(pszName, "MinSleepCompany"))
380 *pu64Value = pGVMM->nsMinSleepCompany;
381 else if (!strcmp(pszName, "EarlyWakeUp1"))
382 *pu64Value = pGVMM->nsEarlyWakeUp1;
383 else if (!strcmp(pszName, "EarlyWakeUp2"))
384 *pu64Value = pGVMM->nsEarlyWakeUp2;
385 else
386 rc = VERR_CFGM_VALUE_NOT_FOUND;
387 return rc;
388}
389
390
391/**
392 * Try acquire the 'used' lock.
393 *
394 * @returns IPRT status code, see RTSemFastMutexRequest.
395 * @param pGVMM The GVMM instance data.
396 */
397DECLINLINE(int) gvmmR0UsedLock(PGVMM pGVMM)
398{
399 LogFlow(("++gvmmR0UsedLock(%p)\n", pGVMM));
400 int rc = RTSemFastMutexRequest(pGVMM->UsedLock);
401 LogFlow(("gvmmR0UsedLock(%p)->%Rrc\n", pGVMM, rc));
402 return rc;
403}
404
405
406/**
407 * Release the 'used' lock.
408 *
409 * @returns IPRT status code, see RTSemFastMutexRelease.
410 * @param pGVMM The GVMM instance data.
411 */
412DECLINLINE(int) gvmmR0UsedUnlock(PGVMM pGVMM)
413{
414 LogFlow(("--gvmmR0UsedUnlock(%p)\n", pGVMM));
415 int rc = RTSemFastMutexRelease(pGVMM->UsedLock);
416 AssertRC(rc);
417 return rc;
418}
419
420
421/**
422 * Try acquire the 'create & destroy' lock.
423 *
424 * @returns IPRT status code, see RTSemFastMutexRequest.
425 * @param pGVMM The GVMM instance data.
426 */
427DECLINLINE(int) gvmmR0CreateDestroyLock(PGVMM pGVMM)
428{
429 LogFlow(("++gvmmR0CreateDestroyLock(%p)\n", pGVMM));
430 int rc = RTSemFastMutexRequest(pGVMM->CreateDestroyLock);
431 LogFlow(("gvmmR0CreateDestroyLock(%p)->%Rrc\n", pGVMM, rc));
432 return rc;
433}
434
435
436/**
437 * Release the 'create & destroy' lock.
438 *
439 * @returns IPRT status code, see RTSemFastMutexRequest.
440 * @param pGVMM The GVMM instance data.
441 */
442DECLINLINE(int) gvmmR0CreateDestroyUnlock(PGVMM pGVMM)
443{
444 LogFlow(("--gvmmR0CreateDestroyUnlock(%p)\n", pGVMM));
445 int rc = RTSemFastMutexRelease(pGVMM->CreateDestroyLock);
446 AssertRC(rc);
447 return rc;
448}
449
450
451/**
452 * Request wrapper for the GVMMR0CreateVM API.
453 *
454 * @returns VBox status code.
455 * @param pReq The request buffer.
456 */
457GVMMR0DECL(int) GVMMR0CreateVMReq(PGVMMCREATEVMREQ pReq)
458{
459 /*
460 * Validate the request.
461 */
462 if (!VALID_PTR(pReq))
463 return VERR_INVALID_POINTER;
464 if (pReq->Hdr.cbReq != sizeof(*pReq))
465 return VERR_INVALID_PARAMETER;
466 if (!VALID_PTR(pReq->pSession))
467 return VERR_INVALID_POINTER;
468
469 /*
470 * Execute it.
471 */
472 PVM pVM;
473 pReq->pVMR0 = NULL;
474 pReq->pVMR3 = NIL_RTR3PTR;
475 int rc = GVMMR0CreateVM(pReq->pSession, &pVM);
476 if (RT_SUCCESS(rc))
477 {
478 pReq->pVMR0 = pVM;
479 pReq->pVMR3 = pVM->pVMR3;
480 }
481 return rc;
482}
483
484
485/**
486 * Allocates the VM structure and registers it with GVM.
487 *
488 * The caller will become the VM owner and there by the EMT.
489 *
490 * @returns VBox status code.
491 * @param pSession The support driver session.
492 * @param ppVM Where to store the pointer to the VM structure.
493 *
494 * @thread EMT.
495 */
496GVMMR0DECL(int) GVMMR0CreateVM(PSUPDRVSESSION pSession, PVM *ppVM)
497{
498 LogFlow(("GVMMR0CreateVM: pSession=%p\n", pSession));
499 PGVMM pGVMM;
500 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
501
502 AssertPtrReturn(ppVM, VERR_INVALID_POINTER);
503 *ppVM = NULL;
504
505 RTNATIVETHREAD hEMT = RTThreadNativeSelf();
506 AssertReturn(hEMT != NIL_RTNATIVETHREAD, VERR_INTERNAL_ERROR);
507
508 /*
509 * The whole allocation process is protected by the lock.
510 */
511 int rc = gvmmR0CreateDestroyLock(pGVMM);
512 AssertRCReturn(rc, rc);
513
514 /*
515 * Allocate a handle first so we don't waste resources unnecessarily.
516 */
517 uint16_t iHandle = pGVMM->iFreeHead;
518 if (iHandle)
519 {
520 PGVMHANDLE pHandle = &pGVMM->aHandles[iHandle];
521
522 /* consistency checks, a bit paranoid as always. */
523 if ( !pHandle->pVM
524 && !pHandle->pGVM
525 && !pHandle->pvObj
526 && pHandle->iSelf == iHandle)
527 {
528 pHandle->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_VM, gvmmR0HandleObjDestructor, pGVMM, pHandle);
529 if (pHandle->pvObj)
530 {
531 /*
532 * Move the handle from the free to used list and perform permission checks.
533 */
534 rc = gvmmR0UsedLock(pGVMM);
535 AssertRC(rc);
536
537 pGVMM->iFreeHead = pHandle->iNext;
538 pHandle->iNext = pGVMM->iUsedHead;
539 pGVMM->iUsedHead = iHandle;
540 pGVMM->cVMs++;
541
542 pHandle->pVM = NULL;
543 pHandle->pGVM = NULL;
544 pHandle->pSession = pSession;
545 pHandle->hEMT = NIL_RTNATIVETHREAD;
546
547 gvmmR0UsedUnlock(pGVMM);
548
549 rc = SUPR0ObjVerifyAccess(pHandle->pvObj, pSession, NULL);
550 if (RT_SUCCESS(rc))
551 {
552 /*
553 * Allocate the global VM structure (GVM) and initialize it.
554 */
555 PGVM pGVM = (PGVM)RTMemAllocZ(sizeof(*pGVM));
556 if (pGVM)
557 {
558 pGVM->u32Magic = GVM_MAGIC;
559 pGVM->hSelf = iHandle;
560 pGVM->hEMT = NIL_RTNATIVETHREAD;
561 pGVM->pVM = NULL;
562
563 gvmmR0InitPerVMData(pGVM);
564 /* GMMR0InitPerVMData(pGVM); - later */
565
566 /*
567 * Allocate the shared VM structure and associated page array.
568 */
569 const size_t cPages = RT_ALIGN(sizeof(VM), PAGE_SIZE) >> PAGE_SHIFT;
570 rc = RTR0MemObjAllocLow(&pGVM->gvmm.s.VMMemObj, cPages << PAGE_SHIFT, false /* fExecutable */);
571 if (RT_SUCCESS(rc))
572 {
573 PVM pVM = (PVM)RTR0MemObjAddress(pGVM->gvmm.s.VMMemObj); AssertPtr(pVM);
574 memset(pVM, 0, cPages << PAGE_SHIFT);
575 pVM->enmVMState = VMSTATE_CREATING;
576 pVM->pVMR0 = pVM;
577 pVM->pSession = pSession;
578 pVM->hSelf = iHandle;
579
580 rc = RTR0MemObjAllocPage(&pGVM->gvmm.s.VMPagesMemObj, cPages * sizeof(SUPPAGE), false /* fExecutable */);
581 if (RT_SUCCESS(rc))
582 {
583 PSUPPAGE paPages = (PSUPPAGE)RTR0MemObjAddress(pGVM->gvmm.s.VMPagesMemObj); AssertPtr(paPages);
584 for (size_t iPage = 0; iPage < cPages; iPage++)
585 {
586 paPages[iPage].uReserved = 0;
587 paPages[iPage].Phys = RTR0MemObjGetPagePhysAddr(pGVM->gvmm.s.VMMemObj, iPage);
588 Assert(paPages[iPage].Phys != NIL_RTHCPHYS);
589 }
590
591 /*
592 * Map them into ring-3.
593 */
594 rc = RTR0MemObjMapUser(&pGVM->gvmm.s.VMMapObj, pGVM->gvmm.s.VMMemObj, (RTR3PTR)-1, 0,
595 RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS);
596 if (RT_SUCCESS(rc))
597 {
598 pVM->pVMR3 = RTR0MemObjAddressR3(pGVM->gvmm.s.VMMapObj);
599 AssertPtr((void *)pVM->pVMR3);
600
601 rc = RTR0MemObjMapUser(&pGVM->gvmm.s.VMPagesMapObj, pGVM->gvmm.s.VMPagesMemObj, (RTR3PTR)-1, 0,
602 RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS);
603 if (RT_SUCCESS(rc))
604 {
605 pVM->paVMPagesR3 = RTR0MemObjAddressR3(pGVM->gvmm.s.VMPagesMapObj);
606 AssertPtr((void *)pVM->paVMPagesR3);
607
608 /* complete the handle - take the UsedLock sem just to be careful. */
609 rc = gvmmR0UsedLock(pGVMM);
610 AssertRC(rc);
611
612 pHandle->pVM = pVM;
613 pHandle->pGVM = pGVM;
614 pHandle->hEMT = hEMT;
615 pGVM->pVM = pVM;
616 pGVM->hEMT = hEMT;
617
618 gvmmR0UsedUnlock(pGVMM);
619 gvmmR0CreateDestroyUnlock(pGVMM);
620
621 *ppVM = pVM;
622 Log(("GVMMR0CreateVM: pVM=%p pVMR3=%p pGVM=%p hGVM=%d\n", pVM, pVM->pVMR3, pGVM, iHandle));
623 return VINF_SUCCESS;
624 }
625
626 RTR0MemObjFree(pGVM->gvmm.s.VMMapObj, false /* fFreeMappings */);
627 pGVM->gvmm.s.VMMapObj = NIL_RTR0MEMOBJ;
628 }
629 RTR0MemObjFree(pGVM->gvmm.s.VMPagesMemObj, false /* fFreeMappings */);
630 pGVM->gvmm.s.VMPagesMemObj = NIL_RTR0MEMOBJ;
631 }
632 RTR0MemObjFree(pGVM->gvmm.s.VMMemObj, false /* fFreeMappings */);
633 pGVM->gvmm.s.VMMemObj = NIL_RTR0MEMOBJ;
634 }
635 }
636 }
637 /* else: The user wasn't permitted to create this VM. */
638
639 /*
640 * The handle will be freed by gvmmR0HandleObjDestructor as we release the
641 * object reference here. A little extra mess because of non-recursive lock.
642 */
643 void *pvObj = pHandle->pvObj;
644 pHandle->pvObj = NULL;
645 gvmmR0CreateDestroyUnlock(pGVMM);
646
647 SUPR0ObjRelease(pvObj, pSession);
648
649 SUPR0Printf("GVMMR0CreateVM: failed, rc=%d\n", rc);
650 return rc;
651 }
652
653 rc = VERR_NO_MEMORY;
654 }
655 else
656 rc = VERR_INTERNAL_ERROR;
657 }
658 else
659 rc = VERR_GVM_TOO_MANY_VMS;
660
661 gvmmR0CreateDestroyUnlock(pGVMM);
662 return rc;
663}
664
665
666/**
667 * Initializes the per VM data belonging to GVMM.
668 *
669 * @param pGVM Pointer to the global VM structure.
670 */
671static void gvmmR0InitPerVMData(PGVM pGVM)
672{
673 AssertCompile(RT_SIZEOFMEMB(GVM,gvmm.s) <= RT_SIZEOFMEMB(GVM,gvmm.padding));
674 Assert(RT_SIZEOFMEMB(GVM,gvmm.s) <= RT_SIZEOFMEMB(GVM,gvmm.padding));
675 pGVM->gvmm.s.VMMemObj = NIL_RTR0MEMOBJ;
676 pGVM->gvmm.s.VMMapObj = NIL_RTR0MEMOBJ;
677 pGVM->gvmm.s.VMPagesMemObj = NIL_RTR0MEMOBJ;
678 pGVM->gvmm.s.VMPagesMapObj = NIL_RTR0MEMOBJ;
679 pGVM->gvmm.s.HaltEventMulti = NIL_RTSEMEVENTMULTI;
680}
681
682
683/**
684 * Does the VM initialization.
685 *
686 * @returns VBox status code.
687 * @param pVM Pointer to the shared VM structure.
688 */
689GVMMR0DECL(int) GVMMR0InitVM(PVM pVM)
690{
691 LogFlow(("GVMMR0InitVM: pVM=%p\n", pVM));
692
693 /*
694 * Validate the VM structure, state and handle.
695 */
696 PGVM pGVM;
697 PGVMM pGVMM;
698 int rc = gvmmR0ByVMAndEMT(pVM, &pGVM, &pGVMM);
699 if (RT_SUCCESS(rc))
700 {
701 if (pGVM->gvmm.s.HaltEventMulti == NIL_RTSEMEVENTMULTI)
702 {
703 rc = RTSemEventMultiCreate(&pGVM->gvmm.s.HaltEventMulti);
704 if (RT_FAILURE(rc))
705 pGVM->gvmm.s.HaltEventMulti = NIL_RTSEMEVENTMULTI;
706 }
707 else
708 rc = VERR_WRONG_ORDER;
709 }
710
711 LogFlow(("GVMMR0InitVM: returns %Rrc\n", rc));
712 return rc;
713}
714
715
716/**
717 * Destroys the VM, freeing all associated resources (the ring-0 ones anyway).
718 *
719 * This is call from the vmR3DestroyFinalBit and from a error path in VMR3Create,
720 * and the caller is not the EMT thread, unfortunately. For security reasons, it
721 * would've been nice if the caller was actually the EMT thread or that we somehow
722 * could've associated the calling thread with the VM up front.
723 *
724 * @returns VBox status code.
725 * @param pVM Where to store the pointer to the VM structure.
726 *
727 * @thread EMT if it's associated with the VM, otherwise any thread.
728 */
729GVMMR0DECL(int) GVMMR0DestroyVM(PVM pVM)
730{
731 LogFlow(("GVMMR0DestroyVM: pVM=%p\n", pVM));
732 PGVMM pGVMM;
733 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
734
735
736 /*
737 * Validate the VM structure, state and caller.
738 */
739 AssertPtrReturn(pVM, VERR_INVALID_POINTER);
740 AssertReturn(!((uintptr_t)pVM & PAGE_OFFSET_MASK), VERR_INVALID_POINTER);
741 AssertMsgReturn(pVM->enmVMState >= VMSTATE_CREATING && pVM->enmVMState <= VMSTATE_TERMINATED, ("%d\n", pVM->enmVMState), VERR_WRONG_ORDER);
742
743 uint32_t hGVM = pVM->hSelf;
744 AssertReturn(hGVM != NIL_GVM_HANDLE, VERR_INVALID_HANDLE);
745 AssertReturn(hGVM < RT_ELEMENTS(pGVMM->aHandles), VERR_INVALID_HANDLE);
746
747 PGVMHANDLE pHandle = &pGVMM->aHandles[hGVM];
748 AssertReturn(pHandle->pVM == pVM, VERR_NOT_OWNER);
749
750 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
751 AssertReturn(pHandle->hEMT == hSelf || pHandle->hEMT == NIL_RTNATIVETHREAD, VERR_NOT_OWNER);
752
753 /*
754 * Lookup the handle and destroy the object.
755 * Since the lock isn't recursive and we'll have to leave it before dereferencing the
756 * object, we take some precautions against racing callers just in case...
757 */
758 int rc = gvmmR0CreateDestroyLock(pGVMM);
759 AssertRC(rc);
760
761 /* be careful here because we might theoretically be racing someone else cleaning up. */
762 if ( pHandle->pVM == pVM
763 && ( pHandle->hEMT == hSelf
764 || pHandle->hEMT == NIL_RTNATIVETHREAD)
765 && VALID_PTR(pHandle->pvObj)
766 && VALID_PTR(pHandle->pSession)
767 && VALID_PTR(pHandle->pGVM)
768 && pHandle->pGVM->u32Magic == GVM_MAGIC)
769 {
770 void *pvObj = pHandle->pvObj;
771 pHandle->pvObj = NULL;
772 gvmmR0CreateDestroyUnlock(pGVMM);
773
774 SUPR0ObjRelease(pvObj, pHandle->pSession);
775 }
776 else
777 {
778 SUPR0Printf("GVMMR0DestroyVM: pHandle=%p:{.pVM=%p, hEMT=%p, .pvObj=%p} pVM=%p hSelf=%p\n",
779 pHandle, pHandle->pVM, pHandle->hEMT, pHandle->pvObj, pVM, hSelf);
780 gvmmR0CreateDestroyUnlock(pGVMM);
781 rc = VERR_INTERNAL_ERROR;
782 }
783
784 return rc;
785}
786
787
788/**
789 * Handle destructor.
790 *
791 * @param pvGVMM The GVM instance pointer.
792 * @param pvHandle The handle pointer.
793 */
794static DECLCALLBACK(void) gvmmR0HandleObjDestructor(void *pvObj, void *pvGVMM, void *pvHandle)
795{
796 LogFlow(("gvmmR0HandleObjDestructor: %p %p %p\n", pvObj, pvGVMM, pvHandle));
797
798 /*
799 * Some quick, paranoid, input validation.
800 */
801 PGVMHANDLE pHandle = (PGVMHANDLE)pvHandle;
802 AssertPtr(pHandle);
803 PGVMM pGVMM = (PGVMM)pvGVMM;
804 Assert(pGVMM == g_pGVMM);
805 const uint16_t iHandle = pHandle - &pGVMM->aHandles[0];
806 if ( !iHandle
807 || iHandle >= RT_ELEMENTS(pGVMM->aHandles)
808 || iHandle != pHandle->iSelf)
809 {
810 SUPR0Printf("GVM: handle %d is out of range or corrupt (iSelf=%d)!\n", iHandle, pHandle->iSelf);
811 return;
812 }
813
814 int rc = gvmmR0CreateDestroyLock(pGVMM);
815 AssertRC(rc);
816 rc = gvmmR0UsedLock(pGVMM);
817 AssertRC(rc);
818
819 /*
820 * This is a tad slow but a doubly linked list is too much hazzle.
821 */
822 if (RT_UNLIKELY(pHandle->iNext >= RT_ELEMENTS(pGVMM->aHandles)))
823 {
824 SUPR0Printf("GVM: used list index %d is out of range!\n", pHandle->iNext);
825 gvmmR0UsedUnlock(pGVMM);
826 gvmmR0CreateDestroyUnlock(pGVMM);
827 return;
828 }
829
830 if (pGVMM->iUsedHead == iHandle)
831 pGVMM->iUsedHead = pHandle->iNext;
832 else
833 {
834 uint16_t iPrev = pGVMM->iUsedHead;
835 int c = RT_ELEMENTS(pGVMM->aHandles) + 2;
836 while (iPrev)
837 {
838 if (RT_UNLIKELY(iPrev >= RT_ELEMENTS(pGVMM->aHandles)))
839 {
840 SUPR0Printf("GVM: used list index %d is out of range!\n");
841 gvmmR0UsedUnlock(pGVMM);
842 gvmmR0CreateDestroyUnlock(pGVMM);
843 return;
844 }
845 if (RT_UNLIKELY(c-- <= 0))
846 {
847 iPrev = 0;
848 break;
849 }
850
851 if (pGVMM->aHandles[iPrev].iNext == iHandle)
852 break;
853 iPrev = pGVMM->aHandles[iPrev].iNext;
854 }
855 if (!iPrev)
856 {
857 SUPR0Printf("GVM: can't find the handle previous previous of %d!\n", pHandle->iSelf);
858 gvmmR0UsedUnlock(pGVMM);
859 gvmmR0CreateDestroyUnlock(pGVMM);
860 return;
861 }
862
863 Assert(pGVMM->aHandles[iPrev].iNext == iHandle);
864 pGVMM->aHandles[iPrev].iNext = pHandle->iNext;
865 }
866 pHandle->iNext = 0;
867 pGVMM->cVMs--;
868
869 gvmmR0UsedUnlock(pGVMM);
870
871 /*
872 * Do the global cleanup round.
873 */
874 PGVM pGVM = pHandle->pGVM;
875 if ( VALID_PTR(pGVM)
876 && pGVM->u32Magic == GVM_MAGIC)
877 {
878 /// @todo GMMR0CleanupVM(pGVM);
879
880 /*
881 * Do the GVMM cleanup - must be done last.
882 */
883 /* The VM and VM pages mappings/allocations. */
884 if (pGVM->gvmm.s.VMPagesMapObj != NIL_RTR0MEMOBJ)
885 {
886 rc = RTR0MemObjFree(pGVM->gvmm.s.VMPagesMapObj, false /* fFreeMappings */); AssertRC(rc);
887 pGVM->gvmm.s.VMPagesMapObj = NIL_RTR0MEMOBJ;
888 }
889
890 if (pGVM->gvmm.s.VMMapObj != NIL_RTR0MEMOBJ)
891 {
892 rc = RTR0MemObjFree(pGVM->gvmm.s.VMMapObj, false /* fFreeMappings */); AssertRC(rc);
893 pGVM->gvmm.s.VMMapObj = NIL_RTR0MEMOBJ;
894 }
895
896 if (pGVM->gvmm.s.VMPagesMemObj != NIL_RTR0MEMOBJ)
897 {
898 rc = RTR0MemObjFree(pGVM->gvmm.s.VMPagesMemObj, false /* fFreeMappings */); AssertRC(rc);
899 pGVM->gvmm.s.VMPagesMemObj = NIL_RTR0MEMOBJ;
900 }
901
902 if (pGVM->gvmm.s.VMMemObj != NIL_RTR0MEMOBJ)
903 {
904 rc = RTR0MemObjFree(pGVM->gvmm.s.VMMemObj, false /* fFreeMappings */); AssertRC(rc);
905 pGVM->gvmm.s.VMMemObj = NIL_RTR0MEMOBJ;
906 }
907
908 /* the GVM structure itself. */
909 pGVM->u32Magic++;
910 RTMemFree(pGVM);
911 }
912 /* else: GVMMR0CreateVM cleanup. */
913
914 /*
915 * Free the handle.
916 * Reacquire the UsedLock here to since we're updating handle fields.
917 */
918 rc = gvmmR0UsedLock(pGVMM);
919 AssertRC(rc);
920
921 pHandle->iNext = pGVMM->iFreeHead;
922 pGVMM->iFreeHead = iHandle;
923 ASMAtomicXchgPtr((void * volatile *)&pHandle->pGVM, NULL);
924 ASMAtomicXchgPtr((void * volatile *)&pHandle->pVM, NULL);
925 ASMAtomicXchgPtr((void * volatile *)&pHandle->pvObj, NULL);
926 ASMAtomicXchgPtr((void * volatile *)&pHandle->pSession, NULL);
927 ASMAtomicXchgSize(&pHandle->hEMT, NIL_RTNATIVETHREAD);
928
929 gvmmR0UsedUnlock(pGVMM);
930 gvmmR0CreateDestroyUnlock(pGVMM);
931 LogFlow(("gvmmR0HandleObjDestructor: returns\n"));
932}
933
934
935/**
936 * Lookup a GVM structure by its handle.
937 *
938 * @returns The GVM pointer on success, NULL on failure.
939 * @param hGVM The global VM handle. Asserts on bad handle.
940 */
941GVMMR0DECL(PGVM) GVMMR0ByHandle(uint32_t hGVM)
942{
943 PGVMM pGVMM;
944 GVMM_GET_VALID_INSTANCE(pGVMM, NULL);
945
946 /*
947 * Validate.
948 */
949 AssertReturn(hGVM != NIL_GVM_HANDLE, NULL);
950 AssertReturn(hGVM < RT_ELEMENTS(pGVMM->aHandles), NULL);
951
952 /*
953 * Look it up.
954 */
955 PGVMHANDLE pHandle = &pGVMM->aHandles[hGVM];
956 AssertPtrReturn(pHandle->pVM, NULL);
957 AssertPtrReturn(pHandle->pvObj, NULL);
958 PGVM pGVM = pHandle->pGVM;
959 AssertPtrReturn(pGVM, NULL);
960 AssertReturn(pGVM->pVM == pHandle->pVM, NULL);
961
962 return pHandle->pGVM;
963}
964
965
966/**
967 * Lookup a GVM structure by the shared VM structure.
968 *
969 * @returns VBox status code.
970 * @param pVM The shared VM structure (the ring-0 mapping).
971 * @param ppGVM Where to store the GVM pointer.
972 * @param ppGVMM Where to store the pointer to the GVMM instance data.
973 * @param fTakeUsedLock Whether to take the used lock or not.
974 * Be very careful if not taking the lock as it's possible that
975 * the VM will disappear then.
976 *
977 * @remark This will not assert on an invalid pVM but try return sliently.
978 */
979static int gvmmR0ByVM(PVM pVM, PGVM *ppGVM, PGVMM *ppGVMM, bool fTakeUsedLock)
980{
981 PGVMM pGVMM;
982 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
983
984 /*
985 * Validate.
986 */
987 if (RT_UNLIKELY( !VALID_PTR(pVM)
988 || ((uintptr_t)pVM & PAGE_OFFSET_MASK)))
989 return VERR_INVALID_POINTER;
990 if (RT_UNLIKELY( pVM->enmVMState < VMSTATE_CREATING
991 || pVM->enmVMState >= VMSTATE_TERMINATED))
992 return VERR_INVALID_POINTER;
993
994 uint16_t hGVM = pVM->hSelf;
995 if (RT_UNLIKELY( hGVM == NIL_GVM_HANDLE
996 || hGVM >= RT_ELEMENTS(pGVMM->aHandles)))
997 return VERR_INVALID_HANDLE;
998
999 /*
1000 * Look it up.
1001 */
1002 PGVMHANDLE pHandle = &pGVMM->aHandles[hGVM];
1003 PGVM pGVM;
1004 if (fTakeUsedLock)
1005 {
1006 int rc = gvmmR0UsedLock(pGVMM);
1007 AssertRCReturn(rc, rc);
1008
1009 pGVM = pHandle->pGVM;
1010 if (RT_UNLIKELY( pHandle->pVM != pVM
1011 || !VALID_PTR(pHandle->pvObj)
1012 || !VALID_PTR(pGVM)
1013 || pGVM->pVM != pVM))
1014 {
1015 gvmmR0UsedUnlock(pGVMM);
1016 return VERR_INVALID_HANDLE;
1017 }
1018 }
1019 else
1020 {
1021 if (RT_UNLIKELY(pHandle->pVM != pVM))
1022 return VERR_INVALID_HANDLE;
1023 if (RT_UNLIKELY(!VALID_PTR(pHandle->pvObj)))
1024 return VERR_INVALID_HANDLE;
1025
1026 pGVM = pHandle->pGVM;
1027 if (RT_UNLIKELY(!VALID_PTR(pGVM)))
1028 return VERR_INVALID_HANDLE;
1029 if (RT_UNLIKELY(pGVM->pVM != pVM))
1030 return VERR_INVALID_HANDLE;
1031 }
1032
1033 *ppGVM = pGVM;
1034 *ppGVMM = pGVMM;
1035 return VINF_SUCCESS;
1036}
1037
1038
1039/**
1040 * Lookup a GVM structure by the shared VM structure.
1041 *
1042 * @returns The GVM pointer on success, NULL on failure.
1043 * @param pVM The shared VM structure (the ring-0 mapping).
1044 *
1045 * @remark This will not take the 'used'-lock because it doesn't do
1046 * nesting and this function will be used from under the lock.
1047 */
1048GVMMR0DECL(PGVM) GVMMR0ByVM(PVM pVM)
1049{
1050 PGVMM pGVMM;
1051 PGVM pGVM;
1052 int rc = gvmmR0ByVM(pVM, &pGVM, &pGVMM, false /* fTakeUsedLock */);
1053 if (RT_SUCCESS(rc))
1054 return pGVM;
1055 AssertRC(rc);
1056 return NULL;
1057}
1058
1059
1060/**
1061 * Lookup a GVM structure by the shared VM structure
1062 * and ensuring that the caller is the EMT thread.
1063 *
1064 * @returns VBox status code.
1065 * @param pVM The shared VM structure (the ring-0 mapping).
1066 * @param ppGVM Where to store the GVM pointer.
1067 * @param ppGVMM Where to store the pointer to the GVMM instance data.
1068 * @thread EMT
1069 *
1070 * @remark This will assert in failure paths.
1071 */
1072static int gvmmR0ByVMAndEMT(PVM pVM, PGVM *ppGVM, PGVMM *ppGVMM)
1073{
1074 PGVMM pGVMM;
1075 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
1076
1077 /*
1078 * Validate.
1079 */
1080 AssertPtrReturn(pVM, VERR_INVALID_POINTER);
1081 AssertReturn(!((uintptr_t)pVM & PAGE_OFFSET_MASK), VERR_INVALID_POINTER);
1082
1083 uint16_t hGVM = pVM->hSelf;
1084 AssertReturn(hGVM != NIL_GVM_HANDLE, VERR_INVALID_HANDLE);
1085 AssertReturn(hGVM < RT_ELEMENTS(pGVMM->aHandles), VERR_INVALID_HANDLE);
1086
1087 /*
1088 * Look it up.
1089 */
1090 PGVMHANDLE pHandle = &pGVMM->aHandles[hGVM];
1091 RTNATIVETHREAD hAllegedEMT = RTThreadNativeSelf();
1092 AssertMsgReturn(pHandle->hEMT == hAllegedEMT, ("hEMT %x hAllegedEMT %x\n", pHandle->hEMT, hAllegedEMT), VERR_NOT_OWNER);
1093 AssertReturn(pHandle->pVM == pVM, VERR_NOT_OWNER);
1094 AssertPtrReturn(pHandle->pvObj, VERR_INTERNAL_ERROR);
1095
1096 PGVM pGVM = pHandle->pGVM;
1097 AssertPtrReturn(pGVM, VERR_INTERNAL_ERROR);
1098 AssertReturn(pGVM->pVM == pVM, VERR_INTERNAL_ERROR);
1099 AssertReturn(pGVM->hEMT == hAllegedEMT, VERR_INTERNAL_ERROR);
1100
1101 *ppGVM = pGVM;
1102 *ppGVMM = pGVMM;
1103 return VINF_SUCCESS;
1104}
1105
1106
1107/**
1108 * Lookup a GVM structure by the shared VM structure
1109 * and ensuring that the caller is the EMT thread.
1110 *
1111 * @returns VBox status code.
1112 * @param pVM The shared VM structure (the ring-0 mapping).
1113 * @param ppGVM Where to store the GVM pointer.
1114 * @thread EMT
1115 */
1116GVMMR0DECL(int) GVMMR0ByVMAndEMT(PVM pVM, PGVM *ppGVM)
1117{
1118 AssertPtrReturn(ppGVM, VERR_INVALID_POINTER);
1119 PGVMM pGVMM;
1120 return gvmmR0ByVMAndEMT(pVM, ppGVM, &pGVMM);
1121}
1122
1123
1124/**
1125 * Lookup a VM by its global handle.
1126 *
1127 * @returns The VM handle on success, NULL on failure.
1128 * @param hGVM The global VM handle. Asserts on bad handle.
1129 */
1130GVMMR0DECL(PVM) GVMMR0GetVMByHandle(uint32_t hGVM)
1131{
1132 PGVM pGVM = GVMMR0ByHandle(hGVM);
1133 return pGVM ? pGVM->pVM : NULL;
1134}
1135
1136
1137/**
1138 * Looks up the VM belonging to the specified EMT thread.
1139 *
1140 * This is used by the assertion machinery in VMMR0.cpp to avoid causing
1141 * unnecessary kernel panics when the EMT thread hits an assertion. The
1142 * call may or not be an EMT thread.
1143 *
1144 * @returns The VM handle on success, NULL on failure.
1145 * @param hEMT The native thread handle of the EMT.
1146 * NIL_RTNATIVETHREAD means the current thread
1147 */
1148GVMMR0DECL(PVM) GVMMR0GetVMByEMT(RTNATIVETHREAD hEMT)
1149{
1150 /*
1151 * No Assertions here as we're usually called in a AssertMsgN or
1152 * RTAssert* context.
1153 */
1154 PGVMM pGVMM = g_pGVMM;
1155 if ( !VALID_PTR(pGVMM)
1156 || pGVMM->u32Magic != GVMM_MAGIC)
1157 return NULL;
1158
1159 if (hEMT == NIL_RTNATIVETHREAD)
1160 hEMT = RTThreadNativeSelf();
1161
1162 /*
1163 * Search the handles in a linear fashion as we don't dare take the lock (assert).
1164 */
1165 for (unsigned i = 1; i < RT_ELEMENTS(pGVMM->aHandles); i++)
1166 if ( pGVMM->aHandles[i].hEMT == hEMT
1167 && pGVMM->aHandles[i].iSelf == i
1168 && VALID_PTR(pGVMM->aHandles[i].pvObj)
1169 && VALID_PTR(pGVMM->aHandles[i].pVM))
1170 return pGVMM->aHandles[i].pVM;
1171
1172 return NULL;
1173}
1174
1175
1176/**
1177 * This is will wake up expired and soon-to-be expired VMs.
1178 *
1179 * @returns Number of VMs that has been woken up.
1180 * @param pGVMM Pointer to the GVMM instance data.
1181 * @param u64Now The current time.
1182 */
1183static unsigned gvmmR0SchedDoWakeUps(PGVMM pGVMM, uint64_t u64Now)
1184{
1185 /*
1186 * The first pass will wake up VMs which has actually expired
1187 * and look for VMs that should be woken up in the 2nd and 3rd passes.
1188 */
1189 unsigned cWoken = 0;
1190 unsigned cHalted = 0;
1191 unsigned cTodo2nd = 0;
1192 unsigned cTodo3rd = 0;
1193 for (unsigned i = pGVMM->iUsedHead, cGuard = 0;
1194 i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
1195 i = pGVMM->aHandles[i].iNext)
1196 {
1197 PGVM pCurGVM = pGVMM->aHandles[i].pGVM;
1198 if ( VALID_PTR(pCurGVM)
1199 && pCurGVM->u32Magic == GVM_MAGIC)
1200 {
1201 uint64_t u64 = pCurGVM->gvmm.s.u64HaltExpire;
1202 if (u64)
1203 {
1204 if (u64 <= u64Now)
1205 {
1206 if (ASMAtomicXchgU64(&pCurGVM->gvmm.s.u64HaltExpire, 0))
1207 {
1208 int rc = RTSemEventMultiSignal(pCurGVM->gvmm.s.HaltEventMulti);
1209 AssertRC(rc);
1210 cWoken++;
1211 }
1212 }
1213 else
1214 {
1215 cHalted++;
1216 if (u64 <= u64Now + pGVMM->nsEarlyWakeUp1)
1217 cTodo2nd++;
1218 else if (u64 <= u64Now + pGVMM->nsEarlyWakeUp2)
1219 cTodo3rd++;
1220 }
1221 }
1222 }
1223 AssertLogRelBreak(cGuard++ < RT_ELEMENTS(pGVMM->aHandles));
1224 }
1225
1226 if (cTodo2nd)
1227 {
1228 for (unsigned i = pGVMM->iUsedHead, cGuard;
1229 i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
1230 i = pGVMM->aHandles[i].iNext)
1231 {
1232 PGVM pCurGVM = pGVMM->aHandles[i].pGVM;
1233 if ( VALID_PTR(pCurGVM)
1234 && pCurGVM->u32Magic == GVM_MAGIC
1235 && pCurGVM->gvmm.s.u64HaltExpire
1236 && pCurGVM->gvmm.s.u64HaltExpire <= u64Now + pGVMM->nsEarlyWakeUp1)
1237 {
1238 if (ASMAtomicXchgU64(&pCurGVM->gvmm.s.u64HaltExpire, 0))
1239 {
1240 int rc = RTSemEventMultiSignal(pCurGVM->gvmm.s.HaltEventMulti);
1241 AssertRC(rc);
1242 cWoken++;
1243 }
1244 }
1245 AssertLogRelBreak(cGuard++ < RT_ELEMENTS(pGVMM->aHandles));
1246 }
1247 }
1248
1249 if (cTodo3rd)
1250 {
1251 for (unsigned i = pGVMM->iUsedHead, cGuard = 0;
1252 i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
1253 i = pGVMM->aHandles[i].iNext)
1254 {
1255 PGVM pCurGVM = pGVMM->aHandles[i].pGVM;
1256 if ( VALID_PTR(pCurGVM)
1257 && pCurGVM->u32Magic == GVM_MAGIC
1258 && pCurGVM->gvmm.s.u64HaltExpire
1259 && pCurGVM->gvmm.s.u64HaltExpire <= u64Now + pGVMM->nsEarlyWakeUp2)
1260 {
1261 if (ASMAtomicXchgU64(&pCurGVM->gvmm.s.u64HaltExpire, 0))
1262 {
1263 int rc = RTSemEventMultiSignal(pCurGVM->gvmm.s.HaltEventMulti);
1264 AssertRC(rc);
1265 cWoken++;
1266 }
1267 }
1268 AssertLogRelBreak(cGuard++ < RT_ELEMENTS(pGVMM->aHandles));
1269 }
1270 }
1271
1272 return cWoken;
1273}
1274
1275
1276/**
1277 * Halt the EMT thread.
1278 *
1279 * @returns VINF_SUCCESS normal wakeup (timeout or kicked by other thread).
1280 * VERR_INTERRUPTED if a signal was scheduled for the thread.
1281 * @param pVM Pointer to the shared VM structure.
1282 * @param u64ExpireGipTime The time for the sleep to expire expressed as GIP time.
1283 * @thread EMT.
1284 */
1285GVMMR0DECL(int) GVMMR0SchedHalt(PVM pVM, uint64_t u64ExpireGipTime)
1286{
1287 LogFlow(("GVMMR0SchedHalt: pVM=%p\n", pVM));
1288
1289 /*
1290 * Validate the VM structure, state and handle.
1291 */
1292 PGVMM pGVMM;
1293 PGVM pGVM;
1294 int rc = gvmmR0ByVMAndEMT(pVM, &pGVM, &pGVMM);
1295 if (RT_FAILURE(rc))
1296 return rc;
1297 pGVM->gvmm.s.StatsSched.cHaltCalls++;
1298
1299 Assert(!pGVM->gvmm.s.u64HaltExpire);
1300
1301 /*
1302 * Take the UsedList semaphore, get the current time
1303 * and check if anyone needs waking up.
1304 * Interrupts must NOT be disabled at this point because we ask for GIP time!
1305 */
1306 rc = gvmmR0UsedLock(pGVMM);
1307 AssertRC(rc);
1308
1309 pGVM->gvmm.s.iCpuEmt = ASMGetApicId();
1310
1311 Assert(ASMGetFlags() & X86_EFL_IF);
1312 const uint64_t u64Now = RTTimeNanoTS(); /* (GIP time) */
1313 pGVM->gvmm.s.StatsSched.cHaltWakeUps += gvmmR0SchedDoWakeUps(pGVMM, u64Now);
1314
1315 /*
1316 * Go to sleep if we must...
1317 */
1318 if ( u64Now < u64ExpireGipTime
1319 && u64ExpireGipTime - u64Now > (pGVMM->cVMs > pGVMM->cVMsMeansCompany
1320 ? pGVMM->nsMinSleepCompany
1321 : pGVMM->nsMinSleepAlone))
1322 {
1323 pGVM->gvmm.s.StatsSched.cHaltBlocking++;
1324 ASMAtomicXchgU64(&pGVM->gvmm.s.u64HaltExpire, u64ExpireGipTime);
1325 gvmmR0UsedUnlock(pGVMM);
1326
1327 uint32_t cMillies = (u64ExpireGipTime - u64Now) / 1000000;
1328 rc = RTSemEventMultiWaitNoResume(pGVM->gvmm.s.HaltEventMulti, cMillies ? cMillies : 1);
1329 ASMAtomicXchgU64(&pGVM->gvmm.s.u64HaltExpire, 0);
1330 if (rc == VERR_TIMEOUT)
1331 {
1332 pGVM->gvmm.s.StatsSched.cHaltTimeouts++;
1333 rc = VINF_SUCCESS;
1334 }
1335 }
1336 else
1337 {
1338 pGVM->gvmm.s.StatsSched.cHaltNotBlocking++;
1339 gvmmR0UsedUnlock(pGVMM);
1340 }
1341
1342 /* Make sure false wake up calls (gvmmR0SchedDoWakeUps) cause us to spin. */
1343 RTSemEventMultiReset(pGVM->gvmm.s.HaltEventMulti);
1344
1345 return rc;
1346}
1347
1348
1349/**
1350 * Wakes up the halted EMT thread so it can service a pending request.
1351 *
1352 * @returns VINF_SUCCESS if not yielded.
1353 * VINF_GVM_NOT_BLOCKED if the EMT thread wasn't blocked.
1354 * @param pVM Pointer to the shared VM structure.
1355 * @thread Any but EMT.
1356 */
1357GVMMR0DECL(int) GVMMR0SchedWakeUp(PVM pVM)
1358{
1359 /*
1360 * Validate input and take the UsedLock.
1361 */
1362 PGVM pGVM;
1363 PGVMM pGVMM;
1364 int rc = gvmmR0ByVM(pVM, &pGVM, &pGVMM, true /* fTakeUsedLock */);
1365 if (RT_SUCCESS(rc))
1366 {
1367 pGVM->gvmm.s.StatsSched.cWakeUpCalls++;
1368
1369 /*
1370 * Signal the semaphore regardless of whether it's current blocked on it.
1371 *
1372 * The reason for this is that there is absolutely no way we can be 100%
1373 * certain that it isn't *about* go to go to sleep on it and just got
1374 * delayed a bit en route. So, we will always signal the semaphore when
1375 * the it is flagged as halted in the VMM.
1376 */
1377 if (pGVM->gvmm.s.u64HaltExpire)
1378 {
1379 rc = VINF_SUCCESS;
1380 ASMAtomicXchgU64(&pGVM->gvmm.s.u64HaltExpire, 0);
1381 }
1382 else
1383 {
1384 rc = VINF_GVM_NOT_BLOCKED;
1385 pGVM->gvmm.s.StatsSched.cWakeUpNotHalted++;
1386 }
1387
1388 int rc2 = RTSemEventMultiSignal(pGVM->gvmm.s.HaltEventMulti);
1389 AssertRC(rc2);
1390
1391 /*
1392 * While we're here, do a round of scheduling.
1393 */
1394 Assert(ASMGetFlags() & X86_EFL_IF);
1395 const uint64_t u64Now = RTTimeNanoTS(); /* (GIP time) */
1396 pGVM->gvmm.s.StatsSched.cWakeUpWakeUps += gvmmR0SchedDoWakeUps(pGVMM, u64Now);
1397
1398
1399 rc2 = gvmmR0UsedUnlock(pGVMM);
1400 AssertRC(rc2);
1401 }
1402
1403 LogFlow(("GVMMR0SchedWakeUp: returns %Rrc\n", rc));
1404 return rc;
1405}
1406
1407
1408/**
1409 * Poll the schedule to see if someone else should get a chance to run.
1410 *
1411 * This is a bit hackish and will not work too well if the machine is
1412 * under heavy load from non-VM processes.
1413 *
1414 * @returns VINF_SUCCESS if not yielded.
1415 * VINF_GVM_YIELDED if an attempt to switch to a different VM task was made.
1416 * @param pVM Pointer to the shared VM structure.
1417 * @param u64ExpireGipTime The time for the sleep to expire expressed as GIP time.
1418 * @param fYield Whether to yield or not.
1419 * This is for when we're spinning in the halt loop.
1420 * @thread EMT.
1421 */
1422GVMMR0DECL(int) GVMMR0SchedPoll(PVM pVM, bool fYield)
1423{
1424 /*
1425 * Validate input.
1426 */
1427 PGVM pGVM;
1428 PGVMM pGVMM;
1429 int rc = gvmmR0ByVMAndEMT(pVM, &pGVM, &pGVMM);
1430 if (RT_SUCCESS(rc))
1431 {
1432 rc = gvmmR0UsedLock(pGVMM);
1433 AssertRC(rc);
1434 pGVM->gvmm.s.StatsSched.cPollCalls++;
1435
1436 Assert(ASMGetFlags() & X86_EFL_IF);
1437 const uint64_t u64Now = RTTimeNanoTS(); /* (GIP time) */
1438
1439 if (!fYield)
1440 pGVM->gvmm.s.StatsSched.cPollWakeUps += gvmmR0SchedDoWakeUps(pGVMM, u64Now);
1441 else
1442 {
1443 /** @todo implement this... */
1444 rc = VERR_NOT_IMPLEMENTED;
1445 }
1446
1447 gvmmR0UsedUnlock(pGVMM);
1448 }
1449
1450 LogFlow(("GVMMR0SchedWakeUp: returns %Rrc\n", rc));
1451 return rc;
1452}
1453
1454
1455
1456/**
1457 * Retrieves the GVMM statistics visible to the caller.
1458 *
1459 * @returns VBox status code.
1460 *
1461 * @param pStats Where to put the statistics.
1462 * @param pSession The current session.
1463 * @param pVM The VM to obtain statistics for. Optional.
1464 */
1465GVMMR0DECL(int) GVMMR0QueryStatistics(PGVMMSTATS pStats, PSUPDRVSESSION pSession, PVM pVM)
1466{
1467 LogFlow(("GVMMR0QueryStatistics: pStats=%p pSession=%p pVM=%p\n", pStats, pSession, pVM));
1468
1469 /*
1470 * Validate input.
1471 */
1472 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1473 AssertPtrReturn(pStats, VERR_INVALID_POINTER);
1474 pStats->cVMs = 0; /* (crash before taking the sem...) */
1475
1476 /*
1477 * Take the lock and get the VM statistics.
1478 */
1479 PGVMM pGVMM;
1480 if (pVM)
1481 {
1482 PGVM pGVM;
1483 int rc = gvmmR0ByVM(pVM, &pGVM, &pGVMM, true /*fTakeUsedLock*/);
1484 if (RT_FAILURE(rc))
1485 return rc;
1486 pStats->SchedVM = pGVM->gvmm.s.StatsSched;
1487 }
1488 else
1489 {
1490 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
1491 memset(&pStats->SchedVM, 0, sizeof(pStats->SchedVM));
1492
1493 int rc = gvmmR0UsedLock(pGVMM);
1494 AssertRCReturn(rc, rc);
1495 }
1496
1497 /*
1498 * Enumerate the VMs and add the ones visibile to the statistics.
1499 */
1500 pStats->cVMs = 0;
1501 memset(&pStats->SchedSum, 0, sizeof(pStats->SchedSum));
1502
1503 for (unsigned i = pGVMM->iUsedHead;
1504 i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
1505 i = pGVMM->aHandles[i].iNext)
1506 {
1507 PGVM pGVM = pGVMM->aHandles[i].pGVM;
1508 void *pvObj = pGVMM->aHandles[i].pvObj;
1509 if ( VALID_PTR(pvObj)
1510 && VALID_PTR(pGVM)
1511 && pGVM->u32Magic == GVM_MAGIC
1512 && RT_SUCCESS(SUPR0ObjVerifyAccess(pvObj, pSession, NULL)))
1513 {
1514 pStats->cVMs++;
1515
1516 pStats->SchedSum.cHaltCalls += pGVM->gvmm.s.StatsSched.cHaltCalls;
1517 pStats->SchedSum.cHaltBlocking += pGVM->gvmm.s.StatsSched.cHaltBlocking;
1518 pStats->SchedSum.cHaltTimeouts += pGVM->gvmm.s.StatsSched.cHaltTimeouts;
1519 pStats->SchedSum.cHaltNotBlocking += pGVM->gvmm.s.StatsSched.cHaltNotBlocking;
1520 pStats->SchedSum.cHaltWakeUps += pGVM->gvmm.s.StatsSched.cHaltWakeUps;
1521
1522 pStats->SchedSum.cWakeUpCalls += pGVM->gvmm.s.StatsSched.cWakeUpCalls;
1523 pStats->SchedSum.cWakeUpNotHalted += pGVM->gvmm.s.StatsSched.cWakeUpNotHalted;
1524 pStats->SchedSum.cWakeUpWakeUps += pGVM->gvmm.s.StatsSched.cWakeUpWakeUps;
1525
1526 pStats->SchedSum.cPollCalls += pGVM->gvmm.s.StatsSched.cPollCalls;
1527 pStats->SchedSum.cPollHalts += pGVM->gvmm.s.StatsSched.cPollHalts;
1528 pStats->SchedSum.cPollWakeUps += pGVM->gvmm.s.StatsSched.cPollWakeUps;
1529 }
1530 }
1531
1532 gvmmR0UsedUnlock(pGVMM);
1533
1534 return VINF_SUCCESS;
1535}
1536
1537
1538/**
1539 * VMMR0 request wrapper for GVMMR0QueryStatistics.
1540 *
1541 * @returns see GVMMR0QueryStatistics.
1542 * @param pVM Pointer to the shared VM structure. Optional.
1543 * @param pReq The request packet.
1544 */
1545GVMMR0DECL(int) GVMMR0QueryStatisticsReq(PVM pVM, PGVMMQUERYSTATISTICSSREQ pReq)
1546{
1547 /*
1548 * Validate input and pass it on.
1549 */
1550 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
1551 AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
1552
1553 return GVMMR0QueryStatistics(&pReq->Stats, pReq->pSession, pVM);
1554}
1555
1556
1557/**
1558 * Resets the specified GVMM statistics.
1559 *
1560 * @returns VBox status code.
1561 *
1562 * @param pStats Which statistics to reset, that is, non-zero fields indicates which to reset.
1563 * @param pSession The current session.
1564 * @param pVM The VM to reset statistics for. Optional.
1565 */
1566GVMMR0DECL(int) GVMMR0ResetStatistics(PCGVMMSTATS pStats, PSUPDRVSESSION pSession, PVM pVM)
1567{
1568 LogFlow(("GVMMR0ResetStatistics: pStats=%p pSession=%p pVM=%p\n", pStats, pSession, pVM));
1569
1570 /*
1571 * Validate input.
1572 */
1573 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1574 AssertPtrReturn(pStats, VERR_INVALID_POINTER);
1575
1576 /*
1577 * Take the lock and get the VM statistics.
1578 */
1579 PGVMM pGVMM;
1580 if (pVM)
1581 {
1582 PGVM pGVM;
1583 int rc = gvmmR0ByVM(pVM, &pGVM, &pGVMM, true /*fTakeUsedLock*/);
1584 if (RT_FAILURE(rc))
1585 return rc;
1586# define MAYBE_RESET_FIELD(field) \
1587 do { if (pStats->SchedVM. field ) { pGVM->gvmm.s.StatsSched. field = 0; } } while (0)
1588 MAYBE_RESET_FIELD(cHaltCalls);
1589 MAYBE_RESET_FIELD(cHaltBlocking);
1590 MAYBE_RESET_FIELD(cHaltTimeouts);
1591 MAYBE_RESET_FIELD(cHaltNotBlocking);
1592 MAYBE_RESET_FIELD(cHaltWakeUps);
1593 MAYBE_RESET_FIELD(cWakeUpCalls);
1594 MAYBE_RESET_FIELD(cWakeUpNotHalted);
1595 MAYBE_RESET_FIELD(cWakeUpWakeUps);
1596 MAYBE_RESET_FIELD(cPollCalls);
1597 MAYBE_RESET_FIELD(cPollHalts);
1598 MAYBE_RESET_FIELD(cPollWakeUps);
1599# undef MAYBE_RESET_FIELD
1600 }
1601 else
1602 {
1603 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
1604
1605 int rc = gvmmR0UsedLock(pGVMM);
1606 AssertRCReturn(rc, rc);
1607 }
1608
1609 /*
1610 * Enumerate the VMs and add the ones visibile to the statistics.
1611 */
1612 if (ASMMemIsAll8(&pStats->SchedSum, sizeof(pStats->SchedSum), 0))
1613 {
1614 for (unsigned i = pGVMM->iUsedHead;
1615 i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
1616 i = pGVMM->aHandles[i].iNext)
1617 {
1618 PGVM pGVM = pGVMM->aHandles[i].pGVM;
1619 void *pvObj = pGVMM->aHandles[i].pvObj;
1620 if ( VALID_PTR(pvObj)
1621 && VALID_PTR(pGVM)
1622 && pGVM->u32Magic == GVM_MAGIC
1623 && RT_SUCCESS(SUPR0ObjVerifyAccess(pvObj, pSession, NULL)))
1624 {
1625# define MAYBE_RESET_FIELD(field) \
1626 do { if (pStats->SchedSum. field ) { pGVM->gvmm.s.StatsSched. field = 0; } } while (0)
1627 MAYBE_RESET_FIELD(cHaltCalls);
1628 MAYBE_RESET_FIELD(cHaltBlocking);
1629 MAYBE_RESET_FIELD(cHaltTimeouts);
1630 MAYBE_RESET_FIELD(cHaltNotBlocking);
1631 MAYBE_RESET_FIELD(cHaltWakeUps);
1632 MAYBE_RESET_FIELD(cWakeUpCalls);
1633 MAYBE_RESET_FIELD(cWakeUpNotHalted);
1634 MAYBE_RESET_FIELD(cWakeUpWakeUps);
1635 MAYBE_RESET_FIELD(cPollCalls);
1636 MAYBE_RESET_FIELD(cPollHalts);
1637 MAYBE_RESET_FIELD(cPollWakeUps);
1638# undef MAYBE_RESET_FIELD
1639 }
1640 }
1641 }
1642
1643 gvmmR0UsedUnlock(pGVMM);
1644
1645 return VINF_SUCCESS;
1646}
1647
1648
1649/**
1650 * VMMR0 request wrapper for GVMMR0ResetStatistics.
1651 *
1652 * @returns see GVMMR0ResetStatistics.
1653 * @param pVM Pointer to the shared VM structure. Optional.
1654 * @param pReq The request packet.
1655 */
1656GVMMR0DECL(int) GVMMR0ResetStatisticsReq(PVM pVM, PGVMMRESETSTATISTICSSREQ pReq)
1657{
1658 /*
1659 * Validate input and pass it on.
1660 */
1661 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
1662 AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
1663
1664 return GVMMR0ResetStatistics(&pReq->Stats, pReq->pSession, pVM);
1665}
1666
Note: See TracBrowser for help on using the repository browser.

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