SUPLib.cpp revision c307f0d7384bfc4d19d2290a28be89868f02f42a
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * VirtualBox Support Library - Common code.
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * available from http://www.virtualbox.org. This file is free software;
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * you can redistribute it and/or modify it under the terms of the GNU
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * General Public License (GPL) as published by the Free Software
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * The contents of this file may alternatively be used under the terms
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * of the Common Development and Distribution License Version 1.0
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * VirtualBox OSE distribution, in which case the provisions of the
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * CDDL are applicable instead of those of the GPL.
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * You may elect to license modified versions of this file under the
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * terms and conditions of either the GPL or the CDDL or both.
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * additional information or have any questions.
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/** @page pg_sup SUP - The Support Library
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * The support library is responsible for providing facilities to load
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * VMM Host Ring-0 code, to call Host VMM Ring-0 code from Ring-3 Host
f32de6c198a491c28ace2b4c53f8d04a79fd6d69vboxsync * code, to pin down physical memory, and more.
0ce6ae9d6efed5d54222a13bbdabce9e688e4447vboxsync * The VMM Host Ring-0 code can be combined in the support driver if
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * permitted by kernel module license policies. If it is not combined
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * it will be externalized in a .r0 module that will be loaded using
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * the IPRT loader.
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * The Ring-0 calling is done thru a generic SUP interface which will
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * tranfer an argument set and call a predefined entry point in the Host
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * VMM Ring-0 code.
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * See @ref grp_sup "SUP - Support APIs" for API details.
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/*******************************************************************************
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync* Header Files *
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync*******************************************************************************/
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/*******************************************************************************
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync* Defined Constants And Macros *
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync*******************************************************************************/
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/** R0 VMM module name. */
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/*******************************************************************************
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync* Structures and Typedefs *
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync*******************************************************************************/
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsynctypedef DECLCALLBACK(int) FNCALLVMMR0(PVMR0 pVMR0, unsigned uOperation, void *pvArg);
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/*******************************************************************************
57b49c1557a310ee615bc0ee79dd2a2e92319a1bvboxsync* Global Variables *
57b49c1557a310ee615bc0ee79dd2a2e92319a1bvboxsync*******************************************************************************/
57b49c1557a310ee615bc0ee79dd2a2e92319a1bvboxsync/** Init counter. */
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/** Whether we've been preinitied. */
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsyncstatic bool g_fPreInited = false;
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/** The SUPLib instance data.
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * Well, at least parts of it, specificly the parts that are being handed over
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * via the pre-init mechanism from the hardened executable stub. */
fe06619ae576367ff3568e6abd99fb8ad28cc73avboxsync/** Pointer to the Global Information Page.
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * This pointer is valid as long as SUPLib has a open session. Anyone using
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * the page must treat this pointer as higly volatile and not trust it beyond
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * one transaction.
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync * @todo This will probably deserve it's own session or some other good solution...
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsyncDECLEXPORT(PSUPGLOBALINFOPAGE) g_pSUPGlobalInfoPage;
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/** Address of the ring-0 mapping of the GIP. */
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/** The physical address of the GIP. */
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsyncstatic RTHCPHYS g_HCPhysSUPGlobalInfoPage = NIL_RTHCPHYS;
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/** The negotiated cookie. */
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/** The negotiated session cookie. */
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/** Session handle. */
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/** R0 SUP Functions used for resolving referenced to the SUPR0 module. */
fe06619ae576367ff3568e6abd99fb8ad28cc73avboxsync/** VMMR0 Load Address. */
fe06619ae576367ff3568e6abd99fb8ad28cc73avboxsync/** PAGE_ALLOC_EX sans kernel mapping support indicator. */
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsyncstatic bool g_fSupportsPageAllocNoKernel = true;
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/** Fake mode indicator. (~0 at first, 0 or 1 after first test) */
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/*******************************************************************************
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync* Internal Functions *
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync*******************************************************************************/
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsyncstatic int supLoadModule(const char *pszFilename, const char *pszModule, const char *pszSrvReqHandler, void **ppvImageBase);
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsyncstatic DECLCALLBACK(int) supLoadModuleResolveImport(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol, RTUINTPTR *pValue, void *pvUser);
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync/** Touch a range of pages. */
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsyncDECLINLINE(void) supR3TouchPages(void *pv, size_t cPages)
7189dad3705d42e9874af1e1c8a8c7e5cceee9f7vboxsync while (cPages-- > 0)
return VERR_INVALID_POINTER;
return VERR_WRONG_ORDER;
return VERR_INVALID_MAGIC;
return VERR_INVALID_HANDLE;
return VERR_INVALID_PARAMETER;
return rc;
/** @todo This may need some small restructuring later, it doesn't quite work with a root service flag... */
g_fPreInited = true;
return VINF_SUCCESS;
if (ppSession)
if (g_cInits++ > 0)
return VINF_SUCCESS;
if (g_u32FakeMode == ~0U)
PSUPQUERYFUNCS pFuncsReq = (PSUPQUERYFUNCS)RTMemAllocZ(SUP_IOCTL_QUERY_FUNCS_SIZE(CookieReq.u.Out.cFunctions));
if (pFuncsReq)
rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_QUERY_FUNCS(CookieReq.u.Out.cFunctions), pFuncsReq, SUP_IOCTL_QUERY_FUNCS_SIZE(CookieReq.u.Out.cFunctions));
ASMAtomicCmpXchgPtr((void * volatile *)&g_pSUPGlobalInfoPageR0, (void *)GipMapReq.u.Out.pGipR0, NULL);
if (ppSession)
return VINF_SUCCESS;
LogRel(("Support driver version mismatch: SessionVersion=%#x DriverVersion=%#x ClientVersion=%#x MinVersion=%#x\n",
CookieReq.u.Out.u32SessionVersion, CookieReq.u.Out.u32DriverVersion, SUPDRV_IOC_VERSION, uMinVersion));
LogRel(("Support driver version mismatch: DriverVersion=too-old ClientVersion=%#x\n", SUPDRV_IOC_VERSION));
g_cInits--;
return rc;
g_pFunctions = (PSUPQUERYFUNCS)RTMemAllocZ(SUP_IOCTL_QUERY_FUNCS_SIZE(RT_ELEMENTS(s_aFakeFunctions)));
if (g_pFunctions)
if (ppSession)
if (g_pSUPGlobalInfoPage)
return VINF_SUCCESS;
return VERR_NO_MEMORY;
if (g_cInits == 0)
return VERR_WRONG_ORDER;
if (g_pSUPGlobalInfoPage)
if (rc)
return rc;
g_u32Cookie = 0;
g_u32SessionCookie = 0;
g_cInits = 0;
g_cInits--;
#ifdef RT_ARCH_AMD64
return SUPPAGINGMODE_AMD64_GLOBAL_NX;
return SUPPAGINGMODE_32_BIT_GLOBAL;
int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_GET_PAGING_MODE, &Req, SUP_IOCTL_GET_PAGING_MODE_SIZE);
static int supCallVMMR0ExFake(PVMR0 pVMR0, unsigned uOperation, uint64_t u64Arg, PSUPVMMR0REQHDR pReqHdr)
return VERR_NOT_SUPPORTED;
return VERR_INTERNAL_ERROR;
SUPR3DECL(int) SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned uOperation, uint64_t u64Arg, PSUPVMMR0REQHDR pReqHdr)
int rc;
if (!pReqHdr)
rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_VMMR0(cbReq), pReq, SUP_IOCTL_CALL_VMMR0_SIZE(cbReq));
return rc;
return VINF_SUCCESS;
int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_SET_VM_FOR_FAST, &Req, SUP_IOCTL_SET_VM_FOR_FAST_SIZE);
return rc;
SUPR3DECL(int) SUPR3CallR0Service(const char *pszService, size_t cchService, uint32_t uOperation, uint64_t u64Arg, PSUPR0SERVICEREQHDR pReqHdr)
return VERR_NOT_SUPPORTED;
int rc;
if (!pReqHdr)
rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_SERVICE(cbReq), pReq, SUP_IOCTL_CALL_SERVICE_SIZE(cbReq));
return rc;
static int supR3LoggerSettings(SUPLOGGER enmWhich, uint32_t fWhat, const char *pszFlags, const char *pszGroups, const char *pszDest)
switch (enmWhich)
return VERR_INVALID_PARAMETER;
if (cchFlags)
if (cchGroups)
if (cchDest)
if (!off)
off++;
int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LOGGER_SETTINGS(cbStrTab), pReq, SUP_IOCTL_LOGGER_SETTINGS_SIZE(cbStrTab));
return rc;
SUPR3DECL(int) SUPR3LoggerSettings(SUPLOGGER enmWhich, const char *pszFlags, const char *pszGroups, const char *pszDest)
return supR3LoggerSettings(enmWhich, SUPLOGGERSETTINGS_WHAT_SETTINGS, pszFlags, pszGroups, pszDest);
SUPR3DECL(int) SUPR3LoggerCreate(SUPLOGGER enmWhich, const char *pszFlags, const char *pszGroups, const char *pszDest)
AssertMsg(RT_ALIGN_P(pvStart, PAGE_SIZE) == pvStart, ("pvStart (%p) must be page aligned\n", pvStart));
while (iPage-- > 0)
return VINF_SUCCESS;
int rc;
return rc;
AssertMsg(RT_ALIGN_P(pvStart, PAGE_SIZE) == pvStart, ("pvStart (%p) must be page aligned\n", pvStart));
return VINF_SUCCESS;
return rc;
if (!paPages)
return rc;
SUPR3DECL(int) SUPR3PageAllocEx(size_t cPages, uint32_t fFlags, void **ppvPages, PRTR0PTR pR0Ptr, PSUPPAGE paPages)
if (pR0Ptr)
AssertMsgReturn(cPages > 0 && cPages <= VBOX_MAX_ALLOC_PAGE_COUNT, ("cPages=%zu\n", cPages), VERR_PAGE_COUNT_OUT_OF_RANGE);
if (!pv)
return VERR_NO_MEMORY;
if (pR0Ptr)
if (paPages)
return VINF_SUCCESS;
if ( !pR0Ptr
int rc;
if (pReq)
rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_ALLOC_EX, pReq, SUP_IOCTL_PAGE_ALLOC_EX_SIZE(cPages));
if (pR0Ptr)
if (paPages)
&& !pR0Ptr)
g_fSupportsPageAllocNoKernel = false;
return rc;
SUPR3DECL(int) SUPR3PageMapKernel(void *pvR3, uint32_t off, uint32_t cb, uint32_t fFlags, PRTR0PTR pR0Ptr)
return VERR_NOT_SUPPORTED;
int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_MAP_KERNEL, &Req, SUP_IOCTL_PAGE_MAP_KERNEL_SIZE);
return rc;
SUPR3DECL(int) SUPR3PageProtect(void *pvR3, RTR0PTR R0Ptr, uint32_t off, uint32_t cb, uint32_t fProt)
AssertReturn(!(fProt & ~(RTMEM_PROT_NONE | RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC)), VERR_INVALID_PARAMETER);
#if !defined(RT_OS_SOLARIS)
return rc;
return VINF_SUCCESS;
return rc;
if (pR0Ptr)
if (pR0Ptr)
if (pHCPhys)
return pv;
if (pR0Ptr)
return NULL;
if (!pv)
return VINF_SUCCESS;
return VINF_SUCCESS;
return rc;
AssertMsgReturn(cPages > 0 && cPages < 256, ("cPages=%d must be > 0 and < 256\n", cPages), VERR_PAGE_COUNT_OUT_OF_RANGE);
if (!*ppvPages)
return VERR_NO_LOW_MEMORY;
while (iPage-- > 0)
return VINF_SUCCESS;
int rc;
if (pReq)
if (ppvPagesR0)
if (paPages)
return rc;
if (!pv)
return VINF_SUCCESS;
return VINF_SUCCESS;
return rc;
AssertReturn(!phFile, VERR_NOT_IMPLEMENTED); /** @todo Implement this. The deal is that we make sure the
#ifdef VBOX_WITH_HARDENING
LogRel(("SUPR3HardenedVerifyFile: %s: Verification of \"%s\" failed, rc=%Rrc\n", pszMsg, pszFilename, rc));
return rc;
return VINF_SUCCESS;
#ifdef VBOX_WITH_HARDENING
return rc;
#ifdef VBOX_WITH_HARDENING
return rc;
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;
return VERR_SYMBOL_NOT_FOUND;
void *pvValue;
return VINF_SUCCESS;
return VINF_SUCCESS;
pFunc++;
if ( pszSymbol
return VINF_SUCCESS;
pFunc++;
if (g_u32FakeMode)
return VINF_SUCCESS;
return VERR_SYMBOL_NOT_FOUND;
typedef struct SUPLDRCALCSIZEARGS
static DECLCALLBACK(int) supLoadModuleCalcSizeCB(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, RTUINTPTR Value, void *pvUser)
&& *pszSymbol
return VINF_SUCCESS;
typedef struct SUPLDRCREATETABSARGS
char *pszBase;
char *psz;
static DECLCALLBACK(int) supLoadModuleCreateTabsCB(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, RTUINTPTR Value, void *pvUser)
&& *pszSymbol
return VINF_SUCCESS;
static int supLoadModule(const char *pszFilename, const char *pszModule, const char *pszSrvReqHandler, void **ppvImageBase)
int rc;
return rc;
return rc;
if (!g_u32FakeMode)
if (pLoadReq)
if (fIsVMMR0)
rc = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.abImage[0], (uintptr_t)OpenReq.u.Out.pvImageBase, "VMMR0EntryInt", &VMMR0EntryInt);
rc = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.abImage[0], (uintptr_t)OpenReq.u.Out.pvImageBase, "VMMR0EntryFast", &VMMR0EntryFast);
rc = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.abImage[0], (uintptr_t)OpenReq.u.Out.pvImageBase, "VMMR0EntryEx", &VMMR0EntryEx);
else if (pszSrvReqHandler)
rc = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.abImage[0], (uintptr_t)OpenReq.u.Out.pvImageBase, pszSrvReqHandler, &SrvReqHandler);
int rc2 = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.abImage[0], (uintptr_t)OpenReq.u.Out.pvImageBase, "ModuleInit", &ModuleInit);
ModuleInit = 0;
rc2 = RTLdrGetSymbolEx(hLdrMod, &pLoadReq->u.In.abImage[0], (uintptr_t)OpenReq.u.Out.pvImageBase, "ModuleTerm", &ModuleTerm);
ModuleTerm = 0;
AssertRelease((size_t)(CreateArgs.pSym - (PSUPLDRSYM)&pLoadReq->u.In.abImage[offSymTab]) <= CalcArgs.cSymbols);
if (fIsVMMR0)
else if (pszSrvReqHandler)
if (!g_u32FakeMode)
rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LDR_LOAD, pLoadReq, SUP_IOCTL_LDR_LOAD_SIZE(cbImageWithTabs));
if (fIsVMMR0)
LogRel(("SUP: VMMR0EntryEx located at %RTptr, VMMR0EntryFast at %RTptr and VMMR0EntryInt at %RTptr\n",
#ifdef RT_OS_WINDOWS
return VINF_SUCCESS;
AssertMsgFailed(("failed to allocated %u bytes for SUPLDRLOAD_IN structure!\n", SUP_IOCTL_LDR_LOAD_SIZE(cbImageWithTabs)));
if (fIsVMMR0)
#ifdef RT_OS_WINDOWS
return rc;
return VINF_SUCCESS;
return rc;
return VINF_SUCCESS;
return VERR_SYMBOL_NOT_FOUND;
int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LDR_GET_SYMBOL, &Req, SUP_IOCTL_LDR_GET_SYMBOL_SIZE);
return rc;
void *pvImageBase;
if (g_pSUPGlobalInfoPage)
return VINF_SUCCESS;
return VERR_WRONG_ORDER;
#ifdef VBOX_WITH_HARDENING
return rc;
LogFlow(("SUPR3HardenedLdrLoadAppPriv: pszFilename=%p:{%s} phLdrMod=%p\n", pszFilename, pszFilename, phLdrMod));
AssertMsgReturn(cchFilename < (RTPATH_MAX / 4) * 3, ("%zu\n", cchFilename), VERR_INVALID_PARAMETER);
return VERR_FILE_NOT_FOUND;
return rc;
#ifdef RT_OS_LINUX
return suplibOsQueryVTxSupported();
return VINF_SUCCESS;
*pfCaps = 0;
return VINF_SUCCESS;
return rc;