VirtualBox

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

Last change on this file since 50701 was 50701, checked in by vboxsync, 11 years ago

Darwin Additions: intoduced a dedicated workloop in VBoxGuest.kext in order to handle DevVMM interrupts.

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

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