VirtualBox

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

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

Darwin Additions: fix kernel panic during VBoxGuest.kext unloading (do not use logger in IOService' ::stop and ::terminate sinse its instance was destroyed in BSD kext unload handler); log messages updated slightly; use macro instead of printf()s.

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