VirtualBox

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

Last change on this file since 68643 was 68583, checked in by vboxsync, 7 years ago

merging vbglioc r117761: Darwin adjustments (GAs).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.8 KB
Line 
1/* $Id: VBoxGuest-darwin.cpp 68583 2017-08-31 12:11:19Z vboxsync $ */
2/** @file
3 * VBoxGuest - Darwin Specifics.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_VGDRV
23/*
24 * Deal with conflicts first.
25 * PVM - BSD mess, that FreeBSD has correct a long time ago.
26 * iprt/types.h before sys/param.h - prevents UINT32_C and friends.
27 */
28#include <iprt/types.h>
29#include <sys/param.h>
30#undef PVM
31
32#include <IOKit/IOLib.h> /* Assert as function */
33
34#include <VBox/version.h>
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/initterm.h>
38#include <iprt/mem.h>
39#include <iprt/process.h>
40#include <iprt/power.h>
41#include <iprt/semaphore.h>
42#include <iprt/spinlock.h>
43#include <iprt/string.h>
44#include <VBox/err.h>
45#include <VBox/log.h>
46
47#include <mach/kmod.h>
48#include <miscfs/devfs/devfs.h>
49#include <sys/conf.h>
50#include <sys/errno.h>
51#include <sys/ioccom.h>
52#include <sys/malloc.h>
53#include <sys/proc.h>
54#include <sys/kauth.h>
55#include <IOKit/IOService.h>
56#include <IOKit/IOUserClient.h>
57#include <IOKit/pwr_mgt/RootDomain.h>
58#include <IOKit/pci/IOPCIDevice.h>
59#include <IOKit/IOBufferMemoryDescriptor.h>
60#include <IOKit/IOFilterInterruptEventSource.h>
61#include "VBoxGuestInternal.h"
62
63
64/*********************************************************************************************************************************
65* Defined Constants And Macros *
66*********************************************************************************************************************************/
67
68/** The system device node name. */
69#define DEVICE_NAME_SYS "vboxguest"
70/** The user device node name. */
71#define DEVICE_NAME_USR "vboxguestu"
72
73
74/*********************************************************************************************************************************
75* Internal Functions *
76*********************************************************************************************************************************/
77RT_C_DECLS_BEGIN
78static kern_return_t vgdrvDarwinStart(struct kmod_info *pKModInfo, void *pvData);
79static kern_return_t vgdrvDarwinStop(struct kmod_info *pKModInfo, void *pvData);
80static int vgdrvDarwinCharDevRemove(void);
81
82static int vgdrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
83static int vgdrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
84static int vgdrvDarwinIOCtlSlow(PVBOXGUESTSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess);
85static int vgdrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess);
86
87static int vgdrvDarwinErr2DarwinErr(int rc);
88
89static IOReturn vgdrvDarwinSleepHandler(void *pvTarget, void *pvRefCon, UInt32 uMessageType, IOService *pProvider, void *pvMessageArgument, vm_size_t argSize);
90RT_C_DECLS_END
91
92
93/*********************************************************************************************************************************
94* Structures and Typedefs *
95*********************************************************************************************************************************/
96/**
97 * The service class for handling the VMMDev PCI device.
98 *
99 * Instantiated when the module is loaded (and on PCI hotplugging?).
100 */
101class org_virtualbox_VBoxGuest : public IOService
102{
103 OSDeclareDefaultStructors(org_virtualbox_VBoxGuest);
104
105private:
106 IOPCIDevice *m_pIOPCIDevice;
107 IOMemoryMap *m_pMap;
108 IOFilterInterruptEventSource *m_pInterruptSrc;
109
110 bool setupVmmDevInterrupts(IOService *pProvider);
111 bool disableVmmDevInterrupts(void);
112 bool isVmmDev(IOPCIDevice *pIOPCIDevice);
113
114protected:
115 IOWorkLoop *m_pWorkLoop;
116
117public:
118 virtual bool start(IOService *pProvider);
119 virtual void stop(IOService *pProvider);
120 virtual bool terminate(IOOptionBits fOptions);
121 IOWorkLoop * getWorkLoop();
122};
123
124OSDefineMetaClassAndStructors(org_virtualbox_VBoxGuest, IOService);
125
126
127/**
128 * An attempt at getting that clientDied() notification.
129 * I don't think it'll work as I cannot figure out where/what creates the correct
130 * port right.
131 *
132 * Instantiated when userland does IOServiceOpen().
133 */
134class org_virtualbox_VBoxGuestClient : public IOUserClient
135{
136 OSDeclareDefaultStructors(org_virtualbox_VBoxGuestClient);
137
138private:
139 PVBOXGUESTSESSION m_pSession; /**< The session. */
140 task_t m_Task; /**< The client task. */
141 org_virtualbox_VBoxGuest *m_pProvider; /**< The service provider. */
142
143public:
144 virtual bool initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type);
145 virtual bool start(IOService *pProvider);
146 static void sessionClose(RTPROCESS Process);
147 virtual IOReturn clientClose(void);
148};
149
150OSDefineMetaClassAndStructors(org_virtualbox_VBoxGuestClient, IOUserClient);
151
152
153
154/*********************************************************************************************************************************
155* Global Variables *
156*********************************************************************************************************************************/
157/**
158 * Declare the module stuff.
159 */
160RT_C_DECLS_BEGIN
161extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData);
162extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData);
163
164KMOD_EXPLICIT_DECL(VBoxGuest, VBOX_VERSION_STRING, _start, _stop)
165DECLHIDDEN(kmod_start_func_t *) _realmain = vgdrvDarwinStart;
166DECLHIDDEN(kmod_stop_func_t *) _antimain = vgdrvDarwinStop;
167DECLHIDDEN(int) _kext_apple_cc = __APPLE_CC__;
168RT_C_DECLS_END
169
170
171/**
172 * Device extention & session data association structure.
173 */
174static VBOXGUESTDEVEXT g_DevExt;
175
176/**
177 * The character device switch table for the driver.
178 */
179static struct cdevsw g_DevCW =
180{
181 /*.d_open = */ vgdrvDarwinOpen,
182 /*.d_close = */ vgdrvDarwinClose,
183 /*.d_read = */ eno_rdwrt,
184 /*.d_write = */ eno_rdwrt,
185 /*.d_ioctl = */ vgdrvDarwinIOCtl,
186 /*.d_stop = */ eno_stop,
187 /*.d_reset = */ eno_reset,
188 /*.d_ttys = */ NULL,
189 /*.d_select = */ eno_select,
190 /*.d_mmap = */ eno_mmap,
191 /*.d_strategy = */ eno_strat,
192 /*.d_getc = */ (void *)(uintptr_t)&enodev, //eno_getc,
193 /*.d_putc = */ (void *)(uintptr_t)&enodev, //eno_putc,
194 /*.d_type = */ 0
195};
196
197/** Major device number. */
198static int g_iMajorDeviceNo = -1;
199/** Registered devfs device handle. */
200static void *g_hDevFsDeviceSys = NULL;
201/** Registered devfs device handle for the user device. */
202static void *g_hDevFsDeviceUsr = NULL; /**< @todo 4 later */
203
204/** Spinlock protecting g_apSessionHashTab. */
205static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
206/** Hash table */
207static PVBOXGUESTSESSION g_apSessionHashTab[19];
208/** Calculates the index into g_apSessionHashTab.*/
209#define SESSION_HASH(pid) ((pid) % RT_ELEMENTS(g_apSessionHashTab))
210/** The number of open sessions. */
211static int32_t volatile g_cSessions = 0;
212/** The number of IOService class instances. */
213static bool volatile g_fInstantiated = 0;
214/** The notifier handle for the sleep callback handler. */
215static IONotifier *g_pSleepNotifier = NULL;
216
217/* States of atimic variable aimed to protect dynamic object allocation in SMP environment. */
218#define VBOXGUEST_OBJECT_UNINITIALIZED (0)
219#define VBOXGUEST_OBJECT_INITIALIZING (1)
220#define VBOXGUEST_OBJECT_INITIALIZED (2)
221#define VBOXGUEST_OBJECT_INVALID (3)
222/** Atomic variable used to protect work loop allocation when multiple threads attempt to obtain it. */
223static uint8_t volatile g_fWorkLoopCreated = VBOXGUEST_OBJECT_UNINITIALIZED;
224
225
226/**
227 * Start the kernel module.
228 */
229static kern_return_t vgdrvDarwinStart(struct kmod_info *pKModInfo, void *pvData)
230{
231 RT_NOREF(pKModInfo, pvData);
232
233 /*
234 * Initialize IPRT.
235 */
236 int rc = RTR0Init(0);
237 if (RT_SUCCESS(rc))
238 {
239 Log(("VBoxGuest: driver loaded\n"));
240 return KMOD_RETURN_SUCCESS;
241 }
242
243 printf("VBoxGuest: RTR0Init failed with rc=%d\n", rc);
244 return KMOD_RETURN_FAILURE;
245}
246
247
248/**
249 * Register VBoxGuest char device
250 */
251static int vgdrvDarwinCharDevInit(void)
252{
253 int rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxGuestDarwin");
254 if (RT_SUCCESS(rc))
255 {
256 /*
257 * Registering ourselves as a character device.
258 */
259 g_iMajorDeviceNo = cdevsw_add(-1, &g_DevCW);
260 if (g_iMajorDeviceNo >= 0)
261 {
262 g_hDevFsDeviceSys = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR,
263 UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME_SYS);
264 if (g_hDevFsDeviceSys != NULL)
265 {
266 /*
267 * Register a sleep/wakeup notification callback.
268 */
269 g_pSleepNotifier = registerPrioritySleepWakeInterest(&vgdrvDarwinSleepHandler, &g_DevExt, NULL);
270 if (g_pSleepNotifier != NULL)
271 {
272 return KMOD_RETURN_SUCCESS;
273 }
274 }
275 }
276 vgdrvDarwinCharDevRemove();
277 }
278 return KMOD_RETURN_FAILURE;
279}
280
281
282/**
283 * Stop the kernel module.
284 */
285static kern_return_t vgdrvDarwinStop(struct kmod_info *pKModInfo, void *pvData)
286{
287 RT_NOREF(pKModInfo, pvData);
288 RTR0TermForced();
289
290 printf("VBoxGuest: driver unloaded\n");
291 return KMOD_RETURN_SUCCESS;
292}
293
294
295/* Unregister VBoxGuest char device */
296static int vgdrvDarwinCharDevRemove(void)
297{
298 int rc = KMOD_RETURN_SUCCESS;
299
300 if (g_pSleepNotifier)
301 {
302 g_pSleepNotifier->remove();
303 g_pSleepNotifier = NULL;
304 }
305
306 if (g_hDevFsDeviceSys)
307 {
308 devfs_remove(g_hDevFsDeviceSys);
309 g_hDevFsDeviceSys = NULL;
310 }
311
312 if (g_hDevFsDeviceUsr)
313 {
314 devfs_remove(g_hDevFsDeviceUsr);
315 g_hDevFsDeviceUsr = NULL;
316 }
317
318 if (g_iMajorDeviceNo != -1)
319 {
320 int rc2 = cdevsw_remove(g_iMajorDeviceNo, &g_DevCW);
321 Assert(rc2 == g_iMajorDeviceNo); NOREF(rc2);
322 g_iMajorDeviceNo = -1;
323 }
324
325 if (g_Spinlock != NIL_RTSPINLOCK)
326 {
327 int rc2 = RTSpinlockDestroy(g_Spinlock); AssertRC(rc2);
328 g_Spinlock = NIL_RTSPINLOCK;
329 }
330
331 return rc;
332}
333
334
335/**
336 * Device open. Called on open /dev/vboxguest and (later) /dev/vboxguestu.
337 *
338 * @param Dev The device number.
339 * @param fFlags ???.
340 * @param fDevType ???.
341 * @param pProcess The process issuing this request.
342 */
343static int vgdrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
344{
345 RT_NOREF(fFlags, fDevType);
346
347 /*
348 * Only two minor devices numbers are allowed.
349 */
350 if (minor(Dev) != 0 && minor(Dev) != 1)
351 return EACCES;
352
353 /*
354 * Find the session created by org_virtualbox_VBoxGuestClient, fail
355 * if no such session, and mark it as opened. We set the uid & gid
356 * here too, since that is more straight forward at this point.
357 */
358 //const bool fUnrestricted = minor(Dev) == 0;
359 int rc = VINF_SUCCESS;
360 PVBOXGUESTSESSION pSession = NULL;
361 kauth_cred_t pCred = kauth_cred_proc_ref(pProcess);
362 if (pCred)
363 {
364 RTPROCESS Process = RTProcSelf();
365 unsigned iHash = SESSION_HASH(Process);
366 RTSpinlockAcquire(g_Spinlock);
367
368 pSession = g_apSessionHashTab[iHash];
369 while (pSession && pSession->Process != Process)
370 pSession = pSession->pNextHash;
371 if (pSession)
372 {
373 if (!pSession->fOpened)
374 {
375 pSession->fOpened = true;
376 /*pSession->fUnrestricted = fUnrestricted; - later */
377 }
378 else
379 rc = VERR_ALREADY_LOADED;
380 }
381 else
382 rc = VERR_GENERAL_FAILURE;
383
384 RTSpinlockRelease(g_Spinlock);
385#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
386 kauth_cred_unref(&pCred);
387#else /* 10.4 */
388 /* The 10.4u SDK headers and 10.4.11 kernel source have inconsistent definitions
389 of kauth_cred_unref(), so use the other (now deprecated) API for releasing it. */
390 kauth_cred_rele(pCred);
391#endif /* 10.4 */
392 }
393 else
394 rc = VERR_INVALID_PARAMETER;
395
396 Log(("vgdrvDarwinOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, proc_pid(pProcess)));
397 return vgdrvDarwinErr2DarwinErr(rc);
398}
399
400
401/**
402 * Close device.
403 */
404static int vgdrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
405{
406 RT_NOREF(Dev, fFlags, fDevType, pProcess);
407 Log(("vgdrvDarwinClose: pid=%d\n", (int)RTProcSelf()));
408 Assert(proc_pid(pProcess) == (int)RTProcSelf());
409
410 /*
411 * Hand the session closing to org_virtualbox_VBoxGuestClient.
412 */
413 org_virtualbox_VBoxGuestClient::sessionClose(RTProcSelf());
414 return 0;
415}
416
417
418/**
419 * Device I/O Control entry point.
420 *
421 * @returns Darwin for slow IOCtls and VBox status code for the fast ones.
422 * @param Dev The device number (major+minor).
423 * @param iCmd The IOCtl command.
424 * @param pData Pointer to the request data.
425 * @param fFlags Flag saying we're a character device (like we didn't know already).
426 * @param pProcess The process issuing this request.
427 */
428static int vgdrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess)
429{
430 RT_NOREF(Dev, fFlags);
431 //const bool fUnrestricted = minor(Dev) == 0;
432 const RTPROCESS Process = proc_pid(pProcess);
433 const unsigned iHash = SESSION_HASH(Process);
434 PVBOXGUESTSESSION pSession;
435
436 /*
437 * Find the session.
438 */
439 RTSpinlockAcquire(g_Spinlock);
440 pSession = g_apSessionHashTab[iHash];
441 while (pSession && pSession->Process != Process && (/*later: pSession->fUnrestricted != fUnrestricted ||*/ !pSession->fOpened))
442 pSession = pSession->pNextHash;
443 RTSpinlockRelease(g_Spinlock);
444 if (!pSession)
445 {
446 Log(("VBoxDrvDarwinIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#lx\n", (int)Process, iCmd));
447 return EINVAL;
448 }
449
450 /*
451 * Deal with the high-speed IOCtl.
452 */
453 if (VBGL_IOCTL_IS_FAST(iCmd))
454 return VGDrvCommonIoCtlFast(iCmd, &g_DevExt, pSession);
455
456 return vgdrvDarwinIOCtlSlow(pSession, iCmd, pData, pProcess);
457}
458
459
460/**
461 * Worker for VBoxDrvDarwinIOCtl that takes the slow IOCtl functions.
462 *
463 * @returns Darwin errno.
464 *
465 * @param pSession The session.
466 * @param iCmd The IOCtl command.
467 * @param pData Pointer to the request data.
468 * @param pProcess The calling process.
469 */
470static int vgdrvDarwinIOCtlSlow(PVBOXGUESTSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess)
471{
472 RT_NOREF(pProcess);
473 LogFlow(("vgdrvDarwinIOCtlSlow: pSession=%p iCmd=%p pData=%p pProcess=%p\n", pSession, iCmd, pData, pProcess));
474
475
476 /*
477 * Buffered or unbuffered?
478 */
479 PVBGLREQHDR pHdr;
480 user_addr_t pUser = 0;
481 void *pvPageBuf = NULL;
482 uint32_t cbReq = IOCPARM_LEN(iCmd);
483 if ((IOC_DIRMASK & iCmd) == IOC_INOUT)
484 {
485 pHdr = (PVBGLREQHDR)pData;
486 if (RT_UNLIKELY(cbReq < sizeof(*pHdr)))
487 {
488 LogRel(("vgdrvDarwinIOCtlSlow: cbReq=%#x < %#x; iCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), iCmd));
489 return EINVAL;
490 }
491 if (RT_UNLIKELY(pHdr->uVersion != VBGLREQHDR_VERSION))
492 {
493 LogRel(("vgdrvDarwinIOCtlSlow: bad uVersion=%#x; iCmd=%#lx\n", pHdr->uVersion, iCmd));
494 return EINVAL;
495 }
496 if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq
497 || pHdr->cbIn < sizeof(*pHdr)
498 || (pHdr->cbOut < sizeof(*pHdr) && pHdr->cbOut != 0)))
499 {
500 LogRel(("vgdrvDarwinIOCtlSlow: max(%#x,%#x) != %#x; iCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, iCmd));
501 return EINVAL;
502 }
503 }
504 else if ((IOC_DIRMASK & iCmd) == IOC_VOID && !cbReq)
505 {
506 /*
507 * Get the header and figure out how much we're gonna have to read.
508 */
509 VBGLREQHDR Hdr;
510 pUser = (user_addr_t)*(void **)pData;
511 int rc = copyin(pUser, &Hdr, sizeof(Hdr));
512 if (RT_UNLIKELY(rc))
513 {
514 LogRel(("vgdrvDarwinIOCtlSlow: copyin(%llx,Hdr,) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, rc, iCmd));
515 return rc;
516 }
517 if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION))
518 {
519 LogRel(("vgdrvDarwinIOCtlSlow: bad uVersion=%#x; iCmd=%#lx\n", Hdr.uVersion, iCmd));
520 return EINVAL;
521 }
522 cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut);
523 if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr)
524 || (Hdr.cbOut < sizeof(Hdr) && Hdr.cbOut != 0)
525 || cbReq > _1M*16))
526 {
527 LogRel(("vgdrvDarwinIOCtlSlow: max(%#x,%#x); iCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, iCmd));
528 return EINVAL;
529 }
530
531 /*
532 * Allocate buffer and copy in the data.
533 */
534 pHdr = (PVBGLREQHDR)RTMemTmpAlloc(cbReq);
535 if (!pHdr)
536 pvPageBuf = pHdr = (PVBGLREQHDR)IOMallocAligned(RT_ALIGN_Z(cbReq, PAGE_SIZE), 8);
537 if (RT_UNLIKELY(!pHdr))
538 {
539 LogRel(("vgdrvDarwinIOCtlSlow: failed to allocate buffer of %d bytes; iCmd=%#lx\n", cbReq, iCmd));
540 return ENOMEM;
541 }
542 rc = copyin(pUser, pHdr, Hdr.cbIn);
543 if (RT_UNLIKELY(rc))
544 {
545 LogRel(("vgdrvDarwinIOCtlSlow: copyin(%llx,%p,%#x) -> %#x; iCmd=%#lx\n",
546 (unsigned long long)pUser, pHdr, Hdr.cbIn, rc, iCmd));
547 if (pvPageBuf)
548 IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
549 else
550 RTMemTmpFree(pHdr);
551 return rc;
552 }
553 if (Hdr.cbIn < cbReq)
554 RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbReq - Hdr.cbIn);
555 }
556 else
557 {
558 Log(("vgdrvDarwinIOCtlSlow: huh? cbReq=%#x iCmd=%#lx\n", cbReq, iCmd));
559 return EINVAL;
560 }
561
562 /*
563 * Process the IOCtl.
564 */
565 int rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, cbReq);
566 if (RT_LIKELY(!rc))
567 {
568 /*
569 * If not buffered, copy back the buffer before returning.
570 */
571 if (pUser)
572 {
573 uint32_t cbOut = pHdr->cbOut;
574 if (cbOut > cbReq)
575 {
576 LogRel(("vgdrvDarwinIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, iCmd));
577 cbOut = cbReq;
578 }
579 rc = copyout(pHdr, pUser, cbOut);
580 if (RT_UNLIKELY(rc))
581 LogRel(("vgdrvDarwinIOCtlSlow: copyout(%p,%llx,%#x) -> %d; uCmd=%#lx!\n",
582 pHdr, (unsigned long long)pUser, cbOut, rc, iCmd));
583
584 /* cleanup */
585 if (pvPageBuf)
586 IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
587 else
588 RTMemTmpFree(pHdr);
589 }
590 }
591 else
592 {
593 /*
594 * The request failed, just clean up.
595 */
596 if (pUser)
597 {
598 if (pvPageBuf)
599 IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
600 else
601 RTMemTmpFree(pHdr);
602 }
603
604 Log(("vgdrvDarwinIOCtlSlow: pid=%d iCmd=%lx pData=%p failed, rc=%d\n", proc_pid(pProcess), iCmd, (void *)pData, rc));
605 rc = EINVAL;
606 }
607
608 Log2(("vgdrvDarwinIOCtlSlow: returns %d\n", rc));
609 return rc;
610}
611
612
613/**
614 * @note This code is duplicated on other platforms with variations, so please
615 * keep them all up to date when making changes!
616 */
617int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
618{
619 /*
620 * Simple request validation (common code does the rest).
621 */
622 int rc;
623 if ( RT_VALID_PTR(pReqHdr)
624 && cbReq >= sizeof(*pReqHdr))
625 {
626 /*
627 * All requests except the connect one requires a valid session.
628 */
629 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
630 if (pSession)
631 {
632 if ( RT_VALID_PTR(pSession)
633 && pSession->pDevExt == &g_DevExt)
634 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
635 else
636 rc = VERR_INVALID_HANDLE;
637 }
638 else if (uReq == VBGL_IOCTL_IDC_CONNECT)
639 {
640 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
641 if (RT_SUCCESS(rc))
642 {
643 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
644 if (RT_FAILURE(rc))
645 VGDrvCommonCloseSession(&g_DevExt, pSession);
646 }
647 }
648 else
649 rc = VERR_INVALID_HANDLE;
650 }
651 else
652 rc = VERR_INVALID_POINTER;
653 return rc;
654}
655
656
657void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
658{
659 NOREF(pDevExt);
660}
661
662
663/**
664 * Callback for blah blah blah.
665 */
666static IOReturn vgdrvDarwinSleepHandler(void *pvTarget, void *pvRefCon, UInt32 uMessageType,
667 IOService *pProvider, void *pvMsgArg, vm_size_t cbMsgArg)
668{
669 RT_NOREF(pvTarget, pProvider, pvMsgArg, cbMsgArg);
670 LogFlow(("VBoxGuest: Got sleep/wake notice. Message type was %x\n", uMessageType));
671
672 if (uMessageType == kIOMessageSystemWillSleep)
673 RTPowerSignalEvent(RTPOWEREVENT_SUSPEND);
674 else if (uMessageType == kIOMessageSystemHasPoweredOn)
675 RTPowerSignalEvent(RTPOWEREVENT_RESUME);
676
677 acknowledgeSleepWakeNotification(pvRefCon);
678
679 return 0;
680}
681
682
683/**
684 * Converts an IPRT error code to a darwin error code.
685 *
686 * @returns corresponding darwin error code.
687 * @param rc IPRT status code.
688 */
689static int vgdrvDarwinErr2DarwinErr(int rc)
690{
691 switch (rc)
692 {
693 case VINF_SUCCESS: return 0;
694 case VERR_GENERAL_FAILURE: return EACCES;
695 case VERR_INVALID_PARAMETER: return EINVAL;
696 case VERR_INVALID_MAGIC: return EILSEQ;
697 case VERR_INVALID_HANDLE: return ENXIO;
698 case VERR_INVALID_POINTER: return EFAULT;
699 case VERR_LOCK_FAILED: return ENOLCK;
700 case VERR_ALREADY_LOADED: return EEXIST;
701 case VERR_PERMISSION_DENIED: return EPERM;
702 case VERR_VERSION_MISMATCH: return ENOSYS;
703 }
704
705 return EPERM;
706}
707
708
709/*
710 *
711 * org_virtualbox_VBoxGuest
712 *
713 */
714
715
716/**
717 * Lazy initialization of the m_pWorkLoop member.
718 *
719 * @returns m_pWorkLoop.
720 */
721IOWorkLoop *org_virtualbox_VBoxGuest::getWorkLoop()
722{
723/** @todo r=bird: This is actually a classic RTOnce scenario, except it's
724 * tied to a org_virtualbox_VBoxGuest instance. */
725 /*
726 * Handle the case when work loop was not created yet.
727 */
728 if (ASMAtomicCmpXchgU8(&g_fWorkLoopCreated, VBOXGUEST_OBJECT_INITIALIZING, VBOXGUEST_OBJECT_UNINITIALIZED))
729 {
730 m_pWorkLoop = IOWorkLoop::workLoop();
731 if (m_pWorkLoop)
732 {
733 /* Notify the rest of threads about the fact that work
734 * loop was successully allocated and can be safely used */
735 Log(("VBoxGuest: created new work loop\n"));
736 ASMAtomicWriteU8(&g_fWorkLoopCreated, VBOXGUEST_OBJECT_INITIALIZED);
737 }
738 else
739 {
740 /* Notify the rest of threads about the fact that there was
741 * an error during allocation of a work loop */
742 Log(("VBoxGuest: failed to create new work loop!\n"));
743 ASMAtomicWriteU8(&g_fWorkLoopCreated, VBOXGUEST_OBJECT_UNINITIALIZED);
744 }
745 }
746 /*
747 * Handle the case when work loop is already create or
748 * in the process of being.
749 */
750 else
751 {
752 uint8_t fWorkLoopCreated = ASMAtomicReadU8(&g_fWorkLoopCreated);
753 while (fWorkLoopCreated == VBOXGUEST_OBJECT_INITIALIZING)
754 {
755 thread_block(0);
756 fWorkLoopCreated = ASMAtomicReadU8(&g_fWorkLoopCreated);
757 }
758 if (fWorkLoopCreated != VBOXGUEST_OBJECT_INITIALIZED)
759 Log(("VBoxGuest: No work loop!\n"));
760 }
761
762 return m_pWorkLoop;
763}
764
765
766/**
767 * Perform pending wake ups in work loop context.
768 */
769static void vgdrvDarwinDeferredIrqHandler(OSObject *pOwner, IOInterruptEventSource *pSrc, int cInts)
770{
771 NOREF(pOwner); NOREF(pSrc); NOREF(cInts);
772
773 VGDrvCommonWaitDoWakeUps(&g_DevExt);
774}
775
776
777/**
778 * Callback triggered when interrupt occurs.
779 */
780static bool vgdrvDarwinDirectIrqHandler(OSObject *pOwner, IOFilterInterruptEventSource *pSrc)
781{
782 RT_NOREF(pOwner);
783 if (!pSrc)
784 return false;
785
786 bool fTaken = VGDrvCommonISR(&g_DevExt);
787 if (!fTaken) /** @todo r=bird: This looks bogus as we might actually be sharing interrupts with someone. */
788 Log(("VGDrvCommonISR error\n"));
789
790 return fTaken;
791}
792
793
794bool org_virtualbox_VBoxGuest::setupVmmDevInterrupts(IOService *pProvider)
795{
796 IOWorkLoop *pWorkLoop = getWorkLoop();
797 if (!pWorkLoop)
798 return false;
799
800 m_pInterruptSrc = IOFilterInterruptEventSource::filterInterruptEventSource(this,
801 &vgdrvDarwinDeferredIrqHandler,
802 &vgdrvDarwinDirectIrqHandler,
803 pProvider);
804 IOReturn rc = pWorkLoop->addEventSource(m_pInterruptSrc);
805 if (rc == kIOReturnSuccess)
806 {
807 m_pInterruptSrc->enable();
808 return true;
809 }
810
811 m_pInterruptSrc->disable();
812 m_pInterruptSrc->release();
813 m_pInterruptSrc = NULL;
814 return false;
815}
816
817
818bool org_virtualbox_VBoxGuest::disableVmmDevInterrupts(void)
819{
820 IOWorkLoop *pWorkLoop = (IOWorkLoop *)getWorkLoop();
821
822 if (!pWorkLoop)
823 return false;
824
825 if (!m_pInterruptSrc)
826 return false;
827
828 m_pInterruptSrc->disable();
829 pWorkLoop->removeEventSource(m_pInterruptSrc);
830 m_pInterruptSrc->release();
831 m_pInterruptSrc = NULL;
832
833 return true;
834}
835
836
837bool org_virtualbox_VBoxGuest::isVmmDev(IOPCIDevice *pIOPCIDevice)
838{
839 UInt16 uVendorId, uDeviceId;
840
841 if (!pIOPCIDevice)
842 return false;
843
844 uVendorId = m_pIOPCIDevice->configRead16(kIOPCIConfigVendorID);
845 uDeviceId = m_pIOPCIDevice->configRead16(kIOPCIConfigDeviceID);
846
847 if (uVendorId == VMMDEV_VENDORID && uDeviceId == VMMDEV_DEVICEID)
848 return true;
849
850 return true;
851}
852
853
854/**
855 * Start this service.
856 */
857bool org_virtualbox_VBoxGuest::start(IOService *pProvider)
858{
859 /*
860 * Low level initialization / device initialization should be performed only once.
861 */
862 if (!ASMAtomicCmpXchgBool(&g_fInstantiated, true, false))
863 return false;
864
865 if (!IOService::start(pProvider))
866 return false;
867
868 m_pIOPCIDevice = OSDynamicCast(IOPCIDevice, pProvider);
869 if (m_pIOPCIDevice)
870 {
871 if (isVmmDev(m_pIOPCIDevice))
872 {
873 /* Enable memory response from VMM device */
874 m_pIOPCIDevice->setMemoryEnable(true);
875 m_pIOPCIDevice->setIOEnable(true);
876
877 IOMemoryDescriptor *pMem = m_pIOPCIDevice->getDeviceMemoryWithIndex(0);
878 if (pMem)
879 {
880 IOPhysicalAddress IOPortBasePhys = pMem->getPhysicalAddress();
881 /* Check that returned value is from I/O port range (at least it is 16-bit lenght) */
882 if((IOPortBasePhys >> 16) == 0)
883 {
884
885 RTIOPORT IOPortBase = (RTIOPORT)IOPortBasePhys;
886 void *pvMMIOBase = NULL;
887 uint32_t cbMMIO = 0;
888 m_pMap = m_pIOPCIDevice->mapDeviceMemoryWithIndex(1);
889 if (m_pMap)
890 {
891 pvMMIOBase = (void *)m_pMap->getVirtualAddress();
892 cbMMIO = m_pMap->getLength();
893 }
894
895 int rc = VGDrvCommonInitDevExt(&g_DevExt,
896 IOPortBase,
897 pvMMIOBase,
898 cbMMIO,
899#if ARCH_BITS == 64
900 VBOXOSTYPE_MacOS_x64,
901#else
902 VBOXOSTYPE_MacOS,
903#endif
904 0);
905 if (RT_SUCCESS(rc))
906 {
907 rc = vgdrvDarwinCharDevInit();
908 if (rc == KMOD_RETURN_SUCCESS)
909 {
910 if (setupVmmDevInterrupts(pProvider))
911 {
912 /* register the service. */
913 registerService();
914 LogRel(("VBoxGuest: IOService started\n"));
915 return true;
916 }
917
918 LogRel(("VBoxGuest: Failed to set up interrupts\n"));
919 vgdrvDarwinCharDevRemove();
920 }
921 else
922 LogRel(("VBoxGuest: Failed to initialize character device (rc=%d).\n", rc));
923
924 VGDrvCommonDeleteDevExt(&g_DevExt);
925 }
926 else
927 LogRel(("VBoxGuest: Failed to initialize common code (rc=%d).\n", rc));
928
929 if (m_pMap)
930 {
931 m_pMap->release();
932 m_pMap = NULL;
933 }
934 }
935 }
936 else
937 LogRel(("VBoxGuest: The device missing is the I/O port range (#0).\n"));
938 }
939 else
940 LogRel(("VBoxGuest: Not the VMMDev (%#x:%#x).\n",
941 m_pIOPCIDevice->configRead16(kIOPCIConfigVendorID), m_pIOPCIDevice->configRead16(kIOPCIConfigDeviceID)));
942 }
943 else
944 LogRel(("VBoxGuest: Provider is not an instance of IOPCIDevice.\n"));
945
946 ASMAtomicXchgBool(&g_fInstantiated, false);
947 IOService::stop(pProvider);
948 return false;
949}
950
951
952/**
953 * Stop this service.
954 */
955void org_virtualbox_VBoxGuest::stop(IOService *pProvider)
956{
957 /* Do not use Log*() here (in IOService instance) because its instance
958 * already terminated in BSD's module unload callback! */
959 Log(("org_virtualbox_VBoxGuest::stop([%p], %p)\n", this, pProvider));
960
961 AssertReturnVoid(ASMAtomicReadBool(&g_fInstantiated));
962
963 /* Low level termination should be performed only once */
964 if (!disableVmmDevInterrupts())
965 printf("VBoxGuest: unable to unregister interrupt handler\n");
966
967 vgdrvDarwinCharDevRemove();
968 VGDrvCommonDeleteDevExt(&g_DevExt);
969
970 if (m_pMap)
971 {
972 m_pMap->release();
973 m_pMap = NULL;
974 }
975
976 IOService::stop(pProvider);
977
978 ASMAtomicWriteBool(&g_fInstantiated, false);
979
980 printf("VBoxGuest: IOService stopped\n");
981}
982
983
984/**
985 * Termination request.
986 *
987 * @return true if we're ok with shutting down now, false if we're not.
988 * @param fOptions Flags.
989 */
990bool org_virtualbox_VBoxGuest::terminate(IOOptionBits fOptions)
991{
992 /* Do not use Log*() here (in IOService instance) because its instance
993 * already terminated in BSD's module unload callback! */
994#ifdef LOG_ENABLED
995 printf("org_virtualbox_VBoxGuest::terminate: reference_count=%d g_cSessions=%d (fOptions=%#x)\n",
996 KMOD_INFO_NAME.reference_count, ASMAtomicUoReadS32(&g_cSessions), fOptions);
997#endif
998
999 bool fRc;
1000 if ( KMOD_INFO_NAME.reference_count != 0
1001 || ASMAtomicUoReadS32(&g_cSessions))
1002 fRc = false;
1003 else
1004 fRc = IOService::terminate(fOptions);
1005
1006#ifdef LOG_ENABLED
1007 printf("org_virtualbox_SupDrv::terminate: returns %d\n", fRc);
1008#endif
1009 return fRc;
1010}
1011
1012
1013/*
1014 *
1015 * org_virtualbox_VBoxGuestClient
1016 *
1017 */
1018
1019
1020/**
1021 * Initializer called when the client opens the service.
1022 */
1023bool org_virtualbox_VBoxGuestClient::initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type)
1024{
1025 LogFlow(("org_virtualbox_VBoxGuestClient::initWithTask([%p], %#x, %p, %#x) (cur pid=%d proc=%p)\n",
1026 this, OwningTask, pvSecurityId, u32Type, RTProcSelf(), RTR0ProcHandleSelf()));
1027 AssertMsg((RTR0PROCESS)OwningTask == RTR0ProcHandleSelf(), ("%p %p\n", OwningTask, RTR0ProcHandleSelf()));
1028
1029 if (!OwningTask)
1030 return false;
1031
1032 if (u32Type != VBOXGUEST_DARWIN_IOSERVICE_COOKIE)
1033 {
1034 Log(("org_virtualbox_VBoxGuestClient::initWithTask: Bad cookie %#x\n", u32Type));
1035 return false;
1036 }
1037
1038 if (IOUserClient::initWithTask(OwningTask, pvSecurityId , u32Type))
1039 {
1040 /*
1041 * In theory we have to call task_reference() to make sure that the task is
1042 * valid during the lifetime of this object. The pointer is only used to check
1043 * for the context this object is called in though and never dereferenced
1044 * or passed to anything which might, so we just skip this step.
1045 */
1046 m_Task = OwningTask;
1047 m_pSession = NULL;
1048 m_pProvider = NULL;
1049 return true;
1050 }
1051 return false;
1052}
1053
1054
1055/**
1056 * Start the client service.
1057 */
1058bool org_virtualbox_VBoxGuestClient::start(IOService *pProvider)
1059{
1060 LogFlow(("org_virtualbox_VBoxGuestClient::start([%p], %p) (cur pid=%d proc=%p)\n",
1061 this, pProvider, RTProcSelf(), RTR0ProcHandleSelf() ));
1062 AssertMsgReturn((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(),
1063 ("%p %p\n", m_Task, RTR0ProcHandleSelf()),
1064 false);
1065
1066 if (IOUserClient::start(pProvider))
1067 {
1068 m_pProvider = OSDynamicCast(org_virtualbox_VBoxGuest, pProvider);
1069 if (m_pProvider)
1070 {
1071 Assert(!m_pSession);
1072
1073 /*
1074 * Create a new session.
1075 */
1076 int rc = VGDrvCommonCreateUserSession(&g_DevExt, &m_pSession);
1077 if (RT_SUCCESS(rc))
1078 {
1079 m_pSession->fOpened = false;
1080 /* The fUnrestricted field is set on open. */
1081
1082 /*
1083 * Insert it into the hash table, checking that there isn't
1084 * already one for this process first. (One session per proc!)
1085 */
1086 unsigned iHash = SESSION_HASH(m_pSession->Process);
1087 RTSpinlockAcquire(g_Spinlock);
1088
1089 PVBOXGUESTSESSION pCur = g_apSessionHashTab[iHash];
1090 if (pCur && pCur->Process != m_pSession->Process)
1091 {
1092 do pCur = pCur->pNextHash;
1093 while (pCur && pCur->Process != m_pSession->Process);
1094 }
1095 if (!pCur)
1096 {
1097 m_pSession->pNextHash = g_apSessionHashTab[iHash];
1098 g_apSessionHashTab[iHash] = m_pSession;
1099 m_pSession->pvVBoxGuestClient = this;
1100 ASMAtomicIncS32(&g_cSessions);
1101 rc = VINF_SUCCESS;
1102 }
1103 else
1104 rc = VERR_ALREADY_LOADED;
1105
1106 RTSpinlockRelease(g_Spinlock);
1107 if (RT_SUCCESS(rc))
1108 {
1109 Log(("org_virtualbox_VBoxGuestClient::start: created session %p for pid %d\n", m_pSession, (int)RTProcSelf()));
1110 return true;
1111 }
1112
1113 LogFlow(("org_virtualbox_VBoxGuestClient::start: already got a session for this process (%p)\n", pCur));
1114 VGDrvCommonCloseSession(&g_DevExt, m_pSession);
1115 }
1116
1117 m_pSession = NULL;
1118 LogFlow(("org_virtualbox_VBoxGuestClient::start: rc=%Rrc from supdrvCreateSession\n", rc));
1119 }
1120 else
1121 LogFlow(("org_virtualbox_VBoxGuestClient::start: %p isn't org_virtualbox_VBoxGuest\n", pProvider));
1122 }
1123 return false;
1124}
1125
1126
1127/**
1128 * Common worker for clientClose and VBoxDrvDarwinClose.
1129 */
1130/* static */ void org_virtualbox_VBoxGuestClient::sessionClose(RTPROCESS Process)
1131{
1132 /*
1133 * Find the session and remove it from the hash table.
1134 *
1135 * Note! Only one session per process. (Both start() and
1136 * vgdrvDarwinOpen makes sure this is so.)
1137 */
1138 const unsigned iHash = SESSION_HASH(Process);
1139 RTSpinlockAcquire(g_Spinlock);
1140 PVBOXGUESTSESSION pSession = g_apSessionHashTab[iHash];
1141 if (pSession)
1142 {
1143 if (pSession->Process == Process)
1144 {
1145 g_apSessionHashTab[iHash] = pSession->pNextHash;
1146 pSession->pNextHash = NULL;
1147 ASMAtomicDecS32(&g_cSessions);
1148 }
1149 else
1150 {
1151 PVBOXGUESTSESSION pPrev = pSession;
1152 pSession = pSession->pNextHash;
1153 while (pSession)
1154 {
1155 if (pSession->Process == Process)
1156 {
1157 pPrev->pNextHash = pSession->pNextHash;
1158 pSession->pNextHash = NULL;
1159 ASMAtomicDecS32(&g_cSessions);
1160 break;
1161 }
1162
1163 /* next */
1164 pPrev = pSession;
1165 pSession = pSession->pNextHash;
1166 }
1167 }
1168 }
1169 RTSpinlockRelease(g_Spinlock);
1170 if (!pSession)
1171 {
1172 Log(("VBoxGuestClient::sessionClose: pSession == NULL, pid=%d; freed already?\n", (int)Process));
1173 return;
1174 }
1175
1176 /*
1177 * Remove it from the client object.
1178 */
1179 org_virtualbox_VBoxGuestClient *pThis = (org_virtualbox_VBoxGuestClient *)pSession->pvVBoxGuestClient;
1180 pSession->pvVBoxGuestClient = NULL;
1181 if (pThis)
1182 {
1183 Assert(pThis->m_pSession == pSession);
1184 pThis->m_pSession = NULL;
1185 }
1186
1187 /*
1188 * Close the session.
1189 */
1190 VGDrvCommonCloseSession(&g_DevExt, pSession);
1191}
1192
1193
1194/**
1195 * Client exits normally.
1196 */
1197IOReturn org_virtualbox_VBoxGuestClient::clientClose(void)
1198{
1199 LogFlow(("org_virtualbox_VBoxGuestClient::clientClose([%p]) (cur pid=%d proc=%p)\n", this, RTProcSelf(), RTR0ProcHandleSelf()));
1200 AssertMsg((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(), ("%p %p\n", m_Task, RTR0ProcHandleSelf()));
1201
1202 /*
1203 * Clean up the session if it's still around.
1204 *
1205 * We cannot rely 100% on close, and in the case of a dead client
1206 * we'll end up hanging inside vm_map_remove() if we postpone it.
1207 */
1208 if (m_pSession)
1209 {
1210 sessionClose(RTProcSelf());
1211 Assert(!m_pSession);
1212 }
1213
1214 m_pProvider = NULL;
1215 terminate();
1216
1217 return kIOReturnSuccess;
1218}
1219
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