VirtualBox

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

Last change on this file since 23491 was 23491, checked in by vboxsync, 15 years ago

Darwin: case-sensitive FS burns fixes.

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