VirtualBox

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

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

Automated rebranding to Oracle copyright/license strings via filemuncher

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