VirtualBox

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

Last change on this file since 63516 was 63516, checked in by vboxsync, 8 years ago

VBoxGuest: warnings (clang)

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