VirtualBox

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

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

SUPDrv: Sketched out support for native module loading. (OS parts are all stubs and the IOC interface changes disabled.)

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette