VirtualBox

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

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

Increased the IOC version to 7.4 with the exporting of SUPR0ComponentQueryFactory and friends. Added release logging to darwin, windows and freebsd.

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

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