VirtualBox

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

Last change on this file since 57444 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

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