SUPLib.cpp revision e55ac7a60ed1aa72fb39f3c2914ea7c37e0fde0f
/* $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;
* 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.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
/** @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
#ifdef VBOX_WITHOUT_IDT_PATCHING
#endif
#include "SUPLibInternal.h"
#include "SUPDRVIOC.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** R0 VMM module name. */
#define VMMR0_NAME "VMMR0"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
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...
*/
/** Address of the ring-0 mapping of the GIP. */
/** The physical address of the GIP. */
/** The negotiated cookie. */
uint32_t g_u32Cookie = 0;
/** The negotiated session cookie. */
/** Session handle. */
/** 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. */
/** Pointer to the generated code fore calling VMMR0. */
static PFNCALLVMMR0 g_pfnCallVMMR0;
#endif
/** VMMR0 Load Address. */
/** 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 *
*******************************************************************************/
#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();
}
{
/*
* Perform some sanity checks.
* (Got some trouble with compile time member alignment assertions.)
*/
/*
* 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)
{
else
ASMAtomicCmpXchgU32(&g_u32FakeMode, 0, ~0U);
}
if (RT_UNLIKELY(g_u32FakeMode))
return supInitFake(ppSession);
/**
* Open the support driver.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Negotiate the cookie.
*/
if (VBOX_SUCCESS(rc))
{
{
/*
* Query the functions.
*/
if (pFuncsOut)
{
if (VBOX_SUCCESS(rc))
{
if (ppSession)
/*
* Map the GIP into userspace.
* This is an optional feature, so we will ignore any failures here.
*/
if (!g_pSUPGlobalInfoPage)
{
SUPGIPMAP_IN GipIn = {0};
if (VBOX_SUCCESS(rc))
{
}
else
rc = VINF_SUCCESS;
}
return rc;
}
}
else
rc = VERR_NO_MEMORY;
}
else
{
LogRel(("Support driver version mismatch: SessionVersion=%#x DriverVersion=%#x ClientVersion=%#x\n",
}
}
else
{
if (rc == VERR_VM_DRIVER_VERSION_MISMATCH)
LogRel(("Support driver version mismatch: DriverVersion=%#x ClientVersion=%#x\n",
else
}
suplibOsTerm();
}
g_cInits--;
return rc;
}
/**
* Fake mode init.
*/
{
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)
{
if (ppSession)
*ppSession = g_pSession;
#ifndef VBOX_WITHOUT_IDT_PATCHING
#endif
/* fake the GIP. */
if (g_pSUPGlobalInfoPage)
{
/* the page is supposed to be invalid, so don't set the magic. */
return VINF_SUCCESS;
}
g_pFunctions = NULL;
}
return VERR_NO_MEMORY;
}
{
/*
* Verify state.
*/
if (g_cInits == 0)
return VERR_WRONG_ORDER;
{
/*
* NULL the GIP pointer.
*/
if (g_pSUPGlobalInfoPage)
{
/* 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;
}
{
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
int rc;
if (!g_u32FakeMode)
{
if (VBOX_FAILURE(rc))
}
else
}
{
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
if (VBOX_SUCCESS(rc))
return rc;
}
{
#ifndef VBOX_WITHOUT_IDT_PATCHING
#else
{
}
{
}
if (uOperation == VMMR0_DO_NOP)
{
return suplibOSIOCtlFast(SUP_IOCTL_FAST_DO_NOP);
}
#endif
}
{
int rc;
if (RT_LIKELY(!g_u32FakeMode))
else
rc = VINF_SUCCESS;
return rc;
}
{
/*
* Validate.
*/
AssertMsg(RT_ALIGN_P(pvStart, PAGE_SIZE) == pvStart, ("pvStart (%p) must be page aligned\n", pvStart));
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
int rc;
if (!g_u32FakeMode)
{
#if 0
if (!pOut)
return VERR_NO_TMP_MEMORY;
if (RT_SUCCESS(rc))
#else
/* a hack to save some time. */
rc = suplibOsIOCtl(SUP_IOCTL_PINPAGES, &In, sizeof(In), pOut, RT_OFFSETOF(SUPPINPAGES_OUT, aPages[cPages]));
#endif
}
else
{
/* fake a successfull result. */
while (iPage-- > 0)
rc = VINF_SUCCESS;
}
return rc;
}
{
/*
* Validate.
*/
AssertMsg(RT_ALIGN_P(pvStart, PAGE_SIZE) == pvStart, ("pvStart (%p) must be page aligned\n", pvStart));
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
int rc;
if (!g_u32FakeMode)
else
rc = VINF_SUCCESS;
return rc;
}
{
}
{
/*
* Validate.
*/
*pHCPhys = NIL_RTHCPHYS;
if (pR0Ptr)
*pR0Ptr = NIL_RTR0PTR;
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
int rc;
if (!g_u32FakeMode)
else
{
}
if (VBOX_SUCCESS(rc))
{
if (pR0Ptr)
}
return NULL;
}
{
/*
* Validate.
*/
if (!pv)
return VINF_SUCCESS;
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
int rc;
if (!g_u32FakeMode)
else
return rc;
}
{
/*
* Validate.
*/
int rc;
if (!g_u32FakeMode)
{
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
if (pOut)
{
if (VBOX_SUCCESS(rc))
{
if (ppvPagesR0)
#ifdef VBOX_STRICT
for (unsigned i = 0; i < cPages; i++)
#endif
}
}
else
}
else
{
if (VBOX_SUCCESS(rc))
{
/* fake physical addresses. */
while (iPage-- > 0)
}
}
return rc;
}
{
/*
* Validate.
*/
if (!pv)
return VINF_SUCCESS;
/*
* Issue IOCtl to the SUPDRV kernel module.
*/
int rc;
if (!g_u32FakeMode)
else
return rc;
}
{
/*
* Validate.
*/
if (cPages == 0)
{
AssertMsgFailed(("Invalid param cPages=0, must be > 0\n"));
return VERR_INVALID_PARAMETER;
}
if (!ppvPages)
return VERR_INVALID_PARAMETER;
/*
* Call OS specific worker.
*/
}
{
/*
* Validate.
*/
if (!pvPages)
return VINF_SUCCESS;
/*
* Call OS specific worker.
*/
}
{
/*
* Load the module.
* If it's VMMR0.r0 we need to install the IDTE.
*/
#ifndef VBOX_WITHOUT_IDT_PATCHING
if ( VBOX_SUCCESS(rc)
{
rc = supInstallIDTE();
if (VBOX_FAILURE(rc))
}
#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.
*/
{
/*
* Allocate memory.
*/
/*
* Generate the code.
*/
#ifdef __AMD64__
/*
* reg params:
* <GCC> <MSC> <argument>
* rdi rcx pVMR0
* esi edx uOperation
* rdx r8 pvArg
*
* eax eax [g_u32Gookie]
*/
*pb++ = u8Interrupt;
#else
/*
* x86 stack:
* 0 saved esi
* 0 4 ret
* 4 8 pVM
* 8 c uOperation
* c 10 pvArg
*/
*pb++ = 0x44;
*pb++ = 0x24;
*pb++ = 0x54;
*pb++ = 0x24;
*pb++ = 0x4c;
*pb++ = 0x24;
*pb++ = u8Interrupt;
#endif
return VINF_SUCCESS;
}
/**
* Installs the IDTE patch.
*
* @return VBox status code.
*/
static int supInstallIDTE(void)
{
/* already installed? */
return VINF_SUCCESS;
int rc = VINF_SUCCESS;
const unsigned cCpus = RTSystemProcessorGetCount();
if (cCpus <= 1)
{
/* UNI */
if (VBOX_SUCCESS(rc))
{
}
}
else
{
/* SMP */
unsigned cCpusPatched = 0;
for (int i = 0; i < 64; i++)
{
/* Skip absent and inactive processors. */
if (!(u64Mask & u64AffMaskPatched))
continue;
/* Change CPU */
if (VBOX_FAILURE(rc2))
{
u64AffMaskPatched &= ~u64Mask;
continue;
}
/* Patch the CPU. */
if (VBOX_SUCCESS(rc2))
{
if (!cCpusPatched)
{
if (VBOX_FAILURE(rc))
}
else
cCpusPatched++;
}
else
{
if (VBOX_SUCCESS(rc))
}
}
/* Fail if no CPUs was patched! */
/* Ignore failures if a CPU was patched. */
{
rc = VINF_SUCCESS;
}
if (VBOX_SUCCESS(rc))
{
}
else
{
}
}
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.
*/
{
/*
* Only SUPR0 and VMMR0.r0
*/
if ( pszModule
&& *pszModule
{
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)
{
return VERR_SYMBOL_NOT_FOUND;
}
/*
* Lookup symbol.
*/
/* skip the 64-bit ELF import prefix first. */
/*
* 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;
{
return VINF_SUCCESS;
}
}
/* iterate the function table. */
int c = g_pFunctions->cFunctions;
while (c-- > 0)
{
{
return VINF_SUCCESS;
}
pFunc++;
}
/*
* The GIP.
*/
/** @todo R0 mapping? */
if ( pszSymbol
{
return VINF_SUCCESS;
}
/*
* Despair.
*/
c = g_pFunctions->cFunctions;
while (c-- > 0)
{
pFunc++;
}
return VERR_SYMBOL_NOT_FOUND;
}
/** Argument package for supLoadModuleCalcSizeCB. */
typedef struct SUPLDRCALCSIZEARGS
{
/**
* 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)
{
&& *pszSymbol
{
}
return VINF_SUCCESS;
}
/** Argument package for supLoadModuleCreateTabsCB. */
typedef struct SUPLDRCREATETABSARGS
{
char *pszBase;
char *psz;
/**
* 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)
{
&& *pszSymbol
{
}
return VINF_SUCCESS;
}
/**
* Worker for SUPLoadModule().
*
* @returns VBox status code.
* @param pszFilename Name of the VMMR0 image file
*/
{
/*
* Validate input.
*/
*ppvImageBase = NULL;
/*
* Open image file and figure its size.
*/
if (!VBOX_SUCCESS(rc))
return rc;
if (VBOX_SUCCESS(rc))
{
/*
* Open the R0 image.
*/
if (!g_u32FakeMode)
else
{
OpenOut.fNeedsLoading = true;
}
if ( VBOX_SUCCESS(rc)
&& OpenOut.fNeedsLoading)
{
/*
* We need to load it.
* Allocate memory for the image bits.
*/
if (pIn)
{
/*
* Get the image bits.
*/
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.
*/
if (VBOX_SUCCESS(rc))
{
AssertRelease((size_t)(CreateArgs.pSym - (PSUPLDRSYM)&pIn->achImage[offSymTab]) <= CalcArgs.cSymbols);
/*
* Upload the image.
*/
if (fIsVMMR0)
{
}
else
if (!g_u32FakeMode)
else
rc = VINF_SUCCESS;
if ( VBOX_SUCCESS(rc)
)
{
if (fIsVMMR0)
return VINF_SUCCESS;
}
}
}
}
}
else
{
}
}
}
return rc;
}
{
/*
* 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.
*/
{
/*
* 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);
#endif
return VINF_SUCCESS;
}
#ifndef VBOX_WITHOUT_IDT_PATCHING
/*
* Uninstall IDT entry.
*/
int rc = 0;
if (g_u8Interrupt != 3)
{
g_u8Interrupt = 3;
RTMemExecFree(*(void **)&g_pfnCallVMMR0);
}
#endif
}
/*
* Free the requested module.
*/
int rc = VINF_SUCCESS;
if (!g_u32FakeMode)
if ( VBOX_SUCCESS(rc)
return rc;
}
{
/*
* Do ioctl.
*/
int rc;
if (RT_LIKELY(!g_u32FakeMode))
else
{
rc = VINF_SUCCESS;
}
if (VBOX_SUCCESS(rc))
return rc;
}
{
void *pvImageBase;
}
SUPR3DECL(int) SUPUnloadVMM(void)
{
return SUPFreeModule((void*)g_pvVMMR0);
}
{
if (g_pSUPGlobalInfoPage)
{
return VINF_SUCCESS;
}
*pHCPhys = NIL_RTHCPHYS;
return VERR_WRONG_ORDER;
}