VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/SUPDrv.c@ 10377

Last change on this file since 10377 was 10377, checked in by vboxsync, 16 years ago

Implemented the IDC methods. Moved the setting of the R0Process and Process SUPDRVSESSION members to SUPDrv.c, and made SUPDrv.c provide default initialization of the Uid and Gid members.

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