VirtualBox

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

Last change on this file since 38075 was 37970, checked in by vboxsync, 13 years ago

SUPDrv: Use RTStrPrintfV instead of host snprintf in SUPR0Printf. (darwin+linux, todo: windows)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.4 KB
Line 
1/* $Id: SUPDrv-darwin.cpp 37970 2011-07-14 14:37:23Z 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#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
364 RTUID Uid = kauth_cred_getruid(pCred);
365 RTGID Gid = kauth_cred_getrgid(pCred);
366#else
367 RTUID Uid = pCred->cr_ruid;
368 RTGID Gid = pCred->cr_rgid;
369#endif
370 RTPROCESS Process = RTProcSelf();
371 unsigned iHash = SESSION_HASH(Process);
372 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
373 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
374
375 pSession = g_apSessionHashTab[iHash];
376 if (pSession && pSession->Process != Process)
377 {
378 do pSession = pSession->pNextHash;
379 while (pSession && pSession->Process != Process);
380 }
381 if (pSession)
382 {
383 if (!pSession->fOpened)
384 {
385 pSession->fOpened = true;
386 pSession->Uid = Uid;
387 pSession->Gid = Gid;
388 }
389 else
390 rc = VERR_ALREADY_LOADED;
391 }
392 else
393 rc = VERR_GENERAL_FAILURE;
394
395 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
396#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
397 kauth_cred_unref(&pCred);
398#else /* 10.4 */
399 /* The 10.4u SDK headers and 10.4.11 kernel source have inconsistent definitions
400 of kauth_cred_unref(), so use the other (now deprecated) API for releasing it. */
401 kauth_cred_rele(pCred);
402#endif /* 10.4 */
403 }
404 else
405 rc = VERR_INVALID_PARAMETER;
406
407#ifdef DEBUG_DARWIN_GIP
408 OSDBGPRINT(("VBoxDrvDarwinOpen: pid=%d '%s' pSession=%p rc=%d\n", proc_pid(pProcess), szName, pSession, rc));
409#else
410 Log(("VBoxDrvDarwinOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, proc_pid(pProcess)));
411#endif
412 return VBoxDrvDarwinErr2DarwinErr(rc);
413}
414
415
416/**
417 * Close device.
418 */
419static int VBoxDrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
420{
421 Log(("VBoxDrvDarwinClose: pid=%d\n", (int)RTProcSelf()));
422 Assert(proc_pid(pProcess) == (int)RTProcSelf());
423
424 /*
425 * Hand the session closing to org_virtualbox_SupDrvClient.
426 */
427 org_virtualbox_SupDrvClient::sessionClose(RTProcSelf());
428 return 0;
429}
430
431
432/**
433 * Device I/O Control entry point.
434 *
435 * @returns Darwin for slow IOCtls and VBox status code for the fast ones.
436 * @param Dev The device number (major+minor).
437 * @param iCmd The IOCtl command.
438 * @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)).
439 * @param fFlags Flag saying we're a character device (like we didn't know already).
440 * @param pProcess The process issuing this request.
441 */
442static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess)
443{
444 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
445 const RTPROCESS Process = proc_pid(pProcess);
446 const unsigned iHash = SESSION_HASH(Process);
447 PSUPDRVSESSION pSession;
448
449 /*
450 * Find the session.
451 */
452 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
453 pSession = g_apSessionHashTab[iHash];
454 if (pSession && pSession->Process != Process)
455 {
456 do pSession = pSession->pNextHash;
457 while (pSession && pSession->Process != Process);
458 }
459 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
460 if (!pSession)
461 {
462 OSDBGPRINT(("VBoxDrvDarwinIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#lx\n",
463 (int)Process, iCmd));
464 return EINVAL;
465 }
466
467 /*
468 * Deal with the two high-speed IOCtl that takes it's arguments from
469 * the session and iCmd, and only returns a VBox status code.
470 */
471 if ( iCmd == SUP_IOCTL_FAST_DO_RAW_RUN
472 || iCmd == SUP_IOCTL_FAST_DO_HWACC_RUN
473 || iCmd == SUP_IOCTL_FAST_DO_NOP)
474 return supdrvIOCtlFast(iCmd, *(uint32_t *)pData, &g_DevExt, pSession);
475 return VBoxDrvDarwinIOCtlSlow(pSession, iCmd, pData, pProcess);
476}
477
478
479/**
480 * Worker for VBoxDrvDarwinIOCtl that takes the slow IOCtl functions.
481 *
482 * @returns Darwin errno.
483 *
484 * @param pSession The session.
485 * @param iCmd The IOCtl command.
486 * @param pData Pointer to the kernel copy of the SUPDRVIOCTLDATA buffer.
487 * @param pProcess The calling process.
488 */
489static int VBoxDrvDarwinIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess)
490{
491 LogFlow(("VBoxDrvDarwinIOCtlSlow: pSession=%p iCmd=%p pData=%p pProcess=%p\n", pSession, iCmd, pData, pProcess));
492
493
494 /*
495 * Buffered or unbuffered?
496 */
497 PSUPREQHDR pHdr;
498 user_addr_t pUser = 0;
499 void *pvPageBuf = NULL;
500 uint32_t cbReq = IOCPARM_LEN(iCmd);
501 if ((IOC_DIRMASK & iCmd) == IOC_INOUT)
502 {
503 pHdr = (PSUPREQHDR)pData;
504 if (RT_UNLIKELY(cbReq < sizeof(*pHdr)))
505 {
506 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: cbReq=%#x < %#x; iCmd=%#lx\n", cbReq, (int)sizeof(*pHdr), iCmd));
507 return EINVAL;
508 }
509 if (RT_UNLIKELY((pHdr->fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
510 {
511 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", pHdr->fFlags, iCmd));
512 return EINVAL;
513 }
514 if (RT_UNLIKELY( RT_MAX(pHdr->cbIn, pHdr->cbOut) != cbReq
515 || pHdr->cbIn < sizeof(*pHdr)
516 || pHdr->cbOut < sizeof(*pHdr)))
517 {
518 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x) != %#x; iCmd=%#lx\n", pHdr->cbIn, pHdr->cbOut, cbReq, iCmd));
519 return EINVAL;
520 }
521 }
522 else if ((IOC_DIRMASK & iCmd) == IOC_VOID && !cbReq)
523 {
524 /*
525 * Get the header and figure out how much we're gonna have to read.
526 */
527 SUPREQHDR Hdr;
528 pUser = (user_addr_t)*(void **)pData;
529 int rc = copyin(pUser, &Hdr, sizeof(Hdr));
530 if (RT_UNLIKELY(rc))
531 {
532 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,Hdr,) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, rc, iCmd));
533 return rc;
534 }
535 if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
536 {
537 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: bad magic fFlags=%#x; iCmd=%#lx\n", Hdr.fFlags, iCmd));
538 return EINVAL;
539 }
540 cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut);
541 if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr)
542 || Hdr.cbOut < sizeof(Hdr)
543 || cbReq > _1M*16))
544 {
545 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: max(%#x,%#x); iCmd=%#lx\n", Hdr.cbIn, Hdr.cbOut, iCmd));
546 return EINVAL;
547 }
548
549 /*
550 * Allocate buffer and copy in the data.
551 */
552 pHdr = (PSUPREQHDR)RTMemTmpAlloc(cbReq);
553 if (!pHdr)
554 pvPageBuf = pHdr = (PSUPREQHDR)IOMallocAligned(RT_ALIGN_Z(cbReq, PAGE_SIZE), 8);
555 if (RT_UNLIKELY(!pHdr))
556 {
557 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: failed to allocate buffer of %d bytes; iCmd=%#lx\n", cbReq, iCmd));
558 return ENOMEM;
559 }
560 rc = copyin(pUser, pHdr, Hdr.cbIn);
561 if (RT_UNLIKELY(rc))
562 {
563 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyin(%llx,%p,%#x) -> %#x; iCmd=%#lx\n",
564 (unsigned long long)pUser, pHdr, Hdr.cbIn, rc, iCmd));
565 if (pvPageBuf)
566 IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
567 else
568 RTMemTmpFree(pHdr);
569 return rc;
570 }
571 }
572 else
573 {
574 Log(("VBoxDrvDarwinIOCtlSlow: huh? cbReq=%#x iCmd=%#lx\n", cbReq, iCmd));
575 return EINVAL;
576 }
577
578 /*
579 * Process the IOCtl.
580 */
581 int rc = supdrvIOCtl(iCmd, &g_DevExt, pSession, pHdr);
582 if (RT_LIKELY(!rc))
583 {
584 /*
585 * If not buffered, copy back the buffer before returning.
586 */
587 if (pUser)
588 {
589 uint32_t cbOut = pHdr->cbOut;
590 if (cbOut > cbReq)
591 {
592 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbOut, cbReq, iCmd));
593 cbOut = cbReq;
594 }
595 rc = copyout(pHdr, pUser, cbOut);
596 if (RT_UNLIKELY(rc))
597 OSDBGPRINT(("VBoxDrvDarwinIOCtlSlow: copyout(%p,%llx,%#x) -> %d; uCmd=%#lx!\n",
598 pHdr, (unsigned long long)pUser, cbOut, rc, iCmd));
599
600 /* cleanup */
601 if (pvPageBuf)
602 IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
603 else
604 RTMemTmpFree(pHdr);
605 }
606 }
607 else
608 {
609 /*
610 * The request failed, just clean up.
611 */
612 if (pUser)
613 {
614 if (pvPageBuf)
615 IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE));
616 else
617 RTMemTmpFree(pHdr);
618 }
619
620 Log(("VBoxDrvDarwinIOCtlSlow: pid=%d iCmd=%lx pData=%p failed, rc=%d\n", proc_pid(pProcess), iCmd, (void *)pData, rc));
621 rc = EINVAL;
622 }
623
624 Log2(("VBoxDrvDarwinIOCtlSlow: returns %d\n", rc));
625 return rc;
626}
627
628
629/**
630 * The SUPDRV IDC entry point.
631 *
632 * @returns VBox status code, see supdrvIDC.
633 * @param iReq The request code.
634 * @param pReq The request.
635 */
636int VBOXCALL SUPDrvDarwinIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq)
637{
638 PSUPDRVSESSION pSession;
639
640 /*
641 * Some quick validations.
642 */
643 if (RT_UNLIKELY(!VALID_PTR(pReq)))
644 return VERR_INVALID_POINTER;
645
646 pSession = pReq->pSession;
647 if (pSession)
648 {
649 if (RT_UNLIKELY(!VALID_PTR(pSession)))
650 return VERR_INVALID_PARAMETER;
651 if (RT_UNLIKELY(pSession->pDevExt != &g_DevExt))
652 return VERR_INVALID_PARAMETER;
653 }
654 else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT))
655 return VERR_INVALID_PARAMETER;
656
657 /*
658 * Do the job.
659 */
660 return supdrvIDC(uReq, &g_DevExt, pSession, pReq);
661}
662
663
664/**
665 * Initializes any OS specific object creator fields.
666 */
667void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
668{
669 NOREF(pObj);
670 NOREF(pSession);
671}
672
673
674/**
675 * Checks if the session can access the object.
676 *
677 * @returns true if a decision has been made.
678 * @returns false if the default access policy should be applied.
679 *
680 * @param pObj The object in question.
681 * @param pSession The session wanting to access the object.
682 * @param pszObjName The object name, can be NULL.
683 * @param prc Where to store the result when returning true.
684 */
685bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
686{
687 NOREF(pObj);
688 NOREF(pSession);
689 NOREF(pszObjName);
690 NOREF(prc);
691 return false;
692}
693
694/**
695 * Callback for blah blah blah.
696 */
697IOReturn VBoxDrvDarwinSleepHandler(void * /* pvTarget */, void *pvRefCon, UInt32 uMessageType, IOService * /* pProvider */, void * /* pvMessageArgument */, vm_size_t /* argSize */)
698{
699 LogFlow(("VBoxDrv: Got sleep/wake notice. Message type was %X\n", (uint)uMessageType));
700
701 if (uMessageType == kIOMessageSystemWillSleep)
702 RTPowerSignalEvent(RTPOWEREVENT_SUSPEND);
703 else if (uMessageType == kIOMessageSystemHasPoweredOn)
704 RTPowerSignalEvent(RTPOWEREVENT_RESUME);
705
706 acknowledgeSleepWakeNotification(pvRefCon);
707
708 return 0;
709}
710
711
712/**
713 * Enables or disables VT-x using kernel functions.
714 *
715 * @returns VBox status code. VERR_NOT_SUPPORTED has a special meaning.
716 * @param fEnable Whether to enable or disable.
717 */
718int VBOXCALL supdrvOSEnableVTx(bool fEnable)
719{
720/* Zarking amateurish Apple engineering!
721 host_vmxon is actually buggy and may panic multicore machines. Reason, it
722 uses a simple lock which will disable preemption of the cpu/thread trying
723 to acquire it. Then it allocate wired memory in the kernel map for each
724 of the cpus in the system. If anyone else tries to mess around in the
725 kernel map on another CPU while this is going on, there is a fair chance
726 that it might cause the host_vmxon thread to block and hence panic since
727 preemption is disabled. Argh! */
728#if 0 /*def VBOX_WITH_HOST_VMX*/
729 int rc;
730 if (fEnable)
731 {
732 rc = host_vmxon(false /* exclusive */);
733 if (rc == 0 /* all ok */)
734 rc = VINF_SUCCESS;
735 else if (rc == 1 /* unsupported */)
736 rc = VERR_VMX_NO_VMX;
737 else if (rc == 2 /* exclusive user */)
738 rc = VERR_VMX_IN_VMX_ROOT_MODE;
739 else /* shouldn't happen, but just in case. */
740 {
741 LogRel(("host_vmxon returned %d\n", rc));
742 rc = VERR_UNRESOLVED_ERROR;
743 }
744 }
745 else
746 {
747 host_vmxoff();
748 rc = VINF_SUCCESS;
749 }
750 return rc;
751#else
752 return VERR_NOT_SUPPORTED;
753#endif
754}
755
756
757bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt)
758{
759 NOREF(pDevExt);
760 return false;
761}
762
763
764int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
765{
766 NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
767 return VERR_NOT_SUPPORTED;
768}
769
770
771int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv, const uint8_t *pbImageBits)
772{
773 NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits);
774 return VERR_NOT_SUPPORTED;
775}
776
777
778int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq)
779{
780 NOREF(pDevExt); NOREF(pImage); NOREF(pbImageBits); NOREF(pReq);
781 return VERR_NOT_SUPPORTED;
782}
783
784
785void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
786{
787 NOREF(pDevExt); NOREF(pImage);
788}
789
790
791/**
792 * Converts an IPRT error code to a darwin error code.
793 *
794 * @returns corresponding darwin error code.
795 * @param rc IPRT status code.
796 */
797static int VBoxDrvDarwinErr2DarwinErr(int rc)
798{
799 switch (rc)
800 {
801 case VINF_SUCCESS: return 0;
802 case VERR_GENERAL_FAILURE: return EACCES;
803 case VERR_INVALID_PARAMETER: return EINVAL;
804 case VERR_INVALID_MAGIC: return EILSEQ;
805 case VERR_INVALID_HANDLE: return ENXIO;
806 case VERR_INVALID_POINTER: return EFAULT;
807 case VERR_LOCK_FAILED: return ENOLCK;
808 case VERR_ALREADY_LOADED: return EEXIST;
809 case VERR_PERMISSION_DENIED: return EPERM;
810 case VERR_VERSION_MISMATCH: return ENOSYS;
811 }
812
813 return EPERM;
814}
815
816
817RTDECL(int) SUPR0Printf(const char *pszFormat, ...)
818{
819 va_list va;
820 char szMsg[512];
821
822 va_start(va, pszFormat);
823 RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
824 va_end(va);
825 szMsg[sizeof(szMsg) - 1] = '\0';
826
827 printf("%s", szMsg);
828 return 0;
829}
830
831
832/*
833 *
834 * org_virtualbox_SupDrv
835 *
836 */
837
838
839/**
840 * Initialize the object.
841 */
842bool org_virtualbox_SupDrv::init(OSDictionary *pDictionary)
843{
844 LogFlow(("org_virtualbox_SupDrv::init([%p], %p)\n", this, pDictionary));
845 if (IOService::init(pDictionary))
846 {
847 /* init members. */
848 return true;
849 }
850 return false;
851}
852
853
854/**
855 * Free the object.
856 */
857void org_virtualbox_SupDrv::free(void)
858{
859 LogFlow(("IOService::free([%p])\n", this));
860 IOService::free();
861}
862
863
864/**
865 * Check if it's ok to start this service.
866 * It's always ok by us, so it's up to IOService to decide really.
867 */
868IOService *org_virtualbox_SupDrv::probe(IOService *pProvider, SInt32 *pi32Score)
869{
870 LogFlow(("org_virtualbox_SupDrv::probe([%p])\n", this));
871 return IOService::probe(pProvider, pi32Score);
872}
873
874
875/**
876 * Start this service.
877 */
878bool org_virtualbox_SupDrv::start(IOService *pProvider)
879{
880 LogFlow(("org_virtualbox_SupDrv::start([%p])\n", this));
881
882 if (IOService::start(pProvider))
883 {
884 /* register the service. */
885 registerService();
886 return true;
887 }
888 return false;
889}
890
891
892/**
893 * Stop this service.
894 */
895void org_virtualbox_SupDrv::stop(IOService *pProvider)
896{
897 LogFlow(("org_virtualbox_SupDrv::stop([%p], %p)\n", this, pProvider));
898 IOService::stop(pProvider);
899}
900
901
902/**
903 * Termination request.
904 *
905 * @return true if we're ok with shutting down now, false if we're not.
906 * @param fOptions Flags.
907 */
908bool org_virtualbox_SupDrv::terminate(IOOptionBits fOptions)
909{
910 bool fRc;
911 LogFlow(("org_virtualbox_SupDrv::terminate: reference_count=%d g_cSessions=%d (fOptions=%#x)\n",
912 KMOD_INFO_NAME.reference_count, ASMAtomicUoReadS32(&g_cSessions), fOptions));
913 if ( KMOD_INFO_NAME.reference_count != 0
914 || ASMAtomicUoReadS32(&g_cSessions))
915 fRc = false;
916 else
917 fRc = IOService::terminate(fOptions);
918 LogFlow(("org_virtualbox_SupDrv::terminate: returns %d\n", fRc));
919 return fRc;
920}
921
922
923/*
924 *
925 * org_virtualbox_SupDrvClient
926 *
927 */
928
929
930/**
931 * Initializer called when the client opens the service.
932 */
933bool org_virtualbox_SupDrvClient::initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type)
934{
935 LogFlow(("org_virtualbox_SupDrvClient::initWithTask([%p], %#x, %p, %#x) (cur pid=%d proc=%p)\n",
936 this, OwningTask, pvSecurityId, u32Type, RTProcSelf(), RTR0ProcHandleSelf()));
937 AssertMsg((RTR0PROCESS)OwningTask == RTR0ProcHandleSelf(), ("%p %p\n", OwningTask, RTR0ProcHandleSelf()));
938
939 if (!OwningTask)
940 return false;
941 if (IOUserClient::initWithTask(OwningTask, pvSecurityId , u32Type))
942 {
943 m_Task = OwningTask;
944 m_pSession = NULL;
945 m_pProvider = NULL;
946 return true;
947 }
948 return false;
949}
950
951
952/**
953 * Start the client service.
954 */
955bool org_virtualbox_SupDrvClient::start(IOService *pProvider)
956{
957 LogFlow(("org_virtualbox_SupDrvClient::start([%p], %p) (cur pid=%d proc=%p)\n",
958 this, pProvider, RTProcSelf(), RTR0ProcHandleSelf() ));
959 AssertMsgReturn((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(),
960 ("%p %p\n", m_Task, RTR0ProcHandleSelf()),
961 false);
962
963 if (IOUserClient::start(pProvider))
964 {
965 m_pProvider = OSDynamicCast(org_virtualbox_SupDrv, pProvider);
966 if (m_pProvider)
967 {
968 Assert(!m_pSession);
969
970 /*
971 * Create a new session.
972 */
973 int rc = supdrvCreateSession(&g_DevExt, true /* fUser */, &m_pSession);
974 if (RT_SUCCESS(rc))
975 {
976 m_pSession->fOpened = false;
977 /* The Uid and Gid fields are set on open. */
978
979 /*
980 * Insert it into the hash table, checking that there isn't
981 * already one for this process first.
982 */
983 unsigned iHash = SESSION_HASH(m_pSession->Process);
984 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
985 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
986
987 PSUPDRVSESSION pCur = g_apSessionHashTab[iHash];
988 if (pCur && pCur->Process != m_pSession->Process)
989 {
990 do pCur = pCur->pNextHash;
991 while (pCur && pCur->Process != m_pSession->Process);
992 }
993 if (!pCur)
994 {
995 m_pSession->pNextHash = g_apSessionHashTab[iHash];
996 g_apSessionHashTab[iHash] = m_pSession;
997 m_pSession->pvSupDrvClient = this;
998 ASMAtomicIncS32(&g_cSessions);
999 rc = VINF_SUCCESS;
1000 }
1001 else
1002 rc = VERR_ALREADY_LOADED;
1003
1004 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
1005 if (RT_SUCCESS(rc))
1006 {
1007 Log(("org_virtualbox_SupDrvClient::start: created session %p for pid %d\n", m_pSession, (int)RTProcSelf()));
1008 return true;
1009 }
1010
1011 LogFlow(("org_virtualbox_SupDrvClient::start: already got a session for this process (%p)\n", pCur));
1012 supdrvCloseSession(&g_DevExt, m_pSession);
1013 }
1014
1015 m_pSession = NULL;
1016 LogFlow(("org_virtualbox_SupDrvClient::start: rc=%Rrc from supdrvCreateSession\n", rc));
1017 }
1018 else
1019 LogFlow(("org_virtualbox_SupDrvClient::start: %p isn't org_virtualbox_SupDrv\n", pProvider));
1020 }
1021 return false;
1022}
1023
1024
1025/**
1026 * Common worker for clientClose and VBoxDrvDarwinClose.
1027 *
1028 * It will
1029 */
1030/* static */ void org_virtualbox_SupDrvClient::sessionClose(RTPROCESS Process)
1031{
1032 /*
1033 * Look for the session.
1034 */
1035 const unsigned iHash = SESSION_HASH(Process);
1036 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1037 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
1038 PSUPDRVSESSION pSession = g_apSessionHashTab[iHash];
1039 if (pSession)
1040 {
1041 if (pSession->Process == Process)
1042 {
1043 g_apSessionHashTab[iHash] = pSession->pNextHash;
1044 pSession->pNextHash = NULL;
1045 ASMAtomicDecS32(&g_cSessions);
1046 }
1047 else
1048 {
1049 PSUPDRVSESSION pPrev = pSession;
1050 pSession = pSession->pNextHash;
1051 while (pSession)
1052 {
1053 if (pSession->Process == Process)
1054 {
1055 pPrev->pNextHash = pSession->pNextHash;
1056 pSession->pNextHash = NULL;
1057 ASMAtomicDecS32(&g_cSessions);
1058 break;
1059 }
1060
1061 /* next */
1062 pPrev = pSession;
1063 pSession = pSession->pNextHash;
1064 }
1065 }
1066 }
1067 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
1068 if (!pSession)
1069 {
1070 Log(("SupDrvClient::sessionClose: pSession == NULL, pid=%d; freed already?\n", (int)Process));
1071 return;
1072 }
1073
1074 /*
1075 * Remove it from the client object.
1076 */
1077 org_virtualbox_SupDrvClient *pThis = (org_virtualbox_SupDrvClient *)pSession->pvSupDrvClient;
1078 pSession->pvSupDrvClient = NULL;
1079 if (pThis)
1080 {
1081 Assert(pThis->m_pSession == pSession);
1082 pThis->m_pSession = NULL;
1083 }
1084
1085 /*
1086 * Close the session.
1087 */
1088 supdrvCloseSession(&g_DevExt, pSession);
1089}
1090
1091
1092/**
1093 * Client exits normally.
1094 */
1095IOReturn org_virtualbox_SupDrvClient::clientClose(void)
1096{
1097 LogFlow(("org_virtualbox_SupDrvClient::clientClose([%p]) (cur pid=%d proc=%p)\n", this, RTProcSelf(), RTR0ProcHandleSelf()));
1098 AssertMsg((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(), ("%p %p\n", m_Task, RTR0ProcHandleSelf()));
1099
1100 /*
1101 * Clean up the session if it's still around.
1102 *
1103 * We cannot rely 100% on close, and in the case of a dead client
1104 * we'll end up hanging inside vm_map_remove() if we postpone it.
1105 */
1106 if (m_pSession)
1107 {
1108 sessionClose(RTProcSelf());
1109 Assert(!m_pSession);
1110 }
1111
1112 m_pProvider = NULL;
1113 terminate();
1114
1115 return kIOReturnSuccess;
1116}
1117
1118
1119/**
1120 * The client exits abnormally / forgets to do cleanups. (logging)
1121 */
1122IOReturn org_virtualbox_SupDrvClient::clientDied(void)
1123{
1124 LogFlow(("org_virtualbox_SupDrvClient::clientDied([%p]) m_Task=%p R0Process=%p Process=%d\n",
1125 this, m_Task, RTR0ProcHandleSelf(), RTProcSelf()));
1126
1127 /* IOUserClient::clientDied() calls clientClose, so we'll just do the work there. */
1128 return IOUserClient::clientDied();
1129}
1130
1131
1132/**
1133 * Terminate the service (initiate the destruction). (logging)
1134 */
1135bool org_virtualbox_SupDrvClient::terminate(IOOptionBits fOptions)
1136{
1137 LogFlow(("org_virtualbox_SupDrvClient::terminate([%p], %#x)\n", this, fOptions));
1138 return IOUserClient::terminate(fOptions);
1139}
1140
1141
1142/**
1143 * The final stage of the client service destruction. (logging)
1144 */
1145bool org_virtualbox_SupDrvClient::finalize(IOOptionBits fOptions)
1146{
1147 LogFlow(("org_virtualbox_SupDrvClient::finalize([%p], %#x)\n", this, fOptions));
1148 return IOUserClient::finalize(fOptions);
1149}
1150
1151
1152/**
1153 * Stop the client service. (logging)
1154 */
1155void org_virtualbox_SupDrvClient::stop(IOService *pProvider)
1156{
1157 LogFlow(("org_virtualbox_SupDrvClient::stop([%p])\n", this));
1158 IOUserClient::stop(pProvider);
1159}
1160
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