VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/VBoxGuest-darwin.cpp

Last change on this file was 100267, checked in by vboxsync, 11 months ago

Additions: Make the R0 physical heap configurable to allow for allocations >= 4GiB if supported by the VBox device (the MMIO request path is available), add support for the MMIO request path required for ARM, bugref:10457

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.6 KB
RevLine 
[22053]1/* $Id: VBoxGuest-darwin.cpp 100267 2023-06-23 14:57:53Z vboxsync $ */
[1]2/** @file
[45459]3 * VBoxGuest - Darwin Specifics.
[1]4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[1]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
[69308]11 *
[96407]12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
[69308]25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
[96407]27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
[69308]29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
[96407]33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
[1]35 */
36
[57358]37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
[58113]41#define LOG_GROUP LOG_GROUP_VGDRV
[9498]42/*
[4817]43 * Deal with conflicts first.
44 * PVM - BSD mess, that FreeBSD has correct a long time ago.
45 * iprt/types.h before sys/param.h - prevents UINT32_C and friends.
46 */
47#include <iprt/types.h>
[1]48#include <sys/param.h>
49#undef PVM
[4817]50
[378]51#include <IOKit/IOLib.h> /* Assert as function */
[1]52
[378]53#include <VBox/version.h>
[29255]54#include <iprt/asm.h>
[68583]55#include <iprt/assert.h>
[206]56#include <iprt/initterm.h>
[68583]57#include <iprt/mem.h>
[75705]58#include <iprt/power.h>
[387]59#include <iprt/process.h>
[68583]60#include <iprt/semaphore.h>
61#include <iprt/spinlock.h>
62#include <iprt/string.h>
[14901]63#include <VBox/err.h>
[10662]64#include <VBox/log.h>
[1]65
66#include <mach/kmod.h>
67#include <miscfs/devfs/devfs.h>
68#include <sys/conf.h>
69#include <sys/errno.h>
70#include <sys/ioccom.h>
71#include <sys/malloc.h>
72#include <sys/proc.h>
[16352]73#include <sys/kauth.h>
[92621]74#define _OS_OSUNSERIALIZE_H /* HACK ALERT! Block importing OSUnserialized.h as it causes compilation trouble with
75 newer clang versions and the 10.15 SDK, and we really don't need it. Sample error:
76 libkern/c++/OSUnserialize.h:72:2: error: use of OSPtr outside of a return type [-Werror,-Wossharedptr-misuse] */
[378]77#include <IOKit/IOService.h>
[23491]78#include <IOKit/IOUserClient.h>
[15041]79#include <IOKit/pwr_mgt/RootDomain.h>
[46354]80#include <IOKit/pci/IOPCIDevice.h>
81#include <IOKit/IOBufferMemoryDescriptor.h>
82#include <IOKit/IOFilterInterruptEventSource.h>
[45459]83#include "VBoxGuestInternal.h"
[1]84
85
[57358]86/*********************************************************************************************************************************
87* Defined Constants And Macros *
88*********************************************************************************************************************************/
[44173]89/** The system device node name. */
[45459]90#define DEVICE_NAME_SYS "vboxguest"
[44173]91/** The user device node name. */
[45459]92#define DEVICE_NAME_USR "vboxguestu"
[1]93
94
[75705]95/** @name For debugging/whatever, now permanent.
96 * @{ */
97#define VBOX_PROC_SELFNAME_LEN 31
98#define VBOX_RETRIEVE_CUR_PROC_NAME(a_Name) char a_Name[VBOX_PROC_SELFNAME_LEN + 1]; \
99 proc_selfname(a_Name, VBOX_PROC_SELFNAME_LEN)
100/** @} */
101
[92621]102#ifndef minor
103/* The inlined C++ function version minor() takes the wrong parameter
104 type, uint32_t instead of dev_t. This kludge works around that. */
105# define minor(x) minor((uint32_t)(x))
106#endif
[75705]107
[92621]108
[57358]109/*********************************************************************************************************************************
110* Internal Functions *
111*********************************************************************************************************************************/
[20374]112RT_C_DECLS_BEGIN
[58113]113static kern_return_t vgdrvDarwinStart(struct kmod_info *pKModInfo, void *pvData);
114static kern_return_t vgdrvDarwinStop(struct kmod_info *pKModInfo, void *pvData);
115static int vgdrvDarwinCharDevRemove(void);
[1]116
[58113]117static int vgdrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
118static int vgdrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
119static int vgdrvDarwinIOCtlSlow(PVBOXGUESTSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess);
120static int vgdrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess);
[1]121
[58113]122static int vgdrvDarwinErr2DarwinErr(int rc);
[15041]123
[58113]124static IOReturn vgdrvDarwinSleepHandler(void *pvTarget, void *pvRefCon, UInt32 uMessageType, IOService *pProvider, void *pvMessageArgument, vm_size_t argSize);
[20374]125RT_C_DECLS_END
[1]126
127
[57358]128/*********************************************************************************************************************************
129* Structures and Typedefs *
130*********************************************************************************************************************************/
[378]131/**
[45459]132 * The service class for handling the VMMDev PCI device.
133 *
134 * Instantiated when the module is loaded (and on PCI hotplugging?).
[378]135 */
[45459]136class org_virtualbox_VBoxGuest : public IOService
[378]137{
[45459]138 OSDeclareDefaultStructors(org_virtualbox_VBoxGuest);
[378]139
[45459]140private:
141 IOPCIDevice *m_pIOPCIDevice;
142 IOMemoryMap *m_pMap;
143 IOFilterInterruptEventSource *m_pInterruptSrc;
144
145 bool setupVmmDevInterrupts(IOService *pProvider);
146 bool disableVmmDevInterrupts(void);
147 bool isVmmDev(IOPCIDevice *pIOPCIDevice);
148
[50701]149protected:
[75705]150 /** Non-NULL if interrupts are registered. Probably same as getProvider(). */
151 IOService *m_pInterruptProvider;
[50701]152
[378]153public:
[75705]154 virtual bool init(OSDictionary *pDictionary = 0);
155 virtual void free(void);
156 virtual IOService *probe(IOService *pProvider, SInt32 *pi32Score);
[378]157 virtual bool start(IOService *pProvider);
158 virtual void stop(IOService *pProvider);
[10391]159 virtual bool terminate(IOOptionBits fOptions);
[75705]160 static void vgdrvDarwinIrqHandler(OSObject *pTarget, void *pvRefCon, IOService *pNub, int iSrc);
[378]161};
162
[45459]163OSDefineMetaClassAndStructors(org_virtualbox_VBoxGuest, IOService);
[378]164
165
166/**
167 * An attempt at getting that clientDied() notification.
168 * I don't think it'll work as I cannot figure out where/what creates the correct
169 * port right.
[45459]170 *
171 * Instantiated when userland does IOServiceOpen().
[378]172 */
[45459]173class org_virtualbox_VBoxGuestClient : public IOUserClient
[378]174{
[45459]175 OSDeclareDefaultStructors(org_virtualbox_VBoxGuestClient);
[378]176
177private:
[75705]178 /** Guard against the parent class growing and us using outdated headers. */
179 uint8_t m_abSafetyPadding[256];
180
[45459]181 PVBOXGUESTSESSION m_pSession; /**< The session. */
182 task_t m_Task; /**< The client task. */
183 org_virtualbox_VBoxGuest *m_pProvider; /**< The service provider. */
[378]184
185public:
186 virtual bool initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type);
187 virtual bool start(IOService *pProvider);
[10836]188 static void sessionClose(RTPROCESS Process);
[378]189 virtual IOReturn clientClose(void);
[75705]190 virtual IOReturn clientDied(void);
191 virtual bool terminate(IOOptionBits fOptions = 0);
192 virtual bool finalize(IOOptionBits fOptions);
193 virtual void stop(IOService *pProvider);
194
195 RTR0MEMEF_NEW_AND_DELETE_OPERATORS_IOKIT();
[378]196};
197
[45459]198OSDefineMetaClassAndStructors(org_virtualbox_VBoxGuestClient, IOUserClient);
[378]199
200
201
[57358]202/*********************************************************************************************************************************
203* Global Variables *
204*********************************************************************************************************************************/
[1]205/**
206 * Declare the module stuff.
207 */
[20374]208RT_C_DECLS_BEGIN
[378]209extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData);
210extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData);
[1]211
[45459]212KMOD_EXPLICIT_DECL(VBoxGuest, VBOX_VERSION_STRING, _start, _stop)
[85124]213DECL_HIDDEN_DATA(kmod_start_func_t *) _realmain = vgdrvDarwinStart;
214DECL_HIDDEN_DATA(kmod_stop_func_t *) _antimain = vgdrvDarwinStop;
215DECL_HIDDEN_DATA(int) _kext_apple_cc = __APPLE_CC__;
[20374]216RT_C_DECLS_END
[378]217
218
[1]219/**
220 * Device extention & session data association structure.
221 */
[45459]222static VBOXGUESTDEVEXT g_DevExt;
[1]223
224/**
225 * The character device switch table for the driver.
226 */
227static struct cdevsw g_DevCW =
228{
[58113]229 /*.d_open = */ vgdrvDarwinOpen,
230 /*.d_close = */ vgdrvDarwinClose,
[45459]231 /*.d_read = */ eno_rdwrt,
232 /*.d_write = */ eno_rdwrt,
[58113]233 /*.d_ioctl = */ vgdrvDarwinIOCtl,
[45459]234 /*.d_stop = */ eno_stop,
235 /*.d_reset = */ eno_reset,
236 /*.d_ttys = */ NULL,
237 /*.d_select = */ eno_select,
238 /*.d_mmap = */ eno_mmap,
239 /*.d_strategy = */ eno_strat,
[63516]240 /*.d_getc = */ (void *)(uintptr_t)&enodev, //eno_getc,
241 /*.d_putc = */ (void *)(uintptr_t)&enodev, //eno_putc,
[45459]242 /*.d_type = */ 0
[1]243};
244
245/** Major device number. */
[75705]246static int g_iMajorDeviceNo = -1;
[45459]247/** Registered devfs device handle. */
[75705]248static void *g_hDevFsDeviceSys = NULL;
[44173]249/** Registered devfs device handle for the user device. */
[75705]250static void *g_hDevFsDeviceUsr = NULL; /**< @todo 4 later */
[1]251
252/** Spinlock protecting g_apSessionHashTab. */
[75705]253static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
[1]254/** Hash table */
[45459]255static PVBOXGUESTSESSION g_apSessionHashTab[19];
[1]256/** Calculates the index into g_apSessionHashTab.*/
[45459]257#define SESSION_HASH(pid) ((pid) % RT_ELEMENTS(g_apSessionHashTab))
[10391]258/** The number of open sessions. */
[75705]259static int32_t volatile g_cSessions = 0;
260/** Makes sure there is only one org_virtualbox_VBoxGuest instance. */
261static bool volatile g_fInstantiated = 0;
[15041]262/** The notifier handle for the sleep callback handler. */
[75705]263static IONotifier *g_pSleepNotifier = NULL;
[1]264
265
266/**
267 * Start the kernel module.
268 */
[58113]269static kern_return_t vgdrvDarwinStart(struct kmod_info *pKModInfo, void *pvData)
[1]270{
[63516]271 RT_NOREF(pKModInfo, pvData);
[75705]272#ifdef DEBUG
273 printf("vgdrvDarwinStart\n");
274#endif
275#if 0
276 gIOKitDebug |= 0x001 //kIOLogAttach
277 | 0x002 //kIOLogProbe
278 | 0x004 //kIOLogStart
279 | 0x008 //kIOLogRegister
280 | 0x010 //kIOLogMatch
281 | 0x020 //kIOLogConfig
282 ;
283#endif
[63516]284
[1]285 /*
[206]286 * Initialize IPRT.
[1]287 */
[45459]288 int rc = RTR0Init(0);
[58113]289 if (RT_SUCCESS(rc))
[1]290 {
[58113]291 Log(("VBoxGuest: driver loaded\n"));
292 return KMOD_RETURN_SUCCESS;
[45459]293 }
[15041]294
[75705]295 RTLogBackdoorPrintf("VBoxGuest: RTR0Init failed with rc=%Rrc\n", rc);
[58113]296 printf("VBoxGuest: RTR0Init failed with rc=%d\n", rc);
297 return KMOD_RETURN_FAILURE;
[45459]298}
[15041]299
[44173]300
[58113]301/**
[75705]302 * Stop the kernel module.
303 */
304static kern_return_t vgdrvDarwinStop(struct kmod_info *pKModInfo, void *pvData)
305{
306 RT_NOREF(pKModInfo, pvData);
307
308 /** @todo we need to check for VBoxSF clients? */
309
310 RTLogBackdoorPrintf("VBoxGuest: calling RTR0TermForced ...\n");
311 RTR0TermForced();
312
313 RTLogBackdoorPrintf("VBoxGuest: vgdrvDarwinStop returns.\n");
314 printf("VBoxGuest: driver unloaded\n");
315 return KMOD_RETURN_SUCCESS;
316}
317
318
319/**
[58113]320 * Register VBoxGuest char device
321 */
322static int vgdrvDarwinCharDevInit(void)
[45459]323{
324 int rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxGuestDarwin");
[58113]325 if (RT_SUCCESS(rc))
[45459]326 {
[58113]327 /*
328 * Registering ourselves as a character device.
329 */
330 g_iMajorDeviceNo = cdevsw_add(-1, &g_DevCW);
331 if (g_iMajorDeviceNo >= 0)
332 {
[75705]333 /** @todo limit /dev/vboxguest access. */
[92621]334 g_hDevFsDeviceSys = devfs_make_node(makedev((uint32_t)g_iMajorDeviceNo, 0), DEVFS_CHAR,
[58113]335 UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME_SYS);
336 if (g_hDevFsDeviceSys != NULL)
337 {
338 /*
[75705]339 * And a all-user device.
[58113]340 */
[92621]341 g_hDevFsDeviceUsr = devfs_make_node(makedev((uint32_t)g_iMajorDeviceNo, 1), DEVFS_CHAR,
[75705]342 UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME_USR);
343 if (g_hDevFsDeviceUsr != NULL)
[58113]344 {
[75705]345 /*
346 * Register a sleep/wakeup notification callback.
347 */
348 g_pSleepNotifier = registerPrioritySleepWakeInterest(&vgdrvDarwinSleepHandler, &g_DevExt, NULL);
349 if (g_pSleepNotifier != NULL)
350 return KMOD_RETURN_SUCCESS;
[58113]351 }
352 }
353 }
354 vgdrvDarwinCharDevRemove();
[45459]355 }
[58113]356 return KMOD_RETURN_FAILURE;
[43379]357}
358
359
360/**
[75705]361 * Unregister VBoxGuest char devices and associated session spinlock.
[1]362 */
[58113]363static int vgdrvDarwinCharDevRemove(void)
[45459]364{
[15041]365 if (g_pSleepNotifier)
366 {
367 g_pSleepNotifier->remove();
368 g_pSleepNotifier = NULL;
369 }
370
[45459]371 if (g_hDevFsDeviceSys)
372 {
373 devfs_remove(g_hDevFsDeviceSys);
374 g_hDevFsDeviceSys = NULL;
375 }
[1]376
[45459]377 if (g_hDevFsDeviceUsr)
378 {
379 devfs_remove(g_hDevFsDeviceUsr);
380 g_hDevFsDeviceUsr = NULL;
381 }
[44173]382
[45459]383 if (g_iMajorDeviceNo != -1)
384 {
385 int rc2 = cdevsw_remove(g_iMajorDeviceNo, &g_DevCW);
[63516]386 Assert(rc2 == g_iMajorDeviceNo); NOREF(rc2);
[45459]387 g_iMajorDeviceNo = -1;
388 }
[1]389
[45459]390 if (g_Spinlock != NIL_RTSPINLOCK)
391 {
392 int rc2 = RTSpinlockDestroy(g_Spinlock); AssertRC(rc2);
393 g_Spinlock = NIL_RTSPINLOCK;
394 }
[1]395
[75705]396 return KMOD_RETURN_SUCCESS;
[1]397}
398
399
400/**
[45459]401 * Device open. Called on open /dev/vboxguest and (later) /dev/vboxguestu.
[1]402 *
[44173]403 * @param Dev The device number.
404 * @param fFlags ???.
405 * @param fDevType ???.
406 * @param pProcess The process issuing this request.
[1]407 */
[58113]408static int vgdrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
[1]409{
[63516]410 RT_NOREF(fFlags, fDevType);
411
[1]412 /*
[44173]413 * Only two minor devices numbers are allowed.
414 */
415 if (minor(Dev) != 0 && minor(Dev) != 1)
416 return EACCES;
417
418 /*
[75705]419 * The process issuing the request must be the current process.
420 */
421 RTPROCESS Process = RTProcSelf();
422 if ((int)Process != proc_pid(pProcess))
423 return EIO;
424
425 /*
[45459]426 * Find the session created by org_virtualbox_VBoxGuestClient, fail
[10836]427 * if no such session, and mark it as opened. We set the uid & gid
428 * here too, since that is more straight forward at this point.
[1]429 */
[75705]430 const bool fUnrestricted = minor(Dev) == 0;
[45459]431 int rc = VINF_SUCCESS;
432 PVBOXGUESTSESSION pSession = NULL;
433 kauth_cred_t pCred = kauth_cred_proc_ref(pProcess);
[10836]434 if (pCred)
[1]435 {
[75777]436#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
437 RTUID Uid = kauth_cred_getruid(pCred);
438 RTGID Gid = kauth_cred_getrgid(pCred);
439#else
440 RTUID Uid = pCred->cr_ruid;
441 RTGID Gid = pCred->cr_rgid;
442#endif
[10836]443 unsigned iHash = SESSION_HASH(Process);
[40806]444 RTSpinlockAcquire(g_Spinlock);
[10836]445
446 pSession = g_apSessionHashTab[iHash];
[44173]447 while (pSession && pSession->Process != Process)
448 pSession = pSession->pNextHash;
[10836]449 if (pSession)
450 {
451 if (!pSession->fOpened)
452 {
453 pSession->fOpened = true;
[75705]454 pSession->fUserSession = !fUnrestricted;
[75777]455 pSession->fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN;
456 if (Uid == 0)
457 pSession->fRequestor |= VMMDEV_REQUESTOR_USR_ROOT;
458 else
459 pSession->fRequestor |= VMMDEV_REQUESTOR_USR_USER;
460 if (Gid == 0)
461 pSession->fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL;
462 if (!fUnrestricted)
463 pSession->fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE;
[75780]464 pSession->fRequestor |= VMMDEV_REQUESTOR_CON_DONT_KNOW; /** @todo see if we can figure out console relationship of pProc. */
[10836]465 }
466 else
467 rc = VERR_ALREADY_LOADED;
468 }
469 else
470 rc = VERR_GENERAL_FAILURE;
[1]471
[52618]472 RTSpinlockRelease(g_Spinlock);
[16356]473#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
[16352]474 kauth_cred_unref(&pCred);
[16356]475#else /* 10.4 */
[33540]476 /* The 10.4u SDK headers and 10.4.11 kernel source have inconsistent definitions
[16356]477 of kauth_cred_unref(), so use the other (now deprecated) API for releasing it. */
478 kauth_cred_rele(pCred);
479#endif /* 10.4 */
[1]480 }
[10836]481 else
[25466]482 rc = VERR_INVALID_PARAMETER;
[1]483
[58113]484 Log(("vgdrvDarwinOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, proc_pid(pProcess)));
485 return vgdrvDarwinErr2DarwinErr(rc);
[1]486}
487
488
489/**
490 * Close device.
491 */
[58113]492static int vgdrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
[1]493{
[63516]494 RT_NOREF(Dev, fFlags, fDevType, pProcess);
[58113]495 Log(("vgdrvDarwinClose: pid=%d\n", (int)RTProcSelf()));
[10836]496 Assert(proc_pid(pProcess) == (int)RTProcSelf());
[1]497
498 /*
[45459]499 * Hand the session closing to org_virtualbox_VBoxGuestClient.
[1]500 */
[45459]501 org_virtualbox_VBoxGuestClient::sessionClose(RTProcSelf());
[1]502 return 0;
503}
504
505
506/**
507 * Device I/O Control entry point.
508 *
509 * @returns Darwin for slow IOCtls and VBox status code for the fast ones.
510 * @param Dev The device number (major+minor).
511 * @param iCmd The IOCtl command.
[75705]512 * @param pData Pointer to the request data.
[1]513 * @param fFlags Flag saying we're a character device (like we didn't know already).
514 * @param pProcess The process issuing this request.
515 */
[58113]516static int vgdrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess)
[1]517{
[63516]518 RT_NOREF(Dev, fFlags);
[75705]519 const bool fUnrestricted = minor(Dev) == 0;
[92621]520 const RTPROCESS Process = (RTPROCESS)proc_pid(pProcess);
[1]521 const unsigned iHash = SESSION_HASH(Process);
[45459]522 PVBOXGUESTSESSION pSession;
[1]523
524 /*
525 * Find the session.
526 */
[40806]527 RTSpinlockAcquire(g_Spinlock);
[1]528 pSession = g_apSessionHashTab[iHash];
[75705]529 while (pSession && (pSession->Process != Process || pSession->fUserSession == fUnrestricted || !pSession->fOpened))
[44173]530 pSession = pSession->pNextHash;
[75705]531
532 //if (RT_LIKELY(pSession))
533 // supdrvSessionRetain(pSession);
534
[52618]535 RTSpinlockRelease(g_Spinlock);
[1]536 if (!pSession)
537 {
[75705]538 Log(("VBoxDrvDarwinIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#lx\n",
539 (int)Process, iCmd));
[1]540 return EINVAL;
541 }
542
543 /*
[68583]544 * Deal with the high-speed IOCtl.
[1]545 */
[75705]546 int rc;
[68583]547 if (VBGL_IOCTL_IS_FAST(iCmd))
[75705]548 rc = VGDrvCommonIoCtlFast(iCmd, &g_DevExt, pSession);
549 else
550 rc = vgdrvDarwinIOCtlSlow(pSession, iCmd, pData, pProcess);
[45459]551
[75705]552 //supdrvSessionRelease(pSession);
553 return rc;
[1]554}
555
556
557/**
[68583]558 * Worker for VBoxDrvDarwinIOCtl that takes the slow IOCtl functions.
[1]559 *
560 * @returns Darwin errno.
561 *
562 * @param pSession The session.
563 * @param iCmd The IOCtl command.
[68583]564 * @param pData Pointer to the request data.
[1]565 * @param pProcess The calling process.
566 */
[58113]567static int vgdrvDarwinIOCtlSlow(PVBOXGUESTSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess)
[1]568{
[63516]569 RT_NOREF(pProcess);
[58113]570 LogFlow(("vgdrvDarwinIOCtlSlow: pSession=%p iCmd=%p pData=%p pProcess=%p\n", pSession, iCmd, pData, pProcess));
[1]571
[4817]572
[1]573 /*
[4817]574 * Buffered or unbuffered?
[1]575 */
[68583]576 PVBGLREQHDR pHdr;
[4818]577 user_addr_t pUser = 0;
[4817]578 void *pvPageBuf = NULL;
[4818]579 uint32_t cbReq = IOCPARM_LEN(iCmd);
580 if ((IOC_DIRMASK & iCmd) == IOC_INOUT)
[1]581 {
[68583]582 pHdr = (PVBGLREQHDR)pData;
583 if (RT_UNLIKELY(cbReq < sizeof(*pHdr)))
584 {
585 LogRel(("vgdrvDarwinIOCtlSlow: cbReq=%#x < %#x; iCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), iCmd));
586 return EINVAL;
587 }
588 if (RT_UNLIKELY(pHdr->uVersion != VBGLREQHDR_VERSION))
589 {
590 LogRel(("vgdrvDarwinIOCtlSlow: bad uVersion=%#x; iCmd=%#lx\n", pHdr->uVersion, iCmd));
591 return EINVAL;
592 }
593 if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq
594 || pHdr->cbIn < sizeof(*pHdr)
595 || (pHdr->cbOut < sizeof(*pHdr) && pHdr->cbOut != 0)))
596 {
597 LogRel(("vgdrvDarwinIOCtlSlow: max(%#x,%#x) != %#x; iCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, iCmd));
598 return EINVAL;
599 }
[1]600 }
[4818]601 else if ((IOC_DIRMASK & iCmd) == IOC_VOID && !cbReq)
[1]602 {
[9498]603 /*
[4817]604 * Get the header and figure out how much we're gonna have to read.
[9498]605 */
[68583]606 VBGLREQHDR Hdr;
[4818]607 pUser = (user_addr_t)*(void **)pData;
608 int rc = copyin(pUser, &Hdr, sizeof(Hdr));
[4817]609 if (RT_UNLIKELY(rc))
[1]610 {
[68583]611 LogRel(("vgdrvDarwinIOCtlSlow: copyin(%llx,Hdr,) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, rc, iCmd));
[4817]612 return rc;
613 }
[68583]614 if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION))
[4817]615 {
[68583]616 LogRel(("vgdrvDarwinIOCtlSlow: bad uVersion=%#x; iCmd=%#lx\n", Hdr.uVersion, iCmd));
[4817]617 return EINVAL;
618 }
[68583]619 cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut);
620 if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr)
621 || (Hdr.cbOut < sizeof(Hdr) && Hdr.cbOut != 0)
622 || cbReq > _1M*16))
[4817]623 {
[68583]624 LogRel(("vgdrvDarwinIOCtlSlow: max(%#x,%#x); iCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, iCmd));
[4817]625 return EINVAL;
626 }
627
628 /*
629 * Allocate buffer and copy in the data.
630 */
[68583]631 pHdr = (PVBGLREQHDR)RTMemTmpAlloc(cbReq);
632 if (!pHdr)
633 pvPageBuf = pHdr = (PVBGLREQHDR)IOMallocAligned(RT_ALIGN_Z(cbReq, PAGE_SIZE), 8);
634 if (RT_UNLIKELY(!pHdr))
[4817]635 {
[68583]636 LogRel(("vgdrvDarwinIOCtlSlow: failed to allocate buffer of %d bytes; iCmd=%#lx\n", cbReq, iCmd));
[1]637 return ENOMEM;
638 }
[68583]639 rc = copyin(pUser, pHdr, Hdr.cbIn);
[4817]640 if (RT_UNLIKELY(rc))
[1]641 {
[68583]642 LogRel(("vgdrvDarwinIOCtlSlow: copyin(%llx,%p,%#x) -> %#x; iCmd=%#lx\n",
643 (unsigned long long)pUser, pHdr, Hdr.cbIn, rc, iCmd));
[219]644 if (pvPageBuf)
[4818]645 IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
[219]646 else
[68583]647 RTMemTmpFree(pHdr);
[1]648 return rc;
649 }
[68583]650 if (Hdr.cbIn < cbReq)
651 RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbReq - Hdr.cbIn);
[1]652 }
[4818]653 else
654 {
[58113]655 Log(("vgdrvDarwinIOCtlSlow: huh? cbReq=%#x iCmd=%#lx\n", cbReq, iCmd));
[4818]656 return EINVAL;
657 }
[1]658
659 /*
660 * Process the IOCtl.
661 */
[68583]662 int rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, cbReq);
663 if (RT_LIKELY(!rc))
[1]664 {
[4817]665 /*
666 * If not buffered, copy back the buffer before returning.
667 */
[4818]668 if (pUser)
[1]669 {
[68583]670 uint32_t cbOut = pHdr->cbOut;
671 if (cbOut > cbReq)
[1]672 {
[68583]673 LogRel(("vgdrvDarwinIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, iCmd));
674 cbOut = cbReq;
[1]675 }
[68583]676 rc = copyout(pHdr, pUser, cbOut);
[4817]677 if (RT_UNLIKELY(rc))
[68583]678 LogRel(("vgdrvDarwinIOCtlSlow: copyout(%p,%llx,%#x) -> %d; uCmd=%#lx!\n",
679 pHdr, (unsigned long long)pUser, cbOut, rc, iCmd));
[4817]680
681 /* cleanup */
682 if (pvPageBuf)
[4818]683 IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
[4817]684 else
[68583]685 RTMemTmpFree(pHdr);
[1]686 }
[4817]687 }
688 else
689 {
[9498]690 /*
[4817]691 * The request failed, just clean up.
692 */
[4818]693 if (pUser)
[1]694 {
[4817]695 if (pvPageBuf)
[4818]696 IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
[4817]697 else
[68583]698 RTMemTmpFree(pHdr);
[1]699 }
[4817]700
[58113]701 Log(("vgdrvDarwinIOCtlSlow: pid=%d iCmd=%lx pData=%p failed, rc=%d\n", proc_pid(pProcess), iCmd, (void *)pData, rc));
[4817]702 rc = EINVAL;
[1]703 }
704
[58113]705 Log2(("vgdrvDarwinIOCtlSlow: returns %d\n", rc));
[1]706 return rc;
707}
708
709
[68583]710/**
711 * @note This code is duplicated on other platforms with variations, so please
712 * keep them all up to date when making changes!
[10263]713 */
[68583]714int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
715{
716 /*
717 * Simple request validation (common code does the rest).
718 */
719 int rc;
720 if ( RT_VALID_PTR(pReqHdr)
721 && cbReq >= sizeof(*pReqHdr))
722 {
723 /*
724 * All requests except the connect one requires a valid session.
725 */
726 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
727 if (pSession)
728 {
729 if ( RT_VALID_PTR(pSession)
730 && pSession->pDevExt == &g_DevExt)
731 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
732 else
733 rc = VERR_INVALID_HANDLE;
734 }
735 else if (uReq == VBGL_IOCTL_IDC_CONNECT)
736 {
737 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
738 if (RT_SUCCESS(rc))
739 {
740 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
741 if (RT_FAILURE(rc))
742 VGDrvCommonCloseSession(&g_DevExt, pSession);
743 }
744 }
745 else
746 rc = VERR_INVALID_HANDLE;
747 }
748 else
749 rc = VERR_INVALID_POINTER;
750 return rc;
751}
[1]752
[10263]753
[58053]754void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
[1]755{
[45459]756 NOREF(pDevExt);
[1]757}
758
759
[70066]760bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
761{
762 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
763 return false;
764}
765
766
[1]767/**
[15130]768 * Callback for blah blah blah.
[75705]769 *
770 * @todo move to IPRT.
[15130]771 */
[58113]772static IOReturn vgdrvDarwinSleepHandler(void *pvTarget, void *pvRefCon, UInt32 uMessageType,
773 IOService *pProvider, void *pvMsgArg, vm_size_t cbMsgArg)
[15041]774{
[63516]775 RT_NOREF(pvTarget, pProvider, pvMsgArg, cbMsgArg);
[58113]776 LogFlow(("VBoxGuest: Got sleep/wake notice. Message type was %x\n", uMessageType));
[15041]777
778 if (uMessageType == kIOMessageSystemWillSleep)
779 RTPowerSignalEvent(RTPOWEREVENT_SUSPEND);
780 else if (uMessageType == kIOMessageSystemHasPoweredOn)
781 RTPowerSignalEvent(RTPOWEREVENT_RESUME);
782
783 acknowledgeSleepWakeNotification(pvRefCon);
784
785 return 0;
786}
787
[9498]788
[14901]789/**
[45459]790 * Converts an IPRT error code to a darwin error code.
791 *
792 * @returns corresponding darwin error code.
793 * @param rc IPRT status code.
[14901]794 */
[58113]795static int vgdrvDarwinErr2DarwinErr(int rc)
[14901]796{
[45459]797 switch (rc)
[14901]798 {
[45459]799 case VINF_SUCCESS: return 0;
800 case VERR_GENERAL_FAILURE: return EACCES;
801 case VERR_INVALID_PARAMETER: return EINVAL;
802 case VERR_INVALID_MAGIC: return EILSEQ;
803 case VERR_INVALID_HANDLE: return ENXIO;
804 case VERR_INVALID_POINTER: return EFAULT;
805 case VERR_LOCK_FAILED: return ENOLCK;
806 case VERR_ALREADY_LOADED: return EEXIST;
807 case VERR_PERMISSION_DENIED: return EPERM;
808 case VERR_VERSION_MISMATCH: return ENOSYS;
[15003]809 }
[45459]810
811 return EPERM;
[15003]812}
813
814
[45459]815/*
816 *
817 * org_virtualbox_VBoxGuest
818 *
[75705]819 * - IOService diff resync -
820 * - IOService diff resync -
821 * - IOService diff resync -
822 *
[43379]823 */
[45459]824
[50701]825
[58113]826/**
[75705]827 * Initialize the object.
828 */
829bool org_virtualbox_VBoxGuest::init(OSDictionary *pDictionary)
830{
831 LogFlow(("IOService::init([%p], %p)\n", this, pDictionary));
832 if (IOService::init(pDictionary))
833 {
834 /* init members. */
835 return true;
836 }
837 return false;
838}
839
840
841/**
842 * Free the object.
843 */
844void org_virtualbox_VBoxGuest::free(void)
845{
[75712]846 RTLogBackdoorPrintf("IOService::free([%p])\n", this); /* might go sideways if we use LogFlow() here. weird. */
[75705]847 IOService::free();
848}
849
850
851/**
852 * Check if it's ok to start this service.
853 * It's always ok by us, so it's up to IOService to decide really.
854 */
855IOService *org_virtualbox_VBoxGuest::probe(IOService *pProvider, SInt32 *pi32Score)
856{
857 LogFlow(("IOService::probe([%p])\n", this));
858 IOService *pRet = IOService::probe(pProvider, pi32Score);
859 LogFlow(("IOService::probe([%p]) returns %p *pi32Score=%d\n", this, pRet, pi32Score ? *pi32Score : -1));
860 return pRet;
861}
862
863
864/**
865 * Start this service.
866 */
867bool org_virtualbox_VBoxGuest::start(IOService *pProvider)
868{
869 LogFlow(("IOService::start([%p])\n", this));
870
871 /*
872 * Low level initialization / device initialization should be performed only once.
873 */
874 if (ASMAtomicCmpXchgBool(&g_fInstantiated, true, false))
875 {
876 /*
877 * Make sure it's a PCI device.
878 */
879 m_pIOPCIDevice = OSDynamicCast(IOPCIDevice, pProvider);
880 if (m_pIOPCIDevice)
881 {
882 /*
883 * Call parent.
884 */
885 if (IOService::start(pProvider))
886 {
887 /*
888 * Is it the VMM device?
889 */
890 if (isVmmDev(m_pIOPCIDevice))
891 {
892 /*
893 * Enable I/O port and memory regions on the device.
894 */
895 m_pIOPCIDevice->setMemoryEnable(true);
896 m_pIOPCIDevice->setIOEnable(true);
897
898 /*
899 * Region #0: I/O ports. Mandatory.
900 */
901 IOMemoryDescriptor *pMem = m_pIOPCIDevice->getDeviceMemoryWithIndex(0);
902 if (pMem)
903 {
904 IOPhysicalAddress IOPortBasePhys = pMem->getPhysicalAddress();
905 if ((IOPortBasePhys >> 16) == 0)
906 {
907 RTIOPORT IOPortBase = (RTIOPORT)IOPortBasePhys;
908 void *pvMMIOBase = NULL;
909 uint32_t cbMMIO = 0;
910
911 /*
912 * Region #1: Shared Memory. Technically optional.
913 */
914 m_pMap = m_pIOPCIDevice->mapDeviceMemoryWithIndex(1);
915 if (m_pMap)
916 {
917 pvMMIOBase = (void *)m_pMap->getVirtualAddress();
[92621]918 cbMMIO = (uint32_t)m_pMap->getLength();
[75705]919 }
920
921 /*
922 * Initialize the device extension.
923 */
[100267]924 int rc = VGDrvCommonInitDevExt(&g_DevExt, IOPortBase, NULL /*pvMmioReq*/, pvMMIOBase, cbMMIO,
[75705]925 ARCH_BITS == 64 ? VBOXOSTYPE_MacOS_x64 : VBOXOSTYPE_MacOS, 0);
926 if (RT_SUCCESS(rc))
927 {
928 /*
929 * Register the device nodes and enable interrupts.
930 */
931 rc = vgdrvDarwinCharDevInit();
932 if (rc == KMOD_RETURN_SUCCESS)
933 {
934 if (setupVmmDevInterrupts(pProvider))
935 {
936 /*
937 * Read host configuration.
938 */
939 VGDrvCommonProcessOptionsFromHost(&g_DevExt);
940
941 /*
942 * Just register the service and we're done!
943 */
944 registerService();
945
946 LogRel(("VBoxGuest: IOService started\n"));
947 return true;
948 }
949
950 LogRel(("VBoxGuest: Failed to set up interrupts\n"));
951 vgdrvDarwinCharDevRemove();
952 }
953 else
954 LogRel(("VBoxGuest: Failed to initialize character devices (rc=%#x).\n", rc));
955
956 VGDrvCommonDeleteDevExt(&g_DevExt);
957 }
958 else
959 LogRel(("VBoxGuest: Failed to initialize common code (rc=%Rrc).\n", rc));
960
961 if (m_pMap)
962 {
963 m_pMap->release();
964 m_pMap = NULL;
965 }
966 }
967 else
968 LogRel(("VBoxGuest: Bad I/O port address: %#RX64\n", (uint64_t)IOPortBasePhys));
969 }
970 else
971 LogRel(("VBoxGuest: The device missing is the I/O port range (#0).\n"));
972 }
973 else
974 LogRel(("VBoxGuest: Not the VMMDev (%#x:%#x).\n",
975 m_pIOPCIDevice->configRead16(kIOPCIConfigVendorID), m_pIOPCIDevice->configRead16(kIOPCIConfigDeviceID)));
976
977 IOService::stop(pProvider);
978 }
979 }
980 else
981 LogRel(("VBoxGuest: Provider is not an instance of IOPCIDevice.\n"));
982
983 ASMAtomicXchgBool(&g_fInstantiated, false);
984 }
985 return false;
986}
987
988
989/**
990 * Stop this service.
991 */
992void org_virtualbox_VBoxGuest::stop(IOService *pProvider)
993{
994#ifdef LOG_ENABLED
995 RTLogBackdoorPrintf("org_virtualbox_VBoxGuest::stop([%p], %p)\n", this, pProvider); /* Being cautious here, no Log(). */
996#endif
997 AssertReturnVoid(ASMAtomicReadBool(&g_fInstantiated));
998
999 /* Low level termination should be performed only once */
1000 if (!disableVmmDevInterrupts())
1001 printf("VBoxGuest: unable to unregister interrupt handler\n");
1002
1003 vgdrvDarwinCharDevRemove();
1004 VGDrvCommonDeleteDevExt(&g_DevExt);
1005
1006 if (m_pMap)
1007 {
1008 m_pMap->release();
1009 m_pMap = NULL;
1010 }
1011
1012 IOService::stop(pProvider);
1013
1014 ASMAtomicWriteBool(&g_fInstantiated, false);
1015
1016 printf("VBoxGuest: IOService stopped\n");
1017 RTLogBackdoorPrintf("org_virtualbox_VBoxGuest::stop: returning\n"); /* Being cautious here, no Log(). */
1018}
1019
1020
1021/**
1022 * Termination request.
1023 *
1024 * @return true if we're ok with shutting down now, false if we're not.
1025 * @param fOptions Flags.
1026 */
1027bool org_virtualbox_VBoxGuest::terminate(IOOptionBits fOptions)
1028{
1029#ifdef LOG_ENABLED
1030 RTLogBackdoorPrintf("org_virtualbox_VBoxGuest::terminate: reference_count=%d g_cSessions=%d (fOptions=%#x)\n",
1031 KMOD_INFO_NAME.reference_count, ASMAtomicUoReadS32(&g_cSessions), fOptions); /* Being cautious here, no Log(). */
1032#endif
1033
1034 bool fRc;
1035 if ( KMOD_INFO_NAME.reference_count != 0
1036 || ASMAtomicUoReadS32(&g_cSessions))
1037 fRc = false;
1038 else
1039 fRc = IOService::terminate(fOptions);
1040
1041#ifdef LOG_ENABLED
1042 RTLogBackdoorPrintf("org_virtualbox_SupDrv::terminate: returns %d\n", fRc); /* Being cautious here, no Log(). */
1043#endif
1044 return fRc;
1045}
1046
1047
1048/**
1049 * Implementes a IOInterruptHandler, called by provider when an interrupt occurs.
1050 */
1051/*static*/ void org_virtualbox_VBoxGuest::vgdrvDarwinIrqHandler(OSObject *pTarget, void *pvRefCon, IOService *pNub, int iSrc)
[45459]1052{
[75712]1053#ifdef LOG_ENABLED
[75705]1054 RTLogBackdoorPrintf("vgdrvDarwinIrqHandler: %p %p %p %d\n", pTarget, pvRefCon, pNub, iSrc);
[75712]1055#endif
[76032]1056 RT_NOREF(pTarget, pvRefCon, pNub, iSrc);
[25258]1057
[75712]1058 VGDrvCommonISR(&g_DevExt);
1059 /* There is in fact no way of indicating that this is our interrupt, other
1060 than making the device lower it. So, the return code is ignored. */
[25258]1061}
1062
1063
[1]1064/**
[75705]1065 * Sets up and enables interrupts on the device.
1066 *
1067 * Interrupts are handled directly, no messing around with workloops. The
1068 * rational here is is that the main job of our interrupt handler is waking up
1069 * other threads currently sitting in HGCM calls, i.e. little more effort than
1070 * waking up the workloop thread.
1071 *
1072 * @returns success indicator. Failures are fully logged.
[1]1073 */
[75705]1074bool org_virtualbox_VBoxGuest::setupVmmDevInterrupts(IOService *pProvider)
[1]1075{
[75705]1076 AssertReturn(pProvider, false);
[45459]1077
[75705]1078 if (m_pInterruptProvider != pProvider)
1079 {
[75714]1080 pProvider->retain();
1081 if (m_pInterruptProvider)
[75705]1082 m_pInterruptProvider->release();
1083 m_pInterruptProvider = pProvider;
1084 }
[1]1085
[75705]1086 IOReturn rc = pProvider->registerInterrupt(0 /*intIndex*/, this, vgdrvDarwinIrqHandler, this);
1087 if (rc == kIOReturnSuccess)
[45459]1088 {
[75705]1089 rc = pProvider->enableInterrupt(0 /*intIndex*/);
1090 if (rc == kIOReturnSuccess)
1091 return true;
[1]1092
[75705]1093 LogRel(("VBoxGuest: Failed to enable interrupt: %#x\n", rc));
1094 m_pInterruptProvider->unregisterInterrupt(0 /*intIndex*/);
[45459]1095 }
1096 else
[75705]1097 LogRel(("VBoxGuest: Failed to register interrupt: %#x\n", rc));
[378]1098 return false;
1099}
1100
1101
1102/**
[75705]1103 * Counterpart to setupVmmDevInterrupts().
[378]1104 */
[75705]1105bool org_virtualbox_VBoxGuest::disableVmmDevInterrupts(void)
[378]1106{
[75705]1107 if (m_pInterruptProvider)
1108 {
1109 IOReturn rc = m_pInterruptProvider->disableInterrupt(0 /*intIndex*/);
1110 AssertMsg(rc == kIOReturnSuccess, ("%#x\n", rc));
1111 rc = m_pInterruptProvider->unregisterInterrupt(0 /*intIndex*/);
1112 AssertMsg(rc == kIOReturnSuccess, ("%#x\n", rc));
1113 RT_NOREF_PV(rc);
[378]1114
[75705]1115 m_pInterruptProvider->release();
1116 m_pInterruptProvider = NULL;
[378]1117 }
1118
[75705]1119 return true;
[378]1120}
1121
1122
[10391]1123/**
[75705]1124 * Checks if it's the VMM device.
[10391]1125 *
[75705]1126 * @returns true if it is, false if it isn't.
1127 * @param pIOPCIDevice The PCI device we think might be the VMM device.
[10391]1128 */
[75705]1129bool org_virtualbox_VBoxGuest::isVmmDev(IOPCIDevice *pIOPCIDevice)
[10391]1130{
[75705]1131 if (pIOPCIDevice)
1132 {
1133 uint16_t idVendor = m_pIOPCIDevice->configRead16(kIOPCIConfigVendorID);
1134 if (idVendor == VMMDEV_VENDORID)
1135 {
1136 uint16_t idDevice = m_pIOPCIDevice->configRead16(kIOPCIConfigDeviceID);
1137 if (idDevice == VMMDEV_DEVICEID)
1138 return true;
1139 }
1140 }
1141 return false;
1142}
[50699]1143
[58113]1144
[10391]1145
[378]1146/*
1147 *
[45459]1148 * org_virtualbox_VBoxGuestClient
[378]1149 *
1150 */
1151
1152
1153/**
1154 * Initializer called when the client opens the service.
1155 */
[45459]1156bool org_virtualbox_VBoxGuestClient::initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type)
[378]1157{
[45459]1158 LogFlow(("org_virtualbox_VBoxGuestClient::initWithTask([%p], %#x, %p, %#x) (cur pid=%d proc=%p)\n",
[10836]1159 this, OwningTask, pvSecurityId, u32Type, RTProcSelf(), RTR0ProcHandleSelf()));
1160 AssertMsg((RTR0PROCESS)OwningTask == RTR0ProcHandleSelf(), ("%p %p\n", OwningTask, RTR0ProcHandleSelf()));
[378]1161
1162 if (!OwningTask)
1163 return false;
[51490]1164
1165 if (u32Type != VBOXGUEST_DARWIN_IOSERVICE_COOKIE)
1166 {
[75705]1167 VBOX_RETRIEVE_CUR_PROC_NAME(szProcName);
1168 LogRelMax(10, ("org_virtualbox_VBoxGuestClient::initWithTask: Bad cookie %#x (%s)\n", u32Type, szProcName));
[51490]1169 return false;
1170 }
1171
[378]1172 if (IOUserClient::initWithTask(OwningTask, pvSecurityId , u32Type))
1173 {
[64432]1174 /*
1175 * In theory we have to call task_reference() to make sure that the task is
1176 * valid during the lifetime of this object. The pointer is only used to check
1177 * for the context this object is called in though and never dereferenced
1178 * or passed to anything which might, so we just skip this step.
1179 */
[378]1180 m_Task = OwningTask;
1181 m_pSession = NULL;
1182 m_pProvider = NULL;
1183 return true;
1184 }
1185 return false;
1186}
1187
1188
1189/**
1190 * Start the client service.
1191 */
[45459]1192bool org_virtualbox_VBoxGuestClient::start(IOService *pProvider)
[378]1193{
[45459]1194 LogFlow(("org_virtualbox_VBoxGuestClient::start([%p], %p) (cur pid=%d proc=%p)\n",
[10836]1195 this, pProvider, RTProcSelf(), RTR0ProcHandleSelf() ));
1196 AssertMsgReturn((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(),
1197 ("%p %p\n", m_Task, RTR0ProcHandleSelf()),
1198 false);
1199
[378]1200 if (IOUserClient::start(pProvider))
1201 {
[45459]1202 m_pProvider = OSDynamicCast(org_virtualbox_VBoxGuest, pProvider);
[378]1203 if (m_pProvider)
1204 {
[10836]1205 Assert(!m_pSession);
1206
1207 /*
1208 * Create a new session.
[75777]1209 * Note! We complete the requestor stuff in the open method.
[10836]1210 */
[70873]1211 int rc = VGDrvCommonCreateUserSession(&g_DevExt, VMMDEV_REQUESTOR_USERMODE, &m_pSession);
[10836]1212 if (RT_SUCCESS(rc))
1213 {
1214 m_pSession->fOpened = false;
[75705]1215 /* The Uid, Gid and fUnrestricted fields are set on open. */
[10836]1216
1217 /*
1218 * Insert it into the hash table, checking that there isn't
[44173]1219 * already one for this process first. (One session per proc!)
[10836]1220 */
1221 unsigned iHash = SESSION_HASH(m_pSession->Process);
[40806]1222 RTSpinlockAcquire(g_Spinlock);
[10836]1223
[45459]1224 PVBOXGUESTSESSION pCur = g_apSessionHashTab[iHash];
[75705]1225 while (pCur && pCur->Process != m_pSession->Process)
1226 pCur = pCur->pNextHash;
[10836]1227 if (!pCur)
1228 {
1229 m_pSession->pNextHash = g_apSessionHashTab[iHash];
1230 g_apSessionHashTab[iHash] = m_pSession;
[45459]1231 m_pSession->pvVBoxGuestClient = this;
[10836]1232 ASMAtomicIncS32(&g_cSessions);
1233 rc = VINF_SUCCESS;
1234 }
1235 else
1236 rc = VERR_ALREADY_LOADED;
1237
[45459]1238 RTSpinlockRelease(g_Spinlock);
[10836]1239 if (RT_SUCCESS(rc))
1240 {
[45459]1241 Log(("org_virtualbox_VBoxGuestClient::start: created session %p for pid %d\n", m_pSession, (int)RTProcSelf()));
[10836]1242 return true;
1243 }
1244
[45459]1245 LogFlow(("org_virtualbox_VBoxGuestClient::start: already got a session for this process (%p)\n", pCur));
[75705]1246 VGDrvCommonCloseSession(&g_DevExt, m_pSession); //supdrvSessionRelease(m_pSession);
[10836]1247 }
1248
1249 m_pSession = NULL;
[45459]1250 LogFlow(("org_virtualbox_VBoxGuestClient::start: rc=%Rrc from supdrvCreateSession\n", rc));
[378]1251 }
[10836]1252 else
[45459]1253 LogFlow(("org_virtualbox_VBoxGuestClient::start: %p isn't org_virtualbox_VBoxGuest\n", pProvider));
[378]1254 }
1255 return false;
1256}
1257
1258
1259/**
[10836]1260 * Common worker for clientClose and VBoxDrvDarwinClose.
1261 */
[45459]1262/* static */ void org_virtualbox_VBoxGuestClient::sessionClose(RTPROCESS Process)
[10836]1263{
1264 /*
[44173]1265 * Find the session and remove it from the hash table.
1266 *
1267 * Note! Only one session per process. (Both start() and
[58113]1268 * vgdrvDarwinOpen makes sure this is so.)
[10836]1269 */
1270 const unsigned iHash = SESSION_HASH(Process);
[40806]1271 RTSpinlockAcquire(g_Spinlock);
[45459]1272 PVBOXGUESTSESSION pSession = g_apSessionHashTab[iHash];
[10836]1273 if (pSession)
1274 {
1275 if (pSession->Process == Process)
1276 {
1277 g_apSessionHashTab[iHash] = pSession->pNextHash;
1278 pSession->pNextHash = NULL;
1279 ASMAtomicDecS32(&g_cSessions);
1280 }
1281 else
1282 {
[45459]1283 PVBOXGUESTSESSION pPrev = pSession;
[10836]1284 pSession = pSession->pNextHash;
1285 while (pSession)
1286 {
1287 if (pSession->Process == Process)
1288 {
1289 pPrev->pNextHash = pSession->pNextHash;
1290 pSession->pNextHash = NULL;
1291 ASMAtomicDecS32(&g_cSessions);
1292 break;
1293 }
1294
1295 /* next */
1296 pPrev = pSession;
1297 pSession = pSession->pNextHash;
1298 }
1299 }
1300 }
[45459]1301 RTSpinlockRelease(g_Spinlock);
[10836]1302 if (!pSession)
1303 {
[45459]1304 Log(("VBoxGuestClient::sessionClose: pSession == NULL, pid=%d; freed already?\n", (int)Process));
[10836]1305 return;
1306 }
1307
1308 /*
1309 * Remove it from the client object.
1310 */
[45459]1311 org_virtualbox_VBoxGuestClient *pThis = (org_virtualbox_VBoxGuestClient *)pSession->pvVBoxGuestClient;
1312 pSession->pvVBoxGuestClient = NULL;
[10836]1313 if (pThis)
1314 {
1315 Assert(pThis->m_pSession == pSession);
1316 pThis->m_pSession = NULL;
1317 }
1318
1319 /*
1320 * Close the session.
1321 */
[75705]1322 VGDrvCommonCloseSession(&g_DevExt, pSession); // supdrvSessionRelease(m_pSession);
[10836]1323}
1324
1325
1326/**
[378]1327 * Client exits normally.
1328 */
[45459]1329IOReturn org_virtualbox_VBoxGuestClient::clientClose(void)
[378]1330{
[45459]1331 LogFlow(("org_virtualbox_VBoxGuestClient::clientClose([%p]) (cur pid=%d proc=%p)\n", this, RTProcSelf(), RTR0ProcHandleSelf()));
[10836]1332 AssertMsg((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(), ("%p %p\n", m_Task, RTR0ProcHandleSelf()));
[378]1333
[10836]1334 /*
1335 * Clean up the session if it's still around.
1336 *
1337 * We cannot rely 100% on close, and in the case of a dead client
1338 * we'll end up hanging inside vm_map_remove() if we postpone it.
1339 */
1340 if (m_pSession)
1341 {
1342 sessionClose(RTProcSelf());
1343 Assert(!m_pSession);
1344 }
1345
[378]1346 m_pProvider = NULL;
1347 terminate();
1348
1349 return kIOReturnSuccess;
1350}
1351
[75705]1352
1353/**
1354 * The client exits abnormally / forgets to do cleanups. (logging)
1355 */
1356IOReturn org_virtualbox_VBoxGuestClient::clientDied(void)
1357{
1358 LogFlow(("IOService::clientDied([%p]) m_Task=%p R0Process=%p Process=%d\n", this, m_Task, RTR0ProcHandleSelf(), RTProcSelf()));
1359
1360 /* IOUserClient::clientDied() calls clientClose, so we'll just do the work there. */
1361 return IOUserClient::clientDied();
1362}
1363
1364
1365/**
1366 * Terminate the service (initiate the destruction). (logging)
1367 */
1368bool org_virtualbox_VBoxGuestClient::terminate(IOOptionBits fOptions)
1369{
1370 LogFlow(("IOService::terminate([%p], %#x)\n", this, fOptions));
1371 return IOUserClient::terminate(fOptions);
1372}
1373
1374
1375/**
1376 * The final stage of the client service destruction. (logging)
1377 */
1378bool org_virtualbox_VBoxGuestClient::finalize(IOOptionBits fOptions)
1379{
1380 LogFlow(("IOService::finalize([%p], %#x)\n", this, fOptions));
1381 return IOUserClient::finalize(fOptions);
1382}
1383
1384
1385/**
1386 * Stop the client service. (logging)
1387 */
1388void org_virtualbox_VBoxGuestClient::stop(IOService *pProvider)
1389{
1390 LogFlow(("IOService::stop([%p])\n", this));
1391 IOUserClient::stop(pProvider);
1392}
1393
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use