SUPLib.cpp revision 37eb780874007e6c73f493edcfd7a1e498a6a2ef
/** @file
*
* VBox host drivers - Ring-0 support drivers - Shared code:
* Support library that implements the basic lowlevel OS interfaces
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung 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, and to pin down physical memory.
*
* 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 Win32 PE binary and will use the PDM
* PE loader to load it into memory.
*
* 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();
}
{
/*
* 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.
*/
SUPCOOKIE_OUT Out = {0,0};
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
}
suplibOsTerm();
}
g_cInits--;
return rc;
}
/**
* Fake mode init.
*/
{
Log(("SUP: Fake mode!\n"));
static const SUPFUNC s_aFakeFunctions[] =
{
/* name function */
{ "SUPR0ObjRegister", (void *)0xefef0000 },
{ "SUPR0ObjAddRef", (void *)0xefef0001 },
{ "SUPR0ObjRelease", (void *)0xefef0002 },
{ "SUPR0ObjVerifyAccess", (void *)0xefef0003 },
{ "SUPR0LockMem", (void *)0xefef0004 },
{ "SUPR0UnlockMem", (void *)0xefef0005 },
{ "SUPR0ContAlloc", (void *)0xefef0006 },
{ "SUPR0ContFree", (void *)0xefef0007 },
{ "SUPR0MemAlloc", (void *)0xefef0008 },
{ "SUPR0MemGetPhys", (void *)0xefef0009 },
{ "SUPR0MemFree", (void *)0xefef000a },
{ "SUPR0Printf", (void *)0xefef000b },
{ "RTMemAlloc", (void *)0xefef000c },
{ "RTMemAllocZ", (void *)0xefef000d },
{ "RTMemFree", (void *)0xefef000e },
{ "RTSemFastMutexCreate", (void *)0xefef000f },
{ "RTSemFastMutexDestroy", (void *)0xefef0010 },
{ "RTSemFastMutexRequest", (void *)0xefef0011 },
{ "RTSemFastMutexRelease", (void *)0xefef0012 },
{ "RTSemEventCreate", (void *)0xefef0013 },
{ "RTSemEventSignal", (void *)0xefef0014 },
{ "RTSemEventWait", (void *)0xefef0015 },
{ "RTSemEventDestroy", (void *)0xefef0016 },
{ "RTSpinlockCreate", (void *)0xefef0017 },
{ "RTSpinlockDestroy", (void *)0xefef0018 },
{ "RTSpinlockAcquire", (void *)0xefef0019 },
{ "RTSpinlockRelease", (void *)0xefef001a },
{ "RTSpinlockAcquireNoInts", (void *)0xefef001b },
{ "RTSpinlockReleaseNoInts", (void *)0xefef001c },
{ "RTThreadNativeSelf", (void *)0xefef001d },
{ "RTThreadSleep", (void *)0xefef001e },
{ "RTThreadYield", (void *)0xefef001f },
{ "RTLogDefaultInstance", (void *)0xefef0020 },
{ "RTLogRelDefaultInstance", (void *)0xefef0021 },
{ "RTLogSetDefaultInstanceThread", (void *)0xefef0022 },
{ "RTLogLogger", (void *)0xefef0023 },
{ "RTLogLoggerEx", (void *)0xefef0024 },
{ "RTLogLoggerExV", (void *)0xefef0025 },
{ "AssertMsg1", (void *)0xefef0026 },
{ "AssertMsg2", (void *)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));
AssertMsg(RT_ALIGN_Z(cbMemory, PAGE_SIZE) == cbMemory, ("cbMemory (%#zx) must be page aligned\n", cbMemory));
/*
* 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[cbMemory >> PAGE_SHIFT]));
#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.
*/
AssertMsg(cb > 64 && cb < PAGE_SIZE * 256, ("cb=%d must be > 64 and < %d (256 pages)\n", cb, PAGE_SIZE * 256));
*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.
*/
return suplibOsPageFree(pvPages);
}
{
/*
* 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. */
/* iterate the function table. */
int c = g_pFunctions->cFunctions;
while (c-- > 0)
{
{
return VINF_SUCCESS;
}
pFunc++;
}
/*
* Check the VMMR0.r0 module if loaded.
*/
/** @todo call the SUPLoadModule caller.... */
/** @todo proper reference counting and such. */
if (g_pvVMMR0)
{
void *pvValue;
{
return VINF_SUCCESS;
}
}
/*
* 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);
/*
* Get the entry points.
*/
RTUINTPTR VMMR0Entry = 0;
RTUINTPTR ModuleInit = 0;
RTUINTPTR ModuleTerm = 0;
rc = RTLdrGetSymbolEx(hLdrMod, &pIn->achImage[0], (uintptr_t)OpenOut.pvImageBase, "VMMR0Entry", &VMMR0Entry);
if (VBOX_SUCCESS(rc))
{
rc = RTLdrGetSymbolEx(hLdrMod, &pIn->achImage[0], (uintptr_t)OpenOut.pvImageBase, "ModuleInit", &ModuleInit);
if (VBOX_FAILURE(rc))
ModuleInit = 0;
rc = RTLdrGetSymbolEx(hLdrMod, &pIn->achImage[0], (uintptr_t)OpenOut.pvImageBase, "ModuleTerm", &ModuleTerm);
if (VBOX_FAILURE(rc))
ModuleTerm = 0;
}
/*
* 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.
*/
if (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);
#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)
&& pvImageBase == g_pvVMMR0)
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(g_pvVMMR0);
}
{
if (g_pSUPGlobalInfoPage)
{
return VINF_SUCCESS;
}
*pHCPhys = NIL_RTHCPHYS;
return VERR_WRONG_ORDER;
}