VirtualBox

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

Last change on this file since 54239 was 52618, checked in by vboxsync, 10 years ago

HostDrivers, Runtime, Devices, Additions: TSC delta measurement and other changes resulting from bumping supdrv major version. TSC delta measurement currently disabled.

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