VirtualBox

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

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

Increase minor version, needed some timestamp functions.

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