VirtualBox

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

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

(C) 2016

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

© 2023 Oracle
ContactPrivacy policyTerms of Use