VirtualBox

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

Last change on this file since 107032 was 106061, checked in by vboxsync, 5 months ago

Copyright year updates by scm.

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