VirtualBox

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

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

VMM+SUPDrv: Changed the VMMR0EntryEx interface to also take the session as an argument, this means breaking the current IOC interface and thus a major driver version change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 171.2 KB
Line 
1/* $Revision: 10805 $ */
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 { "RTTimeNanoTS", (void *)RTTimeNanoTS },
195 { "RTTimeMillieTS", (void *)RTTimeMilliTS },
196 { "RTTimeSystemNanoTS", (void *)RTTimeSystemNanoTS },
197 { "RTTimeSystemMillieTS", (void *)RTTimeSystemMilliTS },
198 { "RTThreadNativeSelf", (void *)RTThreadNativeSelf },
199 { "RTThreadSleep", (void *)RTThreadSleep },
200 { "RTThreadYield", (void *)RTThreadYield },
201#if 0 /* Thread APIs, Part 2. */
202 { "RTThreadSelf", (void *)RTThreadSelf },
203 { "RTThreadCreate", (void *)RTThreadCreate },
204 { "RTThreadGetNative", (void *)RTThreadGetNative },
205 { "RTThreadWait", (void *)RTThreadWait },
206 { "RTThreadWaitNoResume", (void *)RTThreadWaitNoResume },
207 { "RTThreadGetName", (void *)RTThreadGetName },
208 { "RTThreadSelfName", (void *)RTThreadSelfName },
209 { "RTThreadGetType", (void *)RTThreadGetType },
210 { "RTThreadUserSignal", (void *)RTThreadUserSignal },
211 { "RTThreadUserReset", (void *)RTThreadUserReset },
212 { "RTThreadUserWait", (void *)RTThreadUserWait },
213 { "RTThreadUserWaitNoResume", (void *)RTThreadUserWaitNoResume },
214#endif
215 { "RTLogDefaultInstance", (void *)RTLogDefaultInstance },
216 { "RTMpCpuId", (void *)RTMpCpuId },
217 { "RTMpCpuIdFromSetIndex", (void *)RTMpCpuIdFromSetIndex },
218 { "RTMpCpuIdToSetIndex", (void *)RTMpCpuIdToSetIndex },
219 { "RTMpIsCpuPossible", (void *)RTMpIsCpuPossible },
220 { "RTMpGetCount", (void *)RTMpGetCount },
221 { "RTMpGetMaxCpuId", (void *)RTMpGetMaxCpuId },
222 { "RTMpGetOnlineCount", (void *)RTMpGetOnlineCount },
223 { "RTMpGetOnlineSet", (void *)RTMpGetOnlineSet },
224 { "RTMpGetSet", (void *)RTMpGetSet },
225 { "RTMpIsCpuOnline", (void *)RTMpIsCpuOnline },
226 { "RTMpOnAll", (void *)RTMpOnAll },
227 { "RTMpOnOthers", (void *)RTMpOnOthers },
228 { "RTMpOnSpecific", (void *)RTMpOnSpecific },
229 { "RTLogRelDefaultInstance", (void *)RTLogRelDefaultInstance },
230 { "RTLogSetDefaultInstanceThread", (void *)RTLogSetDefaultInstanceThread },
231 { "RTLogLogger", (void *)RTLogLogger },
232 { "RTLogLoggerEx", (void *)RTLogLoggerEx },
233 { "RTLogLoggerExV", (void *)RTLogLoggerExV },
234 { "RTLogPrintf", (void *)RTLogPrintf },
235 { "RTLogPrintfV", (void *)RTLogPrintfV },
236 { "AssertMsg1", (void *)AssertMsg1 },
237 { "AssertMsg2", (void *)AssertMsg2 },
238};
239
240
241/*******************************************************************************
242* Internal Functions *
243*******************************************************************************/
244static int supdrvMemAdd(PSUPDRVMEMREF pMem, PSUPDRVSESSION pSession);
245static int supdrvMemRelease(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr, SUPDRVMEMREFTYPE eType);
246#ifdef VBOX_WITH_IDT_PATCHING
247static int supdrvIOCtl_IdtInstall(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPIDTINSTALL pReq);
248static PSUPDRVPATCH supdrvIdtPatchOne(PSUPDRVDEVEXT pDevExt, PSUPDRVPATCH pPatch);
249static int supdrvIOCtl_IdtRemoveAll(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession);
250static void supdrvIdtRemoveOne(PSUPDRVDEVEXT pDevExt, PSUPDRVPATCH pPatch);
251static void supdrvIdtWrite(volatile void *pvIdtEntry, const SUPDRVIDTE *pNewIDTEntry);
252#endif /* VBOX_WITH_IDT_PATCHING */
253static int supdrvIOCtl_LdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDROPEN pReq);
254static int supdrvIOCtl_LdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRLOAD pReq);
255static int supdrvIOCtl_LdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRFREE pReq);
256static int supdrvIOCtl_LdrGetSymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRGETSYMBOL pReq);
257static int supdrvIDC_LdrGetSymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVIDCREQGETSYM pReq);
258static int supdrvLdrSetR0EP(PSUPDRVDEVEXT pDevExt, void *pvVMMR0, void *pvVMMR0EntryInt, void *pvVMMR0EntryFast, void *pvVMMR0EntryEx);
259static void supdrvLdrUnsetR0EP(PSUPDRVDEVEXT pDevExt);
260static int supdrvLdrAddUsage(PSUPDRVSESSION pSession, PSUPDRVLDRIMAGE pImage);
261static void supdrvLdrFree(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage);
262static SUPPAGINGMODE supdrvIOCtl_GetPagingMode(void);
263static SUPGIPMODE supdrvGipDeterminTscMode(PSUPDRVDEVEXT pDevExt);
264#ifdef RT_OS_WINDOWS
265static int supdrvPageGetPhys(PSUPDRVSESSION pSession, RTR3PTR pvR3, uint32_t cPages, PRTHCPHYS paPages);
266static bool supdrvPageWasLockedByPageAlloc(PSUPDRVSESSION pSession, RTR3PTR pvR3);
267#endif /* RT_OS_WINDOWS */
268static int supdrvGipCreate(PSUPDRVDEVEXT pDevExt);
269static void supdrvGipDestroy(PSUPDRVDEVEXT pDevExt);
270static DECLCALLBACK(void) supdrvGipSyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
271static DECLCALLBACK(void) supdrvGipAsyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
272static DECLCALLBACK(void) supdrvGipMpEvent(RTMPEVENT enmEvent, RTCPUID idCpu, void *pvUser);
273
274
275/**
276 * Initializes the device extentsion structure.
277 *
278 * @returns IPRT status code.
279 * @param pDevExt The device extension to initialize.
280 */
281int VBOXCALL supdrvInitDevExt(PSUPDRVDEVEXT pDevExt)
282{
283 int rc;
284
285#ifdef SUPDRV_WITH_RELEASE_LOGGER
286 /*
287 * Create the release log.
288 */
289 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
290 PRTLOGGER pRelLogger;
291 rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all",
292 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
293 RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER, NULL);
294 if (RT_SUCCESS(rc))
295 RTLogRelSetDefaultInstance(pRelLogger);
296#endif
297
298 /*
299 * Initialize it.
300 */
301 memset(pDevExt, 0, sizeof(*pDevExt));
302 rc = RTSpinlockCreate(&pDevExt->Spinlock);
303 if (!rc)
304 {
305 rc = RTSemFastMutexCreate(&pDevExt->mtxLdr);
306 if (!rc)
307 {
308 rc = RTSemFastMutexCreate(&pDevExt->mtxComponentFactory);
309 if (!rc)
310 {
311 rc = RTSemFastMutexCreate(&pDevExt->mtxGip);
312 if (!rc)
313 {
314 rc = supdrvGipCreate(pDevExt);
315 if (RT_SUCCESS(rc))
316 {
317 pDevExt->u32Cookie = BIRD; /** @todo make this random? */
318 return VINF_SUCCESS;
319 }
320
321 RTSemFastMutexDestroy(pDevExt->mtxGip);
322 pDevExt->mtxGip = NIL_RTSEMFASTMUTEX;
323 }
324 RTSemFastMutexDestroy(pDevExt->mtxComponentFactory);
325 pDevExt->mtxComponentFactory = NIL_RTSEMFASTMUTEX;
326 }
327 RTSemFastMutexDestroy(pDevExt->mtxLdr);
328 pDevExt->mtxLdr = NIL_RTSEMFASTMUTEX;
329 }
330 RTSpinlockDestroy(pDevExt->Spinlock);
331 pDevExt->Spinlock = NIL_RTSPINLOCK;
332 }
333#ifdef SUPDRV_WITH_RELEASE_LOGGER
334 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
335 RTLogDestroy(RTLogSetDefaultInstance(NULL));
336#endif
337
338 return rc;
339}
340
341
342/**
343 * Delete the device extension (e.g. cleanup members).
344 *
345 * @param pDevExt The device extension to delete.
346 */
347void VBOXCALL supdrvDeleteDevExt(PSUPDRVDEVEXT pDevExt)
348{
349#ifdef VBOX_WITH_IDT_PATCHING
350 PSUPDRVPATCH pPatch;
351#endif
352 PSUPDRVOBJ pObj;
353 PSUPDRVUSAGE pUsage;
354
355 /*
356 * Kill mutexes and spinlocks.
357 */
358 RTSemFastMutexDestroy(pDevExt->mtxGip);
359 pDevExt->mtxGip = NIL_RTSEMFASTMUTEX;
360 RTSemFastMutexDestroy(pDevExt->mtxLdr);
361 pDevExt->mtxLdr = NIL_RTSEMFASTMUTEX;
362 RTSpinlockDestroy(pDevExt->Spinlock);
363 pDevExt->Spinlock = NIL_RTSPINLOCK;
364
365 /*
366 * Free lists.
367 */
368#ifdef VBOX_WITH_IDT_PATCHING
369 /* patches */
370 /** @todo make sure we don't uninstall patches which has been patched by someone else. */
371 pPatch = pDevExt->pIdtPatchesFree;
372 pDevExt->pIdtPatchesFree = NULL;
373 while (pPatch)
374 {
375 void *pvFree = pPatch;
376 pPatch = pPatch->pNext;
377 RTMemExecFree(pvFree);
378 }
379#endif /* VBOX_WITH_IDT_PATCHING */
380
381 /* objects. */
382 pObj = pDevExt->pObjs;
383#if !defined(DEBUG_bird) || !defined(RT_OS_LINUX) /* breaks unloading, temporary, remove me! */
384 Assert(!pObj); /* (can trigger on forced unloads) */
385#endif
386 pDevExt->pObjs = NULL;
387 while (pObj)
388 {
389 void *pvFree = pObj;
390 pObj = pObj->pNext;
391 RTMemFree(pvFree);
392 }
393
394 /* usage records. */
395 pUsage = pDevExt->pUsageFree;
396 pDevExt->pUsageFree = NULL;
397 while (pUsage)
398 {
399 void *pvFree = pUsage;
400 pUsage = pUsage->pNext;
401 RTMemFree(pvFree);
402 }
403
404 /* kill the GIP. */
405 supdrvGipDestroy(pDevExt);
406
407#ifdef SUPDRV_WITH_RELEASE_LOGGER
408 /* destroy the loggers. */
409 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
410 RTLogDestroy(RTLogSetDefaultInstance(NULL));
411#endif
412}
413
414
415/**
416 * Create session.
417 *
418 * @returns IPRT status code.
419 * @param pDevExt Device extension.
420 * @param fUser Flag indicating whether this is a user or kernel session.
421 * @param ppSession Where to store the pointer to the session data.
422 */
423int VBOXCALL supdrvCreateSession(PSUPDRVDEVEXT pDevExt, bool fUser, PSUPDRVSESSION *ppSession)
424{
425 /*
426 * Allocate memory for the session data.
427 */
428 int rc = VERR_NO_MEMORY;
429 PSUPDRVSESSION pSession = *ppSession = (PSUPDRVSESSION)RTMemAllocZ(sizeof(*pSession));
430 if (pSession)
431 {
432 /* Initialize session data. */
433 rc = RTSpinlockCreate(&pSession->Spinlock);
434 if (!rc)
435 {
436 Assert(pSession->Spinlock != NIL_RTSPINLOCK);
437 pSession->pDevExt = pDevExt;
438 pSession->u32Cookie = BIRD_INV;
439 /*pSession->pLdrUsage = NULL;
440 pSession->pPatchUsage = NULL;
441 pSession->pVM = NULL;
442 pSession->pUsage = NULL;
443 pSession->pGip = NULL;
444 pSession->fGipReferenced = false;
445 pSession->Bundle.cUsed = 0; */
446 pSession->Uid = NIL_RTUID;
447 pSession->Gid = NIL_RTGID;
448 if (fUser)
449 {
450 pSession->Process = RTProcSelf();
451 pSession->R0Process = RTR0ProcHandleSelf();
452 }
453 else
454 {
455 pSession->Process = NIL_RTPROCESS;
456 pSession->R0Process = NIL_RTR0PROCESS;
457 }
458
459 LogFlow(("Created session %p initial cookie=%#x\n", pSession, pSession->u32Cookie));
460 return VINF_SUCCESS;
461 }
462
463 RTMemFree(pSession);
464 *ppSession = NULL;
465 Log(("Failed to create spinlock, rc=%d!\n", rc));
466 }
467
468 return rc;
469}
470
471
472/**
473 * Shared code for cleaning up a session.
474 *
475 * @param pDevExt Device extension.
476 * @param pSession Session data.
477 * This data will be freed by this routine.
478 */
479void VBOXCALL supdrvCloseSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
480{
481 /*
482 * Cleanup the session first.
483 */
484 supdrvCleanupSession(pDevExt, pSession);
485
486 /*
487 * Free the rest of the session stuff.
488 */
489 RTSpinlockDestroy(pSession->Spinlock);
490 pSession->Spinlock = NIL_RTSPINLOCK;
491 pSession->pDevExt = NULL;
492 RTMemFree(pSession);
493 LogFlow(("supdrvCloseSession: returns\n"));
494}
495
496
497/**
498 * Shared code for cleaning up a session (but not quite freeing it).
499 *
500 * This is primarily intended for MAC OS X where we have to clean up the memory
501 * stuff before the file handle is closed.
502 *
503 * @param pDevExt Device extension.
504 * @param pSession Session data.
505 * This data will be freed by this routine.
506 */
507void VBOXCALL supdrvCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
508{
509 PSUPDRVBUNDLE pBundle;
510 LogFlow(("supdrvCleanupSession: pSession=%p\n", pSession));
511
512 /*
513 * Remove logger instances related to this session.
514 */
515 RTLogSetDefaultInstanceThread(NULL, (uintptr_t)pSession);
516
517#ifdef VBOX_WITH_IDT_PATCHING
518 /*
519 * Uninstall any IDT patches installed for this session.
520 */
521 supdrvIOCtl_IdtRemoveAll(pDevExt, pSession);
522#endif
523
524 /*
525 * Release object references made in this session.
526 * In theory there should be noone racing us in this session.
527 */
528 Log2(("release objects - start\n"));
529 if (pSession->pUsage)
530 {
531 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
532 PSUPDRVUSAGE pUsage;
533 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
534
535 while ((pUsage = pSession->pUsage) != NULL)
536 {
537 PSUPDRVOBJ pObj = pUsage->pObj;
538 pSession->pUsage = pUsage->pNext;
539
540 AssertMsg(pUsage->cUsage >= 1 && pObj->cUsage >= pUsage->cUsage, ("glob %d; sess %d\n", pObj->cUsage, pUsage->cUsage));
541 if (pUsage->cUsage < pObj->cUsage)
542 {
543 pObj->cUsage -= pUsage->cUsage;
544 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
545 }
546 else
547 {
548 /* Destroy the object and free the record. */
549 if (pDevExt->pObjs == pObj)
550 pDevExt->pObjs = pObj->pNext;
551 else
552 {
553 PSUPDRVOBJ pObjPrev;
554 for (pObjPrev = pDevExt->pObjs; pObjPrev; pObjPrev = pObjPrev->pNext)
555 if (pObjPrev->pNext == pObj)
556 {
557 pObjPrev->pNext = pObj->pNext;
558 break;
559 }
560 Assert(pObjPrev);
561 }
562 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
563
564 Log(("supdrvCleanupSession: destroying %p/%d (%p/%p) cpid=%RTproc pid=%RTproc dtor=%p\n",
565 pObj, pObj->enmType, pObj->pvUser1, pObj->pvUser2, pObj->CreatorProcess, RTProcSelf(), pObj->pfnDestructor));
566 if (pObj->pfnDestructor)
567 pObj->pfnDestructor(pObj, pObj->pvUser1, pObj->pvUser2);
568 RTMemFree(pObj);
569 }
570
571 /* free it and continue. */
572 RTMemFree(pUsage);
573
574 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
575 }
576
577 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
578 AssertMsg(!pSession->pUsage, ("Some buster reregistered an object during desturction!\n"));
579 }
580 Log2(("release objects - done\n"));
581
582 /*
583 * Release memory allocated in the session.
584 *
585 * We do not serialize this as we assume that the application will
586 * not allocated memory while closing the file handle object.
587 */
588 Log2(("freeing memory:\n"));
589 pBundle = &pSession->Bundle;
590 while (pBundle)
591 {
592 PSUPDRVBUNDLE pToFree;
593 unsigned i;
594
595 /*
596 * Check and unlock all entries in the bundle.
597 */
598 for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
599 {
600 if (pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ)
601 {
602 int rc;
603 Log2(("eType=%d pvR0=%p pvR3=%p cb=%ld\n", pBundle->aMem[i].eType, RTR0MemObjAddress(pBundle->aMem[i].MemObj),
604 (void *)RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3), (long)RTR0MemObjSize(pBundle->aMem[i].MemObj)));
605 if (pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ)
606 {
607 rc = RTR0MemObjFree(pBundle->aMem[i].MapObjR3, false);
608 AssertRC(rc); /** @todo figure out how to handle this. */
609 pBundle->aMem[i].MapObjR3 = NIL_RTR0MEMOBJ;
610 }
611 rc = RTR0MemObjFree(pBundle->aMem[i].MemObj, false);
612 AssertRC(rc); /** @todo figure out how to handle this. */
613 pBundle->aMem[i].MemObj = NIL_RTR0MEMOBJ;
614 pBundle->aMem[i].eType = MEMREF_TYPE_UNUSED;
615 }
616 }
617
618 /*
619 * Advance and free previous bundle.
620 */
621 pToFree = pBundle;
622 pBundle = pBundle->pNext;
623
624 pToFree->pNext = NULL;
625 pToFree->cUsed = 0;
626 if (pToFree != &pSession->Bundle)
627 RTMemFree(pToFree);
628 }
629 Log2(("freeing memory - done\n"));
630
631 /*
632 * Deregister component factories.
633 */
634 RTSemFastMutexRequest(pDevExt->mtxComponentFactory);
635 Log2(("deregistering component factories:\n"));
636 if (pDevExt->pComponentFactoryHead)
637 {
638 PSUPDRVFACTORYREG pPrev = NULL;
639 PSUPDRVFACTORYREG pCur = pDevExt->pComponentFactoryHead;
640 while (pCur)
641 {
642 if (pCur->pSession == pSession)
643 {
644 /* unlink it */
645 PSUPDRVFACTORYREG pNext = pCur->pNext;
646 if (pPrev)
647 pPrev->pNext = pNext;
648 else
649 pDevExt->pComponentFactoryHead = pNext;
650
651 /* free it */
652 pCur->pNext = NULL;
653 pCur->pSession = NULL;
654 pCur->pFactory = NULL;
655 RTMemFree(pCur);
656
657 /* next */
658 pCur = pNext;
659 }
660 else
661 {
662 /* next */
663 pPrev = pCur;
664 pCur = pCur->pNext;
665 }
666 }
667 }
668 RTSemFastMutexRelease(pDevExt->mtxComponentFactory);
669 Log2(("deregistering component factories - done\n"));
670
671 /*
672 * Loaded images needs to be dereferenced and possibly freed up.
673 */
674 RTSemFastMutexRequest(pDevExt->mtxLdr);
675 Log2(("freeing images:\n"));
676 if (pSession->pLdrUsage)
677 {
678 PSUPDRVLDRUSAGE pUsage = pSession->pLdrUsage;
679 pSession->pLdrUsage = NULL;
680 while (pUsage)
681 {
682 void *pvFree = pUsage;
683 PSUPDRVLDRIMAGE pImage = pUsage->pImage;
684 if (pImage->cUsage > pUsage->cUsage)
685 pImage->cUsage -= pUsage->cUsage;
686 else
687 supdrvLdrFree(pDevExt, pImage);
688 pUsage->pImage = NULL;
689 pUsage = pUsage->pNext;
690 RTMemFree(pvFree);
691 }
692 }
693 RTSemFastMutexRelease(pDevExt->mtxLdr);
694 Log2(("freeing images - done\n"));
695
696 /*
697 * Unmap the GIP.
698 */
699 Log2(("umapping GIP:\n"));
700 if (pSession->GipMapObjR3 != NIL_RTR0MEMOBJ)
701 {
702 SUPR0GipUnmap(pSession);
703 pSession->fGipReferenced = 0;
704 }
705 Log2(("umapping GIP - done\n"));
706}
707
708
709/**
710 * Fast path I/O Control worker.
711 *
712 * @returns VBox status code that should be passed down to ring-3 unchanged.
713 * @param uIOCtl Function number.
714 * @param pDevExt Device extention.
715 * @param pSession Session data.
716 */
717int VBOXCALL supdrvIOCtlFast(uintptr_t uIOCtl, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
718{
719 /*
720 * We check the two prereqs after doing this only to allow the compiler to optimize things better.
721 */
722 if (RT_LIKELY(pSession->pVM && pDevExt->pfnVMMR0EntryFast))
723 {
724 switch (uIOCtl)
725 {
726 case SUP_IOCTL_FAST_DO_RAW_RUN:
727 pDevExt->pfnVMMR0EntryFast(pSession->pVM, SUP_VMMR0_DO_RAW_RUN);
728 break;
729 case SUP_IOCTL_FAST_DO_HWACC_RUN:
730 pDevExt->pfnVMMR0EntryFast(pSession->pVM, SUP_VMMR0_DO_HWACC_RUN);
731 break;
732 case SUP_IOCTL_FAST_DO_NOP:
733 pDevExt->pfnVMMR0EntryFast(pSession->pVM, SUP_VMMR0_DO_NOP);
734 break;
735 default:
736 return VERR_INTERNAL_ERROR;
737 }
738 return VINF_SUCCESS;
739 }
740 return VERR_INTERNAL_ERROR;
741}
742
743
744/**
745 * Helper for supdrvIOCtl. Check if pszStr contains any character of pszChars.
746 * We would use strpbrk here if this function would be contained in the RedHat kABI white
747 * list, see http://www.kerneldrivers.org/RHEL5.
748 *
749 * @return 1 if pszStr does contain any character of pszChars, 0 otherwise.
750 * @param pszStr String to check
751 * @param pszChars Character set
752 */
753static int supdrvCheckInvalidChar(const char *pszStr, const char *pszChars)
754{
755 int chCur;
756 while ((chCur = *pszStr++) != '\0')
757 {
758 int ch;
759 const char *psz = pszChars;
760 while ((ch = *psz++) != '\0')
761 if (ch == chCur)
762 return 1;
763
764 }
765 return 0;
766}
767
768
769/**
770 * I/O Control worker.
771 *
772 * @returns 0 on success.
773 * @returns VERR_INVALID_PARAMETER if the request is invalid.
774 *
775 * @param uIOCtl Function number.
776 * @param pDevExt Device extention.
777 * @param pSession Session data.
778 * @param pReqHdr The request header.
779 */
780int VBOXCALL supdrvIOCtl(uintptr_t uIOCtl, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPREQHDR pReqHdr)
781{
782 /*
783 * Validate the request.
784 */
785 /* this first check could probably be omitted as its also done by the OS specific code... */
786 if (RT_UNLIKELY( (pReqHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC
787 || pReqHdr->cbIn < sizeof(*pReqHdr)
788 || pReqHdr->cbOut < sizeof(*pReqHdr)))
789 {
790 OSDBGPRINT(("vboxdrv: Bad ioctl request header; cbIn=%#lx cbOut=%#lx fFlags=%#lx\n",
791 (long)pReqHdr->cbIn, (long)pReqHdr->cbOut, (long)pReqHdr->fFlags));
792 return VERR_INVALID_PARAMETER;
793 }
794 if (RT_UNLIKELY(uIOCtl == SUP_IOCTL_COOKIE))
795 {
796 if (pReqHdr->u32Cookie != SUPCOOKIE_INITIAL_COOKIE)
797 {
798 OSDBGPRINT(("SUP_IOCTL_COOKIE: bad cookie %#lx\n", (long)pReqHdr->u32Cookie));
799 return VERR_INVALID_PARAMETER;
800 }
801 }
802 else if (RT_UNLIKELY( pReqHdr->u32Cookie != pDevExt->u32Cookie
803 || pReqHdr->u32SessionCookie != pSession->u32Cookie))
804 {
805 OSDBGPRINT(("vboxdrv: bad cookie %#lx / %#lx.\n", (long)pReqHdr->u32Cookie, (long)pReqHdr->u32SessionCookie));
806 return VERR_INVALID_PARAMETER;
807 }
808
809/*
810 * Validation macros
811 */
812#define REQ_CHECK_SIZES_EX(Name, cbInExpect, cbOutExpect) \
813 do { \
814 if (RT_UNLIKELY(pReqHdr->cbIn != (cbInExpect) || pReqHdr->cbOut != (cbOutExpect))) \
815 { \
816 OSDBGPRINT(( #Name ": Invalid input/output sizes. cbIn=%ld expected %ld. cbOut=%ld expected %ld.\n", \
817 (long)pReq->Hdr.cbIn, (long)(cbInExpect), (long)pReq->Hdr.cbOut, (long)(cbOutExpect))); \
818 return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \
819 } \
820 } while (0)
821
822#define REQ_CHECK_SIZES(Name) REQ_CHECK_SIZES_EX(Name, Name ## _SIZE_IN, Name ## _SIZE_OUT)
823
824#define REQ_CHECK_SIZE_IN(Name, cbInExpect) \
825 do { \
826 if (RT_UNLIKELY(pReqHdr->cbIn != (cbInExpect))) \
827 { \
828 OSDBGPRINT(( #Name ": Invalid input/output sizes. cbIn=%ld expected %ld.\n", \
829 (long)pReq->Hdr.cbIn, (long)(cbInExpect))); \
830 return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \
831 } \
832 } while (0)
833
834#define REQ_CHECK_SIZE_OUT(Name, cbOutExpect) \
835 do { \
836 if (RT_UNLIKELY(pReqHdr->cbOut != (cbOutExpect))) \
837 { \
838 OSDBGPRINT(( #Name ": Invalid input/output sizes. cbOut=%ld expected %ld.\n", \
839 (long)pReq->Hdr.cbOut, (long)(cbOutExpect))); \
840 return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \
841 } \
842 } while (0)
843
844#define REQ_CHECK_EXPR(Name, expr) \
845 do { \
846 if (RT_UNLIKELY(!(expr))) \
847 { \
848 OSDBGPRINT(( #Name ": %s\n", #expr)); \
849 return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \
850 } \
851 } while (0)
852
853#define REQ_CHECK_EXPR_FMT(expr, fmt) \
854 do { \
855 if (RT_UNLIKELY(!(expr))) \
856 { \
857 OSDBGPRINT( fmt ); \
858 return pReq->Hdr.rc = VERR_INVALID_PARAMETER; \
859 } \
860 } while (0)
861
862
863 /*
864 * The switch.
865 */
866 switch (SUP_CTL_CODE_NO_SIZE(uIOCtl))
867 {
868 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_COOKIE):
869 {
870 PSUPCOOKIE pReq = (PSUPCOOKIE)pReqHdr;
871 REQ_CHECK_SIZES(SUP_IOCTL_COOKIE);
872 if (strncmp(pReq->u.In.szMagic, SUPCOOKIE_MAGIC, sizeof(pReq->u.In.szMagic)))
873 {
874 OSDBGPRINT(("SUP_IOCTL_COOKIE: invalid magic %.16s\n", pReq->u.In.szMagic));
875 pReq->Hdr.rc = VERR_INVALID_MAGIC;
876 return 0;
877 }
878
879#if 0
880 /*
881 * Call out to the OS specific code and let it do permission checks on the
882 * client process.
883 */
884 if (!supdrvOSValidateClientProcess(pDevExt, pSession))
885 {
886 pReq->u.Out.u32Cookie = 0xffffffff;
887 pReq->u.Out.u32SessionCookie = 0xffffffff;
888 pReq->u.Out.u32SessionVersion = 0xffffffff;
889 pReq->u.Out.u32DriverVersion = SUPDRV_IOC_VERSION;
890 pReq->u.Out.pSession = NULL;
891 pReq->u.Out.cFunctions = 0;
892 pReq->Hdr.rc = VERR_PERMISSION_DENIED;
893 return 0;
894 }
895#endif
896
897 /*
898 * Match the version.
899 * The current logic is very simple, match the major interface version.
900 */
901 if ( pReq->u.In.u32MinVersion > SUPDRV_IOC_VERSION
902 || (pReq->u.In.u32MinVersion & 0xffff0000) != (SUPDRV_IOC_VERSION & 0xffff0000))
903 {
904 OSDBGPRINT(("SUP_IOCTL_COOKIE: Version mismatch. Requested: %#x Min: %#x Current: %#x\n",
905 pReq->u.In.u32ReqVersion, pReq->u.In.u32MinVersion, SUPDRV_IOC_VERSION));
906 pReq->u.Out.u32Cookie = 0xffffffff;
907 pReq->u.Out.u32SessionCookie = 0xffffffff;
908 pReq->u.Out.u32SessionVersion = 0xffffffff;
909 pReq->u.Out.u32DriverVersion = SUPDRV_IOC_VERSION;
910 pReq->u.Out.pSession = NULL;
911 pReq->u.Out.cFunctions = 0;
912 pReq->Hdr.rc = VERR_VERSION_MISMATCH;
913 return 0;
914 }
915
916 /*
917 * Fill in return data and be gone.
918 * N.B. The first one to change SUPDRV_IOC_VERSION shall makes sure that
919 * u32SessionVersion <= u32ReqVersion!
920 */
921 /** @todo Somehow validate the client and negotiate a secure cookie... */
922 pReq->u.Out.u32Cookie = pDevExt->u32Cookie;
923 pReq->u.Out.u32SessionCookie = pSession->u32Cookie;
924 pReq->u.Out.u32SessionVersion = SUPDRV_IOC_VERSION;
925 pReq->u.Out.u32DriverVersion = SUPDRV_IOC_VERSION;
926 pReq->u.Out.pSession = pSession;
927 pReq->u.Out.cFunctions = sizeof(g_aFunctions) / sizeof(g_aFunctions[0]);
928 pReq->Hdr.rc = VINF_SUCCESS;
929 return 0;
930 }
931
932 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_QUERY_FUNCS(0)):
933 {
934 /* validate */
935 PSUPQUERYFUNCS pReq = (PSUPQUERYFUNCS)pReqHdr;
936 REQ_CHECK_SIZES_EX(SUP_IOCTL_QUERY_FUNCS, SUP_IOCTL_QUERY_FUNCS_SIZE_IN, SUP_IOCTL_QUERY_FUNCS_SIZE_OUT(RT_ELEMENTS(g_aFunctions)));
937
938 /* execute */
939 pReq->u.Out.cFunctions = RT_ELEMENTS(g_aFunctions);
940 memcpy(&pReq->u.Out.aFunctions[0], g_aFunctions, sizeof(g_aFunctions));
941 pReq->Hdr.rc = VINF_SUCCESS;
942 return 0;
943 }
944
945 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_IDT_INSTALL):
946 {
947 /* validate */
948 PSUPIDTINSTALL pReq = (PSUPIDTINSTALL)pReqHdr;
949 REQ_CHECK_SIZES(SUP_IOCTL_IDT_INSTALL);
950
951 /* execute */
952#ifdef VBOX_WITH_IDT_PATCHING
953 pReq->Hdr.rc = supdrvIOCtl_IdtInstall(pDevExt, pSession, pReq);
954#else
955 pReq->u.Out.u8Idt = 3;
956 pReq->Hdr.rc = VERR_NOT_SUPPORTED;
957#endif
958 return 0;
959 }
960
961 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_IDT_REMOVE):
962 {
963 /* validate */
964 PSUPIDTREMOVE pReq = (PSUPIDTREMOVE)pReqHdr;
965 REQ_CHECK_SIZES(SUP_IOCTL_IDT_REMOVE);
966
967 /* execute */
968#ifdef VBOX_WITH_IDT_PATCHING
969 pReq->Hdr.rc = supdrvIOCtl_IdtRemoveAll(pDevExt, pSession);
970#else
971 pReq->Hdr.rc = VERR_NOT_SUPPORTED;
972#endif
973 return 0;
974 }
975
976 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_LOCK):
977 {
978 /* validate */
979 PSUPPAGELOCK pReq = (PSUPPAGELOCK)pReqHdr;
980 REQ_CHECK_SIZE_IN(SUP_IOCTL_PAGE_LOCK, SUP_IOCTL_PAGE_LOCK_SIZE_IN);
981 REQ_CHECK_SIZE_OUT(SUP_IOCTL_PAGE_LOCK, SUP_IOCTL_PAGE_LOCK_SIZE_OUT(pReq->u.In.cPages));
982 REQ_CHECK_EXPR(SUP_IOCTL_PAGE_LOCK, pReq->u.In.cPages > 0);
983 REQ_CHECK_EXPR(SUP_IOCTL_PAGE_LOCK, pReq->u.In.pvR3 >= PAGE_SIZE);
984
985 /* execute */
986 pReq->Hdr.rc = SUPR0LockMem(pSession, pReq->u.In.pvR3, pReq->u.In.cPages, &pReq->u.Out.aPages[0]);
987 if (RT_FAILURE(pReq->Hdr.rc))
988 pReq->Hdr.cbOut = sizeof(pReq->Hdr);
989 return 0;
990 }
991
992 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_UNLOCK):
993 {
994 /* validate */
995 PSUPPAGEUNLOCK pReq = (PSUPPAGEUNLOCK)pReqHdr;
996 REQ_CHECK_SIZES(SUP_IOCTL_PAGE_UNLOCK);
997
998 /* execute */
999 pReq->Hdr.rc = SUPR0UnlockMem(pSession, pReq->u.In.pvR3);
1000 return 0;
1001 }
1002
1003 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CONT_ALLOC):
1004 {
1005 /* validate */
1006 PSUPCONTALLOC pReq = (PSUPCONTALLOC)pReqHdr;
1007 REQ_CHECK_SIZES(SUP_IOCTL_CONT_ALLOC);
1008
1009 /* execute */
1010 pReq->Hdr.rc = SUPR0ContAlloc(pSession, pReq->u.In.cPages, &pReq->u.Out.pvR0, &pReq->u.Out.pvR3, &pReq->u.Out.HCPhys);
1011 if (RT_FAILURE(pReq->Hdr.rc))
1012 pReq->Hdr.cbOut = sizeof(pReq->Hdr);
1013 return 0;
1014 }
1015
1016 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CONT_FREE):
1017 {
1018 /* validate */
1019 PSUPCONTFREE pReq = (PSUPCONTFREE)pReqHdr;
1020 REQ_CHECK_SIZES(SUP_IOCTL_CONT_FREE);
1021
1022 /* execute */
1023 pReq->Hdr.rc = SUPR0ContFree(pSession, (RTHCUINTPTR)pReq->u.In.pvR3);
1024 return 0;
1025 }
1026
1027 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_OPEN):
1028 {
1029 /* validate */
1030 PSUPLDROPEN pReq = (PSUPLDROPEN)pReqHdr;
1031 REQ_CHECK_SIZES(SUP_IOCTL_LDR_OPEN);
1032 REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.cbImage > 0);
1033 REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.cbImage < _1M*16);
1034 REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, pReq->u.In.szName[0]);
1035 REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, memchr(pReq->u.In.szName, '\0', sizeof(pReq->u.In.szName)));
1036 REQ_CHECK_EXPR(SUP_IOCTL_LDR_OPEN, !supdrvCheckInvalidChar(pReq->u.In.szName, ";:()[]{}/\\|&*%#@!~`\"'"));
1037
1038 /* execute */
1039 pReq->Hdr.rc = supdrvIOCtl_LdrOpen(pDevExt, pSession, pReq);
1040 return 0;
1041 }
1042
1043 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_LOAD):
1044 {
1045 /* validate */
1046 PSUPLDRLOAD pReq = (PSUPLDRLOAD)pReqHdr;
1047 REQ_CHECK_EXPR(Name, pReq->Hdr.cbIn >= sizeof(*pReq));
1048 REQ_CHECK_SIZES_EX(SUP_IOCTL_LDR_LOAD, SUP_IOCTL_LDR_LOAD_SIZE_IN(pReq->u.In.cbImage), SUP_IOCTL_LDR_LOAD_SIZE_OUT);
1049 REQ_CHECK_EXPR(SUP_IOCTL_LDR_LOAD, pReq->u.In.cSymbols <= 16384);
1050 REQ_CHECK_EXPR_FMT( !pReq->u.In.cSymbols
1051 || ( pReq->u.In.offSymbols < pReq->u.In.cbImage
1052 && pReq->u.In.offSymbols + pReq->u.In.cSymbols * sizeof(SUPLDRSYM) <= pReq->u.In.cbImage),
1053 ("SUP_IOCTL_LDR_LOAD: offSymbols=%#lx cSymbols=%#lx cbImage=%#lx\n", (long)pReq->u.In.offSymbols,
1054 (long)pReq->u.In.cSymbols, (long)pReq->u.In.cbImage));
1055 REQ_CHECK_EXPR_FMT( !pReq->u.In.cbStrTab
1056 || ( pReq->u.In.offStrTab < pReq->u.In.cbImage
1057 && pReq->u.In.offStrTab + pReq->u.In.cbStrTab <= pReq->u.In.cbImage
1058 && pReq->u.In.cbStrTab <= pReq->u.In.cbImage),
1059 ("SUP_IOCTL_LDR_LOAD: offStrTab=%#lx cbStrTab=%#lx cbImage=%#lx\n", (long)pReq->u.In.offStrTab,
1060 (long)pReq->u.In.cbStrTab, (long)pReq->u.In.cbImage));
1061
1062 if (pReq->u.In.cSymbols)
1063 {
1064 uint32_t i;
1065 PSUPLDRSYM paSyms = (PSUPLDRSYM)&pReq->u.In.achImage[pReq->u.In.offSymbols];
1066 for (i = 0; i < pReq->u.In.cSymbols; i++)
1067 {
1068 REQ_CHECK_EXPR_FMT(paSyms[i].offSymbol < pReq->u.In.cbImage,
1069 ("SUP_IOCTL_LDR_LOAD: sym #%ld: symb off %#lx (max=%#lx)\n", (long)i, (long)paSyms[i].offSymbol, (long)pReq->u.In.cbImage));
1070 REQ_CHECK_EXPR_FMT(paSyms[i].offName < pReq->u.In.cbStrTab,
1071 ("SUP_IOCTL_LDR_LOAD: sym #%ld: name off %#lx (max=%#lx)\n", (long)i, (long)paSyms[i].offName, (long)pReq->u.In.cbImage));
1072 REQ_CHECK_EXPR_FMT(memchr(&pReq->u.In.achImage[pReq->u.In.offStrTab + paSyms[i].offName], '\0', pReq->u.In.cbStrTab - paSyms[i].offName),
1073 ("SUP_IOCTL_LDR_LOAD: sym #%ld: unterminated name! (%#lx / %#lx)\n", (long)i, (long)paSyms[i].offName, (long)pReq->u.In.cbImage));
1074 }
1075 }
1076
1077 /* execute */
1078 pReq->Hdr.rc = supdrvIOCtl_LdrLoad(pDevExt, pSession, pReq);
1079 return 0;
1080 }
1081
1082 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_FREE):
1083 {
1084 /* validate */
1085 PSUPLDRFREE pReq = (PSUPLDRFREE)pReqHdr;
1086 REQ_CHECK_SIZES(SUP_IOCTL_LDR_FREE);
1087
1088 /* execute */
1089 pReq->Hdr.rc = supdrvIOCtl_LdrFree(pDevExt, pSession, pReq);
1090 return 0;
1091 }
1092
1093 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LDR_GET_SYMBOL):
1094 {
1095 /* validate */
1096 PSUPLDRGETSYMBOL pReq = (PSUPLDRGETSYMBOL)pReqHdr;
1097 REQ_CHECK_SIZES(SUP_IOCTL_LDR_GET_SYMBOL);
1098 REQ_CHECK_EXPR(SUP_IOCTL_LDR_GET_SYMBOL, memchr(pReq->u.In.szSymbol, '\0', sizeof(pReq->u.In.szSymbol)));
1099
1100 /* execute */
1101 pReq->Hdr.rc = supdrvIOCtl_LdrGetSymbol(pDevExt, pSession, pReq);
1102 return 0;
1103 }
1104
1105 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_CALL_VMMR0(0)):
1106 {
1107 /* validate */
1108 PSUPCALLVMMR0 pReq = (PSUPCALLVMMR0)pReqHdr;
1109 Log4(("SUP_IOCTL_CALL_VMMR0: op=%u in=%u arg=%RX64 p/t=%RTproc/%RTthrd\n",
1110 pReq->u.In.uOperation, pReq->Hdr.cbIn, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf()));
1111
1112 if (pReq->Hdr.cbIn == SUP_IOCTL_CALL_VMMR0_SIZE(0))
1113 {
1114 REQ_CHECK_SIZES_EX(SUP_IOCTL_CALL_VMMR0, SUP_IOCTL_CALL_VMMR0_SIZE_IN(0), SUP_IOCTL_CALL_VMMR0_SIZE_OUT(0));
1115
1116 /* execute */
1117 if (RT_LIKELY(pDevExt->pfnVMMR0EntryEx))
1118 pReq->Hdr.rc = pDevExt->pfnVMMR0EntryEx(pReq->u.In.pVMR0, pReq->u.In.uOperation, NULL, pReq->u.In.u64Arg, pSession);
1119 else
1120 pReq->Hdr.rc = VERR_WRONG_ORDER;
1121 }
1122 else
1123 {
1124 PSUPVMMR0REQHDR pVMMReq = (PSUPVMMR0REQHDR)&pReq->abReqPkt[0];
1125 REQ_CHECK_EXPR_FMT(pReq->Hdr.cbIn >= SUP_IOCTL_CALL_VMMR0_SIZE(sizeof(SUPVMMR0REQHDR)),
1126 ("SUP_IOCTL_CALL_VMMR0: cbIn=%#x < %#x\n", pReq->Hdr.cbIn, SUP_IOCTL_CALL_VMMR0_SIZE(sizeof(SUPVMMR0REQHDR))));
1127 REQ_CHECK_EXPR(SUP_IOCTL_CALL_VMMR0, pVMMReq->u32Magic == SUPVMMR0REQHDR_MAGIC);
1128 REQ_CHECK_SIZES_EX(SUP_IOCTL_CALL_VMMR0, SUP_IOCTL_CALL_VMMR0_SIZE_IN(pVMMReq->cbReq), SUP_IOCTL_CALL_VMMR0_SIZE_OUT(pVMMReq->cbReq));
1129
1130 /* execute */
1131 if (RT_LIKELY(pDevExt->pfnVMMR0EntryEx))
1132 pReq->Hdr.rc = pDevExt->pfnVMMR0EntryEx(pReq->u.In.pVMR0, pReq->u.In.uOperation, pVMMReq, pReq->u.In.u64Arg, pSession);
1133 else
1134 pReq->Hdr.rc = VERR_WRONG_ORDER;
1135 }
1136
1137 if ( RT_FAILURE(pReq->Hdr.rc)
1138 && pReq->Hdr.rc != VERR_INTERRUPTED
1139 && pReq->Hdr.rc != VERR_TIMEOUT)
1140 Log(("SUP_IOCTL_CALL_VMMR0: rc=%Rrc op=%u out=%u arg=%RX64 p/t=%RTproc/%RTthrd\n",
1141 pReq->Hdr.rc, pReq->u.In.uOperation, pReq->Hdr.cbOut, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf()));
1142 else
1143 Log4(("SUP_IOCTL_CALL_VMMR0: rc=%Rrc op=%u out=%u arg=%RX64 p/t=%RTproc/%RTthrd\n",
1144 pReq->Hdr.rc, pReq->u.In.uOperation, pReq->Hdr.cbOut, pReq->u.In.u64Arg, RTProcSelf(), RTThreadNativeSelf()));
1145 return 0;
1146 }
1147
1148 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GET_PAGING_MODE):
1149 {
1150 /* validate */
1151 PSUPGETPAGINGMODE pReq = (PSUPGETPAGINGMODE)pReqHdr;
1152 REQ_CHECK_SIZES(SUP_IOCTL_GET_PAGING_MODE);
1153
1154 /* execute */
1155 pReq->Hdr.rc = VINF_SUCCESS;
1156 pReq->u.Out.enmMode = supdrvIOCtl_GetPagingMode();
1157 return 0;
1158 }
1159
1160 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LOW_ALLOC):
1161 {
1162 /* validate */
1163 PSUPLOWALLOC pReq = (PSUPLOWALLOC)pReqHdr;
1164 REQ_CHECK_EXPR(SUP_IOCTL_LOW_ALLOC, pReq->Hdr.cbIn <= SUP_IOCTL_LOW_ALLOC_SIZE_IN);
1165 REQ_CHECK_SIZES_EX(SUP_IOCTL_LOW_ALLOC, SUP_IOCTL_LOW_ALLOC_SIZE_IN, SUP_IOCTL_LOW_ALLOC_SIZE_OUT(pReq->u.In.cPages));
1166
1167 /* execute */
1168 pReq->Hdr.rc = SUPR0LowAlloc(pSession, pReq->u.In.cPages, &pReq->u.Out.pvR0, &pReq->u.Out.pvR3, &pReq->u.Out.aPages[0]);
1169 if (RT_FAILURE(pReq->Hdr.rc))
1170 pReq->Hdr.cbOut = sizeof(pReq->Hdr);
1171 return 0;
1172 }
1173
1174 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_LOW_FREE):
1175 {
1176 /* validate */
1177 PSUPLOWFREE pReq = (PSUPLOWFREE)pReqHdr;
1178 REQ_CHECK_SIZES(SUP_IOCTL_LOW_FREE);
1179
1180 /* execute */
1181 pReq->Hdr.rc = SUPR0LowFree(pSession, (RTHCUINTPTR)pReq->u.In.pvR3);
1182 return 0;
1183 }
1184
1185 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GIP_MAP):
1186 {
1187 /* validate */
1188 PSUPGIPMAP pReq = (PSUPGIPMAP)pReqHdr;
1189 REQ_CHECK_SIZES(SUP_IOCTL_GIP_MAP);
1190
1191 /* execute */
1192 pReq->Hdr.rc = SUPR0GipMap(pSession, &pReq->u.Out.pGipR3, &pReq->u.Out.HCPhysGip);
1193 if (RT_SUCCESS(pReq->Hdr.rc))
1194 pReq->u.Out.pGipR0 = pDevExt->pGip;
1195 return 0;
1196 }
1197
1198 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_GIP_UNMAP):
1199 {
1200 /* validate */
1201 PSUPGIPUNMAP pReq = (PSUPGIPUNMAP)pReqHdr;
1202 REQ_CHECK_SIZES(SUP_IOCTL_GIP_UNMAP);
1203
1204 /* execute */
1205 pReq->Hdr.rc = SUPR0GipUnmap(pSession);
1206 return 0;
1207 }
1208
1209 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_SET_VM_FOR_FAST):
1210 {
1211 /* validate */
1212 PSUPSETVMFORFAST pReq = (PSUPSETVMFORFAST)pReqHdr;
1213 REQ_CHECK_SIZES(SUP_IOCTL_SET_VM_FOR_FAST);
1214 REQ_CHECK_EXPR_FMT( !pReq->u.In.pVMR0
1215 || ( VALID_PTR(pReq->u.In.pVMR0)
1216 && !((uintptr_t)pReq->u.In.pVMR0 & (PAGE_SIZE - 1))),
1217 ("SUP_IOCTL_SET_VM_FOR_FAST: pVMR0=%p!\n", pReq->u.In.pVMR0));
1218 /* execute */
1219 pSession->pVM = pReq->u.In.pVMR0;
1220 pReq->Hdr.rc = VINF_SUCCESS;
1221 return 0;
1222 }
1223
1224 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_ALLOC):
1225 {
1226 /* validate */
1227 PSUPPAGEALLOC pReq = (PSUPPAGEALLOC)pReqHdr;
1228 REQ_CHECK_EXPR(SUP_IOCTL_PAGE_ALLOC, pReq->Hdr.cbIn <= SUP_IOCTL_PAGE_ALLOC_SIZE_IN);
1229 REQ_CHECK_SIZES_EX(SUP_IOCTL_PAGE_ALLOC, SUP_IOCTL_PAGE_ALLOC_SIZE_IN, SUP_IOCTL_PAGE_ALLOC_SIZE_OUT(pReq->u.In.cPages));
1230
1231 /* execute */
1232 pReq->Hdr.rc = SUPR0PageAlloc(pSession, pReq->u.In.cPages, &pReq->u.Out.pvR3, &pReq->u.Out.aPages[0]);
1233 if (RT_FAILURE(pReq->Hdr.rc))
1234 pReq->Hdr.cbOut = sizeof(pReq->Hdr);
1235 return 0;
1236 }
1237
1238 case SUP_CTL_CODE_NO_SIZE(SUP_IOCTL_PAGE_FREE):
1239 {
1240 /* validate */
1241 PSUPPAGEFREE pReq = (PSUPPAGEFREE)pReqHdr;
1242 REQ_CHECK_SIZES(SUP_IOCTL_PAGE_FREE);
1243
1244 /* execute */
1245 pReq->Hdr.rc = SUPR0PageFree(pSession, pReq->u.In.pvR3);
1246 return 0;
1247 }
1248
1249 default:
1250 Log(("Unknown IOCTL %#lx\n", (long)uIOCtl));
1251 break;
1252 }
1253 return SUPDRV_ERR_GENERAL_FAILURE;
1254}
1255
1256
1257/**
1258 * Inter-Driver Communcation (IDC) worker.
1259 *
1260 * @returns VBox status code.
1261 * @retval VINF_SUCCESS on success.
1262 * @retval VERR_INVALID_PARAMETER if the request is invalid.
1263 * @retval VERR_NOT_SUPPORTED if the request isn't supported.
1264 *
1265 * @param uReq The request (function) code.
1266 * @param pDevExt Device extention.
1267 * @param pSession Session data.
1268 * @param pReqHdr The request header.
1269 */
1270int VBOXCALL supdrvIDC(uintptr_t uReq, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVIDCREQHDR pReqHdr)
1271{
1272 /*
1273 * The OS specific code has already validated the pSession
1274 * pointer, and the request size being greater or equal to
1275 * size of the header.
1276 *
1277 * So, just check that pSession is a kernel context session.
1278 */
1279 if (RT_UNLIKELY( pSession
1280 && pSession->R0Process != NIL_RTR0PROCESS))
1281 return VERR_INVALID_PARAMETER;
1282
1283/*
1284 * Validation macro.
1285 */
1286#define REQ_CHECK_IDC_SIZE(Name, cbExpect) \
1287 do { \
1288 if (RT_UNLIKELY(pReqHdr->cb != (cbExpect))) \
1289 { \
1290 OSDBGPRINT(( #Name ": Invalid input/output sizes. cb=%ld expected %ld.\n", \
1291 (long)pReqHdr->cb, (long)(cbExpect))); \
1292 return pReqHdr->rc = VERR_INVALID_PARAMETER; \
1293 } \
1294 } while (0)
1295
1296 switch (uReq)
1297 {
1298 case SUPDRV_IDC_REQ_CONNECT:
1299 {
1300 PSUPDRVIDCREQCONNECT pReq = (PSUPDRVIDCREQCONNECT)pReqHdr;
1301 REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_CONNECT, sizeof(*pReq));
1302
1303 /*
1304 * Validate the cookie and other input.
1305 */
1306 if (pReq->Hdr.pSession != NULL)
1307 {
1308 OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: pSession=%p expected NULL!\n", pReq->Hdr.pSession));
1309 return pReqHdr->rc = VERR_INVALID_PARAMETER;
1310 }
1311 if (pReq->u.In.u32MagicCookie != SUPDRVIDCREQ_CONNECT_MAGIC_COOKIE)
1312 {
1313 OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: u32MagicCookie=%#x expected %#x!\n",
1314 pReq->u.In.u32MagicCookie, SUPDRVIDCREQ_CONNECT_MAGIC_COOKIE));
1315 return pReqHdr->rc = VERR_INVALID_PARAMETER;
1316 }
1317 if ( pReq->u.In.uMinVersion > pReq->u.In.uReqVersion
1318 || (pReq->u.In.uMinVersion & UINT32_C(0xffff0000)) != (pReq->u.In.uReqVersion & UINT32_C(0xffff0000)))
1319 {
1320 OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: uMinVersion=%#x uMaxVersion=%#x doesn't match!\n",
1321 pReq->u.In.uMinVersion, pReq->u.In.uReqVersion));
1322 return pReqHdr->rc = VERR_INVALID_PARAMETER;
1323 }
1324
1325 /*
1326 * Match the version.
1327 * The current logic is very simple, match the major interface version.
1328 */
1329 if ( pReq->u.In.uMinVersion > SUPDRV_IDC_VERSION
1330 || (pReq->u.In.uMinVersion & 0xffff0000) != (SUPDRV_IDC_VERSION & 0xffff0000))
1331 {
1332 OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: Version mismatch. Requested: %#x Min: %#x Current: %#x\n",
1333 pReq->u.In.uReqVersion, pReq->u.In.uMinVersion, SUPDRV_IDC_VERSION));
1334 pReq->u.Out.pSession = NULL;
1335 pReq->u.Out.uSessionVersion = 0xffffffff;
1336 pReq->u.Out.uDriverVersion = SUPDRV_IDC_VERSION;
1337 pReq->u.Out.uDriverRevision = VBOX_SVN_REV;
1338 pReq->Hdr.rc = VERR_VERSION_MISMATCH;
1339 return VINF_SUCCESS;
1340 }
1341
1342 pReq->u.Out.pSession = NULL;
1343 pReq->u.Out.uSessionVersion = SUPDRV_IDC_VERSION;
1344 pReq->u.Out.uDriverVersion = SUPDRV_IDC_VERSION;
1345 pReq->u.Out.uDriverRevision = VBOX_SVN_REV;
1346
1347 /*
1348 * On NT we will already have a session associated with the
1349 * client, just like with the SUP_IOCTL_COOKIE request, while
1350 * the other doesn't.
1351 */
1352#ifdef RT_OS_WINDOWS
1353 pReq->Hdr.rc = VINF_SUCCESS;
1354#else
1355 AssertReturn(!pSession, VERR_INTERNAL_ERROR);
1356 pReq->Hdr.rc = supdrvCreateSession(pDevExt, false /* fUser */, &pSession);
1357 if (RT_FAILURE(pReq->Hdr.rc))
1358 {
1359 OSDBGPRINT(("SUPDRV_IDC_REQ_CONNECT: failed to create session, rc=%d\n", pReq->Hdr.rc));
1360 return VINF_SUCCESS;
1361 }
1362#endif
1363
1364 pReq->u.Out.pSession = pSession;
1365 pReq->Hdr.pSession = pSession;
1366
1367 return VINF_SUCCESS;
1368 }
1369
1370 case SUPDRV_IDC_REQ_DISCONNECT:
1371 {
1372 REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_DISCONNECT, sizeof(*pReqHdr));
1373
1374#ifdef RT_OS_WINDOWS
1375 /* Windows will destroy the session when the file object is destroyed. */
1376#else
1377 supdrvCloseSession(pDevExt, pSession);
1378#endif
1379 return pReqHdr->rc = VINF_SUCCESS;
1380 }
1381
1382 case SUPDRV_IDC_REQ_GET_SYMBOL:
1383 {
1384 PSUPDRVIDCREQGETSYM pReq = (PSUPDRVIDCREQGETSYM)pReqHdr;
1385 REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_GET_SYMBOL, sizeof(*pReq));
1386
1387 pReq->Hdr.rc = supdrvIDC_LdrGetSymbol(pDevExt, pSession, pReq);
1388 return VINF_SUCCESS;
1389 }
1390
1391 case SUPDRV_IDC_REQ_COMPONENT_REGISTER_FACTORY:
1392 {
1393 PSUPDRVIDCREQCOMPREGFACTORY pReq = (PSUPDRVIDCREQCOMPREGFACTORY)pReqHdr;
1394 REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_COMPONENT_REGISTER_FACTORY, sizeof(*pReq));
1395
1396 pReq->Hdr.rc = SUPR0ComponentRegisterFactory(pSession, pReq->u.In.pFactory);
1397 return VINF_SUCCESS;
1398 }
1399
1400 case SUPDRV_IDC_REQ_COMPONENT_DEREGISTER_FACTORY:
1401 {
1402 PSUPDRVIDCREQCOMPDEREGFACTORY pReq = (PSUPDRVIDCREQCOMPDEREGFACTORY)pReqHdr;
1403 REQ_CHECK_IDC_SIZE(SUPDRV_IDC_REQ_COMPONENT_DEREGISTER_FACTORY, sizeof(*pReq));
1404
1405 pReq->Hdr.rc = SUPR0ComponentDeregisterFactory(pSession, pReq->u.In.pFactory);
1406 return VINF_SUCCESS;
1407 }
1408
1409 default:
1410 Log(("Unknown IDC %#lx\n", (long)uReq));
1411 break;
1412 }
1413
1414#undef REQ_CHECK_IDC_SIZE
1415 return VERR_NOT_SUPPORTED;
1416}
1417
1418
1419/**
1420 * Register a object for reference counting.
1421 * The object is registered with one reference in the specified session.
1422 *
1423 * @returns Unique identifier on success (pointer).
1424 * All future reference must use this identifier.
1425 * @returns NULL on failure.
1426 * @param pfnDestructor The destructore function which will be called when the reference count reaches 0.
1427 * @param pvUser1 The first user argument.
1428 * @param pvUser2 The second user argument.
1429 */
1430SUPR0DECL(void *) SUPR0ObjRegister(PSUPDRVSESSION pSession, SUPDRVOBJTYPE enmType, PFNSUPDRVDESTRUCTOR pfnDestructor, void *pvUser1, void *pvUser2)
1431{
1432 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
1433 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
1434 PSUPDRVOBJ pObj;
1435 PSUPDRVUSAGE pUsage;
1436
1437 /*
1438 * Validate the input.
1439 */
1440 AssertReturn(SUP_IS_SESSION_VALID(pSession), NULL);
1441 AssertReturn(enmType > SUPDRVOBJTYPE_INVALID && enmType < SUPDRVOBJTYPE_END, NULL);
1442 AssertPtrReturn(pfnDestructor, NULL);
1443
1444 /*
1445 * Allocate and initialize the object.
1446 */
1447 pObj = (PSUPDRVOBJ)RTMemAlloc(sizeof(*pObj));
1448 if (!pObj)
1449 return NULL;
1450 pObj->u32Magic = SUPDRVOBJ_MAGIC;
1451 pObj->enmType = enmType;
1452 pObj->pNext = NULL;
1453 pObj->cUsage = 1;
1454 pObj->pfnDestructor = pfnDestructor;
1455 pObj->pvUser1 = pvUser1;
1456 pObj->pvUser2 = pvUser2;
1457 pObj->CreatorUid = pSession->Uid;
1458 pObj->CreatorGid = pSession->Gid;
1459 pObj->CreatorProcess= pSession->Process;
1460 supdrvOSObjInitCreator(pObj, pSession);
1461
1462 /*
1463 * Allocate the usage record.
1464 * (We keep freed usage records around to simplify SUPR0ObjAddRef().)
1465 */
1466 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
1467
1468 pUsage = pDevExt->pUsageFree;
1469 if (pUsage)
1470 pDevExt->pUsageFree = pUsage->pNext;
1471 else
1472 {
1473 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
1474 pUsage = (PSUPDRVUSAGE)RTMemAlloc(sizeof(*pUsage));
1475 if (!pUsage)
1476 {
1477 RTMemFree(pObj);
1478 return NULL;
1479 }
1480 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
1481 }
1482
1483 /*
1484 * Insert the object and create the session usage record.
1485 */
1486 /* The object. */
1487 pObj->pNext = pDevExt->pObjs;
1488 pDevExt->pObjs = pObj;
1489
1490 /* The session record. */
1491 pUsage->cUsage = 1;
1492 pUsage->pObj = pObj;
1493 pUsage->pNext = pSession->pUsage;
1494 /* Log2(("SUPR0ObjRegister: pUsage=%p:{.pObj=%p, .pNext=%p}\n", pUsage, pUsage->pObj, pUsage->pNext)); */
1495 pSession->pUsage = pUsage;
1496
1497 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
1498
1499 Log(("SUPR0ObjRegister: returns %p (pvUser1=%p, pvUser=%p)\n", pObj, pvUser1, pvUser2));
1500 return pObj;
1501}
1502
1503
1504/**
1505 * Increment the reference counter for the object associating the reference
1506 * with the specified session.
1507 *
1508 * @returns IPRT status code.
1509 * @param pvObj The identifier returned by SUPR0ObjRegister().
1510 * @param pSession The session which is referencing the object.
1511 *
1512 * @remarks The caller should not own any spinlocks and must carefully protect
1513 * itself against potential race with the destructor so freed memory
1514 * isn't accessed here.
1515 */
1516SUPR0DECL(int) SUPR0ObjAddRef(void *pvObj, PSUPDRVSESSION pSession)
1517{
1518 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
1519 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
1520 PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj;
1521 PSUPDRVUSAGE pUsagePre;
1522 PSUPDRVUSAGE pUsage;
1523
1524 /*
1525 * Validate the input.
1526 * Be ready for the destruction race (someone might be stuck in the
1527 * destructor waiting a lock we own).
1528 */
1529 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1530 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1531 AssertMsgReturn(pObj->u32Magic == SUPDRVOBJ_MAGIC || pObj->u32Magic == SUPDRVOBJ_MAGIC + 1,
1532 ("Invalid pvObj=%p magic=%#x (expected %#x or %#x)\n", pvObj, pObj->u32Magic, SUPDRVOBJ_MAGIC, SUPDRVOBJ_MAGIC + 1),
1533 VERR_INVALID_PARAMETER);
1534
1535 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
1536
1537 if (RT_UNLIKELY(pObj->u32Magic != SUPDRVOBJ_MAGIC))
1538 {
1539 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
1540
1541 AssertMsgFailed(("pvObj=%p magic=%#x\n", pvObj, pObj->u32Magic));
1542 return VERR_WRONG_ORDER;
1543 }
1544
1545 /*
1546 * Preallocate the usage record.
1547 */
1548 pUsagePre = pDevExt->pUsageFree;
1549 if (pUsagePre)
1550 pDevExt->pUsageFree = pUsagePre->pNext;
1551 else
1552 {
1553 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
1554 pUsagePre = (PSUPDRVUSAGE)RTMemAlloc(sizeof(*pUsagePre));
1555 if (!pUsagePre)
1556 return VERR_NO_MEMORY;
1557
1558 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
1559 }
1560
1561 /*
1562 * Reference the object.
1563 */
1564 pObj->cUsage++;
1565
1566 /*
1567 * Look for the session record.
1568 */
1569 for (pUsage = pSession->pUsage; pUsage; pUsage = pUsage->pNext)
1570 {
1571 /*Log(("SUPR0AddRef: pUsage=%p:{.pObj=%p, .pNext=%p}\n", pUsage, pUsage->pObj, pUsage->pNext));*/
1572 if (pUsage->pObj == pObj)
1573 break;
1574 }
1575 if (pUsage)
1576 pUsage->cUsage++;
1577 else
1578 {
1579 /* create a new session record. */
1580 pUsagePre->cUsage = 1;
1581 pUsagePre->pObj = pObj;
1582 pUsagePre->pNext = pSession->pUsage;
1583 pSession->pUsage = pUsagePre;
1584 /*Log(("SUPR0AddRef: pUsagePre=%p:{.pObj=%p, .pNext=%p}\n", pUsagePre, pUsagePre->pObj, pUsagePre->pNext));*/
1585
1586 pUsagePre = NULL;
1587 }
1588
1589 /*
1590 * Put any unused usage record into the free list..
1591 */
1592 if (pUsagePre)
1593 {
1594 pUsagePre->pNext = pDevExt->pUsageFree;
1595 pDevExt->pUsageFree = pUsagePre;
1596 }
1597
1598 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
1599
1600 return VINF_SUCCESS;
1601}
1602
1603
1604/**
1605 * Decrement / destroy a reference counter record for an object.
1606 *
1607 * The object is uniquely identified by pfnDestructor+pvUser1+pvUser2.
1608 *
1609 * @returns IPRT status code.
1610 * @param pvObj The identifier returned by SUPR0ObjRegister().
1611 * @param pSession The session which is referencing the object.
1612 */
1613SUPR0DECL(int) SUPR0ObjRelease(void *pvObj, PSUPDRVSESSION pSession)
1614{
1615 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
1616 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
1617 PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj;
1618 bool fDestroy = false;
1619 PSUPDRVUSAGE pUsage;
1620 PSUPDRVUSAGE pUsagePrev;
1621
1622 /*
1623 * Validate the input.
1624 */
1625 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1626 AssertMsgReturn(VALID_PTR(pObj) && pObj->u32Magic == SUPDRVOBJ_MAGIC,
1627 ("Invalid pvObj=%p magic=%#x (exepcted %#x)\n", pvObj, pObj ? pObj->u32Magic : 0, SUPDRVOBJ_MAGIC),
1628 VERR_INVALID_PARAMETER);
1629
1630 /*
1631 * Acquire the spinlock and look for the usage record.
1632 */
1633 RTSpinlockAcquire(pDevExt->Spinlock, &SpinlockTmp);
1634
1635 for (pUsagePrev = NULL, pUsage = pSession->pUsage;
1636 pUsage;
1637 pUsagePrev = pUsage, pUsage = pUsage->pNext)
1638 {
1639 /*Log2(("SUPR0ObjRelease: pUsage=%p:{.pObj=%p, .pNext=%p}\n", pUsage, pUsage->pObj, pUsage->pNext));*/
1640 if (pUsage->pObj == pObj)
1641 {
1642 AssertMsg(pUsage->cUsage >= 1 && pObj->cUsage >= pUsage->cUsage, ("glob %d; sess %d\n", pObj->cUsage, pUsage->cUsage));
1643 if (pUsage->cUsage > 1)
1644 {
1645 pObj->cUsage--;
1646 pUsage->cUsage--;
1647 }
1648 else
1649 {
1650 /*
1651 * Free the session record.
1652 */
1653 if (pUsagePrev)
1654 pUsagePrev->pNext = pUsage->pNext;
1655 else
1656 pSession->pUsage = pUsage->pNext;
1657 pUsage->pNext = pDevExt->pUsageFree;
1658 pDevExt->pUsageFree = pUsage;
1659
1660 /* What about the object? */
1661 if (pObj->cUsage > 1)
1662 pObj->cUsage--;
1663 else
1664 {
1665 /*
1666 * Object is to be destroyed, unlink it.
1667 */
1668 pObj->u32Magic = SUPDRVOBJ_MAGIC + 1;
1669 fDestroy = true;
1670 if (pDevExt->pObjs == pObj)
1671 pDevExt->pObjs = pObj->pNext;
1672 else
1673 {
1674 PSUPDRVOBJ pObjPrev;
1675 for (pObjPrev = pDevExt->pObjs; pObjPrev; pObjPrev = pObjPrev->pNext)
1676 if (pObjPrev->pNext == pObj)
1677 {
1678 pObjPrev->pNext = pObj->pNext;
1679 break;
1680 }
1681 Assert(pObjPrev);
1682 }
1683 }
1684 }
1685 break;
1686 }
1687 }
1688
1689 RTSpinlockRelease(pDevExt->Spinlock, &SpinlockTmp);
1690
1691 /*
1692 * Call the destructor and free the object if required.
1693 */
1694 if (fDestroy)
1695 {
1696 Log(("SUPR0ObjRelease: destroying %p/%d (%p/%p) cpid=%RTproc pid=%RTproc dtor=%p\n",
1697 pObj, pObj->enmType, pObj->pvUser1, pObj->pvUser2, pObj->CreatorProcess, RTProcSelf(), pObj->pfnDestructor));
1698 if (pObj->pfnDestructor)
1699 pObj->pfnDestructor(pObj, pObj->pvUser1, pObj->pvUser2);
1700 RTMemFree(pObj);
1701 }
1702
1703 AssertMsg(pUsage, ("pvObj=%p\n", pvObj));
1704 return pUsage ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
1705}
1706
1707/**
1708 * Verifies that the current process can access the specified object.
1709 *
1710 * @returns The following IPRT status code:
1711 * @retval VINF_SUCCESS if access was granted.
1712 * @retval VERR_PERMISSION_DENIED if denied access.
1713 * @retval VERR_INVALID_PARAMETER if invalid parameter.
1714 *
1715 * @param pvObj The identifier returned by SUPR0ObjRegister().
1716 * @param pSession The session which wishes to access the object.
1717 * @param pszObjName Object string name. This is optional and depends on the object type.
1718 *
1719 * @remark The caller is responsible for making sure the object isn't removed while
1720 * we're inside this function. If uncertain about this, just call AddRef before calling us.
1721 */
1722SUPR0DECL(int) SUPR0ObjVerifyAccess(void *pvObj, PSUPDRVSESSION pSession, const char *pszObjName)
1723{
1724 PSUPDRVOBJ pObj = (PSUPDRVOBJ)pvObj;
1725 int rc;
1726
1727 /*
1728 * Validate the input.
1729 */
1730 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1731 AssertMsgReturn(VALID_PTR(pObj) && pObj->u32Magic == SUPDRVOBJ_MAGIC,
1732 ("Invalid pvObj=%p magic=%#x (exepcted %#x)\n", pvObj, pObj ? pObj->u32Magic : 0, SUPDRVOBJ_MAGIC),
1733 VERR_INVALID_PARAMETER);
1734
1735 /*
1736 * Check access. (returns true if a decision has been made.)
1737 */
1738 rc = VERR_INTERNAL_ERROR;
1739 if (supdrvOSObjCanAccess(pObj, pSession, pszObjName, &rc))
1740 return rc;
1741
1742 /*
1743 * Default policy is to allow the user to access his own
1744 * stuff but nothing else.
1745 */
1746 if (pObj->CreatorUid == pSession->Uid)
1747 return VINF_SUCCESS;
1748 return VERR_PERMISSION_DENIED;
1749}
1750
1751
1752/**
1753 * Lock pages.
1754 *
1755 * @returns IPRT status code.
1756 * @param pSession Session to which the locked memory should be associated.
1757 * @param pvR3 Start of the memory range to lock.
1758 * This must be page aligned.
1759 * @param cb Size of the memory range to lock.
1760 * This must be page aligned.
1761 */
1762SUPR0DECL(int) SUPR0LockMem(PSUPDRVSESSION pSession, RTR3PTR pvR3, uint32_t cPages, PRTHCPHYS paPages)
1763{
1764 int rc;
1765 SUPDRVMEMREF Mem = {0};
1766 const size_t cb = (size_t)cPages << PAGE_SHIFT;
1767 LogFlow(("SUPR0LockMem: pSession=%p pvR3=%p cPages=%d paPages=%p\n", pSession, (void *)pvR3, cPages, paPages));
1768
1769 /*
1770 * Verify input.
1771 */
1772 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1773 AssertPtrReturn(paPages, VERR_INVALID_PARAMETER);
1774 if ( RT_ALIGN_R3PT(pvR3, PAGE_SIZE, RTR3PTR) != pvR3
1775 || !pvR3)
1776 {
1777 Log(("pvR3 (%p) must be page aligned and not NULL!\n", (void *)pvR3));
1778 return VERR_INVALID_PARAMETER;
1779 }
1780
1781#ifdef RT_OS_WINDOWS /* A temporary hack for windows, will be removed once all ring-3 code has been cleaned up. */
1782 /* First check if we allocated it using SUPPageAlloc; if so then we don't need to lock it again */
1783 rc = supdrvPageGetPhys(pSession, pvR3, cPages, paPages);
1784 if (RT_SUCCESS(rc))
1785 return rc;
1786#endif
1787
1788 /*
1789 * Let IPRT do the job.
1790 */
1791 Mem.eType = MEMREF_TYPE_LOCKED;
1792 rc = RTR0MemObjLockUser(&Mem.MemObj, pvR3, cb, RTR0ProcHandleSelf());
1793 if (RT_SUCCESS(rc))
1794 {
1795 uint32_t iPage = cPages;
1796 AssertMsg(RTR0MemObjAddressR3(Mem.MemObj) == pvR3, ("%p == %p\n", RTR0MemObjAddressR3(Mem.MemObj), pvR3));
1797 AssertMsg(RTR0MemObjSize(Mem.MemObj) == cb, ("%x == %x\n", RTR0MemObjSize(Mem.MemObj), cb));
1798
1799 while (iPage-- > 0)
1800 {
1801 paPages[iPage] = RTR0MemObjGetPagePhysAddr(Mem.MemObj, iPage);
1802 if (RT_UNLIKELY(paPages[iPage] == NIL_RTCCPHYS))
1803 {
1804 AssertMsgFailed(("iPage=%d\n", iPage));
1805 rc = VERR_INTERNAL_ERROR;
1806 break;
1807 }
1808 }
1809 if (RT_SUCCESS(rc))
1810 rc = supdrvMemAdd(&Mem, pSession);
1811 if (RT_FAILURE(rc))
1812 {
1813 int rc2 = RTR0MemObjFree(Mem.MemObj, false);
1814 AssertRC(rc2);
1815 }
1816 }
1817
1818 return rc;
1819}
1820
1821
1822/**
1823 * Unlocks the memory pointed to by pv.
1824 *
1825 * @returns IPRT status code.
1826 * @param pSession Session to which the memory was locked.
1827 * @param pvR3 Memory to unlock.
1828 */
1829SUPR0DECL(int) SUPR0UnlockMem(PSUPDRVSESSION pSession, RTR3PTR pvR3)
1830{
1831 LogFlow(("SUPR0UnlockMem: pSession=%p pvR3=%p\n", pSession, (void *)pvR3));
1832 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1833#ifdef RT_OS_WINDOWS
1834 /*
1835 * Temporary hack for windows - SUPR0PageFree will unlock SUPR0PageAlloc
1836 * allocations; ignore this call.
1837 */
1838 if (supdrvPageWasLockedByPageAlloc(pSession, pvR3))
1839 {
1840 LogFlow(("Page will be unlocked in SUPR0PageFree -> ignore\n"));
1841 return VINF_SUCCESS;
1842 }
1843#endif
1844 return supdrvMemRelease(pSession, (RTHCUINTPTR)pvR3, MEMREF_TYPE_LOCKED);
1845}
1846
1847
1848/**
1849 * Allocates a chunk of page aligned memory with contiguous and fixed physical
1850 * backing.
1851 *
1852 * @returns IPRT status code.
1853 * @param pSession Session data.
1854 * @param cb Number of bytes to allocate.
1855 * @param ppvR0 Where to put the address of Ring-0 mapping the allocated memory.
1856 * @param ppvR3 Where to put the address of Ring-3 mapping the allocated memory.
1857 * @param pHCPhys Where to put the physical address of allocated memory.
1858 */
1859SUPR0DECL(int) SUPR0ContAlloc(PSUPDRVSESSION pSession, uint32_t cPages, PRTR0PTR ppvR0, PRTR3PTR ppvR3, PRTHCPHYS pHCPhys)
1860{
1861 int rc;
1862 SUPDRVMEMREF Mem = {0};
1863 LogFlow(("SUPR0ContAlloc: pSession=%p cPages=%d ppvR0=%p ppvR3=%p pHCPhys=%p\n", pSession, cPages, ppvR0, ppvR3, pHCPhys));
1864
1865 /*
1866 * Validate input.
1867 */
1868 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1869 if (!ppvR3 || !ppvR0 || !pHCPhys)
1870 {
1871 Log(("Null pointer. All of these should be set: pSession=%p ppvR0=%p ppvR3=%p pHCPhys=%p\n",
1872 pSession, ppvR0, ppvR3, pHCPhys));
1873 return VERR_INVALID_PARAMETER;
1874
1875 }
1876 if (cPages < 1 || cPages >= 256)
1877 {
1878 Log(("Illegal request cPages=%d, must be greater than 0 and smaller than 256\n", cPages));
1879 return VERR_INVALID_PARAMETER;
1880 }
1881
1882 /*
1883 * Let IPRT do the job.
1884 */
1885 rc = RTR0MemObjAllocCont(&Mem.MemObj, cPages << PAGE_SHIFT, true /* executable R0 mapping */);
1886 if (RT_SUCCESS(rc))
1887 {
1888 int rc2;
1889 rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0,
1890 RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, RTR0ProcHandleSelf());
1891 if (RT_SUCCESS(rc))
1892 {
1893 Mem.eType = MEMREF_TYPE_CONT;
1894 rc = supdrvMemAdd(&Mem, pSession);
1895 if (!rc)
1896 {
1897 *ppvR0 = RTR0MemObjAddress(Mem.MemObj);
1898 *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3);
1899 *pHCPhys = RTR0MemObjGetPagePhysAddr(Mem.MemObj, 0);
1900 return 0;
1901 }
1902
1903 rc2 = RTR0MemObjFree(Mem.MapObjR3, false);
1904 AssertRC(rc2);
1905 }
1906 rc2 = RTR0MemObjFree(Mem.MemObj, false);
1907 AssertRC(rc2);
1908 }
1909
1910 return rc;
1911}
1912
1913
1914/**
1915 * Frees memory allocated using SUPR0ContAlloc().
1916 *
1917 * @returns IPRT status code.
1918 * @param pSession The session to which the memory was allocated.
1919 * @param uPtr Pointer to the memory (ring-3 or ring-0).
1920 */
1921SUPR0DECL(int) SUPR0ContFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr)
1922{
1923 LogFlow(("SUPR0ContFree: pSession=%p uPtr=%p\n", pSession, (void *)uPtr));
1924 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1925 return supdrvMemRelease(pSession, uPtr, MEMREF_TYPE_CONT);
1926}
1927
1928
1929/**
1930 * Allocates a chunk of page aligned memory with fixed physical backing below 4GB.
1931 *
1932 * The memory isn't zeroed.
1933 *
1934 * @returns IPRT status code.
1935 * @param pSession Session data.
1936 * @param cPages Number of pages to allocate.
1937 * @param ppvR0 Where to put the address of Ring-0 mapping of the allocated memory.
1938 * @param ppvR3 Where to put the address of Ring-3 mapping of the allocated memory.
1939 * @param paPages Where to put the physical addresses of allocated memory.
1940 */
1941SUPR0DECL(int) SUPR0LowAlloc(PSUPDRVSESSION pSession, uint32_t cPages, PRTR0PTR ppvR0, PRTR3PTR ppvR3, PRTHCPHYS paPages)
1942{
1943 unsigned iPage;
1944 int rc;
1945 SUPDRVMEMREF Mem = {0};
1946 LogFlow(("SUPR0LowAlloc: pSession=%p cPages=%d ppvR3=%p ppvR0=%p paPages=%p\n", pSession, cPages, ppvR3, ppvR0, paPages));
1947
1948 /*
1949 * Validate input.
1950 */
1951 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
1952 if (!ppvR3 || !ppvR0 || !paPages)
1953 {
1954 Log(("Null pointer. All of these should be set: pSession=%p ppvR3=%p ppvR0=%p paPages=%p\n",
1955 pSession, ppvR3, ppvR0, paPages));
1956 return VERR_INVALID_PARAMETER;
1957
1958 }
1959 if (cPages < 1 || cPages > 256)
1960 {
1961 Log(("Illegal request cPages=%d, must be greater than 0 and smaller than 256.\n", cPages));
1962 return VERR_INVALID_PARAMETER;
1963 }
1964
1965 /*
1966 * Let IPRT do the work.
1967 */
1968 rc = RTR0MemObjAllocLow(&Mem.MemObj, cPages << PAGE_SHIFT, true /* executable ring-0 mapping */);
1969 if (RT_SUCCESS(rc))
1970 {
1971 int rc2;
1972 rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0,
1973 RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, RTR0ProcHandleSelf());
1974 if (RT_SUCCESS(rc))
1975 {
1976 Mem.eType = MEMREF_TYPE_LOW;
1977 rc = supdrvMemAdd(&Mem, pSession);
1978 if (!rc)
1979 {
1980 for (iPage = 0; iPage < cPages; iPage++)
1981 {
1982 paPages[iPage] = RTR0MemObjGetPagePhysAddr(Mem.MemObj, iPage);
1983 AssertMsg(!(paPages[iPage] & (PAGE_SIZE - 1)), ("iPage=%d Phys=%VHp\n", paPages[iPage]));
1984 }
1985 *ppvR0 = RTR0MemObjAddress(Mem.MemObj);
1986 *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3);
1987 return 0;
1988 }
1989
1990 rc2 = RTR0MemObjFree(Mem.MapObjR3, false);
1991 AssertRC(rc2);
1992 }
1993
1994 rc2 = RTR0MemObjFree(Mem.MemObj, false);
1995 AssertRC(rc2);
1996 }
1997
1998 return rc;
1999}
2000
2001
2002/**
2003 * Frees memory allocated using SUPR0LowAlloc().
2004 *
2005 * @returns IPRT status code.
2006 * @param pSession The session to which the memory was allocated.
2007 * @param uPtr Pointer to the memory (ring-3 or ring-0).
2008 */
2009SUPR0DECL(int) SUPR0LowFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr)
2010{
2011 LogFlow(("SUPR0LowFree: pSession=%p uPtr=%p\n", pSession, (void *)uPtr));
2012 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
2013 return supdrvMemRelease(pSession, uPtr, MEMREF_TYPE_LOW);
2014}
2015
2016
2017
2018/**
2019 * Allocates a chunk of memory with both R0 and R3 mappings.
2020 * The memory is fixed and it's possible to query the physical addresses using SUPR0MemGetPhys().
2021 *
2022 * @returns IPRT status code.
2023 * @param pSession The session to associated the allocation with.
2024 * @param cb Number of bytes to allocate.
2025 * @param ppvR0 Where to store the address of the Ring-0 mapping.
2026 * @param ppvR3 Where to store the address of the Ring-3 mapping.
2027 */
2028SUPR0DECL(int) SUPR0MemAlloc(PSUPDRVSESSION pSession, uint32_t cb, PRTR0PTR ppvR0, PRTR3PTR ppvR3)
2029{
2030 int rc;
2031 SUPDRVMEMREF Mem = {0};
2032 LogFlow(("SUPR0MemAlloc: pSession=%p cb=%d ppvR0=%p ppvR3=%p\n", pSession, cb, ppvR0, ppvR3));
2033
2034 /*
2035 * Validate input.
2036 */
2037 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
2038 AssertPtrReturn(ppvR0, VERR_INVALID_POINTER);
2039 AssertPtrReturn(ppvR3, VERR_INVALID_POINTER);
2040 if (cb < 1 || cb >= _4M)
2041 {
2042 Log(("Illegal request cb=%u; must be greater than 0 and smaller than 4MB.\n", cb));
2043 return VERR_INVALID_PARAMETER;
2044 }
2045
2046 /*
2047 * Let IPRT do the work.
2048 */
2049 rc = RTR0MemObjAllocPage(&Mem.MemObj, cb, true /* executable ring-0 mapping */);
2050 if (RT_SUCCESS(rc))
2051 {
2052 int rc2;
2053 rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0,
2054 RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, RTR0ProcHandleSelf());
2055 if (RT_SUCCESS(rc))
2056 {
2057 Mem.eType = MEMREF_TYPE_MEM;
2058 rc = supdrvMemAdd(&Mem, pSession);
2059 if (!rc)
2060 {
2061 *ppvR0 = RTR0MemObjAddress(Mem.MemObj);
2062 *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3);
2063 return VINF_SUCCESS;
2064 }
2065 rc2 = RTR0MemObjFree(Mem.MapObjR3, false);
2066 AssertRC(rc2);
2067 }
2068
2069 rc2 = RTR0MemObjFree(Mem.MemObj, false);
2070 AssertRC(rc2);
2071 }
2072
2073 return rc;
2074}
2075
2076
2077/**
2078 * Get the physical addresses of memory allocated using SUPR0MemAlloc().
2079 *
2080 * @returns IPRT status code.
2081 * @param pSession The session to which the memory was allocated.
2082 * @param uPtr The Ring-0 or Ring-3 address returned by SUPR0MemAlloc().
2083 * @param paPages Where to store the physical addresses.
2084 */
2085SUPR0DECL(int) SUPR0MemGetPhys(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr, PSUPPAGE paPages) /** @todo switch this bugger to RTHCPHYS */
2086{
2087 PSUPDRVBUNDLE pBundle;
2088 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
2089 LogFlow(("SUPR0MemGetPhys: pSession=%p uPtr=%p paPages=%p\n", pSession, (void *)uPtr, paPages));
2090
2091 /*
2092 * Validate input.
2093 */
2094 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
2095 AssertPtrReturn(paPages, VERR_INVALID_POINTER);
2096 AssertReturn(uPtr, VERR_INVALID_PARAMETER);
2097
2098 /*
2099 * Search for the address.
2100 */
2101 RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp);
2102 for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
2103 {
2104 if (pBundle->cUsed > 0)
2105 {
2106 unsigned i;
2107 for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
2108 {
2109 if ( pBundle->aMem[i].eType == MEMREF_TYPE_MEM
2110 && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ
2111 && ( (RTHCUINTPTR)RTR0MemObjAddress(pBundle->aMem[i].MemObj) == uPtr
2112 || ( pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ
2113 && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == uPtr)
2114 )
2115 )
2116 {
2117 const unsigned cPages = RTR0MemObjSize(pBundle->aMem[i].MemObj) >> PAGE_SHIFT;
2118 unsigned iPage;
2119 for (iPage = 0; iPage < cPages; iPage++)
2120 {
2121 paPages[iPage].Phys = RTR0MemObjGetPagePhysAddr(pBundle->aMem[i].MemObj, iPage);
2122 paPages[iPage].uReserved = 0;
2123 }
2124 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2125 return VINF_SUCCESS;
2126 }
2127 }
2128 }
2129 }
2130 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2131 Log(("Failed to find %p!!!\n", (void *)uPtr));
2132 return VERR_INVALID_PARAMETER;
2133}
2134
2135
2136/**
2137 * Free memory allocated by SUPR0MemAlloc().
2138 *
2139 * @returns IPRT status code.
2140 * @param pSession The session owning the allocation.
2141 * @param uPtr The Ring-0 or Ring-3 address returned by SUPR0MemAlloc().
2142 */
2143SUPR0DECL(int) SUPR0MemFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr)
2144{
2145 LogFlow(("SUPR0MemFree: pSession=%p uPtr=%p\n", pSession, (void *)uPtr));
2146 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
2147 return supdrvMemRelease(pSession, uPtr, MEMREF_TYPE_MEM);
2148}
2149
2150
2151/**
2152 * Allocates a chunk of memory with only a R3 mappings.
2153 * The memory is fixed and it's possible to query the physical addresses using SUPR0MemGetPhys().
2154 *
2155 * @returns IPRT status code.
2156 * @param pSession The session to associated the allocation with.
2157 * @param cPages The number of pages to allocate.
2158 * @param ppvR3 Where to store the address of the Ring-3 mapping.
2159 * @param paPages Where to store the addresses of the pages. Optional.
2160 */
2161SUPR0DECL(int) SUPR0PageAlloc(PSUPDRVSESSION pSession, uint32_t cPages, PRTR3PTR ppvR3, PRTHCPHYS paPages)
2162{
2163 int rc;
2164 SUPDRVMEMREF Mem = {0};
2165 LogFlow(("SUPR0PageAlloc: pSession=%p cb=%d ppvR3=%p\n", pSession, cPages, ppvR3));
2166
2167 /*
2168 * Validate input. The allowed allocation size must be at least equal to the maximum guest VRAM size.
2169 */
2170 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
2171 AssertPtrReturn(ppvR3, VERR_INVALID_POINTER);
2172 if (cPages < 1 || cPages > (128 * _1M)/PAGE_SIZE)
2173 {
2174 Log(("SUPR0PageAlloc: Illegal request cb=%u; must be greater than 0 and smaller than 128MB.\n", cPages));
2175 return VERR_INVALID_PARAMETER;
2176 }
2177
2178 /*
2179 * Let IPRT do the work.
2180 */
2181 rc = RTR0MemObjAllocPhysNC(&Mem.MemObj, (size_t)cPages * PAGE_SIZE, NIL_RTHCPHYS);
2182 if (RT_SUCCESS(rc))
2183 {
2184 int rc2;
2185 rc = RTR0MemObjMapUser(&Mem.MapObjR3, Mem.MemObj, (RTR3PTR)-1, 0,
2186 RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ, RTR0ProcHandleSelf());
2187 if (RT_SUCCESS(rc))
2188 {
2189 Mem.eType = MEMREF_TYPE_LOCKED_SUP;
2190 rc = supdrvMemAdd(&Mem, pSession);
2191 if (!rc)
2192 {
2193 *ppvR3 = RTR0MemObjAddressR3(Mem.MapObjR3);
2194 if (paPages)
2195 {
2196 uint32_t iPage = cPages;
2197 while (iPage-- > 0)
2198 {
2199 paPages[iPage] = RTR0MemObjGetPagePhysAddr(Mem.MapObjR3, iPage);
2200 Assert(paPages[iPage] != NIL_RTHCPHYS);
2201 }
2202 }
2203 return VINF_SUCCESS;
2204 }
2205 rc2 = RTR0MemObjFree(Mem.MapObjR3, false);
2206 AssertRC(rc2);
2207 }
2208
2209 rc2 = RTR0MemObjFree(Mem.MemObj, false);
2210 AssertRC(rc2);
2211 }
2212 return rc;
2213}
2214
2215
2216#ifdef RT_OS_WINDOWS
2217/**
2218 * Check if the pages were locked by SUPR0PageAlloc
2219 *
2220 * This function will be removed along with the lock/unlock hacks when
2221 * we've cleaned up the ring-3 code properly.
2222 *
2223 * @returns boolean
2224 * @param pSession The session to which the memory was allocated.
2225 * @param pvR3 The Ring-3 address returned by SUPR0PageAlloc().
2226 */
2227static bool supdrvPageWasLockedByPageAlloc(PSUPDRVSESSION pSession, RTR3PTR pvR3)
2228{
2229 PSUPDRVBUNDLE pBundle;
2230 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
2231 LogFlow(("SUPR0PageIsLockedByPageAlloc: pSession=%p pvR3=%p\n", pSession, (void *)pvR3));
2232
2233 /*
2234 * Search for the address.
2235 */
2236 RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp);
2237 for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
2238 {
2239 if (pBundle->cUsed > 0)
2240 {
2241 unsigned i;
2242 for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
2243 {
2244 if ( pBundle->aMem[i].eType == MEMREF_TYPE_LOCKED_SUP
2245 && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ
2246 && pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ
2247 && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == pvR3)
2248 {
2249 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2250 return true;
2251 }
2252 }
2253 }
2254 }
2255 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2256 return false;
2257}
2258
2259
2260/**
2261 * Get the physical addresses of memory allocated using SUPR0PageAlloc().
2262 *
2263 * This function will be removed along with the lock/unlock hacks when
2264 * we've cleaned up the ring-3 code properly.
2265 *
2266 * @returns IPRT status code.
2267 * @param pSession The session to which the memory was allocated.
2268 * @param pvR3 The Ring-3 address returned by SUPR0PageAlloc().
2269 * @param cPages Number of pages in paPages
2270 * @param paPages Where to store the physical addresses.
2271 */
2272static int supdrvPageGetPhys(PSUPDRVSESSION pSession, RTR3PTR pvR3, uint32_t cPages, PRTHCPHYS paPages)
2273{
2274 PSUPDRVBUNDLE pBundle;
2275 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
2276 LogFlow(("supdrvPageGetPhys: pSession=%p pvR3=%p cPages=%#lx paPages=%p\n", pSession, (void *)pvR3, (long)cPages, paPages));
2277
2278 /*
2279 * Search for the address.
2280 */
2281 RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp);
2282 for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
2283 {
2284 if (pBundle->cUsed > 0)
2285 {
2286 unsigned i;
2287 for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
2288 {
2289 if ( pBundle->aMem[i].eType == MEMREF_TYPE_LOCKED_SUP
2290 && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ
2291 && pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ
2292 && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == pvR3)
2293 {
2294 uint32_t iPage = RTR0MemObjSize(pBundle->aMem[i].MemObj) >> PAGE_SHIFT;
2295 cPages = RT_MIN(iPage, cPages);
2296 for (iPage = 0; iPage < cPages; iPage++)
2297 paPages[iPage] = RTR0MemObjGetPagePhysAddr(pBundle->aMem[i].MemObj, iPage);
2298 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2299 return VINF_SUCCESS;
2300 }
2301 }
2302 }
2303 }
2304 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2305 return VERR_INVALID_PARAMETER;
2306}
2307#endif /* RT_OS_WINDOWS */
2308
2309
2310/**
2311 * Free memory allocated by SUPR0PageAlloc().
2312 *
2313 * @returns IPRT status code.
2314 * @param pSession The session owning the allocation.
2315 * @param pvR3 The Ring-3 address returned by SUPR0PageAlloc().
2316 */
2317SUPR0DECL(int) SUPR0PageFree(PSUPDRVSESSION pSession, RTR3PTR pvR3)
2318{
2319 LogFlow(("SUPR0PageFree: pSession=%p pvR3=%p\n", pSession, (void *)pvR3));
2320 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
2321 return supdrvMemRelease(pSession, (RTHCUINTPTR)pvR3, MEMREF_TYPE_LOCKED_SUP);
2322}
2323
2324
2325/**
2326 * Maps the GIP into userspace and/or get the physical address of the GIP.
2327 *
2328 * @returns IPRT status code.
2329 * @param pSession Session to which the GIP mapping should belong.
2330 * @param ppGipR3 Where to store the address of the ring-3 mapping. (optional)
2331 * @param pHCPhysGip Where to store the physical address. (optional)
2332 *
2333 * @remark There is no reference counting on the mapping, so one call to this function
2334 * count globally as one reference. One call to SUPR0GipUnmap() is will unmap GIP
2335 * and remove the session as a GIP user.
2336 */
2337SUPR0DECL(int) SUPR0GipMap(PSUPDRVSESSION pSession, PRTR3PTR ppGipR3, PRTHCPHYS pHCPhysGip)
2338{
2339 int rc = 0;
2340 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
2341 RTR3PTR pGip = NIL_RTR3PTR;
2342 RTHCPHYS HCPhys = NIL_RTHCPHYS;
2343 LogFlow(("SUPR0GipMap: pSession=%p ppGipR3=%p pHCPhysGip=%p\n", pSession, ppGipR3, pHCPhysGip));
2344
2345 /*
2346 * Validate
2347 */
2348 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
2349 AssertPtrNullReturn(ppGipR3, VERR_INVALID_POINTER);
2350 AssertPtrNullReturn(pHCPhysGip, VERR_INVALID_POINTER);
2351
2352 RTSemFastMutexRequest(pDevExt->mtxGip);
2353 if (pDevExt->pGip)
2354 {
2355 /*
2356 * Map it?
2357 */
2358 if (ppGipR3)
2359 {
2360 if (pSession->GipMapObjR3 == NIL_RTR0MEMOBJ)
2361 rc = RTR0MemObjMapUser(&pSession->GipMapObjR3, pDevExt->GipMemObj, (RTR3PTR)-1, 0,
2362 RTMEM_PROT_READ, RTR0ProcHandleSelf());
2363 if (RT_SUCCESS(rc))
2364 {
2365 pGip = RTR0MemObjAddressR3(pSession->GipMapObjR3);
2366 rc = VINF_SUCCESS; /** @todo remove this and replace the !rc below with RT_SUCCESS(rc). */
2367 }
2368 }
2369
2370 /*
2371 * Get physical address.
2372 */
2373 if (pHCPhysGip && !rc)
2374 HCPhys = pDevExt->HCPhysGip;
2375
2376 /*
2377 * Reference globally.
2378 */
2379 if (!pSession->fGipReferenced && !rc)
2380 {
2381 pSession->fGipReferenced = 1;
2382 pDevExt->cGipUsers++;
2383 if (pDevExt->cGipUsers == 1)
2384 {
2385 PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
2386 unsigned i;
2387
2388 LogFlow(("SUPR0GipMap: Resumes GIP updating\n"));
2389
2390 for (i = 0; i < RT_ELEMENTS(pGip->aCPUs); i++)
2391 ASMAtomicXchgU32(&pGip->aCPUs[i].u32TransactionId, pGip->aCPUs[i].u32TransactionId & ~(GIP_UPDATEHZ_RECALC_FREQ * 2 - 1));
2392 ASMAtomicXchgU64(&pGip->u64NanoTSLastUpdateHz, 0);
2393
2394 rc = RTTimerStart(pDevExt->pGipTimer, 0);
2395 AssertRC(rc); rc = VINF_SUCCESS;
2396 }
2397 }
2398 }
2399 else
2400 {
2401 rc = SUPDRV_ERR_GENERAL_FAILURE;
2402 Log(("SUPR0GipMap: GIP is not available!\n"));
2403 }
2404 RTSemFastMutexRelease(pDevExt->mtxGip);
2405
2406 /*
2407 * Write returns.
2408 */
2409 if (pHCPhysGip)
2410 *pHCPhysGip = HCPhys;
2411 if (ppGipR3)
2412 *ppGipR3 = pGip;
2413
2414#ifdef DEBUG_DARWIN_GIP
2415 OSDBGPRINT(("SUPR0GipMap: returns %d *pHCPhysGip=%lx *ppGip=%p GipMapObjR3\n", rc, (unsigned long)HCPhys, pGip, pSession->GipMapObjR3));
2416#else
2417 LogFlow(("SUPR0GipMap: returns %d *pHCPhysGip=%lx *ppGipR3=%p\n", rc, (unsigned long)HCPhys, (void *)(uintptr_t)pGip));
2418#endif
2419 return rc;
2420}
2421
2422
2423/**
2424 * Unmaps any user mapping of the GIP and terminates all GIP access
2425 * from this session.
2426 *
2427 * @returns IPRT status code.
2428 * @param pSession Session to which the GIP mapping should belong.
2429 */
2430SUPR0DECL(int) SUPR0GipUnmap(PSUPDRVSESSION pSession)
2431{
2432 int rc = VINF_SUCCESS;
2433 PSUPDRVDEVEXT pDevExt = pSession->pDevExt;
2434#ifdef DEBUG_DARWIN_GIP
2435 OSDBGPRINT(("SUPR0GipUnmap: pSession=%p pGip=%p GipMapObjR3=%p\n",
2436 pSession,
2437 pSession->GipMapObjR3 != NIL_RTR0MEMOBJ ? RTR0MemObjAddress(pSession->GipMapObjR3) : NULL,
2438 pSession->GipMapObjR3));
2439#else
2440 LogFlow(("SUPR0GipUnmap: pSession=%p\n", pSession));
2441#endif
2442 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
2443
2444 RTSemFastMutexRequest(pDevExt->mtxGip);
2445
2446 /*
2447 * Unmap anything?
2448 */
2449 if (pSession->GipMapObjR3 != NIL_RTR0MEMOBJ)
2450 {
2451 rc = RTR0MemObjFree(pSession->GipMapObjR3, false);
2452 AssertRC(rc);
2453 if (RT_SUCCESS(rc))
2454 pSession->GipMapObjR3 = NIL_RTR0MEMOBJ;
2455 }
2456
2457 /*
2458 * Dereference global GIP.
2459 */
2460 if (pSession->fGipReferenced && !rc)
2461 {
2462 pSession->fGipReferenced = 0;
2463 if ( pDevExt->cGipUsers > 0
2464 && !--pDevExt->cGipUsers)
2465 {
2466 LogFlow(("SUPR0GipUnmap: Suspends GIP updating\n"));
2467 rc = RTTimerStop(pDevExt->pGipTimer); AssertRC(rc); rc = 0;
2468 }
2469 }
2470
2471 RTSemFastMutexRelease(pDevExt->mtxGip);
2472
2473 return rc;
2474}
2475
2476
2477/**
2478 * Register a component factory with the support driver.
2479 *
2480 * This is currently restricted to kernel sessions only.
2481 *
2482 * @returns VBox status code.
2483 * @retval VINF_SUCCESS on success.
2484 * @retval VERR_NO_MEMORY if we're out of memory.
2485 * @retval VERR_ALREADY_EXISTS if the factory has already been registered.
2486 * @retval VERR_ACCESS_DENIED if it isn't a kernel session.
2487 * @retval VERR_INVALID_PARAMETER on invalid parameter.
2488 * @retval VERR_INVALID_POINTER on invalid pointer parameter.
2489 *
2490 * @param pSession The SUPDRV session (must be a ring-0 session).
2491 * @param pFactory Pointer to the component factory registration structure.
2492 *
2493 * @remarks This interface is also available via SUPR0IdcComponentRegisterFactory.
2494 */
2495SUPR0DECL(int) SUPR0ComponentRegisterFactory(PSUPDRVSESSION pSession, PCSUPDRVFACTORY pFactory)
2496{
2497 PSUPDRVFACTORYREG pNewReg;
2498 const char *psz;
2499 int rc;
2500
2501 /*
2502 * Validate parameters.
2503 */
2504 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
2505 AssertReturn(pSession->R0Process == NIL_RTR0PROCESS, VERR_ACCESS_DENIED);
2506 AssertPtrReturn(pFactory, VERR_INVALID_POINTER);
2507 AssertPtrReturn(pFactory->pfnQueryFactoryInterface, VERR_INVALID_POINTER);
2508 psz = (const char *)memchr(pFactory->szName, '\0', sizeof(pFactory->szName));
2509 AssertReturn(psz, VERR_INVALID_PARAMETER);
2510
2511 /*
2512 * Allocate and initialize a new registration structure.
2513 */
2514 pNewReg = (PSUPDRVFACTORYREG)RTMemAlloc(sizeof(SUPDRVFACTORYREG));
2515 if (pNewReg)
2516 {
2517 pNewReg->pNext = NULL;
2518 pNewReg->pFactory = pFactory;
2519 pNewReg->pSession = pSession;
2520 pNewReg->cchName = psz - &pFactory->szName[0];
2521
2522 /*
2523 * Add it to the tail of the list after checking for prior registration.
2524 */
2525 rc = RTSemFastMutexRequest(pSession->pDevExt->mtxComponentFactory);
2526 if (RT_SUCCESS(rc))
2527 {
2528 PSUPDRVFACTORYREG pPrev = NULL;
2529 PSUPDRVFACTORYREG pCur = pSession->pDevExt->pComponentFactoryHead;
2530 while (pCur && pCur->pFactory != pFactory)
2531 {
2532 pPrev = pCur;
2533 pCur = pCur->pNext;
2534 }
2535 if (!pCur)
2536 {
2537 if (pPrev)
2538 pPrev->pNext = pNewReg;
2539 else
2540 pSession->pDevExt->pComponentFactoryHead = pNewReg;
2541 rc = VINF_SUCCESS;
2542 }
2543 else
2544 rc = VERR_ALREADY_EXISTS;
2545
2546 RTSemFastMutexRelease(pSession->pDevExt->mtxComponentFactory);
2547 }
2548
2549 if (RT_FAILURE(rc))
2550 RTMemFree(pNewReg);
2551 }
2552 else
2553 rc = VERR_NO_MEMORY;
2554 return rc;
2555}
2556
2557
2558/**
2559 * Deregister a component factory.
2560 *
2561 * @returns VBox status code.
2562 * @retval VINF_SUCCESS on success.
2563 * @retval VERR_NOT_FOUND if the factory wasn't registered.
2564 * @retval VERR_ACCESS_DENIED if it isn't a kernel session.
2565 * @retval VERR_INVALID_PARAMETER on invalid parameter.
2566 * @retval VERR_INVALID_POINTER on invalid pointer parameter.
2567 *
2568 * @param pSession The SUPDRV session (must be a ring-0 session).
2569 * @param pFactory Pointer to the component factory registration structure
2570 * previously passed SUPR0ComponentRegisterFactory().
2571 *
2572 * @remarks This interface is also available via SUPR0IdcComponentDeregisterFactory.
2573 */
2574SUPR0DECL(int) SUPR0ComponentDeregisterFactory(PSUPDRVSESSION pSession, PCSUPDRVFACTORY pFactory)
2575{
2576 int rc;
2577
2578 /*
2579 * Validate parameters.
2580 */
2581 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
2582 AssertReturn(pSession->R0Process == NIL_RTR0PROCESS, VERR_ACCESS_DENIED);
2583 AssertPtrReturn(pFactory, VERR_INVALID_POINTER);
2584
2585 /*
2586 * Take the lock and look for the registration record.
2587 */
2588 rc = RTSemFastMutexRequest(pSession->pDevExt->mtxComponentFactory);
2589 if (RT_SUCCESS(rc))
2590 {
2591 PSUPDRVFACTORYREG pPrev = NULL;
2592 PSUPDRVFACTORYREG pCur = pSession->pDevExt->pComponentFactoryHead;
2593 while (pCur && pCur->pFactory != pFactory)
2594 {
2595 pPrev = pCur;
2596 pCur = pCur->pNext;
2597 }
2598 if (pCur)
2599 {
2600 if (!pPrev)
2601 pSession->pDevExt->pComponentFactoryHead = pCur->pNext;
2602 else
2603 pPrev->pNext = pCur->pNext;
2604
2605 pCur->pNext = NULL;
2606 pCur->pFactory = NULL;
2607 pCur->pSession = NULL;
2608 rc = VINF_SUCCESS;
2609 }
2610 else
2611 rc = VERR_NOT_FOUND;
2612
2613 RTSemFastMutexRelease(pSession->pDevExt->mtxComponentFactory);
2614
2615 RTMemFree(pCur);
2616 }
2617 return rc;
2618}
2619
2620
2621/**
2622 * Queries a component factory.
2623 *
2624 * @returns VBox status code.
2625 * @retval VERR_INVALID_PARAMETER on invalid parameter.
2626 * @retval VERR_INVALID_POINTER on invalid pointer parameter.
2627 * @retval VERR_SUPDRV_COMPONENT_NOT_FOUND if the component factory wasn't found.
2628 * @retval VERR_SUPDRV_INTERFACE_NOT_SUPPORTED if the interface wasn't supported.
2629 *
2630 * @param pSession The SUPDRV session.
2631 * @param pszName The name of the component factory.
2632 * @param pszInterfaceUuid The UUID of the factory interface (stringified).
2633 * @param ppvFactoryIf Where to store the factory interface.
2634 */
2635SUPR0DECL(int) SUPR0ComponentQueryFactory(PSUPDRVSESSION pSession, const char *pszName, const char *pszInterfaceUuid, void **ppvFactoryIf)
2636{
2637 const char *pszEnd;
2638 size_t cchName;
2639 int rc;
2640
2641 /*
2642 * Validate parameters.
2643 */
2644 AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
2645
2646 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
2647 pszEnd = memchr(pszName, '\0', RT_SIZEOFMEMB(SUPDRVFACTORY, szName));
2648 AssertReturn(pszEnd, VERR_INVALID_PARAMETER);
2649 cchName = pszEnd - pszName;
2650
2651 AssertPtrReturn(pszInterfaceUuid, VERR_INVALID_POINTER);
2652 pszEnd = memchr(pszInterfaceUuid, '\0', RTUUID_STR_LENGTH);
2653 AssertReturn(pszEnd, VERR_INVALID_PARAMETER);
2654
2655 AssertPtrReturn(ppvFactoryIf, VERR_INVALID_POINTER);
2656 *ppvFactoryIf = NULL;
2657
2658 /*
2659 * Take the lock and try all factories by this name.
2660 */
2661 rc = RTSemFastMutexRequest(pSession->pDevExt->mtxComponentFactory);
2662 if (RT_SUCCESS(rc))
2663 {
2664 PSUPDRVFACTORYREG pCur = pSession->pDevExt->pComponentFactoryHead;
2665 rc = VERR_SUPDRV_COMPONENT_NOT_FOUND;
2666 while (pCur)
2667 {
2668 if ( pCur->cchName == cchName
2669 && !memcmp(pCur->pFactory->szName, pszName, cchName))
2670 {
2671 void *pvFactory = pCur->pFactory->pfnQueryFactoryInterface(pCur->pFactory, pSession, pszInterfaceUuid);
2672 if (pvFactory)
2673 {
2674 *ppvFactoryIf = pvFactory;
2675 rc = VINF_SUCCESS;
2676 break;
2677 }
2678 rc = VERR_SUPDRV_INTERFACE_NOT_SUPPORTED;
2679 }
2680
2681 /* next */
2682 pCur = pCur->pNext;
2683 }
2684
2685 RTSemFastMutexRelease(pSession->pDevExt->mtxComponentFactory);
2686 }
2687 return rc;
2688}
2689
2690
2691/**
2692 * Adds a memory object to the session.
2693 *
2694 * @returns IPRT status code.
2695 * @param pMem Memory tracking structure containing the
2696 * information to track.
2697 * @param pSession The session.
2698 */
2699static int supdrvMemAdd(PSUPDRVMEMREF pMem, PSUPDRVSESSION pSession)
2700{
2701 PSUPDRVBUNDLE pBundle;
2702 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
2703
2704 /*
2705 * Find free entry and record the allocation.
2706 */
2707 RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp);
2708 for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
2709 {
2710 if (pBundle->cUsed < RT_ELEMENTS(pBundle->aMem))
2711 {
2712 unsigned i;
2713 for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
2714 {
2715 if (pBundle->aMem[i].MemObj == NIL_RTR0MEMOBJ)
2716 {
2717 pBundle->cUsed++;
2718 pBundle->aMem[i] = *pMem;
2719 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2720 return VINF_SUCCESS;
2721 }
2722 }
2723 AssertFailed(); /* !!this can't be happening!!! */
2724 }
2725 }
2726 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2727
2728 /*
2729 * Need to allocate a new bundle.
2730 * Insert into the last entry in the bundle.
2731 */
2732 pBundle = (PSUPDRVBUNDLE)RTMemAllocZ(sizeof(*pBundle));
2733 if (!pBundle)
2734 return VERR_NO_MEMORY;
2735
2736 /* take last entry. */
2737 pBundle->cUsed++;
2738 pBundle->aMem[RT_ELEMENTS(pBundle->aMem) - 1] = *pMem;
2739
2740 /* insert into list. */
2741 RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp);
2742 pBundle->pNext = pSession->Bundle.pNext;
2743 pSession->Bundle.pNext = pBundle;
2744 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2745
2746 return VINF_SUCCESS;
2747}
2748
2749
2750/**
2751 * Releases a memory object referenced by pointer and type.
2752 *
2753 * @returns IPRT status code.
2754 * @param pSession Session data.
2755 * @param uPtr Pointer to memory. This is matched against both the R0 and R3 addresses.
2756 * @param eType Memory type.
2757 */
2758static int supdrvMemRelease(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr, SUPDRVMEMREFTYPE eType)
2759{
2760 PSUPDRVBUNDLE pBundle;
2761 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
2762
2763 /*
2764 * Validate input.
2765 */
2766 if (!uPtr)
2767 {
2768 Log(("Illegal address %p\n", (void *)uPtr));
2769 return VERR_INVALID_PARAMETER;
2770 }
2771
2772 /*
2773 * Search for the address.
2774 */
2775 RTSpinlockAcquire(pSession->Spinlock, &SpinlockTmp);
2776 for (pBundle = &pSession->Bundle; pBundle; pBundle = pBundle->pNext)
2777 {
2778 if (pBundle->cUsed > 0)
2779 {
2780 unsigned i;
2781 for (i = 0; i < RT_ELEMENTS(pBundle->aMem); i++)
2782 {
2783 if ( pBundle->aMem[i].eType == eType
2784 && pBundle->aMem[i].MemObj != NIL_RTR0MEMOBJ
2785 && ( (RTHCUINTPTR)RTR0MemObjAddress(pBundle->aMem[i].MemObj) == uPtr
2786 || ( pBundle->aMem[i].MapObjR3 != NIL_RTR0MEMOBJ
2787 && RTR0MemObjAddressR3(pBundle->aMem[i].MapObjR3) == uPtr))
2788 )
2789 {
2790 /* Make a copy of it and release it outside the spinlock. */
2791 SUPDRVMEMREF Mem = pBundle->aMem[i];
2792 pBundle->aMem[i].eType = MEMREF_TYPE_UNUSED;
2793 pBundle->aMem[i].MemObj = NIL_RTR0MEMOBJ;
2794 pBundle->aMem[i].MapObjR3 = NIL_RTR0MEMOBJ;
2795 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2796
2797 if (Mem.MapObjR3)
2798 {
2799 int rc = RTR0MemObjFree(Mem.MapObjR3, false);
2800 AssertRC(rc); /** @todo figure out how to handle this. */
2801 }
2802 if (Mem.MemObj)
2803 {
2804 int rc = RTR0MemObjFree(Mem.MemObj, false);
2805 AssertRC(rc); /** @todo figure out how to handle this. */
2806 }
2807 return VINF_SUCCESS;
2808 }
2809 }
2810 }
2811 }
2812 RTSpinlockRelease(pSession->Spinlock, &SpinlockTmp);
2813 Log(("Failed to find %p!!! (eType=%d)\n", (void *)uPtr, eType));
2814 return VERR_INVALID_PARAMETER;
2815}
2816
2817
2818#ifdef VBOX_WITH_IDT_PATCHING
2819/**
2820 * Install IDT for the current CPU.
2821 *
2822 * @returns One of the following IPRT status codes:
2823 * @retval VINF_SUCCESS on success.
2824 * @retval VERR_IDT_FAILED.
2825 * @retval VERR_NO_MEMORY.
2826 * @param pDevExt The device extension.
2827 * @param pSession The session data.
2828 * @param pReq The request.
2829 */
2830static int supdrvIOCtl_IdtInstall(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPIDTINSTALL pReq)
2831{
2832 PSUPDRVPATCHUSAGE pUsagePre;
2833 PSUPDRVPATCH pPatchPre;
2834 RTIDTR Idtr;
2835 PSUPDRVPATCH pPatch;
2836 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
2837 LogFlow(("supdrvIOCtl_IdtInstall\n"));
2838
2839 /*
2840 * Preallocate entry for this CPU cause we don't wanna do
2841 * that inside the spinlock!
2842 */
2843 pUsagePre = (PSUPDRVPATCHUSAGE)RTMemAlloc(sizeof(*pUsagePre));
2844 if (!pUsagePre)
2845 return VERR_NO_MEMORY;
2846
2847 /*
2848 * Take the spinlock and see what we need to do.
2849 */
2850 RTSpinlockAcquireNoInts(pDevExt->Spinlock, &SpinlockTmp);
2851
2852 /* check if we already got a free patch. */
2853 if (!pDevExt->pIdtPatchesFree)
2854 {
2855 /*
2856 * Allocate a patch - outside the spinlock of course.
2857 */
2858 RTSpinlockReleaseNoInts(pDevExt->Spinlock, &SpinlockTmp);
2859
2860 pPatchPre = (PSUPDRVPATCH)RTMemExecAlloc(sizeof(*pPatchPre));
2861 if (!pPatchPre)
2862 return VERR_NO_MEMORY;
2863
2864 RTSpinlockAcquireNoInts(pDevExt->Spinlock, &SpinlockTmp);
2865 }
2866 else
2867 {
2868 pPatchPre = pDevExt->pIdtPatchesFree;
2869 pDevExt->pIdtPatchesFree = pPatchPre->pNext;
2870 }
2871
2872 /* look for matching patch entry */
2873 ASMGetIDTR(&Idtr);
2874 pPatch = pDevExt->pIdtPatches;
2875 while (pPatch && pPatch->pvIdt != (void *)Idtr.pIdt)
2876 pPatch = pPatch->pNext;
2877
2878 if (!pPatch)
2879 {
2880 /*
2881 * Create patch.
2882 */
2883 pPatch = supdrvIdtPatchOne(pDevExt, pPatchPre);
2884 if (pPatch)
2885 pPatchPre = NULL; /* mark as used. */
2886 }
2887 else
2888 {
2889 /*
2890 * Simply increment patch usage.
2891 */
2892 pPatch->cUsage++;
2893 }
2894
2895 if (pPatch)
2896 {
2897 /*
2898 * Increment and add if need be the session usage record for this patch.
2899 */
2900 PSUPDRVPATCHUSAGE pUsage = pSession->pPatchUsage;
2901 while (pUsage && pUsage->pPatch != pPatch)
2902 pUsage = pUsage->pNext;
2903
2904 if (!pUsage)
2905 {
2906 /*
2907 * Add usage record.
2908 */
2909 pUsagePre->cUsage = 1;
2910 pUsagePre->pPatch = pPatch;
2911 pUsagePre->pNext = pSession->pPatchUsage;
2912 pSession->pPatchUsage = pUsagePre;
2913 pUsagePre = NULL; /* mark as used. */
2914 }
2915 else
2916 {
2917 /*
2918 * Increment usage count.
2919 */
2920 pUsage->cUsage++;
2921 }
2922 }
2923
2924 /* free patch - we accumulate them for paranoid saftly reasons. */
2925 if (pPatchPre)
2926 {
2927 pPatchPre->pNext = pDevExt->pIdtPatchesFree;
2928 pDevExt->pIdtPatchesFree = pPatchPre;
2929 }
2930
2931 RTSpinlockReleaseNoInts(pDevExt->Spinlock, &SpinlockTmp);
2932
2933 /*
2934 * Free unused preallocated buffers.
2935 */
2936 if (pUsagePre)
2937 RTMemFree(pUsagePre);
2938
2939 pReq->u.Out.u8Idt = pDevExt->u8Idt;
2940
2941 return pPatch ? VINF_SUCCESS : VERR_IDT_FAILED;
2942}
2943
2944
2945/**
2946 * This creates a IDT patch entry.
2947 * If the first patch being installed it'll also determin the IDT entry
2948 * to use.
2949 *
2950 * @returns pPatch on success.
2951 * @returns NULL on failure.
2952 * @param pDevExt Pointer to globals.
2953 * @param pPatch Patch entry to use.
2954 * This will be linked into SUPDRVDEVEXT::pIdtPatches on
2955 * successful return.
2956 * @remark Call must be owning the SUPDRVDEVEXT::Spinlock!
2957 */
2958static PSUPDRVPATCH supdrvIdtPatchOne(PSUPDRVDEVEXT pDevExt, PSUPDRVPATCH pPatch)
2959{
2960 RTIDTR Idtr;
2961 PSUPDRVIDTE paIdt;
2962 LogFlow(("supdrvIOCtl_IdtPatchOne: pPatch=%p\n", pPatch));
2963
2964 /*
2965 * Get IDT.
2966 */
2967 ASMGetIDTR(&Idtr);
2968 paIdt = (PSUPDRVIDTE)Idtr.pIdt;
2969 /*
2970 * Recent Linux kernels can be configured to 1G user /3G kernel.
2971 */
2972 if ((uintptr_t)paIdt < 0x40000000)
2973 {
2974 AssertMsgFailed(("bad paIdt=%p\n", paIdt));
2975 return NULL;
2976 }
2977
2978 if (!pDevExt->u8Idt)
2979 {
2980 /*
2981 * Test out the alternatives.
2982 *
2983 * At the moment we do not support chaining thus we ASSUME that one of
2984 * these 48 entries is unused (which is not a problem on Win32 and
2985 * Linux to my knowledge).
2986 */
2987 /** @todo we MUST change this detection to try grab an entry which is NOT in use. This can be
2988 * combined with gathering info about which guest system call gates we can hook up directly. */
2989 unsigned i;
2990 uint8_t u8Idt = 0;
2991 static uint8_t au8Ints[] =
2992 {
2993#ifdef RT_OS_WINDOWS /* We don't use 0xef and above because they are system stuff on linux (ef is IPI,
2994 * local apic timer, or some other frequently fireing thing). */
2995 0xef, 0xee, 0xed, 0xec,
2996#endif
2997 0xeb, 0xea, 0xe9, 0xe8,
2998 0xdf, 0xde, 0xdd, 0xdc,
2999 0x7b, 0x7a, 0x79, 0x78,
3000 0xbf, 0xbe, 0xbd, 0xbc,
3001 };
3002#if defined(RT_ARCH_AMD64) && defined(DEBUG)
3003 static int s_iWobble = 0;
3004 unsigned iMax = !(s_iWobble++ % 2) ? 0x80 : 0x100;
3005 Log2(("IDT: Idtr=%p:%#x\n", (void *)Idtr.pIdt, (unsigned)Idtr.cbIdt));
3006 for (i = iMax - 0x80; i*16+15 < Idtr.cbIdt && i < iMax; i++)
3007 {
3008 Log2(("%#x: %04x:%08x%04x%04x P=%d DPL=%d IST=%d Type1=%#x u32Reserved=%#x u5Reserved=%#x\n",
3009 i, paIdt[i].u16SegSel, paIdt[i].u32OffsetTop, paIdt[i].u16OffsetHigh, paIdt[i].u16OffsetLow,
3010 paIdt[i].u1Present, paIdt[i].u2DPL, paIdt[i].u3IST, paIdt[i].u5Type2,
3011 paIdt[i].u32Reserved, paIdt[i].u5Reserved));
3012 }
3013#endif
3014 /* look for entries which are not present or otherwise unused. */
3015 for (i = 0; i < sizeof(au8Ints) / sizeof(au8Ints[0]); i++)
3016 {
3017 u8Idt = au8Ints[i];
3018 if ( u8Idt * sizeof(SUPDRVIDTE) < Idtr.cbIdt
3019 && ( !paIdt[u8Idt].u1Present
3020 || paIdt[u8Idt].u5Type2 == 0))
3021 break;
3022 u8Idt = 0;
3023 }
3024 if (!u8Idt)
3025 {
3026 /* try again, look for a compatible entry .*/
3027 for (i = 0; i < sizeof(au8Ints) / sizeof(au8Ints[0]); i++)
3028 {
3029 u8Idt = au8Ints[i];
3030 if ( u8Idt * sizeof(SUPDRVIDTE) < Idtr.cbIdt
3031 && paIdt[u8Idt].u1Present
3032 && paIdt[u8Idt].u5Type2 == SUPDRV_IDTE_TYPE2_INTERRUPT_GATE
3033 && !(paIdt[u8Idt].u16SegSel & 3))
3034 break;
3035 u8Idt = 0;
3036 }
3037 if (!u8Idt)
3038 {
3039 Log(("Failed to find appropirate IDT entry!!\n"));
3040 return NULL;
3041 }
3042 }
3043 pDevExt->u8Idt = u8Idt;
3044 LogFlow(("supdrvIOCtl_IdtPatchOne: u8Idt=%x\n", u8Idt));
3045 }
3046
3047 /*
3048 * Prepare the patch
3049 */
3050 memset(pPatch, 0, sizeof(*pPatch));
3051 pPatch->pvIdt = paIdt;
3052 pPatch->cUsage = 1;
3053 pPatch->pIdtEntry = &paIdt[pDevExt->u8Idt];
3054 pPatch->SavedIdt = paIdt[pDevExt->u8Idt];
3055 pPatch->ChangedIdt.u16OffsetLow = (uint32_t)((uintptr_t)&pPatch->auCode[0] & 0xffff);
3056 pPatch->ChangedIdt.u16OffsetHigh = (uint32_t)((uintptr_t)&pPatch->auCode[0] >> 16);
3057#ifdef RT_ARCH_AMD64
3058 pPatch->ChangedIdt.u32OffsetTop = (uint32_t)((uintptr_t)&pPatch->auCode[0] >> 32);
3059#endif
3060 pPatch->ChangedIdt.u16SegSel = ASMGetCS();
3061#ifdef RT_ARCH_AMD64
3062 pPatch->ChangedIdt.u3IST = 0;
3063 pPatch->ChangedIdt.u5Reserved = 0;
3064#else /* x86 */
3065 pPatch->ChangedIdt.u5Reserved = 0;
3066 pPatch->ChangedIdt.u3Type1 = 0;
3067#endif /* x86 */
3068 pPatch->ChangedIdt.u5Type2 = SUPDRV_IDTE_TYPE2_INTERRUPT_GATE;
3069 pPatch->ChangedIdt.u2DPL = 3;
3070 pPatch->ChangedIdt.u1Present = 1;
3071
3072 /*
3073 * Generate the patch code.
3074 */
3075 {
3076#ifdef RT_ARCH_AMD64
3077 union
3078 {
3079 uint8_t *pb;
3080 uint32_t *pu32;
3081 uint64_t *pu64;
3082 } u, uFixJmp, uFixCall, uNotNested;
3083 u.pb = &pPatch->auCode[0];
3084
3085 /* check the cookie */
3086 *u.pb++ = 0x3d; // cmp eax, GLOBALCOOKIE
3087 *u.pu32++ = pDevExt->u32Cookie;
3088
3089 *u.pb++ = 0x74; // jz @VBoxCall
3090 *u.pb++ = 2;
3091
3092 /* jump to forwarder code. */
3093 *u.pb++ = 0xeb;
3094 uFixJmp = u;
3095 *u.pb++ = 0xfe;
3096
3097 // @VBoxCall:
3098 *u.pb++ = 0x0f; // swapgs
3099 *u.pb++ = 0x01;
3100 *u.pb++ = 0xf8;
3101
3102 /*
3103 * Call VMMR0Entry
3104 * We don't have to push the arguments here, but we have top
3105 * reserve some stack space for the interrupt forwarding.
3106 */
3107# ifdef RT_OS_WINDOWS
3108 *u.pb++ = 0x50; // push rax ; alignment filler.
3109 *u.pb++ = 0x41; // push r8 ; uArg
3110 *u.pb++ = 0x50;
3111 *u.pb++ = 0x52; // push rdx ; uOperation
3112 *u.pb++ = 0x51; // push rcx ; pVM
3113# else
3114 *u.pb++ = 0x51; // push rcx ; alignment filler.
3115 *u.pb++ = 0x52; // push rdx ; uArg
3116 *u.pb++ = 0x56; // push rsi ; uOperation
3117 *u.pb++ = 0x57; // push rdi ; pVM
3118# endif
3119
3120 *u.pb++ = 0xff; // call qword [pfnVMMR0EntryInt wrt rip]
3121 *u.pb++ = 0x15;
3122 uFixCall = u;
3123 *u.pu32++ = 0;
3124
3125 *u.pb++ = 0x48; // add rsp, 20h ; remove call frame.
3126 *u.pb++ = 0x81;
3127 *u.pb++ = 0xc4;
3128 *u.pu32++ = 0x20;
3129
3130 *u.pb++ = 0x0f; // swapgs
3131 *u.pb++ = 0x01;
3132 *u.pb++ = 0xf8;
3133
3134 /* Return to R3. */
3135 uNotNested = u;
3136 *u.pb++ = 0x48; // iretq
3137 *u.pb++ = 0xcf;
3138
3139 while ((uintptr_t)u.pb & 0x7) // align 8
3140 *u.pb++ = 0xcc;
3141
3142 /* Pointer to the VMMR0Entry. */ // pfnVMMR0EntryInt dq StubVMMR0Entry
3143 *uFixCall.pu32 = (uint32_t)(u.pb - uFixCall.pb - 4); uFixCall.pb = NULL;
3144 pPatch->offVMMR0EntryFixup = (uint16_t)(u.pb - &pPatch->auCode[0]);
3145 *u.pu64++ = pDevExt->pvVMMR0 ? (uint64_t)pDevExt->pfnVMMR0EntryInt : (uint64_t)u.pb + 8;
3146
3147 /* stub entry. */ // StubVMMR0Entry:
3148 pPatch->offStub = (uint16_t)(u.pb - &pPatch->auCode[0]);
3149 *u.pb++ = 0x33; // xor eax, eax
3150 *u.pb++ = 0xc0;
3151
3152 *u.pb++ = 0x48; // dec rax
3153 *u.pb++ = 0xff;
3154 *u.pb++ = 0xc8;
3155
3156 *u.pb++ = 0xc3; // ret
3157
3158 /* forward to the original handler using a retf. */
3159 *uFixJmp.pb = (uint8_t)(u.pb - uFixJmp.pb - 1); uFixJmp.pb = NULL;
3160
3161 *u.pb++ = 0x68; // push <target cs>
3162 *u.pu32++ = !pPatch->SavedIdt.u5Type2 ? ASMGetCS() : pPatch->SavedIdt.u16SegSel;
3163
3164 *u.pb++ = 0x68; // push <low target rip>
3165 *u.pu32++ = !pPatch->SavedIdt.u5Type2
3166 ? (uint32_t)(uintptr_t)uNotNested.pb
3167 : (uint32_t)pPatch->SavedIdt.u16OffsetLow
3168 | (uint32_t)pPatch->SavedIdt.u16OffsetHigh << 16;
3169
3170 *u.pb++ = 0xc7; // mov dword [rsp + 4], <high target rip>
3171 *u.pb++ = 0x44;
3172 *u.pb++ = 0x24;
3173 *u.pb++ = 0x04;
3174 *u.pu32++ = !pPatch->SavedIdt.u5Type2
3175 ? (uint32_t)((uint64_t)uNotNested.pb >> 32)
3176 : pPatch->SavedIdt.u32OffsetTop;
3177
3178 *u.pb++ = 0x48; // retf ; does this require prefix?
3179 *u.pb++ = 0xcb;
3180
3181#else /* RT_ARCH_X86 */
3182
3183 union
3184 {
3185 uint8_t *pb;
3186 uint16_t *pu16;
3187 uint32_t *pu32;
3188 } u, uFixJmpNotNested, uFixJmp, uFixCall, uNotNested;
3189 u.pb = &pPatch->auCode[0];
3190
3191 /* check the cookie */
3192 *u.pb++ = 0x81; // cmp esi, GLOBALCOOKIE
3193 *u.pb++ = 0xfe;
3194 *u.pu32++ = pDevExt->u32Cookie;
3195
3196 *u.pb++ = 0x74; // jz VBoxCall
3197 uFixJmp = u;
3198 *u.pb++ = 0;
3199
3200 /* jump (far) to the original handler / not-nested-stub. */
3201 *u.pb++ = 0xea; // jmp far NotNested
3202 uFixJmpNotNested = u;
3203 *u.pu32++ = 0;
3204 *u.pu16++ = 0;
3205
3206 /* save selector registers. */ // VBoxCall:
3207 *uFixJmp.pb = (uint8_t)(u.pb - uFixJmp.pb - 1);
3208 *u.pb++ = 0x0f; // push fs
3209 *u.pb++ = 0xa0;
3210
3211 *u.pb++ = 0x1e; // push ds
3212
3213 *u.pb++ = 0x06; // push es
3214
3215 /* call frame */
3216 *u.pb++ = 0x51; // push ecx
3217
3218 *u.pb++ = 0x52; // push edx
3219
3220 *u.pb++ = 0x50; // push eax
3221
3222 /* load ds, es and perhaps fs before call. */
3223 *u.pb++ = 0xb8; // mov eax, KernelDS
3224 *u.pu32++ = ASMGetDS();
3225
3226 *u.pb++ = 0x8e; // mov ds, eax
3227 *u.pb++ = 0xd8;
3228
3229 *u.pb++ = 0x8e; // mov es, eax
3230 *u.pb++ = 0xc0;
3231
3232#ifdef RT_OS_WINDOWS
3233 *u.pb++ = 0xb8; // mov eax, KernelFS
3234 *u.pu32++ = ASMGetFS();
3235
3236 *u.pb++ = 0x8e; // mov fs, eax
3237 *u.pb++ = 0xe0;
3238#endif
3239
3240 /* do the call. */
3241 *u.pb++ = 0xe8; // call _VMMR0Entry / StubVMMR0Entry
3242 uFixCall = u;
3243 pPatch->offVMMR0EntryFixup = (uint16_t)(u.pb - &pPatch->auCode[0]);
3244 *u.pu32++ = 0xfffffffb;
3245
3246 *u.pb++ = 0x83; // add esp, 0ch ; cdecl
3247 *u.pb++ = 0xc4;
3248 *u.pb++ = 0x0c;
3249
3250 /* restore selector registers. */
3251 *u.pb++ = 0x07; // pop es
3252 //
3253 *u.pb++ = 0x1f; // pop ds
3254
3255 *u.pb++ = 0x0f; // pop fs
3256 *u.pb++ = 0xa1;
3257
3258 uNotNested = u; // NotNested:
3259 *u.pb++ = 0xcf; // iretd
3260
3261 /* the stub VMMR0Entry. */ // StubVMMR0Entry:
3262 pPatch->offStub = (uint16_t)(u.pb - &pPatch->auCode[0]);
3263 *u.pb++ = 0x33; // xor eax, eax
3264 *u.pb++ = 0xc0;
3265
3266 *u.pb++ = 0x48; // dec eax
3267
3268 *u.pb++ = 0xc3; // ret
3269
3270 /* Fixup the VMMR0Entry call. */
3271 if (pDevExt->pvVMMR0)
3272 *uFixCall.pu32 = (uint32_t)pDevExt->pfnVMMR0EntryInt - (uint32_t)(uFixCall.pu32 + 1);
3273 else
3274 *uFixCall.pu32 = (uint32_t)&pPatch->auCode[pPatch->offStub] - (uint32_t)(uFixCall.pu32 + 1);
3275
3276 /* Fixup the forward / nested far jump. */
3277 if (!pPatch->SavedIdt.u5Type2)
3278 {
3279 *uFixJmpNotNested.pu32++ = (uint32_t)uNotNested.pb;
3280 *uFixJmpNotNested.pu16++ = ASMGetCS();
3281 }
3282 else
3283 {
3284 *uFixJmpNotNested.pu32++ = ((uint32_t)pPatch->SavedIdt.u16OffsetHigh << 16) | pPatch->SavedIdt.u16OffsetLow;
3285 *uFixJmpNotNested.pu16++ = pPatch->SavedIdt.u16SegSel;
3286 }
3287#endif /* RT_ARCH_X86 */
3288 Assert(u.pb <= &pPatch->auCode[sizeof(pPatch->auCode)]);
3289#if 0
3290 /* dump the patch code */
3291 Log2(("patch code: %p\n", &pPatch->auCode[0]));
3292 for (uFixCall.pb = &pPatch->auCode[0]; uFixCall.pb < u.pb; uFixCall.pb++)
3293 Log2(("0x%02x,\n", *uFixCall.pb));
3294#endif
3295 }
3296
3297 /*
3298 * Install the patch.
3299 */
3300 supdrvIdtWrite(pPatch->pIdtEntry, &pPatch->ChangedIdt);
3301 AssertMsg(!memcmp((void *)pPatch->pIdtEntry, &pPatch->ChangedIdt, sizeof(pPatch->ChangedIdt)), ("The stupid change code didn't work!!!!!\n"));
3302
3303 /*
3304 * Link in the patch.
3305 */
3306 pPatch->pNext = pDevExt->pIdtPatches;
3307 pDevExt->pIdtPatches = pPatch;
3308
3309 return pPatch;
3310}
3311
3312
3313/**
3314 * Removes the sessions IDT references.
3315 * This will uninstall our IDT patch if we left unreferenced.
3316 *
3317 * @returns VINF_SUCCESS.
3318 * @param pDevExt Device globals.
3319 * @param pSession Session data.
3320 */
3321static int supdrvIOCtl_IdtRemoveAll(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
3322{
3323 PSUPDRVPATCHUSAGE pUsage;
3324 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
3325 LogFlow(("supdrvIOCtl_IdtRemoveAll: pSession=%p\n", pSession));
3326
3327 /*
3328 * Take the spinlock.
3329 */
3330 RTSpinlockAcquireNoInts(pDevExt->Spinlock, &SpinlockTmp);
3331
3332 /*
3333 * Walk usage list, removing patches as their usage count reaches zero.
3334 */
3335 pUsage = pSession->pPatchUsage;
3336 while (pUsage)
3337 {
3338 if (pUsage->pPatch->cUsage <= pUsage->cUsage)
3339 supdrvIdtRemoveOne(pDevExt, pUsage->pPatch);
3340 else
3341 pUsage->pPatch->cUsage -= pUsage->cUsage;
3342
3343 /* next */
3344 pUsage = pUsage->pNext;
3345 }
3346
3347 /*
3348 * Empty the usage chain and we're done inside the spinlock.
3349 */
3350 pUsage = pSession->pPatchUsage;
3351 pSession->pPatchUsage = NULL;
3352
3353 RTSpinlockReleaseNoInts(pDevExt->Spinlock, &SpinlockTmp);
3354
3355 /*
3356 * Free usage entries.
3357 */
3358 while (pUsage)
3359 {
3360 void *pvToFree = pUsage;
3361 pUsage->cUsage = 0;
3362 pUsage->pPatch = NULL;
3363 pUsage = pUsage->pNext;
3364 RTMemFree(pvToFree);
3365 }
3366
3367 return VINF_SUCCESS;
3368}
3369
3370
3371/**
3372 * Remove one patch.
3373 *
3374 * Worker for supdrvIOCtl_IdtRemoveAll.
3375 *
3376 * @param pDevExt Device globals.
3377 * @param pPatch Patch entry to remove.
3378 * @remark Caller must own SUPDRVDEVEXT::Spinlock!
3379 */
3380static void supdrvIdtRemoveOne(PSUPDRVDEVEXT pDevExt, PSUPDRVPATCH pPatch)
3381{
3382 LogFlow(("supdrvIdtRemoveOne: pPatch=%p\n", pPatch));
3383
3384 pPatch->cUsage = 0;
3385
3386 /*
3387 * If the IDT entry was changed it have to kick around for ever!
3388 * This will be attempted freed again, perhaps next time we'll succeed :-)
3389 */
3390 if (memcmp((void *)pPatch->pIdtEntry, &pPatch->ChangedIdt, sizeof(pPatch->ChangedIdt)))
3391 {
3392 AssertMsgFailed(("The hijacked IDT entry has CHANGED!!!\n"));
3393 return;
3394 }
3395
3396 /*
3397 * Unlink it.
3398 */
3399 if (pDevExt->pIdtPatches != pPatch)
3400 {
3401 PSUPDRVPATCH pPatchPrev = pDevExt->pIdtPatches;
3402 while (pPatchPrev)
3403 {
3404 if (pPatchPrev->pNext == pPatch)
3405 {
3406 pPatchPrev->pNext = pPatch->pNext;
3407 break;
3408 }
3409 pPatchPrev = pPatchPrev->pNext;
3410 }
3411 Assert(!pPatchPrev);
3412 }
3413 else
3414 pDevExt->pIdtPatches = pPatch->pNext;
3415 pPatch->pNext = NULL;
3416
3417
3418 /*
3419 * Verify and restore the IDT.
3420 */
3421 AssertMsg(!memcmp((void *)pPatch->pIdtEntry, &pPatch->ChangedIdt, sizeof(pPatch->ChangedIdt)), ("The hijacked IDT entry has CHANGED!!!\n"));
3422 supdrvIdtWrite(pPatch->pIdtEntry, &pPatch->SavedIdt);
3423 AssertMsg(!memcmp((void *)pPatch->pIdtEntry, &pPatch->SavedIdt, sizeof(pPatch->SavedIdt)), ("The hijacked IDT entry has CHANGED!!!\n"));
3424
3425 /*
3426 * Put it in the free list.
3427 * (This free list stuff is to calm my paranoia.)
3428 */
3429 pPatch->pvIdt = NULL;
3430 pPatch->pIdtEntry = NULL;
3431
3432 pPatch->pNext = pDevExt->pIdtPatchesFree;
3433 pDevExt->pIdtPatchesFree = pPatch;
3434}
3435
3436
3437/**
3438 * Write to an IDT entry.
3439 *
3440 * @param pvIdtEntry Where to write.
3441 * @param pNewIDTEntry What to write.
3442 */
3443static void supdrvIdtWrite(volatile void *pvIdtEntry, const SUPDRVIDTE *pNewIDTEntry)
3444{
3445 RTR0UINTREG uCR0;
3446 RTR0UINTREG uFlags;
3447
3448 /*
3449 * On SMP machines (P4 hyperthreading included) we must preform a
3450 * 64-bit locked write when updating the IDT entry.
3451 *
3452 * The F00F bugfix for linux (and probably other OSes) causes
3453 * the IDT to be pointing to an readonly mapping. We get around that
3454 * by temporarily turning of WP. Since we're inside a spinlock at this
3455 * point, interrupts are disabled and there isn't any way the WP bit
3456 * flipping can cause any trouble.
3457 */
3458
3459 /* Save & Clear interrupt flag; Save & clear WP. */
3460 uFlags = ASMGetFlags();
3461 ASMSetFlags(uFlags & ~(RTR0UINTREG)(1 << 9)); /*X86_EFL_IF*/
3462 Assert(!(ASMGetFlags() & (1 << 9)));
3463 uCR0 = ASMGetCR0();
3464 ASMSetCR0(uCR0 & ~(RTR0UINTREG)(1 << 16)); /*X86_CR0_WP*/
3465
3466 /* Update IDT Entry */
3467#ifdef RT_ARCH_AMD64
3468 ASMAtomicXchgU128((volatile uint128_t *)pvIdtEntry, *(uint128_t *)(uintptr_t)pNewIDTEntry);
3469#else
3470 ASMAtomicXchgU64((volatile uint64_t *)pvIdtEntry, *(uint64_t *)(uintptr_t)pNewIDTEntry);
3471#endif
3472
3473 /* Restore CR0 & Flags */
3474 ASMSetCR0(uCR0);
3475 ASMSetFlags(uFlags);
3476}
3477#endif /* VBOX_WITH_IDT_PATCHING */
3478
3479
3480/**
3481 * Opens an image. If it's the first time it's opened the call must upload
3482 * the bits using the supdrvIOCtl_LdrLoad() / SUPDRV_IOCTL_LDR_LOAD function.
3483 *
3484 * This is the 1st step of the loading.
3485 *
3486 * @returns IPRT status code.
3487 * @param pDevExt Device globals.
3488 * @param pSession Session data.
3489 * @param pReq The open request.
3490 */
3491static int supdrvIOCtl_LdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDROPEN pReq)
3492{
3493 PSUPDRVLDRIMAGE pImage;
3494 unsigned cb;
3495 void *pv;
3496 LogFlow(("supdrvIOCtl_LdrOpen: szName=%s cbImage=%d\n", pReq->u.In.szName, pReq->u.In.cbImage));
3497
3498 /*
3499 * Check if we got an instance of the image already.
3500 */
3501 RTSemFastMutexRequest(pDevExt->mtxLdr);
3502 for (pImage = pDevExt->pLdrImages; pImage; pImage = pImage->pNext)
3503 {
3504 if (!strcmp(pImage->szName, pReq->u.In.szName))
3505 {
3506 pImage->cUsage++;
3507 pReq->u.Out.pvImageBase = pImage->pvImage;
3508 pReq->u.Out.fNeedsLoading = pImage->uState == SUP_IOCTL_LDR_OPEN;
3509 supdrvLdrAddUsage(pSession, pImage);
3510 RTSemFastMutexRelease(pDevExt->mtxLdr);
3511 return VINF_SUCCESS;
3512 }
3513 }
3514 /* (not found - add it!) */
3515
3516 /*
3517 * Allocate memory.
3518 */
3519 cb = pReq->u.In.cbImage + sizeof(SUPDRVLDRIMAGE) + 31;
3520 pv = RTMemExecAlloc(cb);
3521 if (!pv)
3522 {
3523 RTSemFastMutexRelease(pDevExt->mtxLdr);
3524 Log(("supdrvIOCtl_LdrOpen: RTMemExecAlloc(%u) failed\n", cb));
3525 return VERR_NO_MEMORY;
3526 }
3527
3528 /*
3529 * Setup and link in the LDR stuff.
3530 */
3531 pImage = (PSUPDRVLDRIMAGE)pv;
3532 pImage->pvImage = RT_ALIGN_P(pImage + 1, 32);
3533 pImage->cbImage = pReq->u.In.cbImage;
3534 pImage->pfnModuleInit = NULL;
3535 pImage->pfnModuleTerm = NULL;
3536 pImage->uState = SUP_IOCTL_LDR_OPEN;
3537 pImage->cUsage = 1;
3538 strcpy(pImage->szName, pReq->u.In.szName);
3539
3540 pImage->pNext = pDevExt->pLdrImages;
3541 pDevExt->pLdrImages = pImage;
3542
3543 supdrvLdrAddUsage(pSession, pImage);
3544
3545 pReq->u.Out.pvImageBase = pImage->pvImage;
3546 pReq->u.Out.fNeedsLoading = true;
3547 RTSemFastMutexRelease(pDevExt->mtxLdr);
3548 return VINF_SUCCESS;
3549}
3550
3551
3552/**
3553 * Loads the image bits.
3554 *
3555 * This is the 2nd step of the loading.
3556 *
3557 * @returns IPRT status code.
3558 * @param pDevExt Device globals.
3559 * @param pSession Session data.
3560 * @param pReq The request.
3561 */
3562static int supdrvIOCtl_LdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPLDRLOAD pReq)
3563{
3564 PSUPDRVLDRUSAGE pUsage;
3565 PSUPDRVLDRIMAGE pImage;
3566 int rc;
3567 LogFlow(("supdrvIOCtl_LdrLoad: pvImageBase=%p cbImage=%d\n", pReq->u.In.pvImageBase, pReq->u.In.cbImage));
3568
3569 /*
3570 * Find the ldr image.
3571 */
3572 RTSemFastMutexRequest(pDevExt->mtxLdr);
3573 pUsage = pSession->pLdrUsage;
3574 while (pUsage && pUsage->pImage->pvImage != pReq->u.In.pvImageBase)
3575 pUsage = pUsage->pNext;
3576 if (!pUsage)
3577 {
3578 RTSemFastMutexRelease(pDevExt->mtxLdr);
3579 Log(("SUP_IOCTL_LDR_LOAD: couldn't find image!\n"));
3580 return VERR_INVALID_HANDLE;
3581 }
3582 pImage = pUsage->pImage;
3583 if (pImage->cbImage != pReq->u.In.cbImage)
3584 {
3585 RTSemFastMutexRelease(pDevExt->mtxLdr);
3586 Log(("SUP_IOCTL_LDR_LOAD: image size mismatch!! %d(prep) != %d(load)\n", pImage->cbImage, pReq->u.In.cbImage));
3587 return VERR_INVALID_HANDLE;
3588 }
3589 if (pImage->uState != SUP_IOCTL_LDR_OPEN)
3590 {
3591 unsigned uState = pImage->uState;
3592 RTSemFastMutexRelease(pDevExt->mtxLdr);
3593 if (uState != SUP_IOCTL_LDR_LOAD)
3594 AssertMsgFailed(("SUP_IOCTL_LDR_LOAD: invalid image state %d (%#x)!\n", uState, uState));
3595 return SUPDRV_ERR_ALREADY_LOADED;
3596 }
3597 switch (pReq->u.In.eEPType)
3598 {
3599 case SUPLDRLOADEP_NOTHING:
3600 break;
3601 case SUPLDRLOADEP_VMMR0:
3602 if ( !pReq->u.In.EP.VMMR0.pvVMMR0
3603 || !pReq->u.In.EP.VMMR0.pvVMMR0EntryInt
3604 || !pReq->u.In.EP.VMMR0.pvVMMR0EntryFast
3605 || !pReq->u.In.EP.VMMR0.pvVMMR0EntryEx)
3606 {
3607 RTSemFastMutexRelease(pDevExt->mtxLdr);
3608 Log(("NULL pointer: pvVMMR0=%p pvVMMR0EntryInt=%p pvVMMR0EntryFast=%p pvVMMR0EntryEx=%p!\n",
3609 pReq->u.In.EP.VMMR0.pvVMMR0, pReq->u.In.EP.VMMR0.pvVMMR0EntryInt,
3610 pReq->u.In.EP.VMMR0.pvVMMR0EntryFast, pReq->u.In.EP.VMMR0.pvVMMR0EntryEx));
3611 return VERR_INVALID_PARAMETER;
3612 }
3613 /** @todo validate pReq->u.In.EP.VMMR0.pvVMMR0 against pvImage! */
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.

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