diff options
Diffstat (limited to 'src/VBox/HostDrivers/Support/SUPLib.cpp')
-rw-r--r-- | src/VBox/HostDrivers/Support/SUPLib.cpp | 2364 |
1 files changed, 2364 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/Support/SUPLib.cpp b/src/VBox/HostDrivers/Support/SUPLib.cpp new file mode 100644 index 00000000..cee26f2a --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPLib.cpp @@ -0,0 +1,2364 @@ +/* $Id: SUPLib.cpp $ */ +/** @file + * VirtualBox Support Library - Common code. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * 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 (GPL) 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +/** @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 + * transfer 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> +#include <VBox/log.h> +#include <VBox/VBoxTpG.h> + +#include <iprt/assert.h> +#include <iprt/alloc.h> +#include <iprt/alloca.h> +#include <iprt/ldr.h> +#include <iprt/asm.h> +#include <iprt/mp.h> +#include <iprt/cpuset.h> +#include <iprt/thread.h> +#include <iprt/process.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/env.h> +#include <iprt/rand.h> +#include <iprt/x86.h> + +#include "SUPDrvIOC.h" +#include "SUPLibInternal.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 * +*********************************************************************************************************************************/ +/** Init counter. */ +static uint32_t g_cInits = 0; +/** Whether we've been preinitied. */ +static bool g_fPreInited = false; +/** The SUPLib instance data. + * Well, at least parts of it, specifically the parts that are being handed over + * via the pre-init mechanism from the hardened executable stub. */ +SUPLIBDATA g_supLibData = +{ + /*.hDevice = */ SUP_HDEVICE_NIL, + /*.fUnrestricted = */ true +#if defined(RT_OS_DARWIN) + ,/* .uConnection = */ 0 +#elif defined(RT_OS_LINUX) + ,/* .fSysMadviseWorks = */ false +#endif +}; + +/** 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 highly 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. */ +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. */ +PSUPQUERYFUNCS g_pSupFunctions; + +/** PAGE_ALLOC_EX sans kernel mapping support indicator. */ +static bool g_fSupportsPageAllocNoKernel = true; +/** Fake mode indicator. (~0 at first, 0 or 1 after first test) */ +uint32_t g_uSupFakeMode = UINT32_MAX; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int supInitFake(PSUPDRVSESSION *ppSession); + + +/** Touch a range of pages. */ +DECLINLINE(void) supR3TouchPages(void *pv, size_t cPages) +{ + uint32_t volatile *pu32 = (uint32_t volatile *)pv; + while (cPages-- > 0) + { + ASMAtomicCmpXchgU32(pu32, 0, 0); + pu32 += PAGE_SIZE / sizeof(uint32_t); + } +} + + +SUPR3DECL(int) SUPR3Install(void) +{ + return suplibOsInstall(); +} + + +SUPR3DECL(int) SUPR3Uninstall(void) +{ + return suplibOsUninstall(); +} + + +DECLEXPORT(int) supR3PreInit(PSUPPREINITDATA pPreInitData, uint32_t fFlags) +{ + /* + * The caller is kind of trustworthy, just perform some basic checks. + * + * Note! Do not do any fancy stuff here because IPRT has NOT been + * initialized at this point. + */ + if (!VALID_PTR(pPreInitData)) + return VERR_INVALID_POINTER; + if (g_fPreInited || g_cInits > 0) + return VERR_WRONG_ORDER; + + if ( pPreInitData->u32Magic != SUPPREINITDATA_MAGIC + || pPreInitData->u32EndMagic != SUPPREINITDATA_MAGIC) + return VERR_INVALID_MAGIC; + if ( !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV) + && pPreInitData->Data.hDevice == SUP_HDEVICE_NIL) + return VERR_INVALID_HANDLE; + if ( (fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV) + && pPreInitData->Data.hDevice != SUP_HDEVICE_NIL) + return VERR_INVALID_PARAMETER; + + /* + * Hand out the data. + */ + int rc = supR3HardenedRecvPreInitData(pPreInitData); + if (RT_FAILURE(rc)) + return rc; + + /** @todo This may need some small restructuring later, it doesn't quite work with a root service flag... */ + if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)) + { + g_supLibData = pPreInitData->Data; + g_fPreInited = true; + } + + return VINF_SUCCESS; +} + + +SUPR3DECL(int) SUPR3InitEx(bool fUnrestricted, PSUPDRVSESSION *ppSession) +{ + /* + * Perform some sanity checks. + * (Got some trouble with compile time member alignment assertions.) + */ + Assert(!(RT_UOFFSETOF(SUPGLOBALINFOPAGE, u64NanoTSLastUpdateHz) & 0x7)); + Assert(!(RT_UOFFSETOF(SUPGLOBALINFOPAGE, aCPUs) & 0x1f)); + Assert(!(RT_UOFFSETOF(SUPGLOBALINFOPAGE, aCPUs[1]) & 0x1f)); + Assert(!(RT_UOFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64NanoTS) & 0x7)); + Assert(!(RT_UOFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64TSC) & 0x7)); + Assert(!(RT_UOFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64CpuHz) & 0x7)); + + /* + * Check if already initialized. + */ + if (ppSession) + *ppSession = g_pSession; + if (g_cInits++ > 0) + { + if (fUnrestricted && !g_supLibData.fUnrestricted) + { + g_cInits--; + if (ppSession) + *ppSession = NIL_RTR0PTR; + return VERR_VM_DRIVER_NOT_ACCESSIBLE; /** @todo different status code? */ + } + 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_uSupFakeMode == ~0U) + { + const char *psz = RTEnvGet("VBOX_SUPLIB_FAKE"); + if (psz && !strcmp(psz, "fake")) + ASMAtomicCmpXchgU32(&g_uSupFakeMode, 1, ~0U); + else + ASMAtomicCmpXchgU32(&g_uSupFakeMode, 0, ~0U); + } + if (RT_UNLIKELY(g_uSupFakeMode)) + return supInitFake(ppSession); + + /* + * Open the support driver. + */ + SUPINITOP enmWhat = kSupInitOp_Driver; + int rc = suplibOsInit(&g_supLibData, g_fPreInited, fUnrestricted, &enmWhat, NULL); + if (RT_SUCCESS(rc)) + { + /* + * Negotiate the cookie. + */ + SUPCOOKIE CookieReq; + memset(&CookieReq, 0xff, sizeof(CookieReq)); + CookieReq.Hdr.u32Cookie = SUPCOOKIE_INITIAL_COOKIE; + CookieReq.Hdr.u32SessionCookie = RTRandU32(); + CookieReq.Hdr.cbIn = SUP_IOCTL_COOKIE_SIZE_IN; + CookieReq.Hdr.cbOut = SUP_IOCTL_COOKIE_SIZE_OUT; + CookieReq.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + CookieReq.Hdr.rc = VERR_INTERNAL_ERROR; + strcpy(CookieReq.u.In.szMagic, SUPCOOKIE_MAGIC); + CookieReq.u.In.u32ReqVersion = SUPDRV_IOC_VERSION; + const uint32_t uMinVersion = (SUPDRV_IOC_VERSION & 0xffff0000) == 0x00290000 + ? 0x00290008 + : SUPDRV_IOC_VERSION & 0xffff0000; + CookieReq.u.In.u32MinVersion = uMinVersion; + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_COOKIE, &CookieReq, SUP_IOCTL_COOKIE_SIZE); + if ( RT_SUCCESS(rc) + && RT_SUCCESS(CookieReq.Hdr.rc)) + { + if ( (CookieReq.u.Out.u32SessionVersion & 0xffff0000) == (SUPDRV_IOC_VERSION & 0xffff0000) + && CookieReq.u.Out.u32SessionVersion >= uMinVersion) + { + /* + * Query the functions. + */ + PSUPQUERYFUNCS pFuncsReq = NULL; + if (g_supLibData.fUnrestricted) + { + pFuncsReq = (PSUPQUERYFUNCS)RTMemAllocZ(SUP_IOCTL_QUERY_FUNCS_SIZE(CookieReq.u.Out.cFunctions)); + if (pFuncsReq) + { + pFuncsReq->Hdr.u32Cookie = CookieReq.u.Out.u32Cookie; + pFuncsReq->Hdr.u32SessionCookie = CookieReq.u.Out.u32SessionCookie; + pFuncsReq->Hdr.cbIn = SUP_IOCTL_QUERY_FUNCS_SIZE_IN; + pFuncsReq->Hdr.cbOut = SUP_IOCTL_QUERY_FUNCS_SIZE_OUT(CookieReq.u.Out.cFunctions); + pFuncsReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + pFuncsReq->Hdr.rc = VERR_INTERNAL_ERROR; + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_QUERY_FUNCS(CookieReq.u.Out.cFunctions), pFuncsReq, + SUP_IOCTL_QUERY_FUNCS_SIZE(CookieReq.u.Out.cFunctions)); + if (RT_SUCCESS(rc)) + rc = pFuncsReq->Hdr.rc; + if (RT_SUCCESS(rc)) + { + /* + * Map the GIP into userspace. + */ + Assert(!g_pSUPGlobalInfoPage); + SUPGIPMAP GipMapReq; + GipMapReq.Hdr.u32Cookie = CookieReq.u.Out.u32Cookie; + GipMapReq.Hdr.u32SessionCookie = CookieReq.u.Out.u32SessionCookie; + GipMapReq.Hdr.cbIn = SUP_IOCTL_GIP_MAP_SIZE_IN; + GipMapReq.Hdr.cbOut = SUP_IOCTL_GIP_MAP_SIZE_OUT; + GipMapReq.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + GipMapReq.Hdr.rc = VERR_INTERNAL_ERROR; + GipMapReq.u.Out.HCPhysGip = NIL_RTHCPHYS; + GipMapReq.u.Out.pGipR0 = NIL_RTR0PTR; + GipMapReq.u.Out.pGipR3 = NULL; + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_GIP_MAP, &GipMapReq, SUP_IOCTL_GIP_MAP_SIZE); + if (RT_SUCCESS(rc)) + rc = GipMapReq.Hdr.rc; + if (RT_SUCCESS(rc)) + { + /* + * Set the GIP globals. + */ + AssertRelease(GipMapReq.u.Out.pGipR3->u32Magic == SUPGLOBALINFOPAGE_MAGIC); + AssertRelease(GipMapReq.u.Out.pGipR3->u32Version >= SUPGLOBALINFOPAGE_VERSION); + + ASMAtomicXchgSize(&g_HCPhysSUPGlobalInfoPage, GipMapReq.u.Out.HCPhysGip); + ASMAtomicCmpXchgPtr((void * volatile *)&g_pSUPGlobalInfoPage, GipMapReq.u.Out.pGipR3, NULL); + ASMAtomicCmpXchgPtr((void * volatile *)&g_pSUPGlobalInfoPageR0, (void *)GipMapReq.u.Out.pGipR0, NULL); + } + } + } + else + rc = VERR_NO_MEMORY; + } + + if (RT_SUCCESS(rc)) + { + /* + * Set the globals and return success. + */ + g_u32Cookie = CookieReq.u.Out.u32Cookie; + g_u32SessionCookie = CookieReq.u.Out.u32SessionCookie; + g_pSession = CookieReq.u.Out.pSession; + g_pSupFunctions = pFuncsReq; + if (ppSession) + *ppSession = CookieReq.u.Out.pSession; + return VINF_SUCCESS; + } + + /* bailout */ + RTMemFree(pFuncsReq); + } + else + { + 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)); + rc = VERR_VM_DRIVER_VERSION_MISMATCH; + } + } + else + { + if (RT_SUCCESS(rc)) + { + rc = CookieReq.Hdr.rc; + LogRel(("Support driver version mismatch: DriverVersion=%#x ClientVersion=%#x rc=%Rrc\n", + CookieReq.u.Out.u32DriverVersion, SUPDRV_IOC_VERSION, rc)); + if (rc != VERR_VM_DRIVER_VERSION_MISMATCH) + rc = VERR_VM_DRIVER_VERSION_MISMATCH; + } + else + { + /* for pre 0x00060000 drivers */ + LogRel(("Support driver version mismatch: DriverVersion=too-old ClientVersion=%#x\n", SUPDRV_IOC_VERSION)); + rc = VERR_VM_DRIVER_VERSION_MISMATCH; + } + } + + suplibOsTerm(&g_supLibData); + } + g_cInits--; + + return rc; +} + + +SUPR3DECL(int) SUPR3Init(PSUPDRVSESSION *ppSession) +{ + return SUPR3InitEx(true, ppSession); +} + +/** + * Fake mode init. + */ +static int supInitFake(PSUPDRVSESSION *ppSession) +{ + Log(("SUP: Fake mode!\n")); + static const SUPFUNC s_aFakeFunctions[] = + { + /* name function */ + { "SUPR0AbsIs64bit", 0 }, + { "SUPR0Abs64bitKernelCS", 0 }, + { "SUPR0Abs64bitKernelSS", 0 }, + { "SUPR0Abs64bitKernelDS", 0 }, + { "SUPR0AbsKernelCS", 8 }, + { "SUPR0AbsKernelSS", 16 }, + { "SUPR0AbsKernelDS", 16 }, + { "SUPR0AbsKernelES", 16 }, + { "SUPR0AbsKernelFS", 24 }, + { "SUPR0AbsKernelGS", 32 }, + { "SUPR0ComponentRegisterFactory", 0xefeefffd }, + { "SUPR0ComponentDeregisterFactory", 0xefeefffe }, + { "SUPR0ComponentQueryFactory", 0xefeeffff }, + { "SUPR0ObjRegister", 0xefef0000 }, + { "SUPR0ObjAddRef", 0xefef0001 }, + { "SUPR0ObjAddRefEx", 0xefef0001 }, + { "SUPR0ObjRelease", 0xefef0002 }, + { "SUPR0ObjVerifyAccess", 0xefef0003 }, + { "SUPR0LockMem", 0xefef0004 }, + { "SUPR0UnlockMem", 0xefef0005 }, + { "SUPR0ContAlloc", 0xefef0006 }, + { "SUPR0ContFree", 0xefef0007 }, + { "SUPR0MemAlloc", 0xefef0008 }, + { "SUPR0MemGetPhys", 0xefef0009 }, + { "SUPR0MemFree", 0xefef000a }, + { "SUPR0Printf", 0xefef000b }, + { "SUPR0GetPagingMode", 0xefef000c }, + { "SUPR0EnableVTx", 0xefef000e }, + { "RTMemAlloc", 0xefef000f }, + { "RTMemAllocZ", 0xefef0010 }, + { "RTMemFree", 0xefef0011 }, + { "RTR0MemObjAddress", 0xefef0012 }, + { "RTR0MemObjAddressR3", 0xefef0013 }, + { "RTR0MemObjAllocPage", 0xefef0014 }, + { "RTR0MemObjAllocPhysNC", 0xefef0015 }, + { "RTR0MemObjAllocLow", 0xefef0016 }, + { "RTR0MemObjEnterPhys", 0xefef0017 }, + { "RTR0MemObjFree", 0xefef0018 }, + { "RTR0MemObjGetPagePhysAddr", 0xefef0019 }, + { "RTR0MemObjMapUser", 0xefef001a }, + { "RTR0MemObjMapKernel", 0xefef001b }, + { "RTR0MemObjMapKernelEx", 0xefef001c }, + { "RTMpGetArraySize", 0xefef001c }, + { "RTProcSelf", 0xefef001d }, + { "RTR0ProcHandleSelf", 0xefef001e }, + { "RTSemEventCreate", 0xefef001f }, + { "RTSemEventSignal", 0xefef0020 }, + { "RTSemEventWait", 0xefef0021 }, + { "RTSemEventWaitNoResume", 0xefef0022 }, + { "RTSemEventDestroy", 0xefef0023 }, + { "RTSemEventMultiCreate", 0xefef0024 }, + { "RTSemEventMultiSignal", 0xefef0025 }, + { "RTSemEventMultiReset", 0xefef0026 }, + { "RTSemEventMultiWait", 0xefef0027 }, + { "RTSemEventMultiWaitNoResume", 0xefef0028 }, + { "RTSemEventMultiDestroy", 0xefef0029 }, + { "RTSemFastMutexCreate", 0xefef002a }, + { "RTSemFastMutexDestroy", 0xefef002b }, + { "RTSemFastMutexRequest", 0xefef002c }, + { "RTSemFastMutexRelease", 0xefef002d }, + { "RTSpinlockCreate", 0xefef002e }, + { "RTSpinlockDestroy", 0xefef002f }, + { "RTSpinlockAcquire", 0xefef0030 }, + { "RTSpinlockRelease", 0xefef0031 }, + { "RTSpinlockAcquireNoInts", 0xefef0032 }, + { "RTTimeNanoTS", 0xefef0034 }, + { "RTTimeMillieTS", 0xefef0035 }, + { "RTTimeSystemNanoTS", 0xefef0036 }, + { "RTTimeSystemMillieTS", 0xefef0037 }, + { "RTThreadNativeSelf", 0xefef0038 }, + { "RTThreadSleep", 0xefef0039 }, + { "RTThreadYield", 0xefef003a }, + { "RTTimerCreate", 0xefef003a }, + { "RTTimerCreateEx", 0xefef003a }, + { "RTTimerDestroy", 0xefef003a }, + { "RTTimerStart", 0xefef003a }, + { "RTTimerStop", 0xefef003a }, + { "RTTimerChangeInterval", 0xefef003a }, + { "RTTimerGetSystemGranularity", 0xefef003a }, + { "RTTimerRequestSystemGranularity", 0xefef003a }, + { "RTTimerReleaseSystemGranularity", 0xefef003a }, + { "RTTimerCanDoHighResolution", 0xefef003a }, + { "RTLogDefaultInstance", 0xefef003b }, + { "RTLogRelGetDefaultInstance", 0xefef003c }, + { "RTLogSetDefaultInstanceThread", 0xefef003d }, + { "RTLogLogger", 0xefef003e }, + { "RTLogLoggerEx", 0xefef003f }, + { "RTLogLoggerExV", 0xefef0040 }, + { "RTAssertMsg1", 0xefef0041 }, + { "RTAssertMsg2", 0xefef0042 }, + { "RTAssertMsg2V", 0xefef0043 }, + { "SUPR0QueryVTCaps", 0xefef0044 }, + }; + + /* fake r0 functions. */ + g_pSupFunctions = (PSUPQUERYFUNCS)RTMemAllocZ(SUP_IOCTL_QUERY_FUNCS_SIZE(RT_ELEMENTS(s_aFakeFunctions))); + if (g_pSupFunctions) + { + g_pSupFunctions->u.Out.cFunctions = RT_ELEMENTS(s_aFakeFunctions); + memcpy(&g_pSupFunctions->u.Out.aFunctions[0], &s_aFakeFunctions[0], sizeof(s_aFakeFunctions)); + g_pSession = (PSUPDRVSESSION)(void *)g_pSupFunctions; + if (ppSession) + *ppSession = g_pSession; + + /* fake the GIP. */ + g_pSUPGlobalInfoPage = (PSUPGLOBALINFOPAGE)RTMemPageAllocZ(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_pSupFunctions); + g_pSupFunctions = NULL; + } + return VERR_NO_MEMORY; +} + + +SUPR3DECL(int) SUPR3Term(bool fForced) +{ + /* + * Verify state. + */ + AssertMsg(g_cInits > 0, ("SUPR3Term() is called before SUPR3Init()!\n")); + if (g_cInits == 0) + return VERR_WRONG_ORDER; + if (g_cInits == 1 || fForced) + { + /* + * NULL the GIP pointer. + */ + if (g_pSUPGlobalInfoPage) + { + ASMAtomicWriteNullPtr((void * volatile *)&g_pSUPGlobalInfoPage); + ASMAtomicWriteNullPtr((void * volatile *)&g_pSUPGlobalInfoPageR0); + ASMAtomicWriteU64(&g_HCPhysSUPGlobalInfoPage, NIL_RTHCPHYS); + /* just a little safe guard against threads using the page. */ + RTThreadSleep(50); + } + + /* + * Close the support driver. + */ + int rc = suplibOsTerm(&g_supLibData); + if (rc) + return rc; + + g_u32Cookie = 0; + g_u32SessionCookie = 0; + g_cInits = 0; + } + else + g_cInits--; + + return 0; +} + + +SUPR3DECL(SUPPAGINGMODE) SUPR3GetPagingMode(void) +{ + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) +#ifdef RT_ARCH_AMD64 + return SUPPAGINGMODE_AMD64_GLOBAL_NX; +#else + return SUPPAGINGMODE_32_BIT_GLOBAL; +#endif + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPGETPAGINGMODE Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_GET_PAGING_MODE_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_GET_PAGING_MODE_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_GET_PAGING_MODE, &Req, SUP_IOCTL_GET_PAGING_MODE_SIZE); + if ( RT_FAILURE(rc) + || RT_FAILURE(Req.Hdr.rc)) + { + LogRel(("SUPR3GetPagingMode: %Rrc %Rrc\n", rc, Req.Hdr.rc)); + Req.u.Out.enmMode = SUPPAGINGMODE_INVALID; + } + + return Req.u.Out.enmMode; +} + + +/** + * For later. + */ +static int supCallVMMR0ExFake(PVMR0 pVMR0, unsigned uOperation, uint64_t u64Arg, PSUPVMMR0REQHDR pReqHdr) +{ + AssertMsgFailed(("%d\n", uOperation)); NOREF(pVMR0); NOREF(uOperation); NOREF(u64Arg); NOREF(pReqHdr); + return VERR_NOT_SUPPORTED; +} + + +SUPR3DECL(int) SUPR3CallVMMR0Fast(PVMR0 pVMR0, unsigned uOperation, VMCPUID idCpu) +{ + NOREF(pVMR0); + static const uintptr_t s_auFunctions[4] = + { + SUP_IOCTL_FAST_DO_RAW_RUN, + SUP_IOCTL_FAST_DO_HM_RUN, + SUP_IOCTL_FAST_DO_NOP, + SUP_IOCTL_FAST_DO_NEM_RUN + }; + AssertCompile(SUP_VMMR0_DO_RAW_RUN == 0); + AssertCompile(SUP_VMMR0_DO_HM_RUN == 1); + AssertCompile(SUP_VMMR0_DO_NOP == 2); + AssertCompile(SUP_VMMR0_DO_NEM_RUN == 3); + AssertMsgReturn(uOperation < RT_ELEMENTS(s_auFunctions), ("%#x\n", uOperation), VERR_INTERNAL_ERROR); + return suplibOsIOCtlFast(&g_supLibData, s_auFunctions[uOperation], idCpu); +} + + +SUPR3DECL(int) SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned uOperation, uint64_t u64Arg, PSUPVMMR0REQHDR pReqHdr) +{ + /* + * The following operations don't belong here. + */ + AssertMsgReturn( uOperation != SUP_VMMR0_DO_RAW_RUN + && uOperation != SUP_VMMR0_DO_HM_RUN + && uOperation != SUP_VMMR0_DO_NOP, + ("%#x\n", uOperation), + VERR_INTERNAL_ERROR); + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + return supCallVMMR0ExFake(pVMR0, uOperation, u64Arg, pReqHdr); + + int rc; + if (!pReqHdr) + { + /* no data. */ + SUPCALLVMMR0 Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_CALL_VMMR0_SIZE_IN(0); + Req.Hdr.cbOut = SUP_IOCTL_CALL_VMMR0_SIZE_OUT(0); + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pVMR0 = pVMR0; + Req.u.In.idCpu = idCpu; + Req.u.In.uOperation = uOperation; + Req.u.In.u64Arg = u64Arg; + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_VMMR0(0), &Req, SUP_IOCTL_CALL_VMMR0_SIZE(0)); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + } + else if (SUP_IOCTL_CALL_VMMR0_SIZE(pReqHdr->cbReq) < _4K) /* FreeBSD won't copy more than 4K. */ + { + AssertPtrReturn(pReqHdr, VERR_INVALID_POINTER); + AssertReturn(pReqHdr->u32Magic == SUPVMMR0REQHDR_MAGIC, VERR_INVALID_MAGIC); + const size_t cbReq = pReqHdr->cbReq; + + PSUPCALLVMMR0 pReq = (PSUPCALLVMMR0)alloca(SUP_IOCTL_CALL_VMMR0_SIZE(cbReq)); + pReq->Hdr.u32Cookie = g_u32Cookie; + pReq->Hdr.u32SessionCookie = g_u32SessionCookie; + pReq->Hdr.cbIn = SUP_IOCTL_CALL_VMMR0_SIZE_IN(cbReq); + pReq->Hdr.cbOut = SUP_IOCTL_CALL_VMMR0_SIZE_OUT(cbReq); + pReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + pReq->Hdr.rc = VERR_INTERNAL_ERROR; + pReq->u.In.pVMR0 = pVMR0; + pReq->u.In.idCpu = idCpu; + pReq->u.In.uOperation = uOperation; + pReq->u.In.u64Arg = u64Arg; + memcpy(&pReq->abReqPkt[0], pReqHdr, cbReq); + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_VMMR0(cbReq), pReq, SUP_IOCTL_CALL_VMMR0_SIZE(cbReq)); + if (RT_SUCCESS(rc)) + rc = pReq->Hdr.rc; + memcpy(pReqHdr, &pReq->abReqPkt[0], cbReq); + } + else if (pReqHdr->cbReq <= _512K) + { + AssertPtrReturn(pReqHdr, VERR_INVALID_POINTER); + AssertReturn(pReqHdr->u32Magic == SUPVMMR0REQHDR_MAGIC, VERR_INVALID_MAGIC); + const size_t cbReq = pReqHdr->cbReq; + + PSUPCALLVMMR0 pReq = (PSUPCALLVMMR0)RTMemTmpAlloc(SUP_IOCTL_CALL_VMMR0_BIG_SIZE(cbReq)); + pReq->Hdr.u32Cookie = g_u32Cookie; + pReq->Hdr.u32SessionCookie = g_u32SessionCookie; + pReq->Hdr.cbIn = SUP_IOCTL_CALL_VMMR0_BIG_SIZE_IN(cbReq); + pReq->Hdr.cbOut = SUP_IOCTL_CALL_VMMR0_BIG_SIZE_OUT(cbReq); + pReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + pReq->Hdr.rc = VERR_INTERNAL_ERROR; + pReq->u.In.pVMR0 = pVMR0; + pReq->u.In.idCpu = idCpu; + pReq->u.In.uOperation = uOperation; + pReq->u.In.u64Arg = u64Arg; + memcpy(&pReq->abReqPkt[0], pReqHdr, cbReq); + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_VMMR0_BIG, pReq, SUP_IOCTL_CALL_VMMR0_BIG_SIZE(cbReq)); + if (RT_SUCCESS(rc)) + rc = pReq->Hdr.rc; + memcpy(pReqHdr, &pReq->abReqPkt[0], cbReq); + RTMemTmpFree(pReq); + } + else + AssertMsgFailedReturn(("cbReq=%#x\n", pReqHdr->cbReq), VERR_OUT_OF_RANGE); + return rc; +} + + +SUPR3DECL(int) SUPR3CallVMMR0(PVMR0 pVMR0, VMCPUID idCpu, unsigned uOperation, void *pvArg) +{ + /* + * The following operations don't belong here. + */ + AssertMsgReturn( uOperation != SUP_VMMR0_DO_RAW_RUN + && uOperation != SUP_VMMR0_DO_HM_RUN + && uOperation != SUP_VMMR0_DO_NOP, + ("%#x\n", uOperation), + VERR_INTERNAL_ERROR); + return SUPR3CallVMMR0Ex(pVMR0, idCpu, uOperation, (uintptr_t)pvArg, NULL); +} + + +SUPR3DECL(int) SUPR3SetVMForFastIOCtl(PVMR0 pVMR0) +{ + if (RT_UNLIKELY(g_uSupFakeMode)) + return VINF_SUCCESS; + + SUPSETVMFORFAST Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_SET_VM_FOR_FAST_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_SET_VM_FOR_FAST_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pVMR0 = pVMR0; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_SET_VM_FOR_FAST, &Req, SUP_IOCTL_SET_VM_FOR_FAST_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +SUPR3DECL(int) SUPR3CallR0Service(const char *pszService, size_t cchService, uint32_t uOperation, uint64_t u64Arg, PSUPR0SERVICEREQHDR pReqHdr) +{ + AssertReturn(cchService < RT_SIZEOFMEMB(SUPCALLSERVICE, u.In.szName), VERR_INVALID_PARAMETER); + Assert(strlen(pszService) == cchService); + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + return VERR_NOT_SUPPORTED; + + int rc; + if (!pReqHdr) + { + /* no data. */ + SUPCALLSERVICE Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_CALL_SERVICE_SIZE_IN(0); + Req.Hdr.cbOut = SUP_IOCTL_CALL_SERVICE_SIZE_OUT(0); + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + memcpy(Req.u.In.szName, pszService, cchService); + Req.u.In.szName[cchService] = '\0'; + Req.u.In.uOperation = uOperation; + Req.u.In.u64Arg = u64Arg; + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_SERVICE(0), &Req, SUP_IOCTL_CALL_SERVICE_SIZE(0)); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + } + else if (SUP_IOCTL_CALL_SERVICE_SIZE(pReqHdr->cbReq) < _4K) /* FreeBSD won't copy more than 4K. */ + { + AssertPtrReturn(pReqHdr, VERR_INVALID_POINTER); + AssertReturn(pReqHdr->u32Magic == SUPR0SERVICEREQHDR_MAGIC, VERR_INVALID_MAGIC); + const size_t cbReq = pReqHdr->cbReq; + + PSUPCALLSERVICE pReq = (PSUPCALLSERVICE)alloca(SUP_IOCTL_CALL_SERVICE_SIZE(cbReq)); + pReq->Hdr.u32Cookie = g_u32Cookie; + pReq->Hdr.u32SessionCookie = g_u32SessionCookie; + pReq->Hdr.cbIn = SUP_IOCTL_CALL_SERVICE_SIZE_IN(cbReq); + pReq->Hdr.cbOut = SUP_IOCTL_CALL_SERVICE_SIZE_OUT(cbReq); + pReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + pReq->Hdr.rc = VERR_INTERNAL_ERROR; + memcpy(pReq->u.In.szName, pszService, cchService); + pReq->u.In.szName[cchService] = '\0'; + pReq->u.In.uOperation = uOperation; + pReq->u.In.u64Arg = u64Arg; + memcpy(&pReq->abReqPkt[0], pReqHdr, cbReq); + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CALL_SERVICE(cbReq), pReq, SUP_IOCTL_CALL_SERVICE_SIZE(cbReq)); + if (RT_SUCCESS(rc)) + rc = pReq->Hdr.rc; + memcpy(pReqHdr, &pReq->abReqPkt[0], cbReq); + } + else /** @todo may have to remove the size limits one this request... */ + AssertMsgFailedReturn(("cbReq=%#x\n", pReqHdr->cbReq), VERR_INTERNAL_ERROR); + return rc; +} + + +/** + * Worker for the SUPR3Logger* APIs. + * + * @returns VBox status code. + * @param enmWhich Which logger. + * @param fWhat What to do with the logger. + * @param pszFlags The flags settings. + * @param pszGroups The groups settings. + * @param pszDest The destination specificier. + */ +static int supR3LoggerSettings(SUPLOGGER enmWhich, uint32_t fWhat, const char *pszFlags, const char *pszGroups, const char *pszDest) +{ + uint32_t const cchFlags = pszFlags ? (uint32_t)strlen(pszFlags) : 0; + uint32_t const cchGroups = pszGroups ? (uint32_t)strlen(pszGroups) : 0; + uint32_t const cchDest = pszDest ? (uint32_t)strlen(pszDest) : 0; + uint32_t const cbStrTab = cchFlags + !!cchFlags + + cchGroups + !!cchGroups + + cchDest + !!cchDest + + (!cchFlags && !cchGroups && !cchDest); + + PSUPLOGGERSETTINGS pReq = (PSUPLOGGERSETTINGS)alloca(SUP_IOCTL_LOGGER_SETTINGS_SIZE(cbStrTab)); + pReq->Hdr.u32Cookie = g_u32Cookie; + pReq->Hdr.u32SessionCookie = g_u32SessionCookie; + pReq->Hdr.cbIn = SUP_IOCTL_LOGGER_SETTINGS_SIZE_IN(cbStrTab); + pReq->Hdr.cbOut = SUP_IOCTL_LOGGER_SETTINGS_SIZE_OUT; + pReq->Hdr.fFlags= SUPREQHDR_FLAGS_DEFAULT; + pReq->Hdr.rc = VERR_INTERNAL_ERROR; + switch (enmWhich) + { + case SUPLOGGER_DEBUG: pReq->u.In.fWhich = SUPLOGGERSETTINGS_WHICH_DEBUG; break; + case SUPLOGGER_RELEASE: pReq->u.In.fWhich = SUPLOGGERSETTINGS_WHICH_RELEASE; break; + default: + return VERR_INVALID_PARAMETER; + } + pReq->u.In.fWhat = fWhat; + + uint32_t off = 0; + if (cchFlags) + { + pReq->u.In.offFlags = off; + memcpy(&pReq->u.In.szStrings[off], pszFlags, cchFlags + 1); + off += cchFlags + 1; + } + else + pReq->u.In.offFlags = cbStrTab - 1; + + if (cchGroups) + { + pReq->u.In.offGroups = off; + memcpy(&pReq->u.In.szStrings[off], pszGroups, cchGroups + 1); + off += cchGroups + 1; + } + else + pReq->u.In.offGroups = cbStrTab - 1; + + if (cchDest) + { + pReq->u.In.offDestination = off; + memcpy(&pReq->u.In.szStrings[off], pszDest, cchDest + 1); + off += cchDest + 1; + } + else + pReq->u.In.offDestination = cbStrTab - 1; + + if (!off) + { + pReq->u.In.szStrings[0] = '\0'; + off++; + } + Assert(off == cbStrTab); + Assert(pReq->u.In.szStrings[cbStrTab - 1] == '\0'); + + + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LOGGER_SETTINGS(cbStrTab), pReq, SUP_IOCTL_LOGGER_SETTINGS_SIZE(cbStrTab)); + if (RT_SUCCESS(rc)) + rc = pReq->Hdr.rc; + 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) +{ + return supR3LoggerSettings(enmWhich, SUPLOGGERSETTINGS_WHAT_CREATE, pszFlags, pszGroups, pszDest); +} + + +SUPR3DECL(int) SUPR3LoggerDestroy(SUPLOGGER enmWhich) +{ + return supR3LoggerSettings(enmWhich, SUPLOGGERSETTINGS_WHAT_DESTROY, NULL, NULL, NULL); +} + + +SUPR3DECL(int) SUPR3PageAlloc(size_t cPages, void **ppvPages) +{ + /* + * Validate. + */ + AssertPtrReturn(ppvPages, VERR_INVALID_POINTER); + *ppvPages = NULL; + AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE); + + /* + * Call OS specific worker. + */ + return suplibOsPageAlloc(&g_supLibData, cPages, ppvPages); +} + + +SUPR3DECL(int) SUPR3PageFree(void *pvPages, size_t cPages) +{ + /* + * Validate. + */ + AssertPtrReturn(pvPages, VERR_INVALID_POINTER); + AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE); + + /* + * Call OS specific worker. + */ + return suplibOsPageFree(&g_supLibData, pvPages, cPages); +} + + +/** + * Locks down the physical memory backing a virtual memory + * range in the current process. + * + * @returns VBox status code. + * @param pvStart Start of virtual memory range. + * Must be page aligned. + * @param cPages Number of pages. + * @param paPages Where to store the physical page addresses returned. + * On entry this will point to an array of with cbMemory >> PAGE_SHIFT entries. + */ +SUPR3DECL(int) supR3PageLock(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); + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + { + RTHCPHYS Phys = (uintptr_t)pvStart + PAGE_SIZE * 1024; + size_t iPage = cPages; + while (iPage-- > 0) + paPages[iPage].Phys = Phys + (iPage << PAGE_SHIFT); + return VINF_SUCCESS; + } + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + int rc; + PSUPPAGELOCK pReq = (PSUPPAGELOCK)RTMemTmpAllocZ(SUP_IOCTL_PAGE_LOCK_SIZE(cPages)); + if (RT_LIKELY(pReq)) + { + pReq->Hdr.u32Cookie = g_u32Cookie; + pReq->Hdr.u32SessionCookie = g_u32SessionCookie; + pReq->Hdr.cbIn = SUP_IOCTL_PAGE_LOCK_SIZE_IN; + pReq->Hdr.cbOut = SUP_IOCTL_PAGE_LOCK_SIZE_OUT(cPages); + pReq->Hdr.fFlags = SUPREQHDR_FLAGS_MAGIC | SUPREQHDR_FLAGS_EXTRA_OUT; + pReq->Hdr.rc = VERR_INTERNAL_ERROR; + pReq->u.In.pvR3 = pvStart; + pReq->u.In.cPages = (uint32_t)cPages; AssertRelease(pReq->u.In.cPages == cPages); + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_LOCK, pReq, SUP_IOCTL_PAGE_LOCK_SIZE(cPages)); + if (RT_SUCCESS(rc)) + rc = pReq->Hdr.rc; + if (RT_SUCCESS(rc)) + { + for (uint32_t iPage = 0; iPage < cPages; iPage++) + { + paPages[iPage].uReserved = 0; + paPages[iPage].Phys = pReq->u.Out.aPages[iPage]; + Assert(!(paPages[iPage].Phys & ~X86_PTE_PAE_PG_MASK)); + } + } + RTMemTmpFree(pReq); + } + else + rc = VERR_NO_TMP_MEMORY; + + return rc; +} + + +/** + * Releases locked down pages. + * + * @returns VBox status code. + * @param pvStart Start of virtual memory range previously locked + * down by SUPPageLock(). + */ +SUPR3DECL(int) supR3PageUnlock(void *pvStart) +{ + /* + * Validate. + */ + AssertPtr(pvStart); + AssertMsg(RT_ALIGN_P(pvStart, PAGE_SIZE) == pvStart, ("pvStart (%p) must be page aligned\n", pvStart)); + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + return VINF_SUCCESS; + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPPAGEUNLOCK Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_PAGE_UNLOCK_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_PAGE_UNLOCK_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pvR3 = pvStart; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_UNLOCK, &Req, SUP_IOCTL_PAGE_UNLOCK_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +SUPR3DECL(int) SUPR3LockDownLoader(PRTERRINFO pErrInfo) +{ + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + return VINF_SUCCESS; + + /* + * Lock down the module loader interface. + */ + SUPREQHDR ReqHdr; + ReqHdr.u32Cookie = g_u32Cookie; + ReqHdr.u32SessionCookie = g_u32SessionCookie; + ReqHdr.cbIn = SUP_IOCTL_LDR_LOCK_DOWN_SIZE_IN; + ReqHdr.cbOut = SUP_IOCTL_LDR_LOCK_DOWN_SIZE_OUT; + ReqHdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + ReqHdr.rc = VERR_INTERNAL_ERROR; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LDR_LOCK_DOWN, &ReqHdr, SUP_IOCTL_LDR_LOCK_DOWN_SIZE); + if (RT_FAILURE(rc)) + return RTErrInfoSetF(pErrInfo, rc, + "SUPR3LockDownLoader: SUP_IOCTL_LDR_LOCK_DOWN ioctl returned %Rrc", rc); + + return ReqHdr.rc; +} + + +/** + * Fallback for SUPR3PageAllocEx on systems where RTR0MemObjPhysAllocNC isn't + * supported. + */ +static int supPagePageAllocNoKernelFallback(size_t cPages, void **ppvPages, PSUPPAGE paPages) +{ + int rc = suplibOsPageAlloc(&g_supLibData, cPages, ppvPages); + if (RT_SUCCESS(rc)) + { + if (!paPages) + paPages = (PSUPPAGE)alloca(sizeof(paPages[0]) * cPages); + rc = supR3PageLock(*ppvPages, cPages, paPages); + if (RT_FAILURE(rc)) + suplibOsPageFree(&g_supLibData, *ppvPages, cPages); + } + return rc; +} + + +SUPR3DECL(int) SUPR3PageAllocEx(size_t cPages, uint32_t fFlags, void **ppvPages, PRTR0PTR pR0Ptr, PSUPPAGE paPages) +{ + /* + * Validate. + */ + AssertPtrReturn(ppvPages, VERR_INVALID_POINTER); + *ppvPages = NULL; + AssertPtrNullReturn(pR0Ptr, VERR_INVALID_POINTER); + if (pR0Ptr) + *pR0Ptr = NIL_RTR0PTR; + AssertPtrNullReturn(paPages, VERR_INVALID_POINTER); + AssertMsgReturn(cPages > 0 && cPages <= VBOX_MAX_ALLOC_PAGE_COUNT, ("cPages=%zu\n", cPages), VERR_PAGE_COUNT_OUT_OF_RANGE); + AssertReturn(!fFlags, VERR_INVALID_PARAMETER); + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + { + void *pv = RTMemPageAllocZ(cPages * PAGE_SIZE); + if (!pv) + return VERR_NO_MEMORY; + *ppvPages = pv; + if (pR0Ptr) + *pR0Ptr = (RTR0PTR)pv; + if (paPages) + for (size_t iPage = 0; iPage < cPages; iPage++) + { + paPages[iPage].uReserved = 0; + paPages[iPage].Phys = (iPage + 4321) << PAGE_SHIFT; + Assert(!(paPages[iPage].Phys & ~X86_PTE_PAE_PG_MASK)); + } + return VINF_SUCCESS; + } + + /* Check that we've got a kernel connection so rtMemSaferSupR3AllocPages + can do fallback without first having to hit assertions. */ + if (g_supLibData.hDevice != SUP_HDEVICE_NIL) + { /* likely */ } + else + return VERR_WRONG_ORDER; + + /* + * Use fallback for non-R0 mapping? + */ + if ( !pR0Ptr + && !g_fSupportsPageAllocNoKernel) + return supPagePageAllocNoKernelFallback(cPages, ppvPages, paPages); + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + int rc; + PSUPPAGEALLOCEX pReq = (PSUPPAGEALLOCEX)RTMemTmpAllocZ(SUP_IOCTL_PAGE_ALLOC_EX_SIZE(cPages)); + if (pReq) + { + pReq->Hdr.u32Cookie = g_u32Cookie; + pReq->Hdr.u32SessionCookie = g_u32SessionCookie; + pReq->Hdr.cbIn = SUP_IOCTL_PAGE_ALLOC_EX_SIZE_IN; + pReq->Hdr.cbOut = SUP_IOCTL_PAGE_ALLOC_EX_SIZE_OUT(cPages); + pReq->Hdr.fFlags = SUPREQHDR_FLAGS_MAGIC | SUPREQHDR_FLAGS_EXTRA_OUT; + pReq->Hdr.rc = VERR_INTERNAL_ERROR; + pReq->u.In.cPages = (uint32_t)cPages; AssertRelease(pReq->u.In.cPages == cPages); + pReq->u.In.fKernelMapping = pR0Ptr != NULL; + pReq->u.In.fUserMapping = true; + pReq->u.In.fReserved0 = false; + pReq->u.In.fReserved1 = false; + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_ALLOC_EX, pReq, SUP_IOCTL_PAGE_ALLOC_EX_SIZE(cPages)); + if (RT_SUCCESS(rc)) + { + rc = pReq->Hdr.rc; + if (RT_SUCCESS(rc)) + { + *ppvPages = pReq->u.Out.pvR3; + if (pR0Ptr) + *pR0Ptr = pReq->u.Out.pvR0; + if (paPages) + for (size_t iPage = 0; iPage < cPages; iPage++) + { + paPages[iPage].uReserved = 0; + paPages[iPage].Phys = pReq->u.Out.aPages[iPage]; + Assert(!(paPages[iPage].Phys & ~X86_PTE_PAE_PG_MASK)); + } +#ifdef RT_OS_DARWIN /* HACK ALERT! */ + supR3TouchPages(pReq->u.Out.pvR3, cPages); +#endif + } + else if ( rc == VERR_NOT_SUPPORTED + && !pR0Ptr) + { + g_fSupportsPageAllocNoKernel = false; + rc = supPagePageAllocNoKernelFallback(cPages, ppvPages, paPages); + } + } + + RTMemTmpFree(pReq); + } + else + rc = VERR_NO_TMP_MEMORY; + return rc; + +} + + +SUPR3DECL(int) SUPR3PageMapKernel(void *pvR3, uint32_t off, uint32_t cb, uint32_t fFlags, PRTR0PTR pR0Ptr) +{ + /* + * Validate. + */ + AssertPtrReturn(pvR3, VERR_INVALID_POINTER); + AssertPtrReturn(pR0Ptr, VERR_INVALID_POINTER); + Assert(!(off & PAGE_OFFSET_MASK)); + Assert(!(cb & PAGE_OFFSET_MASK) && cb); + Assert(!fFlags); + *pR0Ptr = NIL_RTR0PTR; + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + return VERR_NOT_SUPPORTED; + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPPAGEMAPKERNEL Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_PAGE_MAP_KERNEL_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_PAGE_MAP_KERNEL_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pvR3 = pvR3; + Req.u.In.offSub = off; + Req.u.In.cbSub = cb; + Req.u.In.fFlags = fFlags; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_MAP_KERNEL, &Req, SUP_IOCTL_PAGE_MAP_KERNEL_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + if (RT_SUCCESS(rc)) + *pR0Ptr = Req.u.Out.pvR0; + return rc; +} + + +SUPR3DECL(int) SUPR3PageProtect(void *pvR3, RTR0PTR R0Ptr, uint32_t off, uint32_t cb, uint32_t fProt) +{ + /* + * Validate. + */ + AssertPtrReturn(pvR3, VERR_INVALID_POINTER); + Assert(!(off & PAGE_OFFSET_MASK)); + Assert(!(cb & PAGE_OFFSET_MASK) && cb); + AssertReturn(!(fProt & ~(RTMEM_PROT_NONE | RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC)), VERR_INVALID_PARAMETER); + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + return RTMemProtect((uint8_t *)pvR3 + off, cb, fProt); + + /* + * Some OSes can do this from ring-3, so try that before we + * issue the IOCtl to the SUPDRV kernel module. + * (Yea, this isn't very nice, but just try get the job done for now.) + */ +#if !defined(RT_OS_SOLARIS) + RTMemProtect((uint8_t *)pvR3 + off, cb, fProt); +#endif + + SUPPAGEPROTECT Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_PAGE_PROTECT_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_PAGE_PROTECT_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pvR3 = pvR3; + Req.u.In.pvR0 = R0Ptr; + Req.u.In.offSub = off; + Req.u.In.cbSub = cb; + Req.u.In.fProt = fProt; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_PROTECT, &Req, SUP_IOCTL_PAGE_PROTECT_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +SUPR3DECL(int) SUPR3PageFreeEx(void *pvPages, size_t cPages) +{ + /* + * Validate. + */ + AssertPtrReturn(pvPages, VERR_INVALID_POINTER); + AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE); + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + { + RTMemPageFree(pvPages, cPages * PAGE_SIZE); + return VINF_SUCCESS; + } + + /* + * Try normal free first, then if it fails check if we're using the fallback + * for the allocations without kernel mappings and attempt unlocking it. + */ + NOREF(cPages); + SUPPAGEFREE Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_PAGE_FREE_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_PAGE_FREE_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pvR3 = pvPages; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_PAGE_FREE, &Req, SUP_IOCTL_PAGE_FREE_SIZE); + if (RT_SUCCESS(rc)) + { + rc = Req.Hdr.rc; + if ( rc == VERR_INVALID_PARAMETER + && !g_fSupportsPageAllocNoKernel) + { + int rc2 = supR3PageUnlock(pvPages); + if (RT_SUCCESS(rc2)) + rc = suplibOsPageFree(&g_supLibData, pvPages, cPages); + } + } + return rc; +} + + +SUPR3DECL(void *) SUPR3ContAlloc(size_t cPages, PRTR0PTR pR0Ptr, PRTHCPHYS pHCPhys) +{ + /* + * Validate. + */ + AssertPtrReturn(pHCPhys, NULL); + *pHCPhys = NIL_RTHCPHYS; + AssertPtrNullReturn(pR0Ptr, NULL); + if (pR0Ptr) + *pR0Ptr = NIL_RTR0PTR; + AssertPtrNullReturn(pHCPhys, NULL); + AssertMsgReturn(cPages > 0 && cPages < 256, ("cPages=%d must be > 0 and < 256\n", cPages), NULL); + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + { + void *pv = RTMemPageAllocZ(cPages * PAGE_SIZE); + if (pR0Ptr) + *pR0Ptr = (RTR0PTR)pv; + if (pHCPhys) + *pHCPhys = (uintptr_t)pv + (PAGE_SHIFT * 1024); + return pv; + } + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPCONTALLOC Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_CONT_ALLOC_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_CONT_ALLOC_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.cPages = (uint32_t)cPages; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CONT_ALLOC, &Req, SUP_IOCTL_CONT_ALLOC_SIZE); + if ( RT_SUCCESS(rc) + && RT_SUCCESS(Req.Hdr.rc)) + { + *pHCPhys = Req.u.Out.HCPhys; + if (pR0Ptr) + *pR0Ptr = Req.u.Out.pvR0; +#ifdef RT_OS_DARWIN /* HACK ALERT! */ + supR3TouchPages(Req.u.Out.pvR3, cPages); +#endif + return Req.u.Out.pvR3; + } + + return NULL; +} + + +SUPR3DECL(int) SUPR3ContFree(void *pv, size_t cPages) +{ + /* + * Validate. + */ + if (!pv) + return VINF_SUCCESS; + AssertPtrReturn(pv, VERR_INVALID_POINTER); + AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE); + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + { + RTMemPageFree(pv, cPages * PAGE_SIZE); + return VINF_SUCCESS; + } + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPCONTFREE Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_CONT_FREE_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_CONT_FREE_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pvR3 = pv; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_CONT_FREE, &Req, SUP_IOCTL_CONT_FREE_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +SUPR3DECL(int) SUPR3LowAlloc(size_t cPages, void **ppvPages, PRTR0PTR ppvPagesR0, PSUPPAGE paPages) +{ + /* + * Validate. + */ + AssertPtrReturn(ppvPages, VERR_INVALID_POINTER); + *ppvPages = NULL; + AssertPtrReturn(paPages, VERR_INVALID_POINTER); + AssertMsgReturn(cPages > 0 && cPages < 256, ("cPages=%d must be > 0 and < 256\n", cPages), VERR_PAGE_COUNT_OUT_OF_RANGE); + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + { + *ppvPages = RTMemPageAllocZ((size_t)cPages * PAGE_SIZE); + if (!*ppvPages) + return VERR_NO_LOW_MEMORY; + + /* fake physical addresses. */ + RTHCPHYS Phys = (uintptr_t)*ppvPages + PAGE_SIZE * 1024; + size_t iPage = cPages; + while (iPage-- > 0) + paPages[iPage].Phys = Phys + (iPage << PAGE_SHIFT); + return VINF_SUCCESS; + } + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + int rc; + PSUPLOWALLOC pReq = (PSUPLOWALLOC)RTMemTmpAllocZ(SUP_IOCTL_LOW_ALLOC_SIZE(cPages)); + if (pReq) + { + pReq->Hdr.u32Cookie = g_u32Cookie; + pReq->Hdr.u32SessionCookie = g_u32SessionCookie; + pReq->Hdr.cbIn = SUP_IOCTL_LOW_ALLOC_SIZE_IN; + pReq->Hdr.cbOut = SUP_IOCTL_LOW_ALLOC_SIZE_OUT(cPages); + pReq->Hdr.fFlags = SUPREQHDR_FLAGS_MAGIC | SUPREQHDR_FLAGS_EXTRA_OUT; + pReq->Hdr.rc = VERR_INTERNAL_ERROR; + pReq->u.In.cPages = (uint32_t)cPages; AssertRelease(pReq->u.In.cPages == cPages); + rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LOW_ALLOC, pReq, SUP_IOCTL_LOW_ALLOC_SIZE(cPages)); + if (RT_SUCCESS(rc)) + rc = pReq->Hdr.rc; + if (RT_SUCCESS(rc)) + { + *ppvPages = pReq->u.Out.pvR3; + if (ppvPagesR0) + *ppvPagesR0 = pReq->u.Out.pvR0; + if (paPages) + for (size_t iPage = 0; iPage < cPages; iPage++) + { + paPages[iPage].uReserved = 0; + paPages[iPage].Phys = pReq->u.Out.aPages[iPage]; + Assert(!(paPages[iPage].Phys & ~X86_PTE_PAE_PG_MASK)); + Assert(paPages[iPage].Phys <= UINT32_C(0xfffff000)); + } +#ifdef RT_OS_DARWIN /* HACK ALERT! */ + supR3TouchPages(pReq->u.Out.pvR3, cPages); +#endif + } + RTMemTmpFree(pReq); + } + else + rc = VERR_NO_TMP_MEMORY; + + return rc; +} + + +SUPR3DECL(int) SUPR3LowFree(void *pv, size_t cPages) +{ + /* + * Validate. + */ + if (!pv) + return VINF_SUCCESS; + AssertPtrReturn(pv, VERR_INVALID_POINTER); + AssertReturn(cPages > 0, VERR_PAGE_COUNT_OUT_OF_RANGE); + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + { + RTMemPageFree(pv, cPages * PAGE_SIZE); + return VINF_SUCCESS; + } + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPCONTFREE Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_LOW_FREE_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_LOW_FREE_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pvR3 = pv; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_LOW_FREE, &Req, SUP_IOCTL_LOW_FREE_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +SUPR3DECL(int) SUPR3HardenedVerifyInit(void) +{ +#ifdef RT_OS_WINDOWS + if (g_cInits == 0) + return suplibOsHardenedVerifyInit(); +#endif + return VINF_SUCCESS; +} + + +SUPR3DECL(int) SUPR3HardenedVerifyTerm(void) +{ +#ifdef RT_OS_WINDOWS + if (g_cInits == 0) + return suplibOsHardenedVerifyTerm(); +#endif + return VINF_SUCCESS; +} + + +SUPR3DECL(int) SUPR3HardenedVerifyFile(const char *pszFilename, const char *pszMsg, PRTFILE phFile) +{ + /* + * Quick input validation. + */ + AssertPtr(pszFilename); + AssertPtr(pszMsg); + AssertReturn(!phFile, VERR_NOT_IMPLEMENTED); /** @todo Implement this. The deal is that we make sure the + file is the same we verified after opening it. */ + RT_NOREF2(pszFilename, pszMsg); + + /* + * Only do the actual check in hardened builds. + */ +#ifdef VBOX_WITH_HARDENING + int rc = supR3HardenedVerifyFixedFile(pszFilename, false /* fFatal */); + if (RT_FAILURE(rc)) + LogRel(("SUPR3HardenedVerifyFile: %s: Verification of \"%s\" failed, rc=%Rrc\n", pszMsg, pszFilename, rc)); + return rc; +#else + return VINF_SUCCESS; +#endif +} + + +SUPR3DECL(int) SUPR3HardenedVerifySelf(const char *pszArgv0, bool fInternal, PRTERRINFO pErrInfo) +{ + /* + * Quick input validation. + */ + AssertPtr(pszArgv0); + RTErrInfoClear(pErrInfo); + + /* + * Get the executable image path as we need it for all the tests here. + */ + char szExecPath[RTPATH_MAX]; + if (!RTProcGetExecutablePath(szExecPath, sizeof(szExecPath))) + return RTErrInfoSet(pErrInfo, VERR_INTERNAL_ERROR_2, "RTProcGetExecutablePath failed"); + + int rc; + if (fInternal) + { + /* + * Internal applications must be launched directly without any PATH + * searching involved. + */ + if (RTPathCompare(pszArgv0, szExecPath) != 0) + return RTErrInfoSetF(pErrInfo, VERR_SUPLIB_INVALID_ARGV0_INTERNAL, + "argv[0] does not match the executable image path: '%s' != '%s'", pszArgv0, szExecPath); + + /* + * Internal applications must reside in or under the + * RTPathAppPrivateArch directory. + */ + char szAppPrivateArch[RTPATH_MAX]; + rc = RTPathAppPrivateArch(szAppPrivateArch, sizeof(szAppPrivateArch)); + if (RT_FAILURE(rc)) + return RTErrInfoSetF(pErrInfo, VERR_SUPLIB_INVALID_ARGV0_INTERNAL, + "RTPathAppPrivateArch failed with rc=%Rrc", rc); + size_t cchAppPrivateArch = strlen(szAppPrivateArch); + if ( cchAppPrivateArch >= strlen(szExecPath) + || !RTPATH_IS_SLASH(szExecPath[cchAppPrivateArch])) + return RTErrInfoSet(pErrInfo, VERR_SUPLIB_INVALID_INTERNAL_APP_DIR, + "Internal executable does reside under RTPathAppPrivateArch"); + szExecPath[cchAppPrivateArch] = '\0'; + if (RTPathCompare(szExecPath, szAppPrivateArch) != 0) + return RTErrInfoSet(pErrInfo, VERR_SUPLIB_INVALID_INTERNAL_APP_DIR, + "Internal executable does reside under RTPathAppPrivateArch"); + szExecPath[cchAppPrivateArch] = RTPATH_SLASH; + } + +#ifdef VBOX_WITH_HARDENING + /* + * Verify that the image file and parent directories are sane. + */ + rc = supR3HardenedVerifyFile(szExecPath, RTHCUINTPTR_MAX, false /*fMaybe3rdParty*/, pErrInfo); + if (RT_FAILURE(rc)) + return rc; +#endif + + return VINF_SUCCESS; +} + + +SUPR3DECL(int) SUPR3HardenedVerifyDir(const char *pszDirPath, bool fRecursive, bool fCheckFiles, PRTERRINFO pErrInfo) +{ + /* + * Quick input validation + */ + AssertPtr(pszDirPath); + RTErrInfoClear(pErrInfo); + + /* + * Only do the actual check in hardened builds. + */ +#ifdef VBOX_WITH_HARDENING + int rc = supR3HardenedVerifyDir(pszDirPath, fRecursive, fCheckFiles, pErrInfo); + if (RT_FAILURE(rc) && !RTErrInfoIsSet(pErrInfo)) + LogRel(("supR3HardenedVerifyDir: Verification of \"%s\" failed, rc=%Rrc\n", pszDirPath, rc)); + return rc; +#else + NOREF(pszDirPath); NOREF(fRecursive); NOREF(fCheckFiles); + return VINF_SUCCESS; +#endif +} + + +SUPR3DECL(int) SUPR3HardenedVerifyPlugIn(const char *pszFilename, PRTERRINFO pErrInfo) +{ + /* + * Quick input validation + */ + AssertPtr(pszFilename); + RTErrInfoClear(pErrInfo); + + /* + * Only do the actual check in hardened builds. + */ +#ifdef VBOX_WITH_HARDENING + int rc = supR3HardenedVerifyFile(pszFilename, RTHCUINTPTR_MAX, true /*fMaybe3rdParty*/, pErrInfo); + if (RT_FAILURE(rc) && !RTErrInfoIsSet(pErrInfo)) + LogRel(("supR3HardenedVerifyFile: Verification of \"%s\" failed, rc=%Rrc\n", pszFilename, rc)); + return rc; +#else + RT_NOREF1(pszFilename); + return VINF_SUCCESS; +#endif +} + + +SUPR3DECL(int) SUPR3GipGetPhys(PRTHCPHYS pHCPhys) +{ + if (g_pSUPGlobalInfoPage) + { + *pHCPhys = g_HCPhysSUPGlobalInfoPage; + return VINF_SUCCESS; + } + *pHCPhys = NIL_RTHCPHYS; + return VERR_WRONG_ORDER; +} + + +SUPR3DECL(int) SUPR3QueryVTxSupported(const char **ppszWhy) +{ + *ppszWhy = NULL; +#ifdef RT_OS_LINUX + return suplibOsQueryVTxSupported(ppszWhy); +#else + return VINF_SUCCESS; +#endif +} + + +SUPR3DECL(int) SUPR3QueryVTCaps(uint32_t *pfCaps) +{ + AssertPtrReturn(pfCaps, VERR_INVALID_POINTER); + + *pfCaps = 0; + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + return VINF_SUCCESS; + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPVTCAPS Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_VT_CAPS_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_VT_CAPS_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.Out.fCaps = 0; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_VT_CAPS, &Req, SUP_IOCTL_VT_CAPS_SIZE); + if (RT_SUCCESS(rc)) + { + rc = Req.Hdr.rc; + if (RT_SUCCESS(rc)) + *pfCaps = Req.u.Out.fCaps; + } + return rc; +} + + +SUPR3DECL(bool) SUPR3IsNemSupportedWhenNoVtxOrAmdV(void) +{ +#ifdef RT_OS_WINDOWS + return suplibOsIsNemSupportedWhenNoVtxOrAmdV(); +#else + return false; +#endif +} + + +SUPR3DECL(int) SUPR3QueryMicrocodeRev(uint32_t *uMicrocodeRev) +{ + AssertPtrReturn(uMicrocodeRev, VERR_INVALID_POINTER); + + *uMicrocodeRev = 0; + + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + return VINF_SUCCESS; + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPUCODEREV Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_UCODE_REV_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_UCODE_REV_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.Out.MicrocodeRev = 0; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_UCODE_REV, &Req, SUP_IOCTL_UCODE_REV_SIZE); + if (RT_SUCCESS(rc)) + { + rc = Req.Hdr.rc; + if (RT_SUCCESS(rc)) + *uMicrocodeRev = Req.u.Out.MicrocodeRev; + } + return rc; +} + + +SUPR3DECL(int) SUPR3TracerOpen(uint32_t uCookie, uintptr_t uArg) +{ + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + return VINF_SUCCESS; + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPTRACEROPEN Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie= g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_TRACER_OPEN_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_TRACER_OPEN_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.uCookie = uCookie; + Req.u.In.uArg = uArg; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TRACER_OPEN, &Req, SUP_IOCTL_TRACER_OPEN_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +SUPR3DECL(int) SUPR3TracerClose(void) +{ + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + return VINF_SUCCESS; + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPREQHDR Req; + Req.u32Cookie = g_u32Cookie; + Req.u32SessionCookie= g_u32SessionCookie; + Req.cbIn = SUP_IOCTL_TRACER_OPEN_SIZE_IN; + Req.cbOut = SUP_IOCTL_TRACER_OPEN_SIZE_OUT; + Req.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.rc = VERR_INTERNAL_ERROR; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TRACER_CLOSE, &Req, SUP_IOCTL_TRACER_CLOSE_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.rc; + return rc; +} + + +SUPR3DECL(int) SUPR3TracerIoCtl(uintptr_t uCmd, uintptr_t uArg, int32_t *piRetVal) +{ + /* fake */ + if (RT_UNLIKELY(g_uSupFakeMode)) + { + *piRetVal = -1; + return VERR_NOT_SUPPORTED; + } + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPTRACERIOCTL Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie= g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_TRACER_IOCTL_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_TRACER_IOCTL_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.uCmd = uCmd; + Req.u.In.uArg = uArg; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TRACER_IOCTL, &Req, SUP_IOCTL_TRACER_IOCTL_SIZE); + if (RT_SUCCESS(rc)) + { + rc = Req.Hdr.rc; + *piRetVal = Req.u.Out.iRetVal; + } + return rc; +} + + + +typedef struct SUPDRVTRACERSTRTAB +{ + /** Pointer to the string table. */ + char *pchStrTab; + /** The actual string table size. */ + uint32_t cbStrTab; + /** The original string pointers. */ + RTUINTPTR apszOrgFunctions[1]; +} SUPDRVTRACERSTRTAB, *PSUPDRVTRACERSTRTAB; + + +/** + * Destroys a string table, restoring the original pszFunction member valus. + * + * @param pThis The string table structure. + * @param paProbeLocs32 The probe location array, 32-bit type variant. + * @param paProbeLocs64 The probe location array, 64-bit type variant. + * @param cProbeLocs The number of elements in the array. + * @param f32Bit Set if @a paProbeLocs32 should be used, when + * clear use @a paProbeLocs64. + */ +static void supr3TracerDestroyStrTab(PSUPDRVTRACERSTRTAB pThis, PVTGPROBELOC32 paProbeLocs32, PVTGPROBELOC64 paProbeLocs64, + uint32_t cProbeLocs, bool f32Bit) +{ + /* Restore. */ + size_t i = cProbeLocs; + if (f32Bit) + while (i--) + paProbeLocs32[i].pszFunction = (uint32_t)pThis->apszOrgFunctions[i]; + else + while (i--) + paProbeLocs64[i].pszFunction = pThis->apszOrgFunctions[i]; + + /* Free. */ + RTMemFree(pThis->pchStrTab); + RTMemFree(pThis); +} + + +/** + * Creates a string table for the pszFunction members in the probe location + * array. + * + * This will save and replace the pszFunction members with offsets. + * + * @returns Pointer to a string table structure. NULL on failure. + * @param paProbeLocs32 The probe location array, 32-bit type variant. + * @param paProbeLocs64 The probe location array, 64-bit type variant. + * @param cProbeLocs The number of elements in the array. + * @param offDelta Relocation offset for the string pointers. + * @param f32Bit Set if @a paProbeLocs32 should be used, when + * clear use @a paProbeLocs64. + */ +static PSUPDRVTRACERSTRTAB supr3TracerCreateStrTab(PVTGPROBELOC32 paProbeLocs32, + PVTGPROBELOC64 paProbeLocs64, + uint32_t cProbeLocs, + RTUINTPTR offDelta, + bool f32Bit) +{ + if (cProbeLocs > _128K) + return NULL; + + /* + * Allocate the string table structures. + */ + size_t cbThis = RT_UOFFSETOF_DYN(SUPDRVTRACERSTRTAB, apszOrgFunctions[cProbeLocs]); + PSUPDRVTRACERSTRTAB pThis = (PSUPDRVTRACERSTRTAB)RTMemAlloc(cbThis); + if (!pThis) + return NULL; + + uint32_t const cHashBits = cProbeLocs * 2 - 1; + uint32_t *pbmHash = (uint32_t *)RTMemAllocZ(RT_ALIGN_32(cHashBits, 64) / 8 ); + if (!pbmHash) + { + RTMemFree(pThis); + return NULL; + } + + /* + * Calc the max string table size and save the orignal pointers so we can + * replace them later. + */ + size_t cbMax = 1; + for (uint32_t i = 0; i < cProbeLocs; i++) + { + pThis->apszOrgFunctions[i] = f32Bit ? paProbeLocs32[i].pszFunction : paProbeLocs64[i].pszFunction; + const char *pszFunction = (const char *)(uintptr_t)(pThis->apszOrgFunctions[i] + offDelta); + size_t cch = strlen(pszFunction); + if (cch > _1K) + { + cbMax = 0; + break; + } + cbMax += cch + 1; + } + + /* Alloc space for it. */ + if (cbMax > 0) + pThis->pchStrTab = (char *)RTMemAlloc(cbMax); + else + pThis->pchStrTab = NULL; + if (!pThis->pchStrTab) + { + RTMemFree(pbmHash); + RTMemFree(pThis); + return NULL; + } + + /* + * Create the string table. + */ + uint32_t off = 0; + uint32_t offPrev = 0; + + for (uint32_t i = 0; i < cProbeLocs; i++) + { + const char * const psz = (const char *)(uintptr_t)(pThis->apszOrgFunctions[i] + offDelta); + size_t const cch = strlen(psz); + uint32_t const iHashBit = RTStrHash1(psz) % cHashBits; + if (ASMBitTestAndSet(pbmHash, iHashBit)) + { + /* Often it's the most recent string. */ + if ( off - offPrev < cch + 1 + || memcmp(&pThis->pchStrTab[offPrev], psz, cch + 1)) + { + /* It wasn't, search the entire string table. (lazy bird) */ + offPrev = 0; + while (offPrev < off) + { + size_t cchCur = strlen(&pThis->pchStrTab[offPrev]); + if ( cchCur == cch + && !memcmp(&pThis->pchStrTab[offPrev], psz, cch + 1)) + break; + offPrev += (uint32_t)cchCur + 1; + } + } + } + else + offPrev = off; + + /* Add the string to the table. */ + if (offPrev >= off) + { + memcpy(&pThis->pchStrTab[off], psz, cch + 1); + offPrev = off; + off += (uint32_t)cch + 1; + } + + /* Update the entry */ + if (f32Bit) + paProbeLocs32[i].pszFunction = offPrev; + else + paProbeLocs64[i].pszFunction = offPrev; + } + + pThis->cbStrTab = off; + RTMemFree(pbmHash); + return pThis; +} + + + +SUPR3DECL(int) SUPR3TracerRegisterModule(uintptr_t hModNative, const char *pszModule, struct VTGOBJHDR *pVtgHdr, + RTUINTPTR uVtgHdrAddr, uint32_t fFlags) +{ + /* Validate input. */ + NOREF(hModNative); + AssertPtrReturn(pVtgHdr, VERR_INVALID_POINTER); + AssertReturn(!memcmp(pVtgHdr->szMagic, VTGOBJHDR_MAGIC, sizeof(pVtgHdr->szMagic)), VERR_SUPDRV_VTG_MAGIC); + AssertPtrReturn(pszModule, VERR_INVALID_POINTER); + size_t cchModule = strlen(pszModule); + AssertReturn(cchModule < RT_SIZEOFMEMB(SUPTRACERUMODREG, u.In.szName), VERR_FILENAME_TOO_LONG); + AssertReturn(!RTPathHavePath(pszModule), VERR_INVALID_PARAMETER); + AssertReturn(fFlags == SUP_TRACER_UMOD_FLAGS_EXE || fFlags == SUP_TRACER_UMOD_FLAGS_SHARED, VERR_INVALID_PARAMETER); + + /* + * Set the probe location array offset and size members. If the size is + * zero, don't bother ring-0 with it. + */ + if (!pVtgHdr->offProbeLocs) + { + uint64_t u64Tmp = pVtgHdr->uProbeLocsEnd.u64 - pVtgHdr->uProbeLocs.u64; + if (u64Tmp >= UINT32_MAX) + return VERR_SUPDRV_VTG_BAD_HDR_TOO_MUCH; + pVtgHdr->cbProbeLocs = (uint32_t)u64Tmp; + + u64Tmp = pVtgHdr->uProbeLocs.u64 - uVtgHdrAddr; + if ((int64_t)u64Tmp != (int32_t)u64Tmp) + { + LogRel(("SUPR3TracerRegisterModule: VERR_SUPDRV_VTG_BAD_HDR_PTR - u64Tmp=%#llx uProbeLocs=%#llx uVtgHdrAddr=%RTptr\n", + u64Tmp, pVtgHdr->uProbeLocs.u64, uVtgHdrAddr)); + return VERR_SUPDRV_VTG_BAD_HDR_PTR; + } + pVtgHdr->offProbeLocs = (int32_t)u64Tmp; + } + + if ( !pVtgHdr->cbProbeLocs + || !pVtgHdr->cbProbes) + return VINF_SUCCESS; + + /* + * Fake out. + */ + if (RT_UNLIKELY(g_uSupFakeMode)) + return VINF_SUCCESS; + + /* + * Create a string table for the function names in the location array. + * It's somewhat easier to do that here than from ring-0. + */ + uint32_t const cProbeLocs = pVtgHdr->cbProbeLocs + / (pVtgHdr->cBits == 32 ? sizeof(VTGPROBELOC32) : sizeof(VTGPROBELOC64)); + PVTGPROBELOC paProbeLocs = (PVTGPROBELOC)((uintptr_t)pVtgHdr + pVtgHdr->offProbeLocs); + PSUPDRVTRACERSTRTAB pStrTab = supr3TracerCreateStrTab((PVTGPROBELOC32)paProbeLocs, + (PVTGPROBELOC64)paProbeLocs, + cProbeLocs, (uintptr_t)pVtgHdr - uVtgHdrAddr, + pVtgHdr->cBits == 32); + if (!pStrTab) + return VERR_NO_MEMORY; + + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPTRACERUMODREG Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie= g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_TRACER_UMOD_REG_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_TRACER_UMOD_REG_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.uVtgHdrAddr = uVtgHdrAddr; + Req.u.In.R3PtrVtgHdr = pVtgHdr; + Req.u.In.R3PtrStrTab = pStrTab->pchStrTab; + Req.u.In.cbStrTab = pStrTab->cbStrTab; + Req.u.In.fFlags = fFlags; + + memcpy(Req.u.In.szName, pszModule, cchModule + 1); + if (!RTPathHasSuffix(Req.u.In.szName)) + { + /* Add the default suffix if none is given. */ + switch (fFlags & SUP_TRACER_UMOD_FLAGS_TYPE_MASK) + { +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + case SUP_TRACER_UMOD_FLAGS_EXE: + if (cchModule + sizeof(".exe") <= sizeof(Req.u.In.szName)) + strcpy(&Req.u.In.szName[cchModule], ".exe"); + break; +#endif + + case SUP_TRACER_UMOD_FLAGS_SHARED: + { + const char *pszSuff = RTLdrGetSuff(); + size_t cchSuff = strlen(pszSuff); + if (cchModule + cchSuff < sizeof(Req.u.In.szName)) + memcpy(&Req.u.In.szName[cchModule], pszSuff, cchSuff + 1); + break; + } + } + } + + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TRACER_UMOD_REG, &Req, SUP_IOCTL_TRACER_UMOD_REG_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + + supr3TracerDestroyStrTab(pStrTab, (PVTGPROBELOC32)paProbeLocs, (PVTGPROBELOC64)paProbeLocs, + cProbeLocs, pVtgHdr->cBits == 32); + return rc; +} + + +SUPR3DECL(int) SUPR3TracerDeregisterModule(struct VTGOBJHDR *pVtgHdr) +{ + /* Validate input. */ + AssertPtrReturn(pVtgHdr, VERR_INVALID_POINTER); + AssertReturn(!memcmp(pVtgHdr->szMagic, VTGOBJHDR_MAGIC, sizeof(pVtgHdr->szMagic)), VERR_SUPDRV_VTG_MAGIC); + + /* + * Don't bother if the object is empty. + */ + if ( !pVtgHdr->cbProbeLocs + || !pVtgHdr->cbProbes) + return VINF_SUCCESS; + + /* + * Fake out. + */ + if (RT_UNLIKELY(g_uSupFakeMode)) + return VINF_SUCCESS; + + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPTRACERUMODDEREG Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie= g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_TRACER_UMOD_REG_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_TRACER_UMOD_REG_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + Req.u.In.pVtgHdr = pVtgHdr; + + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TRACER_UMOD_DEREG, &Req, SUP_IOCTL_TRACER_UMOD_DEREG_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +DECLASM(void) suplibTracerFireProbe(PVTGPROBELOC pProbeLoc, PSUPTRACERUMODFIREPROBE pReq) +{ + RT_NOREF1(pProbeLoc); + + pReq->Hdr.u32Cookie = g_u32Cookie; + pReq->Hdr.u32SessionCookie = g_u32SessionCookie; + Assert(pReq->Hdr.cbIn == SUP_IOCTL_TRACER_UMOD_FIRE_PROBE_SIZE_IN); + Assert(pReq->Hdr.cbOut == SUP_IOCTL_TRACER_UMOD_FIRE_PROBE_SIZE_OUT); + pReq->Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + pReq->Hdr.rc = VINF_SUCCESS; + + suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TRACER_UMOD_FIRE_PROBE, pReq, SUP_IOCTL_TRACER_UMOD_FIRE_PROBE_SIZE); +} + + +SUPR3DECL(int) SUPR3MsrProberRead(uint32_t uMsr, RTCPUID idCpu, uint64_t *puValue, bool *pfGp) +{ + SUPMSRPROBER Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_MSR_PROBER_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_MSR_PROBER_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + + Req.u.In.enmOp = SUPMSRPROBEROP_READ; + Req.u.In.uMsr = uMsr; + Req.u.In.idCpu = idCpu == NIL_RTCPUID ? UINT32_MAX : idCpu; + + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_MSR_PROBER, &Req, SUP_IOCTL_MSR_PROBER_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + if (RT_SUCCESS(rc)) + { + if (puValue) + *puValue = Req.u.Out.uResults.Read.uValue; + if (pfGp) + *pfGp = Req.u.Out.uResults.Read.fGp; + } + + return rc; +} + + +SUPR3DECL(int) SUPR3MsrProberWrite(uint32_t uMsr, RTCPUID idCpu, uint64_t uValue, bool *pfGp) +{ + SUPMSRPROBER Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_MSR_PROBER_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_MSR_PROBER_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + + Req.u.In.enmOp = SUPMSRPROBEROP_WRITE; + Req.u.In.uMsr = uMsr; + Req.u.In.idCpu = idCpu == NIL_RTCPUID ? UINT32_MAX : idCpu; + Req.u.In.uArgs.Write.uToWrite = uValue; + + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_MSR_PROBER, &Req, SUP_IOCTL_MSR_PROBER_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + if (RT_SUCCESS(rc) && pfGp) + *pfGp = Req.u.Out.uResults.Write.fGp; + + return rc; +} + + +SUPR3DECL(int) SUPR3MsrProberModify(uint32_t uMsr, RTCPUID idCpu, uint64_t fAndMask, uint64_t fOrMask, + PSUPMSRPROBERMODIFYRESULT pResult) +{ + return SUPR3MsrProberModifyEx(uMsr, idCpu, fAndMask, fOrMask, false /*fFaster*/, pResult); +} + + +SUPR3DECL(int) SUPR3MsrProberModifyEx(uint32_t uMsr, RTCPUID idCpu, uint64_t fAndMask, uint64_t fOrMask, bool fFaster, + PSUPMSRPROBERMODIFYRESULT pResult) +{ + SUPMSRPROBER Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_MSR_PROBER_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_MSR_PROBER_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + + Req.u.In.enmOp = fFaster ? SUPMSRPROBEROP_MODIFY_FASTER : SUPMSRPROBEROP_MODIFY; + Req.u.In.uMsr = uMsr; + Req.u.In.idCpu = idCpu == NIL_RTCPUID ? UINT32_MAX : idCpu; + Req.u.In.uArgs.Modify.fAndMask = fAndMask; + Req.u.In.uArgs.Modify.fOrMask = fOrMask; + + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_MSR_PROBER, &Req, SUP_IOCTL_MSR_PROBER_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + if (RT_SUCCESS(rc)) + *pResult = Req.u.Out.uResults.Modify; + + return rc; +} + + +SUPR3DECL(int) SUPR3ResumeSuspendedKeyboards(void) +{ +#ifdef RT_OS_DARWIN + /* + * Issue IOCtl to the SUPDRV kernel module. + */ + SUPREQHDR Req; + Req.u32Cookie = g_u32Cookie; + Req.u32SessionCookie= g_u32SessionCookie; + Req.cbIn = SUP_IOCTL_RESUME_SUSPENDED_KBDS_SIZE_IN; + Req.cbOut = SUP_IOCTL_RESUME_SUSPENDED_KBDS_SIZE_OUT; + Req.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.rc = VERR_INTERNAL_ERROR; + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_RESUME_SUSPENDED_KBDS, &Req, SUP_IOCTL_RESUME_SUSPENDED_KBDS_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.rc; + return rc; +#else /* !RT_OS_DARWIN */ + return VERR_NOT_SUPPORTED; +#endif +} + + +SUPR3DECL(int) SUPR3TscDeltaMeasure(RTCPUID idCpu, bool fAsync, bool fForce, uint8_t cRetries, uint8_t cMsWaitRetry) +{ + SUPTSCDELTAMEASURE Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_TSC_DELTA_MEASURE_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_TSC_DELTA_MEASURE_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + + Req.u.In.cRetries = cRetries; + Req.u.In.fAsync = fAsync; + Req.u.In.fForce = fForce; + Req.u.In.idCpu = idCpu; + Req.u.In.cMsWaitRetry = cMsWaitRetry; + + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TSC_DELTA_MEASURE, &Req, SUP_IOCTL_TSC_DELTA_MEASURE_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +SUPR3DECL(int) SUPR3ReadTsc(uint64_t *puTsc, uint16_t *pidApic) +{ + AssertReturn(puTsc, VERR_INVALID_PARAMETER); + + SUPTSCREAD Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_TSC_READ_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_TSC_READ_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_TSC_READ, &Req, SUP_IOCTL_TSC_READ_SIZE); + if (RT_SUCCESS(rc)) + { + rc = Req.Hdr.rc; + *puTsc = Req.u.Out.u64AdjustedTsc; + if (pidApic) + *pidApic = Req.u.Out.idApic; + } + return rc; +} + + +SUPR3DECL(int) SUPR3GipSetFlags(uint32_t fOrMask, uint32_t fAndMask) +{ + AssertMsgReturn(!(fOrMask & ~SUPGIP_FLAGS_VALID_MASK), + ("fOrMask=%#x ValidMask=%#x\n", fOrMask, SUPGIP_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); + AssertMsgReturn((fAndMask & ~SUPGIP_FLAGS_VALID_MASK) == ~SUPGIP_FLAGS_VALID_MASK, + ("fAndMask=%#x ValidMask=%#x\n", fAndMask, SUPGIP_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); + + SUPGIPSETFLAGS Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_GIP_SET_FLAGS_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_GIP_SET_FLAGS_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + + Req.u.In.fAndMask = fAndMask; + Req.u.In.fOrMask = fOrMask; + + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_GIP_SET_FLAGS, &Req, SUP_IOCTL_GIP_SET_FLAGS_SIZE); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + return rc; +} + + +SUPR3DECL(int) SUPR3GetHwvirtMsrs(PSUPHWVIRTMSRS pHwvirtMsrs, bool fForceRequery) +{ + AssertReturn(pHwvirtMsrs, VERR_INVALID_PARAMETER); + + SUPGETHWVIRTMSRS Req; + Req.Hdr.u32Cookie = g_u32Cookie; + Req.Hdr.u32SessionCookie = g_u32SessionCookie; + Req.Hdr.cbIn = SUP_IOCTL_GET_HWVIRT_MSRS_SIZE_IN; + Req.Hdr.cbOut = SUP_IOCTL_GET_HWVIRT_MSRS_SIZE_OUT; + Req.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT; + Req.Hdr.rc = VERR_INTERNAL_ERROR; + + Req.u.In.fForce = fForceRequery; + Req.u.In.fReserved0 = false; + Req.u.In.fReserved1 = false; + Req.u.In.fReserved2 = false; + + int rc = suplibOsIOCtl(&g_supLibData, SUP_IOCTL_GET_HWVIRT_MSRS, &Req, SUP_IOCTL_GET_HWVIRT_MSRS_SIZE); + if (RT_SUCCESS(rc)) + { + rc = Req.Hdr.rc; + *pHwvirtMsrs = Req.u.Out.HwvirtMsrs; + } + else + RT_ZERO(*pHwvirtMsrs); + return rc; +} + |