/* $Id: SUPLib.cpp 54650 2015-03-05 13:42:10Z vboxsync $ */ /** @file * VirtualBox Support Library - Common code. */ /* * Copyright (C) 2006-2014 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 = */ NULL #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; /** VMMR0 Load Address. */ static RTR0PTR g_pvVMMR0 = NIL_RTR0PTR; /** 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 = ~0; /******************************************************************************* * Internal Functions * *******************************************************************************/ static int supInitFake(PSUPDRVSESSION *ppSession); static int supLoadModule(const char *pszFilename, const char *pszModule, const char *pszSrvReqHandler, void **ppvImageBase); static DECLCALLBACK(int) supLoadModuleResolveImport(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol, RTUINTPTR *pValue, void *pvUser); /** 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_OFFSETOF(SUPGLOBALINFOPAGE, u64NanoTSLastUpdateHz) & 0x7)); Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs) & 0x1f)); Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs[1]) & 0x1f)); Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64NanoTS) & 0x7)); Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64TSC) & 0x7)); Assert(!(RT_OFFSETOF(SUPGLOBALINFOPAGE, aCPUs[0].u64CpuHz) & 0x7)); /* * Check if already initialized. */ if (ppSession) *ppSession = g_pSession; if (g_cInits++ > 0) { 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) == 0x001f0000 ? 0x001f0004 : 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 }, { "RTLogRelDefaultInstance", 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); ASMAtomicWriteSize(&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); if (RT_LIKELY(uOperation == SUP_VMMR0_DO_RAW_RUN)) return suplibOsIOCtlFast(&g_supLibData, SUP_IOCTL_FAST_DO_RAW_RUN, idCpu); if (RT_LIKELY(uOperation == SUP_VMMR0_DO_HM_RUN)) return suplibOsIOCtlFast(&g_supLibData, SUP_IOCTL_FAST_DO_HM_RUN, idCpu); if (RT_LIKELY(uOperation == SUP_VMMR0_DO_NOP)) return suplibOsIOCtlFast(&g_supLibData, SUP_IOCTL_FAST_DO_NOP, idCpu); AssertMsgFailed(("%#x\n", uOperation)); return VERR_INTERNAL_ERROR; } 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; } /* * 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. */ /* * 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 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(void) { #ifdef RT_OS_LINUX return suplibOsQueryVTxSupported(); #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.Caps = 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.Caps; } 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 paProbLocs The probe location array. * @param cProbLocs The number of probe locations. */ 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 paProbLocs The probe location array. * @param cProbLocs The number of elements in the array. * @param cBits */ 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_OFFSETOF(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. */ size_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) { 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; }