VirtualBox

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

Last change on this file since 18478 was 16356, checked in by vboxsync, 16 years ago

SUPDrv-darwin.cpp: kauth_cred_unref messup in the 10.4u SDK headers, use kauth_cred_rele on 10.4.

File size: 34.8 KB
Line 
1/* $Id: $ */
2/** @file
3 *
4 * VBox host drivers - Ring-0 support drivers - Darwin host:
5 * Darwin driver C code
6 */
7
8/*
9 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * The contents of this file may alternatively be used under the terms
20 * of the Common Development and Distribution License Version 1.0
21 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
22 * VirtualBox OSE distribution, in which case the provisions of the
23 * CDDL are applicable instead of those of the GPL.
24 *
25 * You may elect to license modified versions of this file under the
26 * terms and conditions of either the GPL or the CDDL or both.
27 *
28 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
29 * Clara, CA 95054 USA or visit http://www.sun.com if you need
30 * additional information or have any questions.
31 */
32
33/*******************************************************************************
34* Header Files *
35*******************************************************************************/
36#define LOG_GROUP LOG_GROUP_SUP_DRV
37/*
38 * Deal with conflicts first.
39 * PVM - BSD mess, that FreeBSD has correct a long time ago.
40 * iprt/types.h before sys/param.h - prevents UINT32_C and friends.
41 */
42#include <iprt/types.h>
43#include <sys/param.h>
44#undef PVM
45
46#include <IOKit/IOLib.h> /* Assert as function */
47
48#include "../SUPDrvInternal.h"
49#include <VBox/version.h>
50#include <iprt/initterm.h>
51#include <iprt/assert.h>
52#include <iprt/spinlock.h>
53#include <iprt/semaphore.h>
54#include <iprt/process.h>
55#include <iprt/alloc.h>
56#include <iprt/power.h>
57#include <VBox/err.h>
58#include <VBox/log.h>
59
60#include <mach/kmod.h>
61#include <miscfs/devfs/devfs.h>
62#include <sys/conf.h>
63#include <sys/errno.h>
64#include <sys/ioccom.h>
65#include <sys/malloc.h>
66#include <sys/proc.h>
67#include <sys/kauth.h>
68#include <IOKit/IOService.h>
69#include <IOKit/IOUserclient.h>
70#include <IOKit/pwr_mgt/RootDomain.h>
71
72#ifdef VBOX_WITH_HOST_VMX
73__BEGIN_DECLS
74# include <i386/vmx.h>
75__END_DECLS
76#endif
77
78
79/*******************************************************************************
80* Defined Constants And Macros *
81*******************************************************************************/
82
83/** The module name. */
84#define DEVICE_NAME "vboxdrv"
85
86
87
88/*******************************************************************************
89* Internal Functions *
90*******************************************************************************/
91__BEGIN_DECLS
92static kern_return_t VBoxDrvDarwinStart(struct kmod_info *pKModInfo, void *pvData);
93static kern_return_t VBoxDrvDarwinStop(struct kmod_info *pKModInfo, void *pvData);
94
95static int VBoxDrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
96static int VBoxDrvDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
97static int VBoxDrvDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess);
98static int VBoxDrvDarwinIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess);
99
100static int VBoxDrvDarwinErr2DarwinErr(int rc);
101
102static IOReturn VBoxDrvDarwinSleepHandler(void *pvTarget, void *pvRefCon, UInt32 uMessageType, IOService *pProvider, void *pvMessageArgument, vm_size_t argSize);
103__END_DECLS
104
105
106/*******************************************************************************
107* Structures and Typedefs *
108*******************************************************************************/
109/**
110 * The service class.
111 * This is just a formality really.
112 */
113class org_virtualbox_SupDrv : public IOService
114{
115 OSDeclareDefaultStructors(org_virtualbox_SupDrv);
116
117public:
118 virtual bool init(OSDictionary *pDictionary = 0);
119 virtual void free(void);
120 virtual bool start(IOService *pProvider);
121 virtual void stop(IOService *pProvider);
122 virtual IOService *probe(IOService *pProvider, SInt32 *pi32Score);
123 virtual bool terminate(IOOptionBits fOptions);
124};
125
126OSDefineMetaClassAndStructors(org_virtualbox_SupDrv, IOService);
127
128
129/**
130 * An attempt at getting that clientDied() notification.
131 * I don't think it'll work as I cannot figure out where/what creates the correct
132 * port right.
133 */
134class org_virtualbox_SupDrvClient : public IOUserClient
135{
136 OSDeclareDefaultStructors(org_virtualbox_SupDrvClient);
137
138private:
139 PSUPDRVSESSION m_pSession; /**< The session. */
140 task_t m_Task; /**< The client task. */
141 org_virtualbox_SupDrv *m_pProvider; /**< The service provider. */
142
143public:
144 virtual bool initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type);
145 virtual bool start(IOService *pProvider);
146 static void sessionClose(RTPROCESS Process);
147 virtual IOReturn clientClose(void);
148 virtual IOReturn clientDied(void);
149 virtual bool terminate(IOOptionBits fOptions = 0);
150 virtual bool finalize(IOOptionBits fOptions);
151 virtual void stop(IOService *pProvider);
152};
153
154OSDefineMetaClassAndStructors(org_virtualbox_SupDrvClient, IOUserClient);
155
156
157
158/*******************************************************************************
159* Global Variables *
160*******************************************************************************/
161/**
162 * Declare the module stuff.
163 */
164__BEGIN_DECLS
165extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData);
166extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData);
167
168KMOD_EXPLICIT_DECL(VBoxDrv, VBOX_VERSION_STRING, _start, _stop)
169DECLHIDDEN(kmod_start_func_t *) _realmain = VBoxDrvDarwinStart;
170DECLHIDDEN(kmod_stop_func_t *) _antimain = VBoxDrvDarwinStop;
171DECLHIDDEN(int) _kext_apple_cc = __APPLE_CC__;
172__END_DECLS
173
174
175/**
176 * Device extention & session data association structure.
177 */
178static SUPDRVDEVEXT g_DevExt;
179
180/**
181 * The character device switch table for the driver.
182 */
183static struct cdevsw g_DevCW =
184{
185 /** @todo g++ doesn't like this syntax - it worked with gcc before renaming to .cpp. */
186 /*.d_open = */VBoxDrvDarwinOpen,
187 /*.d_close = */VBoxDrvDarwinClose,
188 /*.d_read = */eno_rdwrt,
189 /*.d_write = */eno_rdwrt,
190 /*.d_ioctl = */VBoxDrvDarwinIOCtl,
191 /*.d_stop = */eno_stop,
192 /*.d_reset = */eno_reset,
193 /*.d_ttys = */NULL,
194 /*.d_select= */eno_select,
195 /*.d_mmap = */eno_mmap,
196 /*.d_strategy = */eno_strat,
197 /*.d_getc = */eno_getc,
198 /*.d_putc = */eno_putc,
199 /*.d_type = */0
200};
201
202/** Major device number. */
203static int g_iMajorDeviceNo = -1;
204/** Registered devfs device handle. */
205static void *g_hDevFsDevice = NULL;
206
207/** Spinlock protecting g_apSessionHashTab. */
208static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
209/** Hash table */
210static PSUPDRVSESSION g_apSessionHashTab[19];
211/** Calculates the index into g_apSessionHashTab.*/
212#define SESSION_HASH(pid) ((pid) % RT_ELEMENTS(g_apSessionHashTab))
213/** The number of open sessions. */
214static int32_t volatile g_cSessions = 0;
215/** The notifier handle for the sleep callback handler. */
216static IONotifier *g_pSleepNotifier = NULL;
217
218
219
220/**
221 * Start the kernel module.
222 */
223static kern_return_t VBoxDrvDarwinStart(struct kmod_info *pKModInfo, void *pvData)
224{
225 int rc;
226#ifdef DEBUG
227 printf("VBoxDrvDarwinStart\n");
228#endif
229
230 /*
231 * Initialize IPRT.
232 */
233 rc = RTR0Init(0);
234 if (RT_SUCCESS(rc))
235 {
236 /*
237 * Initialize the device extension.
238 */
239 rc = supdrvInitDevExt(&g_DevExt);
240 if (RT_SUCCESS(rc))
241 {
242 /*
243 * Initialize the session hash table.
244 */
245 memset(g_apSessionHashTab, 0, sizeof(g_apSessionHashTab)); /* paranoia */
246 rc = RTSpinlockCreate(&g_Spinlock);
247 if (RT_SUCCESS(rc))
248 {
249 /*
250 * Registering ourselves as a character device.
251 */
252 g_iMajorDeviceNo = cdevsw_add(-1, &g_DevCW);
253 if (g_iMajorDeviceNo >= 0)
254 {
255#ifdef VBOX_WITH_HARDENING
256 g_hDevFsDevice = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR,
257 UID_ROOT, GID_WHEEL, 0600, DEVICE_NAME);
258#else
259 g_hDevFsDevice = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR,
260 UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME);
261#endif
262 if (g_hDevFsDevice)
263 {
264 LogRel(("VBoxDrv: version " VBOX_VERSION_STRING " r%d; IOCtl version %#x; IDC version %#x; dev major=%d\n",
265 VBOX_SVN_REV, SUPDRV_IOC_VERSION, SUPDRV_IDC_VERSION, g_iMajorDeviceNo));
266
267 /* Register a sleep/wakeup notification callback */
268 g_pSleepNotifier = registerPrioritySleepWakeInterest(&VBoxDrvDarwinSleepHandler, &g_DevExt, NULL);
269 if (g_pSleepNotifier == NULL)
270 LogRel(("VBoxDrv: register for sleep/wakeup events failed\n"));
271
272 return KMOD_RETURN_SUCCESS;
273 }
274
275 LogRel(("VBoxDrv: devfs_make_node(makedev(%d,0),,,,%s) failed\n", g_iMajorDeviceNo, DEVICE_NAME));
276 cdevsw_remove(g_iMajorDeviceNo, &g_DevCW);
277 g_iMajorDeviceNo = -1;
278 }
279 else
280 LogRel(("VBoxDrv: cdevsw_add failed (%d)\n", g_iMajorDeviceNo));
281 RTSpinlockDestroy(g_Spinlock);
282 g_Spinlock = NIL_RTSPINLOCK;
283 }
284 else
285 LogRel(("VBoxDrv: RTSpinlockCreate failed (rc=%d)\n", rc));
286 supdrvDeleteDevExt(&g_DevExt);
287 }
288 else
289 printf("VBoxDrv: failed to initialize device extension (rc=%d)\n", rc);
290 RTR0Term();
291 }
292 else
293 printf("VBoxDrv: failed to initialize IPRT (rc=%d)\n", rc);
294
295 memset(&g_DevExt, 0, sizeof(g_DevExt));
296 return KMOD_RETURN_FAILURE;
297}
298
299
300/**
301 * Stop the kernel module.
302 */
303static kern_return_t VBoxDrvDarwinStop(struct kmod_info *pKModInfo, void *pvData)
304{
305 int rc;
306 LogFlow(("VBoxDrvDarwinStop\n"));
307
308 /** @todo I've got a nagging feeling that we'll have to keep track of users and refuse
309 * unloading if we're busy. Investigate and implement this! */
310
311 /*
312 * Undo the work done during start (in reverse order).
313 */
314 if (g_pSleepNotifier)
315 {
316 g_pSleepNotifier->remove();
317 g_pSleepNotifier = NULL;
318 }
319
320 devfs_remove(g_hDevFsDevice);
321 g_hDevFsDevice = NULL;
322
323 rc = cdevsw_remove(g_iMajorDeviceNo, &g_DevCW);
324 Assert(rc == g_iMajorDeviceNo);
325 g_iMajorDeviceNo = -1;
326
327 supdrvDeleteDevExt(&g_DevExt);
328
329 rc = RTSpinlockDestroy(g_Spinlock);
330 AssertRC(rc);
331 g_Spinlock = NIL_RTSPINLOCK;
332
333 RTR0Term();
334
335 memset(&g_DevExt, 0, sizeof(g_DevExt));
336#ifdef DEBUG
337 printf("VBoxDrvDarwinStop - done\n");
338#endif
339 return KMOD_RETURN_SUCCESS;
340}
341
342
343/**
344 * Device open. Called on open /dev/vboxdrv
345 *
346 * @param pInode Pointer to inode info structure.
347 * @param pFilp Associated file pointer.
348 */
349static int VBoxDrvDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
350{
351#ifdef DEBUG_DARWIN_GIP
352 char szName[128];
353 szName[0] = '\0';
354 proc_name(proc_pid(pProcess), szName, sizeof(szName));
355 Log(("VBoxDrvDarwinOpen: pid=%d '%s'\n", proc_pid(pProcess), szName));
356#endif
357
358 /*
359 * Find the session created by org_virtualbox_SupDrvClient, fail
360 * if no such session, and mark it as opened. We set the uid & gid
361 * here too, since that is more straight forward at this point.
362 */
363 int rc = VINF_SUCCESS;
364 PSUPDRVSESSION pSession = NULL;
365 kauth_cred_t pCred = kauth_cred_proc_ref(pProcess);
366 if (pCred)
367 {
368 RTUID Uid = pCred->cr_ruid;
369 RTGID Gid = pCred->cr_rgid;
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 inconsisten defintions
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 = SUPDRV_ERR_INVALID_PARAM;
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. Arrrg! */
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
764/**
765 * Converts a supdrv error code to a darwin error code.
766 *
767 * @returns corresponding darwin error code.
768 * @param rc supdrv error code (SUPDRV_ERR_* defines).
769 */
770static int VBoxDrvDarwinErr2DarwinErr(int rc)
771{
772 switch (rc)
773 {
774 case 0: return 0;
775 case SUPDRV_ERR_GENERAL_FAILURE: return EACCES;
776 case SUPDRV_ERR_INVALID_PARAM: return EINVAL;
777 case SUPDRV_ERR_INVALID_MAGIC: return EILSEQ;
778 case SUPDRV_ERR_INVALID_HANDLE: return ENXIO;
779 case SUPDRV_ERR_INVALID_POINTER: return EFAULT;
780 case SUPDRV_ERR_LOCK_FAILED: return ENOLCK;
781 case SUPDRV_ERR_ALREADY_LOADED: return EEXIST;
782 case SUPDRV_ERR_PERMISSION_DENIED: return EPERM;
783 case SUPDRV_ERR_VERSION_MISMATCH: return ENOSYS;
784 }
785
786 return EPERM;
787}
788
789
790/** @todo move this to assembly where a simple "jmp printf" will to the trick. */
791RTDECL(int) SUPR0Printf(const char *pszFormat, ...)
792{
793 va_list args;
794 char szMsg[512];
795
796 va_start(args, pszFormat);
797 vsnprintf(szMsg, sizeof(szMsg) - 1, pszFormat, args);
798 va_end(args);
799
800 szMsg[sizeof(szMsg) - 1] = '\0';
801 printf("%s", szMsg);
802 return 0;
803}
804
805
806/*
807 *
808 * org_virtualbox_SupDrv
809 *
810 */
811
812
813/**
814 * Initialize the object.
815 */
816bool org_virtualbox_SupDrv::init(OSDictionary *pDictionary)
817{
818 LogFlow(("org_virtualbox_SupDrv::init([%p], %p)\n", this, pDictionary));
819 if (IOService::init(pDictionary))
820 {
821 /* init members. */
822 return true;
823 }
824 return false;
825}
826
827
828/**
829 * Free the object.
830 */
831void org_virtualbox_SupDrv::free(void)
832{
833 LogFlow(("IOService::free([%p])\n", this));
834 IOService::free();
835}
836
837
838/**
839 * Check if it's ok to start this service.
840 * It's always ok by us, so it's up to IOService to decide really.
841 */
842IOService *org_virtualbox_SupDrv::probe(IOService *pProvider, SInt32 *pi32Score)
843{
844 LogFlow(("org_virtualbox_SupDrv::probe([%p])\n", this));
845 return IOService::probe(pProvider, pi32Score);
846}
847
848
849/**
850 * Start this service.
851 */
852bool org_virtualbox_SupDrv::start(IOService *pProvider)
853{
854 LogFlow(("org_virtualbox_SupDrv::start([%p])\n", this));
855
856 if (IOService::start(pProvider))
857 {
858 /* register the service. */
859 registerService();
860 return true;
861 }
862 return false;
863}
864
865
866/**
867 * Stop this service.
868 */
869void org_virtualbox_SupDrv::stop(IOService *pProvider)
870{
871 LogFlow(("org_virtualbox_SupDrv::stop([%p], %p)\n", this, pProvider));
872 IOService::stop(pProvider);
873}
874
875
876/**
877 * Termination request.
878 *
879 * @return true if we're ok with shutting down now, false if we're not.
880 * @param fOptions Flags.
881 */
882bool org_virtualbox_SupDrv::terminate(IOOptionBits fOptions)
883{
884 bool fRc;
885 LogFlow(("org_virtualbox_SupDrv::terminate: reference_count=%d g_cSessions=%d (fOptions=%#x)\n",
886 KMOD_INFO_NAME.reference_count, ASMAtomicUoReadS32(&g_cSessions), fOptions));
887 if ( KMOD_INFO_NAME.reference_count != 0
888 || ASMAtomicUoReadS32(&g_cSessions))
889 fRc = false;
890 else
891 fRc = IOService::terminate(fOptions);
892 LogFlow(("org_virtualbox_SupDrv::terminate: returns %d\n", fRc));
893 return fRc;
894}
895
896
897/*
898 *
899 * org_virtualbox_SupDrvClient
900 *
901 */
902
903
904/**
905 * Initializer called when the client opens the service.
906 */
907bool org_virtualbox_SupDrvClient::initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type)
908{
909 LogFlow(("org_virtualbox_SupDrvClient::initWithTask([%p], %#x, %p, %#x) (cur pid=%d proc=%p)\n",
910 this, OwningTask, pvSecurityId, u32Type, RTProcSelf(), RTR0ProcHandleSelf()));
911 AssertMsg((RTR0PROCESS)OwningTask == RTR0ProcHandleSelf(), ("%p %p\n", OwningTask, RTR0ProcHandleSelf()));
912
913 if (!OwningTask)
914 return false;
915 if (IOUserClient::initWithTask(OwningTask, pvSecurityId , u32Type))
916 {
917 m_Task = OwningTask;
918 m_pSession = NULL;
919 m_pProvider = NULL;
920 return true;
921 }
922 return false;
923}
924
925
926/**
927 * Start the client service.
928 */
929bool org_virtualbox_SupDrvClient::start(IOService *pProvider)
930{
931 LogFlow(("org_virtualbox_SupDrvClient::start([%p], %p) (cur pid=%d proc=%p)\n",
932 this, pProvider, RTProcSelf(), RTR0ProcHandleSelf() ));
933 AssertMsgReturn((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(),
934 ("%p %p\n", m_Task, RTR0ProcHandleSelf()),
935 false);
936
937 if (IOUserClient::start(pProvider))
938 {
939 m_pProvider = OSDynamicCast(org_virtualbox_SupDrv, pProvider);
940 if (m_pProvider)
941 {
942 Assert(!m_pSession);
943
944 /*
945 * Create a new session.
946 */
947 int rc = supdrvCreateSession(&g_DevExt, true /* fUser */, &m_pSession);
948 if (RT_SUCCESS(rc))
949 {
950 m_pSession->fOpened = false;
951 /* The Uid and Gid fields are set on open. */
952
953 /*
954 * Insert it into the hash table, checking that there isn't
955 * already one for this process first.
956 */
957 unsigned iHash = SESSION_HASH(m_pSession->Process);
958 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
959 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
960
961 PSUPDRVSESSION pCur = g_apSessionHashTab[iHash];
962 if (pCur && pCur->Process != m_pSession->Process)
963 {
964 do pCur = pCur->pNextHash;
965 while (pCur && pCur->Process != m_pSession->Process);
966 }
967 if (!pCur)
968 {
969 m_pSession->pNextHash = g_apSessionHashTab[iHash];
970 g_apSessionHashTab[iHash] = m_pSession;
971 m_pSession->pvSupDrvClient = this;
972 ASMAtomicIncS32(&g_cSessions);
973 rc = VINF_SUCCESS;
974 }
975 else
976 rc = VERR_ALREADY_LOADED;
977
978 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
979 if (RT_SUCCESS(rc))
980 {
981 Log(("org_virtualbox_SupDrvClient::start: created session %p for pid %d\n", m_pSession, (int)RTProcSelf()));
982 return true;
983 }
984
985 LogFlow(("org_virtualbox_SupDrvClient::start: already got a session for this process (%p)\n", pCur));
986 supdrvCloseSession(&g_DevExt, m_pSession);
987 }
988
989 m_pSession = NULL;
990 LogFlow(("org_virtualbox_SupDrvClient::start: rc=%Rrc from supdrvCreateSession\n", rc));
991 }
992 else
993 LogFlow(("org_virtualbox_SupDrvClient::start: %p isn't org_virtualbox_SupDrv\n", pProvider));
994 }
995 return false;
996}
997
998
999/**
1000 * Common worker for clientClose and VBoxDrvDarwinClose.
1001 *
1002 * It will
1003 */
1004/* static */ void org_virtualbox_SupDrvClient::sessionClose(RTPROCESS Process)
1005{
1006 /*
1007 * Look for the session.
1008 */
1009 const unsigned iHash = SESSION_HASH(Process);
1010 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1011 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
1012 PSUPDRVSESSION pSession = g_apSessionHashTab[iHash];
1013 if (pSession)
1014 {
1015 if (pSession->Process == Process)
1016 {
1017 g_apSessionHashTab[iHash] = pSession->pNextHash;
1018 pSession->pNextHash = NULL;
1019 ASMAtomicDecS32(&g_cSessions);
1020 }
1021 else
1022 {
1023 PSUPDRVSESSION pPrev = pSession;
1024 pSession = pSession->pNextHash;
1025 while (pSession)
1026 {
1027 if (pSession->Process == Process)
1028 {
1029 pPrev->pNextHash = pSession->pNextHash;
1030 pSession->pNextHash = NULL;
1031 ASMAtomicDecS32(&g_cSessions);
1032 break;
1033 }
1034
1035 /* next */
1036 pPrev = pSession;
1037 pSession = pSession->pNextHash;
1038 }
1039 }
1040 }
1041 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
1042 if (!pSession)
1043 {
1044 Log(("SupDrvClient::sessionClose: pSession == NULL, pid=%d; freed already?\n", (int)Process));
1045 return;
1046 }
1047
1048 /*
1049 * Remove it from the client object.
1050 */
1051 org_virtualbox_SupDrvClient *pThis = (org_virtualbox_SupDrvClient *)pSession->pvSupDrvClient;
1052 pSession->pvSupDrvClient = NULL;
1053 if (pThis)
1054 {
1055 Assert(pThis->m_pSession == pSession);
1056 pThis->m_pSession = NULL;
1057 }
1058
1059 /*
1060 * Close the session.
1061 */
1062 supdrvCloseSession(&g_DevExt, pSession);
1063}
1064
1065
1066/**
1067 * Client exits normally.
1068 */
1069IOReturn org_virtualbox_SupDrvClient::clientClose(void)
1070{
1071 LogFlow(("org_virtualbox_SupDrvClient::clientClose([%p]) (cur pid=%d proc=%p)\n", this, RTProcSelf(), RTR0ProcHandleSelf()));
1072 AssertMsg((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(), ("%p %p\n", m_Task, RTR0ProcHandleSelf()));
1073
1074 /*
1075 * Clean up the session if it's still around.
1076 *
1077 * We cannot rely 100% on close, and in the case of a dead client
1078 * we'll end up hanging inside vm_map_remove() if we postpone it.
1079 */
1080 if (m_pSession)
1081 {
1082 sessionClose(RTProcSelf());
1083 Assert(!m_pSession);
1084 }
1085
1086 m_pProvider = NULL;
1087 terminate();
1088
1089 return kIOReturnSuccess;
1090}
1091
1092
1093/**
1094 * The client exits abnormally / forgets to do cleanups. (logging)
1095 */
1096IOReturn org_virtualbox_SupDrvClient::clientDied(void)
1097{
1098 LogFlow(("org_virtualbox_SupDrvClient::clientDied([%p]) m_Task=%p R0Process=%p Process=%d\n",
1099 this, m_Task, RTR0ProcHandleSelf(), RTProcSelf()));
1100
1101 /* IOUserClient::clientDied() calls clientClose, so we'll just do the work there. */
1102 return IOUserClient::clientDied();
1103}
1104
1105
1106/**
1107 * Terminate the service (initiate the destruction). (logging)
1108 */
1109bool org_virtualbox_SupDrvClient::terminate(IOOptionBits fOptions)
1110{
1111 LogFlow(("org_virtualbox_SupDrvClient::terminate([%p], %#x)\n", this, fOptions));
1112 return IOUserClient::terminate(fOptions);
1113}
1114
1115
1116/**
1117 * The final stage of the client service destruction. (logging)
1118 */
1119bool org_virtualbox_SupDrvClient::finalize(IOOptionBits fOptions)
1120{
1121 LogFlow(("org_virtualbox_SupDrvClient::finalize([%p], %#x)\n", this, fOptions));
1122 return IOUserClient::finalize(fOptions);
1123}
1124
1125
1126/**
1127 * Stop the client service. (logging)
1128 */
1129void org_virtualbox_SupDrvClient::stop(IOService *pProvider)
1130{
1131 LogFlow(("org_virtualbox_SupDrvClient::stop([%p])\n", this));
1132 IOUserClient::stop(pProvider);
1133}
1134
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