VirtualBox

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

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

SUPDrv: Fixed recent regression that may cause the SUPDRVSESSION allocation to be too small on some platforms, and thereby corrupting heap or/and triggering system panic.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.5 KB
Line 
1/* $Id: SUPDrv-darwin.cpp 25484 2009-12-18 14:04:56Z 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, sizeof(SUPDRVSESSION));
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 RTR0TermForced();
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 RTR0TermForced();
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 = VERR_INVALID_PARAMETER;
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
762int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
763{
764 NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
765 return VERR_NOT_SUPPORTED;
766}
767
768
769int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv, const uint8_t *pbImageBits)
770{
771 NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits);
772 return VERR_NOT_SUPPORTED;
773}
774
775
776int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits)
777{
778 NOREF(pDevExt); NOREF(pImage); NOREF(pbImageBits);
779 return VERR_NOT_SUPPORTED;
780}
781
782
783void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
784{
785 NOREF(pDevExt); NOREF(pImage);
786}
787
788
789/**
790 * Converts an IPRT error code to a darwin error code.
791 *
792 * @returns corresponding darwin error code.
793 * @param rc IPRT status code.
794 */
795static int VBoxDrvDarwinErr2DarwinErr(int rc)
796{
797 switch (rc)
798 {
799 case VINF_SUCCESS: return 0;
800 case VERR_GENERAL_FAILURE: return EACCES;
801 case VERR_INVALID_PARAMETER: return EINVAL;
802 case VERR_INVALID_MAGIC: return EILSEQ;
803 case VERR_INVALID_HANDLE: return ENXIO;
804 case VERR_INVALID_POINTER: return EFAULT;
805 case VERR_LOCK_FAILED: return ENOLCK;
806 case VERR_ALREADY_LOADED: return EEXIST;
807 case VERR_PERMISSION_DENIED: return EPERM;
808 case VERR_VERSION_MISMATCH: return ENOSYS;
809 }
810
811 return EPERM;
812}
813
814
815/** @todo move this to assembly where a simple "jmp printf" will to the trick. */
816RTDECL(int) SUPR0Printf(const char *pszFormat, ...)
817{
818 va_list args;
819 char szMsg[512];
820
821 va_start(args, pszFormat);
822 vsnprintf(szMsg, sizeof(szMsg) - 1, pszFormat, args);
823 va_end(args);
824
825 szMsg[sizeof(szMsg) - 1] = '\0';
826 printf("%s", szMsg);
827 return 0;
828}
829
830
831/*
832 *
833 * org_virtualbox_SupDrv
834 *
835 */
836
837
838/**
839 * Initialize the object.
840 */
841bool org_virtualbox_SupDrv::init(OSDictionary *pDictionary)
842{
843 LogFlow(("org_virtualbox_SupDrv::init([%p], %p)\n", this, pDictionary));
844 if (IOService::init(pDictionary))
845 {
846 /* init members. */
847 return true;
848 }
849 return false;
850}
851
852
853/**
854 * Free the object.
855 */
856void org_virtualbox_SupDrv::free(void)
857{
858 LogFlow(("IOService::free([%p])\n", this));
859 IOService::free();
860}
861
862
863/**
864 * Check if it's ok to start this service.
865 * It's always ok by us, so it's up to IOService to decide really.
866 */
867IOService *org_virtualbox_SupDrv::probe(IOService *pProvider, SInt32 *pi32Score)
868{
869 LogFlow(("org_virtualbox_SupDrv::probe([%p])\n", this));
870 return IOService::probe(pProvider, pi32Score);
871}
872
873
874/**
875 * Start this service.
876 */
877bool org_virtualbox_SupDrv::start(IOService *pProvider)
878{
879 LogFlow(("org_virtualbox_SupDrv::start([%p])\n", this));
880
881 if (IOService::start(pProvider))
882 {
883 /* register the service. */
884 registerService();
885 return true;
886 }
887 return false;
888}
889
890
891/**
892 * Stop this service.
893 */
894void org_virtualbox_SupDrv::stop(IOService *pProvider)
895{
896 LogFlow(("org_virtualbox_SupDrv::stop([%p], %p)\n", this, pProvider));
897 IOService::stop(pProvider);
898}
899
900
901/**
902 * Termination request.
903 *
904 * @return true if we're ok with shutting down now, false if we're not.
905 * @param fOptions Flags.
906 */
907bool org_virtualbox_SupDrv::terminate(IOOptionBits fOptions)
908{
909 bool fRc;
910 LogFlow(("org_virtualbox_SupDrv::terminate: reference_count=%d g_cSessions=%d (fOptions=%#x)\n",
911 KMOD_INFO_NAME.reference_count, ASMAtomicUoReadS32(&g_cSessions), fOptions));
912 if ( KMOD_INFO_NAME.reference_count != 0
913 || ASMAtomicUoReadS32(&g_cSessions))
914 fRc = false;
915 else
916 fRc = IOService::terminate(fOptions);
917 LogFlow(("org_virtualbox_SupDrv::terminate: returns %d\n", fRc));
918 return fRc;
919}
920
921
922/*
923 *
924 * org_virtualbox_SupDrvClient
925 *
926 */
927
928
929/**
930 * Initializer called when the client opens the service.
931 */
932bool org_virtualbox_SupDrvClient::initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type)
933{
934 LogFlow(("org_virtualbox_SupDrvClient::initWithTask([%p], %#x, %p, %#x) (cur pid=%d proc=%p)\n",
935 this, OwningTask, pvSecurityId, u32Type, RTProcSelf(), RTR0ProcHandleSelf()));
936 AssertMsg((RTR0PROCESS)OwningTask == RTR0ProcHandleSelf(), ("%p %p\n", OwningTask, RTR0ProcHandleSelf()));
937
938 if (!OwningTask)
939 return false;
940 if (IOUserClient::initWithTask(OwningTask, pvSecurityId , u32Type))
941 {
942 m_Task = OwningTask;
943 m_pSession = NULL;
944 m_pProvider = NULL;
945 return true;
946 }
947 return false;
948}
949
950
951/**
952 * Start the client service.
953 */
954bool org_virtualbox_SupDrvClient::start(IOService *pProvider)
955{
956 LogFlow(("org_virtualbox_SupDrvClient::start([%p], %p) (cur pid=%d proc=%p)\n",
957 this, pProvider, RTProcSelf(), RTR0ProcHandleSelf() ));
958 AssertMsgReturn((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(),
959 ("%p %p\n", m_Task, RTR0ProcHandleSelf()),
960 false);
961
962 if (IOUserClient::start(pProvider))
963 {
964 m_pProvider = OSDynamicCast(org_virtualbox_SupDrv, pProvider);
965 if (m_pProvider)
966 {
967 Assert(!m_pSession);
968
969 /*
970 * Create a new session.
971 */
972 int rc = supdrvCreateSession(&g_DevExt, true /* fUser */, &m_pSession);
973 if (RT_SUCCESS(rc))
974 {
975 m_pSession->fOpened = false;
976 /* The Uid and Gid fields are set on open. */
977
978 /*
979 * Insert it into the hash table, checking that there isn't
980 * already one for this process first.
981 */
982 unsigned iHash = SESSION_HASH(m_pSession->Process);
983 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
984 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
985
986 PSUPDRVSESSION pCur = g_apSessionHashTab[iHash];
987 if (pCur && pCur->Process != m_pSession->Process)
988 {
989 do pCur = pCur->pNextHash;
990 while (pCur && pCur->Process != m_pSession->Process);
991 }
992 if (!pCur)
993 {
994 m_pSession->pNextHash = g_apSessionHashTab[iHash];
995 g_apSessionHashTab[iHash] = m_pSession;
996 m_pSession->pvSupDrvClient = this;
997 ASMAtomicIncS32(&g_cSessions);
998 rc = VINF_SUCCESS;
999 }
1000 else
1001 rc = VERR_ALREADY_LOADED;
1002
1003 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
1004 if (RT_SUCCESS(rc))
1005 {
1006 Log(("org_virtualbox_SupDrvClient::start: created session %p for pid %d\n", m_pSession, (int)RTProcSelf()));
1007 return true;
1008 }
1009
1010 LogFlow(("org_virtualbox_SupDrvClient::start: already got a session for this process (%p)\n", pCur));
1011 supdrvCloseSession(&g_DevExt, m_pSession);
1012 }
1013
1014 m_pSession = NULL;
1015 LogFlow(("org_virtualbox_SupDrvClient::start: rc=%Rrc from supdrvCreateSession\n", rc));
1016 }
1017 else
1018 LogFlow(("org_virtualbox_SupDrvClient::start: %p isn't org_virtualbox_SupDrv\n", pProvider));
1019 }
1020 return false;
1021}
1022
1023
1024/**
1025 * Common worker for clientClose and VBoxDrvDarwinClose.
1026 *
1027 * It will
1028 */
1029/* static */ void org_virtualbox_SupDrvClient::sessionClose(RTPROCESS Process)
1030{
1031 /*
1032 * Look for the session.
1033 */
1034 const unsigned iHash = SESSION_HASH(Process);
1035 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1036 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
1037 PSUPDRVSESSION pSession = g_apSessionHashTab[iHash];
1038 if (pSession)
1039 {
1040 if (pSession->Process == Process)
1041 {
1042 g_apSessionHashTab[iHash] = pSession->pNextHash;
1043 pSession->pNextHash = NULL;
1044 ASMAtomicDecS32(&g_cSessions);
1045 }
1046 else
1047 {
1048 PSUPDRVSESSION pPrev = pSession;
1049 pSession = pSession->pNextHash;
1050 while (pSession)
1051 {
1052 if (pSession->Process == Process)
1053 {
1054 pPrev->pNextHash = pSession->pNextHash;
1055 pSession->pNextHash = NULL;
1056 ASMAtomicDecS32(&g_cSessions);
1057 break;
1058 }
1059
1060 /* next */
1061 pPrev = pSession;
1062 pSession = pSession->pNextHash;
1063 }
1064 }
1065 }
1066 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
1067 if (!pSession)
1068 {
1069 Log(("SupDrvClient::sessionClose: pSession == NULL, pid=%d; freed already?\n", (int)Process));
1070 return;
1071 }
1072
1073 /*
1074 * Remove it from the client object.
1075 */
1076 org_virtualbox_SupDrvClient *pThis = (org_virtualbox_SupDrvClient *)pSession->pvSupDrvClient;
1077 pSession->pvSupDrvClient = NULL;
1078 if (pThis)
1079 {
1080 Assert(pThis->m_pSession == pSession);
1081 pThis->m_pSession = NULL;
1082 }
1083
1084 /*
1085 * Close the session.
1086 */
1087 supdrvCloseSession(&g_DevExt, pSession);
1088}
1089
1090
1091/**
1092 * Client exits normally.
1093 */
1094IOReturn org_virtualbox_SupDrvClient::clientClose(void)
1095{
1096 LogFlow(("org_virtualbox_SupDrvClient::clientClose([%p]) (cur pid=%d proc=%p)\n", this, RTProcSelf(), RTR0ProcHandleSelf()));
1097 AssertMsg((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(), ("%p %p\n", m_Task, RTR0ProcHandleSelf()));
1098
1099 /*
1100 * Clean up the session if it's still around.
1101 *
1102 * We cannot rely 100% on close, and in the case of a dead client
1103 * we'll end up hanging inside vm_map_remove() if we postpone it.
1104 */
1105 if (m_pSession)
1106 {
1107 sessionClose(RTProcSelf());
1108 Assert(!m_pSession);
1109 }
1110
1111 m_pProvider = NULL;
1112 terminate();
1113
1114 return kIOReturnSuccess;
1115}
1116
1117
1118/**
1119 * The client exits abnormally / forgets to do cleanups. (logging)
1120 */
1121IOReturn org_virtualbox_SupDrvClient::clientDied(void)
1122{
1123 LogFlow(("org_virtualbox_SupDrvClient::clientDied([%p]) m_Task=%p R0Process=%p Process=%d\n",
1124 this, m_Task, RTR0ProcHandleSelf(), RTProcSelf()));
1125
1126 /* IOUserClient::clientDied() calls clientClose, so we'll just do the work there. */
1127 return IOUserClient::clientDied();
1128}
1129
1130
1131/**
1132 * Terminate the service (initiate the destruction). (logging)
1133 */
1134bool org_virtualbox_SupDrvClient::terminate(IOOptionBits fOptions)
1135{
1136 LogFlow(("org_virtualbox_SupDrvClient::terminate([%p], %#x)\n", this, fOptions));
1137 return IOUserClient::terminate(fOptions);
1138}
1139
1140
1141/**
1142 * The final stage of the client service destruction. (logging)
1143 */
1144bool org_virtualbox_SupDrvClient::finalize(IOOptionBits fOptions)
1145{
1146 LogFlow(("org_virtualbox_SupDrvClient::finalize([%p], %#x)\n", this, fOptions));
1147 return IOUserClient::finalize(fOptions);
1148}
1149
1150
1151/**
1152 * Stop the client service. (logging)
1153 */
1154void org_virtualbox_SupDrvClient::stop(IOService *pProvider)
1155{
1156 LogFlow(("org_virtualbox_SupDrvClient::stop([%p])\n", this));
1157 IOUserClient::stop(pProvider);
1158}
1159
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