SUPLib.cpp revision 2a650f7a05edb7157650935d4985edaf753f4561
/* $Id$ */
/** @file
* VirtualBox Support Library - Common code.
*/
/*
* Copyright (C) 2006-2007 innotek GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation,
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
* distribution. VirtualBox OSE is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/** @page pg_sup SUP - The Support Library
*
* The support library is responsible for providing facilities to load
* VMM Host Ring-0 code, to call Host VMM Ring-0 code from Ring-3 Host
* code, to pin down physical memory, and more.
*
* The VMM Host Ring-0 code can be combined in the support driver if
* permitted by kernel module license policies. If it is not combined
* it will be externalized in a .r0 module that will be loaded using
* the IPRT loader.
*
* The Ring-0 calling is done thru a generic SUP interface which will
* tranfer an argument set and call a predefined entry point in the Host
* VMM Ring-0 code.
*
* See @ref grp_sup "SUP - Support APIs" for API details.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SUP
#include <VBox/sup.h>
#include <VBox/err.h>
#include <VBox/param.h>
#ifdef VBOX_WITHOUT_IDT_PATCHING
# include <VBox/vmm.h>
#endif
#include <VBox/log.h>
#include <iprt/assert.h>
#include <iprt/alloc.h>
#include <iprt/alloca.h>
#include <iprt/ldr.h>
#include <iprt/asm.h>
#include <iprt/system.h>
#include <iprt/thread.h>
#include <iprt/process.h>
#include <iprt/string.h>
#include <iprt/env.h>
#include "SUPLibInternal.h"
#include "SUPDRVIOC.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** R0 VMM module name. */
#define VMMR0_NAME "VMMR0"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
typedef DECLCALLBACK(int) FNCALLVMMR0(PVMR0 pVMR0, unsigned uOperation, void *pvArg);
typedef FNCALLVMMR0 *PFNCALLVMMR0;
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Pointer to the Global Information Page.
*
* This pointer is valid as long as SUPLib has a open session. Anyone using
* the page must treat this pointer as higly volatile and not trust it beyond
* one transaction.
*
* @todo This will probably deserve it's own session or some other good solution...
*/
DECLEXPORT(PSUPGLOBALINFOPAGE) g_pSUPGlobalInfoPage;
/** Address of the ring-0 mapping of the GIP. */
static PSUPGLOBALINFOPAGE g_pSUPGlobalInfoPageR0;
/** The physical address of the GIP. */
static RTHCPHYS g_HCPhysSUPGlobalInfoPage = NIL_RTHCPHYS;
/** The negotiated cookie. */
uint32_t g_u32Cookie = 0;
/** The negotiated session cookie. */
uint32_t g_u32SessionCookie;
/** Session handle. */
PSUPDRVSESSION g_pSession;
/** R0 SUP Functions used for resolving referenced to the SUPR0 module. */
static PSUPQUERYFUNCS_OUT g_pFunctions;
#ifndef VBOX_WITHOUT_IDT_PATCHING
/** The negotiated interrupt number. */
static uint8_t g_u8Interrupt = 3;
/** Pointer to the generated code fore calling VMMR0. */
static PFNCALLVMMR0 g_pfnCallVMMR0;
#endif
/** VMMR0 Load Address. */
static RTR0PTR g_pvVMMR0 = NIL_RTR0PTR;
/** Init counter. */
static unsigned g_cInits = 0;
/** Fake mode indicator. (~0 at first, 0 or 1 after first test) */
static uint32_t g_u32FakeMode = ~0;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int supInitFake(PSUPDRVSESSION *ppSession);
static int supLoadModule(const char *pszFilename, const char *pszModule, void **ppvImageBase);
#ifndef VBOX_WITHOUT_IDT_PATCHING
static int supInstallIDTE(void);
#endif
static DECLCALLBACK(int) supLoadModuleResolveImport(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol, RTUINTPTR *pValue, void *pvUser);
SUPR3DECL(int) SUPInstall(void)
{
return suplibOsInstall();
}
SUPR3DECL(int) SUPUninstall(void)
{
return suplibOsUninstall();
}
SUPR3DECL(int) SUPInit(PSUPDRVSESSION *ppSession /* NULL */, size_t cbReserve /* 0 */)
{
/*
* Perform some sanity checks.
* (Got some trouble with compile time member alignment assertions.)
*/
Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, u64NanoTSLastUpdateHz) & 0x7));
Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs) & 0x1f));
Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs[1]) & 0x1f));
Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64NanoTS) & 0x7));
Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64TSC) & 0x7));
Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64CpuHz) & 0x7));
/*
* Check if already initialized.
*/
if (ppSession)
*ppSession = g_pSession;
if (g_cInits++ > 0)
return VINF_SUCCESS;
/*
* Check for fake mode.
*
* Fake mode is used when we're doing smoke testing and debugging.
* It's also useful on platforms where we haven't root access or which
* we haven't ported the support driver to.
*/
if (g_u32FakeMode == ~0U)
{
const char *psz = RTEnvGet("VBOX_SUPLIB_FAKE");
if (psz && !strcmp(psz, "fake"))
ASMAtomicCmpXchgU32(&g_u32FakeMode, 1, ~0U);
else
ASMAtomicCmpXchgU32(&g_u32FakeMode, 0, ~0U);
}
if (RT_UNLIKELY(g_u32FakeMode))
return supInitFake(ppSession);
/**
* Open the support driver.
*/
int rc = suplibOsInit(cbReserve);
if (VBOX_SUCCESS(rc))
{
/*
* Negotiate the cookie.
*/
SUPCOOKIE_IN In;
SUPCOOKIE_OUT Out = {0,0,0,0,0,NIL_RTR0PTR};
strcpy(In.szMagic, SUPCOOKIE_MAGIC);
In.u32ReqVersion = SUPDRVIOC_VERSION;
In.u32MinVersion = SUPDRVIOC_VERSION & 0xffff0000;
rc = suplibOsIOCtl(SUP_IOCTL_COOKIE, &In, sizeof(In), &Out, sizeof(Out));
if (VBOX_SUCCESS(rc))
{
if ((Out.u32SessionVersion & 0xffff0000) == (SUPDRVIOC_VERSION & 0xffff0000))
{
/*
* Query the functions.
*/
SUPQUERYFUNCS_IN FuncsIn;
FuncsIn.u32Cookie = Out.u32Cookie;
FuncsIn.u32SessionCookie = Out.u32SessionCookie;
unsigned cbFuncsOut = RT_OFFSETOF(SUPQUERYFUNCS_OUT, aFunctions[Out.cFunctions]);
PSUPQUERYFUNCS_OUT pFuncsOut = (PSUPQUERYFUNCS_OUT)RTMemAllocZ(cbFuncsOut);
if (pFuncsOut)
{
rc = suplibOsIOCtl(SUP_IOCTL_QUERY_FUNCS, &FuncsIn, sizeof(FuncsIn), pFuncsOut, cbFuncsOut);
if (VBOX_SUCCESS(rc))
{
g_u32Cookie = Out.u32Cookie;
g_u32SessionCookie = Out.u32SessionCookie;
g_pSession = Out.pSession;
g_pFunctions = pFuncsOut;
if (ppSession)
*ppSession = Out.pSession;
/*
* Map the GIP into userspace.
* This is an optional feature, so we will ignore any failures here.
*/
if (!g_pSUPGlobalInfoPage)
{
SUPGIPMAP_IN GipIn = {0};
SUPGIPMAP_OUT GipOut = {NULL, 0};
GipIn.u32Cookie = Out.u32Cookie;
GipIn.u32SessionCookie = Out.u32SessionCookie;
rc = suplibOsIOCtl(SUP_IOCTL_GIP_MAP, &GipIn, sizeof(GipIn), &GipOut, sizeof(GipOut));
if (VBOX_SUCCESS(rc))
{
AssertRelease(GipOut.pGipR3->u32Magic == SUPGLOBALINFOPAGE_MAGIC);
AssertRelease(GipOut.pGipR3->u32Version >= SUPGLOBALINFOPAGE_VERSION);
ASMAtomicXchgSize(&g_HCPhysSUPGlobalInfoPage, GipOut.HCPhysGip);
ASMAtomicCmpXchgPtr((void * volatile *)&g_pSUPGlobalInfoPage, (void *)GipOut.pGipR3, NULL);
ASMAtomicCmpXchgPtr((void * volatile *)&g_pSUPGlobalInfoPageR0, (void *)GipOut.pGipR0, NULL);
}
else
rc = VINF_SUCCESS;
}
return rc;
}
RTMemFree(pFuncsOut);
}
else
rc = VERR_NO_MEMORY;
}
else
{
LogRel(("Support driver version mismatch: SessionVersion=%#x DriverVersion=%#x ClientVersion=%#x\n",
Out.u32SessionVersion, Out.u32DriverVersion, SUPDRVIOC_VERSION));
rc = VERR_VM_DRIVER_VERSION_MISMATCH;
}
}
else
{
if (rc == VERR_INVALID_PARAMETER) /* for pre 0x00040002 drivers */
rc = VERR_VM_DRIVER_VERSION_MISMATCH;
if (rc == VERR_VM_DRIVER_VERSION_MISMATCH)
LogRel(("Support driver version mismatch: DriverVersion=%#x ClientVersion=%#x\n",
Out.u32DriverVersion, SUPDRVIOC_VERSION));
else
LogRel(("Support driver version/Cookie negotiations error: rc=%Vrc\n", rc));
}
suplibOsTerm();
}
AssertMsgFailed(("SUPInit() failed rc=%Vrc\n", rc));
g_cInits--;
return rc;
}
/**
* Fake mode init.
*/
static int supInitFake(PSUPDRVSESSION *ppSession)
{
Log(("SUP: Fake mode!\n"));
static const SUPFUNC s_aFakeFunctions[] =
{
/* name function */
{ "SUPR0ObjRegister", 0xefef0000 },
{ "SUPR0ObjAddRef", 0xefef0001 },
{ "SUPR0ObjRelease", 0xefef0002 },
{ "SUPR0ObjVerifyAccess", 0xefef0003 },
{ "SUPR0LockMem", 0xefef0004 },
{ "SUPR0UnlockMem", 0xefef0005 },
{ "SUPR0ContAlloc", 0xefef0006 },
{ "SUPR0ContFree", 0xefef0007 },
{ "SUPR0MemAlloc", 0xefef0008 },
{ "SUPR0MemGetPhys", 0xefef0009 },
{ "SUPR0MemFree", 0xefef000a },
{ "SUPR0Printf", 0xefef000b },
{ "RTMemAlloc", 0xefef000c },
{ "RTMemAllocZ", 0xefef000d },
{ "RTMemFree", 0xefef000e },
{ "RTSemFastMutexCreate", 0xefef000f },
{ "RTSemFastMutexDestroy", 0xefef0010 },
{ "RTSemFastMutexRequest", 0xefef0011 },
{ "RTSemFastMutexRelease", 0xefef0012 },
{ "RTSemEventCreate", 0xefef0013 },
{ "RTSemEventSignal", 0xefef0014 },
{ "RTSemEventWait", 0xefef0015 },
{ "RTSemEventDestroy", 0xefef0016 },
{ "RTSpinlockCreate", 0xefef0017 },
{ "RTSpinlockDestroy", 0xefef0018 },
{ "RTSpinlockAcquire", 0xefef0019 },
{ "RTSpinlockRelease", 0xefef001a },
{ "RTSpinlockAcquireNoInts", 0xefef001b },
{ "RTSpinlockReleaseNoInts", 0xefef001c },
{ "RTThreadNativeSelf", 0xefef001d },
{ "RTThreadSleep", 0xefef001e },
{ "RTThreadYield", 0xefef001f },
{ "RTLogDefaultInstance", 0xefef0020 },
{ "RTLogRelDefaultInstance", 0xefef0021 },
{ "RTLogSetDefaultInstanceThread", 0xefef0022 },
{ "RTLogLogger", 0xefef0023 },
{ "RTLogLoggerEx", 0xefef0024 },
{ "RTLogLoggerExV", 0xefef0025 },
{ "AssertMsg1", 0xefef0026 },
{ "AssertMsg2", 0xefef0027 },
};
/* fake r0 functions. */
g_pFunctions = (PSUPQUERYFUNCS_OUT)RTMemAllocZ(RT_OFFSETOF(SUPQUERYFUNCS_OUT, aFunctions[RT_ELEMENTS(s_aFakeFunctions)]));
if (g_pFunctions)
{
g_pFunctions->cFunctions = RT_ELEMENTS(s_aFakeFunctions);
memcpy(&g_pFunctions->aFunctions[0], &s_aFakeFunctions[0], sizeof(s_aFakeFunctions));
g_pSession = (PSUPDRVSESSION)(void *)g_pFunctions;
if (ppSession)
*ppSession = g_pSession;
#ifndef VBOX_WITHOUT_IDT_PATCHING
Assert(g_u8Interrupt == 3);
#endif
/* fake the GIP. */
g_pSUPGlobalInfoPage = (PSUPGLOBALINFOPAGE)RTMemPageAlloc(PAGE_SIZE);
if (g_pSUPGlobalInfoPage)
{
g_pSUPGlobalInfoPageR0 = g_pSUPGlobalInfoPage;
g_HCPhysSUPGlobalInfoPage = NIL_RTHCPHYS & ~(RTHCPHYS)PAGE_OFFSET_MASK;
/* the page is supposed to be invalid, so don't set the magic. */
return VINF_SUCCESS;
}
RTMemFree(g_pFunctions);
g_pFunctions = NULL;
}
return VERR_NO_MEMORY;
}
SUPR3DECL(int) SUPTerm(bool fForced)
{
/*
* Verify state.
*/
AssertMsg(g_cInits > 0, ("SUPTerm() is called before SUPInit()!\n"));
if (g_cInits == 0)
return VERR_WRONG_ORDER;
if (g_cInits == 1 || fForced)
{
/*
* NULL the GIP pointer.
*/
if (g_pSUPGlobalInfoPage)
{
ASMAtomicXchgPtr((void * volatile *)&g_pSUPGlobalInfoPage, NULL);
ASMAtomicXchgPtr((void * volatile *)&g_pSUPGlobalInfoPageR0, NULL);
ASMAtomicXchgSize(&g_HCPhysSUPGlobalInfoPage, NIL_RTHCPHYS);
/* just a little safe guard against threads using the page. */
RTThreadSleep(50);
}
/*
* Close the support driver.
*/
int rc = suplibOsTerm();
if (rc)
return rc;
g_u32Cookie = 0;
g_u32SessionCookie = 0;
#ifndef VBOX_WITHOUT_IDT_PATCHING
g_u8Interrupt = 3;
#endif
g_cInits = 0;
}
else
g_cInits--;
return 0;
}
SUPR3DECL(SUPPAGINGMODE) SUPGetPagingMode(void)
{
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
SUPGETPAGINGMODE_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
SUPGETPAGINGMODE_OUT Out = {SUPPAGINGMODE_INVALID};
int rc;
if (!g_u32FakeMode)
{
rc = suplibOsIOCtl(SUP_IOCTL_GET_PAGING_MODE, &In, sizeof(In), &Out, sizeof(Out));
if (VBOX_FAILURE(rc))
Out.enmMode = SUPPAGINGMODE_INVALID;
}
else
#ifdef RT_ARCH_AMD64
Out.enmMode = SUPPAGINGMODE_AMD64_GLOBAL_NX;
#else
Out.enmMode = SUPPAGINGMODE_32_BIT_GLOBAL;
#endif
return Out.enmMode;
}
SUPR3DECL(int) SUPCallVMMR0Ex(PVMR0 pVMR0, unsigned uOperation, void *pvArg, unsigned cbArg)
{
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
SUPCALLVMMR0_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
In.pVMR0 = pVMR0;
In.uOperation = uOperation;
In.cbArg = cbArg;
In.pvArg = pvArg;
Assert(!g_u32FakeMode);
SUPCALLVMMR0_OUT Out = {VINF_SUCCESS};
int rc = suplibOsIOCtl(SUP_IOCTL_CALL_VMMR0, &In, sizeof(In), &Out, sizeof(Out));
if (VBOX_SUCCESS(rc))
rc = Out.rc;
return rc;
}
SUPR3DECL(int) SUPCallVMMR0(PVMR0 pVMR0, unsigned uOperation, void *pvArg)
{
#ifndef VBOX_WITHOUT_IDT_PATCHING
return g_pfnCallVMMR0(pVMR0, uOperation, pvArg);
#else
if (RT_LIKELY(uOperation == VMMR0_DO_RAW_RUN))
{
Assert(!pvArg);
return suplibOSIOCtlFast(SUP_IOCTL_FAST_DO_RAW_RUN);
}
if (RT_LIKELY(uOperation == VMMR0_DO_HWACC_RUN))
{
Assert(!pvArg);
return suplibOSIOCtlFast(SUP_IOCTL_FAST_DO_HWACC_RUN);
}
if (uOperation == VMMR0_DO_NOP)
{
Assert(!pvArg);
return suplibOSIOCtlFast(SUP_IOCTL_FAST_DO_NOP);
}
return SUPCallVMMR0Ex(pVMR0, uOperation, pvArg, pvArg ? sizeof(pvArg) : 0);
#endif
}
SUPR3DECL(int) SUPSetVMForFastIOCtl(PVMR0 pVMR0)
{
SUPSETVMFORFAST_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
In.pVMR0 = pVMR0;
int rc;
if (RT_LIKELY(!g_u32FakeMode))
rc = suplibOsIOCtl(SUP_IOCTL_SET_VM_FOR_FAST, &In, sizeof(In), NULL, 0);
else
rc = VINF_SUCCESS;
return rc;
}
SUPR3DECL(int) SUPPageLock(void *pvStart, size_t cPages, PSUPPAGE paPages)
{
/*
* Validate.
*/
AssertPtr(pvStart);
AssertMsg(RT_ALIGN_P(pvStart, PAGE_SIZE) == pvStart, ("pvStart (%p) must be page aligned\n", pvStart));
AssertPtr(paPages);
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
SUPPINPAGES_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
In.pvR3 = pvStart;
In.cPages = cPages; AssertRelease(In.cPages == cPages);
int rc;
if (!g_u32FakeMode)
{
PSUPPINPAGES_OUT pOut;
AssertCompile(sizeof(paPages[0]) == sizeof(pOut->aPages[0]));
#if 0
size_t cbOut = RT_OFFSETOF(SUPPINPAGES_OUT, aPages[cPages]);
pOut = (PSUPPINPAGES_OUT)RTMemTmpAllocZ(cbOut);
if (!pOut)
return VERR_NO_TMP_MEMORY;
rc = suplibOsIOCtl(SUP_IOCTL_PINPAGES, &In, sizeof(In), pOut, cbOut);
if (RT_SUCCESS(rc))
memcpy(paPages, &pOut->aPages[0], sizeof(paPages[0]) * cPages);
RTMemTmpFree(pOut);
#else
/* a hack to save some time. */
pOut = (PSUPPINPAGES_OUT)(void*)paPages;
Assert(RT_OFFSETOF(SUPPINPAGES_OUT, aPages) == 0 && sizeof(paPages[0]) == sizeof(pOut->aPages[0]));
rc = suplibOsIOCtl(SUP_IOCTL_PINPAGES, &In, sizeof(In), pOut, RT_OFFSETOF(SUPPINPAGES_OUT, aPages[cPages]));
#endif
}
else
{
/* fake a successfull result. */
RTHCPHYS Phys = (uintptr_t)pvStart + PAGE_SIZE * 1024;
unsigned iPage = cPages;
while (iPage-- > 0)
paPages[iPage].Phys = Phys + (iPage << PAGE_SHIFT);
rc = VINF_SUCCESS;
}
return rc;
}
SUPR3DECL(int) SUPPageUnlock(void *pvStart)
{
/*
* Validate.
*/
AssertPtr(pvStart);
AssertMsg(RT_ALIGN_P(pvStart, PAGE_SIZE) == pvStart, ("pvStart (%p) must be page aligned\n", pvStart));
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
SUPUNPINPAGES_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
In.pvR3 = pvStart;
int rc;
if (!g_u32FakeMode)
rc = suplibOsIOCtl(SUP_IOCTL_UNPINPAGES, &In, sizeof(In), NULL, 0);
else
rc = VINF_SUCCESS;
return rc;
}
SUPR3DECL(void *) SUPContAlloc(size_t cPages, PRTHCPHYS pHCPhys)
{
return SUPContAlloc2(cPages, NIL_RTR0PTR, pHCPhys);
}
SUPR3DECL(void *) SUPContAlloc2(size_t cPages, PRTR0PTR pR0Ptr, PRTHCPHYS pHCPhys)
{
/*
* Validate.
*/
AssertMsg(cPages > 0 && cPages < 256, ("cPages=%d must be > 0 and < 256\n", cPages));
AssertPtr(pHCPhys);
*pHCPhys = NIL_RTHCPHYS;
AssertPtrNull(pR0Ptr);
if (pR0Ptr)
*pR0Ptr = NIL_RTR0PTR;
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
SUPCONTALLOC_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
In.cPages = cPages;
SUPCONTALLOC_OUT Out;
int rc;
if (!g_u32FakeMode)
rc = suplibOsIOCtl(SUP_IOCTL_CONT_ALLOC, &In, sizeof(In), &Out, sizeof(Out));
else
{
rc = SUPPageAlloc(In.cPages, &Out.pvR3);
Out.HCPhys = (uintptr_t)Out.pvR3 + (PAGE_SHIFT * 1024);
Out.pvR0 = (uintptr_t)Out.pvR3;
}
if (VBOX_SUCCESS(rc))
{
*pHCPhys = (RTHCPHYS)Out.HCPhys;
if (pR0Ptr)
*pR0Ptr = Out.pvR0;
return Out.pvR3;
}
return NULL;
}
SUPR3DECL(int) SUPContFree(void *pv, size_t cPages)
{
/*
* Validate.
*/
AssertPtr(pv);
if (!pv)
return VINF_SUCCESS;
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
SUPCONTFREE_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
In.pvR3 = pv;
int rc;
if (!g_u32FakeMode)
rc = suplibOsIOCtl(SUP_IOCTL_CONT_FREE, &In, sizeof(In), NULL, 0);
else
rc = SUPPageFree(pv, cPages);
return rc;
}
SUPR3DECL(int) SUPLowAlloc(size_t cPages, void **ppvPages, PRTR0PTR ppvPagesR0, PSUPPAGE paPages)
{
/*
* Validate.
*/
AssertMsg(cPages > 0 && cPages < 256, ("cPages=%d must be > 0 and < 256\n", cPages));
AssertPtr(ppvPages);
*ppvPages = NULL;
AssertPtr(paPages);
int rc;
if (!g_u32FakeMode)
{
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
SUPLOWALLOC_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
In.cPages = cPages;
size_t cbOut = RT_OFFSETOF(SUPLOWALLOC_OUT, aPages[cPages]);
PSUPLOWALLOC_OUT pOut = (PSUPLOWALLOC_OUT)RTMemTmpAllocZ(cbOut);
if (pOut)
{
rc = suplibOsIOCtl(SUP_IOCTL_LOW_ALLOC, &In, sizeof(In), pOut, cbOut);
if (VBOX_SUCCESS(rc))
{
*ppvPages = pOut->pvR3;
if (ppvPagesR0)
*ppvPagesR0 = pOut->pvR0;
AssertCompile(sizeof(paPages[0]) == sizeof(pOut->aPages[0]));
memcpy(paPages, &pOut->aPages[0], sizeof(paPages[0]) * cPages);
#ifdef VBOX_STRICT
for (unsigned i = 0; i < cPages; i++)
AssertReleaseMsg( paPages[i].Phys <= 0xfffff000
&& !(paPages[i].Phys & PAGE_OFFSET_MASK)
&& paPages[i].Phys > 0,
("[%d]=%VHp\n", paPages[i].Phys));
#endif
}
RTMemTmpFree(pOut);
}
else
rc = VERR_NO_TMP_MEMORY;
}
else
{
rc = SUPPageAlloc(cPages, ppvPages);
if (VBOX_SUCCESS(rc))
{
/* fake physical addresses. */
RTHCPHYS Phys = (uintptr_t)*ppvPages + PAGE_SIZE * 1024;
unsigned iPage = cPages;
while (iPage-- > 0)
paPages[iPage].Phys = Phys + (iPage << PAGE_SHIFT);
}
}
return rc;
}
SUPR3DECL(int) SUPLowFree(void *pv, size_t cPages)
{
/*
* Validate.
*/
AssertPtr(pv);
if (!pv)
return VINF_SUCCESS;
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
SUPLOWFREE_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
In.pvR3 = pv;
int rc;
if (!g_u32FakeMode)
rc = suplibOsIOCtl(SUP_IOCTL_LOW_FREE, &In, sizeof(In), NULL, 0);
else
rc = SUPPageFree(pv, cPages);
return rc;
}
SUPR3DECL(int) SUPPageAlloc(size_t cPages, void **ppvPages)
{
/*
* Validate.
*/
if (cPages == 0)
{
AssertMsgFailed(("Invalid param cPages=0, must be > 0\n"));
return VERR_INVALID_PARAMETER;
}
AssertPtr(ppvPages);
if (!ppvPages)
return VERR_INVALID_PARAMETER;
*ppvPages = NULL;
#ifdef RT_OS_WINDOWS
SUPALLOCPAGE_IN In;
SUPALLOCPAGE_OUT Out;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
In.cPages = cPages;
Out.u32Cookie = g_u32Cookie;
int rc = suplibOsIOCtl(SUP_IOCTL_PAGE_ALLOC, &In, sizeof(In), &Out, sizeof(Out));
if (VBOX_SUCCESS(rc))
*ppvPages = Out.pvR3;
return rc;
#else
/*
* Call OS specific worker.
*/
return suplibOsPageAlloc(cPages, ppvPages);
#endif
}
SUPR3DECL(int) SUPPageFree(void *pvPages, size_t cPages)
{
/*
* Validate.
*/
AssertPtr(pvPages);
if (!pvPages)
return VINF_SUCCESS;
#ifdef RT_OS_WINDOWS
SUPFREEPAGE_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
In.pvR3 = pvPages;
return suplibOsIOCtl(SUP_IOCTL_PAGE_FREE, &In, sizeof(In), NULL, 0);
#else
/*
* Call OS specific worker.
*/
return suplibOsPageFree(pvPages, cPages);
#endif
}
SUPR3DECL(int) SUPLoadModule(const char *pszFilename, const char *pszModule, void **ppvImageBase)
{
/*
* Load the module.
* If it's VMMR0.r0 we need to install the IDTE.
*/
int rc = supLoadModule(pszFilename, pszModule, ppvImageBase);
#ifndef VBOX_WITHOUT_IDT_PATCHING
if ( VBOX_SUCCESS(rc)
&& !strcmp(pszModule, "VMMR0.r0"))
{
rc = supInstallIDTE();
if (VBOX_FAILURE(rc))
SUPFreeModule(*ppvImageBase);
}
#endif /* VBOX_WITHOUT_IDT_PATCHING */
return rc;
}
#ifndef VBOX_WITHOUT_IDT_PATCHING
/**
* Generates the code for calling the interrupt gate.
*
* @returns VBox status code.
* g_pfnCallVMMR0 is changed on success.
* @param u8Interrupt The interrupt number.
*/
static int suplibGenerateCallVMMR0(uint8_t u8Interrupt)
{
/*
* Allocate memory.
*/
uint8_t *pb = (uint8_t *)RTMemExecAlloc(256);
AssertReturn(pb, VERR_NO_MEMORY);
memset(pb, 0xcc, 256);
Assert(!g_pfnCallVMMR0);
g_pfnCallVMMR0 = *(PFNCALLVMMR0*)&pb;
/*
* Generate the code.
*/
#ifdef RT_ARCH_AMD64
/*
* reg params:
* <GCC> <MSC> <argument>
* rdi rcx pVMR0
* esi edx uOperation
* rdx r8 pvArg
*
* eax eax [g_u32Gookie]
*/
*pb++ = 0xb8; /* mov eax, <g_u32Cookie> */
*(uint32_t *)pb = g_u32Cookie;
pb += sizeof(uint32_t);
*pb++ = 0xcd; /* int <u8Interrupt> */
*pb++ = u8Interrupt;
*pb++ = 0xc3; /* ret */
#else
/*
* x86 stack:
* 0 saved esi
* 0 4 ret
* 4 8 pVM
* 8 c uOperation
* c 10 pvArg
*/
*pb++ = 0x56; /* push esi */
*pb++ = 0x8b; /* mov eax, [pVM] */
*pb++ = 0x44;
*pb++ = 0x24;
*pb++ = 0x08; /* esp+08h */
*pb++ = 0x8b; /* mov edx, [uOperation] */
*pb++ = 0x54;
*pb++ = 0x24;
*pb++ = 0x0c; /* esp+0ch */
*pb++ = 0x8b; /* mov ecx, [pvArg] */
*pb++ = 0x4c;
*pb++ = 0x24;
*pb++ = 0x10; /* esp+10h */
*pb++ = 0xbe; /* mov esi, <g_u32Cookie> */
*(uint32_t *)pb = g_u32Cookie;
pb += sizeof(uint32_t);
*pb++ = 0xcd; /* int <u8Interrupt> */
*pb++ = u8Interrupt;
*pb++ = 0x5e; /* pop esi */
*pb++ = 0xc3; /* ret */
#endif
return VINF_SUCCESS;
}
/**
* Installs the IDTE patch.
*
* @return VBox status code.
*/
static int supInstallIDTE(void)
{
/* already installed? */
if (g_u8Interrupt != 3 || g_u32FakeMode)
return VINF_SUCCESS;
int rc = VINF_SUCCESS;
const unsigned cCpus = RTSystemProcessorGetCount();
if (cCpus <= 1)
{
/* UNI */
SUPIDTINSTALL_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
SUPIDTINSTALL_OUT Out = {3};
rc = suplibOsIOCtl(SUP_IOCTL_IDT_INSTALL, &In, sizeof(In), &Out, sizeof(Out));
if (VBOX_SUCCESS(rc))
{
g_u8Interrupt = Out.u8Idt;
rc = suplibGenerateCallVMMR0(Out.u8Idt);
}
}
else
{
/* SMP */
uint64_t u64AffMaskSaved = RTThreadGetAffinity();
uint64_t u64AffMaskPatched = RTSystemProcessorGetActiveMask() & u64AffMaskSaved;
unsigned cCpusPatched = 0;
for (int i = 0; i < 64; i++)
{
/* Skip absent and inactive processors. */
uint64_t u64Mask = 1ULL << i;
if (!(u64Mask & u64AffMaskPatched))
continue;
/* Change CPU */
int rc2 = RTThreadSetAffinity(u64Mask);
if (VBOX_FAILURE(rc2))
{
u64AffMaskPatched &= ~u64Mask;
Log(("SUPLoadVMM: Failed to set affinity to cpu no. %d, rc=%Vrc.\n", i, rc2));
continue;
}
/* Patch the CPU. */
SUPIDTINSTALL_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
SUPIDTINSTALL_OUT Out = {3};
rc2 = suplibOsIOCtl(SUP_IOCTL_IDT_INSTALL, &In, sizeof(In), &Out, sizeof(Out));
if (VBOX_SUCCESS(rc2))
{
if (!cCpusPatched)
{
g_u8Interrupt = Out.u8Idt;
rc2 = suplibGenerateCallVMMR0(Out.u8Idt);
if (VBOX_FAILURE(rc))
rc2 = rc;
}
else
Assert(g_u8Interrupt == Out.u8Idt);
cCpusPatched++;
}
else
{
Log(("SUPLoadVMM: Failed to patch cpu no. %d, rc=%Vrc.\n", i, rc2));
if (VBOX_SUCCESS(rc))
rc = rc2;
}
}
/* Fail if no CPUs was patched! */
if (VBOX_SUCCESS(rc) && cCpusPatched <= 0)
rc = VERR_GENERAL_FAILURE;
/* Ignore failures if a CPU was patched. */
else if (VBOX_FAILURE(rc) && cCpusPatched > 0)
{
/** @todo add an eventlog/syslog line out this. */
rc = VINF_SUCCESS;
}
/* Set/restore the thread affinity. */
if (VBOX_SUCCESS(rc))
{
rc = RTThreadSetAffinity(u64AffMaskPatched);
AssertRC(rc);
}
else
{
int rc2 = RTThreadSetAffinity(u64AffMaskSaved);
AssertRC(rc2);
}
}
return rc;
}
#endif /* !VBOX_WITHOUT_IDT_PATCHING */
/**
* Resolve an external symbol during RTLdrGetBits().
*
* @returns VBox status code.
* @param hLdrMod The loader module handle.
* @param pszModule Module name.
* @param pszSymbol Symbol name, NULL if uSymbol should be used.
* @param uSymbol Symbol ordinal, ~0 if pszSymbol should be used.
* @param pValue Where to store the symbol value (address).
* @param pvUser User argument.
*/
static DECLCALLBACK(int) supLoadModuleResolveImport(RTLDRMOD hLdrMod, const char *pszModule,
const char *pszSymbol, unsigned uSymbol, RTUINTPTR *pValue, void *pvUser)
{
AssertPtr(pValue);
AssertPtr(pvUser);
/*
* Only SUPR0 and VMMR0.r0
*/
if ( pszModule
&& *pszModule
&& strcmp(pszModule, "SUPR0.dll")
&& strcmp(pszModule, "VMMR0.r0"))
{
AssertMsgFailed(("%s is importing from %s! (expected 'SUPR0.dll' or 'VMMR0.r0', case-sensitiv)\n", pvUser, pszModule));
return VERR_SYMBOL_NOT_FOUND;
}
/*
* No ordinals.
*/
if (pszSymbol < (const char*)0x10000)
{
AssertMsgFailed(("%s is importing by ordinal (ord=%d)\n", pvUser, (int)(uintptr_t)pszSymbol));
return VERR_SYMBOL_NOT_FOUND;
}
/*
* Lookup symbol.
*/
/* skip the 64-bit ELF import prefix first. */
if (!strncmp(pszSymbol, "SUPR0$", sizeof("SUPR0$") - 1))
pszSymbol += sizeof("SUPR0$") - 1;
/*
* Check the VMMR0.r0 module if loaded.
*/
/** @todo call the SUPLoadModule caller.... */
/** @todo proper reference counting and such. */
if (g_pvVMMR0 != NIL_RTR0PTR)
{
void *pvValue;
if (!SUPGetSymbolR0((void *)g_pvVMMR0, pszSymbol, &pvValue))
{
*pValue = (uintptr_t)pvValue;
return VINF_SUCCESS;
}
}
/* iterate the function table. */
int c = g_pFunctions->cFunctions;
PSUPFUNC pFunc = &g_pFunctions->aFunctions[0];
while (c-- > 0)
{
if (!strcmp(pFunc->szName, pszSymbol))
{
*pValue = (uintptr_t)pFunc->pfn;
return VINF_SUCCESS;
}
pFunc++;
}
/*
* The GIP.
*/
/** @todo R0 mapping? */
if ( pszSymbol
&& g_pSUPGlobalInfoPage
&& g_pSUPGlobalInfoPageR0
&& !strcmp(pszSymbol, "g_SUPGlobalInfoPage"))
{
*pValue = (uintptr_t)g_pSUPGlobalInfoPageR0;
return VINF_SUCCESS;
}
/*
* Despair.
*/
c = g_pFunctions->cFunctions;
pFunc = &g_pFunctions->aFunctions[0];
while (c-- > 0)
{
AssertMsg2("%d: %s\n", g_pFunctions->cFunctions - c, pFunc->szName);
pFunc++;
}
AssertMsg2("%s is importing %s which we couldn't find\n", pvUser, pszSymbol);
AssertMsgFailed(("%s is importing %s which we couldn't find\n", pvUser, pszSymbol));
if (g_u32FakeMode)
{
*pValue = 0xdeadbeef;
return VINF_SUCCESS;
}
return VERR_SYMBOL_NOT_FOUND;
}
/** Argument package for supLoadModuleCalcSizeCB. */
typedef struct SUPLDRCALCSIZEARGS
{
size_t cbStrings;
uint32_t cSymbols;
size_t cbImage;
} SUPLDRCALCSIZEARGS, *PSUPLDRCALCSIZEARGS;
/**
* Callback used to calculate the image size.
* @return VINF_SUCCESS
*/
static DECLCALLBACK(int) supLoadModuleCalcSizeCB(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, RTUINTPTR Value, void *pvUser)
{
PSUPLDRCALCSIZEARGS pArgs = (PSUPLDRCALCSIZEARGS)pvUser;
if ( pszSymbol != NULL
&& *pszSymbol
&& Value <= pArgs->cbImage)
{
pArgs->cSymbols++;
pArgs->cbStrings += strlen(pszSymbol) + 1;
}
return VINF_SUCCESS;
}
/** Argument package for supLoadModuleCreateTabsCB. */
typedef struct SUPLDRCREATETABSARGS
{
size_t cbImage;
PSUPLDRSYM pSym;
char *pszBase;
char *psz;
} SUPLDRCREATETABSARGS, *PSUPLDRCREATETABSARGS;
/**
* Callback used to calculate the image size.
* @return VINF_SUCCESS
*/
static DECLCALLBACK(int) supLoadModuleCreateTabsCB(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, RTUINTPTR Value, void *pvUser)
{
PSUPLDRCREATETABSARGS pArgs = (PSUPLDRCREATETABSARGS)pvUser;
if ( pszSymbol != NULL
&& *pszSymbol
&& Value <= pArgs->cbImage)
{
pArgs->pSym->offSymbol = (uint32_t)Value;
pArgs->pSym->offName = pArgs->psz - pArgs->pszBase;
pArgs->pSym++;
size_t cbCopy = strlen(pszSymbol) + 1;
memcpy(pArgs->psz, pszSymbol, cbCopy);
pArgs->psz += cbCopy;
}
return VINF_SUCCESS;
}
/**
* Worker for SUPLoadModule().
*
* @returns VBox status code.
* @param pszFilename Name of the VMMR0 image file
*/
static int supLoadModule(const char *pszFilename, const char *pszModule, void **ppvImageBase)
{
/*
* Validate input.
*/
AssertPtrReturn(pszFilename, VERR_INVALID_PARAMETER);
AssertPtrReturn(pszModule, VERR_INVALID_PARAMETER);
AssertPtrReturn(ppvImageBase, VERR_INVALID_PARAMETER);
AssertReturn(strlen(pszModule) < SIZEOFMEMB(SUPLDROPEN_IN, szName), VERR_FILENAME_TOO_LONG);
const bool fIsVMMR0 = !strcmp(pszModule, "VMMR0.r0");
*ppvImageBase = NULL;
/*
* Open image file and figure its size.
*/
RTLDRMOD hLdrMod;
int rc = RTLdrOpen(pszFilename, &hLdrMod);
if (!VBOX_SUCCESS(rc))
return rc;
SUPLDRCALCSIZEARGS CalcArgs;
CalcArgs.cbStrings = 0;
CalcArgs.cSymbols = 0;
CalcArgs.cbImage = RTLdrSize(hLdrMod);
rc = RTLdrEnumSymbols(hLdrMod, 0, NULL, 0, supLoadModuleCalcSizeCB, &CalcArgs);
if (VBOX_SUCCESS(rc))
{
const uint32_t offSymTab = RT_ALIGN_32(CalcArgs.cbImage, 8);
const uint32_t offStrTab = offSymTab + CalcArgs.cSymbols * sizeof(SUPLDRSYM);
const uint32_t cbImage = RT_ALIGN_32(offStrTab + CalcArgs.cbStrings, 8);
/*
* Open the R0 image.
*/
SUPLDROPEN_IN OpenIn;
OpenIn.u32Cookie = g_u32Cookie;
OpenIn.u32SessionCookie = g_u32SessionCookie;
OpenIn.cbImage = cbImage;
strcpy(OpenIn.szName, pszModule);
SUPLDROPEN_OUT OpenOut;
if (!g_u32FakeMode)
rc = suplibOsIOCtl(SUP_IOCTL_LDR_OPEN, &OpenIn, sizeof(OpenIn), &OpenOut, sizeof(OpenOut));
else
{
OpenOut.fNeedsLoading = true;
OpenOut.pvImageBase = 0xef423420;
}
*ppvImageBase = (void *)OpenOut.pvImageBase;
if ( VBOX_SUCCESS(rc)
&& OpenOut.fNeedsLoading)
{
/*
* We need to load it.
* Allocate memory for the image bits.
*/
unsigned cbIn = RT_OFFSETOF(SUPLDRLOAD_IN, achImage[cbImage]);
PSUPLDRLOAD_IN pIn = (PSUPLDRLOAD_IN)RTMemTmpAlloc(cbIn);
if (pIn)
{
/*
* Get the image bits.
*/
rc = RTLdrGetBits(hLdrMod, &pIn->achImage[0], (uintptr_t)OpenOut.pvImageBase,
supLoadModuleResolveImport, (void *)pszModule);
if (VBOX_SUCCESS(rc))
{
/*
* Get the entry points.
*/
RTUINTPTR VMMR0Entry = 0;
RTUINTPTR ModuleInit = 0;
RTUINTPTR ModuleTerm = 0;
if (fIsVMMR0)
rc = RTLdrGetSymbolEx(hLdrMod, &pIn->achImage[0], (uintptr_t)OpenOut.pvImageBase, "VMMR0Entry", &VMMR0Entry);
if (VBOX_SUCCESS(rc))
{
int rc2 = RTLdrGetSymbolEx(hLdrMod, &pIn->achImage[0], (uintptr_t)OpenOut.pvImageBase, "ModuleInit", &ModuleInit);
if (VBOX_FAILURE(rc2))
ModuleInit = 0;
rc2 = RTLdrGetSymbolEx(hLdrMod, &pIn->achImage[0], (uintptr_t)OpenOut.pvImageBase, "ModuleTerm", &ModuleTerm);
if (VBOX_FAILURE(rc2))
ModuleTerm = 0;
}
if (VBOX_SUCCESS(rc))
{
/*
* Create the symbol and string tables.
*/
SUPLDRCREATETABSARGS CreateArgs;
CreateArgs.cbImage = CalcArgs.cbImage;
CreateArgs.pSym = (PSUPLDRSYM)&pIn->achImage[offSymTab];
CreateArgs.pszBase = (char *)&pIn->achImage[offStrTab];
CreateArgs.psz = CreateArgs.pszBase;
rc = RTLdrEnumSymbols(hLdrMod, 0, NULL, 0, supLoadModuleCreateTabsCB, &CreateArgs);
if (VBOX_SUCCESS(rc))
{
AssertRelease((size_t)(CreateArgs.psz - CreateArgs.pszBase) <= CalcArgs.cbStrings);
AssertRelease((size_t)(CreateArgs.pSym - (PSUPLDRSYM)&pIn->achImage[offSymTab]) <= CalcArgs.cSymbols);
/*
* Upload the image.
*/
pIn->u32Cookie = g_u32Cookie;
pIn->u32SessionCookie = g_u32SessionCookie;
pIn->pfnModuleInit = (RTR0PTR)ModuleInit;
pIn->pfnModuleTerm = (RTR0PTR)ModuleTerm;
if (fIsVMMR0)
{
pIn->eEPType = pIn->EP_VMMR0;
pIn->EP.VMMR0.pvVMMR0 = OpenOut.pvImageBase;
pIn->EP.VMMR0.pvVMMR0Entry = (RTR0PTR)VMMR0Entry;
}
else
pIn->eEPType = pIn->EP_NOTHING;
pIn->offStrTab = offStrTab;
pIn->cbStrTab = (uint32_t)CalcArgs.cbStrings;
AssertRelease(pIn->cbStrTab == CalcArgs.cbStrings);
pIn->offSymbols = offSymTab;
pIn->cSymbols = CalcArgs.cSymbols;
pIn->cbImage = cbImage;
pIn->pvImageBase = OpenOut.pvImageBase;
if (!g_u32FakeMode)
rc = suplibOsIOCtl(SUP_IOCTL_LDR_LOAD, pIn, cbIn, NULL, 0);
else
rc = VINF_SUCCESS;
if ( VBOX_SUCCESS(rc)
|| rc == VERR_ALREADY_LOADED /* this is because of a competing process. */
)
{
if (fIsVMMR0)
g_pvVMMR0 = OpenOut.pvImageBase;
RTMemTmpFree(pIn);
RTLdrClose(hLdrMod);
return VINF_SUCCESS;
}
}
}
}
RTMemTmpFree(pIn);
}
else
{
AssertMsgFailed(("failed to allocated %d bytes for SUPLDRLOAD_IN structure!\n", cbIn));
rc = VERR_NO_TMP_MEMORY;
}
}
else if (VBOX_SUCCESS(rc) && fIsVMMR0)
g_pvVMMR0 = OpenOut.pvImageBase;
}
RTLdrClose(hLdrMod);
return rc;
}
SUPR3DECL(int) SUPFreeModule(void *pvImageBase)
{
/*
* There is one special module. When this is freed we'll
* free the IDT entry that goes with it.
*
* Note that we don't keep count of VMMR0.r0 loads here, so the
* first unload will free it.
*/
if ((RTR0PTR)pvImageBase == g_pvVMMR0)
{
/*
* This is the point where we remove the IDT hook. We do
* that before unloading the R0 VMM part.
*/
if (g_u32FakeMode)
{
#ifndef VBOX_WITHOUT_IDT_PATCHING
g_u8Interrupt = 3;
RTMemExecFree(*(void **)&g_pfnCallVMMR0);
g_pfnCallVMMR0 = NULL;
#endif
g_pvVMMR0 = NIL_RTR0PTR;
return VINF_SUCCESS;
}
#ifndef VBOX_WITHOUT_IDT_PATCHING
/*
* Uninstall IDT entry.
*/
int rc = 0;
if (g_u8Interrupt != 3)
{
SUPIDTREMOVE_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
rc = suplibOsIOCtl(SUP_IOCTL_IDT_REMOVE, &In, sizeof(In), NULL, 0);
g_u8Interrupt = 3;
RTMemExecFree(*(void **)&g_pfnCallVMMR0);
g_pfnCallVMMR0 = NULL;
}
#endif
}
/*
* Free the requested module.
*/
SUPLDRFREE_IN In;
In.u32Cookie = g_u32Cookie;
In.u32SessionCookie = g_u32SessionCookie;
In.pvImageBase = (RTR0PTR)pvImageBase;
int rc = VINF_SUCCESS;
if (!g_u32FakeMode)
rc = suplibOsIOCtl(SUP_IOCTL_LDR_FREE, &In, sizeof(In), NULL, 0);
if ( VBOX_SUCCESS(rc)
&& (RTR0PTR)pvImageBase == g_pvVMMR0)
g_pvVMMR0 = NIL_RTR0PTR;
return rc;
}
SUPR3DECL(int) SUPGetSymbolR0(void *pvImageBase, const char *pszSymbol, void **ppvValue)
{
*ppvValue = NULL;
/*
* Do ioctl.
*/
size_t cchSymbol = strlen(pszSymbol);
const size_t cbIn = RT_OFFSETOF(SUPLDRGETSYMBOL_IN, szSymbol[cchSymbol + 1]);
SUPLDRGETSYMBOL_OUT Out = { NIL_RTR0PTR };
PSUPLDRGETSYMBOL_IN pIn = (PSUPLDRGETSYMBOL_IN)alloca(cbIn);
pIn->u32Cookie = g_u32Cookie;
pIn->u32SessionCookie = g_u32SessionCookie;
pIn->pvImageBase = (RTR0PTR)pvImageBase;
memcpy(pIn->szSymbol, pszSymbol, cchSymbol + 1);
int rc;
if (RT_LIKELY(!g_u32FakeMode))
rc = suplibOsIOCtl(SUP_IOCTL_LDR_GET_SYMBOL, pIn, cbIn, &Out, sizeof(Out));
else
{
rc = VINF_SUCCESS;
Out.pvSymbol = 0xdeadf00d;
}
if (VBOX_SUCCESS(rc))
*ppvValue = (void *)Out.pvSymbol;
return rc;
}
SUPR3DECL(int) SUPLoadVMM(const char *pszFilename)
{
void *pvImageBase;
return SUPLoadModule(pszFilename, "VMMR0.r0", &pvImageBase);
}
SUPR3DECL(int) SUPUnloadVMM(void)
{
return SUPFreeModule((void*)g_pvVMMR0);
}
SUPR3DECL(int) SUPGipGetPhys(PRTHCPHYS pHCPhys)
{
if (g_pSUPGlobalInfoPage)
{
*pHCPhys = g_HCPhysSUPGlobalInfoPage;
return VINF_SUCCESS;
}
*pHCPhys = NIL_RTHCPHYS;
return VERR_WRONG_ORDER;
}