VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp@ 37070

Last change on this file since 37070 was 37050, checked in by vboxsync, 14 years ago

SUPDrv-darwin.cpp: revert r71677.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.3 KB
Line 
1/* $Id: SUPDrv-darwin.cpp 37050 2011-05-12 13:46:12Z vboxsync $ */
2/** @file
3 * VirtualBox Support Driver - Darwin Specific Code.
4 */
5
6/*
7 * Copyright (C) 2006-2007 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#define LOG_GROUP LOG_GROUP_SUP_DRV
31/*
32 * Deal with conflicts first.
33 * PVM - BSD mess, that FreeBSD has correct a long time ago.
34 * iprt/types.h before sys/param.h - prevents UINT32_C and friends.
35 */
36#include <iprt/types.h>
37#include <sys/param.h>
38#undef PVM
39
40#include <IOKit/IOLib.h> /* Assert as function */
41
42#include "../SUPDrvInternal.h"
43#include <VBox/version.h>
44#include <iprt/asm.h>
45#include <iprt/initterm.h>
46#include <iprt/assert.h>
47#include <iprt/spinlock.h>
48#include <iprt/semaphore.h>
49#include <iprt/process.h>
50#include <iprt/alloc.h>
51#include <iprt/power.h>
52#include <VBox/err.h>
53#include <VBox/log.h>
54
55#include <mach/kmod.h>
56#include <miscfs/devfs/devfs.h>
57#include <sys/conf.h>
58#include <sys/errno.h>
59#include <sys/ioccom.h>
60#include <sys/malloc.h>
61#include <sys/proc.h>
62#include <sys/kauth.h>
63#include <IOKit/IOService.h>
64#include <IOKit/IOUserClient.h>
65#include <IOKit/pwr_mgt/RootDomain.h>
66
67#ifdef VBOX_WITH_HOST_VMX
68RT_C_DECLS_BEGIN
69# include <i386/vmx.h>
70RT_C_DECLS_END
71#endif
72
73
74/*******************************************************************************
75* Defined Constants And Macros *
76*******************************************************************************/
77
78/** The module name. */
79#define DEVICE_NAME "vboxdrv"
80
81
82
83/*******************************************************************************
84* Internal Functions *
85*******************************************************************************/
86RT_C_DECLS_BEGIN
87static kern_return_t VBoxDrvDarwinStart(struct kmod_info *pKModInfo, void *pvData);
88static kern_return_t VBoxDrvDarwinStop(struct kmod_info *pKModInfo, void *pvData);
89
90static int VBoxDrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
91static int VBoxDrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
92static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess);
93static int VBoxDrvDarwinIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess);
94
95static int VBoxDrvDarwinErr2DarwinErr(int rc);
96
97static IOReturn VBoxDrvDarwinSleepHandler(void *pvTarget, void *pvRefCon, UInt32 uMessageType, IOService *pProvider, void *pvMessageArgument, vm_size_t argSize);
98RT_C_DECLS_END
99
100
101/*******************************************************************************
102* Structures and Typedefs *
103*******************************************************************************/
104/**
105 * The service class.
106 * This is just a formality really.
107 */
108class org_virtualbox_SupDrv : public IOService
109{
110 OSDeclareDefaultStructors(org_virtualbox_SupDrv);
111
112public:
113 virtual bool init(OSDictionary *pDictionary = 0);
114 virtual void free(void);
115 virtual bool start(IOService *pProvider);
116 virtual void stop(IOService *pProvider);
117 virtual IOService *probe(IOService *pProvider, SInt32 *pi32Score);
118 virtual bool terminate(IOOptionBits fOptions);
119};
120
121OSDefineMetaClassAndStructors(org_virtualbox_SupDrv, IOService);
122
123
124/**
125 * An attempt at getting that clientDied() notification.
126 * I don't think it'll work as I cannot figure out where/what creates the correct
127 * port right.
128 */
129class org_virtualbox_SupDrvClient : public IOUserClient
130{
131 OSDeclareDefaultStructors(org_virtualbox_SupDrvClient);
132
133private:
134 PSUPDRVSESSION m_pSession; /**< The session. */
135 task_t m_Task; /**< The client task. */
136 org_virtualbox_SupDrv *m_pProvider; /**< The service provider. */
137
138public:
139 virtual bool initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type);
140 virtual bool start(IOService *pProvider);
141 static void sessionClose(RTPROCESS Process);
142 virtual IOReturn clientClose(void);
143 virtual IOReturn clientDied(void);
144 virtual bool terminate(IOOptionBits fOptions = 0);
145 virtual bool finalize(IOOptionBits fOptions);
146 virtual void stop(IOService *pProvider);
147};
148
149OSDefineMetaClassAndStructors(org_virtualbox_SupDrvClient, 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(VBoxDrv, VBOX_VERSION_STRING, _start, _stop)
164DECLHIDDEN(kmod_start_func_t *) _realmain = VBoxDrvDarwinStart;
165DECLHIDDEN(kmod_stop_func_t *) _antimain = VBoxDrvDarwinStop;
166DECLHIDDEN(int) _kext_apple_cc = __APPLE_CC__;
167RT_C_DECLS_END
168
169
170/**
171 * Device extention & session data association structure.
172 */
173static SUPDRVDEVEXT g_DevExt;
174
175/**
176 * The character device switch table for the driver.
177 */
178static struct cdevsw g_DevCW =
179{
180 /** @todo g++ doesn't like this syntax - it worked with gcc before renaming to .cpp. */
181 /*.d_open = */VBoxDrvDarwinOpen,
182 /*.d_close = */VBoxDrvDarwinClose,
183 /*.d_read = */eno_rdwrt,
184 /*.d_write = */eno_rdwrt,
185 /*.d_ioctl = */VBoxDrvDarwinIOCtl,
186 /*.d_stop = */eno_stop,
187 /*.d_reset = */eno_reset,
188 /*.d_ttys = */NULL,
189 /*.d_select= */eno_select,
190 /*.d_mmap = */eno_mmap,
191 /*.d_strategy = */eno_strat,
192 /*.d_getc = */eno_getc,
193 /*.d_putc = */eno_putc,
194 /*.d_type = */0
195};
196
197/** Major device number. */
198static int g_iMajorDeviceNo = -1;
199/** Registered devfs device handle. */
200static void *g_hDevFsDevice = NULL;
201
202/** Spinlock protecting g_apSessionHashTab. */
203static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
204/** Hash table */
205static PSUPDRVSESSION g_apSessionHashTab[19];
206/** Calculates the index into g_apSessionHashTab.*/
207#define SESSION_HASH(pid) ((pid) % RT_ELEMENTS(g_apSessionHashTab))
208/** The number of open sessions. */
209static int32_t volatile g_cSessions = 0;
210/** The notifier handle for the sleep callback handler. */
211static IONotifier *g_pSleepNotifier = NULL;
212
213
214
215/**
216 * Start the kernel module.
217 */
218static kern_return_t VBoxDrvDarwinStart(struct kmod_info *pKModInfo, void *pvData)
219{
220 int rc;
221#ifdef DEBUG
222 printf("VBoxDrvDarwinStart\n");
223#endif
224
225 /*
226 * Initialize IPRT.
227 */
228 rc = RTR0Init(0);
229 if (RT_SUCCESS(rc))
230 {
231 /*
232 * Initialize the device extension.
233 */
234 rc = supdrvInitDevExt(&g_DevExt, sizeof(SUPDRVSESSION));
235 if (RT_SUCCESS(rc))
236 {
237 /*
238 * Initialize the session hash table.
239 */
240 memset(g_apSessionHashTab, 0, sizeof(g_apSessionHashTab)); /* paranoia */
241 rc = RTSpinlockCreate(&g_Spinlock);
242 if (RT_SUCCESS(rc))
243 {
244 /*
245 * Registering ourselves as a character device.
246 */
247 g_iMajorDeviceNo = cdevsw_add(-1, &g_DevCW);
248 if (g_iMajorDeviceNo >= 0)
249 {
250#ifdef VBOX_WITH_HARDENING
251 g_hDevFsDevice = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR,
252 UID_ROOT, GID_WHEEL, 0600, DEVICE_NAME);
253#else
254 g_hDevFsDevice = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR,
255 UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME);
256#endif
257 if (g_hDevFsDevice)
258 {
259 LogRel(("VBoxDrv: version " VBOX_VERSION_STRING " r%d; IOCtl version %#x; IDC version %#x; dev major=%d\n",
260 VBOX_SVN_REV, SUPDRV_IOC_VERSION, SUPDRV_IDC_VERSION, g_iMajorDeviceNo));
261
262 /* Register a sleep/wakeup notification callback */
263 g_pSleepNotifier = registerPrioritySleepWakeInterest(&VBoxDrvDarwinSleepHandler, &g_DevExt, NULL);
264 if (g_pSleepNotifier == NULL)
265 LogRel(("VBoxDrv: register for sleep/wakeup events failed\n"));
266
267 return KMOD_RETURN_SUCCESS;
268 }
269
270 LogRel(("VBoxDrv: devfs_make_node(makedev(%d,0),,,,%s) failed\n", g_iMajorDeviceNo, DEVICE_NAME));
271 cdevsw_remove(g_iMajorDeviceNo, &g_DevCW);
272 g_iMajorDeviceNo = -1;
273 }
274 else
275 LogRel(("VBoxDrv: cdevsw_add failed (%d)\n", g_iMajorDeviceNo));
276 RTSpinlockDestroy(g_Spinlock);
277 g_Spinlock = NIL_RTSPINLOCK;
278 }
279 else
280 LogRel(("VBoxDrv: RTSpinlockCreate failed (rc=%d)\n", rc));
281 supdrvDeleteDevExt(&g_DevExt);
282 }
283 else
284 printf("VBoxDrv: failed to initialize device extension (rc=%d)\n", rc);
285 RTR0TermForced();
286 }
287 else
288 printf("VBoxDrv: failed to initialize IPRT (rc=%d)\n", rc);
289
290 memset(&g_DevExt, 0, sizeof(g_DevExt));
291 return KMOD_RETURN_FAILURE;
292}
293
294
295/**
296 * Stop the kernel module.
297 */
298static kern_return_t VBoxDrvDarwinStop(struct kmod_info *pKModInfo, void *pvData)
299{
300 int rc;
301 LogFlow(("VBoxDrvDarwinStop\n"));
302
303 /** @todo I've got a nagging feeling that we'll have to keep track of users and refuse
304 * unloading if we're busy. Investigate and implement this! */
305
306 /*
307 * Undo the work done during start (in reverse order).
308 */
309 if (g_pSleepNotifier)
310 {
311 g_pSleepNotifier->remove();
312 g_pSleepNotifier = NULL;
313 }
314
315 devfs_remove(g_hDevFsDevice);
316 g_hDevFsDevice = NULL;
317
318 rc = cdevsw_remove(g_iMajorDeviceNo, &g_DevCW);
319 Assert(rc == g_iMajorDeviceNo);
320 g_iMajorDeviceNo = -1;
321
322 supdrvDeleteDevExt(&g_DevExt);
323
324 rc = RTSpinlockDestroy(g_Spinlock);
325 AssertRC(rc);
326 g_Spinlock = NIL_RTSPINLOCK;
327
328 RTR0TermForced();
329
330 memset(&g_DevExt, 0, sizeof(g_DevExt));
331#ifdef DEBUG
332 printf("VBoxDrvDarwinStop - done\n");
333#endif
334 return KMOD_RETURN_SUCCESS;
335}
336
337
338/**
339 * Device open. Called on open /dev/vboxdrv
340 *
341 * @param pInode Pointer to inode info structure.
342 * @param pFilp Associated file pointer.
343 */
344static int VBoxDrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
345{
346#ifdef DEBUG_DARWIN_GIP
347 char szName[128];
348 szName[0] = '\0';
349 proc_name(proc_pid(pProcess), szName, sizeof(szName));
350 Log(("VBoxDrvDarwinOpen: pid=%d '%s'\n", proc_pid(pProcess), szName));
351#endif
352
353 /*
354 * Find the session created by org_virtualbox_SupDrvClient, fail
355 * if no such session, and mark it as opened. We set the uid & gid
356 * here too, since that is more straight forward at this point.
357 */
358 int rc = VINF_SUCCESS;
359 PSUPDRVSESSION pSession = NULL;
360 kauth_cred_t pCred = kauth_cred_proc_ref(pProcess);
361 if (pCred)
362 {
363 RTUID Uid = pCred->cr_ruid;
364 RTGID Gid = pCred->cr_rgid;
365 RTPROCESS Process = RTProcSelf();
366 unsigned iHash = SESSION_HASH(Process);
367 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
368 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
369
370 pSession = g_apSessionHashTab[iHash];
371 if (pSession && pSession->Process != Process)
372 {
373 do pSession = pSession->pNextHash;
374 while (pSession && pSession->Process != Process);
375 }
376 if (pSession)
377 {
378 if (!pSession->fOpened)
379 {
380 pSession->fOpened = true;
381 pSession->Uid = Uid;
382 pSession->Gid = Gid;
383 }
384 else
385 rc = VERR_ALREADY_LOADED;
386 }
387 else
388 rc = VERR_GENERAL_FAILURE;
389
390 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
391#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
392 kauth_cred_unref(&pCred);
393#else /* 10.4 */
394 /* The 10.4u SDK headers and 10.4.11 kernel source have inconsistent definitions
395 of kauth_cred_unref(), so use the other (now deprecated) API for releasing it. */
396 kauth_cred_rele(pCred);
397#endif /* 10.4 */
398 }
399 else
400 rc = VERR_INVALID_PARAMETER;
401
402#ifdef DEBUG_DARWIN_GIP
403 OSDBGPRINT(("VBoxDrvDarwinOpen: pid=%d '%s' pSession=%p rc=%d\n", proc_pid(pProcess), szName, pSession, rc));
404#else
405 Log(("VBoxDrvDarwinOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, proc_pid(pProcess)));
406#endif
407 return VBoxDrvDarwinErr2DarwinErr(rc);
408}
409
410
411/**
412 * Close device.
413 */
414static int VBoxDrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
415{
416 Log(("VBoxDrvDarwinClose: pid=%d\n", (int)RTProcSelf()));
417 Assert(proc_pid(pProcess) == (int)RTProcSelf());
418
419 /*
420 * Hand the session closing to org_virtualbox_SupDrvClient.
421 */
422 org_virtualbox_SupDrvClient::sessionClose(RTProcSelf());
423 return 0;
424}
425
426
427/**
428 * Device I/O Control entry point.
429 *
430 * @returns Darwin for slow IOCtls and VBox status code for the fast ones.
431 * @param Dev The device number (major+minor).
432 * @param iCmd The IOCtl command.
433 * @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)).
434 * @param fFlags Flag saying we're a character device (like we didn't know already).
435 * @param pProcess The process issuing this request.
436 */
437static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess)
438{
439 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
440 const RTPROCESS Process = proc_pid(pProcess);
441 const unsigned iHash = SESSION_HASH(Process);
442 PSUPDRVSESSION pSession;
443
444 /*
445 * Find the session.
446 */
447 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
448 pSession = g_apSessionHashTab[iHash];
449 if (pSession && pSession->Process != Process)
450 {
451 do pSession = pSession->pNextHash;
452 while (pSession && pSession->Process != Process);
453 }
454 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
455 if (!pSession)
456 {
457 OSDBGPRINT(("VBoxDrvDarwinIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#lx\n",
458 (int)Process, iCmd));
459 return EINVAL;
460 }
461
462 /*
463 * Deal with the two high-speed IOCtl that takes it's arguments from
464 * the session and iCmd, and only returns a VBox status code.
465 */
466 if ( iCmd == SUP_IOCTL_FAST_DO_RAW_RUN
467 || iCmd == SUP_IOCTL_FAST_DO_HWACC_RUN
468 || iCmd == SUP_IOCTL_FAST_DO_NOP)
469 return supdrvIOCtlFast(iCmd, *(uint32_t *)pData, &g_DevExt, pSession);
470 return VBoxDrvDarwinIOCtlSlow(pSession, iCmd, pData, pProcess);
471}
472
473
474/**
475 * Worker for VBoxDrvDarwinIOCtl 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 SUPDRVIOCTLDATA buffer.
482 * @param pProcess The calling process.
483 */
484static int VBoxDrvDarwinIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess)
485{
486 LogFlow(("VBoxDrvDarwinIOCtlSlow: pSession=%p iCmd=%p pData=%p pProcess=%p\n", pSession, iCmd, pData, pProcess));
487
488
489 /*
490 * Buffered or unbuffered?
491 */
492 PSUPREQHDR pHdr;
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 pHdr = (PSUPREQHDR)pData;
499 if (RT_UNLIKELY(cbReq < sizeof(*pHdr)))
500 {
501 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: cbReq=%#x < %#x; iCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), iCmd));
502 return EINVAL;
503 }
504 if (RT_UNLIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
505 {
506 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", pHdr->fFlags, iCmd));
507 return EINVAL;
508 }
509 if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq
510 || pHdr->cbIn < sizeof(*pHdr)
511 || pHdr->cbOut < sizeof(*pHdr)))
512 {
513 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x) != %#x; iCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, iCmd));
514 return EINVAL;
515 }
516 }
517 else if ((IOC_DIRMASK & iCmd) == IOC_VOID && !cbReq)
518 {
519 /*
520 * Get the header and figure out how much we're gonna have to read.
521 */
522 SUPREQHDR Hdr;
523 pUser = (user_addr_t)*(void **)pData;
524 int rc = copyin(pUser, &Hdr, sizeof(Hdr));
525 if (RT_UNLIKELY(rc))
526 {
527 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,Hdr,) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, rc, iCmd));
528 return rc;
529 }
530 if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
531 {
532 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", Hdr.fFlags, iCmd));
533 return EINVAL;
534 }
535 cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut);
536 if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr)
537 || Hdr.cbOut < sizeof(Hdr)
538 || cbReq > _1M*16))
539 {
540 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x); iCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, iCmd));
541 return EINVAL;
542 }
543
544 /*
545 * Allocate buffer and copy in the data.
546 */
547 pHdr = (PSUPREQHDR)RTMemTmpAlloc(cbReq);
548 if (!pHdr)
549 pvPageBuf = pHdr = (PSUPREQHDR)IOMallocAligned(RT_ALIGN_Z(cbReq, PAGE_SIZE), 8);
550 if (RT_UNLIKELY(!pHdr))
551 {
552 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: failed to allocate buffer of %d bytes; iCmd=%#lx\n", cbReq, iCmd));
553 return ENOMEM;
554 }
555 rc = copyin(pUser, pHdr, Hdr.cbIn);
556 if (RT_UNLIKELY(rc))
557 {
558 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,%p,%#x) -> %#x; iCmd=%#lx\n",
559 (unsigned long long)pUser, pHdr, Hdr.cbIn, rc, iCmd));
560 if (pvPageBuf)
561 IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
562 else
563 RTMemTmpFree(pHdr);
564 return rc;
565 }
566 }
567 else
568 {
569 Log(("VBoxDrvDarwinIOCtlSlow: huh? cbReq=%#x iCmd=%#lx\n", cbReq, iCmd));
570 return EINVAL;
571 }
572
573 /*
574 * Process the IOCtl.
575 */
576 int rc = supdrvIOCtl(iCmd, &g_DevExt, pSession, pHdr);
577 if (RT_LIKELY(!rc))
578 {
579 /*
580 * If not buffered, copy back the buffer before returning.
581 */
582 if (pUser)
583 {
584 uint32_t cbOut = pHdr->cbOut;
585 if (cbOut > cbReq)
586 {
587 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, iCmd));
588 cbOut = cbReq;
589 }
590 rc = copyout(pHdr, pUser, cbOut);
591 if (RT_UNLIKELY(rc))
592 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyout(%p,%llx,%#x) -> %d; uCmd=%#lx!\n",
593 pHdr, (unsigned long long)pUser, cbOut, rc, iCmd));
594
595 /* cleanup */
596 if (pvPageBuf)
597 IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
598 else
599 RTMemTmpFree(pHdr);
600 }
601 }
602 else
603 {
604 /*
605 * The request failed, just clean up.
606 */
607 if (pUser)
608 {
609 if (pvPageBuf)
610 IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
611 else
612 RTMemTmpFree(pHdr);
613 }
614
615 Log(("VBoxDrvDarwinIOCtlSlow: pid=%d iCmd=%lx pData=%p failed, rc=%d\n", proc_pid(pProcess), iCmd, (void *)pData, rc));
616 rc = EINVAL;
617 }
618
619 Log2(("VBoxDrvDarwinIOCtlSlow: returns %d\n", rc));
620 return rc;
621}
622
623
624/**
625 * The SUPDRV IDC entry point.
626 *
627 * @returns VBox status code, see supdrvIDC.
628 * @param iReq The request code.
629 * @param pReq The request.
630 */
631int VBOXCALL SUPDrvDarwinIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq)
632{
633 PSUPDRVSESSION pSession;
634
635 /*
636 * Some quick validations.
637 */
638 if (RT_UNLIKELY(!VALID_PTR(pReq)))
639 return VERR_INVALID_POINTER;
640
641 pSession = pReq->pSession;
642 if (pSession)
643 {
644 if (RT_UNLIKELY(!VALID_PTR(pSession)))
645 return VERR_INVALID_PARAMETER;
646 if (RT_UNLIKELY(pSession->pDevExt != &g_DevExt))
647 return VERR_INVALID_PARAMETER;
648 }
649 else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT))
650 return VERR_INVALID_PARAMETER;
651
652 /*
653 * Do the job.
654 */
655 return supdrvIDC(uReq, &g_DevExt, pSession, pReq);
656}
657
658
659/**
660 * Initializes any OS specific object creator fields.
661 */
662void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
663{
664 NOREF(pObj);
665 NOREF(pSession);
666}
667
668
669/**
670 * Checks if the session can access the object.
671 *
672 * @returns true if a decision has been made.
673 * @returns false if the default access policy should be applied.
674 *
675 * @param pObj The object in question.
676 * @param pSession The session wanting to access the object.
677 * @param pszObjName The object name, can be NULL.
678 * @param prc Where to store the result when returning true.
679 */
680bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
681{
682 NOREF(pObj);
683 NOREF(pSession);
684 NOREF(pszObjName);
685 NOREF(prc);
686 return false;
687}
688
689/**
690 * Callback for blah blah blah.
691 */
692IOReturn VBoxDrvDarwinSleepHandler(void * /* pvTarget */, void *pvRefCon, UInt32 uMessageType, IOService * /* pProvider */, void * /* pvMessageArgument */, vm_size_t /* argSize */)
693{
694 LogFlow(("VBoxDrv: Got sleep/wake notice. Message type was %X\n", (uint)uMessageType));
695
696 if (uMessageType == kIOMessageSystemWillSleep)
697 RTPowerSignalEvent(RTPOWEREVENT_SUSPEND);
698 else if (uMessageType == kIOMessageSystemHasPoweredOn)
699 RTPowerSignalEvent(RTPOWEREVENT_RESUME);
700
701 acknowledgeSleepWakeNotification(pvRefCon);
702
703 return 0;
704}
705
706
707/**
708 * Enables or disables VT-x using kernel functions.
709 *
710 * @returns VBox status code. VERR_NOT_SUPPORTED has a special meaning.
711 * @param fEnable Whether to enable or disable.
712 */
713int VBOXCALL supdrvOSEnableVTx(bool fEnable)
714{
715/* Zarking amateurish Apple engineering!
716 host_vmxon is actually buggy and may panic multicore machines. Reason, it
717 uses a simple lock which will disable preemption of the cpu/thread trying
718 to acquire it. Then it allocate wired memory in the kernel map for each
719 of the cpus in the system. If anyone else tries to mess around in the
720 kernel map on another CPU while this is going on, there is a fair chance
721 that it might cause the host_vmxon thread to block and hence panic since
722 preemption is disabled. Argh! */
723#if 0 /*def VBOX_WITH_HOST_VMX*/
724 int rc;
725 if (fEnable)
726 {
727 rc = host_vmxon(false /* exclusive */);
728 if (rc == 0 /* all ok */)
729 rc = VINF_SUCCESS;
730 else if (rc == 1 /* unsupported */)
731 rc = VERR_VMX_NO_VMX;
732 else if (rc == 2 /* exclusive user */)
733 rc = VERR_VMX_IN_VMX_ROOT_MODE;
734 else /* shouldn't happen, but just in case. */
735 {
736 LogRel(("host_vmxon returned %d\n", rc));
737 rc = VERR_UNRESOLVED_ERROR;
738 }
739 }
740 else
741 {
742 host_vmxoff();
743 rc = VINF_SUCCESS;
744 }
745 return rc;
746#else
747 return VERR_NOT_SUPPORTED;
748#endif
749}
750
751
752bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt)
753{
754 NOREF(pDevExt);
755 return false;
756}
757
758
759int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
760{
761 NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
762 return VERR_NOT_SUPPORTED;
763}
764
765
766int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv, const uint8_t *pbImageBits)
767{
768 NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits);
769 return VERR_NOT_SUPPORTED;
770}
771
772
773int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits)
774{
775 NOREF(pDevExt); NOREF(pImage); NOREF(pbImageBits);
776 return VERR_NOT_SUPPORTED;
777}
778
779
780void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
781{
782 NOREF(pDevExt); NOREF(pImage);
783}
784
785
786/**
787 * Converts an IPRT error code to a darwin error code.
788 *
789 * @returns corresponding darwin error code.
790 * @param rc IPRT status code.
791 */
792static int VBoxDrvDarwinErr2DarwinErr(int rc)
793{
794 switch (rc)
795 {
796 case VINF_SUCCESS: return 0;
797 case VERR_GENERAL_FAILURE: return EACCES;
798 case VERR_INVALID_PARAMETER: return EINVAL;
799 case VERR_INVALID_MAGIC: return EILSEQ;
800 case VERR_INVALID_HANDLE: return ENXIO;
801 case VERR_INVALID_POINTER: return EFAULT;
802 case VERR_LOCK_FAILED: return ENOLCK;
803 case VERR_ALREADY_LOADED: return EEXIST;
804 case VERR_PERMISSION_DENIED: return EPERM;
805 case VERR_VERSION_MISMATCH: return ENOSYS;
806 }
807
808 return EPERM;
809}
810
811
812/** @todo move this to assembly where a simple "jmp printf" will to the trick. */
813RTDECL(int) SUPR0Printf(const char *pszFormat, ...)
814{
815 va_list args;
816 char szMsg[512];
817
818 va_start(args, pszFormat);
819 vsnprintf(szMsg, sizeof(szMsg) - 1, pszFormat, args);
820 va_end(args);
821
822 szMsg[sizeof(szMsg) - 1] = '\0';
823 printf("%s", szMsg);
824 return 0;
825}
826
827
828/*
829 *
830 * org_virtualbox_SupDrv
831 *
832 */
833
834
835/**
836 * Initialize the object.
837 */
838bool org_virtualbox_SupDrv::init(OSDictionary *pDictionary)
839{
840 LogFlow(("org_virtualbox_SupDrv::init([%p], %p)\n", this, pDictionary));
841 if (IOService::init(pDictionary))
842 {
843 /* init members. */
844 return true;
845 }
846 return false;
847}
848
849
850/**
851 * Free the object.
852 */
853void org_virtualbox_SupDrv::free(void)
854{
855 LogFlow(("IOService::free([%p])\n", this));
856 IOService::free();
857}
858
859
860/**
861 * Check if it's ok to start this service.
862 * It's always ok by us, so it's up to IOService to decide really.
863 */
864IOService *org_virtualbox_SupDrv::probe(IOService *pProvider, SInt32 *pi32Score)
865{
866 LogFlow(("org_virtualbox_SupDrv::probe([%p])\n", this));
867 return IOService::probe(pProvider, pi32Score);
868}
869
870
871/**
872 * Start this service.
873 */
874bool org_virtualbox_SupDrv::start(IOService *pProvider)
875{
876 LogFlow(("org_virtualbox_SupDrv::start([%p])\n", this));
877
878 if (IOService::start(pProvider))
879 {
880 /* register the service. */
881 registerService();
882 return true;
883 }
884 return false;
885}
886
887
888/**
889 * Stop this service.
890 */
891void org_virtualbox_SupDrv::stop(IOService *pProvider)
892{
893 LogFlow(("org_virtualbox_SupDrv::stop([%p], %p)\n", this, pProvider));
894 IOService::stop(pProvider);
895}
896
897
898/**
899 * Termination request.
900 *
901 * @return true if we're ok with shutting down now, false if we're not.
902 * @param fOptions Flags.
903 */
904bool org_virtualbox_SupDrv::terminate(IOOptionBits fOptions)
905{
906 bool fRc;
907 LogFlow(("org_virtualbox_SupDrv::terminate: reference_count=%d g_cSessions=%d (fOptions=%#x)\n",
908 KMOD_INFO_NAME.reference_count, ASMAtomicUoReadS32(&g_cSessions), fOptions));
909 if ( KMOD_INFO_NAME.reference_count != 0
910 || ASMAtomicUoReadS32(&g_cSessions))
911 fRc = false;
912 else
913 fRc = IOService::terminate(fOptions);
914 LogFlow(("org_virtualbox_SupDrv::terminate: returns %d\n", fRc));
915 return fRc;
916}
917
918
919/*
920 *
921 * org_virtualbox_SupDrvClient
922 *
923 */
924
925
926/**
927 * Initializer called when the client opens the service.
928 */
929bool org_virtualbox_SupDrvClient::initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type)
930{
931 LogFlow(("org_virtualbox_SupDrvClient::initWithTask([%p], %#x, %p, %#x) (cur pid=%d proc=%p)\n",
932 this, OwningTask, pvSecurityId, u32Type, RTProcSelf(), RTR0ProcHandleSelf()));
933 AssertMsg((RTR0PROCESS)OwningTask == RTR0ProcHandleSelf(), ("%p %p\n", OwningTask, RTR0ProcHandleSelf()));
934
935 if (!OwningTask)
936 return false;
937 if (IOUserClient::initWithTask(OwningTask, pvSecurityId , u32Type))
938 {
939 m_Task = OwningTask;
940 m_pSession = NULL;
941 m_pProvider = NULL;
942 return true;
943 }
944 return false;
945}
946
947
948/**
949 * Start the client service.
950 */
951bool org_virtualbox_SupDrvClient::start(IOService *pProvider)
952{
953 LogFlow(("org_virtualbox_SupDrvClient::start([%p], %p) (cur pid=%d proc=%p)\n",
954 this, pProvider, RTProcSelf(), RTR0ProcHandleSelf() ));
955 AssertMsgReturn((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(),
956 ("%p %p\n", m_Task, RTR0ProcHandleSelf()),
957 false);
958
959 if (IOUserClient::start(pProvider))
960 {
961 m_pProvider = OSDynamicCast(org_virtualbox_SupDrv, pProvider);
962 if (m_pProvider)
963 {
964 Assert(!m_pSession);
965
966 /*
967 * Create a new session.
968 */
969 int rc = supdrvCreateSession(&g_DevExt, true /* fUser */, &m_pSession);
970 if (RT_SUCCESS(rc))
971 {
972 m_pSession->fOpened = false;
973 /* The Uid and Gid fields are set on open. */
974
975 /*
976 * Insert it into the hash table, checking that there isn't
977 * already one for this process first.
978 */
979 unsigned iHash = SESSION_HASH(m_pSession->Process);
980 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
981 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
982
983 PSUPDRVSESSION pCur = g_apSessionHashTab[iHash];
984 if (pCur && pCur->Process != m_pSession->Process)
985 {
986 do pCur = pCur->pNextHash;
987 while (pCur && pCur->Process != m_pSession->Process);
988 }
989 if (!pCur)
990 {
991 m_pSession->pNextHash = g_apSessionHashTab[iHash];
992 g_apSessionHashTab[iHash] = m_pSession;
993 m_pSession->pvSupDrvClient = this;
994 ASMAtomicIncS32(&g_cSessions);
995 rc = VINF_SUCCESS;
996 }
997 else
998 rc = VERR_ALREADY_LOADED;
999
1000 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
1001 if (RT_SUCCESS(rc))
1002 {
1003 Log(("org_virtualbox_SupDrvClient::start: created session %p for pid %d\n", m_pSession, (int)RTProcSelf()));
1004 return true;
1005 }
1006
1007 LogFlow(("org_virtualbox_SupDrvClient::start: already got a session for this process (%p)\n", pCur));
1008 supdrvCloseSession(&g_DevExt, m_pSession);
1009 }
1010
1011 m_pSession = NULL;
1012 LogFlow(("org_virtualbox_SupDrvClient::start: rc=%Rrc from supdrvCreateSession\n", rc));
1013 }
1014 else
1015 LogFlow(("org_virtualbox_SupDrvClient::start: %p isn't org_virtualbox_SupDrv\n", pProvider));
1016 }
1017 return false;
1018}
1019
1020
1021/**
1022 * Common worker for clientClose and VBoxDrvDarwinClose.
1023 *
1024 * It will
1025 */
1026/* static */ void org_virtualbox_SupDrvClient::sessionClose(RTPROCESS Process)
1027{
1028 /*
1029 * Look for the session.
1030 */
1031 const unsigned iHash = SESSION_HASH(Process);
1032 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1033 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
1034 PSUPDRVSESSION pSession = g_apSessionHashTab[iHash];
1035 if (pSession)
1036 {
1037 if (pSession->Process == Process)
1038 {
1039 g_apSessionHashTab[iHash] = pSession->pNextHash;
1040 pSession->pNextHash = NULL;
1041 ASMAtomicDecS32(&g_cSessions);
1042 }
1043 else
1044 {
1045 PSUPDRVSESSION pPrev = pSession;
1046 pSession = pSession->pNextHash;
1047 while (pSession)
1048 {
1049 if (pSession->Process == Process)
1050 {
1051 pPrev->pNextHash = pSession->pNextHash;
1052 pSession->pNextHash = NULL;
1053 ASMAtomicDecS32(&g_cSessions);
1054 break;
1055 }
1056
1057 /* next */
1058 pPrev = pSession;
1059 pSession = pSession->pNextHash;
1060 }
1061 }
1062 }
1063 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
1064 if (!pSession)
1065 {
1066 Log(("SupDrvClient::sessionClose: pSession == NULL, pid=%d; freed already?\n", (int)Process));
1067 return;
1068 }
1069
1070 /*
1071 * Remove it from the client object.
1072 */
1073 org_virtualbox_SupDrvClient *pThis = (org_virtualbox_SupDrvClient *)pSession->pvSupDrvClient;
1074 pSession->pvSupDrvClient = NULL;
1075 if (pThis)
1076 {
1077 Assert(pThis->m_pSession == pSession);
1078 pThis->m_pSession = NULL;
1079 }
1080
1081 /*
1082 * Close the session.
1083 */
1084 supdrvCloseSession(&g_DevExt, pSession);
1085}
1086
1087
1088/**
1089 * Client exits normally.
1090 */
1091IOReturn org_virtualbox_SupDrvClient::clientClose(void)
1092{
1093 LogFlow(("org_virtualbox_SupDrvClient::clientClose([%p]) (cur pid=%d proc=%p)\n", this, RTProcSelf(), RTR0ProcHandleSelf()));
1094 AssertMsg((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(), ("%p %p\n", m_Task, RTR0ProcHandleSelf()));
1095
1096 /*
1097 * Clean up the session if it's still around.
1098 *
1099 * We cannot rely 100% on close, and in the case of a dead client
1100 * we'll end up hanging inside vm_map_remove() if we postpone it.
1101 */
1102 if (m_pSession)
1103 {
1104 sessionClose(RTProcSelf());
1105 Assert(!m_pSession);
1106 }
1107
1108 m_pProvider = NULL;
1109 terminate();
1110
1111 return kIOReturnSuccess;
1112}
1113
1114
1115/**
1116 * The client exits abnormally / forgets to do cleanups. (logging)
1117 */
1118IOReturn org_virtualbox_SupDrvClient::clientDied(void)
1119{
1120 LogFlow(("org_virtualbox_SupDrvClient::clientDied([%p]) m_Task=%p R0Process=%p Process=%d\n",
1121 this, m_Task, RTR0ProcHandleSelf(), RTProcSelf()));
1122
1123 /* IOUserClient::clientDied() calls clientClose, so we'll just do the work there. */
1124 return IOUserClient::clientDied();
1125}
1126
1127
1128/**
1129 * Terminate the service (initiate the destruction). (logging)
1130 */
1131bool org_virtualbox_SupDrvClient::terminate(IOOptionBits fOptions)
1132{
1133 LogFlow(("org_virtualbox_SupDrvClient::terminate([%p], %#x)\n", this, fOptions));
1134 return IOUserClient::terminate(fOptions);
1135}
1136
1137
1138/**
1139 * The final stage of the client service destruction. (logging)
1140 */
1141bool org_virtualbox_SupDrvClient::finalize(IOOptionBits fOptions)
1142{
1143 LogFlow(("org_virtualbox_SupDrvClient::finalize([%p], %#x)\n", this, fOptions));
1144 return IOUserClient::finalize(fOptions);
1145}
1146
1147
1148/**
1149 * Stop the client service. (logging)
1150 */
1151void org_virtualbox_SupDrvClient::stop(IOService *pProvider)
1152{
1153 LogFlow(("org_virtualbox_SupDrvClient::stop([%p])\n", this));
1154 IOUserClient::stop(pProvider);
1155}
1156
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