VirtualBox

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

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

The Big Sun Rebranding Header Change

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