VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.c@ 1

Last change on this file since 1 was 1, checked in by vboxsync, 55 years ago

import

File size: 16.4 KB
Line 
1/** @file
2 *
3 * VBox host drivers - Ring-0 support drivers - Darwin host:
4 * Darwin driver C code
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27/* deal with conflicts first, we ignore any Darwin variants of ALIGN, MAX, MIN, and PVM. */
28#include <sys/param.h>
29#undef ALIGN
30#undef MAX
31#undef MIN
32#undef PVM
33
34#include "SUPDRV.h"
35#include <iprt/types.h>
36#include <iprt/assert.h>
37#include <iprt/spinlock.h>
38#include <iprt/semaphore.h>
39
40#include <mach/kmod.h>
41#include <miscfs/devfs/devfs.h>
42#include <sys/conf.h>
43#include <sys/errno.h>
44#include <sys/ioccom.h>
45#include <sys/malloc.h>
46#include <sys/proc.h>
47
48
49/*******************************************************************************
50* Defined Constants And Macros *
51*******************************************************************************/
52
53/** The module name. */
54#define DEVICE_NAME "vboxdrv"
55
56
57
58/*******************************************************************************
59* Internal Functions *
60*******************************************************************************/
61static kern_return_t VBoxSupDrvStart(struct kmod_info *pKModInfo, void *pvData);
62static kern_return_t VBoxSupDrvStop(struct kmod_info *pKModInfo, void *pvData);
63
64static int VBoxSupDrvOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
65static int VBoxSupDrvClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
66static int VBoxSupDrvIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess);
67static int VBoxSupDrvIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess);
68
69static int VBoxSupDrvErr2DarwinErr(int rc);
70
71
72/*******************************************************************************
73* Global Variables *
74*******************************************************************************/
75/**
76 * Declare the module stuff.
77 */
78KMOD_EXPLICIT_DECL(vboxdrv, "1.0", VBoxSupDrvStart, VBoxSupDrvStop)
79
80/**
81 * Device extention & session data association structure.
82 */
83static SUPDRVDEVEXT g_DevExt;
84
85/**
86 * The character device switch table for the driver.
87 */
88static struct cdevsw g_DevCW =
89{
90 .d_open = VBoxSupDrvOpen,
91 .d_close = VBoxSupDrvClose,
92 .d_read = eno_rdwrt,
93 .d_write = eno_rdwrt,
94 .d_ioctl = VBoxSupDrvIOCtl,
95 .d_stop = eno_stop,
96 .d_reset = eno_reset,
97 .d_ttys = NULL,
98 .d_select = eno_select,
99 .d_mmap = eno_mmap,
100 .d_strategy = eno_strat,
101 .d_getc = eno_getc,
102 .d_putc = eno_putc,
103 .d_type = 0
104};
105
106/** Major device number. */
107static int g_iMajorDeviceNo = -1;
108/** Registered devfs device handle. */
109static void *g_hDevFsDevice = NULL;
110
111/** Spinlock protecting g_apSessionHashTab. */
112static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
113/** Hash table */
114static PSUPDRVSESSION g_apSessionHashTab[19];
115/** Calculates the index into g_apSessionHashTab.*/
116#define SESSION_HASH(pid) ((pid) % RT_ELEMENTS(g_apSessionHashTab))
117
118
119/**
120 * Start the kernel module.
121 */
122static kern_return_t VBoxSupDrvStart(struct kmod_info *pKModInfo, void *pvData)
123{
124 int rc;
125 dprintf(("VBoxSupDrvStart\n"));
126
127 /*
128 * Initialize the device extension.
129 */
130 rc = supdrvInitDevExt(&g_DevExt);
131 if (RT_SUCCESS(rc))
132 {
133 /*
134 * Initialize the session hash table.
135 */
136 memset(g_apSessionHashTab, 0, sizeof(g_apSessionHashTab)); /* paranoia */
137 rc = RTSpinlockCreate(&g_Spinlock);
138 if (RT_SUCCESS(rc))
139 {
140 /*
141 * Registering ourselves as a character device.
142 */
143 g_iMajorDeviceNo = cdevsw_add(-1, &g_DevCW);
144 if (g_iMajorDeviceNo >= 0)
145 {
146 g_hDevFsDevice = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR,
147 UID_ROOT, GID_WHEEL, 0660, DEVICE_NAME); /** @todo the UID and GID should be configurable! */
148 if (g_hDevFsDevice)
149 {
150 OSDBGPRINT(("VBoxDrv: Successfully started. (major=%d)\n", g_iMajorDeviceNo));
151 return KMOD_RETURN_SUCCESS;
152 }
153 OSDBGPRINT(("VBoxDrv: devfs_make_node(makedev(%d,0),,,,%s) failed\n",
154 g_iMajorDeviceNo, DEVICE_NAME));
155 cdevsw_remove(g_iMajorDeviceNo, &g_DevCW);
156 g_iMajorDeviceNo = -1;
157 }
158 else
159 OSDBGPRINT(("VBoxDrv: cdevsw_add failed (%d)\n", g_iMajorDeviceNo));
160 RTSpinlockDestroy(g_Spinlock);
161 g_Spinlock = NIL_RTSPINLOCK;
162 }
163 else
164 OSDBGPRINT(("VBoxDrv: RTSpinlockCreate failed (rc=%d)\n", rc));
165 supdrvDeleteDevExt(&g_DevExt);
166 }
167 else
168 OSDBGPRINT(("VBoxDrv: failed to initialize device extension (rc=%d)\n", rc));
169
170 memset(&g_DevExt, 0, sizeof(g_DevExt));
171 return KMOD_RETURN_FAILURE;
172}
173
174
175/**
176 * Stop the kernel module.
177 */
178static kern_return_t VBoxSupDrvStop(struct kmod_info *pKModInfo, void *pvData)
179{
180 int rc;
181 dprintf(("VBoxSupDrvStop\n"));
182
183 /** @todo I've got a nagging feeling that we'll have to keep track of users and refuse
184 * unloading if we're busy. Investigate and implement this! */
185
186 /*
187 * Undo the work done during start (in reverse order).
188 */
189 devfs_remove(g_hDevFsDevice);
190 g_hDevFsDevice = NULL;
191
192 rc = cdevsw_remove(g_iMajorDeviceNo, &g_DevCW);
193 Assert(rc == g_iMajorDeviceNo);
194 g_iMajorDeviceNo = -1;
195
196 rc = supdrvDeleteDevExt(&g_DevExt);
197 AssertRC(rc);
198
199 rc = RTSpinlockDestroy(g_Spinlock);
200 AssertRC(rc);
201 g_Spinlock = NIL_RTSPINLOCK;
202
203 memset(&g_DevExt, 0, sizeof(g_DevExt));
204 return KMOD_RETURN_SUCCESS;
205}
206
207
208/**
209 * Device open. Called on open /dev/vboxdrv
210 *
211 * @param pInode Pointer to inode info structure.
212 * @param pFilp Associated file pointer.
213 */
214static int VBoxSupDrvOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
215{
216 int rc;
217 PSUPDRVSESSION pSession;
218 dprintf(("VBoxSupDrvOpen:\n"));
219
220 /*
221 * Create a new session.
222 */
223 rc = supdrvCreateSession(&g_DevExt, &pSession);
224 if (RT_SUCCESS(rc))
225 {
226 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
227 unsigned iHash;
228 struct ucred *pCred = proc_ucred(pProcess);
229 if (pCred)
230 {
231 pSession->Uid = pCred->cr_uid;
232 pSession->Gid = pCred->cr_gid;
233 }
234 pSession->Process = proc_pid(pProcess);
235
236 /*
237 * Insert it into the hash table.
238 */
239 iHash = SESSION_HASH(pSession->Process);
240 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
241 pSession->pNextHash = g_apSessionHashTab[iHash];
242 g_apSessionHashTab[iHash] = pSession;
243 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
244 }
245
246 dprintf(("VBoxSupDrvOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, proc_pid(pProcess)));
247
248 return VBoxSupDrvErr2DarwinErr(rc);
249}
250
251
252/**
253 * Close device.
254 *
255 */
256static int VBoxSupDrvClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
257{
258 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
259 const RTPROCESS Process = proc_pid(pProcess);
260 const unsigned iHash = SESSION_HASH(Process);
261 PSUPDRVSESSION pSession;
262
263 dprintf(("VBoxSupDrvClose: pid=%d\n", (int)Process));
264
265 /*
266 * Remove from the hash table.
267 */
268 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
269 pSession = g_apSessionHashTab[iHash];
270 if (pSession)
271 {
272 if (pSession->Process == Process)
273 {
274 g_apSessionHashTab[iHash] = pSession->pNextHash;
275 pSession->pNextHash = NULL;
276 }
277 else
278 {
279 PSUPDRVSESSION pPrev = pSession;
280 pSession = pSession->pNextHash;
281 while (pSession)
282 {
283 if (pSession->Process == Process)
284 {
285 pPrev->pNextHash = pSession->pNextHash;
286 pSession->pNextHash = NULL;
287 break;
288 }
289
290 /* next */
291 pPrev = pSession;
292 pSession = pSession->pNextHash;
293 }
294 }
295 }
296 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
297 if (!pSession)
298 {
299 OSDBGPRINT(("VBoxSupDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d\n", (int)Process));
300 return EINVAL;
301 }
302
303 /*
304 * Close the session.
305 */
306 supdrvCloseSession(&g_DevExt, pSession);
307 return 0;
308}
309
310
311/**
312 * Device I/O Control entry point.
313 *
314 * @returns Darwin for slow IOCtls and VBox status code for the fast ones.
315 * @param Dev The device number (major+minor).
316 * @param iCmd The IOCtl command.
317 * @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)).
318 * @param fFlags Flag saying we're a character device (like we didn't know already).
319 * @param pProcess The process issuing this request.
320 */
321static int VBoxSupDrvIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess)
322{
323 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
324 const RTPROCESS Process = proc_pid(pProcess);
325 const unsigned iHash = SESSION_HASH(Process);
326 PSUPDRVSESSION pSession;
327
328 /*
329 * Find the session.
330 */
331 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
332 pSession = g_apSessionHashTab[iHash];
333 if (pSession && pSession->Process != Process)
334 {
335 do pSession = pSession->pNextHash;
336 while (pSession && pSession->Process != Process);
337 }
338 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
339 if (!pSession)
340 {
341 OSDBGPRINT(("VBoxSupDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d\n", (int)Process));
342 return EINVAL;
343 }
344
345 /*
346 * Deal with the two high-speed IOCtl that takes it's arguments from
347 * the session and iCmd, and only returns a VBox status code.
348 */
349 if ( iCmd == 1
350 || iCmd == 1)
351 return supdrvIOCtlFast(iCmd, &g_DevExt, pSession);
352 return VBoxSupDrvIOCtlSlow(pSession, iCmd, pData, pProcess);
353}
354
355
356/**
357 * Worker for VBoxSupDrvIOCtl that takes the slow IOCtl functions.
358 *
359 * @returns Darwin errno.
360 *
361 * @param pSession The session.
362 * @param iCmd The IOCtl command.
363 * @param pData Pointer to the kernel copy of the SUPDRVIOCTLDATA buffer.
364 * @param pProcess The calling process.
365 */
366static int VBoxSupDrvIOCtlSlow(PSUPDRVSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess)
367{
368 int rc;
369 void *pvBuf = NULL;
370 int cbBuf = 0;
371 unsigned cbOut = 0;
372 PSUPDRVIOCTLDATA pArgs = (PSUPDRVIOCTLDATA)pData;
373 dprintf(("VBoxSupDrvIOCtl: pSession=%p iCmd=%p pData=%p pProcess=%p\n", pSession, iCmd, pData, pProcess));
374
375 /*
376 * Copy ioctl data structure from user space.
377 */
378 if (IOCPARM_LEN(iCmd) != sizeof(SUPDRVIOCTLDATA))
379 {
380 dprintf(("VBoxSupDrvIOCtl: incorrect input length! cbArgs=%d\n", IOCPARM_LEN(iCmd)));
381 return EINVAL;
382 }
383
384 /*
385 * Allocate and copy user space input data buffer to kernel space.
386 */
387 if (pArgs->cbIn > 0 || pArgs->cbOut > 0)
388 {
389 cbBuf = max(pArgs->cbIn, pArgs->cbOut);
390 MALLOC(pvBuf, void *, cbBuf, M_TEMP, M_WAITOK);
391 if (pvBuf == NULL)
392 {
393 dprintf(("VBoxSupDrvIOCtl: failed to allocate buffer of %d bytes.\n", cbBuf));
394 return ENOMEM;
395 }
396 rc = copyin((const user_addr_t)pArgs->pvIn, pvBuf, pArgs->cbIn);
397 if (rc)
398 {
399 dprintf(("VBoxSupDrvIOCtl: copyin(%p,,%d) failed.\n", pArgs->pvIn, cbBuf));
400 FREE(pvBuf, M_TEMP);
401 return rc;
402 }
403 }
404
405 /*
406 * Process the IOCtl.
407 */
408 rc = supdrvIOCtl(iCmd, &g_DevExt, pSession,
409 pvBuf, pArgs->cbIn, pvBuf, pArgs->cbOut, &cbOut);
410
411 /*
412 * Copy ioctl data and output buffer back to user space.
413 */
414 if (rc)
415 {
416 dprintf(("VBoxSupDrvIOCtl: pid=%d iCmd=%x pData=%p failed, rc=%d (darwin rc=%d)\n",
417 proc_pid(pProcess), iCmd, (void *)pData, rc, VBoxSupDrvErr2DarwinErr(rc)));
418 rc = VBoxSupDrvErr2DarwinErr(rc);
419 }
420 else if (cbOut > 0)
421 {
422 if (pvBuf != NULL && cbOut <= cbBuf)
423 {
424 int rc2 = copyout(pvBuf, (user_addr_t)pArgs->pvOut, cbOut);
425 if (rc2)
426 {
427 dprintf(("VBoxSupDrvIOCtl: copyout(,%p,%d) failed.\n", pArgs->pvOut, cbBuf));
428 rc = rc2;
429 }
430 }
431 else
432 {
433 dprintf(("WHAT!?! supdrvIOCtl messed up! cbOut=%d cbBuf=%d pvBuf=%p\n", cbOut, cbBuf, pvBuf));
434 rc = EPERM;
435 }
436 }
437
438 if (pvBuf)
439 FREE(pvBuf, M_TEMP);
440
441 dprintf2(("VBoxSupDrvIOCtl: returns %d\n", rc));
442 return rc;
443}
444
445
446
447/**
448 * Initializes any OS specific object creator fields.
449 */
450void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
451{
452 NOREF(pObj);
453 NOREF(pSession);
454}
455
456
457/**
458 * Checks if the session can access the object.
459 *
460 * @returns true if a decision has been made.
461 * @returns false if the default access policy should be applied.
462 *
463 * @param pObj The object in question.
464 * @param pSession The session wanting to access the object.
465 * @param pszObjName The object name, can be NULL.
466 * @param prc Where to store the result when returning true.
467 */
468bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
469{
470 NOREF(pObj);
471 NOREF(pSession);
472 NOREF(pszObjName);
473 NOREF(prc);
474 return false;
475}
476
477
478/**
479 * Converts a supdrv error code to a darwin error code.
480 *
481 * @returns corresponding darwin error code.
482 * @param rc supdrv error code (SUPDRV_ERR_* defines).
483 */
484static int VBoxSupDrvErr2DarwinErr(int rc)
485{
486 switch (rc)
487 {
488 case 0: return 0;
489 case SUPDRV_ERR_GENERAL_FAILURE: return EACCES;
490 case SUPDRV_ERR_INVALID_PARAM: return EINVAL;
491 case SUPDRV_ERR_INVALID_MAGIC: return EILSEQ;
492 case SUPDRV_ERR_INVALID_HANDLE: return ENXIO;
493 case SUPDRV_ERR_INVALID_POINTER: return EFAULT;
494 case SUPDRV_ERR_LOCK_FAILED: return ENOLCK;
495 case SUPDRV_ERR_ALREADY_LOADED: return EEXIST;
496 case SUPDRV_ERR_PERMISSION_DENIED: return EPERM;
497 case SUPDRV_ERR_VERSION_MISMATCH: return ENOSYS;
498 }
499
500 return EPERM;
501}
502
503
504/** @todo move this to assembly where a simple "jmp printf" will to the trick. */
505RTDECL(int) SUPR0Printf(const char *pszFormat, ...)
506{
507 va_list args;
508 char szMsg[512];
509
510 va_start(args, pszFormat);
511 vsnprintf(szMsg, sizeof(szMsg) - 1, pszFormat, args);
512 va_end(args);
513
514 szMsg[sizeof(szMsg) - 1] = '\0';
515 printf("%s", szMsg);
516 return 0;
517}
518
519
520/** Runtime assert implementation for Linux Ring-0. */
521RTDECL(void) AssertMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
522{
523 printf("!!Assertion Failed!!\n"
524 "Expression: %s\n"
525 "Location : %s(%d) %s\n",
526 pszExpr, pszFile, uLine, pszFunction);
527}
528
529
530
531/** Runtime assert implementation for the Darwin Ring-0 driver.
532 * @todo this one needs fixing! */
533RTDECL(void) AssertMsg2(const char *pszFormat, ...)
534{ /* forwarder. */
535 va_list ap;
536 char msg[256];
537
538 va_start(ap, pszFormat);
539 vsnprintf(msg, sizeof(msg) - 1, pszFormat, ap);
540 msg[sizeof(msg) - 1] = '\0';
541 printf("%s", msg);
542 va_end(ap);
543}
544
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