VirtualBox

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

Last change on this file since 96399 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette