diff options
Diffstat (limited to 'src/VBox/HostDrivers/Support/darwin')
-rw-r--r-- | src/VBox/HostDrivers/Support/darwin/Info.plist | 46 | ||||
-rw-r--r-- | src/VBox/HostDrivers/Support/darwin/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp | 2345 | ||||
-rw-r--r-- | src/VBox/HostDrivers/Support/darwin/SUPLib-darwin.cpp | 307 | ||||
-rw-r--r-- | src/VBox/HostDrivers/Support/darwin/SUPR0IdcClient-darwin.c | 56 | ||||
-rwxr-xr-x | src/VBox/HostDrivers/Support/darwin/load.sh | 154 | ||||
-rw-r--r-- | src/VBox/HostDrivers/Support/darwin/sys/dtrace_glue.h | 39 |
7 files changed, 2947 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/Support/darwin/Info.plist b/src/VBox/HostDrivers/Support/darwin/Info.plist new file mode 100644 index 00000000..e010a700 --- /dev/null +++ b/src/VBox/HostDrivers/Support/darwin/Info.plist @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> <string>English</string> + <key>CFBundleExecutable</key> <string>VBoxDrv</string> + <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxDrv</string> + <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> + <key>CFBundleName</key> <string>VBoxDrv</string> + <key>CFBundlePackageType</key> <string>KEXT</string> + <key>CFBundleSignature</key> <string>????</string> + <key>CFBundleGetInfoString</key> <string>@VBOX_PRODUCT@ @VBOX_VERSION_STRING@, © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</string> + <key>CFBundleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>CFBundleShortVersionString</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>OSBundleCompatibleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>IOKitPersonalities</key> + <dict> + <key>VBoxDrv</key> + <dict> + <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxDrv</string> + <key>IOClass</key> <string>org_virtualbox_SupDrv</string> + <key>IOMatchCategory</key> <string>org_virtualbox_SupDrv</string> + <key>IOProviderClass</key> <string>IOResources</string> + <key>IOResourceMatch</key> <string>IOKit</string> + <key>IOUserClientClass</key> <string>org_virtualbox_SupDrvClient</string> + </dict> + </dict> + <key>OSBundleLibraries</key> + <dict> + <key>com.apple.kpi.bsd</key> <string>9.0.0</string> + <key>com.apple.kpi.mach</key> <string>9.0.0</string> + <key>com.apple.kpi.libkern</key> <string>9.0.0</string> + <key>com.apple.kpi.unsupported</key> <string>9.0.0</string> + <key>com.apple.kpi.iokit</key> <string>9.0.0</string> + </dict> + <key>OSBundleLibraries_x86_64</key> + <dict> + <key>com.apple.kpi.bsd</key> <string>10.0.0d4</string> + <key>com.apple.kpi.mach</key> <string>10.0.0d3</string> + <key>com.apple.kpi.libkern</key> <string>10.0.0d3</string> + <key>com.apple.kpi.iokit</key> <string>10.0.0d3</string> + <key>com.apple.kpi.unsupported</key> <string>10.0.0d3</string> + </dict> +</dict> +</plist> + diff --git a/src/VBox/HostDrivers/Support/darwin/Makefile.kup b/src/VBox/HostDrivers/Support/darwin/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostDrivers/Support/darwin/Makefile.kup diff --git a/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp b/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp new file mode 100644 index 00000000..42b732f4 --- /dev/null +++ b/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp @@ -0,0 +1,2345 @@ +/* $Id: SUPDrv-darwin.cpp $ */ +/** @file + * VirtualBox Support Driver - Darwin Specific 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. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP_DRV +#include "../../../Runtime/r0drv/darwin/the-darwin-kernel.h" + +#include "../SUPDrvInternal.h" +#include <VBox/version.h> +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <iprt/asm-amd64-x86.h> +#include <iprt/ctype.h> +#include <iprt/dbg.h> +#include <iprt/initterm.h> +#include <iprt/file.h> +#include <iprt/ldr.h> +#include <iprt/mem.h> +#include <iprt/power.h> +#include <iprt/process.h> +#include <iprt/spinlock.h> +#include <iprt/semaphore.h> +#include <iprt/x86.h> +#include <iprt/crypto/applecodesign.h> +#include <iprt/crypto/store.h> +#include <iprt/crypto/pkcs7.h> +#include <iprt/crypto/x509.h> +#include <VBox/err.h> +#include <VBox/log.h> + +#include <mach/kmod.h> +#include <miscfs/devfs/devfs.h> +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/ioccom.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/kauth.h> +#include <IOKit/IOService.h> +#include <IOKit/IOUserClient.h> +#include <IOKit/pwr_mgt/RootDomain.h> +#include <IOKit/IODeviceTreeSupport.h> +#include <IOKit/usb/IOUSBHIDDriver.h> +#include <IOKit/bluetooth/IOBluetoothHIDDriver.h> +#include <IOKit/bluetooth/IOBluetoothHIDDriverTypes.h> + +#ifdef VBOX_WITH_HOST_VMX +# include <libkern/version.h> +RT_C_DECLS_BEGIN +# include <i386/vmx.h> +RT_C_DECLS_END +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ + +/** The system device node name. */ +#define DEVICE_NAME_SYS "vboxdrv" +/** The user device node name. */ +#define DEVICE_NAME_USR "vboxdrvu" + + +/** @name For debugging/whatever, now permanent. + * @{ */ +#define VBOX_PROC_SELFNAME_LEN 31 +#define VBOX_RETRIEVE_CUR_PROC_NAME(a_Name) char a_Name[VBOX_PROC_SELFNAME_LEN + 1]; \ + proc_selfname(a_Name, VBOX_PROC_SELFNAME_LEN) +/** @} */ + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +RT_C_DECLS_BEGIN +static kern_return_t VBoxDrvDarwinStart(struct kmod_info *pKModInfo, void *pvData); +static kern_return_t VBoxDrvDarwinStop(struct kmod_info *pKModInfo, void *pvData); +#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION +static int supdrvDarwinInitCertStores(PSUPDRVDEVEXT pDevExt); +static void supdrvDarwinDestroyCertStores(PSUPDRVDEVEXT pDevExt); +#endif + +static int VBoxDrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess); +static int VBoxDrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess); +static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess); +static int VBoxDrvDarwinIOCtlSMAP(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess); +static int VBoxDrvDarwinIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess); + +static int VBoxDrvDarwinErr2DarwinErr(int rc); + +static IOReturn VBoxDrvDarwinSleepHandler(void *pvTarget, void *pvRefCon, UInt32 uMessageType, IOService *pProvider, void *pvMessageArgument, vm_size_t argSize); +RT_C_DECLS_END + +static int vboxdrvDarwinResolveSymbols(void); +static bool vboxdrvDarwinCpuHasSMAP(void); + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * The service class. + * This is just a formality really. + */ +class org_virtualbox_SupDrv : public IOService +{ + OSDeclareDefaultStructors(org_virtualbox_SupDrv); + +public: + virtual bool init(OSDictionary *pDictionary = 0); + virtual void free(void); + virtual bool start(IOService *pProvider); + virtual void stop(IOService *pProvider); + virtual IOService *probe(IOService *pProvider, SInt32 *pi32Score); + virtual bool terminate(IOOptionBits fOptions); + + RTR0MEMEF_NEW_AND_DELETE_OPERATORS_IOKIT(); + +private: + /** Guard against the parent class growing and us using outdated headers. */ + uint8_t m_abSafetyPadding[256]; +}; + +OSDefineMetaClassAndStructors(org_virtualbox_SupDrv, IOService); + + +/** + * An attempt at getting that clientDied() notification. + * I don't think it'll work as I cannot figure out where/what creates the correct + * port right. + */ +class org_virtualbox_SupDrvClient : public IOUserClient +{ + OSDeclareDefaultStructors(org_virtualbox_SupDrvClient); + +private: + /** Guard against the parent class growing and us using outdated headers. */ + uint8_t m_abSafetyPadding[256]; + + PSUPDRVSESSION m_pSession; /**< The session. */ + task_t m_Task; /**< The client task. */ + org_virtualbox_SupDrv *m_pProvider; /**< The service provider. */ + +public: + virtual bool initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type); + virtual bool start(IOService *pProvider); + static void sessionClose(RTPROCESS Process); + virtual IOReturn clientClose(void); + virtual IOReturn clientDied(void); + virtual bool terminate(IOOptionBits fOptions = 0); + virtual bool finalize(IOOptionBits fOptions); + virtual void stop(IOService *pProvider); + + RTR0MEMEF_NEW_AND_DELETE_OPERATORS_IOKIT(); +}; + +OSDefineMetaClassAndStructors(org_virtualbox_SupDrvClient, IOUserClient); + + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** + * Declare the module stuff. + */ +RT_C_DECLS_BEGIN +extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData); +extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData); + +KMOD_EXPLICIT_DECL(VBoxDrv, VBOX_VERSION_STRING, _start, _stop) +DECLHIDDEN(kmod_start_func_t *) _realmain = VBoxDrvDarwinStart; +DECLHIDDEN(kmod_stop_func_t *) _antimain = VBoxDrvDarwinStop; +DECLHIDDEN(int) _kext_apple_cc = __APPLE_CC__; +RT_C_DECLS_END + + +/** + * Device extention & session data association structure. + */ +static SUPDRVDEVEXT g_DevExt; + +/** + * The character device switch table for the driver. + */ +static struct cdevsw g_DevCW = +{ + /** @todo g++ doesn't like this syntax - it worked with gcc before renaming to .cpp. */ + /*.d_open = */VBoxDrvDarwinOpen, + /*.d_close = */VBoxDrvDarwinClose, + /*.d_read = */eno_rdwrt, + /*.d_write = */eno_rdwrt, + /*.d_ioctl = */VBoxDrvDarwinIOCtl, + /*.d_stop = */eno_stop, + /*.d_reset = */eno_reset, + /*.d_ttys = */NULL, + /*.d_select= */eno_select, + /*.d_mmap = */eno_mmap, + /*.d_strategy = */eno_strat, + /*.d_getc = */(void *)(uintptr_t)&enodev, //eno_getc, + /*.d_putc = */(void *)(uintptr_t)&enodev, //eno_putc, + /*.d_type = */0 +}; + +/** Major device number. */ +static int g_iMajorDeviceNo = -1; +/** Registered devfs device handle for the system device. */ +static void *g_hDevFsDeviceSys = NULL; +/** Registered devfs device handle for the user device. */ +static void *g_hDevFsDeviceUsr = NULL; + +/** Spinlock protecting g_apSessionHashTab. */ +static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK; +/** Hash table */ +static PSUPDRVSESSION g_apSessionHashTab[19]; +/** Calculates the index into g_apSessionHashTab.*/ +#define SESSION_HASH(pid) ((pid) % RT_ELEMENTS(g_apSessionHashTab)) +/** The number of open sessions. */ +static int32_t volatile g_cSessions = 0; +/** The notifier handle for the sleep callback handler. */ +static IONotifier *g_pSleepNotifier = NULL; + +/** Pointer to vmx_suspend(). */ +static PFNRT g_pfnVmxSuspend = NULL; +/** Pointer to vmx_resume(). */ +static PFNRT g_pfnVmxResume = NULL; +/** Pointer to vmx_use_count. */ +static int volatile *g_pVmxUseCount = NULL; + +#ifdef SUPDRV_WITH_MSR_PROBER +/** Pointer to rdmsr_carefully if found. Returns 0 on success. */ +static int (*g_pfnRdMsrCarefully)(uint32_t uMsr, uint32_t *puLow, uint32_t *puHigh) = NULL; +/** Pointer to rdmsr64_carefully if found. Returns 0 on success. */ +static int (*g_pfnRdMsr64Carefully)(uint32_t uMsr, uint64_t *uValue) = NULL; +/** Pointer to wrmsr[64]_carefully if found. Returns 0 on success. */ +static int (*g_pfnWrMsr64Carefully)(uint32_t uMsr, uint64_t uValue) = NULL; +#endif + + +/** + * Start the kernel module. + */ +static kern_return_t VBoxDrvDarwinStart(struct kmod_info *pKModInfo, void *pvData) +{ + RT_NOREF(pKModInfo, pvData); +#ifdef DEBUG + printf("VBoxDrvDarwinStart\n"); +#endif + + /* + * Initialize IPRT. + */ + int rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + /* + * Initialize the device extension. + */ + rc = supdrvInitDevExt(&g_DevExt, sizeof(SUPDRVSESSION)); + if (RT_SUCCESS(rc)) + { +#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION + supdrvDarwinInitCertStores(&g_DevExt); +#endif + + /* + * Initialize the session hash table. + */ + memset(g_apSessionHashTab, 0, sizeof(g_apSessionHashTab)); /* paranoia */ + rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxDrvDarwin"); + if (RT_SUCCESS(rc)) + { + if (vboxdrvDarwinCpuHasSMAP()) + { + LogRel(("disabling SMAP for VBoxDrvDarwinIOCtl\n")); + g_DevCW.d_ioctl = VBoxDrvDarwinIOCtlSMAP; + } + + /* + * Resolve some extra kernel symbols. + */ + rc = vboxdrvDarwinResolveSymbols(); + if (RT_SUCCESS(rc)) + { + + /* + * Registering ourselves as a character device. + */ + g_iMajorDeviceNo = cdevsw_add(-1, &g_DevCW); + if (g_iMajorDeviceNo >= 0) + { +#ifdef VBOX_WITH_HARDENING + g_hDevFsDeviceSys = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR, + UID_ROOT, GID_WHEEL, 0600, DEVICE_NAME_SYS); +#else + g_hDevFsDeviceSys = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR, + UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME_SYS); +#endif + if (g_hDevFsDeviceSys) + { + g_hDevFsDeviceUsr = devfs_make_node(makedev(g_iMajorDeviceNo, 1), DEVFS_CHAR, + UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME_USR); + if (g_hDevFsDeviceUsr) + { + LogRel(("VBoxDrv: version " VBOX_VERSION_STRING " r%d; IOCtl version %#x; IDC version %#x; dev major=%d\n", + VBOX_SVN_REV, SUPDRV_IOC_VERSION, SUPDRV_IDC_VERSION, g_iMajorDeviceNo)); + + /* Register a sleep/wakeup notification callback */ + g_pSleepNotifier = registerPrioritySleepWakeInterest(&VBoxDrvDarwinSleepHandler, &g_DevExt, NULL); + if (g_pSleepNotifier == NULL) + LogRel(("VBoxDrv: register for sleep/wakeup events failed\n")); + + return KMOD_RETURN_SUCCESS; + } + + LogRel(("VBoxDrv: devfs_make_node(makedev(%d,1),,,,%s) failed\n", g_iMajorDeviceNo, DEVICE_NAME_USR)); + devfs_remove(g_hDevFsDeviceSys); + g_hDevFsDeviceSys = NULL; + } + else + LogRel(("VBoxDrv: devfs_make_node(makedev(%d,0),,,,%s) failed\n", g_iMajorDeviceNo, DEVICE_NAME_SYS)); + + cdevsw_remove(g_iMajorDeviceNo, &g_DevCW); + g_iMajorDeviceNo = -1; + } + else + LogRel(("VBoxDrv: cdevsw_add failed (%d)\n", g_iMajorDeviceNo)); + } +#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION + supdrvDarwinDestroyCertStores(&g_DevExt); +#endif + RTSpinlockDestroy(g_Spinlock); + g_Spinlock = NIL_RTSPINLOCK; + } + else + LogRel(("VBoxDrv: RTSpinlockCreate failed (rc=%d)\n", rc)); + supdrvDeleteDevExt(&g_DevExt); + } + else + printf("VBoxDrv: failed to initialize device extension (rc=%d)\n", rc); + RTR0TermForced(); + } + else + printf("VBoxDrv: failed to initialize IPRT (rc=%d)\n", rc); + + memset(&g_DevExt, 0, sizeof(g_DevExt)); + return KMOD_RETURN_FAILURE; +} + + +/** + * Resolves kernel symbols we need and some we just would like to have. + */ +static int vboxdrvDarwinResolveSymbols(void) +{ + RTDBGKRNLINFO hKrnlInfo; + int rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0); + if (RT_SUCCESS(rc)) + { + /* + * The VMX stuff - required with raw-mode (in theory for 64-bit on + * 32-bit too, but we never did that on darwin). + */ + int rc1 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "vmx_resume", (void **)&g_pfnVmxResume); + int rc2 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "vmx_suspend", (void **)&g_pfnVmxSuspend); + int rc3 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "vmx_use_count", (void **)&g_pVmxUseCount); + if (RT_SUCCESS(rc1) && RT_SUCCESS(rc2) && RT_SUCCESS(rc3)) + { + LogRel(("VBoxDrv: vmx_resume=%p vmx_suspend=%p vmx_use_count=%p (%d) cr4=%#x\n", + g_pfnVmxResume, g_pfnVmxSuspend, g_pVmxUseCount, *g_pVmxUseCount, ASMGetCR4() )); + } + else + { + LogRel(("VBoxDrv: failed to resolve vmx stuff: vmx_resume=%Rrc vmx_suspend=%Rrc vmx_use_count=%Rrc", rc1, rc2, rc3)); + g_pfnVmxResume = NULL; + g_pfnVmxSuspend = NULL; + g_pVmxUseCount = NULL; +#ifdef VBOX_WITH_RAW_MODE + rc = VERR_SYMBOL_NOT_FOUND; +#endif + } + + if (RT_SUCCESS(rc)) + { +#ifdef SUPDRV_WITH_MSR_PROBER + /* + * MSR prober stuff - optional! + */ + rc2 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "rdmsr_carefully", (void **)&g_pfnRdMsrCarefully); + if (RT_FAILURE(rc2)) + g_pfnRdMsrCarefully = NULL; + rc2 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "rdmsr64_carefully", (void **)&g_pfnRdMsr64Carefully); + if (RT_FAILURE(rc2)) + g_pfnRdMsr64Carefully = NULL; +# ifdef RT_ARCH_AMD64 /* Missing 64 in name, so if implemented on 32-bit it could have different signature. */ + rc2 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "wrmsr_carefully", (void **)&g_pfnWrMsr64Carefully); + if (RT_FAILURE(rc2)) +# endif + g_pfnWrMsr64Carefully = NULL; + + LogRel(("VBoxDrv: g_pfnRdMsrCarefully=%p g_pfnRdMsr64Carefully=%p g_pfnWrMsr64Carefully=%p\n", + g_pfnRdMsrCarefully, g_pfnRdMsr64Carefully, g_pfnWrMsr64Carefully)); + +#endif /* SUPDRV_WITH_MSR_PROBER */ + } + + RTR0DbgKrnlInfoRelease(hKrnlInfo); + } + else + LogRel(("VBoxDrv: Failed to open kernel symbols, rc=%Rrc\n", rc)); + return rc; +} + + +#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION + +/** + * Initalizes the certificate stores (code signing) in the device extension. + */ +static int supdrvDarwinInitCertStores(PSUPDRVDEVEXT pDevExt) +{ + pDevExt->hAdditionalStore = NIL_RTCRSTORE; + + pDevExt->hRootStore = NIL_RTCRSTORE; + int rc = RTCrStoreCreateInMem(&pDevExt->hRootStore, g_cSUPTrustedTAs + 1); + if (RT_SUCCESS(rc)) + { + for (uint32_t i = 0; i < g_cSUPTrustedTAs; i++) + { + int rc2 = RTCrStoreCertAddEncoded(pDevExt->hRootStore, RTCRCERTCTX_F_ENC_TAF_DER, + g_aSUPTrustedTAs[i].pch, g_aSUPTrustedTAs[i].cb, NULL); + if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) + { + printf("VBoxDrv: Error loading g_aSUPTrustedTAs[%u]: %d\n", i, rc); + rc = rc2; + } + } + + /* We implicitly trust the build certificate. */ + int rc2 = RTCrStoreCertAddEncoded(pDevExt->hRootStore, RTCRCERTCTX_F_ENC_X509_DER, + g_abSUPBuildCert, g_cbSUPBuildCert, NULL); + if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) + { + printf("VBoxDrv: Error loading g_cbSUPBuildCert: %d\n", rc); + rc = rc2; + } + } + return rc; +} + + +/** + * Releases the certificate stores in the device extension. + */ +static void supdrvDarwinDestroyCertStores(PSUPDRVDEVEXT pDevExt) +{ + if (pDevExt->hRootStore != NIL_RTCRSTORE) + { + uint32_t cRefs = RTCrStoreRelease(pDevExt->hRootStore); + Assert(cRefs == 0); RT_NOREF(cRefs); + pDevExt->hRootStore = NIL_RTCRSTORE; + } + if (pDevExt->hAdditionalStore != NIL_RTCRSTORE) + { + uint32_t cRefs = RTCrStoreRelease(pDevExt->hAdditionalStore); + Assert(cRefs == 0); RT_NOREF(cRefs); + pDevExt->hAdditionalStore = NIL_RTCRSTORE; + } +} + +#endif /* VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION */ + +/** + * Stop the kernel module. + */ +static kern_return_t VBoxDrvDarwinStop(struct kmod_info *pKModInfo, void *pvData) +{ + RT_NOREF(pKModInfo, pvData); + int rc; + LogFlow(("VBoxDrvDarwinStop\n")); + + /** @todo I've got a nagging feeling that we'll have to keep track of users and refuse + * unloading if we're busy. Investigate and implement this! */ + + /* + * Undo the work done during start (in reverse order). + */ + if (g_pSleepNotifier) + { + g_pSleepNotifier->remove(); + g_pSleepNotifier = NULL; + } + + devfs_remove(g_hDevFsDeviceUsr); + g_hDevFsDeviceUsr = NULL; + + devfs_remove(g_hDevFsDeviceSys); + g_hDevFsDeviceSys = NULL; + + rc = cdevsw_remove(g_iMajorDeviceNo, &g_DevCW); + Assert(rc == g_iMajorDeviceNo); + g_iMajorDeviceNo = -1; + + supdrvDeleteDevExt(&g_DevExt); + + rc = RTSpinlockDestroy(g_Spinlock); + AssertRC(rc); + g_Spinlock = NIL_RTSPINLOCK; + +#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION + supdrvDarwinDestroyCertStores(&g_DevExt); +#endif + + RTR0TermForced(); + + memset(&g_DevExt, 0, sizeof(g_DevExt)); +#ifdef DEBUG + printf("VBoxDrvDarwinStop - done\n"); +#endif + return KMOD_RETURN_SUCCESS; +} + + +/** + * Device open. Called on open /dev/vboxdrv + * + * @param Dev The device number. + * @param fFlags ???. + * @param fDevType ???. + * @param pProcess The process issuing this request. + */ +static int VBoxDrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess) +{ + RT_NOREF(fFlags, fDevType); +#ifdef DEBUG_DARWIN_GIP + char szName[128]; + szName[0] = '\0'; + proc_name(proc_pid(pProcess), szName, sizeof(szName)); + Log(("VBoxDrvDarwinOpen: pid=%d '%s'\n", proc_pid(pProcess), szName)); +#endif + + /* + * Only two minor devices numbers are allowed. + */ + if (minor(Dev) != 0 && minor(Dev) != 1) + return EACCES; + + /* + * The process issuing the request must be the current process. + */ + RTPROCESS Process = RTProcSelf(); + if ((int)Process != proc_pid(pProcess)) + return EIO; + + /* + * Find the session created by org_virtualbox_SupDrvClient, fail + * if no such session, and mark it as opened. We set the uid & gid + * here too, since that is more straight forward at this point. + */ + const bool fUnrestricted = minor(Dev) == 0; + int rc = VINF_SUCCESS; + PSUPDRVSESSION pSession = NULL; + kauth_cred_t pCred = kauth_cred_proc_ref(pProcess); + if (pCred) + { +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + RTUID Uid = kauth_cred_getruid(pCred); + RTGID Gid = kauth_cred_getrgid(pCred); +#else + RTUID Uid = pCred->cr_ruid; + RTGID Gid = pCred->cr_rgid; +#endif + unsigned iHash = SESSION_HASH(Process); + RTSpinlockAcquire(g_Spinlock); + + pSession = g_apSessionHashTab[iHash]; + while (pSession && pSession->Process != Process) + pSession = pSession->pNextHash; + if (pSession) + { + if (!pSession->fOpened) + { + pSession->fOpened = true; + pSession->fUnrestricted = fUnrestricted; + pSession->Uid = Uid; + pSession->Gid = Gid; + } + else + rc = VERR_ALREADY_LOADED; + } + else + rc = VERR_GENERAL_FAILURE; + + RTSpinlockRelease(g_Spinlock); +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 + kauth_cred_unref(&pCred); +#else /* 10.4 */ + /* The 10.4u SDK headers and 10.4.11 kernel source have inconsistent definitions + of kauth_cred_unref(), so use the other (now deprecated) API for releasing it. */ + kauth_cred_rele(pCred); +#endif /* 10.4 */ + } + else + rc = VERR_INVALID_PARAMETER; + +#ifdef DEBUG_DARWIN_GIP + OSDBGPRINT(("VBoxDrvDarwinOpen: pid=%d '%s' pSession=%p rc=%d\n", proc_pid(pProcess), szName, pSession, rc)); +#else + Log(("VBoxDrvDarwinOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, proc_pid(pProcess))); +#endif + return VBoxDrvDarwinErr2DarwinErr(rc); +} + + +/** + * Close device. + */ +static int VBoxDrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess) +{ + RT_NOREF(Dev, fFlags, fDevType, pProcess); + Log(("VBoxDrvDarwinClose: pid=%d\n", (int)RTProcSelf())); + Assert(proc_pid(pProcess) == (int)RTProcSelf()); + + /* + * Hand the session closing to org_virtualbox_SupDrvClient. + */ + org_virtualbox_SupDrvClient::sessionClose(RTProcSelf()); + return 0; +} + + +/** + * Device I/O Control entry point. + * + * @returns Darwin for slow IOCtls and VBox status code for the fast ones. + * @param Dev The device number (major+minor). + * @param iCmd The IOCtl command. + * @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)). + * @param fFlags Flag saying we're a character device (like we didn't know already). + * @param pProcess The process issuing this request. + */ +static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess) +{ + RT_NOREF(fFlags); + const bool fUnrestricted = minor(Dev) == 0; + const RTPROCESS Process = proc_pid(pProcess); + const unsigned iHash = SESSION_HASH(Process); + PSUPDRVSESSION pSession; + +#ifdef VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV + /* + * Refuse all I/O control calls if we've ever detected EFLAGS.AC being cleared. + * + * This isn't a problem, as there is absolutely nothing in the kernel context that + * depend on user context triggering cleanups. That would be pretty wild, right? + */ + if (RT_UNLIKELY(g_DevExt.cBadContextCalls > 0)) + { + SUPR0Printf("VBoxDrvDarwinIOCtl: EFLAGS.AC=0 detected %u times, refusing all I/O controls!\n", g_DevExt.cBadContextCalls); + return EDEVERR; + } +#endif + + /* + * Find the session. + */ + RTSpinlockAcquire(g_Spinlock); + + pSession = g_apSessionHashTab[iHash]; + while (pSession && (pSession->Process != Process || pSession->fUnrestricted != fUnrestricted || !pSession->fOpened)) + pSession = pSession->pNextHash; + + if (RT_LIKELY(pSession)) + supdrvSessionRetain(pSession); + + RTSpinlockRelease(g_Spinlock); + if (RT_UNLIKELY(!pSession)) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#lx\n", + (int)Process, iCmd)); + return EINVAL; + } + + /* + * Deal with the two high-speed IOCtl that takes it's arguments from + * the session and iCmd, and only returns a VBox status code. + */ + int rc; + AssertCompile((SUP_IOCTL_FAST_DO_FIRST & 0xff) == (SUP_IOCTL_FLAG | 64)); + if ( (uintptr_t)(iCmd - SUP_IOCTL_FAST_DO_FIRST) < (uintptr_t)32 + && fUnrestricted) + rc = supdrvIOCtlFast(iCmd - SUP_IOCTL_FAST_DO_FIRST, *(uint32_t *)pData, &g_DevExt, pSession); + else + rc = VBoxDrvDarwinIOCtlSlow(pSession, iCmd, pData, pProcess); + + supdrvSessionRelease(pSession); + return rc; +} + + +/** + * Alternative Device I/O Control entry point on hosts with SMAP support. + * + * @returns Darwin for slow IOCtls and VBox status code for the fast ones. + * @param Dev The device number (major+minor). + * @param iCmd The IOCtl command. + * @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)). + * @param fFlags Flag saying we're a character device (like we didn't know already). + * @param pProcess The process issuing this request. + */ +static int VBoxDrvDarwinIOCtlSMAP(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess) +{ + /* + * Allow VBox R0 code to touch R3 memory. Setting the AC bit disables the + * SMAP check. + */ + RTCCUINTREG fSavedEfl = ASMAddFlags(X86_EFL_AC); + + int rc = VBoxDrvDarwinIOCtl(Dev, iCmd, pData, fFlags, pProcess); + +#if defined(VBOX_STRICT) || defined(VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV) + /* + * Before we restore AC and the rest of EFLAGS, check if the IOCtl handler code + * accidentially modified it or some other important flag. + */ + if (RT_UNLIKELY( (ASMGetFlags() & (X86_EFL_AC | X86_EFL_IF | X86_EFL_DF | X86_EFL_IOPL)) + != ((fSavedEfl & (X86_EFL_AC | X86_EFL_IF | X86_EFL_DF | X86_EFL_IOPL)) | X86_EFL_AC) )) + { + char szTmp[48]; + RTStrPrintf(szTmp, sizeof(szTmp), "iCmd=%#x: %#x->%#x!", iCmd, (uint32_t)fSavedEfl, (uint32_t)ASMGetFlags()); + supdrvBadContext(&g_DevExt, "SUPDrv-darwin.cpp", __LINE__, szTmp); + } +#endif + + ASMSetFlags(fSavedEfl); + return rc; +} + + +/** + * Worker for VBoxDrvDarwinIOCtl that takes the slow IOCtl functions. + * + * @returns Darwin errno. + * + * @param pSession The session. + * @param iCmd The IOCtl command. + * @param pData Pointer to the kernel copy of the SUPDRVIOCTLDATA buffer. + * @param pProcess The calling process. + */ +static int VBoxDrvDarwinIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess) +{ + RT_NOREF(pProcess); + LogFlow(("VBoxDrvDarwinIOCtlSlow: pSession=%p iCmd=%p pData=%p pProcess=%p\n", pSession, iCmd, pData, pProcess)); + + + /* + * Buffered or unbuffered? + */ + PSUPREQHDR pHdr; + user_addr_t pUser = 0; + void *pvPageBuf = NULL; + uint32_t cbReq = IOCPARM_LEN(iCmd); + if ((IOC_DIRMASK & iCmd) == IOC_INOUT) + { + pHdr = (PSUPREQHDR)pData; + if (RT_UNLIKELY(cbReq < sizeof(*pHdr))) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: cbReq=%#x < %#x; iCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), iCmd)); + return EINVAL; + } + if (RT_UNLIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", pHdr->fFlags, iCmd)); + return EINVAL; + } + if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq + || pHdr->cbIn < sizeof(*pHdr) + || pHdr->cbOut < sizeof(*pHdr))) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x) != %#x; iCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, iCmd)); + return EINVAL; + } + } + else if ((IOC_DIRMASK & iCmd) == IOC_VOID && !cbReq) + { + /* + * Get the header and figure out how much we're gonna have to read. + */ + IPRT_DARWIN_SAVE_EFL_AC(); + SUPREQHDR Hdr; + pUser = (user_addr_t)*(void **)pData; + int rc = copyin(pUser, &Hdr, sizeof(Hdr)); + if (RT_UNLIKELY(rc)) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,Hdr,) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, rc, iCmd)); + IPRT_DARWIN_RESTORE_EFL_AC(); + return rc; + } + if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC)) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", Hdr.fFlags, iCmd)); + IPRT_DARWIN_RESTORE_EFL_AC(); + return EINVAL; + } + cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut); + if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) + || Hdr.cbOut < sizeof(Hdr) + || cbReq > _1M*16)) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x); iCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, iCmd)); + IPRT_DARWIN_RESTORE_EFL_AC(); + return EINVAL; + } + + /* + * Allocate buffer and copy in the data. + */ + pHdr = (PSUPREQHDR)RTMemTmpAlloc(cbReq); + if (!pHdr) + pvPageBuf = pHdr = (PSUPREQHDR)IOMallocAligned(RT_ALIGN_Z(cbReq, PAGE_SIZE), 8); + if (RT_UNLIKELY(!pHdr)) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: failed to allocate buffer of %d bytes; iCmd=%#lx\n", cbReq, iCmd)); + IPRT_DARWIN_RESTORE_EFL_AC(); + return ENOMEM; + } + rc = copyin(pUser, pHdr, Hdr.cbIn); + if (RT_UNLIKELY(rc)) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,%p,%#x) -> %#x; iCmd=%#lx\n", + (unsigned long long)pUser, pHdr, Hdr.cbIn, rc, iCmd)); + if (pvPageBuf) + IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); + else + RTMemTmpFree(pHdr); + IPRT_DARWIN_RESTORE_EFL_AC(); + return rc; + } + if (Hdr.cbIn < cbReq) + RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbReq - Hdr.cbIn); + IPRT_DARWIN_RESTORE_EFL_AC(); + } + else + { + Log(("VBoxDrvDarwinIOCtlSlow: huh? cbReq=%#x iCmd=%#lx\n", cbReq, iCmd)); + return EINVAL; + } + + /* + * Process the IOCtl. + */ + int rc = supdrvIOCtl(iCmd, &g_DevExt, pSession, pHdr, cbReq); + if (RT_LIKELY(!rc)) + { + /* + * If not buffered, copy back the buffer before returning. + */ + if (pUser) + { + IPRT_DARWIN_SAVE_EFL_AC(); + uint32_t cbOut = pHdr->cbOut; + if (cbOut > cbReq) + { + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, iCmd)); + cbOut = cbReq; + } + rc = copyout(pHdr, pUser, cbOut); + if (RT_UNLIKELY(rc)) + OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyout(%p,%llx,%#x) -> %d; uCmd=%#lx!\n", + pHdr, (unsigned long long)pUser, cbOut, rc, iCmd)); + + /* cleanup */ + if (pvPageBuf) + IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); + else + RTMemTmpFree(pHdr); + IPRT_DARWIN_RESTORE_EFL_AC(); + } + } + else + { + /* + * The request failed, just clean up. + */ + if (pUser) + { + if (pvPageBuf) + { + IPRT_DARWIN_SAVE_EFL_AC(); + IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); + IPRT_DARWIN_RESTORE_EFL_AC(); + } + else + RTMemTmpFree(pHdr); + } + + Log(("VBoxDrvDarwinIOCtlSlow: pid=%d iCmd=%lx pData=%p failed, rc=%d\n", proc_pid(pProcess), iCmd, (void *)pData, rc)); + rc = EINVAL; + } + + Log2(("VBoxDrvDarwinIOCtlSlow: returns %d\n", rc)); + return rc; +} + + +/** + * The SUPDRV IDC entry point. + * + * @returns VBox status code, see supdrvIDC. + * @param uReq The request code. + * @param pReq The request. + */ +DECLEXPORT(int) VBOXCALL SUPDrvDarwinIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq) +{ + PSUPDRVSESSION pSession; + + /* + * Some quick validations. + */ + if (RT_UNLIKELY(!VALID_PTR(pReq))) + return VERR_INVALID_POINTER; + + pSession = pReq->pSession; + if (pSession) + { + if (RT_UNLIKELY(!VALID_PTR(pSession))) + return VERR_INVALID_PARAMETER; + if (RT_UNLIKELY(pSession->pDevExt != &g_DevExt)) + return VERR_INVALID_PARAMETER; + } + else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT)) + return VERR_INVALID_PARAMETER; + + /* + * Do the job. + */ + return supdrvIDC(uReq, &g_DevExt, pSession, pReq); +} + + +void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession) +{ + NOREF(pDevExt); + NOREF(pSession); +} + + +void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser) +{ + NOREF(pDevExt); NOREF(pSession); NOREF(pvUser); +} + + +void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser) +{ + NOREF(pDevExt); NOREF(pSession); NOREF(pvUser); +} + + +/** + * Initializes any OS specific object creator fields. + */ +void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession) +{ + NOREF(pObj); + NOREF(pSession); +} + + +/** + * Checks if the session can access the object. + * + * @returns true if a decision has been made. + * @returns false if the default access policy should be applied. + * + * @param pObj The object in question. + * @param pSession The session wanting to access the object. + * @param pszObjName The object name, can be NULL. + * @param prc Where to store the result when returning true. + */ +bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc) +{ + NOREF(pObj); + NOREF(pSession); + NOREF(pszObjName); + NOREF(prc); + return false; +} + +/** + * Callback for blah blah blah. + */ +IOReturn VBoxDrvDarwinSleepHandler(void * /* pvTarget */, void *pvRefCon, UInt32 uMessageType, + IOService *pProvider, void *pvMsgArg, vm_size_t cbMsgArg) +{ + RT_NOREF(pProvider, pvMsgArg, cbMsgArg); + LogFlow(("VBoxDrv: Got sleep/wake notice. Message type was %x\n", uMessageType)); + + if (uMessageType == kIOMessageSystemWillSleep) + RTPowerSignalEvent(RTPOWEREVENT_SUSPEND); + else if (uMessageType == kIOMessageSystemHasPoweredOn) + RTPowerSignalEvent(RTPOWEREVENT_RESUME); + + acknowledgeSleepWakeNotification(pvRefCon); + + return 0; +} + + +#ifdef VBOX_WITH_HOST_VMX +/** + * For cleaning up the mess we left behind on Yosemite with 4.3.28 and earlier. + * + * We ASSUME VT-x is supported by the CPU. + * + * @param idCpu Unused. + * @param pvUser1 Unused. + * @param pvUser2 Unused. + */ +static DECLCALLBACK(void) vboxdrvDarwinVmxEnableFix(RTCPUID idCpu, void *pvUser1, void *pvUser2) +{ + RT_NOREF(idCpu, pvUser1, pvUser2); + RTCCUINTREG uCr4 = ASMGetCR4(); + if (!(uCr4 & X86_CR4_VMXE)) + { + uCr4 |= X86_CR4_VMXE; + ASMSetCR4(uCr4); + } +} +#endif + + +/** + * @copydoc SUPR0EnableVTx + */ +int VBOXCALL supdrvOSEnableVTx(bool fEnable) +{ +#ifdef VBOX_WITH_HOST_VMX + int rc; + if ( version_major >= 10 /* 10 = 10.6.x = Snow Leopard */ +# ifdef VBOX_WITH_RAW_MODE + && g_pfnVmxSuspend + && g_pfnVmxResume + && g_pVmxUseCount +# endif + ) + { + IPRT_DARWIN_SAVE_EFL_AC(); + if (fEnable) + { + /* + * We screwed up on Yosemite and didn't notice that we weren't + * calling host_vmxon. CR4.VMXE may therefore have been disabled + * by us. So, first time around we make sure it's set so we won't + * crash in the pre-4.3.28/5.0RC1 upgrade scenario. + * See @bugref{7907}. + */ + static bool volatile g_fDoneCleanup = false; + if (!g_fDoneCleanup) + { + if (version_major == 14 /* 14 = 10.10 = yosemite */) + { + uint32_t fCaps; + rc = supdrvQueryVTCapsInternal(&fCaps); + if (RT_SUCCESS(rc)) + { + if (fCaps & SUPVTCAPS_VT_X) + rc = RTMpOnAll(vboxdrvDarwinVmxEnableFix, NULL, NULL); + else + rc = VERR_VMX_NO_VMX; + } + if (RT_FAILURE(rc)) + { + IPRT_DARWIN_RESTORE_EFL_AC(); + return rc; + } + } + g_fDoneCleanup = true; + } + + /* + * Call the kernel. + */ + AssertLogRelMsg(!g_pVmxUseCount || *g_pVmxUseCount >= 0, + ("vmx_use_count=%d (@ %p, expected it to be a positive number\n", + *g_pVmxUseCount, g_pVmxUseCount)); + + rc = host_vmxon(false /* exclusive */); + if (rc == VMX_OK) + rc = VINF_SUCCESS; + else if (rc == VMX_UNSUPPORTED) + rc = VERR_VMX_NO_VMX; + else if (rc == VMX_INUSE) + rc = VERR_VMX_IN_VMX_ROOT_MODE; + else /* shouldn't happen, but just in case. */ + { + LogRel(("host_vmxon returned %d\n", rc)); + rc = VERR_UNRESOLVED_ERROR; + } + LogRel(("VBoxDrv: host_vmxon -> vmx_use_count=%d rc=%Rrc\n", *g_pVmxUseCount, rc)); + } + else + { + AssertLogRelMsgReturn(!g_pVmxUseCount || *g_pVmxUseCount >= 1, + ("vmx_use_count=%d (@ %p, expected it to be a non-zero positive number\n", + *g_pVmxUseCount, g_pVmxUseCount), + VERR_WRONG_ORDER); + host_vmxoff(); + rc = VINF_SUCCESS; + LogRel(("VBoxDrv: host_vmxoff -> vmx_use_count=%d\n", *g_pVmxUseCount)); + } + IPRT_DARWIN_RESTORE_EFL_AC(); + } + else + { + /* In 10.5.x the host_vmxon is severely broken! Don't use it, it will + frequnetly panic the host. */ + rc = VERR_NOT_SUPPORTED; + } + return rc; +#else + return VERR_NOT_SUPPORTED; +#endif +} + + +/** + * @copydoc SUPR0SuspendVTxOnCpu + */ +bool VBOXCALL supdrvOSSuspendVTxOnCpu(void) +{ +#ifdef VBOX_WITH_HOST_VMX + /* + * Consult the VMX usage counter, don't try suspend if not enabled. + * + * Note! The host_vmxon/off code is still race prone since, but this is + * currently the best we can do without always enable VMX when + * loading the driver. + */ + if ( g_pVmxUseCount + && *g_pVmxUseCount > 0) + { + IPRT_DARWIN_SAVE_EFL_AC(); + g_pfnVmxSuspend(); + IPRT_DARWIN_RESTORE_EFL_AC(); + return true; + } + return false; +#else + return false; +#endif +} + + +/** + * @copydoc SUPR0ResumeVTxOnCpu + */ +void VBOXCALL supdrvOSResumeVTxOnCpu(bool fSuspended) +{ +#ifdef VBOX_WITH_HOST_VMX + /* + * Don't consult the counter here, the state knows better. + * We're executing with interrupts disabled and anyone racing us with + * disabling VT-x will be waiting in the rendezvous code. + */ + if ( fSuspended + && g_pfnVmxResume) + { + IPRT_DARWIN_SAVE_EFL_AC(); + g_pfnVmxResume(); + IPRT_DARWIN_RESTORE_EFL_AC(); + } + else + Assert(!fSuspended); +#else + Assert(!fSuspended); +#endif +} + + +bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt) +{ + NOREF(pDevExt); + return false; +} + + +bool VBOXCALL supdrvOSAreCpusOfflinedOnSuspend(void) +{ + /** @todo verify this. */ + return false; +} + + +bool VBOXCALL supdrvOSAreTscDeltasInSync(void) +{ + return false; +} + + +#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION + +/** + * @callback_method_impl{FNRTLDRIMPORT} + */ +static DECLCALLBACK(int) supdrvDarwinLdrOpenImportCallback(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, + unsigned uSymbol, PRTLDRADDR pValue, void *pvUser) +{ + PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser; + + /* + * First consult the VMMR0 module if there is one fully loaded. + * This is necessary as VMMR0 may overload assertion and logger symbols. + */ + if (pDevExt->pvVMMR0) + for (PSUPDRVLDRIMAGE pImage = pDevExt->pLdrImages; pImage; pImage = pImage->pNext) + if (pImage->pvImage == pDevExt->pvVMMR0) + { + if ( pImage->uState == SUP_IOCTL_LDR_LOAD + && pImage->hLdrMod != NIL_RTLDRMOD) + { + int rc = RTLdrGetSymbolEx(pImage->hLdrMod, pImage->pvImage, (uintptr_t)pImage->pvImage, + UINT32_MAX, pszSymbol, pValue); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + } + break; + } + + /* + * Then we consult the SUPDrv export table. + */ + uintptr_t uValue = 0; + int rc = supdrvLdrGetExportedSymbol(pszSymbol, &uValue); + if (RT_SUCCESS(rc)) + { + *pValue = uValue; + return VINF_SUCCESS; + } + + /* + * Failed. + */ + printf("VBoxDrv: Unable to resolve symbol '%s'.\n", pszSymbol); + RT_NOREF(hLdrMod, pszModule, uSymbol); + return VERR_SYMBOL_NOT_FOUND; +} + + +/** + * @callback_method_impl{FNRTCRPKCS7VERIFYCERTCALLBACK, + * Verify that the signing certificate is sane.} + */ +static DECLCALLBACK(int) supdrvDarwinLdrOpenVerifyCertificatCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, + uint32_t fFlags, void *pvUser, PRTERRINFO pErrInfo) +{ + RT_NOREF(pvUser); //PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser; +# ifdef DEBUG_bird + printf("supdrvDarwinLdrOpenVerifyCertificatCallback: pCert=%p hCertPaths=%p\n", pCert, hCertPaths); +# endif + +# if 0 + /* + * Test signing certificates normally doesn't have all the necessary + * features required below. So, treat them as special cases. + */ + if ( hCertPaths == NIL_RTCRX509CERTPATHS + && RTCrX509Name_Compare(&pCert->TbsCertificate.Issuer, &pCert->TbsCertificate.Subject) == 0) + { + RTMsgInfo("Test signed.\n"); + return VINF_SUCCESS; + } +# endif + + /* + * Standard code signing capabilites required. + */ + int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, fFlags, NULL, pErrInfo); + if ( RT_SUCCESS(rc) + && (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA)) + { + uint32_t cDevIdApp = 0; + uint32_t cDevIdKext = 0; + for (uint32_t i = 0; i < pCert->TbsCertificate.T3.Extensions.cItems; i++) + { + PCRTCRX509EXTENSION pExt = pCert->TbsCertificate.T3.Extensions.papItems[i]; + if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_APPLICATION_OID) == 0) + { + cDevIdApp++; + if (!pExt->Critical.fValue) + rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE, + "Dev ID Application certificate extension is not flagged critical"); + } + else if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_KEXT_OID) == 0) + { + cDevIdKext++; + if (!pExt->Critical.fValue) + rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE, + "Dev ID kext certificate extension is not flagged critical"); + } + } + if (cDevIdApp == 0) + rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE, + "Certificate is missing the 'Dev ID Application' extension"); + if (cDevIdKext == 0) + rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE, + "Certificate is missing the 'Dev ID kext' extension"); + } + + return rc; +} + + +/** + * @callback_method_impl{FNRTLDRVALIDATESIGNEDDATA} + */ +static DECLCALLBACK(int) supdrvDarwinLdrOpenVerifyCallback(RTLDRMOD hLdrMod, RTLDRSIGNATURETYPE enmSignature, + void const *pvSignature, size_t cbSignature, + void const *pvExternalData, size_t cbExternalData, + PRTERRINFO pErrInfo, void *pvUser) +{ + PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser; + RT_NOREF_PV(hLdrMod); RT_NOREF_PV(cbSignature); + + switch (enmSignature) + { + case RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA: + if (pvExternalData) + { + PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pvSignature; + RTTIMESPEC ValidationTime; + RTTimeNow(&ValidationTime); + + return RTCrPkcs7VerifySignedDataWithExternalData(pContentInfo, + RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY + | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT + | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT, + pDevExt->hAdditionalStore, pDevExt->hRootStore, &ValidationTime, + supdrvDarwinLdrOpenVerifyCertificatCallback, pDevExt, + pvExternalData, cbExternalData, pErrInfo); + } + return RTErrInfoSetF(pErrInfo, VERR_NOT_SUPPORTED, "Expected external data with signature!"); + + default: + return RTErrInfoSetF(pErrInfo, VERR_NOT_SUPPORTED, "Unsupported signature type: %d", enmSignature); + } +} + +#endif /* VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION */ + +int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename) +{ +#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION + /* + * Initialize our members. + */ + pImage->hLdrMod = NIL_RTLDRMOD; + pImage->hMemAlloc = NIL_RTR0MEMOBJ; + + /* + * We have to double buffer the file to be avoid a potential race between + * validation and actual image loading. This could be eliminated later by + * baking the image validation into the RTLdrGetBits operation. + * + * Note! After calling RTLdrOpenInMemory, pvFile is owned by the loader and will be + * freed via the RTFileReadAllFree callback when the loader module is closed. + */ + void *pvFile = NULL; + size_t cbFile = 0; + int rc = RTFileReadAllEx(pszFilename, 0, _32M, RTFILE_RDALL_O_DENY_WRITE, &pvFile, &cbFile); + if (RT_SUCCESS(rc)) + { + PRTERRINFOSTATIC pErrInfo = (PRTERRINFOSTATIC)RTMemTmpAlloc(sizeof(RTERRINFOSTATIC)); + RTLDRMOD hLdrMod = NIL_RTLDRMOD; + rc = RTLdrOpenInMemory(pszFilename, 0 /*fFlags*/, RTLDRARCH_HOST, cbFile, + NULL /*pfnRead*/, RTFileReadAllFree, pvFile, + &hLdrMod, pErrInfo ? RTErrInfoInitStatic(pErrInfo) : NULL); + if (RT_SUCCESS(rc)) + { + /* + * Validate the image. + */ + rc = RTLdrVerifySignature(hLdrMod, supdrvDarwinLdrOpenVerifyCallback, pDevExt, + pErrInfo ? RTErrInfoInitStatic(pErrInfo) : NULL); + if (RT_SUCCESS(rc)) + { + /* + * Allocate memory for the object and load it into it. + */ + size_t cbImage = RTLdrSize(hLdrMod); + if (cbImage == pImage->cbImageBits) + { + RTR0MEMOBJ hMemAlloc; + rc = RTR0MemObjAllocPage(&hMemAlloc, cbImage, true /*fExecutable*/); + if (RT_SUCCESS(rc)) + { + void *pvImageBits = RTR0MemObjAddress(hMemAlloc); + rc = RTLdrGetBits(hLdrMod, pvImageBits, (uintptr_t)pvImageBits, + supdrvDarwinLdrOpenImportCallback, pDevExt); + if (RT_SUCCESS(rc)) + { + /* + * Commit. + */ + pImage->hMemAlloc = hMemAlloc; + pImage->hLdrMod = hLdrMod; + pImage->pvImage = pvImageBits; + RTMemTmpFree(pErrInfo); + /** @todo Call RTLdrDone. */ + return VINF_SUCCESS; + } + + RTR0MemObjFree(hMemAlloc, true /*fFreeMappings*/); + } + else + printf("VBoxDrv: Failed to allocate %u bytes for %s: %d\n", (unsigned)cbImage, pszFilename, rc); + } + else + { + printf("VBoxDrv: Image size mismatch for %s: %#x, ring-3 says %#x\n", + pszFilename, (unsigned)cbImage, (unsigned)pImage->cbImageBits); + rc = VERR_LDR_MISMATCH_NATIVE; + } + } + else if (pErrInfo && RTErrInfoIsSet(&pErrInfo->Core)) + printf("VBoxDrv: RTLdrOpenInMemory(%s) failed: %d - %s\n", pszFilename, rc, pErrInfo->Core.pszMsg); + else + printf("VBoxDrv: RTLdrOpenInMemory(%s) failed: %d\n", pszFilename, rc); + RTLdrClose(hLdrMod); + } + else if (pErrInfo && RTErrInfoIsSet(&pErrInfo->Core)) + printf("VBoxDrv: RTLdrOpenInMemory(%s) failed: %d - %s\n", pszFilename, rc, pErrInfo->Core.pszMsg); + else + printf("VBoxDrv: RTLdrOpenInMemory(%s) failed: %d\n", pszFilename, rc); + RTMemTmpFree(pErrInfo); + } + return rc; +#else /* !VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION */ + NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename); + return VERR_NOT_SUPPORTED; +#endif /* !VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION */ +} + + +#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION +/** + * @callback_method_impl{FNRTLDRENUMSYMS, + * Worker for supdrvOSLdrValidatePointer. + */ +static DECLCALLBACK(int) supdrvDarwinLdrValidatePointerCallback(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, + RTLDRADDR Value, void *pvUser) +{ + RT_NOREF(hLdrMod, pszSymbol, uSymbol); + if (Value == (uintptr_t)pvUser) + return VINF_CALLBACK_RETURN; + return VINF_SUCCESS; +} +#endif + + +int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv, + const uint8_t *pbImageBits, const char *pszSymbol) +{ +#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION + AssertReturn(pImage->hLdrMod != NIL_RTLDRMOD, VERR_INVALID_STATE); + + /* + * If we've got a symbol name, just to a lookup and compare addresses. + */ + int rc; + if (RT_C_IS_UPPER(*pszSymbol)) + { + RTLDRADDR uValueFound; + rc = RTLdrGetSymbolEx(pImage->hLdrMod, pImage->pvImage, (uintptr_t)pImage->pvImage, UINT32_MAX, pszSymbol, &uValueFound); + if (RT_SUCCESS(rc)) + { + if (uValueFound == (uintptr_t)pv) + rc = VINF_SUCCESS; + else + { + SUPR0Printf("SUPDrv: Different exports found for %s in %s: %RTptr, expected %p\n", + pszSymbol, pImage->szName, (RTUINTPTR)uValueFound, pv); + rc = VERR_LDR_BAD_FIXUP; + } + } + else + SUPR0Printf("SUPDrv: No export named %s (%p) in %s!\n", pszSymbol, pv, pImage->szName); + } + /* + * Otherwise do a symbol enumeration and look for the entrypoint. + */ + else + { + rc = RTLdrEnumSymbols(pImage->hLdrMod, 0 /*fFlags*/, pImage->pvImage, (uintptr_t)pImage->pvImage, + supdrvDarwinLdrValidatePointerCallback, pv); + if (rc == VINF_CALLBACK_RETURN) + rc = VINF_SUCCESS; + else if (RT_SUCCESS(rc)) + { + SUPR0Printf("SUPDrv: No export with address %p (%s) in %s!\n", pv, pszSymbol, pImage->szName); + rc = VERR_NOT_FOUND; + } + else + SUPR0Printf("SUPDrv: RTLdrEnumSymbols failed on %s: %Rrc\n", pImage->szName, rc); + } + RT_NOREF(pDevExt, pbImageBits); + return rc; +#else + NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits); NOREF(pszSymbol); + return VERR_NOT_SUPPORTED; +#endif +} + + +int VBOXCALL supdrvOSLdrQuerySymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, + const char *pszSymbol, size_t cchSymbol, void **ppvSymbol) +{ +#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION + /* + * Just hand the problem to RTLdrGetSymbolEx. + */ + RTLDRADDR uValueFound; + int rc = RTLdrGetSymbolEx(pImage->hLdrMod, pImage->pvImage, (uintptr_t)pImage->pvImage, UINT32_MAX, pszSymbol, &uValueFound); + if (RT_SUCCESS(rc)) + { + *ppvSymbol = (void *)(uintptr_t)uValueFound; + return VINF_SUCCESS; + } + RT_NOREF(pDevExt, cchSymbol); + return rc; + +#else + RT_NOREF(pDevExt, pImage, pszSymbol, cchSymbol, ppvSymbol); + return VERR_WRONG_ORDER; +#endif +} + + +int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq) +{ +#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION + /* State paranoia. */ + AssertReturn(pImage->hLdrMod != NIL_RTLDRMOD, VERR_INVALID_STATE); + AssertReturn(pImage->hMemAlloc != NIL_RTR0MEMOBJ, VERR_INVALID_STATE); + AssertReturn(pImage->pvImage, VERR_INVALID_STATE); + + /* + * We should get an identical match with ring-3 here, so the code here is + * trivial in comparision to SUPDrv-win.cpp. + */ + if (!memcmp(pImage->pvImage, pbImageBits, pImage->cbImageBits)) + return VINF_SUCCESS; + + /* + * Try show what when wrong (code is copied from supdrvNtCompare). + */ + uint32_t cbLeft = pImage->cbImageBits; + const uint8_t *pbNativeBits = (const uint8_t *)pImage->pvImage; + for (size_t off = 0; cbLeft > 0; off++, cbLeft--) + if (pbNativeBits[off] != pbImageBits[off]) + { + /* Note! We need to copy image bits into a temporary stack buffer here as we'd + otherwise risk overwriting them while formatting the error message. */ + uint8_t abBytes[64]; + memcpy(abBytes, &pbImageBits[off], RT_MIN(64, cbLeft)); + supdrvLdrLoadError(VERR_LDR_MISMATCH_NATIVE, pReq, + "Mismatch at %#x (%p) of %s loaded at %p:\n" + "ring-0: %.*Rhxs\n" + "ring-3: %.*Rhxs", + off, &pbNativeBits[off], pImage->szName, pImage->pvImage, + RT_MIN(64, cbLeft), &pbNativeBits[off], + RT_MIN(64, cbLeft), &abBytes[0]); + printf("SUPDrv: %s\n", pReq->u.Out.szError); + break; + } + + RT_NOREF(pDevExt); + return VERR_LDR_MISMATCH_NATIVE; + +#else + NOREF(pDevExt); NOREF(pImage); NOREF(pbImageBits); NOREF(pReq); + return VERR_NOT_SUPPORTED; +#endif +} + + +void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage) +{ +#ifdef VBOX_WITH_DARWIN_R0_DARWIN_IMAGE_VERIFICATION + if (pImage->hLdrMod != NIL_RTLDRMOD) + { + int rc = RTLdrClose(pImage->hLdrMod); + AssertRC(rc); + pImage->hLdrMod = NIL_RTLDRMOD; + } + if (pImage->hMemAlloc != NIL_RTR0MEMOBJ) + { + RTR0MemObjFree(pImage->hMemAlloc, true /*fFreeMappings*/); + pImage->hMemAlloc = NIL_RTR0MEMOBJ; + } + NOREF(pDevExt); +#else + NOREF(pDevExt); NOREF(pImage); +#endif +} + + +void VBOXCALL supdrvOSLdrNotifyLoaded(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage) +{ + NOREF(pDevExt); NOREF(pImage); +} + + +void VBOXCALL supdrvOSLdrNotifyOpened(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename) +{ +#if 1 + NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename); +#else + /* + * Try store the image load address in NVRAM so we can retrived it on panic. + * Note! This only works if you're root! - Acutally, it doesn't work at all at the moment. FIXME! + */ + IORegistryEntry *pEntry = IORegistryEntry::fromPath("/options", gIODTPlane); + if (pEntry) + { + char szVar[80]; + RTStrPrintf(szVar, sizeof(szVar), "vboximage"/*-%s*/, pImage->szName); + char szValue[48]; + RTStrPrintf(szValue, sizeof(szValue), "%#llx,%#llx", (uint64_t)(uintptr_t)pImage->pvImage, + (uint64_t)(uintptr_t)pImage->pvImage + pImage->cbImageBits - 1); + bool fRc = pEntry->setProperty(szVar, szValue); NOREF(fRc); + pEntry->release(); + SUPR0Printf("fRc=%d '%s'='%s'\n", fRc, szVar, szValue); + } + /*else + SUPR0Printf("failed to find /options in gIODTPlane\n");*/ +#endif +} + + +void VBOXCALL supdrvOSLdrNotifyUnloaded(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage) +{ + NOREF(pDevExt); NOREF(pImage); +} + + +#ifdef SUPDRV_WITH_MSR_PROBER + +typedef struct SUPDRVDARWINMSRARGS +{ + RTUINT64U uValue; + uint32_t uMsr; + int rc; +} SUPDRVDARWINMSRARGS, *PSUPDRVDARWINMSRARGS; + +/** + * On CPU worker for supdrvOSMsrProberRead. + * + * @param idCpu Ignored. + * @param pvUser1 Pointer to a SUPDRVDARWINMSRARGS. + * @param pvUser2 Ignored. + */ +static DECLCALLBACK(void) supdrvDarwinMsrProberReadOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2) +{ + PSUPDRVDARWINMSRARGS pArgs = (PSUPDRVDARWINMSRARGS)pvUser1; + if (g_pfnRdMsr64Carefully) + pArgs->rc = g_pfnRdMsr64Carefully(pArgs->uMsr, &pArgs->uValue.u); + else if (g_pfnRdMsrCarefully) + pArgs->rc = g_pfnRdMsrCarefully(pArgs->uMsr, &pArgs->uValue.s.Lo, &pArgs->uValue.s.Hi); + else + pArgs->rc = 2; + NOREF(idCpu); NOREF(pvUser2); +} + + +int VBOXCALL supdrvOSMsrProberRead(uint32_t uMsr, RTCPUID idCpu, uint64_t *puValue) +{ + if (!g_pfnRdMsr64Carefully && !g_pfnRdMsrCarefully) + return VERR_NOT_SUPPORTED; + + SUPDRVDARWINMSRARGS Args; + Args.uMsr = uMsr; + Args.uValue.u = 0; + Args.rc = -1; + + if (idCpu == NIL_RTCPUID) + { + IPRT_DARWIN_SAVE_EFL_AC(); + supdrvDarwinMsrProberReadOnCpu(idCpu, &Args, NULL); + IPRT_DARWIN_RESTORE_EFL_AC(); + } + else + { + int rc = RTMpOnSpecific(idCpu, supdrvDarwinMsrProberReadOnCpu, &Args, NULL); + if (RT_FAILURE(rc)) + return rc; + } + + if (Args.rc) + return VERR_ACCESS_DENIED; + *puValue = Args.uValue.u; + return VINF_SUCCESS; +} + + +/** + * On CPU worker for supdrvOSMsrProberWrite. + * + * @param idCpu Ignored. + * @param pvUser1 Pointer to a SUPDRVDARWINMSRARGS. + * @param pvUser2 Ignored. + */ +static DECLCALLBACK(void) supdrvDarwinMsrProberWriteOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2) +{ + PSUPDRVDARWINMSRARGS pArgs = (PSUPDRVDARWINMSRARGS)pvUser1; + if (g_pfnWrMsr64Carefully) + pArgs->rc = g_pfnWrMsr64Carefully(pArgs->uMsr, pArgs->uValue.u); + else + pArgs->rc = 2; + NOREF(idCpu); NOREF(pvUser2); +} + + +int VBOXCALL supdrvOSMsrProberWrite(uint32_t uMsr, RTCPUID idCpu, uint64_t uValue) +{ + if (!g_pfnWrMsr64Carefully) + return VERR_NOT_SUPPORTED; + + SUPDRVDARWINMSRARGS Args; + Args.uMsr = uMsr; + Args.uValue.u = uValue; + Args.rc = -1; + + if (idCpu == NIL_RTCPUID) + { + IPRT_DARWIN_SAVE_EFL_AC(); + supdrvDarwinMsrProberWriteOnCpu(idCpu, &Args, NULL); + IPRT_DARWIN_RESTORE_EFL_AC(); + } + else + { + int rc = RTMpOnSpecific(idCpu, supdrvDarwinMsrProberWriteOnCpu, &Args, NULL); + if (RT_FAILURE(rc)) + return rc; + } + + if (Args.rc) + return VERR_ACCESS_DENIED; + return VINF_SUCCESS; +} + + +/** + * Worker for supdrvOSMsrProberModify. + */ +static DECLCALLBACK(void) supdrvDarwinMsrProberModifyOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2) +{ + RT_NOREF(idCpu, pvUser2); + PSUPMSRPROBER pReq = (PSUPMSRPROBER)pvUser1; + register uint32_t uMsr = pReq->u.In.uMsr; + bool const fFaster = pReq->u.In.enmOp == SUPMSRPROBEROP_MODIFY_FASTER; + uint64_t uBefore; + uint64_t uWritten; + uint64_t uAfter; + int rcBefore, rcWrite, rcAfter, rcRestore; + RTCCUINTREG fOldFlags; + + /* Initialize result variables. */ + uBefore = uWritten = uAfter = 0; + rcWrite = rcAfter = rcRestore = -1; + + /* + * Do the job. + */ + fOldFlags = ASMIntDisableFlags(); + ASMCompilerBarrier(); /* paranoia */ + if (!fFaster) + ASMWriteBackAndInvalidateCaches(); + + rcBefore = g_pfnRdMsr64Carefully(uMsr, &uBefore); + if (rcBefore >= 0) + { + register uint64_t uRestore = uBefore; + uWritten = uRestore; + uWritten &= pReq->u.In.uArgs.Modify.fAndMask; + uWritten |= pReq->u.In.uArgs.Modify.fOrMask; + + rcWrite = g_pfnWrMsr64Carefully(uMsr, uWritten); + rcAfter = g_pfnRdMsr64Carefully(uMsr, &uAfter); + rcRestore = g_pfnWrMsr64Carefully(uMsr, uRestore); + + if (!fFaster) + { + ASMWriteBackAndInvalidateCaches(); + ASMReloadCR3(); + ASMNopPause(); + } + } + + ASMCompilerBarrier(); /* paranoia */ + ASMSetFlags(fOldFlags); + + /* + * Write out the results. + */ + pReq->u.Out.uResults.Modify.uBefore = uBefore; + pReq->u.Out.uResults.Modify.uWritten = uWritten; + pReq->u.Out.uResults.Modify.uAfter = uAfter; + pReq->u.Out.uResults.Modify.fBeforeGp = rcBefore != 0; + pReq->u.Out.uResults.Modify.fModifyGp = rcWrite != 0; + pReq->u.Out.uResults.Modify.fAfterGp = rcAfter != 0; + pReq->u.Out.uResults.Modify.fRestoreGp = rcRestore != 0; + RT_ZERO(pReq->u.Out.uResults.Modify.afReserved); +} + + +int VBOXCALL supdrvOSMsrProberModify(RTCPUID idCpu, PSUPMSRPROBER pReq) +{ + if (!g_pfnWrMsr64Carefully || !g_pfnRdMsr64Carefully) + return VERR_NOT_SUPPORTED; + if (idCpu == NIL_RTCPUID) + { + IPRT_DARWIN_SAVE_EFL_AC(); + supdrvDarwinMsrProberModifyOnCpu(idCpu, pReq, NULL); + IPRT_DARWIN_RESTORE_EFL_AC(); + return VINF_SUCCESS; + } + return RTMpOnSpecific(idCpu, supdrvDarwinMsrProberModifyOnCpu, pReq, NULL); +} + +#endif /* SUPDRV_WITH_MSR_PROBER */ + +/** + * Resume Bluetooth keyboard. + * If there is no Bluetooth keyboard device connected to the system we just ignore this. + */ +static void supdrvDarwinResumeBluetoothKbd(void) +{ + OSDictionary *pDictionary = IOService::serviceMatching("AppleBluetoothHIDKeyboard"); + if (pDictionary) + { + OSIterator *pIter; + IOBluetoothHIDDriver *pDriver; + + pIter = IOService::getMatchingServices(pDictionary); + if (pIter) + { + while ((pDriver = (IOBluetoothHIDDriver *)pIter->getNextObject())) + if (pDriver->isKeyboard()) + (void)pDriver->hidControl(IOBTHID_CONTROL_EXIT_SUSPEND); + + pIter->release(); + } + pDictionary->release(); + } +} + +/** + * Resume built-in keyboard on MacBook Air and Pro hosts. + * If there is no built-in keyboard device attached to the system we just ignore this. + */ +static void supdrvDarwinResumeBuiltinKbd(void) +{ + /* + * AppleUSBTCKeyboard KEXT is responsible for built-in keyboard management. + * We resume keyboard by accessing to its IOService. */ + OSDictionary *pDictionary = IOService::serviceMatching("AppleUSBTCKeyboard"); + if (pDictionary) + { + OSIterator *pIter; + IOUSBHIDDriver *pDriver; + + pIter = IOService::getMatchingServices(pDictionary); + if (pIter) + { + while ((pDriver = (IOUSBHIDDriver *)pIter->getNextObject())) + if (pDriver->IsPortSuspended()) + pDriver->SuspendPort(false, 0); + + pIter->release(); + } + pDictionary->release(); + } +} + + +/** + * Resume suspended keyboard devices (if any). + */ +int VBOXCALL supdrvDarwinResumeSuspendedKbds(void) +{ + IPRT_DARWIN_SAVE_EFL_AC(); + supdrvDarwinResumeBuiltinKbd(); + supdrvDarwinResumeBluetoothKbd(); + IPRT_DARWIN_RESTORE_EFL_AC(); + return 0; +} + + +/** + * Converts an IPRT error code to a darwin error code. + * + * @returns corresponding darwin error code. + * @param rc IPRT status code. + */ +static int VBoxDrvDarwinErr2DarwinErr(int rc) +{ + switch (rc) + { + case VINF_SUCCESS: return 0; + case VERR_GENERAL_FAILURE: return EACCES; + case VERR_INVALID_PARAMETER: return EINVAL; + case VERR_INVALID_MAGIC: return EILSEQ; + case VERR_INVALID_HANDLE: return ENXIO; + case VERR_INVALID_POINTER: return EFAULT; + case VERR_LOCK_FAILED: return ENOLCK; + case VERR_ALREADY_LOADED: return EEXIST; + case VERR_PERMISSION_DENIED: return EPERM; + case VERR_VERSION_MISMATCH: return ENOSYS; + } + + return EPERM; +} + + +/** + * Check if the CPU has SMAP support. + */ +static bool vboxdrvDarwinCpuHasSMAP(void) +{ + uint32_t uMaxId, uEAX, uEBX, uECX, uEDX; + ASMCpuId(0, &uMaxId, &uEBX, &uECX, &uEDX); + if ( ASMIsValidStdRange(uMaxId) + && uMaxId >= 0x00000007) + { + ASMCpuId_Idx_ECX(0x00000007, 0, &uEAX, &uEBX, &uECX, &uEDX); + if (uEBX & X86_CPUID_STEXT_FEATURE_EBX_SMAP) + return true; + } +#ifdef VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV + return true; +#else + return false; +#endif +} + + +RTDECL(int) SUPR0Printf(const char *pszFormat, ...) +{ + IPRT_DARWIN_SAVE_EFL_AC(); + va_list va; + char szMsg[512]; + + va_start(va, pszFormat); + RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va); + va_end(va); + szMsg[sizeof(szMsg) - 1] = '\0'; + + printf("%s", szMsg); + + IPRT_DARWIN_RESTORE_EFL_AC(); + return 0; +} + + +SUPR0DECL(uint32_t) SUPR0GetKernelFeatures(void) +{ + uint32_t fFlags = 0; + if (g_DevCW.d_ioctl == VBoxDrvDarwinIOCtlSMAP) + fFlags |= SUPKERNELFEATURES_SMAP; + else + Assert(!(ASMGetCR4() & X86_CR4_SMAP)); + return fFlags; +} + + +/* + * + * org_virtualbox_SupDrv + * + * - IOService diff resync - + * - IOService diff resync - + * - IOService diff resync - + * + */ + + +/** + * Initialize the object. + */ +bool org_virtualbox_SupDrv::init(OSDictionary *pDictionary) +{ + LogFlow(("IOService::init([%p], %p)\n", this, pDictionary)); + if (IOService::init(pDictionary)) + { + /* init members. */ + return true; + } + return false; +} + + +/** + * Free the object. + */ +void org_virtualbox_SupDrv::free(void) +{ + LogFlow(("IOService::free([%p])\n", this)); + IOService::free(); +} + + +/** + * Check if it's ok to start this service. + * It's always ok by us, so it's up to IOService to decide really. + */ +IOService *org_virtualbox_SupDrv::probe(IOService *pProvider, SInt32 *pi32Score) +{ + LogFlow(("IOService::probe([%p])\n", this)); + return IOService::probe(pProvider, pi32Score); +} + + +/** + * Start this service. + */ +bool org_virtualbox_SupDrv::start(IOService *pProvider) +{ + LogFlow(("org_virtualbox_SupDrv::start([%p])\n", this)); + + if (IOService::start(pProvider)) + { + /* register the service. */ + registerService(); + return true; + } + return false; +} + + +/** + * Stop this service. + */ +void org_virtualbox_SupDrv::stop(IOService *pProvider) +{ + LogFlow(("org_virtualbox_SupDrv::stop([%p], %p)\n", this, pProvider)); + IOService::stop(pProvider); +} + + +/** + * Termination request. + * + * @return true if we're ok with shutting down now, false if we're not. + * @param fOptions Flags. + */ +bool org_virtualbox_SupDrv::terminate(IOOptionBits fOptions) +{ + bool fRc; + LogFlow(("org_virtualbox_SupDrv::terminate: reference_count=%d g_cSessions=%d (fOptions=%#x)\n", + KMOD_INFO_NAME.reference_count, ASMAtomicUoReadS32(&g_cSessions), fOptions)); + if ( KMOD_INFO_NAME.reference_count != 0 + || ASMAtomicUoReadS32(&g_cSessions)) + fRc = false; + else + fRc = IOService::terminate(fOptions); + LogFlow(("org_virtualbox_SupDrv::terminate: returns %d\n", fRc)); + return fRc; +} + + +/* + * + * org_virtualbox_SupDrvClient + * + */ + + +/** + * Initializer called when the client opens the service. + */ +bool org_virtualbox_SupDrvClient::initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type) +{ + LogFlow(("org_virtualbox_SupDrvClient::initWithTask([%p], %#x, %p, %#x) (cur pid=%d proc=%p)\n", + this, OwningTask, pvSecurityId, u32Type, RTProcSelf(), RTR0ProcHandleSelf())); + AssertMsg((RTR0PROCESS)OwningTask == RTR0ProcHandleSelf(), ("%p %p\n", OwningTask, RTR0ProcHandleSelf())); + + if (!OwningTask) + return false; + + if (u32Type != SUP_DARWIN_IOSERVICE_COOKIE) + { + VBOX_RETRIEVE_CUR_PROC_NAME(szProcName); + LogRelMax(10,("org_virtualbox_SupDrvClient::initWithTask: Bad cookie %#x (%s)\n", u32Type, szProcName)); + return false; + } + + if (IOUserClient::initWithTask(OwningTask, pvSecurityId , u32Type)) + { + /* + * In theory we have to call task_reference() to make sure that the task is + * valid during the lifetime of this object. The pointer is only used to check + * for the context this object is called in though and never dereferenced + * or passed to anything which might, so we just skip this step. + */ + m_Task = OwningTask; + m_pSession = NULL; + m_pProvider = NULL; + return true; + } + return false; +} + + +/** + * Start the client service. + */ +bool org_virtualbox_SupDrvClient::start(IOService *pProvider) +{ + LogFlow(("org_virtualbox_SupDrvClient::start([%p], %p) (cur pid=%d proc=%p)\n", + this, pProvider, RTProcSelf(), RTR0ProcHandleSelf() )); + AssertMsgReturn((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(), + ("%p %p\n", m_Task, RTR0ProcHandleSelf()), + false); + + if (IOUserClient::start(pProvider)) + { + m_pProvider = OSDynamicCast(org_virtualbox_SupDrv, pProvider); + if (m_pProvider) + { + Assert(!m_pSession); + + /* + * Create a new session. + */ + int rc = supdrvCreateSession(&g_DevExt, true /* fUser */, false /*fUnrestricted*/, &m_pSession); + if (RT_SUCCESS(rc)) + { + m_pSession->fOpened = false; + /* The Uid, Gid and fUnrestricted fields are set on open. */ + + /* + * Insert it into the hash table, checking that there isn't + * already one for this process first. (One session per proc!) + */ + unsigned iHash = SESSION_HASH(m_pSession->Process); + RTSpinlockAcquire(g_Spinlock); + + PSUPDRVSESSION pCur = g_apSessionHashTab[iHash]; + while (pCur && pCur->Process != m_pSession->Process) + pCur = pCur->pNextHash; + if (!pCur) + { + m_pSession->pNextHash = g_apSessionHashTab[iHash]; + g_apSessionHashTab[iHash] = m_pSession; + m_pSession->pvSupDrvClient = this; + ASMAtomicIncS32(&g_cSessions); + rc = VINF_SUCCESS; + } + else + rc = VERR_ALREADY_LOADED; + + RTSpinlockRelease(g_Spinlock); + if (RT_SUCCESS(rc)) + { + Log(("org_virtualbox_SupDrvClient::start: created session %p for pid %d\n", m_pSession, (int)RTProcSelf())); + return true; + } + + LogFlow(("org_virtualbox_SupDrvClient::start: already got a session for this process (%p)\n", pCur)); + supdrvSessionRelease(m_pSession); + } + + m_pSession = NULL; + LogFlow(("org_virtualbox_SupDrvClient::start: rc=%Rrc from supdrvCreateSession\n", rc)); + } + else + LogFlow(("org_virtualbox_SupDrvClient::start: %p isn't org_virtualbox_SupDrv\n", pProvider)); + } + return false; +} + + +/** + * Common worker for clientClose and VBoxDrvDarwinClose. + */ +/* static */ void org_virtualbox_SupDrvClient::sessionClose(RTPROCESS Process) +{ + /* + * Find the session and remove it from the hash table. + * + * Note! Only one session per process. (Both start() and + * VBoxDrvDarwinOpen makes sure this is so.) + */ + const unsigned iHash = SESSION_HASH(Process); + RTSpinlockAcquire(g_Spinlock); + PSUPDRVSESSION pSession = g_apSessionHashTab[iHash]; + if (pSession) + { + if (pSession->Process == Process) + { + g_apSessionHashTab[iHash] = pSession->pNextHash; + pSession->pNextHash = NULL; + ASMAtomicDecS32(&g_cSessions); + } + else + { + PSUPDRVSESSION pPrev = pSession; + pSession = pSession->pNextHash; + while (pSession) + { + if (pSession->Process == Process) + { + pPrev->pNextHash = pSession->pNextHash; + pSession->pNextHash = NULL; + ASMAtomicDecS32(&g_cSessions); + break; + } + + /* next */ + pPrev = pSession; + pSession = pSession->pNextHash; + } + } + } + RTSpinlockRelease(g_Spinlock); + if (!pSession) + { + Log(("SupDrvClient::sessionClose: pSession == NULL, pid=%d; freed already?\n", (int)Process)); + return; + } + + /* + * Remove it from the client object. + */ + org_virtualbox_SupDrvClient *pThis = (org_virtualbox_SupDrvClient *)pSession->pvSupDrvClient; + pSession->pvSupDrvClient = NULL; + if (pThis) + { + Assert(pThis->m_pSession == pSession); + pThis->m_pSession = NULL; + } + + /* + * Close the session. + */ + supdrvSessionRelease(pSession); +} + + +/** + * Client exits normally. + */ +IOReturn org_virtualbox_SupDrvClient::clientClose(void) +{ + LogFlow(("org_virtualbox_SupDrvClient::clientClose([%p]) (cur pid=%d proc=%p)\n", this, RTProcSelf(), RTR0ProcHandleSelf())); + AssertMsg((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(), ("%p %p\n", m_Task, RTR0ProcHandleSelf())); + + /* + * Clean up the session if it's still around. + * + * We cannot rely 100% on close, and in the case of a dead client + * we'll end up hanging inside vm_map_remove() if we postpone it. + */ + if (m_pSession) + { + sessionClose(RTProcSelf()); + Assert(!m_pSession); + } + + m_pProvider = NULL; + terminate(); + + return kIOReturnSuccess; +} + + +/** + * The client exits abnormally / forgets to do cleanups. (logging) + */ +IOReturn org_virtualbox_SupDrvClient::clientDied(void) +{ + LogFlow(("IOService::clientDied([%p]) m_Task=%p R0Process=%p Process=%d\n", this, m_Task, RTR0ProcHandleSelf(), RTProcSelf())); + + /* IOUserClient::clientDied() calls clientClose, so we'll just do the work there. */ + return IOUserClient::clientDied(); +} + + +/** + * Terminate the service (initiate the destruction). (logging) + */ +bool org_virtualbox_SupDrvClient::terminate(IOOptionBits fOptions) +{ + LogFlow(("IOService::terminate([%p], %#x)\n", this, fOptions)); + return IOUserClient::terminate(fOptions); +} + + +/** + * The final stage of the client service destruction. (logging) + */ +bool org_virtualbox_SupDrvClient::finalize(IOOptionBits fOptions) +{ + LogFlow(("IOService::finalize([%p], %#x)\n", this, fOptions)); + return IOUserClient::finalize(fOptions); +} + + +/** + * Stop the client service. (logging) + */ +void org_virtualbox_SupDrvClient::stop(IOService *pProvider) +{ + LogFlow(("IOService::stop([%p])\n", this)); + IOUserClient::stop(pProvider); +} + diff --git a/src/VBox/HostDrivers/Support/darwin/SUPLib-darwin.cpp b/src/VBox/HostDrivers/Support/darwin/SUPLib-darwin.cpp new file mode 100644 index 00000000..c40322a9 --- /dev/null +++ b/src/VBox/HostDrivers/Support/darwin/SUPLib-darwin.cpp @@ -0,0 +1,307 @@ +/* $Id: SUPLib-darwin.cpp $ */ +/** @file + * VirtualBox Support Library - Darwin specific parts. + */ + +/* + * 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. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_SUP +#ifdef IN_SUP_HARDENED_R3 +# undef DEBUG /* Warning: disables RT_STRICT */ +# ifndef LOG_DISABLED +# define LOG_DISABLED +# endif +# define RTLOG_REL_DISABLED +# include <iprt/log.h> +#endif + +#include <VBox/types.h> +#include <VBox/sup.h> +#include <VBox/param.h> +#include <VBox/err.h> +#include <VBox/log.h> +#include <iprt/path.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/string.h> +#include "../SUPLibInternal.h" +#include "../SUPDrvIOC.h" + +#include <sys/fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <mach/mach_port.h> +#include <IOKit/IOKitLib.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** System device name. */ +#define DEVICE_NAME_SYS "/dev/vboxdrv" +/** User device name. */ +#define DEVICE_NAME_USR "/dev/vboxdrvu" +/** The IOClass key of the service (see SUPDrv-darwin.cpp / Info.plist). */ +#define IOCLASS_NAME "org_virtualbox_SupDrv" + + + +/** + * Opens the BSD device node. + * + * @returns VBox status code. + */ +static int suplibDarwinOpenDevice(PSUPLIBDATA pThis, bool fUnrestricted) +{ + /* + * Open the BSD device. + * This will connect to the session created when the SupDrvClient was + * started, so it has to be done after opening the service (IOC v9.1+). + */ + int hDevice = open(fUnrestricted ? DEVICE_NAME_SYS : DEVICE_NAME_USR, O_RDWR, 0); + if (hDevice < 0) + { + int rc; + switch (errno) + { + case ENODEV: rc = VERR_VM_DRIVER_LOAD_ERROR; break; + case EPERM: + case EACCES: rc = VERR_VM_DRIVER_NOT_ACCESSIBLE; break; + case ENOENT: rc = VERR_VM_DRIVER_NOT_INSTALLED; break; + default: rc = VERR_VM_DRIVER_OPEN_ERROR; break; + } + LogRel(("SUP: Failed to open \"%s\", errno=%d, rc=%Rrc\n", fUnrestricted ? DEVICE_NAME_SYS : DEVICE_NAME_USR, errno, rc)); + return rc; + } + + /* + * Mark the file handle close on exec. + */ + if (fcntl(hDevice, F_SETFD, FD_CLOEXEC) != 0) + { +#ifdef IN_SUP_HARDENED_R3 + int rc = VERR_INTERNAL_ERROR; +#else + int err = errno; + int rc = RTErrConvertFromErrno(err); + LogRel(("suplibOSInit: setting FD_CLOEXEC failed, errno=%d (%Rrc)\n", err, rc)); +#endif + close(hDevice); + return rc; + } + + pThis->hDevice = hDevice; + pThis->fUnrestricted = fUnrestricted; + return VINF_SUCCESS; +} + + +/** + * Opens the IOKit service, instantiating org_virtualbox_SupDrvClient. + * + * @returns VBox status code. + */ +static int suplibDarwinOpenService(PSUPLIBDATA pThis) +{ + /* + * Open the IOKit client first - The first step is finding the service. + */ + mach_port_t MasterPort; + kern_return_t kr = IOMasterPort(MACH_PORT_NULL, &MasterPort); + if (kr != kIOReturnSuccess) + { + LogRel(("IOMasterPort -> %d\n", kr)); + return VERR_GENERAL_FAILURE; + } + + CFDictionaryRef ClassToMatch = IOServiceMatching(IOCLASS_NAME); + if (!ClassToMatch) + { + LogRel(("IOServiceMatching(\"%s\") failed.\n", IOCLASS_NAME)); + return VERR_GENERAL_FAILURE; + } + + /* Create an io_iterator_t for all instances of our drivers class that exist in the IORegistry. */ + io_iterator_t Iterator; + kr = IOServiceGetMatchingServices(MasterPort, ClassToMatch, &Iterator); + if (kr != kIOReturnSuccess) + { + LogRel(("IOServiceGetMatchingServices returned %d\n", kr)); + return VERR_GENERAL_FAILURE; + } + + /* Get the first item in the iterator and release it. */ + io_service_t ServiceObject = IOIteratorNext(Iterator); + IOObjectRelease(Iterator); + if (!ServiceObject) + { + LogRel(("SUP: Couldn't find any matches. The kernel module is probably not loaded.\n")); + return VERR_VM_DRIVER_NOT_INSTALLED; + } + + /* + * Open the service. + * + * This will cause the user client class in SUPDrv-darwin.cpp to be + * instantiated and create a session for this process. + */ + io_connect_t Connection = 0; + kr = IOServiceOpen(ServiceObject, mach_task_self(), SUP_DARWIN_IOSERVICE_COOKIE, &Connection); + IOObjectRelease(ServiceObject); + if (kr != kIOReturnSuccess) + { + LogRel(("SUP: IOServiceOpen returned %d. Driver open failed.\n", kr)); + pThis->uConnection = 0; + return VERR_VM_DRIVER_OPEN_ERROR; + } + + AssertCompile(sizeof(pThis->uConnection) >= sizeof(Connection)); + pThis->uConnection = Connection; + return VINF_SUCCESS; +} + + +int suplibOsInit(PSUPLIBDATA pThis, bool fPreInited, bool fUnrestricted, SUPINITOP *penmWhat, PRTERRINFO pErrInfo) +{ + RT_NOREF(penmWhat, pErrInfo); + + /* + * Nothing to do if pre-inited. + */ + if (fPreInited) + return VINF_SUCCESS; + + /* + * Do the job. + */ + Assert(pThis->hDevice == (intptr_t)NIL_RTFILE); + int rc = suplibDarwinOpenService(pThis); + if (RT_SUCCESS(rc)) + { + rc = suplibDarwinOpenDevice(pThis, fUnrestricted); + if (RT_FAILURE(rc)) + { + kern_return_t kr = IOServiceClose((io_connect_t)pThis->uConnection); + if (kr != kIOReturnSuccess) + { + LogRel(("Warning: IOServiceClose(%RCv) returned %d\n", pThis->uConnection, kr)); + AssertFailed(); + } + pThis->uConnection = 0; + } + } + + return rc; +} + + +int suplibOsTerm(PSUPLIBDATA pThis) +{ + /* + * Close the connection to the IOService. + * This will cause the SUPDRVSESSION to be closed (starting IOC 9.1). + */ + if (pThis->uConnection) + { + kern_return_t kr = IOServiceClose((io_connect_t)pThis->uConnection); + if (kr != kIOReturnSuccess) + { + LogRel(("Warning: IOServiceClose(%RCv) returned %d\n", pThis->uConnection, kr)); + AssertFailed(); + } + pThis->uConnection = 0; + } + + /* + * Check if we're inited at all. + */ + if (pThis->hDevice != (intptr_t)NIL_RTFILE) + { + if (close(pThis->hDevice)) + AssertFailed(); + pThis->hDevice = (intptr_t)NIL_RTFILE; + } + + return VINF_SUCCESS; +} + + +#ifndef IN_SUP_HARDENED_R3 + +int suplibOsInstall(void) +{ + return VERR_NOT_IMPLEMENTED; +} + + +int suplibOsUninstall(void) +{ + return VERR_NOT_IMPLEMENTED; +} + + +int suplibOsIOCtl(PSUPLIBDATA pThis, uintptr_t uFunction, void *pvReq, size_t cbReq) +{ + RT_NOREF(cbReq); + if (RT_LIKELY(ioctl(pThis->hDevice, uFunction, pvReq) >= 0)) + return VINF_SUCCESS; + return RTErrConvertFromErrno(errno); +} + + +int suplibOsIOCtlFast(PSUPLIBDATA pThis, uintptr_t uFunction, uintptr_t idCpu) +{ + int rc = ioctl(pThis->hDevice, uFunction, idCpu); + if (rc == -1) + rc = errno; + return rc; +} + + +int suplibOsPageAlloc(PSUPLIBDATA pThis, size_t cPages, void **ppvPages) +{ + NOREF(pThis); + *ppvPages = valloc(cPages << PAGE_SHIFT); + if (*ppvPages) + { + memset(*ppvPages, 0, cPages << PAGE_SHIFT); + return VINF_SUCCESS; + } + return RTErrConvertFromErrno(errno); +} + + +int suplibOsPageFree(PSUPLIBDATA pThis, void *pvPages, size_t /* cPages */) +{ + NOREF(pThis); + free(pvPages); + return VINF_SUCCESS; +} + +#endif /* !IN_SUP_HARDENED_R3 */ + diff --git a/src/VBox/HostDrivers/Support/darwin/SUPR0IdcClient-darwin.c b/src/VBox/HostDrivers/Support/darwin/SUPR0IdcClient-darwin.c new file mode 100644 index 00000000..c8944eaf --- /dev/null +++ b/src/VBox/HostDrivers/Support/darwin/SUPR0IdcClient-darwin.c @@ -0,0 +1,56 @@ +/* $Id: SUPR0IdcClient-darwin.c $ */ +/** @file + * VirtualBox Support Driver - IDC Client Lib, Darwin Specific Code. + */ + +/* + * Copyright (C) 2008-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. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "../SUPR0IdcClientInternal.h" +#include <iprt/errcore.h> + + +int VBOXCALL supR0IdcNativeOpen(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQCONNECT pReq) +{ + return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_CONNECT, &pReq->Hdr); +} + + +int VBOXCALL supR0IdcNativeClose(PSUPDRVIDCHANDLE pHandle, PSUPDRVIDCREQHDR pReq) +{ + return supR0IdcNativeCall(pHandle, SUPDRV_IDC_REQ_DISCONNECT, pReq); +} + + +int VBOXCALL supR0IdcNativeCall(PSUPDRVIDCHANDLE pHandle, uint32_t iReq, PSUPDRVIDCREQHDR pReq) +{ + int rc = SUPDrvDarwinIDC(iReq, pReq); + if (RT_SUCCESS(rc)) + rc = pReq->rc; + + NOREF(pHandle); + return rc; +} + diff --git a/src/VBox/HostDrivers/Support/darwin/load.sh b/src/VBox/HostDrivers/Support/darwin/load.sh new file mode 100755 index 00000000..0f6b71f9 --- /dev/null +++ b/src/VBox/HostDrivers/Support/darwin/load.sh @@ -0,0 +1,154 @@ +#!/bin/bash +# $Id: load.sh $ +## @file +# For development. +# + +# +# 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. +# + +XNU_VERSION=`LC_ALL=C uname -r | LC_ALL=C cut -d . -f 1` +DRVNAME="VBoxDrv.kext" +BUNDLE="org.virtualbox.kext.VBoxDrv" + +DIR=`dirname "$0"` +DIR=`cd "$DIR" && pwd` +DIR="$DIR/$DRVNAME" +if [ ! -d "$DIR" ]; then + echo "Cannot find $DIR or it's not a directory..." + exit 1; +fi +if [ -n "$*" ]; then + OPTS="$*" +else + OPTS="-t" +fi + +# Make sure VBoxUSB is unloaded as it might be using symbols from us. +LOADED=`kextstat -b org.virtualbox.kext.VBoxUSB -l` +if test -n "$LOADED"; then + echo "load.sh: Unloading org.virtualbox.kext.VBoxUSB..." + sudo kextunload -v 6 -b org.virtualbox.kext.VBoxUSB + LOADED=`kextstat -b org.virtualbox.kext.VBoxUSB -l` + if test -n "$LOADED"; then + echo "load.sh: failed to unload org.virtualbox.kext.VBoxUSB, see above..." + exit 1; + fi + echo "load.sh: Successfully unloaded org.virtualbox.kext.VBoxUSB" +fi + +# Make sure VBoxNetFlt is unloaded as it might be using symbols from us. +LOADED=`kextstat -b org.virtualbox.kext.VBoxNetFlt -l` +if test -n "$LOADED"; then + echo "load.sh: Unloading org.virtualbox.kext.VBoxNetFlt..." + sudo kextunload -v 6 -b org.virtualbox.kext.VBoxNetFlt + LOADED=`kextstat -b org.virtualbox.kext.VBoxNetFlt -l` + if test -n "$LOADED"; then + echo "load.sh: failed to unload org.virtualbox.kext.VBoxNetFlt, see above..." + exit 1; + fi + echo "load.sh: Successfully unloaded org.virtualbox.kext.VBoxNetFlt" +fi + +# Make sure VBoxNetAdp is unloaded as it might be using symbols from us. +LOADED=`kextstat -b org.virtualbox.kext.VBoxNetAdp -l` +if test -n "$LOADED"; then + echo "load.sh: Unloading org.virtualbox.kext.VBoxNetAdp..." + sudo kextunload -v 6 -b org.virtualbox.kext.VBoxNetAdp + LOADED=`kextstat -b org.virtualbox.kext.VBoxNetAdp -l` + if test -n "$LOADED"; then + echo "load.sh: failed to unload org.virtualbox.kext.VBoxNetAdp, see above..." + exit 1; + fi + echo "load.sh: Successfully unloaded org.virtualbox.kext.VBoxNetAdp" +fi + +# Try unload any existing instance first. +LOADED=`kextstat -b $BUNDLE -l` +if test -n "$LOADED"; then + echo "load.sh: Unloading $BUNDLE..." + sudo kextunload -v 6 -b $BUNDLE + LOADED=`kextstat -b $BUNDLE -l` + if test -n "$LOADED"; then + echo "load.sh: failed to unload $BUNDLE, see above..." + exit 1; + fi + echo "load.sh: Successfully unloaded $BUNDLE" +fi + +set -e + +# Copy the .kext to the symbols directory and tweak the kextload options. +if test -n "$VBOX_DARWIN_SYMS"; then + echo "load.sh: copying the extension the symbol area..." + rm -Rf "$VBOX_DARWIN_SYMS/$DRVNAME" + mkdir -p "$VBOX_DARWIN_SYMS" + cp -R "$DIR" "$VBOX_DARWIN_SYMS/" + OPTS="$OPTS -s $VBOX_DARWIN_SYMS/ " + sync +fi + +trap "sudo chown -R `whoami` $DIR; exit 1" INT +trap "sudo chown -R `whoami` $DIR; exit 1" ERR +# On smbfs, this might succeed just fine but make no actual changes, +# so we might have to temporarily copy the driver to a local directory. +if sudo chown -R root:wheel "$DIR"; then + OWNER=`/usr/bin/stat -f "%u" "$DIR"` +else + OWNER=1000 +fi +if test "$OWNER" -ne 0; then + TMP_DIR=/tmp/loaddrv.tmp + echo "load.sh: chown didn't work on $DIR, using temp location $TMP_DIR/$DRVNAME" + + # clean up first (no sudo rm) + if test -e "$TMP_DIR"; then + sudo chown -R `whoami` "$TMP_DIR" + rm -Rf "$TMP_DIR" + fi + + # make a copy and switch over DIR + mkdir -p "$TMP_DIR/" + sudo cp -Rp "$DIR" "$TMP_DIR/" + DIR="$TMP_DIR/$DRVNAME" + + # retry + sudo chown -R root:wheel "$DIR" +fi +sudo chmod -R o-rwx "$DIR" +sync +echo "load.sh: loading $DIR..." + +if [ "$XNU_VERSION" -ge "10" ]; then + echo "${SCRIPT_NAME}.sh: loading $DIR... (kextutil $OPTS \"$DIR\")" + sudo kextutil $OPTS "$DIR" +else + sudo kextload $OPTS "$DIR" +fi +sync +sudo chown -R `whoami` "$DIR" +#sudo chmod 666 /dev/vboxdrv +kextstat | grep org.virtualbox.kext +if [ -n "${VBOX_DARWIN_SYMS}" -a "$XNU_VERSION" -ge "10" ]; then + dsymutil -o "${VBOX_DARWIN_SYMS}/${DRVNAME}.dSYM" "${DIR}/Contents/MacOS/`basename -s .kext ${DRVNAME}`" + sync +fi + diff --git a/src/VBox/HostDrivers/Support/darwin/sys/dtrace_glue.h b/src/VBox/HostDrivers/Support/darwin/sys/dtrace_glue.h new file mode 100644 index 00000000..d29597a3 --- /dev/null +++ b/src/VBox/HostDrivers/Support/darwin/sys/dtrace_glue.h @@ -0,0 +1,39 @@ +/* $Id: dtrace_glue.h $ */ +/** @file + * VirtualBox Support Driver - Darwin, mock-up of missing sys/dtrace-glue.h. + */ + +/* + * 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. + */ + +#ifndef VBOX_INCLUDED_SRC_Support_darwin_sys_dtrace_glue_h +#define VBOX_INCLUDED_SRC_Support_darwin_sys_dtrace_glue_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#define _KERNEL +typedef struct solaris_cred cred_t; +typedef struct solaris_kthread kthread_t; +typedef struct solaris_x86_saved_state x86_saved_state_t; +typedef unsigned int model_t; + +#endif /* !VBOX_INCLUDED_SRC_Support_darwin_sys_dtrace_glue_h */ |