VirtualBox

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

Last change on this file since 41216 was 41091, checked in by vboxsync, 13 years ago

SUPDrv-win.cpp: how to write to the NVRAM...

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