VirtualBox

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

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

SUPDrv-darwin.cpp: replaced proc_ucred with kauth_cred_proc_ref/kauth_cred_unref. (the former isn't available on darwin/AMD64.)

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