diff options
Diffstat (limited to 'src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp')
-rw-r--r-- | src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp | 304 |
1 files changed, 250 insertions, 54 deletions
diff --git a/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp b/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp index d91f34de..33803bca 100644 --- a/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp +++ b/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -42,6 +42,7 @@ #include "../SUPDrvInternal.h" #include <VBox/version.h> #include <iprt/asm.h> +#include <iprt/asm-amd64-x86.h> #include <iprt/initterm.h> #include <iprt/assert.h> #include <iprt/spinlock.h> @@ -49,6 +50,7 @@ #include <iprt/process.h> #include <iprt/alloc.h> #include <iprt/power.h> +#include <iprt/dbg.h> #include <VBox/err.h> #include <VBox/log.h> @@ -64,6 +66,9 @@ #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> @@ -77,8 +82,10 @@ RT_C_DECLS_END * Defined Constants And Macros * *******************************************************************************/ -/** The module name. */ -#define DEVICE_NAME "vboxdrv" +/** The system device node name. */ +#define DEVICE_NAME_SYS "vboxdrv" +/** The user device node name. */ +#define DEVICE_NAME_USR "vboxdrvu" @@ -99,6 +106,8 @@ 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 void vboxdrvDarwinResolveSymbols(void); + /******************************************************************************* * Structures and Typedefs * @@ -198,8 +207,10 @@ static struct cdevsw g_DevCW = /** Major device number. */ static int g_iMajorDeviceNo = -1; -/** Registered devfs device handle. */ -static void *g_hDevFsDevice = NULL; +/** 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; @@ -210,8 +221,14 @@ static PSUPDRVSESSION g_apSessionHashTab[19]; /** 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; +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; /** @@ -250,26 +267,38 @@ static kern_return_t VBoxDrvDarwinStart(struct kmod_info *pKModInfo, void *pv if (g_iMajorDeviceNo >= 0) { #ifdef VBOX_WITH_HARDENING - g_hDevFsDevice = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR, - UID_ROOT, GID_WHEEL, 0600, DEVICE_NAME); + g_hDevFsDeviceSys = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR, + UID_ROOT, GID_WHEEL, 0600, DEVICE_NAME_SYS); #else - g_hDevFsDevice = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR, - UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME); + g_hDevFsDeviceSys = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR, + UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME_SYS); #endif - if (g_hDevFsDevice) + if (g_hDevFsDeviceSys) { - 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; + 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")); + + /* Find kernel symbols that are kind of optional. */ + vboxdrvDarwinResolveSymbols(); + 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)); - LogRel(("VBoxDrv: devfs_make_node(makedev(%d,0),,,,%s) failed\n", g_iMajorDeviceNo, DEVICE_NAME)); cdevsw_remove(g_iMajorDeviceNo, &g_DevCW); g_iMajorDeviceNo = -1; } @@ -295,6 +324,39 @@ static kern_return_t VBoxDrvDarwinStart(struct kmod_info *pKModInfo, void *pv /** + * Resolves kernel symbols we want (but may do without). + */ +static void vboxdrvDarwinResolveSymbols(void) +{ + RTDBGKRNLINFO hKrnlInfo; + int rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0); + if (RT_SUCCESS(rc)) + { + /* The VMX stuff. */ + 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; + } + + RTR0DbgKrnlInfoRelease(hKrnlInfo); + } + else + LogRel(("VBoxDrv: Failed to open kernel symbols, rc=%Rrc\n", rc)); +} + + +/** * Stop the kernel module. */ static kern_return_t VBoxDrvDarwinStop(struct kmod_info *pKModInfo, void *pvData) @@ -314,8 +376,11 @@ static kern_return_t VBoxDrvDarwinStop(struct kmod_info *pKModInfo, void *pvD g_pSleepNotifier = NULL; } - devfs_remove(g_hDevFsDevice); - g_hDevFsDevice = 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); @@ -340,8 +405,10 @@ static kern_return_t VBoxDrvDarwinStop(struct kmod_info *pKModInfo, void *pvD /** * Device open. Called on open /dev/vboxdrv * - * @param pInode Pointer to inode info structure. - * @param pFilp Associated file pointer. + * @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) { @@ -353,10 +420,17 @@ static int VBoxDrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *p #endif /* + * Only two minor devices numbers are allowed. + */ + if (minor(Dev) != 0 && minor(Dev) != 1) + return EACCES; + + /* * 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); @@ -374,16 +448,14 @@ static int VBoxDrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *p RTSpinlockAcquire(g_Spinlock); pSession = g_apSessionHashTab[iHash]; - if (pSession && pSession->Process != Process) - { - do pSession = pSession->pNextHash; - while (pSession && pSession->Process != Process); - } + 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; } @@ -442,6 +514,7 @@ static int VBoxDrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc * */ static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess) { + const bool fUnrestricted = minor(Dev) == 0; const RTPROCESS Process = proc_pid(pProcess); const unsigned iHash = SESSION_HASH(Process); PSUPDRVSESSION pSession; @@ -451,11 +524,11 @@ static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, */ RTSpinlockAcquire(g_Spinlock); pSession = g_apSessionHashTab[iHash]; - if (pSession && pSession->Process != Process) - { - do pSession = pSession->pNextHash; - while (pSession && pSession->Process != Process); - } + while (pSession && pSession->Process != Process && pSession->fUnrestricted == fUnrestricted && pSession->fOpened) + pSession = pSession->pNextHash; + + if (RT_LIKELY(pSession)) + supdrvSessionRetain(pSession); RTSpinlockReleaseNoInts(g_Spinlock); if (!pSession) { @@ -468,11 +541,17 @@ static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, * Deal with the two high-speed IOCtl that takes it's arguments from * the session and iCmd, and only returns a VBox status code. */ - if ( iCmd == SUP_IOCTL_FAST_DO_RAW_RUN - || iCmd == SUP_IOCTL_FAST_DO_HWACC_RUN - || iCmd == SUP_IOCTL_FAST_DO_NOP) - return supdrvIOCtlFast(iCmd, *(uint32_t *)pData, &g_DevExt, pSession); - return VBoxDrvDarwinIOCtlSlow(pSession, iCmd, pData, pProcess); + int rc; + if ( ( iCmd == SUP_IOCTL_FAST_DO_RAW_RUN + || iCmd == SUP_IOCTL_FAST_DO_HM_RUN + || iCmd == SUP_IOCTL_FAST_DO_NOP) + && fUnrestricted) + rc = supdrvIOCtlFast(iCmd, *(uint32_t *)pData, &g_DevExt, pSession); + else + rc = VBoxDrvDarwinIOCtlSlow(pSession, iCmd, pData, pProcess); + + supdrvSessionRelease(pSession); + return rc; } @@ -633,7 +712,7 @@ static int VBoxDrvDarwinIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t * @param iReq The request code. * @param pReq The request. */ -int VBOXCALL SUPDrvDarwinIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq) +DECLEXPORT(int) VBOXCALL SUPDrvDarwinIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq) { PSUPDRVSESSION pSession; @@ -710,16 +789,16 @@ IOReturn VBoxDrvDarwinSleepHandler(void * /* pvTarget */, void *pvRefCon, UInt32 /** - * Enables or disables VT-x using kernel functions. - * - * @returns VBox status code. VERR_NOT_SUPPORTED has a special meaning. - * @param fEnable Whether to enable or disable. + * @copydoc SUPR0EnableVTx */ int VBOXCALL supdrvOSEnableVTx(bool fEnable) { #ifdef VBOX_WITH_HOST_VMX int rc; - if (version_major >= 10 /* 10 = 10.6.x = Snow Leopard */) + if ( version_major >= 10 /* 10 = 10.6.x = Snow Leopard */ + && g_pfnVmxSuspend + && g_pfnVmxResume + && g_pVmxUseCount) { if (fEnable) { @@ -735,11 +814,13 @@ int VBOXCALL supdrvOSEnableVTx(bool fEnable) 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 { host_vmxoff(); rc = VINF_SUCCESS; + LogRel(("VBoxDrv: host_vmxoff -> vmx_use_count=%d\n", *g_pVmxUseCount)); } } else @@ -755,6 +836,54 @@ int VBOXCALL supdrvOSEnableVTx(bool fEnable) } +/** + * @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) + { + g_pfnVmxSuspend(); + 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) + g_pfnVmxResume(); + else + Assert(!fSuspended); +#else + Assert(!fSuspended); +#endif +} + + bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt) { NOREF(pDevExt); @@ -817,6 +946,72 @@ void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage) /** + * 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) +{ + supdrvDarwinResumeBuiltinKbd(); + supdrvDarwinResumeBluetoothKbd(); + + return 0; +} + + +/** * Converts an IPRT error code to a darwin error code. * * @returns corresponding darwin error code. @@ -998,15 +1193,15 @@ bool org_virtualbox_SupDrvClient::start(IOService *pProvider) /* * Create a new session. */ - int rc = supdrvCreateSession(&g_DevExt, true /* fUser */, &m_pSession); + int rc = supdrvCreateSession(&g_DevExt, true /* fUser */, false /*fUnrestricted*/, &m_pSession); if (RT_SUCCESS(rc)) { m_pSession->fOpened = false; - /* The Uid and Gid fields are set on open. */ + /* 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. + * already one for this process first. (One session per proc!) */ unsigned iHash = SESSION_HASH(m_pSession->Process); RTSpinlockAcquire(g_Spinlock); @@ -1036,7 +1231,7 @@ bool org_virtualbox_SupDrvClient::start(IOService *pProvider) } LogFlow(("org_virtualbox_SupDrvClient::start: already got a session for this process (%p)\n", pCur)); - supdrvCloseSession(&g_DevExt, m_pSession); + supdrvSessionRelease(m_pSession); } m_pSession = NULL; @@ -1051,13 +1246,14 @@ bool org_virtualbox_SupDrvClient::start(IOService *pProvider) /** * Common worker for clientClose and VBoxDrvDarwinClose. - * - * It will */ /* static */ void org_virtualbox_SupDrvClient::sessionClose(RTPROCESS Process) { /* - * Look for the session. + * 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); @@ -1111,7 +1307,7 @@ bool org_virtualbox_SupDrvClient::start(IOService *pProvider) /* * Close the session. */ - supdrvCloseSession(&g_DevExt, pSession); + supdrvSessionRelease(pSession); } |