VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/SUPDRVShared.c@ 4985

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

GVM.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 137.8 KB
Line 
1/* $Revision: 4971 $ */
2/** @file
3 * VirtualBox Support Driver - Shared code.
4 */
5
6/*
7 * Copyright (C) 2006-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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include "SUPDRV.h"
23#ifndef PAGE_SHIFT
24# include <iprt/param.h>
25#endif
26#include <iprt/alloc.h>
27#include <iprt/semaphore.h>
28#include <iprt/spinlock.h>
29#include <iprt/thread.h>
30#include <iprt/process.h>
31#include <iprt/log.h>
32
33
34/*******************************************************************************
35* Defined Constants And Macros *
36*******************************************************************************/
37/* from x86.h - clashes with linux thus this duplication */
38#undef X86_CR0_PG
39#define X86_CR0_PG BIT(31)
40#undef X86_CR0_PE
41#define X86_CR0_PE BIT(0)
42#undef X86_CPUID_AMD_FEATURE_EDX_NX
43#define X86_CPUID_AMD_FEATURE_EDX_NX BIT(20)
44#undef MSR_K6_EFER
45#define MSR_K6_EFER 0xc0000080
46#undef MSR_K6_EFER_NXE
47#define MSR_K6_EFER_NXE BIT(11)
48#undef MSR_K6_EFER_LMA
49#define MSR_K6_EFER_LMA BIT(10)
50#undef X86_CR4_PGE
51#define X86_CR4_PGE BIT(7)
52#undef X86_CR4_PAE
53#define X86_CR4_PAE BIT(5)
54#undef X86_CPUID_AMD_FEATURE_EDX_LONG_MODE
55#define X86_CPUID_AMD_FEATURE_EDX_LONG_MODE BIT(29)
56
57
58/** The frequency by which we recalculate the u32UpdateHz and
59 * u32UpdateIntervalNS GIP members. The value must be a power of 2. */
60#define GIP_UPDATEHZ_RECALC_FREQ 0x800
61
62/**
63 * Validates a session pointer.
64 *
65 * @returns true/false accordingly.
66 * @param pSession The session.
67 */
68#define SUP_IS_SESSION_VALID(pSession) \
69 ( VALID_PTR(pSession) \
70 && pSession->u32Cookie == BIRD_INV)
71
72
73/*******************************************************************************
74* Global Variables *
75*******************************************************************************/
76/**
77 * Array of the R0 SUP API.
78 */
79static SUPFUNC g_aFunctions[] =
80{
81 /* name function */
82 { "SUPR0ObjRegister", (void *)SUPR0ObjRegister },
83 { "SUPR0ObjAddRef", (void *)SUPR0ObjAddRef },
84 { "SUPR0ObjRelease", (void *)SUPR0ObjRelease },
85 { "SUPR0ObjVerifyAccess", (void *)SUPR0ObjVerifyAccess },
86 { "SUPR0LockMem", (void *)SUPR0LockMem },
87 { "SUPR0UnlockMem", (void *)SUPR0UnlockMem },
88 { "SUPR0ContAlloc", (void *)SUPR0ContAlloc },
89 { "SUPR0ContFree", (void *)SUPR0ContFree },
90 { "SUPR0LowAlloc", (void *)SUPR0LowAlloc },
91 { "SUPR0LowFree", (void *)SUPR0LowFree },
92 { "SUPR0MemAlloc", (void *)SUPR0MemAlloc },
93 { "SUPR0MemGetPhys", (void *)SUPR0MemGetPhys },
94 { "SUPR0MemFree", (void *)SUPR0MemFree },
95 { "SUPR0PageAlloc", (void *)SUPR0PageAlloc },
96 { "SUPR0PageFree", (void *)SUPR0PageFree },
97 { "SUPR0Printf", (void *)SUPR0Printf },
98 { "RTMemAlloc", (void *)RTMemAlloc },
99 { "RTMemAllocZ", (void *)RTMemAllocZ },
100 { "RTMemFree", (void *)RTMemFree },
101/* These doesn't work yet on linux - use fast mutexes!
102 { "RTSemMutexCreate", (void *)RTSemMutexCreate },
103 { "RTSemMutexRequest", (void *)RTSemMutexRequest },
104 { "RTSemMutexRelease", (void *)RTSemMutexRelease },
105 { "RTSemMutexDestroy", (void *)RTSemMutexDestroy },
106*/
107 { "RTSemFastMutexCreate", (void *)RTSemFastMutexCreate },
108 { "RTSemFastMutexDestroy", (void *)RTSemFastMutexDestroy },
109 { "RTSemFastMutexRequest", (void *)RTSemFastMutexRequest },
110 { "RTSemFastMutexRelease", (void *)RTSemFastMutexRelease },
111 { "RTSemEventCreate", (void *)RTSemEventCreate },
112 { "RTSemEventSignal", (void *)RTSemEventSignal },
113 { "RTSemEventWait", (void *)RTSemEventWait },
114 { "RTSemEventDestroy", (void *)RTSemEventDestroy },
115 { "RTSpinlockCreate", (void *)RTSpinlockCreate },
116 { "RTSpinlockDestroy", (void *)RTSpinlockDestroy },
117 { "RTSpinlockAcquire", (void *)RTSpinlockAcquire },
118 { "RTSpinlockRelease", (void *)RTSpinlockRelease },
119 { "RTSpinlockAcquireNoInts", (void *)RTSpinlockAcquireNoInts },
120 { "RTSpinlockReleaseNoInts", (void *)RTSpinlockReleaseNoInts },
121 { "RTThreadNativeSelf", (void *)RTThreadNativeSelf },
122 { "RTThreadSleep", (void *)RTThreadSleep },
123 { "RTThreadYield", (void *)RTThreadYield },
124#if 0 /* Thread APIs, Part 2. */
125 { "RTThreadSelf", (void *)RTThreadSelf },
126 { "RTThreadCreate", (void *)RTThreadCreate },
127 { "RTThreadGetNative", (void *)RTThreadGetNative },
128 { "RTThreadWait", (void *)RTThreadWait },
129 { "RTThreadWaitNoResume", (void *)RTThreadWaitNoResume },
130 { "RTThreadGetName", (void *)RTThreadGetName },
131 { "RTThreadSelfName", (void *)RTThreadSelfName },
132 { "RTThreadGetType", (void *)RTThreadGetType },
133 { "RTThreadUserSignal", (void *)RTThreadUserSignal },
134 { "RTThreadUserReset", (void *)RTThreadUserReset },
135 { "RTThreadUserWait", (void *)RTThreadUserWait },
136 { "RTThreadUserWaitNoResume", (void *)RTThreadUserWaitNoResume },
137#endif
138 { "RTLogDefaultInstance", (void *)RTLogDefaultInstance },
139 { "RTLogRelDefaultInstance", (void *)RTLogRelDefaultInstance },
140 { "RTLogSetDefaultInstanceThread", (void *)RTLogSetDefaultInstanceThread },
141 { "RTLogLogger", (void *)RTLogLogger },
142 { "RTLogLoggerEx", (void *)RTLogLoggerEx },
143 { "RTLogLoggerExV", (void *)RTLogLoggerExV },
144 { "RTLogPrintf", (void *)RTLogPrintf },
145 { "RTLogPrintfV", (void *)RTLogPrintfV },
146 { "AssertMsg1", (void *)AssertMsg1 },
147 { "AssertMsg2", (void *)AssertMsg2 },
148};
149
150
151/*******************************************************************************
152* Internal Functions *
153*******************************************************************************/
154static int supdrvMemAdd(PSUPDRVMEMREF pMem, PSUPDRVSESSION pSession);
155static int supdrvMemRelease(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr, SUPDRVMEMREFTYPE eType);
156#ifdef VBOX_WITH_IDT_PATCHING
157static int supdrvIOCtl_IdtInstall(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPIDTINSTALL pReq);
158static PSUPDRVPATCH supdrvIdtPatchOne(PSUPDRVDEVEXT pDevExt, PSUPDRVPATCH pPatch);
159static int supdrvIOCtl_IdtRemoveAll(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession);
160static void supdrvIdtRemoveOne(PSUPDRVDEVEXT pDevExt, PSUPDRVPATCH pPatch);
161static void supdrvIdtWrite(volatile void *pvIdtEntry, const SUPDRVIDTE *pNewIDTEntry);
162#endif /* VBOX_WITH_IDT_PATCHING */
163static int supdrvIOCtl_LdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDROPEN pReq);
164static int supdrvIOCtl_LdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRLOAD pReq);
165static int supdrvIOCtl_LdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRFREE pReq);
166static int supdrvIOCtl_LdrGetSymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRGETSYMBOL pReq);
167static int supdrvLdrSetR0EP(PSUPDRVDEVEXT pDevExt, void *pvVMMR0, void *pvVMMR0EntryInt, void *pvVMMR0EntryFast, void *pvVMMR0EntryEx);
168static void supdrvLdrUnsetR0EP(PSUPDRVDEVEXT pDevExt);
169static void supdrvLdrAddUsage(PSUPDRVSESSION pSession, PSUPDRVLDRIMAGE pImage);
170static void supdrvLdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage);
171static SUPPAGINGMODE supdrvIOCtl_GetPagingMode(void);
172static SUPGIPMODE supdrvGipDeterminTscMode(void);
173#ifdef RT_OS_WINDOWS
174static int supdrvPageGetPhys(PSUPDRVSESSION pSession, RTR3PTR pvR3, uint32_t cPages, PRTHCPHYS paPages);
175static bool supdrvPageWasLockedByPageAlloc(PSUPDRVSESSION pSession, RTR3PTR pvR3);
176#endif
177#ifdef USE_NEW_OS_INTERFACE_FOR_GIP
178static int supdrvGipCreate(PSUPDRVDEVEXT pDevExt);
179static void supdrvGipDestroy(PSUPDRVDEVEXT pDevExt);
180static DECLCALLBACK(void) supdrvGipTimer(PRTTIMER pTimer, void *pvUser);
181#endif
182
183
184/**
185 * Initializes the device extentsion structure.
186 *
187 * @returns IPRT status code.
188 * @param pDevExt The device extension to initialize.
189 */
190int VBOXCALL supdrvInitDevExt(PSUPDRVDEVEXT pDevExt)
191{
192 /*
193 * Initialize it.
194 */
195 int rc;
196 memset(pDevExt, 0, sizeof(*pDevExt));
197 rc = RTSpinlockCreate(&pDevExt->Spinlock);
198 if (!rc)
199 {
200 rc = RTSemFastMutexCreate(&pDevExt->mtxLdr);
201 if (!rc)
202 {
203 rc = RTSemFastMutexCreate(&pDevExt->mtxGip);
204 if (!rc)
205 {
206#ifdef USE_NEW_OS_INTERFACE_FOR_GIP
207 rc = supdrvGipCreate(pDevExt);
208 if (RT_SUCCESS(rc))
209 {
210 pDevExt->u32Cookie = BIRD; /** @todo make this random? */
211 return VINF_SUCCESS;
212 }
213#else
214 pDevExt->u32Cookie = BIRD;
215 return VINF_SUCCESS;
216#endif
217 }
218 RTSemFastMutexDestroy(pDevExt->mtxLdr);
219 pDevExt->mtxLdr = NIL_RTSEMFASTMUTEX;
220 }
221 RTSpinlockDestroy(pDevExt->Spinlock);
222 pDevExt->Spinlock = NIL_RTSPINLOCK;
223 }
224 return rc;
225}
226
227
228/**
229 * Delete the device extension (e.g. cleanup members).
230 *
231 * @param pDevExt The device extension to delete.
232 */
233void VBOXCALL supdrvDeleteDevExt(PSUPDRVDEVEXT pDevExt)
234{
235#ifdef VBOX_WITH_IDT_PATCHING
236 PSUPDRVPATCH pPatch;
237#endif
238 PSUPDRVOBJ pObj;
239 PSUPDRVUSAGE pUsage;
240
241 /*
242 * Kill mutexes and spinlocks.
243 */
244 RTSemFastMutexDestroy(pDevExt->mtxGip);
245 pDevExt->mtxGip = NIL_RTSEMFASTMUTEX;
246 RTSemFastMutexDestroy(pDevExt->mtxLdr);
247 pDevExt->mtxLdr = NIL_RTSEMFASTMUTEX;
248 RTSpinlockDestroy(pDevExt->Spinlock);
249 pDevExt->Spinlock = NIL_RTSPINLOCK;
250
251 /*
252 * Free lists.
253 */
254#ifdef VBOX_WITH_IDT_PATCHING
255 /* patches */
256 /** @todo make sure we don't uninstall patches which has been patched by someone else. */
257 pPatch = pDevExt->pIdtPatchesFree;
258 pDevExt->pIdtPatchesFree = NULL;
259 while (pPatch)
260 {
261 void *pvFree = pPatch;
262 pPatch = pPatch->pNext;
263 RTMemExecFree(pvFree);
264 }
265#endif /* VBOX_WITH_IDT_PATCHING */
266
267 /* objects. */
268 pObj = pDevExt->pObjs;
269#if !defined(DEBUG_bird) || !defined(RT_OS_LINUX) /* breaks unloading, temporary, remove me! */
270 Assert(!pObj); /* (can trigger on forced unloads) */
271#endif
272 pDevExt->pObjs = NULL;
273 while (pObj)
274 {
275 void *pvFree = pObj;
276 pObj = pObj->pNext;
277 RTMemFree(pvFree);
278 }
279
280 /* usage records. */
281 pUsage = pDevExt->pUsageFree;
282 pDevExt->pUsageFree = NULL;
283 while (pUsage)
284 {
285 void *pvFree = pUsage;
286 pUsage = pUsage->pNext;
287 RTMemFree(pvFree);
288 }
289
290#ifdef USE_NEW_OS_INTERFACE_FOR_GIP
291 /* kill the GIP */
292 supdrvGipDestroy(pDevExt);
293#endif
294}
295
296
297/**
298 * Create session.
299 *
300 * @returns IPRT status code.
301 * @param pDevExt Device extension.
302 * @param ppSession Where to store the pointer to the session data.
303 */
304int VBOXCALL supdrvCreateSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION *ppSession)
305{
306 /*
307 * Allocate memory for the session data.
308 */
309 int rc = VERR_NO_MEMORY;
310 PSUPDRVSESSION pSession = *ppSession = (PSUPDRVSESSION)RTMemAllocZ(sizeof(*pSession));
311 if (pSession)
312 {
313 /* Initialize session data. */
314 rc = RTSpinlockCreate(&pSession->Spinlock);
315 if (!rc)
316 {
317 Assert(pSession->Spinlock != NIL_RTSPINLOCK);
318 pSession->pDevExt = pDevExt;
319 pSession->u32Cookie = BIRD_INV;
320 /*pSession->pLdrUsage = NULL;
321 pSession->pPatchUsage = NULL;
322 pSession->pUsage = NULL;
323 pSession->pGip = NULL;
324 pSession->fGipReferenced = false;
325 pSession->Bundle.cUsed = 0 */
326
327 dprintf(("Created session %p initial cookie=%#x\n", pSession, pSession->u32Cookie));
328 return VINF_SUCCESS;
329 }
330
331 RTMemFree(pSession);
332 *ppSession = NULL;
333 }
334
335 dprintf(("Failed to create spinlock, rc=%d!\n", rc));
336 return rc;
337}
338
339
340/**
341 * Shared code for cleaning up a session.
342 *
343 * @param pDevExt Device extension.
344 * @param pSession Session data.
345 * This data will be freed by this routine.
346 */
347void VBOXCALL supdrvCloseSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
348{
349 /*
350 * Cleanup the session first.
351 */
352 supdrvCleanupSession(pDevExt, pSession);
353
354 /*
355 * Free the rest of the session stuff.
356 */
357 RTSpinlockDestroy(pSession->Spinlock);
358 pSession->Spinlock = NIL_RTSPINLOCK;
359 pSession->pDevExt = NULL;
360 RTMemFree(pSession);
361 dprintf2(("supdrvCloseSession: returns\n"));
362}
363
364
365/**
366 * Shared code for cleaning up a session (but not quite freeing it).
367 *
368 * This is primarily intended for MAC OS X where we have to clean up the memory
369 * stuff before the file handle is closed.
370 *
371 * @param pDevExt Device extension.
372 * @param pSession Session data.
373 * This data will be freed by this routine.
374 */
375void VBOXCALL supdrvCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
376{
377 PSUPDRVBUNDLE pBundle;
378 dprintf(("supdrvCleanupSession: pSession=%p\n", pSession));
379
380 /*
381 * Remove logger instances related to this session.
382 * (This assumes the dprintf and dprintf2 macros doesn't use the normal logging.)
383 */
384 RTLogSetDefaultInstanceThread(NULL, (uintptr_t)pSession);
385
386#ifdef VBOX_WITH_IDT_PATCHING
387 /*
388 * Uninstall any IDT patches installed for this session.
389 */
390 supdrvIOCtl_IdtRemoveAll(pDevExt, pSession);
391#endif
392
393 /*
394 * Release object references made in this session.
395 * In theory there should be noone racing us in this session.
396 */
397 dprintf2(("release objects - start\n"));
398 if (pSession->pUsage)
399 {
400 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
401 PSUPDRVUSAGE pUsage;
402 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
403
404 while ((pUsage = pSession->pUsage) != NULL)
405 {
406 PSUPDRVOBJ pObj = pUsage->pObj;
407 pSession->pUsage = pUsage->pNext;
408
409 AssertMsg(pUsage->cUsage >= 1 && pObj->cUsage >= pUsage->cUsage, ("glob %d; sess %d\n", pObj->cUsage, pUsage->cUsage));
410 if (pUsage->cUsage < pObj->cUsage)
411 {
412 pObj->cUsage -= pUsage->cUsage;
413 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
414 }
415 else
416 {
417 /* Destroy the object and free the record. */
418 if (pDevExt->pObjs == pObj)
419 pDevExt->pObjs = pObj->pNext;
420 else
421 {
422 PSUPDRVOBJ pObjPrev;
423 for (pObjPrev = pDevExt->pObjs; pObjPrev; pObjPrev = pObjPrev->pNext)
424 if (pObjPrev->pNext == pObj)
425 {
426 pObjPrev->pNext = pObj->pNext;
427 break;
428 }
429 Assert(pObjPrev);
430 }
431 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
432
433 if (pObj->pfnDestructor)
434 pObj->pfnDestructor(pObj, pObj->pvUser1, pObj->pvUser2);
435 RTMemFree(pObj);
436 }
437
438 /* free it and continue. */
439 RTMemFree(pUsage);
440
441 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
442 }
443
444 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
445 AssertMsg(!pSession->pUsage, ("Some buster reregistered an object during desturction!\n"));
446 }
447 dprintf2(("release objects - done\n"));
448
449 /*
450 * Release memory allocated in the session.
451 *
452 * We do not serialize this as we assume that the application will
453 * not allocated memory while closing the file handle object.
454 */
455 dprintf2(("freeing memory:\n"));
456 pBundle = &pSession->Bundle;
457 while (pBundle)
458 {
459 PSUPDRVBUNDLE pToFree;
460 unsigned i;
461
462 /*
463 * Check and unlock all entries in the bundle.
464 */
465 for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
466 {
467 if (pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ)
468 {
469 int rc;
470 dprintf2(("eType=%d pvR0=%p pvR3=%p cb=%ld\n", pBundle->aMem[i].eType, RTR0MemObjAddress(pBundle->aMem[i].MemObj),
471 (void *)RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3), (long)RTR0MemObjSize(pBundle->aMem[i].MemObj)));
472 if (pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ)
473 {
474 rc = RTR0MemObjFree(pBundle->aMem[i].MapObjR3, false);
475 AssertRC(rc); /** @todo figure out how to handle this. */
476 pBundle->aMem[i].MapObjR3 = NIL_RTR0MEMOBJ;
477 }
478 rc = RTR0MemObjFree(pBundle->aMem[i].MemObj, false);
479 AssertRC(rc); /** @todo figure out how to handle this. */
480 pBundle->aMem[i].MemObj = NIL_RTR0MEMOBJ;
481 pBundle->aMem[i].eType = MEMREF_TYPE_UNUSED;
482 }
483 }
484
485 /*
486 * Advance and free previous bundle.
487 */
488 pToFree = pBundle;
489 pBundle = pBundle->pNext;
490
491 pToFree->pNext = NULL;
492 pToFree->cUsed = 0;
493 if (pToFree != &pSession->Bundle)
494 RTMemFree(pToFree);
495 }
496 dprintf2(("freeing memory - done\n"));
497
498 /*
499 * Loaded images needs to be dereferenced and possibly freed up.
500 */
501 RTSemFastMutexRequest(pDevExt->mtxLdr);
502 dprintf2(("freeing images:\n"));
503 if (pSession->pLdrUsage)
504 {
505 PSUPDRVLDRUSAGE pUsage = pSession->pLdrUsage;
506 pSession->pLdrUsage = NULL;
507 while (pUsage)
508 {
509 void *pvFree = pUsage;
510 PSUPDRVLDRIMAGE pImage = pUsage->pImage;
511 if (pImage->cUsage > pUsage->cUsage)
512 pImage->cUsage -= pUsage->cUsage;
513 else
514 supdrvLdrFree(pDevExt, pImage);
515 pUsage->pImage = NULL;
516 pUsage = pUsage->pNext;
517 RTMemFree(pvFree);
518 }
519 }
520 RTSemFastMutexRelease(pDevExt->mtxLdr);
521 dprintf2(("freeing images - done\n"));
522
523 /*
524 * Unmap the GIP.
525 */
526 dprintf2(("umapping GIP:\n"));
527#ifdef USE_NEW_OS_INTERFACE_FOR_GIP
528 if (pSession->GipMapObjR3 != NIL_RTR0MEMOBJ)
529#else
530 if (pSession->pGip)
531#endif
532 {
533 SUPR0GipUnmap(pSession);
534#ifndef USE_NEW_OS_INTERFACE_FOR_GIP
535 pSession->pGip = NULL;
536#endif
537 pSession->fGipReferenced = 0;
538 }
539 dprintf2(("umapping GIP - done\n"));
540}
541
542
543/**
544 * Fast path I/O Control worker.
545 *
546 * @returns VBox status code that should be passed down to ring-3 unchanged.
547 * @param uIOCtl Function number.
548 * @param pDevExt Device extention.
549 * @param pSession Session data.
550 */
551int VBOXCALL supdrvIOCtlFast(uintptr_t uIOCtl, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
552{
553 int rc;
554
555 /*
556 * We check the two prereqs after doing this only to allow the compiler to optimize things better.
557 */
558 if (RT_LIKELY(pSession->pVM && pDevExt->pfnVMMR0EntryFast))
559 {
560 switch (uIOCtl)
561 {
562 case SUP_IOCTL_FAST_DO_RAW_RUN:
563 rc = pDevExt->pfnVMMR0EntryFast(pSession->pVM, SUP_VMMR0_DO_RAW_RUN);
564 break;
565 case SUP_IOCTL_FAST_DO_HWACC_RUN:
566 rc = pDevExt->pfnVMMR0EntryFast(pSession->pVM, SUP_VMMR0_DO_HWACC_RUN);
567 break;
568 case SUP_IOCTL_FAST_DO_NOP:
569 rc = pDevExt->pfnVMMR0EntryFast(pSession->pVM, SUP_VMMR0_DO_NOP);
570 break;
571 default:
572 rc = VERR_INTERNAL_ERROR;
573 break;
574 }
575 }
576 else
577 rc = VERR_INTERNAL_ERROR;
578
579 return rc;
580}
581
582
583/**
584 * I/O Control worker.
585 *
586 * @returns 0 on success.
587 * @returns VERR_INVALID_PARAMETER if the request is invalid.
588 *
589 * @param uIOCtl Function number.
590 * @param pDevExt Device extention.
591 * @param pSession Session data.
592 * @param pReqHdr The request header.
593 */
594int VBOXCALL supdrvIOCtl(uintptr_t uIOCtl, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPREQHDR pReqHdr)
595{
596 /*
597 * Validate the request.
598 */
599 /* this first check could probably be omitted as its also done by the OS specific code... */
600 if (RT_UNLIKELY( (pReqHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC
601 || pReqHdr->cbIn < sizeof(*pReqHdr)
602 || pReqHdr->cbOut < sizeof(*pReqHdr)))
603 {
604 OSDBGPRINT(("vboxdrv: Bad ioctl request header; cbIn=%#lx cbOut=%#lx fFlags=%#lx\n",
605 (long)pReqHdr->cbIn, (long)pReqHdr->cbOut, (long)pReqHdr->fFlags));
606 return VERR_INVALID_PARAMETER;
607 }
608 if (RT_UNLIKELY(uIOCtl == SUP_IOCTL_COOKIE))
609 {
610 if (pReqHdr->u32Cookie != SUPCOOKIE_INITIAL_COOKIE)
611 {
612 OSDBGPRINT(("SUP_IOCTL_COOKIE: bad cookie %#lx\n", (long)pReqHdr->u32Cookie));
613 return VERR_INVALID_PARAMETER;
614 }
615 }
616 else if (RT_UNLIKELY( pReqHdr->u32Cookie != pDevExt->u32Cookie
617 || pReqHdr->u32SessionCookie != pSession->u32Cookie))
618 {
619 OSDBGPRINT(("vboxdrv: bad cookie %#lx / %#lx.\n", (long)pReqHdr->u32Cookie, (long)pReqHdr->u32SessionCookie));
620 return VERR_INVALID_PARAMETER;
621 }
622
623/*
624 * Validation macros
625 */
626#define REQ_CHECK_SIZES_EX(Name, cbInExpect, cbOutExpect) \
627 do { \
628 if (RT_UNLIKELY(pReqHdr->cbIn != (cbInExpect) || pReqHdr->cbOut != (cbOutExpect))) \
629 { \
630 OSDBGPRINT(( #Name ": Invalid input/output sizes. cbIn=%ld expected %ld. cbOut=%ld expected %ld.\n", \
631 (long)pReq->Hdr.cbIn, (long)(cbInExpect), (long)pReq->Hdr.cbOut, (long)(cbOutExpect))); \
632 return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \
633 } \
634 } while (0)
635
636#define REQ_CHECK_SIZES(Name) REQ_CHECK_SIZES_EX(Name, Name ## _SIZE_IN, Name ## _SIZE_OUT)
637
638#define REQ_CHECK_SIZE_IN(Name, cbInExpect) \
639 do { \
640 if (RT_UNLIKELY(pReqHdr->cbIn != (cbInExpect))) \
641 { \
642 OSDBGPRINT(( #Name ": Invalid input/output sizes. cbIn=%ld expected %ld.\n", \
643 (long)pReq->Hdr.cbIn, (long)(cbInExpect))); \
644 return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \
645 } \
646 } while (0)
647
648#define REQ_CHECK_SIZE_OUT(Name, cbOutExpect) \
649 do { \
650 if (RT_UNLIKELY(pReqHdr->cbOut != (cbOutExpect))) \
651 { \
652 OSDBGPRINT(( #Name ": Invalid input/output sizes. cbOut=%ld expected %ld.\n", \
653 (long)pReq->Hdr.cbOut, (long)(cbOutExpect))); \
654 return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \
655 } \
656 } while (0)
657
658#define REQ_CHECK_EXPR(Name, expr) \
659 do { \
660 if (RT_UNLIKELY(!(expr))) \
661 { \
662 OSDBGPRINT(( #Name ": %s\n", #expr)); \
663 return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \
664 } \
665 } while (0)
666
667#define REQ_CHECK_EXPR_FMT(expr, fmt) \
668 do { \
669 if (RT_UNLIKELY(!(expr))) \
670 { \
671 OSDBGPRINT( fmt ); \
672 return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \
673 } \
674 } while (0)
675
676
677 /*
678 * The switch.
679 */
680 switch (SUP_CTL_CODE_NO_SIZE(uIOCtl))
681 {
682 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_COOKIE):
683 {
684 PSUPCOOKIE pReq = (PSUPCOOKIE)pReqHdr;
685 REQ_CHECK_SIZES(SUP_IOCTL_COOKIE);
686 if (strncmp(pReq->u.In.szMagic, SUPCOOKIE_MAGIC, sizeof(pReq->u.In.szMagic)))
687 {
688 OSDBGPRINT(("SUP_IOCTL_COOKIE: invalid magic %.16s\n", pReq->u.In.szMagic));
689 pReq->Hdr.rc = VERR_INVALID_MAGIC;
690 return 0;
691 }
692
693#if 0
694 /*
695 * Call out to the OS specific code and let it do permission checks on the
696 * client process.
697 */
698 if (!supdrvOSValidateClientProcess(pDevExt, pSession))
699 {
700 pReq->u.Out.u32Cookie = 0xffffffff;
701 pReq->u.Out.u32SessionCookie = 0xffffffff;
702 pReq->u.Out.u32SessionVersion = 0xffffffff;
703 pReq->u.Out.u32DriverVersion = SUPDRVIOC_VERSION;
704 pReq->u.Out.pSession = NULL;
705 pReq->u.Out.cFunctions = 0;
706 pReq->Hdr.rc = VERR_PERMISSION_DENIED;
707 return 0;
708 }
709#endif
710
711 /*
712 * Match the version.
713 * The current logic is very simple, match the major interface version.
714 */
715 if ( pReq->u.In.u32MinVersion > SUPDRVIOC_VERSION
716 || (pReq->u.In.u32MinVersion & 0xffff0000) != (SUPDRVIOC_VERSION & 0xffff0000))
717 {
718 OSDBGPRINT(("SUP_IOCTL_COOKIE: Version mismatch. Requested: %#x Min: %#x Current: %#x\n",
719 pReq->u.In.u32ReqVersion, pReq->u.In.u32MinVersion, SUPDRVIOC_VERSION));
720 pReq->u.Out.u32Cookie = 0xffffffff;
721 pReq->u.Out.u32SessionCookie = 0xffffffff;
722 pReq->u.Out.u32SessionVersion = 0xffffffff;
723 pReq->u.Out.u32DriverVersion = SUPDRVIOC_VERSION;
724 pReq->u.Out.pSession = NULL;
725 pReq->u.Out.cFunctions = 0;
726 pReq->Hdr.rc = VERR_VERSION_MISMATCH;
727 return 0;
728 }
729
730 /*
731 * Fill in return data and be gone.
732 * N.B. The first one to change SUPDRVIOC_VERSION shall makes sure that
733 * u32SessionVersion <= u32ReqVersion!
734 */
735 /** @todo Somehow validate the client and negotiate a secure cookie... */
736 pReq->u.Out.u32Cookie = pDevExt->u32Cookie;
737 pReq->u.Out.u32SessionCookie = pSession->u32Cookie;
738 pReq->u.Out.u32SessionVersion = SUPDRVIOC_VERSION;
739 pReq->u.Out.u32DriverVersion = SUPDRVIOC_VERSION;
740 pReq->u.Out.pSession = pSession;
741 pReq->u.Out.cFunctions = sizeof(g_aFunctions) / sizeof(g_aFunctions[0]);
742 pReq->Hdr.rc = VINF_SUCCESS;
743 return 0;
744 }
745
746 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_QUERY_FUNCS(0)):
747 {
748 /* validate */
749 PSUPQUERYFUNCS pReq = (PSUPQUERYFUNCS)pReqHdr;
750 REQ_CHECK_SIZES_EX(SUP_IOCTL_QUERY_FUNCS, SUP_IOCTL_QUERY_FUNCS_SIZE_IN, SUP_IOCTL_QUERY_FUNCS_SIZE_OUT(RT_ELEMENTS(g_aFunctions)));
751
752 /* execute */
753 pReq->u.Out.cFunctions = RT_ELEMENTS(g_aFunctions);
754 memcpy(&pReq->u.Out.aFunctions[0], g_aFunctions, sizeof(g_aFunctions));
755 pReq->Hdr.rc = VINF_SUCCESS;
756 return 0;
757 }
758
759 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_IDT_INSTALL):
760 {
761 /* validate */
762 PSUPIDTINSTALL pReq = (PSUPIDTINSTALL)pReqHdr;
763 REQ_CHECK_SIZES(SUP_IOCTL_IDT_INSTALL);
764
765 /* execute */
766#ifdef VBOX_WITH_IDT_PATCHING
767 pReq->Hdr.rc = supdrvIOCtl_IdtInstall(pDevExt, pSession, pReq);
768#else
769 pReq->u.Out.u8Idt = 3;
770 pReq->Hdr.rc = VERR_NOT_SUPPORTED;
771#endif
772 return 0;
773 }
774
775 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_IDT_REMOVE):
776 {
777 /* validate */
778 PSUPIDTREMOVE pReq = (PSUPIDTREMOVE)pReqHdr;
779 REQ_CHECK_SIZES(SUP_IOCTL_IDT_REMOVE);
780
781 /* execute */
782#ifdef VBOX_WITH_IDT_PATCHING
783 pReq->Hdr.rc = supdrvIOCtl_IdtRemoveAll(pDevExt, pSession);
784#else
785 pReq->Hdr.rc = VERR_NOT_SUPPORTED;
786#endif
787 return 0;
788 }
789
790 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_LOCK):
791 {
792 /* validate */
793 PSUPPAGELOCK pReq = (PSUPPAGELOCK)pReqHdr;
794 REQ_CHECK_SIZE_IN(SUP_IOCTL_PAGE_LOCK, SUP_IOCTL_PAGE_LOCK_SIZE_IN);
795 REQ_CHECK_SIZE_OUT(SUP_IOCTL_PAGE_LOCK, SUP_IOCTL_PAGE_LOCK_SIZE_OUT(pReq->u.In.cPages));
796 REQ_CHECK_EXPR(SUP_IOCTL_PAGE_LOCK, pReq->u.In.cPages > 0);
797 REQ_CHECK_EXPR(SUP_IOCTL_PAGE_LOCK, pReq->u.In.pvR3 >= PAGE_SIZE);
798
799 /* execute */
800 pReq->Hdr.rc = SUPR0LockMem(pSession, pReq->u.In.pvR3, pReq->u.In.cPages, &pReq->u.Out.aPages[0]);
801 if (RT_FAILURE(pReq->Hdr.rc))
802 pReq->Hdr.cbOut = sizeof(pReq->Hdr);
803 return 0;
804 }
805
806 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_UNLOCK):
807 {
808 /* validate */
809 PSUPPAGEUNLOCK pReq = (PSUPPAGEUNLOCK)pReqHdr;
810 REQ_CHECK_SIZES(SUP_IOCTL_PAGE_UNLOCK);
811
812 /* execute */
813 pReq->Hdr.rc = SUPR0UnlockMem(pSession, pReq->u.In.pvR3);
814 return 0;
815 }
816
817 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CONT_ALLOC):
818 {
819 /* validate */
820 PSUPCONTALLOC pReq = (PSUPCONTALLOC)pReqHdr;
821 REQ_CHECK_SIZES(SUP_IOCTL_CONT_ALLOC);
822
823 /* execute */
824 pReq->Hdr.rc = SUPR0ContAlloc(pSession, pReq->u.In.cPages, &pReq->u.Out.pvR0, &pReq->u.Out.pvR3, &pReq->u.Out.HCPhys);
825 if (RT_FAILURE(pReq->Hdr.rc))
826 pReq->Hdr.cbOut = sizeof(pReq->Hdr);
827 return 0;
828 }
829
830 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CONT_FREE):
831 {
832 /* validate */
833 PSUPCONTFREE pReq = (PSUPCONTFREE)pReqHdr;
834 REQ_CHECK_SIZES(SUP_IOCTL_CONT_FREE);
835
836 /* execute */
837 pReq->Hdr.rc = SUPR0ContFree(pSession, (RTHCUINTPTR)pReq->u.In.pvR3);
838 return 0;
839 }
840
841 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_OPEN):
842 {
843 /* validate */
844 PSUPLDROPEN pReq = (PSUPLDROPEN)pReqHdr;
845 REQ_CHECK_SIZES(SUP_IOCTL_LDR_OPEN);
846 REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.cbImage > 0);
847 REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.cbImage < _1M*16);
848 REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.szName[0]);
849 REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, memchr(pReq->u.In.szName, '\0', sizeof(pReq->u.In.szName)));
850 REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, !strpbrk(pReq->u.In.szName, ";:()[]{}/\\|&*%#@!~`\"'"));
851
852 /* execute */
853 pReq->Hdr.rc = supdrvIOCtl_LdrOpen(pDevExt, pSession, pReq);
854 return 0;
855 }
856
857 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_LOAD):
858 {
859 /* validate */
860 PSUPLDRLOAD pReq = (PSUPLDRLOAD)pReqHdr;
861 REQ_CHECK_EXPR(Name, pReq->Hdr.cbIn >= sizeof(*pReq));
862 REQ_CHECK_SIZES_EX(SUP_IOCTL_LDR_LOAD, SUP_IOCTL_LDR_LOAD_SIZE_IN(pReq->u.In.cbImage), SUP_IOCTL_LDR_LOAD_SIZE_OUT);
863 REQ_CHECK_EXPR(SUP_IOCTL_LDR_LOAD, pReq->u.In.cSymbols <= 16384);
864 REQ_CHECK_EXPR_FMT( !pReq->u.In.cSymbols
865 || ( pReq->u.In.offSymbols < pReq->u.In.cbImage
866 && pReq->u.In.offSymbols + pReq->u.In.cSymbols * sizeof(SUPLDRSYM) <= pReq->u.In.cbImage),
867 ("SUP_IOCTL_LDR_LOAD: offSymbols=%#lx cSymbols=%#lx cbImage=%#lx\n", (long)pReq->u.In.offSymbols,
868 (long)pReq->u.In.cSymbols, (long)pReq->u.In.cbImage));
869 REQ_CHECK_EXPR_FMT( !pReq->u.In.cbStrTab
870 || ( pReq->u.In.offStrTab < pReq->u.In.cbImage
871 && pReq->u.In.offStrTab + pReq->u.In.cbStrTab <= pReq->u.In.cbImage
872 && pReq->u.In.cbStrTab <= pReq->u.In.cbImage),
873 ("SUP_IOCTL_LDR_LOAD: offStrTab=%#lx cbStrTab=%#lx cbImage=%#lx\n", (long)pReq->u.In.offStrTab,
874 (long)pReq->u.In.cbStrTab, (long)pReq->u.In.cbImage));
875
876 if (pReq->u.In.cSymbols)
877 {
878 uint32_t i;
879 PSUPLDRSYM paSyms = (PSUPLDRSYM)&pReq->u.In.achImage[pReq->u.In.offSymbols];
880 for (i = 0; i < pReq->u.In.cSymbols; i++)
881 {
882 REQ_CHECK_EXPR_FMT(paSyms[i].offSymbol < pReq->u.In.cbImage,
883 ("SUP_IOCTL_LDR_LOAD: sym #%ld: symb off %#lx (max=%#lx)\n", (long)i, (long)paSyms[i].offSymbol, (long)pReq->u.In.cbImage));
884 REQ_CHECK_EXPR_FMT(paSyms[i].offName < pReq->u.In.cbStrTab,
885 ("SUP_IOCTL_LDR_LOAD: sym #%ld: name off %#lx (max=%#lx)\n", (long)i, (long)paSyms[i].offName, (long)pReq->u.In.cbImage));
886 REQ_CHECK_EXPR_FMT(memchr(&pReq->u.In.achImage[pReq->u.In.offStrTab + paSyms[i].offName], '\0', pReq->u.In.cbStrTab - paSyms[i].offName),
887 ("SUP_IOCTL_LDR_LOAD: sym #%ld: unterminated name! (%#lx / %#lx)\n", (long)i, (long)paSyms[i].offName, (long)pReq->u.In.cbImage));
888 }
889 }
890
891 /* execute */
892 pReq->Hdr.rc = supdrvIOCtl_LdrLoad(pDevExt, pSession, pReq);
893 return 0;
894 }
895
896 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_FREE):
897 {
898 /* validate */
899 PSUPLDRFREE pReq = (PSUPLDRFREE)pReqHdr;
900 REQ_CHECK_SIZES(SUP_IOCTL_LDR_FREE);
901
902 /* execute */
903 pReq->Hdr.rc = supdrvIOCtl_LdrFree(pDevExt, pSession, pReq);
904 return 0;
905 }
906
907 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_GET_SYMBOL):
908 {
909 /* validate */
910 PSUPLDRGETSYMBOL pReq = (PSUPLDRGETSYMBOL)pReqHdr;
911 REQ_CHECK_SIZES(SUP_IOCTL_LDR_GET_SYMBOL);
912 REQ_CHECK_EXPR(SUP_IOCTL_LDR_GET_SYMBOL, memchr(pReq->u.In.szSymbol, '\0', sizeof(pReq->u.In.szSymbol)));
913
914 /* execute */
915 pReq->Hdr.rc = supdrvIOCtl_LdrGetSymbol(pDevExt, pSession, pReq);
916 return 0;
917 }
918
919 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CALL_VMMR0(0)):
920 {
921 /* validate */
922 PSUPCALLVMMR0 pReq = (PSUPCALLVMMR0)pReqHdr;
923 if (pReq->Hdr.cbIn == SUP_IOCTL_CALL_VMMR0_SIZE(0))
924 {
925 REQ_CHECK_SIZES_EX(SUP_IOCTL_CALL_VMMR0, SUP_IOCTL_CALL_VMMR0_SIZE_IN(0), SUP_IOCTL_CALL_VMMR0_SIZE_OUT(0));
926
927 /* execute */
928 if (RT_LIKELY(pDevExt->pfnVMMR0EntryEx))
929 pReq->Hdr.rc = pDevExt->pfnVMMR0EntryEx(pReq->u.In.pVMR0, pReq->u.In.uOperation, NULL, pReq->u.In.u64Arg);
930 else
931 pReq->Hdr.rc = VERR_WRONG_ORDER;
932 }
933 else
934 {
935 PSUPVMMR0REQHDR pVMMReq = (PSUPVMMR0REQHDR)&pReq->abReqPkt[0];
936 REQ_CHECK_EXPR_FMT(pReq->Hdr.cbIn >= SUP_IOCTL_CALL_VMMR0_SIZE(sizeof(SUPVMMR0REQHDR)),
937 ("SUP_IOCTL_CALL_VMMR0: cbIn=%#x < %#x\n", pReq->Hdr.cbIn, SUP_IOCTL_CALL_VMMR0_SIZE(sizeof(SUPVMMR0REQHDR))));
938 REQ_CHECK_EXPR(SUP_IOCTL_CALL_VMMR0, pVMMReq->u32Magic == SUPVMMR0REQHDR_MAGIC);
939 REQ_CHECK_SIZES_EX(SUP_IOCTL_CALL_VMMR0, SUP_IOCTL_CALL_VMMR0_SIZE_IN(pVMMReq->cbReq), SUP_IOCTL_CALL_VMMR0_SIZE_OUT(pVMMReq->cbReq));
940
941 /* execute */
942 if (RT_LIKELY(pDevExt->pfnVMMR0EntryEx))
943 pReq->Hdr.rc = pDevExt->pfnVMMR0EntryEx(pReq->u.In.pVMR0, pReq->u.In.uOperation, pVMMReq, pReq->u.In.u64Arg);
944 else
945 pReq->Hdr.rc = VERR_WRONG_ORDER;
946 }
947 return 0;
948 }
949
950 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GET_PAGING_MODE):
951 {
952 /* validate */
953 PSUPGETPAGINGMODE pReq = (PSUPGETPAGINGMODE)pReqHdr;
954 REQ_CHECK_SIZES(SUP_IOCTL_GET_PAGING_MODE);
955
956 /* execute */
957 pReq->Hdr.rc = VINF_SUCCESS;
958 pReq->u.Out.enmMode = supdrvIOCtl_GetPagingMode();
959 return 0;
960 }
961
962 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LOW_ALLOC):
963 {
964 /* validate */
965 PSUPLOWALLOC pReq = (PSUPLOWALLOC)pReqHdr;
966 REQ_CHECK_EXPR(SUP_IOCTL_LOW_ALLOC, pReq->Hdr.cbIn <= SUP_IOCTL_LOW_ALLOC_SIZE_IN);
967 REQ_CHECK_SIZES_EX(SUP_IOCTL_LOW_ALLOC, SUP_IOCTL_LOW_ALLOC_SIZE_IN, SUP_IOCTL_LOW_ALLOC_SIZE_OUT(pReq->u.In.cPages));
968
969 /* execute */
970 pReq->Hdr.rc = SUPR0LowAlloc(pSession, pReq->u.In.cPages, &pReq->u.Out.pvR0, &pReq->u.Out.pvR3, &pReq->u.Out.aPages[0]);
971 if (RT_FAILURE(pReq->Hdr.rc))
972 pReq->Hdr.cbOut = sizeof(pReq->Hdr);
973 return 0;
974 }
975
976 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LOW_FREE):
977 {
978 /* validate */
979 PSUPLOWFREE pReq = (PSUPLOWFREE)pReqHdr;
980 REQ_CHECK_SIZES(SUP_IOCTL_LOW_FREE);
981
982 /* execute */
983 pReq->Hdr.rc = SUPR0LowFree(pSession, (RTHCUINTPTR)pReq->u.In.pvR3);
984 return 0;
985 }
986
987 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GIP_MAP):
988 {
989 /* validate */
990 PSUPGIPMAP pReq = (PSUPGIPMAP)pReqHdr;
991 REQ_CHECK_SIZES(SUP_IOCTL_GIP_MAP);
992
993 /* execute */
994 pReq->Hdr.rc = SUPR0GipMap(pSession, &pReq->u.Out.pGipR3, &pReq->u.Out.HCPhysGip);
995 if (RT_SUCCESS(pReq->Hdr.rc))
996 pReq->u.Out.pGipR0 = pDevExt->pGip;
997 return 0;
998 }
999
1000 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GIP_UNMAP):
1001 {
1002 /* validate */
1003 PSUPGIPUNMAP pReq = (PSUPGIPUNMAP)pReqHdr;
1004 REQ_CHECK_SIZES(SUP_IOCTL_GIP_UNMAP);
1005
1006 /* execute */
1007 pReq->Hdr.rc = SUPR0GipUnmap(pSession);
1008 return 0;
1009 }
1010
1011 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_SET_VM_FOR_FAST):
1012 {
1013 /* validate */
1014 PSUPSETVMFORFAST pReq = (PSUPSETVMFORFAST)pReqHdr;
1015 REQ_CHECK_SIZES(SUP_IOCTL_SET_VM_FOR_FAST);
1016 REQ_CHECK_EXPR_FMT( !pReq->u.In.pVMR0
1017 || ( VALID_PTR(pReq->u.In.pVMR0)
1018 && !((uintptr_t)pReq->u.In.pVMR0 & (PAGE_SIZE - 1))),
1019 ("SUP_IOCTL_SET_VM_FOR_FAST: pVMR0=%p!\n", pReq->u.In.pVMR0));
1020 /* execute */
1021 pSession->pVM = pReq->u.In.pVMR0;
1022 pReq->Hdr.rc = VINF_SUCCESS;
1023 return 0;
1024 }
1025
1026 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_ALLOC):
1027 {
1028 /* validate */
1029 PSUPPAGEALLOC pReq = (PSUPPAGEALLOC)pReqHdr;
1030 REQ_CHECK_EXPR(SUP_IOCTL_PAGE_ALLOC, pReq->Hdr.cbIn <= SUP_IOCTL_PAGE_ALLOC_SIZE_IN);
1031 REQ_CHECK_SIZES_EX(SUP_IOCTL_PAGE_ALLOC, SUP_IOCTL_PAGE_ALLOC_SIZE_IN, SUP_IOCTL_PAGE_ALLOC_SIZE_OUT(pReq->u.In.cPages));
1032
1033 /* execute */
1034 pReq->Hdr.rc = SUPR0PageAlloc(pSession, pReq->u.In.cPages, &pReq->u.Out.pvR3, &pReq->u.Out.aPages[0]);
1035 if (RT_FAILURE(pReq->Hdr.rc))
1036 pReq->Hdr.cbOut = sizeof(pReq->Hdr);
1037 return 0;
1038 }
1039
1040 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_FREE):
1041 {
1042 /* validate */
1043 PSUPPAGEFREE pReq = (PSUPPAGEFREE)pReqHdr;
1044 REQ_CHECK_SIZES(SUP_IOCTL_PAGE_FREE);
1045
1046 /* execute */
1047 pReq->Hdr.rc = SUPR0PageFree(pSession, pReq->u.In.pvR3);
1048 return 0;
1049 }
1050
1051 default:
1052 dprintf(("Unknown IOCTL %#lx\n", (long)uIOCtl));
1053 break;
1054 }
1055 return SUPDRV_ERR_GENERAL_FAILURE;
1056}
1057
1058
1059/**
1060 * Register a object for reference counting.
1061 * The object is registered with one reference in the specified session.
1062 *
1063 * @returns Unique identifier on success (pointer).
1064 * All future reference must use this identifier.
1065 * @returns NULL on failure.
1066 * @param pfnDestructor The destructore function which will be called when the reference count reaches 0.
1067 * @param pvUser1 The first user argument.
1068 * @param pvUser2 The second user argument.
1069 */
1070SUPR0DECL(void *) SUPR0ObjRegister(PSUPDRVSESSION pSession, SUPDRVOBJTYPE enmType, PFNSUPDRVDESTRUCTOR pfnDestructor, void *pvUser1, void *pvUser2)
1071{
1072 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
1073 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
1074 PSUPDRVOBJ pObj;
1075 PSUPDRVUSAGE pUsage;
1076
1077 /*
1078 * Validate the input.
1079 */
1080 AssertReturn(SUP_IS_SESSION_VALID(pSession), NULL);
1081 AssertReturn(enmType > SUPDRVOBJTYPE_INVALID && enmType < SUPDRVOBJTYPE_END, NULL);
1082 AssertPtrReturn(pfnDestructor, NULL);
1083
1084 /*
1085 * Allocate and initialize the object.
1086 */
1087 pObj = (PSUPDRVOBJ)RTMemAlloc(sizeof(*pObj));
1088 if (!pObj)
1089 return NULL;
1090 pObj->u32Magic = SUPDRVOBJ_MAGIC;
1091 pObj->enmType = enmType;
1092 pObj->pNext = NULL;
1093 pObj->cUsage = 1;
1094 pObj->pfnDestructor = pfnDestructor;
1095 pObj->pvUser1 = pvUser1;
1096 pObj->pvUser2 = pvUser2;
1097 pObj->CreatorUid = pSession->Uid;
1098 pObj->CreatorGid = pSession->Gid;
1099 pObj->CreatorProcess= pSession->Process;
1100 supdrvOSObjInitCreator(pObj, pSession);
1101
1102 /*
1103 * Allocate the usage record.
1104 * (We keep freed usage records around to simplity SUPR0ObjAddRef().)
1105 */
1106 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
1107
1108 pUsage = pDevExt->pUsageFree;
1109 if (pUsage)
1110 pDevExt->pUsageFree = pUsage->pNext;
1111 else
1112 {
1113 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
1114 pUsage = (PSUPDRVUSAGE)RTMemAlloc(sizeof(*pUsage));
1115 if (!pUsage)
1116 {
1117 RTMemFree(pObj);
1118 return NULL;
1119 }
1120 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
1121 }
1122
1123 /*
1124 * Insert the object and create the session usage record.
1125 */
1126 /* The object. */
1127 pObj->pNext = pDevExt->pObjs;
1128 pDevExt->pObjs = pObj;
1129
1130 /* The session record. */
1131 pUsage->cUsage = 1;
1132 pUsage->pObj = pObj;
1133 pUsage->pNext = pSession->pUsage;
1134 dprintf(("SUPR0ObjRegister: pUsage=%p:{.pObj=%p, .pNext=%p}\n", pUsage, pUsage->pObj, pUsage->pNext));
1135 pSession->pUsage = pUsage;
1136
1137 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
1138
1139 dprintf(("SUPR0ObjRegister: returns %p (pvUser1=%p, pvUser=%p)\n", pObj, pvUser1, pvUser2));
1140 return pObj;
1141}
1142
1143
1144/**
1145 * Increment the reference counter for the object associating the reference
1146 * with the specified session.
1147 *
1148 * @returns IPRT status code.
1149 * @param pvObj The identifier returned by SUPR0ObjRegister().
1150 * @param pSession The session which is referencing the object.
1151 */
1152SUPR0DECL(int) SUPR0ObjAddRef(void *pvObj, PSUPDRVSESSION pSession)
1153{
1154 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
1155 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
1156 PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj;
1157 PSUPDRVUSAGE pUsagePre;
1158 PSUPDRVUSAGE pUsage;
1159
1160 /*
1161 * Validate the input.
1162 */
1163 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1164 AssertMsgReturn(VALID_PTR(pObj) && pObj->u32Magic == SUPDRVOBJ_MAGIC,
1165 ("Invalid pvObj=%p magic=%#x (exepcted %#x)\n", pvObj, pObj ? pObj->u32Magic : 0, SUPDRVOBJ_MAGIC),
1166 VERR_INVALID_PARAMETER);
1167
1168 /*
1169 * Preallocate the usage record.
1170 */
1171 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
1172
1173 pUsagePre = pDevExt->pUsageFree;
1174 if (pUsagePre)
1175 pDevExt->pUsageFree = pUsagePre->pNext;
1176 else
1177 {
1178 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
1179 pUsagePre = (PSUPDRVUSAGE)RTMemAlloc(sizeof(*pUsagePre));
1180 if (!pUsagePre)
1181 return VERR_NO_MEMORY;
1182 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
1183 }
1184
1185 /*
1186 * Reference the object.
1187 */
1188 pObj->cUsage++;
1189
1190 /*
1191 * Look for the session record.
1192 */
1193 for (pUsage = pSession->pUsage; pUsage; pUsage = pUsage->pNext)
1194 {
1195 dprintf(("SUPR0AddRef: pUsage=%p:{.pObj=%p, .pNext=%p}\n", pUsage, pUsage->pObj, pUsage->pNext));
1196 if (pUsage->pObj == pObj)
1197 break;
1198 }
1199 if (pUsage)
1200 pUsage->cUsage++;
1201 else
1202 {
1203 /* create a new session record. */
1204 pUsagePre->cUsage = 1;
1205 pUsagePre->pObj = pObj;
1206 pUsagePre->pNext = pSession->pUsage;
1207 pSession->pUsage = pUsagePre;
1208 dprintf(("SUPR0AddRef: pUsagePre=%p:{.pObj=%p, .pNext=%p}\n", pUsagePre, pUsagePre->pObj, pUsagePre->pNext));
1209
1210 pUsagePre = NULL;
1211 }
1212
1213 /*
1214 * Put any unused usage record into the free list..
1215 */
1216 if (pUsagePre)
1217 {
1218 pUsagePre->pNext = pDevExt->pUsageFree;
1219 pDevExt->pUsageFree = pUsagePre;
1220 }
1221
1222 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
1223
1224 return VINF_SUCCESS;
1225}
1226
1227
1228/**
1229 * Decrement / destroy a reference counter record for an object.
1230 *
1231 * The object is uniquely identified by pfnDestructor+pvUser1+pvUser2.
1232 *
1233 * @returns IPRT status code.
1234 * @param pvObj The identifier returned by SUPR0ObjRegister().
1235 * @param pSession The session which is referencing the object.
1236 */
1237SUPR0DECL(int) SUPR0ObjRelease(void *pvObj, PSUPDRVSESSION pSession)
1238{
1239 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
1240 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
1241 PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj;
1242 bool fDestroy = false;
1243 PSUPDRVUSAGE pUsage;
1244 PSUPDRVUSAGE pUsagePrev;
1245
1246 /*
1247 * Validate the input.
1248 */
1249 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1250 AssertMsgReturn(VALID_PTR(pObj) && pObj->u32Magic == SUPDRVOBJ_MAGIC,
1251 ("Invalid pvObj=%p magic=%#x (exepcted %#x)\n", pvObj, pObj ? pObj->u32Magic : 0, SUPDRVOBJ_MAGIC),
1252 VERR_INVALID_PARAMETER);
1253
1254 /*
1255 * Acquire the spinlock and look for the usage record.
1256 */
1257 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
1258
1259 for (pUsagePrev = NULL, pUsage = pSession->pUsage;
1260 pUsage;
1261 pUsagePrev = pUsage, pUsage = pUsage->pNext)
1262 {
1263 dprintf(("SUPR0ObjRelease: pUsage=%p:{.pObj=%p, .pNext=%p}\n", pUsage, pUsage->pObj, pUsage->pNext));
1264 if (pUsage->pObj == pObj)
1265 {
1266 AssertMsg(pUsage->cUsage >= 1 && pObj->cUsage >= pUsage->cUsage, ("glob %d; sess %d\n", pObj->cUsage, pUsage->cUsage));
1267 if (pUsage->cUsage > 1)
1268 {
1269 pObj->cUsage--;
1270 pUsage->cUsage--;
1271 }
1272 else
1273 {
1274 /*
1275 * Free the session record.
1276 */
1277 if (pUsagePrev)
1278 pUsagePrev->pNext = pUsage->pNext;
1279 else
1280 pSession->pUsage = pUsage->pNext;
1281 pUsage->pNext = pDevExt->pUsageFree;
1282 pDevExt->pUsageFree = pUsage;
1283
1284 /* What about the object? */
1285 if (pObj->cUsage > 1)
1286 pObj->cUsage--;
1287 else
1288 {
1289 /*
1290 * Object is to be destroyed, unlink it.
1291 */
1292 fDestroy = true;
1293 if (pDevExt->pObjs == pObj)
1294 pDevExt->pObjs = pObj->pNext;
1295 else
1296 {
1297 PSUPDRVOBJ pObjPrev;
1298 for (pObjPrev = pDevExt->pObjs; pObjPrev; pObjPrev = pObjPrev->pNext)
1299 if (pObjPrev->pNext == pObj)
1300 {
1301 pObjPrev->pNext = pObj->pNext;
1302 break;
1303 }
1304 Assert(pObjPrev);
1305 }
1306 }
1307 }
1308 break;
1309 }
1310 }
1311
1312 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
1313
1314 /*
1315 * Call the destructor and free the object if required.
1316 */
1317 if (fDestroy)
1318 {
1319 pObj->u32Magic++;
1320 if (pObj->pfnDestructor)
1321 pObj->pfnDestructor(pObj, pObj->pvUser1, pObj->pvUser2);
1322 RTMemFree(pObj);
1323 }
1324
1325 AssertMsg(pUsage, ("pvObj=%p\n", pvObj));
1326 return pUsage ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
1327}
1328
1329/**
1330 * Verifies that the current process can access the specified object.
1331 *
1332 * @returns The following IPRT status code:
1333 * @retval VINF_SUCCESS if access was granted.
1334 * @retval VERR_PERMISSION_DENIED if denied access.
1335 * @retval VERR_INVALID_PARAMETER if invalid parameter.
1336 *
1337 * @param pvObj The identifier returned by SUPR0ObjRegister().
1338 * @param pSession The session which wishes to access the object.
1339 * @param pszObjName Object string name. This is optional and depends on the object type.
1340 *
1341 * @remark The caller is responsible for making sure the object isn't removed while
1342 * we're inside this function. If uncertain about this, just call AddRef before calling us.
1343 */
1344SUPR0DECL(int) SUPR0ObjVerifyAccess(void *pvObj, PSUPDRVSESSION pSession, const char *pszObjName)
1345{
1346 PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj;
1347 int rc;
1348
1349 /*
1350 * Validate the input.
1351 */
1352 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1353 AssertMsgReturn(VALID_PTR(pObj) && pObj->u32Magic == SUPDRVOBJ_MAGIC,
1354 ("Invalid pvObj=%p magic=%#x (exepcted %#x)\n", pvObj, pObj ? pObj->u32Magic : 0, SUPDRVOBJ_MAGIC),
1355 VERR_INVALID_PARAMETER);
1356
1357 /*
1358 * Check access. (returns true if a decision has been made.)
1359 */
1360 rc = VERR_INTERNAL_ERROR;
1361 if (supdrvOSObjCanAccess(pObj, pSession, pszObjName, &rc))
1362 return rc;
1363
1364 /*
1365 * Default policy is to allow the user to access his own
1366 * stuff but nothing else.
1367 */
1368 if (pObj->CreatorUid == pSession->Uid)
1369 return VINF_SUCCESS;
1370 return VERR_PERMISSION_DENIED;
1371}
1372
1373
1374/**
1375 * Lock pages.
1376 *
1377 * @returns IPRT status code.
1378 * @param pSession Session to which the locked memory should be associated.
1379 * @param pvR3 Start of the memory range to lock.
1380 * This must be page aligned.
1381 * @param cb Size of the memory range to lock.
1382 * This must be page aligned.
1383 */
1384SUPR0DECL(int) SUPR0LockMem(PSUPDRVSESSION pSession, RTR3PTR pvR3, uint32_t cPages, PRTHCPHYS paPages)
1385{
1386 int rc;
1387 SUPDRVMEMREF Mem = {0};
1388 const size_t cb = (size_t)cPages << PAGE_SHIFT;
1389 dprintf(("SUPR0LockMem: pSession=%p pvR3=%p cPages=%d paPages=%p\n", pSession, (void *)pvR3, cPages, paPages));
1390
1391 /*
1392 * Verify input.
1393 */
1394 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1395 AssertPtrReturn(paPages, VERR_INVALID_PARAMETER);
1396 if ( RT_ALIGN_R3PT(pvR3, PAGE_SIZE, RTR3PTR) != pvR3
1397 || !pvR3)
1398 {
1399 dprintf(("pvR3 (%p) must be page aligned and not NULL!\n", (void *)pvR3));
1400 return VERR_INVALID_PARAMETER;
1401 }
1402
1403#ifdef RT_OS_WINDOWS /* A temporary hack for windows, will be removed once all ring-3 code has been cleaned up. */
1404 /* First check if we allocated it using SUPPageAlloc; if so then we don't need to lock it again */
1405 rc = supdrvPageGetPhys(pSession, pvR3, cPages, paPages);
1406 if (RT_SUCCESS(rc))
1407 return rc;
1408#endif
1409
1410 /*
1411 * Let IPRT do the job.
1412 */
1413 Mem.eType = MEMREF_TYPE_LOCKED;
1414 rc = RTR0MemObjLockUser(&Mem.MemObj, pvR3, cb, RTR0ProcHandleSelf());
1415 if (RT_SUCCESS(rc))
1416 {
1417 uint32_t iPage = cPages;
1418 AssertMsg(RTR0MemObjAddressR3(Mem.MemObj) == pvR3, ("%p == %p\n", RTR0MemObjAddressR3(Mem.MemObj), pvR3));
1419 AssertMsg(RTR0MemObjSize(Mem.MemObj) == cb, ("%x == %x\n", RTR0MemObjSize(Mem.MemObj), cb));
1420
1421 while (iPage-- > 0)
1422 {
1423 paPages[iPage] = RTR0MemObjGetPagePhysAddr(Mem.MemObj, iPage);
1424 if (RT_UNLIKELY(paPages[iPage] == NIL_RTCCPHYS))
1425 {
1426 AssertMsgFailed(("iPage=%d\n", iPage));
1427 rc = VERR_INTERNAL_ERROR;
1428 break;
1429 }
1430 }
1431 if (RT_SUCCESS(rc))
1432 rc = supdrvMemAdd(&Mem, pSession);
1433 if (RT_FAILURE(rc))
1434 {
1435 int rc2 = RTR0MemObjFree(Mem.MemObj, false);
1436 AssertRC(rc2);
1437 }
1438 }
1439
1440 return rc;
1441}
1442
1443
1444/**
1445 * Unlocks the memory pointed to by pv.
1446 *
1447 * @returns IPRT status code.
1448 * @param pSession Session to which the memory was locked.
1449 * @param pvR3 Memory to unlock.
1450 */
1451SUPR0DECL(int) SUPR0UnlockMem(PSUPDRVSESSION pSession, RTR3PTR pvR3)
1452{
1453 dprintf(("SUPR0UnlockMem: pSession=%p pvR3=%p\n", pSession, (void *)pvR3));
1454 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1455#ifdef RT_OS_WINDOWS
1456 /*
1457 * Temporary hack for windows - SUPR0PageFree will unlock SUPR0PageAlloc
1458 * allocations; ignore this call.
1459 */
1460 if (supdrvPageWasLockedByPageAlloc(pSession, pvR3))
1461 {
1462 dprintf(("Page will be unlocked in SUPR0PageFree -> ignore\n"));
1463 return VINF_SUCCESS;
1464 }
1465#endif
1466 return supdrvMemRelease(pSession, (RTHCUINTPTR)pvR3, MEMREF_TYPE_LOCKED);
1467}
1468
1469
1470/**
1471 * Allocates a chunk of page aligned memory with contiguous and fixed physical
1472 * backing.
1473 *
1474 * @returns IPRT status code.
1475 * @param pSession Session data.
1476 * @param cb Number of bytes to allocate.
1477 * @param ppvR0 Where to put the address of Ring-0 mapping the allocated memory.
1478 * @param ppvR3 Where to put the address of Ring-3 mapping the allocated memory.
1479 * @param pHCPhys Where to put the physical address of allocated memory.
1480 */
1481SUPR0DECL(int) SUPR0ContAlloc(PSUPDRVSESSION pSession, uint32_t cPages, PRTR0PTR ppvR0, PRTR3PTR ppvR3, PRTHCPHYS pHCPhys)
1482{
1483 int rc;
1484 SUPDRVMEMREF Mem = {0};
1485 dprintf(("SUPR0ContAlloc: pSession=%p cPages=%d ppvR0=%p ppvR3=%p pHCPhys=%p\n", pSession, cPages, ppvR0, ppvR3, pHCPhys));
1486
1487 /*
1488 * Validate input.
1489 */
1490 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1491 if (!ppvR3 || !ppvR0 || !pHCPhys)
1492 {
1493 dprintf(("Null pointer. All of these should be set: pSession=%p ppvR0=%p ppvR3=%p pHCPhys=%p\n",
1494 pSession, ppvR0, ppvR3, pHCPhys));
1495 return VERR_INVALID_PARAMETER;
1496
1497 }
1498 if (cPages < 1 || cPages >= 256)
1499 {
1500 dprintf(("Illegal request cPages=%d, must be greater than 0 and smaller than 256\n", cPages));
1501 return VERR_INVALID_PARAMETER;
1502 }
1503
1504 /*
1505 * Let IPRT do the job.
1506 */
1507 rc = RTR0MemObjAllocCont(&Mem.MemObj, cPages << PAGE_SHIFT, true /* executable R0 mapping */);
1508 if (RT_SUCCESS(rc))
1509 {
1510 int rc2;
1511 rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0,
1512 RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, RTR0ProcHandleSelf());
1513 if (RT_SUCCESS(rc))
1514 {
1515 Mem.eType = MEMREF_TYPE_CONT;
1516 rc = supdrvMemAdd(&Mem, pSession);
1517 if (!rc)
1518 {
1519 *ppvR0 = RTR0MemObjAddress(Mem.MemObj);
1520 *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3);
1521 *pHCPhys = RTR0MemObjGetPagePhysAddr(Mem.MemObj, 0);
1522 return 0;
1523 }
1524
1525 rc2 = RTR0MemObjFree(Mem.MapObjR3, false);
1526 AssertRC(rc2);
1527 }
1528 rc2 = RTR0MemObjFree(Mem.MemObj, false);
1529 AssertRC(rc2);
1530 }
1531
1532 return rc;
1533}
1534
1535
1536/**
1537 * Frees memory allocated using SUPR0ContAlloc().
1538 *
1539 * @returns IPRT status code.
1540 * @param pSession The session to which the memory was allocated.
1541 * @param uPtr Pointer to the memory (ring-3 or ring-0).
1542 */
1543SUPR0DECL(int) SUPR0ContFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr)
1544{
1545 dprintf(("SUPR0ContFree: pSession=%p uPtr=%p\n", pSession, (void *)uPtr));
1546 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1547 return supdrvMemRelease(pSession, uPtr, MEMREF_TYPE_CONT);
1548}
1549
1550
1551/**
1552 * Allocates a chunk of page aligned memory with fixed physical backing below 4GB.
1553 *
1554 * @returns IPRT status code.
1555 * @param pSession Session data.
1556 * @param cPages Number of pages to allocate.
1557 * @param ppvR0 Where to put the address of Ring-0 mapping of the allocated memory.
1558 * @param ppvR3 Where to put the address of Ring-3 mapping of the allocated memory.
1559 * @param paPages Where to put the physical addresses of allocated memory.
1560 */
1561SUPR0DECL(int) SUPR0LowAlloc(PSUPDRVSESSION pSession, uint32_t cPages, PRTR0PTR ppvR0, PRTR3PTR ppvR3, PRTHCPHYS paPages)
1562{
1563 unsigned iPage;
1564 int rc;
1565 SUPDRVMEMREF Mem = {0};
1566 dprintf(("SUPR0LowAlloc: pSession=%p cPages=%d ppvR3=%p ppvR0=%p paPages=%p\n", pSession, cPages, ppvR3, ppvR0, paPages));
1567
1568 /*
1569 * Validate input.
1570 */
1571 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1572 if (!ppvR3 || !ppvR0 || !paPages)
1573 {
1574 dprintf(("Null pointer. All of these should be set: pSession=%p ppvR3=%p ppvR0=%p paPages=%p\n",
1575 pSession, ppvR3, ppvR0, paPages));
1576 return VERR_INVALID_PARAMETER;
1577
1578 }
1579 if (cPages < 1 || cPages > 256)
1580 {
1581 dprintf(("Illegal request cPages=%d, must be greater than 0 and smaller than 256.\n", cPages));
1582 return VERR_INVALID_PARAMETER;
1583 }
1584
1585 /*
1586 * Let IPRT do the work.
1587 */
1588 rc = RTR0MemObjAllocLow(&Mem.MemObj, cPages << PAGE_SHIFT, true /* executable ring-0 mapping */);
1589 if (RT_SUCCESS(rc))
1590 {
1591 int rc2;
1592 rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0,
1593 RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, RTR0ProcHandleSelf());
1594 if (RT_SUCCESS(rc))
1595 {
1596 Mem.eType = MEMREF_TYPE_LOW;
1597 rc = supdrvMemAdd(&Mem, pSession);
1598 if (!rc)
1599 {
1600 for (iPage = 0; iPage < cPages; iPage++)
1601 {
1602 paPages[iPage] = RTR0MemObjGetPagePhysAddr(Mem.MemObj, iPage);
1603 AssertMsg(!(paPages[iPage] & (PAGE_SIZE - 1)), ("iPage=%d Phys=%VHp\n", paPages[iPage]));
1604 }
1605 *ppvR0 = RTR0MemObjAddress(Mem.MemObj);
1606 *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3);
1607 return 0;
1608 }
1609
1610 rc2 = RTR0MemObjFree(Mem.MapObjR3, false);
1611 AssertRC(rc2);
1612 }
1613
1614 rc2 = RTR0MemObjFree(Mem.MemObj, false);
1615 AssertRC(rc2);
1616 }
1617
1618 return rc;
1619}
1620
1621
1622/**
1623 * Frees memory allocated using SUPR0LowAlloc().
1624 *
1625 * @returns IPRT status code.
1626 * @param pSession The session to which the memory was allocated.
1627 * @param uPtr Pointer to the memory (ring-3 or ring-0).
1628 */
1629SUPR0DECL(int) SUPR0LowFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr)
1630{
1631 dprintf(("SUPR0LowFree: pSession=%p uPtr=%p\n", pSession, (void *)uPtr));
1632 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1633 return supdrvMemRelease(pSession, uPtr, MEMREF_TYPE_LOW);
1634}
1635
1636
1637
1638/**
1639 * Allocates a chunk of memory with both R0 and R3 mappings.
1640 * The memory is fixed and it's possible to query the physical addresses using SUPR0MemGetPhys().
1641 *
1642 * @returns IPRT status code.
1643 * @param pSession The session to associated the allocation with.
1644 * @param cb Number of bytes to allocate.
1645 * @param ppvR0 Where to store the address of the Ring-0 mapping.
1646 * @param ppvR3 Where to store the address of the Ring-3 mapping.
1647 */
1648SUPR0DECL(int) SUPR0MemAlloc(PSUPDRVSESSION pSession, uint32_t cb, PRTR0PTR ppvR0, PRTR3PTR ppvR3)
1649{
1650 int rc;
1651 SUPDRVMEMREF Mem = {0};
1652 dprintf(("SUPR0MemAlloc: pSession=%p cb=%d ppvR0=%p ppvR3=%p\n", pSession, cb, ppvR0, ppvR3));
1653
1654 /*
1655 * Validate input.
1656 */
1657 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1658 AssertPtrReturn(ppvR0, VERR_INVALID_POINTER);
1659 AssertPtrReturn(ppvR3, VERR_INVALID_POINTER);
1660 if (cb < 1 || cb >= _4M)
1661 {
1662 dprintf(("Illegal request cb=%u; must be greater than 0 and smaller than 4MB.\n", cb));
1663 return VERR_INVALID_PARAMETER;
1664 }
1665
1666 /*
1667 * Let IPRT do the work.
1668 */
1669 rc = RTR0MemObjAllocPage(&Mem.MemObj, cb, true /* executable ring-0 mapping */);
1670 if (RT_SUCCESS(rc))
1671 {
1672 int rc2;
1673 rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0,
1674 RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, RTR0ProcHandleSelf());
1675 if (RT_SUCCESS(rc))
1676 {
1677 Mem.eType = MEMREF_TYPE_MEM;
1678 rc = supdrvMemAdd(&Mem, pSession);
1679 if (!rc)
1680 {
1681 *ppvR0 = RTR0MemObjAddress(Mem.MemObj);
1682 *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3);
1683 return VINF_SUCCESS;
1684 }
1685 rc2 = RTR0MemObjFree(Mem.MapObjR3, false);
1686 AssertRC(rc2);
1687 }
1688
1689 rc2 = RTR0MemObjFree(Mem.MemObj, false);
1690 AssertRC(rc2);
1691 }
1692
1693 return rc;
1694}
1695
1696
1697/**
1698 * Get the physical addresses of memory allocated using SUPR0MemAlloc().
1699 *
1700 * @returns IPRT status code.
1701 * @param pSession The session to which the memory was allocated.
1702 * @param uPtr The Ring-0 or Ring-3 address returned by SUPR0MemAlloc().
1703 * @param paPages Where to store the physical addresses.
1704 */
1705SUPR0DECL(int) SUPR0MemGetPhys(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr, PSUPPAGE paPages) /** @todo switch this bugger to RTHCPHYS */
1706{
1707 PSUPDRVBUNDLE pBundle;
1708 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
1709 dprintf(("SUPR0MemGetPhys: pSession=%p uPtr=%p paPages=%p\n", pSession, (void *)uPtr, paPages));
1710
1711 /*
1712 * Validate input.
1713 */
1714 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1715 AssertPtrReturn(paPages, VERR_INVALID_POINTER);
1716 AssertReturn(uPtr, VERR_INVALID_PARAMETER);
1717
1718 /*
1719 * Search for the address.
1720 */
1721 RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp);
1722 for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
1723 {
1724 if (pBundle->cUsed > 0)
1725 {
1726 unsigned i;
1727 for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
1728 {
1729 if ( pBundle->aMem[i].eType == MEMREF_TYPE_MEM
1730 && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ
1731 && ( (RTHCUINTPTR)RTR0MemObjAddress(pBundle->aMem[i].MemObj) == uPtr
1732 || ( pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ
1733 && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == uPtr)
1734 )
1735 )
1736 {
1737 const unsigned cPages = RTR0MemObjSize(pBundle->aMem[i].MemObj) >> PAGE_SHIFT;
1738 unsigned iPage;
1739 for (iPage = 0; iPage < cPages; iPage++)
1740 {
1741 paPages[iPage].Phys = RTR0MemObjGetPagePhysAddr(pBundle->aMem[i].MemObj, iPage);
1742 paPages[iPage].uReserved = 0;
1743 }
1744 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
1745 return VINF_SUCCESS;
1746 }
1747 }
1748 }
1749 }
1750 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
1751 dprintf(("Failed to find %p!!!\n", (void *)uPtr));
1752 return VERR_INVALID_PARAMETER;
1753}
1754
1755
1756/**
1757 * Free memory allocated by SUPR0MemAlloc().
1758 *
1759 * @returns IPRT status code.
1760 * @param pSession The session owning the allocation.
1761 * @param uPtr The Ring-0 or Ring-3 address returned by SUPR0MemAlloc().
1762 */
1763SUPR0DECL(int) SUPR0MemFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr)
1764{
1765 dprintf(("SUPR0MemFree: pSession=%p uPtr=%p\n", pSession, (void *)uPtr));
1766 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1767 return supdrvMemRelease(pSession, uPtr, MEMREF_TYPE_MEM);
1768}
1769
1770
1771/**
1772 * Allocates a chunk of memory with only a R3 mappings.
1773 * The memory is fixed and it's possible to query the physical addresses using SUPR0MemGetPhys().
1774 *
1775 * @returns IPRT status code.
1776 * @param pSession The session to associated the allocation with.
1777 * @param cPages The number of pages to allocate.
1778 * @param ppvR3 Where to store the address of the Ring-3 mapping.
1779 * @param paPages Where to store the addresses of the pages. Optional.
1780 */
1781SUPR0DECL(int) SUPR0PageAlloc(PSUPDRVSESSION pSession, uint32_t cPages, PRTR3PTR ppvR3, PRTHCPHYS paPages)
1782{
1783 int rc;
1784 SUPDRVMEMREF Mem = {0};
1785 dprintf(("SUPR0PageAlloc: pSession=%p cb=%d ppvR3=%p\n", pSession, cPages, ppvR3));
1786
1787 /*
1788 * Validate input. The allowed allocation size must be at least equal to the maximum guest VRAM size.
1789 */
1790 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1791 AssertPtrReturn(ppvR3, VERR_INVALID_POINTER);
1792 if (cPages < 1 || cPages > (128 * _1M)/PAGE_SIZE)
1793 {
1794 dprintf(("SUPR0PageAlloc: Illegal request cb=%u; must be greater than 0 and smaller than 128MB.\n", cPages));
1795 return VERR_INVALID_PARAMETER;
1796 }
1797
1798 /*
1799 * Let IPRT do the work.
1800 */
1801 rc = RTR0MemObjAllocPhysNC(&Mem.MemObj, (size_t)cPages * PAGE_SIZE, NIL_RTHCPHYS);
1802 if (RT_SUCCESS(rc))
1803 {
1804 int rc2;
1805 rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0,
1806 RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, RTR0ProcHandleSelf());
1807 if (RT_SUCCESS(rc))
1808 {
1809 Mem.eType = MEMREF_TYPE_LOCKED_SUP;
1810 rc = supdrvMemAdd(&Mem, pSession);
1811 if (!rc)
1812 {
1813 *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3);
1814 if (paPages)
1815 {
1816 uint32_t iPage = cPages;
1817 while (iPage-- > 0)
1818 {
1819 paPages[iPage] = RTR0MemObjGetPagePhysAddr(Mem.MapObjR3, iPage);
1820 Assert(paPages[iPage] != NIL_RTHCPHYS);
1821 }
1822 }
1823 return VINF_SUCCESS;
1824 }
1825 rc2 = RTR0MemObjFree(Mem.MapObjR3, false);
1826 AssertRC(rc2);
1827 }
1828
1829 rc2 = RTR0MemObjFree(Mem.MemObj, false);
1830 AssertRC(rc2);
1831 }
1832 return rc;
1833}
1834
1835
1836#ifdef RT_OS_WINDOWS
1837/**
1838 * Check if the pages were locked by SUPR0PageAlloc
1839 *
1840 * This function will be removed along with the lock/unlock hacks when
1841 * we've cleaned up the ring-3 code properly.
1842 *
1843 * @returns boolean
1844 * @param pSession The session to which the memory was allocated.
1845 * @param pvR3 The Ring-3 address returned by SUPR0PageAlloc().
1846 */
1847static bool supdrvPageWasLockedByPageAlloc(PSUPDRVSESSION pSession, RTR3PTR pvR3)
1848{
1849 PSUPDRVBUNDLE pBundle;
1850 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
1851 dprintf(("SUPR0PageIsLockedByPageAlloc: pSession=%p pvR3=%p\n", pSession, (void *)pvR3));
1852
1853 /*
1854 * Search for the address.
1855 */
1856 RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp);
1857 for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
1858 {
1859 if (pBundle->cUsed > 0)
1860 {
1861 unsigned i;
1862 for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
1863 {
1864 if ( pBundle->aMem[i].eType == MEMREF_TYPE_LOCKED_SUP
1865 && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ
1866 && pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ
1867 && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == pvR3)
1868 {
1869 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
1870 return true;
1871 }
1872 }
1873 }
1874 }
1875 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
1876 return false;
1877}
1878
1879
1880/**
1881 * Get the physical addresses of memory allocated using SUPR0PageAlloc().
1882 *
1883 * This function will be removed along with the lock/unlock hacks when
1884 * we've cleaned up the ring-3 code properly.
1885 *
1886 * @returns IPRT status code.
1887 * @param pSession The session to which the memory was allocated.
1888 * @param pvR3 The Ring-3 address returned by SUPR0PageAlloc().
1889 * @param cPages Number of pages in paPages
1890 * @param paPages Where to store the physical addresses.
1891 */
1892static int supdrvPageGetPhys(PSUPDRVSESSION pSession, RTR3PTR pvR3, uint32_t cPages, PRTHCPHYS paPages)
1893{
1894 PSUPDRVBUNDLE pBundle;
1895 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
1896 dprintf(("supdrvPageGetPhys: pSession=%p pvR3=%p cPages=%#lx paPages=%p\n", pSession, (void *)pvR3, (long)cPages, paPages));
1897
1898 /*
1899 * Search for the address.
1900 */
1901 RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp);
1902 for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
1903 {
1904 if (pBundle->cUsed > 0)
1905 {
1906 unsigned i;
1907 for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
1908 {
1909 if ( pBundle->aMem[i].eType == MEMREF_TYPE_LOCKED_SUP
1910 && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ
1911 && pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ
1912 && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == pvR3)
1913 {
1914 uint32_t iPage = RTR0MemObjSize(pBundle->aMem[i].MemObj) >> PAGE_SHIFT;
1915 cPages = RT_MIN(iPage, cPages);
1916 for (iPage = 0; iPage < cPages; iPage++)
1917 paPages[iPage] = RTR0MemObjGetPagePhysAddr(pBundle->aMem[i].MemObj, iPage);
1918 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
1919 return VINF_SUCCESS;
1920 }
1921 }
1922 }
1923 }
1924 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
1925 return VERR_INVALID_PARAMETER;
1926}
1927#endif /* RT_OS_WINDOWS */
1928
1929
1930/**
1931 * Free memory allocated by SUPR0PageAlloc().
1932 *
1933 * @returns IPRT status code.
1934 * @param pSession The session owning the allocation.
1935 * @param pvR3 The Ring-3 address returned by SUPR0PageAlloc().
1936 */
1937SUPR0DECL(int) SUPR0PageFree(PSUPDRVSESSION pSession, RTR3PTR pvR3)
1938{
1939 dprintf(("SUPR0PageFree: pSession=%p pvR3=%p\n", pSession, (void *)pvR3));
1940 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1941 return supdrvMemRelease(pSession, (RTHCUINTPTR)pvR3, MEMREF_TYPE_LOCKED_SUP);
1942}
1943
1944
1945/**
1946 * Maps the GIP into userspace and/or get the physical address of the GIP.
1947 *
1948 * @returns IPRT status code.
1949 * @param pSession Session to which the GIP mapping should belong.
1950 * @param ppGipR3 Where to store the address of the ring-3 mapping. (optional)
1951 * @param pHCPhysGip Where to store the physical address. (optional)
1952 *
1953 * @remark There is no reference counting on the mapping, so one call to this function
1954 * count globally as one reference. One call to SUPR0GipUnmap() is will unmap GIP
1955 * and remove the session as a GIP user.
1956 */
1957SUPR0DECL(int) SUPR0GipMap(PSUPDRVSESSION pSession, PRTR3PTR ppGipR3, PRTHCPHYS pHCPhysGip)
1958{
1959 int rc = 0;
1960 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
1961 RTR3PTR pGip = NIL_RTR3PTR;
1962 RTHCPHYS HCPhys = NIL_RTHCPHYS;
1963 dprintf(("SUPR0GipMap: pSession=%p ppGipR3=%p pHCPhysGip=%p\n", pSession, ppGipR3, pHCPhysGip));
1964
1965 /*
1966 * Validate
1967 */
1968 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1969 AssertPtrNullReturn(ppGipR3, VERR_INVALID_POINTER);
1970 AssertPtrNullReturn(pHCPhysGip, VERR_INVALID_POINTER);
1971
1972 RTSemFastMutexRequest(pDevExt->mtxGip);
1973 if (pDevExt->pGip)
1974 {
1975 /*
1976 * Map it?
1977 */
1978 if (ppGipR3)
1979 {
1980#ifdef USE_NEW_OS_INTERFACE_FOR_GIP
1981 if (pSession->GipMapObjR3 == NIL_RTR0MEMOBJ)
1982 rc = RTR0MemObjMapUser(&pSession->GipMapObjR3, pDevExt->GipMemObj, (RTR3PTR)-1, 0,
1983 RTMEM_PROT_READ, RTR0ProcHandleSelf());
1984 if (RT_SUCCESS(rc))
1985 {
1986 pGip = RTR0MemObjAddressR3(pSession->GipMapObjR3);
1987 rc = VINF_SUCCESS; /** @todo remove this and replace the !rc below with RT_SUCCESS(rc). */
1988 }
1989#else /* !USE_NEW_OS_INTERFACE_FOR_GIP */
1990 if (!pSession->pGip)
1991 rc = supdrvOSGipMap(pSession->pDevExt, &pSession->pGip);
1992 if (!rc)
1993 pGip = (RTR3PTR)pSession->pGip;
1994#endif /* !USE_NEW_OS_INTERFACE_FOR_GIP */
1995 }
1996
1997 /*
1998 * Get physical address.
1999 */
2000 if (pHCPhysGip && !rc)
2001 HCPhys = pDevExt->HCPhysGip;
2002
2003 /*
2004 * Reference globally.
2005 */
2006 if (!pSession->fGipReferenced && !rc)
2007 {
2008 pSession->fGipReferenced = 1;
2009 pDevExt->cGipUsers++;
2010 if (pDevExt->cGipUsers == 1)
2011 {
2012 PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
2013 unsigned i;
2014
2015 dprintf(("SUPR0GipMap: Resumes GIP updating\n"));
2016
2017 for (i = 0; i < RT_ELEMENTS(pGip->aCPUs); i++)
2018 ASMAtomicXchgU32(&pGip->aCPUs[i].u32TransactionId, pGip->aCPUs[i].u32TransactionId & ~(GIP_UPDATEHZ_RECALC_FREQ * 2 - 1));
2019 ASMAtomicXchgU64(&pGip->u64NanoTSLastUpdateHz, 0);
2020
2021#ifdef USE_NEW_OS_INTERFACE_FOR_GIP
2022 rc = RTTimerStart(pDevExt->pGipTimer, 0);
2023 AssertRC(rc); rc = VINF_SUCCESS;
2024#else
2025 supdrvOSGipResume(pDevExt);
2026#endif
2027 }
2028 }
2029 }
2030 else
2031 {
2032 rc = SUPDRV_ERR_GENERAL_FAILURE;
2033 dprintf(("SUPR0GipMap: GIP is not available!\n"));
2034 }
2035 RTSemFastMutexRelease(pDevExt->mtxGip);
2036
2037 /*
2038 * Write returns.
2039 */
2040 if (pHCPhysGip)
2041 *pHCPhysGip = HCPhys;
2042 if (ppGipR3)
2043 *ppGipR3 = pGip;
2044
2045#ifdef DEBUG_DARWIN_GIP
2046 OSDBGPRINT(("SUPR0GipMap: returns %d *pHCPhysGip=%lx *ppGip=%p GipMapObjR3\n", rc, (unsigned long)HCPhys, pGip, pSession->GipMapObjR3));
2047#else
2048 dprintf(("SUPR0GipMap: returns %d *pHCPhysGip=%lx *ppGipR3=%p\n", rc, (unsigned long)HCPhys, (void *)(uintptr_t)pGip));
2049#endif
2050 return rc;
2051}
2052
2053
2054/**
2055 * Unmaps any user mapping of the GIP and terminates all GIP access
2056 * from this session.
2057 *
2058 * @returns IPRT status code.
2059 * @param pSession Session to which the GIP mapping should belong.
2060 */
2061SUPR0DECL(int) SUPR0GipUnmap(PSUPDRVSESSION pSession)
2062{
2063 int rc = VINF_SUCCESS;
2064 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
2065#ifdef DEBUG_DARWIN_GIP
2066 OSDBGPRINT(("SUPR0GipUnmap: pSession=%p pGip=%p GipMapObjR3=%p\n",
2067 pSession,
2068 pSession->GipMapObjR3 != NIL_RTR0MEMOBJ ? RTR0MemObjAddress(pSession->GipMapObjR3) : NULL,
2069 pSession->GipMapObjR3));
2070#else
2071 dprintf(("SUPR0GipUnmap: pSession=%p\n", pSession));
2072#endif
2073 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
2074
2075 RTSemFastMutexRequest(pDevExt->mtxGip);
2076
2077 /*
2078 * Unmap anything?
2079 */
2080#ifdef USE_NEW_OS_INTERFACE_FOR_GIP
2081 if (pSession->GipMapObjR3 != NIL_RTR0MEMOBJ)
2082 {
2083 rc = RTR0MemObjFree(pSession->GipMapObjR3, false);
2084 AssertRC(rc);
2085 if (RT_SUCCESS(rc))
2086 pSession->GipMapObjR3 = NIL_RTR0MEMOBJ;
2087 }
2088#else
2089 if (pSession->pGip)
2090 {
2091 rc = supdrvOSGipUnmap(pDevExt, pSession->pGip);
2092 if (!rc)
2093 pSession->pGip = NULL;
2094 }
2095#endif
2096
2097 /*
2098 * Dereference global GIP.
2099 */
2100 if (pSession->fGipReferenced && !rc)
2101 {
2102 pSession->fGipReferenced = 0;
2103 if ( pDevExt->cGipUsers > 0
2104 && !--pDevExt->cGipUsers)
2105 {
2106 dprintf(("SUPR0GipUnmap: Suspends GIP updating\n"));
2107#ifdef USE_NEW_OS_INTERFACE_FOR_GIP
2108 rc = RTTimerStop(pDevExt->pGipTimer); AssertRC(rc); rc = 0;
2109#else
2110 supdrvOSGipSuspend(pDevExt);
2111#endif
2112 }
2113 }
2114
2115 RTSemFastMutexRelease(pDevExt->mtxGip);
2116
2117 return rc;
2118}
2119
2120
2121/**
2122 * Adds a memory object to the session.
2123 *
2124 * @returns IPRT status code.
2125 * @param pMem Memory tracking structure containing the
2126 * information to track.
2127 * @param pSession The session.
2128 */
2129static int supdrvMemAdd(PSUPDRVMEMREF pMem, PSUPDRVSESSION pSession)
2130{
2131 PSUPDRVBUNDLE pBundle;
2132 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
2133
2134 /*
2135 * Find free entry and record the allocation.
2136 */
2137 RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp);
2138 for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
2139 {
2140 if (pBundle->cUsed < RT_ELEMENTS(pBundle->aMem))
2141 {
2142 unsigned i;
2143 for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
2144 {
2145 if (pBundle->aMem[i].MemObj == NIL_RTR0MEMOBJ)
2146 {
2147 pBundle->cUsed++;
2148 pBundle->aMem[i] = *pMem;
2149 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2150 return VINF_SUCCESS;
2151 }
2152 }
2153 AssertFailed(); /* !!this can't be happening!!! */
2154 }
2155 }
2156 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2157
2158 /*
2159 * Need to allocate a new bundle.
2160 * Insert into the last entry in the bundle.
2161 */
2162 pBundle = (PSUPDRVBUNDLE)RTMemAllocZ(sizeof(*pBundle));
2163 if (!pBundle)
2164 return VERR_NO_MEMORY;
2165
2166 /* take last entry. */
2167 pBundle->cUsed++;
2168 pBundle->aMem[RT_ELEMENTS(pBundle->aMem) - 1] = *pMem;
2169
2170 /* insert into list. */
2171 RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp);
2172 pBundle->pNext = pSession->Bundle.pNext;
2173 pSession->Bundle.pNext = pBundle;
2174 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2175
2176 return VINF_SUCCESS;
2177}
2178
2179
2180/**
2181 * Releases a memory object referenced by pointer and type.
2182 *
2183 * @returns IPRT status code.
2184 * @param pSession Session data.
2185 * @param uPtr Pointer to memory. This is matched against both the R0 and R3 addresses.
2186 * @param eType Memory type.
2187 */
2188static int supdrvMemRelease(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr, SUPDRVMEMREFTYPE eType)
2189{
2190 PSUPDRVBUNDLE pBundle;
2191 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
2192
2193 /*
2194 * Validate input.
2195 */
2196 if (!uPtr)
2197 {
2198 dprintf(("Illegal address %p\n", (void *)uPtr));
2199 return VERR_INVALID_PARAMETER;
2200 }
2201
2202 /*
2203 * Search for the address.
2204 */
2205 RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp);
2206 for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
2207 {
2208 if (pBundle->cUsed > 0)
2209 {
2210 unsigned i;
2211 for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
2212 {
2213 if ( pBundle->aMem[i].eType == eType
2214 && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ
2215 && ( (RTHCUINTPTR)RTR0MemObjAddress(pBundle->aMem[i].MemObj) == uPtr
2216 || ( pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ
2217 && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == uPtr))
2218 )
2219 {
2220 /* Make a copy of it and release it outside the spinlock. */
2221 SUPDRVMEMREF Mem = pBundle->aMem[i];
2222 pBundle->aMem[i].eType = MEMREF_TYPE_UNUSED;
2223 pBundle->aMem[i].MemObj = NIL_RTR0MEMOBJ;
2224 pBundle->aMem[i].MapObjR3 = NIL_RTR0MEMOBJ;
2225 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2226
2227 if (Mem.MapObjR3)
2228 {
2229 int rc = RTR0MemObjFree(Mem.MapObjR3, false);
2230 AssertRC(rc); /** @todo figure out how to handle this. */
2231 }
2232 if (Mem.MemObj)
2233 {
2234 int rc = RTR0MemObjFree(Mem.MemObj, false);
2235 AssertRC(rc); /** @todo figure out how to handle this. */
2236 }
2237 return VINF_SUCCESS;
2238 }
2239 }
2240 }
2241 }
2242 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2243 dprintf(("Failed to find %p!!! (eType=%d)\n", (void *)uPtr, eType));
2244 return VERR_INVALID_PARAMETER;
2245}
2246
2247
2248#ifdef VBOX_WITH_IDT_PATCHING
2249/**
2250 * Install IDT for the current CPU.
2251 *
2252 * @returns One of the following IPRT status codes:
2253 * @retval VINF_SUCCESS on success.
2254 * @retval VERR_IDT_FAILED.
2255 * @retval VERR_NO_MEMORY.
2256 * @param pDevExt The device extension.
2257 * @param pSession The session data.
2258 * @param pReq The request.
2259 */
2260static int supdrvIOCtl_IdtInstall(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPIDTINSTALL pReq)
2261{
2262 PSUPDRVPATCHUSAGE pUsagePre;
2263 PSUPDRVPATCH pPatchPre;
2264 RTIDTR Idtr;
2265 PSUPDRVPATCH pPatch;
2266 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
2267 dprintf(("supdrvIOCtl_IdtInstall\n"));
2268
2269 /*
2270 * Preallocate entry for this CPU cause we don't wanna do
2271 * that inside the spinlock!
2272 */
2273 pUsagePre = (PSUPDRVPATCHUSAGE)RTMemAlloc(sizeof(*pUsagePre));
2274 if (!pUsagePre)
2275 return VERR_NO_MEMORY;
2276
2277 /*
2278 * Take the spinlock and see what we need to do.
2279 */
2280 RTSpinlockAcquireNoInts(pDevExt->Spinlock, &SpinlockTmp);
2281
2282 /* check if we already got a free patch. */
2283 if (!pDevExt->pIdtPatchesFree)
2284 {
2285 /*
2286 * Allocate a patch - outside the spinlock of course.
2287 */
2288 RTSpinlockReleaseNoInts(pDevExt->Spinlock, &SpinlockTmp);
2289
2290 pPatchPre = (PSUPDRVPATCH)RTMemExecAlloc(sizeof(*pPatchPre));
2291 if (!pPatchPre)
2292 return VERR_NO_MEMORY;
2293
2294 RTSpinlockAcquireNoInts(pDevExt->Spinlock, &SpinlockTmp);
2295 }
2296 else
2297 {
2298 pPatchPre = pDevExt->pIdtPatchesFree;
2299 pDevExt->pIdtPatchesFree = pPatchPre->pNext;
2300 }
2301
2302 /* look for matching patch entry */
2303 ASMGetIDTR(&Idtr);
2304 pPatch = pDevExt->pIdtPatches;
2305 while (pPatch && pPatch->pvIdt != (void *)Idtr.pIdt)
2306 pPatch = pPatch->pNext;
2307
2308 if (!pPatch)
2309 {
2310 /*
2311 * Create patch.
2312 */
2313 pPatch = supdrvIdtPatchOne(pDevExt, pPatchPre);
2314 if (pPatch)
2315 pPatchPre = NULL; /* mark as used. */
2316 }
2317 else
2318 {
2319 /*
2320 * Simply increment patch usage.
2321 */
2322 pPatch->cUsage++;
2323 }
2324
2325 if (pPatch)
2326 {
2327 /*
2328 * Increment and add if need be the session usage record for this patch.
2329 */
2330 PSUPDRVPATCHUSAGE pUsage = pSession->pPatchUsage;
2331 while (pUsage && pUsage->pPatch != pPatch)
2332 pUsage = pUsage->pNext;
2333
2334 if (!pUsage)
2335 {
2336 /*
2337 * Add usage record.
2338 */
2339 pUsagePre->cUsage = 1;
2340 pUsagePre->pPatch = pPatch;
2341 pUsagePre->pNext = pSession->pPatchUsage;
2342 pSession->pPatchUsage = pUsagePre;
2343 pUsagePre = NULL; /* mark as used. */
2344 }
2345 else
2346 {
2347 /*
2348 * Increment usage count.
2349 */
2350 pUsage->cUsage++;
2351 }
2352 }
2353
2354 /* free patch - we accumulate them for paranoid saftly reasons. */
2355 if (pPatchPre)
2356 {
2357 pPatchPre->pNext = pDevExt->pIdtPatchesFree;
2358 pDevExt->pIdtPatchesFree = pPatchPre;
2359 }
2360
2361 RTSpinlockReleaseNoInts(pDevExt->Spinlock, &SpinlockTmp);
2362
2363 /*
2364 * Free unused preallocated buffers.
2365 */
2366 if (pUsagePre)
2367 RTMemFree(pUsagePre);
2368
2369 pReq->u.Out.u8Idt = pDevExt->u8Idt;
2370
2371 return pPatch ? VINF_SUCCESS : VERR_IDT_FAILED;
2372}
2373
2374
2375/**
2376 * This creates a IDT patch entry.
2377 * If the first patch being installed it'll also determin the IDT entry
2378 * to use.
2379 *
2380 * @returns pPatch on success.
2381 * @returns NULL on failure.
2382 * @param pDevExt Pointer to globals.
2383 * @param pPatch Patch entry to use.
2384 * This will be linked into SUPDRVDEVEXT::pIdtPatches on
2385 * successful return.
2386 * @remark Call must be owning the SUPDRVDEVEXT::Spinlock!
2387 */
2388static PSUPDRVPATCH supdrvIdtPatchOne(PSUPDRVDEVEXT pDevExt, PSUPDRVPATCH pPatch)
2389{
2390 RTIDTR Idtr;
2391 PSUPDRVIDTE paIdt;
2392 dprintf(("supdrvIOCtl_IdtPatchOne: pPatch=%p\n", pPatch));
2393
2394 /*
2395 * Get IDT.
2396 */
2397 ASMGetIDTR(&Idtr);
2398 paIdt = (PSUPDRVIDTE)Idtr.pIdt;
2399 /*
2400 * Recent Linux kernels can be configured to 1G user /3G kernel.
2401 */
2402 if ((uintptr_t)paIdt < 0x40000000)
2403 {
2404 AssertMsgFailed(("bad paIdt=%p\n", paIdt));
2405 return NULL;
2406 }
2407
2408 if (!pDevExt->u8Idt)
2409 {
2410 /*
2411 * Test out the alternatives.
2412 *
2413 * At the moment we do not support chaining thus we ASSUME that one of
2414 * these 48 entries is unused (which is not a problem on Win32 and
2415 * Linux to my knowledge).
2416 */
2417 /** @todo we MUST change this detection to try grab an entry which is NOT in use. This can be
2418 * combined with gathering info about which guest system call gates we can hook up directly. */
2419 unsigned i;
2420 uint8_t u8Idt = 0;
2421 static uint8_t au8Ints[] =
2422 {
2423#ifdef RT_OS_WINDOWS /* We don't use 0xef and above because they are system stuff on linux (ef is IPI,
2424 * local apic timer, or some other frequently fireing thing). */
2425 0xef, 0xee, 0xed, 0xec,
2426#endif
2427 0xeb, 0xea, 0xe9, 0xe8,
2428 0xdf, 0xde, 0xdd, 0xdc,
2429 0x7b, 0x7a, 0x79, 0x78,
2430 0xbf, 0xbe, 0xbd, 0xbc,
2431 };
2432#if defined(RT_ARCH_AMD64) && defined(DEBUG)
2433 static int s_iWobble = 0;
2434 unsigned iMax = !(s_iWobble++ % 2) ? 0x80 : 0x100;
2435 dprintf(("IDT: Idtr=%p:%#x\n", (void *)Idtr.pIdt, (unsigned)Idtr.cbIdt));
2436 for (i = iMax - 0x80; i*16+15 < Idtr.cbIdt && i < iMax; i++)
2437 {
2438 dprintf(("%#x: %04x:%08x%04x%04x P=%d DPL=%d IST=%d Type1=%#x u32Reserved=%#x u5Reserved=%#x\n",
2439 i, paIdt[i].u16SegSel, paIdt[i].u32OffsetTop, paIdt[i].u16OffsetHigh, paIdt[i].u16OffsetLow,
2440 paIdt[i].u1Present, paIdt[i].u2DPL, paIdt[i].u3IST, paIdt[i].u5Type2,
2441 paIdt[i].u32Reserved, paIdt[i].u5Reserved));
2442 }
2443#endif
2444 /* look for entries which are not present or otherwise unused. */
2445 for (i = 0; i < sizeof(au8Ints) / sizeof(au8Ints[0]); i++)
2446 {
2447 u8Idt = au8Ints[i];
2448 if ( u8Idt * sizeof(SUPDRVIDTE) < Idtr.cbIdt
2449 && ( !paIdt[u8Idt].u1Present
2450 || paIdt[u8Idt].u5Type2 == 0))
2451 break;
2452 u8Idt = 0;
2453 }
2454 if (!u8Idt)
2455 {
2456 /* try again, look for a compatible entry .*/
2457 for (i = 0; i < sizeof(au8Ints) / sizeof(au8Ints[0]); i++)
2458 {
2459 u8Idt = au8Ints[i];
2460 if ( u8Idt * sizeof(SUPDRVIDTE) < Idtr.cbIdt
2461 && paIdt[u8Idt].u1Present
2462 && paIdt[u8Idt].u5Type2 == SUPDRV_IDTE_TYPE2_INTERRUPT_GATE
2463 && !(paIdt[u8Idt].u16SegSel & 3))
2464 break;
2465 u8Idt = 0;
2466 }
2467 if (!u8Idt)
2468 {
2469 dprintf(("Failed to find appropirate IDT entry!!\n"));
2470 return NULL;
2471 }
2472 }
2473 pDevExt->u8Idt = u8Idt;
2474 dprintf(("supdrvIOCtl_IdtPatchOne: u8Idt=%x\n", u8Idt));
2475 }
2476
2477 /*
2478 * Prepare the patch
2479 */
2480 memset(pPatch, 0, sizeof(*pPatch));
2481 pPatch->pvIdt = paIdt;
2482 pPatch->cUsage = 1;
2483 pPatch->pIdtEntry = &paIdt[pDevExt->u8Idt];
2484 pPatch->SavedIdt = paIdt[pDevExt->u8Idt];
2485 pPatch->ChangedIdt.u16OffsetLow = (uint32_t)((uintptr_t)&pPatch->auCode[0] & 0xffff);
2486 pPatch->ChangedIdt.u16OffsetHigh = (uint32_t)((uintptr_t)&pPatch->auCode[0] >> 16);
2487#ifdef RT_ARCH_AMD64
2488 pPatch->ChangedIdt.u32OffsetTop = (uint32_t)((uintptr_t)&pPatch->auCode[0] >> 32);
2489#endif
2490 pPatch->ChangedIdt.u16SegSel = ASMGetCS();
2491#ifdef RT_ARCH_AMD64
2492 pPatch->ChangedIdt.u3IST = 0;
2493 pPatch->ChangedIdt.u5Reserved = 0;
2494#else /* x86 */
2495 pPatch->ChangedIdt.u5Reserved = 0;
2496 pPatch->ChangedIdt.u3Type1 = 0;
2497#endif /* x86 */
2498 pPatch->ChangedIdt.u5Type2 = SUPDRV_IDTE_TYPE2_INTERRUPT_GATE;
2499 pPatch->ChangedIdt.u2DPL = 3;
2500 pPatch->ChangedIdt.u1Present = 1;
2501
2502 /*
2503 * Generate the patch code.
2504 */
2505 {
2506#ifdef RT_ARCH_AMD64
2507 union
2508 {
2509 uint8_t *pb;
2510 uint32_t *pu32;
2511 uint64_t *pu64;
2512 } u, uFixJmp, uFixCall, uNotNested;
2513 u.pb = &pPatch->auCode[0];
2514
2515 /* check the cookie */
2516 *u.pb++ = 0x3d; // cmp eax, GLOBALCOOKIE
2517 *u.pu32++ = pDevExt->u32Cookie;
2518
2519 *u.pb++ = 0x74; // jz @VBoxCall
2520 *u.pb++ = 2;
2521
2522 /* jump to forwarder code. */
2523 *u.pb++ = 0xeb;
2524 uFixJmp = u;
2525 *u.pb++ = 0xfe;
2526
2527 // @VBoxCall:
2528 *u.pb++ = 0x0f; // swapgs
2529 *u.pb++ = 0x01;
2530 *u.pb++ = 0xf8;
2531
2532 /*
2533 * Call VMMR0Entry
2534 * We don't have to push the arguments here, but we have top
2535 * reserve some stack space for the interrupt forwarding.
2536 */
2537# ifdef RT_OS_WINDOWS
2538 *u.pb++ = 0x50; // push rax ; alignment filler.
2539 *u.pb++ = 0x41; // push r8 ; uArg
2540 *u.pb++ = 0x50;
2541 *u.pb++ = 0x52; // push rdx ; uOperation
2542 *u.pb++ = 0x51; // push rcx ; pVM
2543# else
2544 *u.pb++ = 0x51; // push rcx ; alignment filler.
2545 *u.pb++ = 0x52; // push rdx ; uArg
2546 *u.pb++ = 0x56; // push rsi ; uOperation
2547 *u.pb++ = 0x57; // push rdi ; pVM
2548# endif
2549
2550 *u.pb++ = 0xff; // call qword [pfnVMMR0EntryInt wrt rip]
2551 *u.pb++ = 0x15;
2552 uFixCall = u;
2553 *u.pu32++ = 0;
2554
2555 *u.pb++ = 0x48; // add rsp, 20h ; remove call frame.
2556 *u.pb++ = 0x81;
2557 *u.pb++ = 0xc4;
2558 *u.pu32++ = 0x20;
2559
2560 *u.pb++ = 0x0f; // swapgs
2561 *u.pb++ = 0x01;
2562 *u.pb++ = 0xf8;
2563
2564 /* Return to R3. */
2565 uNotNested = u;
2566 *u.pb++ = 0x48; // iretq
2567 *u.pb++ = 0xcf;
2568
2569 while ((uintptr_t)u.pb & 0x7) // align 8
2570 *u.pb++ = 0xcc;
2571
2572 /* Pointer to the VMMR0Entry. */ // pfnVMMR0EntryInt dq StubVMMR0Entry
2573 *uFixCall.pu32 = (uint32_t)(u.pb - uFixCall.pb - 4); uFixCall.pb = NULL;
2574 pPatch->offVMMR0EntryFixup = (uint16_t)(u.pb - &pPatch->auCode[0]);
2575 *u.pu64++ = pDevExt->pvVMMR0 ? (uint64_t)pDevExt->pfnVMMR0EntryInt : (uint64_t)u.pb + 8;
2576
2577 /* stub entry. */ // StubVMMR0Entry:
2578 pPatch->offStub = (uint16_t)(u.pb - &pPatch->auCode[0]);
2579 *u.pb++ = 0x33; // xor eax, eax
2580 *u.pb++ = 0xc0;
2581
2582 *u.pb++ = 0x48; // dec rax
2583 *u.pb++ = 0xff;
2584 *u.pb++ = 0xc8;
2585
2586 *u.pb++ = 0xc3; // ret
2587
2588 /* forward to the original handler using a retf. */
2589 *uFixJmp.pb = (uint8_t)(u.pb - uFixJmp.pb - 1); uFixJmp.pb = NULL;
2590
2591 *u.pb++ = 0x68; // push <target cs>
2592 *u.pu32++ = !pPatch->SavedIdt.u5Type2 ? ASMGetCS() : pPatch->SavedIdt.u16SegSel;
2593
2594 *u.pb++ = 0x68; // push <low target rip>
2595 *u.pu32++ = !pPatch->SavedIdt.u5Type2
2596 ? (uint32_t)(uintptr_t)uNotNested.pb
2597 : (uint32_t)pPatch->SavedIdt.u16OffsetLow
2598 | (uint32_t)pPatch->SavedIdt.u16OffsetHigh << 16;
2599
2600 *u.pb++ = 0xc7; // mov dword [rsp + 4], <high target rip>
2601 *u.pb++ = 0x44;
2602 *u.pb++ = 0x24;
2603 *u.pb++ = 0x04;
2604 *u.pu32++ = !pPatch->SavedIdt.u5Type2
2605 ? (uint32_t)((uint64_t)uNotNested.pb >> 32)
2606 : pPatch->SavedIdt.u32OffsetTop;
2607
2608 *u.pb++ = 0x48; // retf ; does this require prefix?
2609 *u.pb++ = 0xcb;
2610
2611#else /* RT_ARCH_X86 */
2612
2613 union
2614 {
2615 uint8_t *pb;
2616 uint16_t *pu16;
2617 uint32_t *pu32;
2618 } u, uFixJmpNotNested, uFixJmp, uFixCall, uNotNested;
2619 u.pb = &pPatch->auCode[0];
2620
2621 /* check the cookie */
2622 *u.pb++ = 0x81; // cmp esi, GLOBALCOOKIE
2623 *u.pb++ = 0xfe;
2624 *u.pu32++ = pDevExt->u32Cookie;
2625
2626 *u.pb++ = 0x74; // jz VBoxCall
2627 uFixJmp = u;
2628 *u.pb++ = 0;
2629
2630 /* jump (far) to the original handler / not-nested-stub. */
2631 *u.pb++ = 0xea; // jmp far NotNested
2632 uFixJmpNotNested = u;
2633 *u.pu32++ = 0;
2634 *u.pu16++ = 0;
2635
2636 /* save selector registers. */ // VBoxCall:
2637 *uFixJmp.pb = (uint8_t)(u.pb - uFixJmp.pb - 1);
2638 *u.pb++ = 0x0f; // push fs
2639 *u.pb++ = 0xa0;
2640
2641 *u.pb++ = 0x1e; // push ds
2642
2643 *u.pb++ = 0x06; // push es
2644
2645 /* call frame */
2646 *u.pb++ = 0x51; // push ecx
2647
2648 *u.pb++ = 0x52; // push edx
2649
2650 *u.pb++ = 0x50; // push eax
2651
2652 /* load ds, es and perhaps fs before call. */
2653 *u.pb++ = 0xb8; // mov eax, KernelDS
2654 *u.pu32++ = ASMGetDS();
2655
2656 *u.pb++ = 0x8e; // mov ds, eax
2657 *u.pb++ = 0xd8;
2658
2659 *u.pb++ = 0x8e; // mov es, eax
2660 *u.pb++ = 0xc0;
2661
2662#ifdef RT_OS_WINDOWS
2663 *u.pb++ = 0xb8; // mov eax, KernelFS
2664 *u.pu32++ = ASMGetFS();
2665
2666 *u.pb++ = 0x8e; // mov fs, eax
2667 *u.pb++ = 0xe0;
2668#endif
2669
2670 /* do the call. */
2671 *u.pb++ = 0xe8; // call _VMMR0Entry / StubVMMR0Entry
2672 uFixCall = u;
2673 pPatch->offVMMR0EntryFixup = (uint16_t)(u.pb - &pPatch->auCode[0]);
2674 *u.pu32++ = 0xfffffffb;
2675
2676 *u.pb++ = 0x83; // add esp, 0ch ; cdecl
2677 *u.pb++ = 0xc4;
2678 *u.pb++ = 0x0c;
2679
2680 /* restore selector registers. */
2681 *u.pb++ = 0x07; // pop es
2682 //
2683 *u.pb++ = 0x1f; // pop ds
2684
2685 *u.pb++ = 0x0f; // pop fs
2686 *u.pb++ = 0xa1;
2687
2688 uNotNested = u; // NotNested:
2689 *u.pb++ = 0xcf; // iretd
2690
2691 /* the stub VMMR0Entry. */ // StubVMMR0Entry:
2692 pPatch->offStub = (uint16_t)(u.pb - &pPatch->auCode[0]);
2693 *u.pb++ = 0x33; // xor eax, eax
2694 *u.pb++ = 0xc0;
2695
2696 *u.pb++ = 0x48; // dec eax
2697
2698 *u.pb++ = 0xc3; // ret
2699
2700 /* Fixup the VMMR0Entry call. */
2701 if (pDevExt->pvVMMR0)
2702 *uFixCall.pu32 = (uint32_t)pDevExt->pfnVMMR0EntryInt - (uint32_t)(uFixCall.pu32 + 1);
2703 else
2704 *uFixCall.pu32 = (uint32_t)&pPatch->auCode[pPatch->offStub] - (uint32_t)(uFixCall.pu32 + 1);
2705
2706 /* Fixup the forward / nested far jump. */
2707 if (!pPatch->SavedIdt.u5Type2)
2708 {
2709 *uFixJmpNotNested.pu32++ = (uint32_t)uNotNested.pb;
2710 *uFixJmpNotNested.pu16++ = ASMGetCS();
2711 }
2712 else
2713 {
2714 *uFixJmpNotNested.pu32++ = ((uint32_t)pPatch->SavedIdt.u16OffsetHigh << 16) | pPatch->SavedIdt.u16OffsetLow;
2715 *uFixJmpNotNested.pu16++ = pPatch->SavedIdt.u16SegSel;
2716 }
2717#endif /* RT_ARCH_X86 */
2718 Assert(u.pb <= &pPatch->auCode[sizeof(pPatch->auCode)]);
2719#if 0
2720 /* dump the patch code */
2721 dprintf(("patch code: %p\n", &pPatch->auCode[0]));
2722 for (uFixCall.pb = &pPatch->auCode[0]; uFixCall.pb < u.pb; uFixCall.pb++)
2723 dprintf(("0x%02x,\n", *uFixCall.pb));
2724#endif
2725 }
2726
2727 /*
2728 * Install the patch.
2729 */
2730 supdrvIdtWrite(pPatch->pIdtEntry, &pPatch->ChangedIdt);
2731 AssertMsg(!memcmp((void *)pPatch->pIdtEntry, &pPatch->ChangedIdt, sizeof(pPatch->ChangedIdt)), ("The stupid change code didn't work!!!!!\n"));
2732
2733 /*
2734 * Link in the patch.
2735 */
2736 pPatch->pNext = pDevExt->pIdtPatches;
2737 pDevExt->pIdtPatches = pPatch;
2738
2739 return pPatch;
2740}
2741
2742
2743/**
2744 * Removes the sessions IDT references.
2745 * This will uninstall our IDT patch if we left unreferenced.
2746 *
2747 * @returns VINF_SUCCESS.
2748 * @param pDevExt Device globals.
2749 * @param pSession Session data.
2750 */
2751static int supdrvIOCtl_IdtRemoveAll(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
2752{
2753 PSUPDRVPATCHUSAGE pUsage;
2754 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
2755 dprintf(("supdrvIOCtl_IdtRemoveAll: pSession=%p\n", pSession));
2756
2757 /*
2758 * Take the spinlock.
2759 */
2760 RTSpinlockAcquireNoInts(pDevExt->Spinlock, &SpinlockTmp);
2761
2762 /*
2763 * Walk usage list, removing patches as their usage count reaches zero.
2764 */
2765 pUsage = pSession->pPatchUsage;
2766 while (pUsage)
2767 {
2768 if (pUsage->pPatch->cUsage <= pUsage->cUsage)
2769 supdrvIdtRemoveOne(pDevExt, pUsage->pPatch);
2770 else
2771 pUsage->pPatch->cUsage -= pUsage->cUsage;
2772
2773 /* next */
2774 pUsage = pUsage->pNext;
2775 }
2776
2777 /*
2778 * Empty the usage chain and we're done inside the spinlock.
2779 */
2780 pUsage = pSession->pPatchUsage;
2781 pSession->pPatchUsage = NULL;
2782
2783 RTSpinlockReleaseNoInts(pDevExt->Spinlock, &SpinlockTmp);
2784
2785 /*
2786 * Free usage entries.
2787 */
2788 while (pUsage)
2789 {
2790 void *pvToFree = pUsage;
2791 pUsage->cUsage = 0;
2792 pUsage->pPatch = NULL;
2793 pUsage = pUsage->pNext;
2794 RTMemFree(pvToFree);
2795 }
2796
2797 return VINF_SUCCESS;
2798}
2799
2800
2801/**
2802 * Remove one patch.
2803 *
2804 * Worker for supdrvIOCtl_IdtRemoveAll.
2805 *
2806 * @param pDevExt Device globals.
2807 * @param pPatch Patch entry to remove.
2808 * @remark Caller must own SUPDRVDEVEXT::Spinlock!
2809 */
2810static void supdrvIdtRemoveOne(PSUPDRVDEVEXT pDevExt, PSUPDRVPATCH pPatch)
2811{
2812 dprintf(("supdrvIdtRemoveOne: pPatch=%p\n", pPatch));
2813
2814 pPatch->cUsage = 0;
2815
2816 /*
2817 * If the IDT entry was changed it have to kick around for ever!
2818 * This will be attempted freed again, perhaps next time we'll succeed :-)
2819 */
2820 if (memcmp((void *)pPatch->pIdtEntry, &pPatch->ChangedIdt, sizeof(pPatch->ChangedIdt)))
2821 {
2822 AssertMsgFailed(("The hijacked IDT entry has CHANGED!!!\n"));
2823 return;
2824 }
2825
2826 /*
2827 * Unlink it.
2828 */
2829 if (pDevExt->pIdtPatches != pPatch)
2830 {
2831 PSUPDRVPATCH pPatchPrev = pDevExt->pIdtPatches;
2832 while (pPatchPrev)
2833 {
2834 if (pPatchPrev->pNext == pPatch)
2835 {
2836 pPatchPrev->pNext = pPatch->pNext;
2837 break;
2838 }
2839 pPatchPrev = pPatchPrev->pNext;
2840 }
2841 Assert(!pPatchPrev);
2842 }
2843 else
2844 pDevExt->pIdtPatches = pPatch->pNext;
2845 pPatch->pNext = NULL;
2846
2847
2848 /*
2849 * Verify and restore the IDT.
2850 */
2851 AssertMsg(!memcmp((void *)pPatch->pIdtEntry, &pPatch->ChangedIdt, sizeof(pPatch->ChangedIdt)), ("The hijacked IDT entry has CHANGED!!!\n"));
2852 supdrvIdtWrite(pPatch->pIdtEntry, &pPatch->SavedIdt);
2853 AssertMsg(!memcmp((void *)pPatch->pIdtEntry, &pPatch->SavedIdt, sizeof(pPatch->SavedIdt)), ("The hijacked IDT entry has CHANGED!!!\n"));
2854
2855 /*
2856 * Put it in the free list.
2857 * (This free list stuff is to calm my paranoia.)
2858 */
2859 pPatch->pvIdt = NULL;
2860 pPatch->pIdtEntry = NULL;
2861
2862 pPatch->pNext = pDevExt->pIdtPatchesFree;
2863 pDevExt->pIdtPatchesFree = pPatch;
2864}
2865
2866
2867/**
2868 * Write to an IDT entry.
2869 *
2870 * @param pvIdtEntry Where to write.
2871 * @param pNewIDTEntry What to write.
2872 */
2873static void supdrvIdtWrite(volatile void *pvIdtEntry, const SUPDRVIDTE *pNewIDTEntry)
2874{
2875 RTUINTREG uCR0;
2876 RTUINTREG uFlags;
2877
2878 /*
2879 * On SMP machines (P4 hyperthreading included) we must preform a
2880 * 64-bit locked write when updating the IDT entry.
2881 *
2882 * The F00F bugfix for linux (and probably other OSes) causes
2883 * the IDT to be pointing to an readonly mapping. We get around that
2884 * by temporarily turning of WP. Since we're inside a spinlock at this
2885 * point, interrupts are disabled and there isn't any way the WP bit
2886 * flipping can cause any trouble.
2887 */
2888
2889 /* Save & Clear interrupt flag; Save & clear WP. */
2890 uFlags = ASMGetFlags();
2891 ASMSetFlags(uFlags & ~(RTUINTREG)(1 << 9)); /*X86_EFL_IF*/
2892 Assert(!(ASMGetFlags() & (1 << 9)));
2893 uCR0 = ASMGetCR0();
2894 ASMSetCR0(uCR0 & ~(RTUINTREG)(1 << 16)); /*X86_CR0_WP*/
2895
2896 /* Update IDT Entry */
2897#ifdef RT_ARCH_AMD64
2898 ASMAtomicXchgU128((volatile uint128_t *)pvIdtEntry, *(uint128_t *)(uintptr_t)pNewIDTEntry);
2899#else
2900 ASMAtomicXchgU64((volatile uint64_t *)pvIdtEntry, *(uint64_t *)(uintptr_t)pNewIDTEntry);
2901#endif
2902
2903 /* Restore CR0 & Flags */
2904 ASMSetCR0(uCR0);
2905 ASMSetFlags(uFlags);
2906}
2907#endif /* VBOX_WITH_IDT_PATCHING */
2908
2909
2910/**
2911 * Opens an image. If it's the first time it's opened the call must upload
2912 * the bits using the supdrvIOCtl_LdrLoad() / SUPDRV_IOCTL_LDR_LOAD function.
2913 *
2914 * This is the 1st step of the loading.
2915 *
2916 * @returns IPRT status code.
2917 * @param pDevExt Device globals.
2918 * @param pSession Session data.
2919 * @param pReq The open request.
2920 */
2921static int supdrvIOCtl_LdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDROPEN pReq)
2922{
2923 PSUPDRVLDRIMAGE pImage;
2924 unsigned cb;
2925 void *pv;
2926 dprintf(("supdrvIOCtl_LdrOpen: szName=%s cbImage=%d\n", pReq->u.In.szName, pReq->u.In.cbImage));
2927
2928 /*
2929 * Check if we got an instance of the image already.
2930 */
2931 RTSemFastMutexRequest(pDevExt->mtxLdr);
2932 for (pImage = pDevExt->pLdrImages; pImage; pImage = pImage->pNext)
2933 {
2934 if (!strcmp(pImage->szName, pReq->u.In.szName))
2935 {
2936 pImage->cUsage++;
2937 pReq->u.Out.pvImageBase = pImage->pvImage;
2938 pReq->u.Out.fNeedsLoading = pImage->uState == SUP_IOCTL_LDR_OPEN;
2939 supdrvLdrAddUsage(pSession, pImage);
2940 RTSemFastMutexRelease(pDevExt->mtxLdr);
2941 return VINF_SUCCESS;
2942 }
2943 }
2944 /* (not found - add it!) */
2945
2946 /*
2947 * Allocate memory.
2948 */
2949 cb = pReq->u.In.cbImage + sizeof(SUPDRVLDRIMAGE) + 31;
2950 pv = RTMemExecAlloc(cb);
2951 if (!pv)
2952 {
2953 RTSemFastMutexRelease(pDevExt->mtxLdr);
2954 return VERR_NO_MEMORY;
2955 }
2956
2957 /*
2958 * Setup and link in the LDR stuff.
2959 */
2960 pImage = (PSUPDRVLDRIMAGE)pv;
2961 pImage->pvImage = ALIGNP(pImage + 1, 32);
2962 pImage->cbImage = pReq->u.In.cbImage;
2963 pImage->pfnModuleInit = NULL;
2964 pImage->pfnModuleTerm = NULL;
2965 pImage->uState = SUP_IOCTL_LDR_OPEN;
2966 pImage->cUsage = 1;
2967 strcpy(pImage->szName, pReq->u.In.szName);
2968
2969 pImage->pNext = pDevExt->pLdrImages;
2970 pDevExt->pLdrImages = pImage;
2971
2972 supdrvLdrAddUsage(pSession, pImage);
2973
2974 pReq->u.Out.pvImageBase = pImage->pvImage;
2975 pReq->u.Out.fNeedsLoading = true;
2976 RTSemFastMutexRelease(pDevExt->mtxLdr);
2977 return VINF_SUCCESS;
2978}
2979
2980
2981/**
2982 * Loads the image bits.
2983 *
2984 * This is the 2nd step of the loading.
2985 *
2986 * @returns IPRT status code.
2987 * @param pDevExt Device globals.
2988 * @param pSession Session data.
2989 * @param pReq The request.
2990 */
2991static int supdrvIOCtl_LdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRLOAD pReq)
2992{
2993 PSUPDRVLDRUSAGE pUsage;
2994 PSUPDRVLDRIMAGE pImage;
2995 int rc;
2996 dprintf(("supdrvIOCtl_LdrLoad: pvImageBase=%p cbImage=%d\n", pReq->u.In.pvImageBase, pReq->u.In.cbImage));
2997
2998 /*
2999 * Find the ldr image.
3000 */
3001 RTSemFastMutexRequest(pDevExt->mtxLdr);
3002 pUsage = pSession->pLdrUsage;
3003 while (pUsage && pUsage->pImage->pvImage != pReq->u.In.pvImageBase)
3004 pUsage = pUsage->pNext;
3005 if (!pUsage)
3006 {
3007 RTSemFastMutexRelease(pDevExt->mtxLdr);
3008 dprintf(("SUP_IOCTL_LDR_LOAD: couldn't find image!\n"));
3009 return VERR_INVALID_HANDLE;
3010 }
3011 pImage = pUsage->pImage;
3012 if (pImage->cbImage != pReq->u.In.cbImage)
3013 {
3014 RTSemFastMutexRelease(pDevExt->mtxLdr);
3015 dprintf(("SUP_IOCTL_LDR_LOAD: image size mismatch!! %d(prep) != %d(load)\n", pImage->cbImage, pReq->u.In.cbImage));
3016 return VERR_INVALID_HANDLE;
3017 }
3018 if (pImage->uState != SUP_IOCTL_LDR_OPEN)
3019 {
3020 unsigned uState = pImage->uState;
3021 RTSemFastMutexRelease(pDevExt->mtxLdr);
3022 if (uState != SUP_IOCTL_LDR_LOAD)
3023 AssertMsgFailed(("SUP_IOCTL_LDR_LOAD: invalid image state %d (%#x)!\n", uState, uState));
3024 return SUPDRV_ERR_ALREADY_LOADED;
3025 }
3026 switch (pReq->u.In.eEPType)
3027 {
3028 case SUPLDRLOADEP_NOTHING:
3029 break;
3030 case SUPLDRLOADEP_VMMR0:
3031 if ( !pReq->u.In.EP.VMMR0.pvVMMR0
3032 || !pReq->u.In.EP.VMMR0.pvVMMR0EntryInt
3033 || !pReq->u.In.EP.VMMR0.pvVMMR0EntryFast
3034 || !pReq->u.In.EP.VMMR0.pvVMMR0EntryEx)
3035 {
3036 RTSemFastMutexRelease(pDevExt->mtxLdr);
3037 dprintf(("NULL pointer: pvVMMR0=%p pvVMMR0EntryInt=%p pvVMMR0EntryFast=%p pvVMMR0EntryEx=%p!\n",
3038 pReq->u.In.EP.VMMR0.pvVMMR0, pReq->u.In.EP.VMMR0.pvVMMR0EntryInt,
3039 pReq->u.In.EP.VMMR0.pvVMMR0EntryFast, pReq->u.In.EP.VMMR0.pvVMMR0EntryEx));
3040 return VERR_INVALID_PARAMETER;
3041 }
3042 if ( (uintptr_t)pReq->u.In.EP.VMMR0.pvVMMR0EntryInt - (uintptr_t)pImage->pvImage >= pReq->u.In.cbImage
3043 || (uintptr_t)pReq->u.In.EP.VMMR0.pvVMMR0EntryFast - (uintptr_t)pImage->pvImage >= pReq->u.In.cbImage
3044 || (uintptr_t)pReq->u.In.EP.VMMR0.pvVMMR0EntryEx - (uintptr_t)pImage->pvImage >= pReq->u.In.cbImage)
3045 {
3046 RTSemFastMutexRelease(pDevExt->mtxLdr);
3047 dprintf(("Out of range (%p LB %#x): pvVMMR0EntryInt=%p, pvVMMR0EntryFast=%p or pvVMMR0EntryEx=%p is NULL!\n",
3048 pImage->pvImage, pReq->u.In.cbImage, pReq->u.In.EP.VMMR0.pvVMMR0EntryInt,
3049 pReq->u.In.EP.VMMR0.pvVMMR0EntryFast, pReq->u.In.EP.VMMR0.pvVMMR0EntryEx));
3050 return VERR_INVALID_PARAMETER;
3051 }
3052 break;
3053 default:
3054 RTSemFastMutexRelease(pDevExt->mtxLdr);
3055 dprintf(("Invalid eEPType=%d\n", pReq->u.In.eEPType));
3056 return VERR_INVALID_PARAMETER;
3057 }
3058 if ( pReq->u.In.pfnModuleInit
3059 && (uintptr_t)pReq->u.In.pfnModuleInit - (uintptr_t)pImage->pvImage >= pReq->u.In.cbImage)
3060 {
3061 RTSemFastMutexRelease(pDevExt->mtxLdr);
3062 dprintf(("SUP_IOCTL_LDR_LOAD: pfnModuleInit=%p is outside the image (%p %d bytes)\n",
3063 pReq->u.In.pfnModuleInit, pImage->pvImage, pReq->u.In.cbImage));
3064 return VERR_INVALID_PARAMETER;
3065 }
3066 if ( pReq->u.In.pfnModuleTerm
3067 && (uintptr_t)pReq->u.In.pfnModuleTerm - (uintptr_t)pImage->pvImage >= pReq->u.In.cbImage)
3068 {
3069 RTSemFastMutexRelease(pDevExt->mtxLdr);
3070 dprintf(("SUP_IOCTL_LDR_LOAD: pfnModuleTerm=%p is outside the image (%p %d bytes)\n",
3071 pReq->u.In.pfnModuleTerm, pImage->pvImage, pReq->u.In.cbImage));
3072 return VERR_INVALID_PARAMETER;
3073 }
3074
3075 /*
3076 * Copy the memory.
3077 */
3078 /* no need to do try/except as this is a buffered request. */
3079 memcpy(pImage->pvImage, &pReq->u.In.achImage[0], pImage->cbImage);
3080 pImage->uState = SUP_IOCTL_LDR_LOAD;
3081 pImage->pfnModuleInit = pReq->u.In.pfnModuleInit;
3082 pImage->pfnModuleTerm = pReq->u.In.pfnModuleTerm;
3083 pImage->offSymbols = pReq->u.In.offSymbols;
3084 pImage->cSymbols = pReq->u.In.cSymbols;
3085 pImage->offStrTab = pReq->u.In.offStrTab;
3086 pImage->cbStrTab = pReq->u.In.cbStrTab;
3087
3088 /*
3089 * Update any entry points.
3090 */
3091 switch (pReq->u.In.eEPType)
3092 {
3093 default:
3094 case SUPLDRLOADEP_NOTHING:
3095 rc = VINF_SUCCESS;
3096 break;
3097 case SUPLDRLOADEP_VMMR0:
3098 rc = supdrvLdrSetR0EP(pDevExt, pReq->u.In.EP.VMMR0.pvVMMR0, pReq->u.In.EP.VMMR0.pvVMMR0EntryInt,
3099 pReq->u.In.EP.VMMR0.pvVMMR0EntryFast, pReq->u.In.EP.VMMR0.pvVMMR0EntryEx);
3100 break;
3101 }
3102
3103 /*
3104 * On success call the module initialization.
3105 */
3106 dprintf(("supdrvIOCtl_LdrLoad: pfnModuleInit=%p\n", pImage->pfnModuleInit));
3107 if (RT_SUCCESS(rc) && pImage->pfnModuleInit)
3108 {
3109 dprintf(("supdrvIOCtl_LdrLoad: calling pfnModuleInit=%p\n", pImage->pfnModuleInit));
3110 rc = pImage->pfnModuleInit();
3111 if (rc && pDevExt->pvVMMR0 == pImage->pvImage)
3112 supdrvLdrUnsetR0EP(pDevExt);
3113 }
3114
3115 if (rc)
3116 pImage->uState = SUP_IOCTL_LDR_OPEN;
3117
3118 RTSemFastMutexRelease(pDevExt->mtxLdr);
3119 return rc;
3120}
3121
3122
3123/**
3124 * Frees a previously loaded (prep'ed) image.
3125 *
3126 * @returns IPRT status code.
3127 * @param pDevExt Device globals.
3128 * @param pSession Session data.
3129 * @param pReq The request.
3130 */
3131static int supdrvIOCtl_LdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRFREE pReq)
3132{
3133 int rc;
3134 PSUPDRVLDRUSAGE pUsagePrev;
3135 PSUPDRVLDRUSAGE pUsage;
3136 PSUPDRVLDRIMAGE pImage;
3137 dprintf(("supdrvIOCtl_LdrFree: pvImageBase=%p\n", pReq->u.In.pvImageBase));
3138
3139 /*
3140 * Find the ldr image.
3141 */
3142 RTSemFastMutexRequest(pDevExt->mtxLdr);
3143 pUsagePrev = NULL;
3144 pUsage = pSession->pLdrUsage;
3145 while (pUsage && pUsage->pImage->pvImage != pReq->u.In.pvImageBase)
3146 {
3147 pUsagePrev = pUsage;
3148 pUsage = pUsage->pNext;
3149 }
3150 if (!pUsage)
3151 {
3152 RTSemFastMutexRelease(pDevExt->mtxLdr);
3153 dprintf(("SUP_IOCTL_LDR_FREE: couldn't find image!\n"));
3154 return VERR_INVALID_HANDLE;
3155 }
3156
3157 /*
3158 * Check if we can remove anything.
3159 */
3160 rc = VINF_SUCCESS;
3161 pImage = pUsage->pImage;
3162 if (pImage->cUsage <= 1 || pUsage->cUsage <= 1)
3163 {
3164 /*
3165 * Check if there are any objects with destructors in the image, if
3166 * so leave it for the session cleanup routine so we get a chance to
3167 * clean things up in the right order and not leave them all dangling.
3168 */
3169 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
3170 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
3171 if (pImage->cUsage <= 1)
3172 {
3173 PSUPDRVOBJ pObj;
3174 for (pObj = pDevExt->pObjs; pObj; pObj = pObj->pNext)
3175 if (RT_UNLIKELY((uintptr_t)pObj->pfnDestructor - (uintptr_t)pImage->pvImage < pImage->cbImage))
3176 {
3177 rc = VERR_SHARING_VIOLATION; /** @todo VERR_DANGLING_OBJECTS */
3178 break;
3179 }
3180 }
3181 else
3182 {
3183 PSUPDRVUSAGE pGenUsage;
3184 for (pGenUsage = pSession->pUsage; pGenUsage; pGenUsage = pGenUsage->pNext)
3185 if (RT_UNLIKELY((uintptr_t)pGenUsage->pObj->pfnDestructor - (uintptr_t)pImage->pvImage < pImage->cbImage))
3186 {
3187 rc = VERR_SHARING_VIOLATION; /** @todo VERR_DANGLING_OBJECTS */
3188 break;
3189 }
3190 }
3191 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
3192 if (rc == VINF_SUCCESS)
3193 {
3194 /* unlink it */
3195 if (pUsagePrev)
3196 pUsagePrev->pNext = pUsage->pNext;
3197 else
3198 pSession->pLdrUsage = pUsage->pNext;
3199
3200 /* free it */
3201 pUsage->pImage = NULL;
3202 pUsage->pNext = NULL;
3203 RTMemFree(pUsage);
3204
3205 /*
3206 * Derefrence the image.
3207 */
3208 if (pImage->cUsage <= 1)
3209 supdrvLdrFree(pDevExt, pImage);
3210 else
3211 pImage->cUsage--;
3212 }
3213 }
3214 else
3215 {
3216 /*
3217 * Dereference both image and usage.
3218 */
3219 pImage->cUsage--;
3220 pUsage->cUsage--;
3221 }
3222
3223 RTSemFastMutexRelease(pDevExt->mtxLdr);
3224 return VINF_SUCCESS;
3225}
3226
3227
3228/**
3229 * Gets the address of a symbol in an open image.
3230 *
3231 * @returns 0 on success.
3232 * @returns SUPDRV_ERR_* on failure.
3233 * @param pDevExt Device globals.
3234 * @param pSession Session data.
3235 * @param pReq The request buffer.
3236 */
3237static int supdrvIOCtl_LdrGetSymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRGETSYMBOL pReq)
3238{
3239 PSUPDRVLDRIMAGE pImage;
3240 PSUPDRVLDRUSAGE pUsage;
3241 uint32_t i;
3242 PSUPLDRSYM paSyms;
3243 const char *pchStrings;
3244 const size_t cbSymbol = strlen(pReq->u.In.szSymbol) + 1;
3245 void *pvSymbol = NULL;
3246 int rc = VERR_GENERAL_FAILURE;
3247 dprintf2(("supdrvIOCtl_LdrGetSymbol: pvImageBase=%p szSymbol=\"%s\"\n", pReq->u.In.pvImageBase, pReq->u.In.szSymbol));
3248
3249 /*
3250 * Find the ldr image.
3251 */
3252 RTSemFastMutexRequest(pDevExt->mtxLdr);
3253 pUsage = pSession->pLdrUsage;
3254 while (pUsage && pUsage->pImage->pvImage != pReq->u.In.pvImageBase)
3255 pUsage = pUsage->pNext;
3256 if (!pUsage)
3257 {
3258 RTSemFastMutexRelease(pDevExt->mtxLdr);
3259 dprintf(("SUP_IOCTL_LDR_GET_SYMBOL: couldn't find image!\n"));
3260 return VERR_INVALID_HANDLE;
3261 }
3262 pImage = pUsage->pImage;
3263 if (pImage->uState != SUP_IOCTL_LDR_LOAD)
3264 {
3265 unsigned uState = pImage->uState;
3266 RTSemFastMutexRelease(pDevExt->mtxLdr);
3267 dprintf(("SUP_IOCTL_LDR_GET_SYMBOL: invalid image state %d (%#x)!\n", uState, uState)); NOREF(uState);
3268 return VERR_ALREADY_LOADED;
3269 }
3270
3271 /*
3272 * Search the symbol string.
3273 */
3274 pchStrings = (const char *)((uint8_t *)pImage->pvImage + pImage->offStrTab);
3275 paSyms = (PSUPLDRSYM)((uint8_t *)pImage->pvImage + pImage->offSymbols);
3276 for (i = 0; i < pImage->cSymbols; i++)
3277 {
3278 if ( paSyms[i].offSymbol < pImage->cbImage /* paranoia */
3279 && paSyms[i].offName + cbSymbol <= pImage->cbStrTab
3280 && !memcmp(pchStrings + paSyms[i].offName, pReq->u.In.szSymbol, cbSymbol))
3281 {
3282 pvSymbol = (uint8_t *)pImage->pvImage + paSyms[i].offSymbol;
3283 rc = VINF_SUCCESS;
3284 break;
3285 }
3286 }
3287 RTSemFastMutexRelease(pDevExt->mtxLdr);
3288 pReq->u.Out.pvSymbol = pvSymbol;
3289 return rc;
3290}
3291
3292
3293/**
3294 * Updates the IDT patches to point to the specified VMM R0 entry
3295 * point (i.e. VMMR0Enter()).
3296 *
3297 * @returns IPRT status code.
3298 * @param pDevExt Device globals.
3299 * @param pSession Session data.
3300 * @param pVMMR0 VMMR0 image handle.
3301 * @param pvVMMR0EntryInt VMMR0EntryInt address.
3302 * @param pvVMMR0EntryFast VMMR0EntryFast address.
3303 * @param pvVMMR0EntryEx VMMR0EntryEx address.
3304 * @remark Caller must own the loader mutex.
3305 */
3306static int supdrvLdrSetR0EP(PSUPDRVDEVEXT pDevExt, void *pvVMMR0, void *pvVMMR0EntryInt, void *pvVMMR0EntryFast, void *pvVMMR0EntryEx)
3307{
3308 int rc = VINF_SUCCESS;
3309 dprintf(("supdrvLdrSetR0EP pvVMMR0=%p pvVMMR0EntryInt=%p\n", pvVMMR0, pvVMMR0EntryInt));
3310
3311
3312 /*
3313 * Check if not yet set.
3314 */
3315 if (!pDevExt->pvVMMR0)
3316 {
3317#ifdef VBOX_WITH_IDT_PATCHING
3318 PSUPDRVPATCH pPatch;
3319#endif
3320
3321 /*
3322 * Set it and update IDT patch code.
3323 */
3324 pDevExt->pvVMMR0 = pvVMMR0;
3325 pDevExt->pfnVMMR0EntryInt = pvVMMR0EntryInt;
3326 pDevExt->pfnVMMR0EntryFast = pvVMMR0EntryFast;
3327 pDevExt->pfnVMMR0EntryEx = pvVMMR0EntryEx;
3328#ifdef VBOX_WITH_IDT_PATCHING
3329 for (pPatch = pDevExt->pIdtPatches; pPatch; pPatch = pPatch->pNext)
3330 {
3331# ifdef RT_ARCH_AMD64
3332 ASMAtomicXchgU64((volatile uint64_t *)&pPatch->auCode[pPatch->offVMMR0EntryFixup], (uint64_t)pvVMMR0);
3333# else /* RT_ARCH_X86 */
3334 ASMAtomicXchgU32((volatile uint32_t *)&pPatch->auCode[pPatch->offVMMR0EntryFixup],
3335 (uint32_t)pvVMMR0 - (uint32_t)&pPatch->auCode[pPatch->offVMMR0EntryFixup + 4]);
3336# endif
3337 }
3338#endif /* VBOX_WITH_IDT_PATCHING */
3339 }
3340 else
3341 {
3342 /*
3343 * Return failure or success depending on whether the values match or not.
3344 */
3345 if ( pDevExt->pvVMMR0 != pvVMMR0
3346 || (void *)pDevExt->pfnVMMR0EntryInt != pvVMMR0EntryInt
3347 || (void *)pDevExt->pfnVMMR0EntryFast != pvVMMR0EntryFast
3348 || (void *)pDevExt->pfnVMMR0EntryEx != pvVMMR0EntryEx)
3349 {
3350 AssertMsgFailed(("SUP_IOCTL_LDR_SETR0EP: Already set pointing to a different module!\n"));
3351 rc = VERR_INVALID_PARAMETER;
3352 }
3353 }
3354 return rc;
3355}
3356
3357
3358/**
3359 * Unsets the R0 entry point installed by supdrvLdrSetR0EP.
3360 *
3361 * @param pDevExt Device globals.
3362 */
3363static void supdrvLdrUnsetR0EP(PSUPDRVDEVEXT pDevExt)
3364{
3365#ifdef VBOX_WITH_IDT_PATCHING
3366 PSUPDRVPATCH pPatch;
3367#endif
3368
3369 pDevExt->pvVMMR0 = NULL;
3370 pDevExt->pfnVMMR0EntryInt = NULL;
3371 pDevExt->pfnVMMR0EntryFast = NULL;
3372 pDevExt->pfnVMMR0EntryEx = NULL;
3373
3374#ifdef VBOX_WITH_IDT_PATCHING
3375 for (pPatch = pDevExt->pIdtPatches; pPatch; pPatch = pPatch->pNext)
3376 {
3377# ifdef RT_ARCH_AMD64
3378 ASMAtomicXchgU64((volatile uint64_t *)&pPatch->auCode[pPatch->offVMMR0EntryFixup],
3379 (uint64_t)&pPatch->auCode[pPatch->offStub]);
3380# else /* RT_ARCH_X86 */
3381 ASMAtomicXchgU32((volatile uint32_t *)&pPatch->auCode[pPatch->offVMMR0EntryFixup],
3382 (uint32_t)&pPatch->auCode[pPatch->offStub] - (uint32_t)&pPatch->auCode[pPatch->offVMMR0EntryFixup + 4]);
3383# endif
3384 }
3385#endif /* VBOX_WITH_IDT_PATCHING */
3386}
3387
3388
3389/**
3390 * Adds a usage reference in the specified session of an image.
3391 *
3392 * @param pSession Session in question.
3393 * @param pImage Image which the session is using.
3394 */
3395static void supdrvLdrAddUsage(PSUPDRVSESSION pSession, PSUPDRVLDRIMAGE pImage)
3396{
3397 PSUPDRVLDRUSAGE pUsage;
3398 dprintf(("supdrvLdrAddUsage: pImage=%p\n", pImage));
3399
3400 /*
3401 * Referenced it already?
3402 */
3403 pUsage = pSession->pLdrUsage;
3404 while (pUsage)
3405 {
3406 if (pUsage->pImage == pImage)
3407 {
3408 pUsage->cUsage++;
3409 return;
3410 }
3411 pUsage = pUsage->pNext;
3412 }
3413
3414 /*
3415 * Allocate new usage record.
3416 */
3417 pUsage = (PSUPDRVLDRUSAGE)RTMemAlloc(sizeof(*pUsage));
3418 Assert(pUsage);
3419 if (pUsage)
3420 {
3421 pUsage->cUsage = 1;
3422 pUsage->pImage = pImage;
3423 pUsage->pNext = pSession->pLdrUsage;
3424 pSession->pLdrUsage = pUsage;
3425 }
3426 /* ignore errors... */
3427}
3428
3429
3430/**
3431 * Frees a load image.
3432 *
3433 * @param pDevExt Pointer to device extension.
3434 * @param pImage Pointer to the image we're gonna free.
3435 * This image must exit!
3436 * @remark The caller MUST own SUPDRVDEVEXT::mtxLdr!
3437 */
3438static void supdrvLdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
3439{
3440 PSUPDRVLDRIMAGE pImagePrev;
3441 dprintf(("supdrvLdrFree: pImage=%p\n", pImage));
3442
3443 /* find it - arg. should've used doubly linked list. */
3444 Assert(pDevExt->pLdrImages);
3445 pImagePrev = NULL;
3446 if (pDevExt->pLdrImages != pImage)
3447 {
3448 pImagePrev = pDevExt->pLdrImages;
3449 while (pImagePrev->pNext != pImage)
3450 pImagePrev = pImagePrev->pNext;
3451 Assert(pImagePrev->pNext == pImage);
3452 }
3453
3454 /* unlink */
3455 if (pImagePrev)
3456 pImagePrev->pNext = pImage->pNext;
3457 else
3458 pDevExt->pLdrImages = pImage->pNext;
3459
3460 /* check if this is VMMR0.r0 and fix the Idt patches if it is. */
3461 if (pDevExt->pvVMMR0 == pImage->pvImage)
3462 supdrvLdrUnsetR0EP(pDevExt);
3463
3464 /* check for objects with destructors in this image. (Shouldn't happen.) */
3465 if (pDevExt->pObjs)
3466 {
3467 unsigned cObjs = 0;
3468 PSUPDRVOBJ pObj;
3469 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
3470 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
3471 for (pObj = pDevExt->pObjs; pObj; pObj = pObj->pNext)
3472 if (RT_UNLIKELY((uintptr_t)pObj->pfnDestructor - (uintptr_t)pImage->pvImage < pImage->cbImage))
3473 {
3474 pObj->pfnDestructor = NULL;
3475 cObjs++;
3476 }
3477 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
3478 if (cObjs)
3479 OSDBGPRINT(("supdrvLdrFree: Image '%s' has %d dangling objects!\n", pImage->szName, cObjs));
3480 }
3481
3482 /* call termination function if fully loaded. */
3483 if ( pImage->pfnModuleTerm
3484 && pImage->uState == SUP_IOCTL_LDR_LOAD)
3485 {
3486 dprintf(("supdrvIOCtl_LdrLoad: calling pfnModuleTerm=%p\n", pImage->pfnModuleTerm));
3487 pImage->pfnModuleTerm();
3488 }
3489
3490 /* free the image */
3491 pImage->cUsage = 0;
3492 pImage->pNext = 0;
3493 pImage->uState = SUP_IOCTL_LDR_FREE;
3494 RTMemExecFree(pImage);
3495}
3496
3497
3498/**
3499 * Gets the current paging mode of the CPU and stores in in pOut.
3500 */
3501static SUPPAGINGMODE supdrvIOCtl_GetPagingMode(void)
3502{
3503 SUPPAGINGMODE enmMode;
3504
3505 RTUINTREG cr0 = ASMGetCR0();
3506 if ((cr0 & (X86_CR0_PG | X86_CR0_PE)) != (X86_CR0_PG | X86_CR0_PE))
3507 enmMode = SUPPAGINGMODE_INVALID;
3508 else
3509 {
3510 RTUINTREG cr4 = ASMGetCR4();
3511 uint32_t fNXEPlusLMA = 0;
3512 if (cr4 & X86_CR4_PAE)
3513 {
3514 uint32_t fAmdFeatures = ASMCpuId_EDX(0x80000001);
3515 if (fAmdFeatures & (X86_CPUID_AMD_FEATURE_EDX_NX | X86_CPUID_AMD_FEATURE_EDX_LONG_MODE))
3516 {
3517 uint64_t efer = ASMRdMsr(MSR_K6_EFER);
3518 if ((fAmdFeatures & X86_CPUID_AMD_FEATURE_EDX_NX) && (efer & MSR_K6_EFER_NXE))
3519 fNXEPlusLMA |= BIT(0);
3520 if ((fAmdFeatures & X86_CPUID_AMD_FEATURE_EDX_LONG_MODE) && (efer & MSR_K6_EFER_LMA))
3521 fNXEPlusLMA |= BIT(1);
3522 }
3523 }
3524
3525 switch ((cr4 & (X86_CR4_PAE | X86_CR4_PGE)) | fNXEPlusLMA)
3526 {
3527 case 0:
3528 enmMode = SUPPAGINGMODE_32_BIT;
3529 break;
3530
3531 case X86_CR4_PGE:
3532 enmMode = SUPPAGINGMODE_32_BIT_GLOBAL;
3533 break;
3534
3535 case X86_CR4_PAE:
3536 enmMode = SUPPAGINGMODE_PAE;
3537 break;
3538
3539 case X86_CR4_PAE | BIT(0):
3540 enmMode = SUPPAGINGMODE_PAE_NX;
3541 break;
3542
3543 case X86_CR4_PAE | X86_CR4_PGE:
3544 enmMode = SUPPAGINGMODE_PAE_GLOBAL;
3545 break;
3546
3547 case X86_CR4_PAE | X86_CR4_PGE | BIT(0):
3548 enmMode = SUPPAGINGMODE_PAE_GLOBAL;
3549 break;
3550
3551 case BIT(1) | X86_CR4_PAE:
3552 enmMode = SUPPAGINGMODE_AMD64;
3553 break;
3554
3555 case BIT(1) | X86_CR4_PAE | BIT(0):
3556 enmMode = SUPPAGINGMODE_AMD64_NX;
3557 break;
3558
3559 case BIT(1) | X86_CR4_PAE | X86_CR4_PGE:
3560 enmMode = SUPPAGINGMODE_AMD64_GLOBAL;
3561 break;
3562
3563 case BIT(1) | X86_CR4_PAE | X86_CR4_PGE | BIT(0):
3564 enmMode = SUPPAGINGMODE_AMD64_GLOBAL_NX;
3565 break;
3566
3567 default:
3568 AssertMsgFailed(("Cannot happen! cr4=%#x fNXEPlusLMA=%d\n", cr4, fNXEPlusLMA));
3569 enmMode = SUPPAGINGMODE_INVALID;
3570 break;
3571 }
3572 }
3573 return enmMode;
3574}
3575
3576
3577#ifdef USE_NEW_OS_INTERFACE_FOR_GIP
3578/**
3579 * Creates the GIP.
3580 *
3581 * @returns negative errno.
3582 * @param pDevExt Instance data. GIP stuff may be updated.
3583 */
3584static int supdrvGipCreate(PSUPDRVDEVEXT pDevExt)
3585{
3586 PSUPGLOBALINFOPAGE pGip;
3587 RTHCPHYS HCPhysGip;
3588 uint32_t u32SystemResolution;
3589 uint32_t u32Interval;
3590 int rc;
3591
3592 dprintf(("supdrvGipCreate:\n"));
3593
3594 /* assert order */
3595 Assert(pDevExt->u32SystemTimerGranularityGrant == 0);
3596 Assert(pDevExt->GipMemObj == NIL_RTR0MEMOBJ);
3597 Assert(!pDevExt->pGipTimer);
3598
3599 /*
3600 * Allocate a suitable page with a default kernel mapping.
3601 */
3602 rc = RTR0MemObjAllocLow(&pDevExt->GipMemObj, PAGE_SIZE, false);
3603 if (RT_FAILURE(rc))
3604 {
3605 OSDBGPRINT(("supdrvGipCreate: failed to allocate the GIP page. rc=%d\n", rc));
3606 return rc;
3607 }
3608 pGip = (PSUPGLOBALINFOPAGE)RTR0MemObjAddress(pDevExt->GipMemObj); AssertPtr(pGip);
3609 HCPhysGip = RTR0MemObjGetPagePhysAddr(pDevExt->GipMemObj, 0); Assert(HCPhysGip != NIL_RTHCPHYS);
3610
3611 /*
3612 * Try bump up the system timer resolution.
3613 * The more interrupts the better...
3614 */
3615 if ( RT_SUCCESS(RTTimerRequestSystemGranularity( 976563 /* 1024 HZ */, &u32SystemResolution))
3616 || RT_SUCCESS(RTTimerRequestSystemGranularity( 1000000 /* 1000 HZ */, &u32SystemResolution))
3617 || RT_SUCCESS(RTTimerRequestSystemGranularity( 3906250 /* 256 HZ */, &u32SystemResolution))
3618 || RT_SUCCESS(RTTimerRequestSystemGranularity( 4000000 /* 250 HZ */, &u32SystemResolution))
3619 || RT_SUCCESS(RTTimerRequestSystemGranularity( 7812500 /* 128 HZ */, &u32SystemResolution))
3620 || RT_SUCCESS(RTTimerRequestSystemGranularity(10000000 /* 100 HZ */, &u32SystemResolution))
3621 || RT_SUCCESS(RTTimerRequestSystemGranularity(15625000 /* 64 HZ */, &u32SystemResolution))
3622 || RT_SUCCESS(RTTimerRequestSystemGranularity(31250000 /* 32 HZ */, &u32SystemResolution))
3623 )
3624 {
3625 Assert(RTTimerGetSystemGranularity() <= u32SystemResolution);
3626 pDevExt->u32SystemTimerGranularityGrant = u32SystemResolution;
3627 }
3628
3629 /*
3630 * Find a reasonable update interval, something close to 10ms would be nice,
3631 * and create a recurring timer.
3632 */
3633 u32Interval = u32SystemResolution = RTTimerGetSystemGranularity();
3634 while (u32Interval < 10000000 /* 10 ms */)
3635 u32Interval += u32SystemResolution;
3636
3637 rc = RTTimerCreateEx(&pDevExt->pGipTimer, u32Interval, 0, supdrvGipTimer, pDevExt);
3638 if (RT_FAILURE(rc))
3639 {
3640 OSDBGPRINT(("supdrvGipCreate: failed create GIP timer at %RU32 ns interval. rc=%d\n", u32Interval, rc));
3641 Assert(!pDevExt->pGipTimer);
3642 supdrvGipDestroy(pDevExt);
3643 return rc;
3644 }
3645
3646 /*
3647 * We're good.
3648 */
3649 supdrvGipInit(pDevExt, pGip, HCPhysGip, RTTimeSystemNanoTS(), 1000000000 / u32Interval /*=Hz*/);
3650 return VINF_SUCCESS;
3651}
3652
3653
3654/**
3655 * Terminates the GIP.
3656 *
3657 * @param pDevExt Instance data. GIP stuff may be updated.
3658 */
3659static void supdrvGipDestroy(PSUPDRVDEVEXT pDevExt)
3660{
3661 int rc;
3662#ifdef DEBUG_DARWIN_GIP
3663 OSDBGPRINT(("supdrvGipDestroy: pDevExt=%p pGip=%p pGipTimer=%p GipMemObj=%p\n", pDevExt,
3664 pDevExt->GipMemObj != NIL_RTR0MEMOBJ ? RTR0MemObjAddress(pDevExt->GipMemObj) : NULL,
3665 pDevExt->pGipTimer, pDevExt->GipMemObj));
3666#endif
3667
3668 /*
3669 * Invalid the GIP data.
3670 */
3671 if (pDevExt->pGip)
3672 {
3673 supdrvGipTerm(pDevExt->pGip);
3674 pDevExt->pGip = NULL;
3675 }
3676
3677 /*
3678 * Destroy the timer and free the GIP memory object.
3679 */
3680 if (pDevExt->pGipTimer)
3681 {
3682 rc = RTTimerDestroy(pDevExt->pGipTimer); AssertRC(rc);
3683 pDevExt->pGipTimer = NULL;
3684 }
3685
3686 if (pDevExt->GipMemObj != NIL_RTR0MEMOBJ)
3687 {
3688 rc = RTR0MemObjFree(pDevExt->GipMemObj, true /* free mappings */); AssertRC(rc);
3689 pDevExt->GipMemObj = NIL_RTR0MEMOBJ;
3690 }
3691
3692 /*
3693 * Finally, release the system timer resolution request if one succeeded.
3694 */
3695 if (pDevExt->u32SystemTimerGranularityGrant)
3696 {
3697 rc = RTTimerReleaseSystemGranularity(pDevExt->u32SystemTimerGranularityGrant); AssertRC(rc);
3698 pDevExt->u32SystemTimerGranularityGrant = 0;
3699 }
3700}
3701
3702
3703/**
3704 * Timer callback function.
3705 * @param pTimer The timer.
3706 * @param pvUser The device extension.
3707 */
3708static DECLCALLBACK(void) supdrvGipTimer(PRTTIMER pTimer, void *pvUser)
3709{
3710 PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser;
3711 supdrvGipUpdate(pDevExt->pGip, RTTimeSystemNanoTS());
3712}
3713#endif /* USE_NEW_OS_INTERFACE_FOR_GIP */
3714
3715
3716/**
3717 * Initializes the GIP data.
3718 *
3719 * @returns IPRT status code.
3720 * @param pDevExt Pointer to the device instance data.
3721 * @param pGip Pointer to the read-write kernel mapping of the GIP.
3722 * @param HCPhys The physical address of the GIP.
3723 * @param u64NanoTS The current nanosecond timestamp.
3724 * @param uUpdateHz The update freqence.
3725 */
3726int VBOXCALL supdrvGipInit(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip, RTHCPHYS HCPhys, uint64_t u64NanoTS, unsigned uUpdateHz)
3727{
3728 unsigned i;
3729#ifdef DEBUG_DARWIN_GIP
3730 OSDBGPRINT(("supdrvGipInit: pGip=%p HCPhys=%lx u64NanoTS=%llu uUpdateHz=%d\n", pGip, (long)HCPhys, u64NanoTS, uUpdateHz));
3731#else
3732 dprintf(("supdrvGipInit: pGip=%p HCPhys=%lx u64NanoTS=%llu uUpdateHz=%d\n", pGip, (long)HCPhys, u64NanoTS, uUpdateHz));
3733#endif
3734
3735 /*
3736 * Initialize the structure.
3737 */
3738 memset(pGip, 0, PAGE_SIZE);
3739 pGip->u32Magic = SUPGLOBALINFOPAGE_MAGIC;
3740 pGip->u32Version = SUPGLOBALINFOPAGE_VERSION;
3741 pGip->u32Mode = supdrvGipDeterminTscMode();
3742 pGip->u32UpdateHz = uUpdateHz;
3743 pGip->u32UpdateIntervalNS = 1000000000 / uUpdateHz;
3744 pGip->u64NanoTSLastUpdateHz = u64NanoTS;
3745
3746 for (i = 0; i < RT_ELEMENTS(pGip->aCPUs); i++)
3747 {
3748 pGip->aCPUs[i].u32TransactionId = 2;
3749 pGip->aCPUs[i].u64NanoTS = u64NanoTS;
3750 pGip->aCPUs[i].u64TSC = ASMReadTSC();
3751
3752 /*
3753 * We don't know the following values until we've executed updates.
3754 * So, we'll just insert very high values.
3755 */
3756 pGip->aCPUs[i].u64CpuHz = _4G + 1;
3757 pGip->aCPUs[i].u32UpdateIntervalTSC = _2G / 4;
3758 pGip->aCPUs[i].au32TSCHistory[0] = _2G / 4;
3759 pGip->aCPUs[i].au32TSCHistory[1] = _2G / 4;
3760 pGip->aCPUs[i].au32TSCHistory[2] = _2G / 4;
3761 pGip->aCPUs[i].au32TSCHistory[3] = _2G / 4;
3762 pGip->aCPUs[i].au32TSCHistory[4] = _2G / 4;
3763 pGip->aCPUs[i].au32TSCHistory[5] = _2G / 4;
3764 pGip->aCPUs[i].au32TSCHistory[6] = _2G / 4;
3765 pGip->aCPUs[i].au32TSCHistory[7] = _2G / 4;
3766 }
3767
3768 /*
3769 * Link it to the device extension.
3770 */
3771 pDevExt->pGip = pGip;
3772 pDevExt->HCPhysGip = HCPhys;
3773 pDevExt->cGipUsers = 0;
3774
3775 return VINF_SUCCESS;
3776}
3777
3778
3779/**
3780 * Determin the GIP TSC mode.
3781 *
3782 * @returns The most suitable TSC mode.
3783 */
3784static SUPGIPMODE supdrvGipDeterminTscMode(void)
3785{
3786#ifndef USE_NEW_OS_INTERFACE_FOR_GIP
3787 /*
3788 * The problem here is that AMD processors with power management features
3789 * may easily end up with different TSCs because the CPUs or even cores
3790 * on the same physical chip run at different frequencies to save power.
3791 *
3792 * It is rumoured that this will be corrected with Barcelona and it's
3793 * expected that this will be indicated by the TscInvariant bit in
3794 * cpuid(0x80000007). So, the "difficult" bit here is to correctly
3795 * identify the older CPUs which don't do different frequency and
3796 * can be relied upon to have somewhat uniform TSC between the cpus.
3797 */
3798 if (supdrvOSGetCPUCount() > 1)
3799 {
3800 uint32_t uEAX, uEBX, uECX, uEDX;
3801
3802 /* Permit user users override. */
3803 if (supdrvOSGetForcedAsyncTscMode())
3804 return SUPGIPMODE_ASYNC_TSC;
3805
3806 /* Check for "AuthenticAMD" */
3807 ASMCpuId(0, &uEAX, &uEBX, &uECX, &uEDX);
3808 if (uEAX >= 1 && uEBX == 0x68747541 && uECX == 0x444d4163 && uEDX == 0x69746e65)
3809 {
3810 /* Check for APM support and that TscInvariant is cleared. */
3811 ASMCpuId(0x80000000, &uEAX, &uEBX, &uECX, &uEDX);
3812 if (uEAX >= 0x80000007)
3813 {
3814 ASMCpuId(0x80000007, &uEAX, &uEBX, &uECX, &uEDX);
3815 if ( !(uEDX & BIT(8))/* TscInvariant */
3816 && (uEDX & 0x3e)) /* STC|TM|THERMTRIP|VID|FID. Ignore TS. */
3817 return SUPGIPMODE_ASYNC_TSC;
3818 }
3819 }
3820 }
3821#endif
3822 return SUPGIPMODE_SYNC_TSC;
3823}
3824
3825
3826/**
3827 * Invalidates the GIP data upon termination.
3828 *
3829 * @param pGip Pointer to the read-write kernel mapping of the GIP.
3830 */
3831void VBOXCALL supdrvGipTerm(PSUPGLOBALINFOPAGE pGip)
3832{
3833 unsigned i;
3834 pGip->u32Magic = 0;
3835 for (i = 0; i < RT_ELEMENTS(pGip->aCPUs); i++)
3836 {
3837 pGip->aCPUs[i].u64NanoTS = 0;
3838 pGip->aCPUs[i].u64TSC = 0;
3839 pGip->aCPUs[i].iTSCHistoryHead = 0;
3840 }
3841}
3842
3843
3844/**
3845 * Worker routine for supdrvGipUpdate and supdrvGipUpdatePerCpu that
3846 * updates all the per cpu data except the transaction id.
3847 *
3848 * @param pGip The GIP.
3849 * @param pGipCpu Pointer to the per cpu data.
3850 * @param u64NanoTS The current time stamp.
3851 */
3852static void supdrvGipDoUpdateCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pGipCpu, uint64_t u64NanoTS)
3853{
3854 uint64_t u64TSC;
3855 uint64_t u64TSCDelta;
3856 uint32_t u32UpdateIntervalTSC;
3857 uint32_t u32UpdateIntervalTSCSlack;
3858 unsigned iTSCHistoryHead;
3859 uint64_t u64CpuHz;
3860
3861 /*
3862 * Update the NanoTS.
3863 */
3864 ASMAtomicXchgU64(&pGipCpu->u64NanoTS, u64NanoTS);
3865
3866 /*
3867 * Calc TSC delta.
3868 */
3869 /** @todo validate the NanoTS delta, don't trust the OS to call us when it should... */
3870 u64TSC = ASMReadTSC();
3871 u64TSCDelta = u64TSC - pGipCpu->u64TSC;
3872 ASMAtomicXchgU64(&pGipCpu->u64TSC, u64TSC);
3873
3874 if (u64TSCDelta >> 32)
3875 {
3876 u64TSCDelta = pGipCpu->u32UpdateIntervalTSC;
3877 pGipCpu->cErrors++;
3878 }
3879
3880 /*
3881 * TSC History.
3882 */
3883 Assert(ELEMENTS(pGipCpu->au32TSCHistory) == 8);
3884
3885 iTSCHistoryHead = (pGipCpu->iTSCHistoryHead + 1) & 7;
3886 ASMAtomicXchgU32(&pGipCpu->iTSCHistoryHead, iTSCHistoryHead);
3887 ASMAtomicXchgU32(&pGipCpu->au32TSCHistory[iTSCHistoryHead], (uint32_t)u64TSCDelta);
3888
3889 /*
3890 * UpdateIntervalTSC = average of last 8,2,1 intervals depending on update HZ.
3891 */
3892 if (pGip->u32UpdateHz >= 1000)
3893 {
3894 uint32_t u32;
3895 u32 = pGipCpu->au32TSCHistory[0];
3896 u32 += pGipCpu->au32TSCHistory[1];
3897 u32 += pGipCpu->au32TSCHistory[2];
3898 u32 += pGipCpu->au32TSCHistory[3];
3899 u32 >>= 2;
3900 u32UpdateIntervalTSC = pGipCpu->au32TSCHistory[4];
3901 u32UpdateIntervalTSC += pGipCpu->au32TSCHistory[5];
3902 u32UpdateIntervalTSC += pGipCpu->au32TSCHistory[6];
3903 u32UpdateIntervalTSC += pGipCpu->au32TSCHistory[7];
3904 u32UpdateIntervalTSC >>= 2;
3905 u32UpdateIntervalTSC += u32;
3906 u32UpdateIntervalTSC >>= 1;
3907
3908 /* Value choosen for a 2GHz Athlon64 running linux 2.6.10/11, . */
3909 u32UpdateIntervalTSCSlack = u32UpdateIntervalTSC >> 14;
3910 }
3911 else if (pGip->u32UpdateHz >= 90)
3912 {
3913 u32UpdateIntervalTSC = (uint32_t)u64TSCDelta;
3914 u32UpdateIntervalTSC += pGipCpu->au32TSCHistory[(iTSCHistoryHead - 1) & 7];
3915 u32UpdateIntervalTSC >>= 1;
3916
3917 /* value choosen on a 2GHz thinkpad running windows */
3918 u32UpdateIntervalTSCSlack = u32UpdateIntervalTSC >> 7;
3919 }
3920 else
3921 {
3922 u32UpdateIntervalTSC = (uint32_t)u64TSCDelta;
3923
3924 /* This value hasn't be checked yet.. waiting for OS/2 and 33Hz timers.. :-) */
3925 u32UpdateIntervalTSCSlack = u32UpdateIntervalTSC >> 6;
3926 }
3927 ASMAtomicXchgU32(&pGipCpu->u32UpdateIntervalTSC, u32UpdateIntervalTSC + u32UpdateIntervalTSCSlack);
3928
3929 /*
3930 * CpuHz.
3931 */
3932 u64CpuHz = ASMMult2xU32RetU64(u32UpdateIntervalTSC, pGip->u32UpdateHz);
3933 ASMAtomicXchgU64(&pGipCpu->u64CpuHz, u64CpuHz);
3934}
3935
3936
3937/**
3938 * Updates the GIP.
3939 *
3940 * @param pGip Pointer to the GIP.
3941 * @param u64NanoTS The current nanosecond timesamp.
3942 */
3943void VBOXCALL supdrvGipUpdate(PSUPGLOBALINFOPAGE pGip, uint64_t u64NanoTS)
3944{
3945 /*
3946 * Determin the relevant CPU data.
3947 */
3948 PSUPGIPCPU pGipCpu;
3949 if (pGip->u32Mode != SUPGIPMODE_ASYNC_TSC)
3950 pGipCpu = &pGip->aCPUs[0];
3951 else
3952 {
3953 unsigned iCpu = ASMGetApicId();
3954 if (RT_LIKELY(iCpu >= RT_ELEMENTS(pGip->aCPUs)))
3955 return;
3956 pGipCpu = &pGip->aCPUs[iCpu];
3957 }
3958
3959 /*
3960 * Start update transaction.
3961 */
3962 if (!(ASMAtomicIncU32(&pGipCpu->u32TransactionId) & 1))
3963 {
3964 /* this can happen on win32 if we're taking to long and there are more CPUs around. shouldn't happen though. */
3965 AssertMsgFailed(("Invalid transaction id, %#x, not odd!\n", pGipCpu->u32TransactionId));
3966 ASMAtomicIncU32(&pGipCpu->u32TransactionId);
3967 pGipCpu->cErrors++;
3968 return;
3969 }
3970
3971 /*
3972 * Recalc the update frequency every 0x800th time.
3973 */
3974 if (!(pGipCpu->u32TransactionId & (GIP_UPDATEHZ_RECALC_FREQ * 2 - 2)))
3975 {
3976 if (pGip->u64NanoTSLastUpdateHz)
3977 {
3978#ifdef RT_ARCH_AMD64 /** @todo fix 64-bit div here to work on x86 linux. */
3979 uint64_t u64Delta = u64NanoTS - pGip->u64NanoTSLastUpdateHz;
3980 uint32_t u32UpdateHz = (uint32_t)((UINT64_C(1000000000) * GIP_UPDATEHZ_RECALC_FREQ) / u64Delta);
3981 if (u32UpdateHz <= 2000 && u32UpdateHz >= 30)
3982 {
3983 ASMAtomicXchgU32(&pGip->u32UpdateHz, u32UpdateHz);
3984 ASMAtomicXchgU32(&pGip->u32UpdateIntervalNS, 1000000000 / u32UpdateHz);
3985 }
3986#endif
3987 }
3988 ASMAtomicXchgU64(&pGip->u64NanoTSLastUpdateHz, u64NanoTS);
3989 }
3990
3991 /*
3992 * Update the data.
3993 */
3994 supdrvGipDoUpdateCpu(pGip, pGipCpu, u64NanoTS);
3995
3996 /*
3997 * Complete transaction.
3998 */
3999 ASMAtomicIncU32(&pGipCpu->u32TransactionId);
4000}
4001
4002
4003/**
4004 * Updates the per cpu GIP data for the calling cpu.
4005 *
4006 * @param pGip Pointer to the GIP.
4007 * @param u64NanoTS The current nanosecond timesamp.
4008 * @param iCpu The CPU index.
4009 */
4010void VBOXCALL supdrvGipUpdatePerCpu(PSUPGLOBALINFOPAGE pGip, uint64_t u64NanoTS, unsigned iCpu)
4011{
4012 PSUPGIPCPU pGipCpu;
4013
4014 if (RT_LIKELY(iCpu <= RT_ELEMENTS(pGip->aCPUs)))
4015 {
4016 pGipCpu = &pGip->aCPUs[iCpu];
4017
4018 /*
4019 * Start update transaction.
4020 */
4021 if (!(ASMAtomicIncU32(&pGipCpu->u32TransactionId) & 1))
4022 {
4023 AssertMsgFailed(("Invalid transaction id, %#x, not odd!\n", pGipCpu->u32TransactionId));
4024 ASMAtomicIncU32(&pGipCpu->u32TransactionId);
4025 pGipCpu->cErrors++;
4026 return;
4027 }
4028
4029 /*
4030 * Update the data.
4031 */
4032 supdrvGipDoUpdateCpu(pGip, pGipCpu, u64NanoTS);
4033
4034 /*
4035 * Complete transaction.
4036 */
4037 ASMAtomicIncU32(&pGipCpu->u32TransactionId);
4038 }
4039}
4040
4041
4042#ifndef DEBUG /** @todo change #ifndef DEBUG -> #ifdef LOG_ENABLED */
4043/**
4044 * Stub function for non-debug builds.
4045 */
4046RTDECL(PRTLOGGER) RTLogDefaultInstance(void)
4047{
4048 return NULL;
4049}
4050
4051RTDECL(PRTLOGGER) RTLogRelDefaultInstance(void)
4052{
4053 return NULL;
4054}
4055
4056/**
4057 * Stub function for non-debug builds.
4058 */
4059RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey)
4060{
4061 return 0;
4062}
4063
4064/**
4065 * Stub function for non-debug builds.
4066 */
4067RTDECL(void) RTLogLogger(PRTLOGGER pLogger, void *pvCallerRet, const char *pszFormat, ...)
4068{
4069}
4070
4071/**
4072 * Stub function for non-debug builds.
4073 */
4074RTDECL(void) RTLogLoggerEx(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
4075{
4076}
4077
4078/**
4079 * Stub function for non-debug builds.
4080 */
4081RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
4082{
4083}
4084
4085/**
4086 * Stub function for non-debug builds.
4087 */
4088RTDECL(void) RTLogPrintf(const char *pszFormat, ...)
4089{
4090}
4091
4092/**
4093 * Stub function for non-debug builds.
4094 */
4095RTDECL(void) RTLogPrintfV(const char *pszFormat, va_list args)
4096{
4097}
4098#endif /* !DEBUG */
4099
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