VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/solaris/SUPDrv-solaris.c@ 4550

Last change on this file since 4550 was 4550, checked in by vboxsync, 17 years ago

another place 'u' needs to be undefined.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 19.6 KB
Line 
1/** @file
2 * VBox host drivers - Ring-0 support drivers - Solaris host:
3 * Solaris driver C code
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <sys/types.h>
23#include <sys/param.h>
24#include <sys/errno.h>
25#include <sys/uio.h>
26#include <sys/buf.h>
27#include <sys/modctl.h>
28#include <sys/open.h>
29#include <sys/conf.h>
30#include <sys/cmn_err.h>
31#include <sys/stat.h>
32#include <sys/ddi.h>
33#include <sys/sunddi.h>
34#include <sys/file.h>
35#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
36
37#include "SUPDRV.h"
38#include <iprt/spinlock.h>
39#include <iprt/process.h>
40#include <iprt/initterm.h>
41#include <iprt/alloc.h>
42
43
44/*******************************************************************************
45* Defined Constants And Macros *
46*******************************************************************************/
47/** The module name. */
48#define DEVICE_NAME "vboxdrv"
49#define DEVICE_DESC "VirtualBox Driver"
50#define DEVICE_MAXINSTANCES 16
51
52
53/*******************************************************************************
54* Internal Functions *
55*******************************************************************************/
56static int VBoxDrvSolarisOpen(dev_t* pDev, int fFlag, int fType, cred_t* pCred);
57static int VBoxDrvSolarisClose(dev_t Dev, int fFlag, int fType, cred_t* pCred);
58static int VBoxDrvSolarisRead(dev_t Dev, struct uio* pUio, cred_t* pCred);
59static int VBoxDrvSolarisWrite(dev_t Dev, struct uio* pUio, cred_t* pCred);
60static int VBoxDrvSolarisIOCtl (dev_t Dev, int Cmd, intptr_t pArgs, int mode, cred_t* pCred, int* pVal);
61
62static int VBoxDrvSolarisAttach(dev_info_t* pDip, ddi_attach_cmd_t Cmd);
63static int VBoxDrvSolarisDetach(dev_info_t* pDip, ddi_detach_cmd_t Cmd);
64
65static int VBoxSupDrvErr2SolarisErr(int rc);
66static int VBoxDrvSolarisIOCtlSlow(PSUPDRVSESSION pSession, int Cmd, int Mode, intptr_t pArgs);
67
68
69/*******************************************************************************
70* Global Variables *
71*******************************************************************************/
72/**
73 * cb_ops: for drivers that support char/block entry points
74 */
75static struct cb_ops g_VBoxDrvSolarisCbOps =
76{
77 VBoxDrvSolarisOpen,
78 VBoxDrvSolarisClose,
79 nodev, /* b strategy */
80 nodev, /* b dump */
81 nodev, /* b print */
82 VBoxDrvSolarisRead,
83 VBoxDrvSolarisWrite,
84 VBoxDrvSolarisIOCtl,
85 nodev, /* c devmap */
86 nodev, /* c mmap */
87 nodev, /* c segmap */
88 nochpoll, /* c poll */
89 ddi_prop_op, /* property ops */
90 NULL, /* streamtab */
91 D_NEW | D_MP, /* compat. flag */
92 CB_REV /* revision */
93};
94
95/**
96 * dev_ops: for driver device operations
97 */
98static struct dev_ops g_VBoxDrvSolarisDevOps =
99{
100 DEVO_REV, /* driver build revision */
101 0, /* ref count */
102 nulldev, /* get info */
103 nulldev, /* identify */
104 nulldev, /* probe */
105 VBoxDrvSolarisAttach,
106 VBoxDrvSolarisDetach,
107 nodev, /* reset */
108 &g_VBoxDrvSolarisCbOps,
109 (struct bus_ops *)0,
110 nodev /* power */
111};
112
113/**
114 * modldrv: export driver specifics to the kernel
115 */
116static struct modldrv g_VBoxDrvSolarisModule =
117{
118 &mod_driverops, /* extern from kernel */
119 DEVICE_DESC,
120 &g_VBoxDrvSolarisDevOps
121};
122
123/**
124 * modlinkage: export install/remove/info to the kernel
125 */
126static struct modlinkage g_VBoxDrvSolarisModLinkage =
127{
128 MODREV_1, /* loadable module system revision */
129 &g_VBoxDrvSolarisModule,
130 NULL /* terminate array of linkage structures */
131};
132
133/** State info. each our kernel module instances */
134typedef struct
135{
136 dev_info_t* pDip; /* Device handle */
137} vbox_devstate_t;
138
139/** Opaque pointer to state */
140static void* g_pVBoxDrvSolarisState;
141
142/** Device extention & session data association structure */
143static SUPDRVDEVEXT g_DevExt;
144
145/* GCC C++ hack. */
146unsigned __gxx_personality_v0 = 0xcccccccc;
147
148/** Hash table */
149static PSUPDRVSESSION g_apSessionHashTab[19];
150/** Spinlock protecting g_apSessionHashTab. */
151static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
152/** Calculates bucket index into g_apSessionHashTab.*/
153#define SESSION_HASH(sfn) ((sfn) % RT_ELEMENTS(g_apSessionHashTab))
154
155/**
156 * Kernel entry points
157 */
158int _init (void)
159{
160 cmn_err(CE_CONT, "VBoxDrvSolaris _init");
161
162 int e = ddi_soft_state_init(&g_pVBoxDrvSolarisState, sizeof (vbox_devstate_t), 1);
163 if (e != 0)
164 return e;
165
166 e = mod_install(&g_VBoxDrvSolarisModLinkage);
167 if (e != 0)
168 ddi_soft_state_fini(&g_pVBoxDrvSolarisState);
169
170 return e;
171}
172
173int _fini (void)
174{
175 cmn_err(CE_CONT, "VBoxDrvSolaris _fini");
176
177 int e = mod_remove(&g_VBoxDrvSolarisModLinkage);
178 if (e != 0)
179 return e;
180
181 ddi_soft_state_fini(&g_pVBoxDrvSolarisState);
182 return e;
183}
184
185int _info (struct modinfo *pModInfo)
186{
187 cmn_err(CE_CONT, "VBoxDrvSolaris _info");
188 return mod_info (&g_VBoxDrvSolarisModLinkage, pModInfo);
189}
190
191/**
192 * User context entry points
193 */
194static int VBoxDrvSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred)
195{
196 cmn_err(CE_CONT, "VBoxDrvSolarisOpen");
197
198 int rc;
199 PSUPDRVSESSION pSession;
200
201 /*
202 * Create a new session.
203 * Sessions in Solaris driver are mostly useless. It's however needed
204 * in VBoxDrvSolarisIOCtlSlow() while calling supdrvIOCtl()
205 */
206 rc = supdrvCreateSession(&g_DevExt, &pSession);
207 if (RT_SUCCESS(rc))
208 {
209 cmn_err(CE_NOTE,"supdrvCreateSession success");
210 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
211 unsigned iHash;
212
213 pSession->Uid = crgetuid(pCred);
214 pSession->Gid = crgetgid(pCred);
215 pSession->Process = RTProcSelf();
216 pSession->R0Process = RTR0ProcHandleSelf();
217
218 /*
219 * Insert it into the hash table.
220 */
221 iHash = SESSION_HASH(pSession->Process);
222 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
223 pSession->pNextHash = g_apSessionHashTab[iHash];
224 g_apSessionHashTab[iHash] = pSession;
225 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
226 cmn_err(CE_NOTE,"VBoxDrvSolarisOpen success");
227 }
228
229 int instance;
230 for (instance = 0; instance < DEVICE_MAXINSTANCES; instance++)
231 {
232 vbox_devstate_t *pState = ddi_get_soft_state (g_pVBoxDrvSolarisState, instance);
233 if (pState)
234 break;
235 }
236
237 if (instance >= DEVICE_MAXINSTANCES)
238 {
239 cmn_err(CE_NOTE, "All instances exhausted\n");
240 return ENXIO;
241 }
242
243 *pDev = makedevice(getmajor(*pDev), instance);
244
245 return VBoxSupDrvErr2SolarisErr(rc);
246}
247
248static int VBoxDrvSolarisClose(dev_t pDev, int flag, int otyp, cred_t *cred)
249{
250 cmn_err(CE_CONT, "VBoxDrvSolarisClose");
251
252 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
253 const RTPROCESS Process = RTProcSelf();
254 const unsigned iHash = SESSION_HASH(Process);
255 PSUPDRVSESSION pSession;
256
257 /*
258 * Remove from the hash table.
259 */
260 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
261 pSession = g_apSessionHashTab[iHash];
262 if (pSession)
263 {
264 if (pSession->Process == Process)
265 {
266 g_apSessionHashTab[iHash] = pSession->pNextHash;
267 pSession->pNextHash = NULL;
268 }
269 else
270 {
271 PSUPDRVSESSION pPrev = pSession;
272 pSession = pSession->pNextHash;
273 while (pSession)
274 {
275 if (pSession->Process == Process)
276 {
277 pPrev->pNextHash = pSession->pNextHash;
278 pSession->pNextHash = NULL;
279 break;
280 }
281
282 /* next */
283 pPrev = pSession;
284 pSession = pSession->pNextHash;
285 }
286 }
287 }
288 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
289 if (!pSession)
290 {
291 OSDBGPRINT(("VBoxDrvSolarisClose: WHAT?!? pSession == NULL! This must be a mistake... pid=%d (close)\n",
292 (int)Process));
293 return DDI_FAILURE;
294 }
295
296 /*
297 * Close the session.
298 */
299 supdrvCloseSession(&g_DevExt, pSession);
300 return DDI_SUCCESS;
301}
302
303static int VBoxDrvSolarisRead(dev_t dev, struct uio* pUio, cred_t* credp)
304{
305 cmn_err(CE_CONT, "VBoxDrvSolarisRead");
306 return DDI_SUCCESS;
307}
308
309static int VBoxDrvSolarisWrite(dev_t dev, struct uio* pUio, cred_t* credp)
310{
311 cmn_err(CE_CONT, "VBoxDrvSolarisWrite");
312 return DDI_SUCCESS;
313}
314
315/**
316 * Attach entry point, to attach a device to the system or resume it.
317 *
318 * @param pDip The module structure instance.
319 * @param enmCmd Attach type (ddi_attach_cmd_t)
320 *
321 * @return corresponding solaris error code.
322 */
323static int VBoxDrvSolarisAttach(dev_info_t* pDip, ddi_attach_cmd_t enmCmd)
324{
325 cmn_err(CE_CONT, "VBoxDrvSolarisAttach");
326 int rc = VINF_SUCCESS;
327 int instance = 0;
328 vbox_devstate_t* pState;
329
330 switch (enmCmd)
331 {
332 case DDI_ATTACH:
333 {
334 instance = ddi_get_instance (pDip);
335
336 if (ddi_soft_state_zalloc(g_pVBoxDrvSolarisState, instance) != DDI_SUCCESS)
337 {
338 cmn_err(CE_NOTE, "VBoxDrvSolarisAttach: state alloc failed");
339 return DDI_FAILURE;
340 }
341
342 pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, instance);
343
344 /*
345 * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
346 */
347 rc = RTR0Init(0);
348 if (RT_SUCCESS(rc))
349 {
350 /*
351 * Initialize the device extension
352 */
353 rc = supdrvInitDevExt(&g_DevExt);
354 if (RT_SUCCESS(rc))
355 {
356 /*
357 * Initialize the session hash table.
358 */
359 memset(g_apSessionHashTab, 0, sizeof(g_apSessionHashTab));
360 rc = RTSpinlockCreate(&g_Spinlock);
361 if (RT_SUCCESS(rc))
362 {
363 /*
364 * Register ourselves as a character device, pseudo-driver
365 */
366 if (ddi_create_minor_node(pDip, "0", S_IFCHR,
367 instance, DDI_PSEUDO, 0) == DDI_SUCCESS)
368 {
369 pState->pDip = pDip;
370 ddi_report_dev(pDip);
371 return DDI_SUCCESS;
372 }
373
374 /* Is this really necessary? */
375 ddi_remove_minor_node(pDip, NULL);
376 cmn_err(CE_NOTE,"VBoxDrvSolarisAttach: ddi_create_minor_node failed.");
377
378 RTSpinlockDestroy(g_Spinlock);
379 g_Spinlock = NIL_RTSPINLOCK;
380 }
381 else
382 cmn_err(CE_NOTE, "VBoxDrvSolarisAttach: RTSpinlockCreate failed");
383 supdrvDeleteDevExt(&g_DevExt);
384 }
385 else
386 cmn_err(CE_NOTE, "VBoxDrvSolarisAttach: supdrvInitDevExt failed");
387 RTR0Term ();
388 }
389 else
390 cmn_err(CE_NOTE, "VBoxDrvSolarisAttach: failed to init R0Drv");
391 memset(&g_DevExt, 0, sizeof(g_DevExt));
392 break;
393 }
394
395 default:
396 return DDI_FAILURE;
397 }
398
399 return DDI_FAILURE;
400}
401
402/**
403 * Detach entry point, to detach a device to the system or suspend it.
404 *
405 * @param pDip The module structure instance.
406 * @param enmCmd Attach type (ddi_attach_cmd_t)
407 *
408 * @return corresponding solaris error code.
409 */
410static int VBoxDrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
411{
412 int rc = VINF_SUCCESS;
413 int instance;
414 register vbox_devstate_t* pState;
415
416 cmn_err(CE_CONT, "VBoxDrvSolarisDetach");
417 switch (enmCmd)
418 {
419 case DDI_DETACH:
420 {
421 instance = ddi_get_instance(pDip);
422 pState = ddi_get_soft_state(g_pVBoxDrvSolarisState, instance);
423
424 ddi_remove_minor_node(pDip, NULL);
425 ddi_soft_state_free(g_pVBoxDrvSolarisState, instance);
426
427 rc = supdrvDeleteDevExt(&g_DevExt);
428 AssertRC(rc);
429
430 rc = RTSpinlockDestroy(g_Spinlock);
431 AssertRC(rc);
432 g_Spinlock = NIL_RTSPINLOCK;
433
434 RTR0Term();
435
436 memset(&g_DevExt, 0, sizeof(g_DevExt));
437 cmn_err(CE_CONT, "VBoxDrvSolarisDetach: Clean Up Done.");
438 return DDI_SUCCESS;
439 }
440
441 default:
442 return DDI_FAILURE;
443 }
444}
445
446/**
447 * Driver ioctl, an alternate entry point for this character driver.
448 *
449 * @param Dev Device number
450 * @param Cmd Operation identifier
451 * @param pArg Arguments from user to driver
452 * @param Mode Information bitfield (read/write, address space etc.)
453 * @param pCred User credentials
454 * @param pVal Return value for calling process.
455 *
456 * @return corresponding solaris error code.
457 */
458static int VBoxDrvSolarisIOCtl (dev_t Dev, int Cmd, intptr_t pArgs, int Mode, cred_t* pCred, int* pVal)
459{
460 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
461 const RTPROCESS Process = RTProcSelf();
462 const unsigned iHash = SESSION_HASH(Process);
463 PSUPDRVSESSION pSession;
464
465 cmn_err(CE_CONT, "VBoxDrvSolarisIOCtl\n");
466 /*
467 * Find the session.
468 */
469 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
470 pSession = g_apSessionHashTab[iHash];
471 if (pSession && pSession->Process != Process)
472 {
473 do pSession = pSession->pNextHash;
474 while (pSession && pSession->Process != Process);
475 }
476 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
477 if (!pSession)
478 {
479 OSDBGPRINT(("VBoxSupDrvIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#x\n",
480 (int)Process, Cmd));
481 return EINVAL;
482 }
483
484 /*
485 * Deal with the two high-speed IOCtl that takes it's arguments from
486 * the session and iCmd, and only returns a VBox status code.
487 */
488#ifdef VBOX_WITHOUT_IDT_PATCHING
489 if ( Cmd == SUP_IOCTL_FAST_DO_RAW_RUN
490 || Cmd == SUP_IOCTL_FAST_DO_HWACC_RUN
491 || Cmd == SUP_IOCTL_FAST_DO_NOP)
492 return supdrvIOCtlFast(Cmd, &g_DevExt, pSession);
493#endif
494
495 return VBoxDrvSolarisIOCtlSlow(pSession, Cmd, Mode, pArgs);
496}
497
498/**
499 * Worker for VBoxSupDrvIOCtl that takes the slow IOCtl functions.
500 *
501 * @returns Solaris errno.
502 *
503 * @param pSession The session.
504 * @param Cmd The IOCtl command.
505 * @param Mode Information bitfield (for specifying ownership of data)
506 * @param pArgs Pointer to the kernel copy of the SUPDRVIOCTLDATA buffer.
507 */
508static int VBoxDrvSolarisIOCtlSlow(PSUPDRVSESSION pSession, int Cmd, int Mode, intptr_t pArgs)
509{
510 int rc;
511 void *pvBuf = NULL;
512 unsigned long cbBuf = 0;
513 unsigned cbOut = 0;
514 PSUPDRVIOCTLDATA pArgData = (PSUPDRVIOCTLDATA)pArgs;
515
516 /*
517 * Allocate and copy user space input data buffer to kernel space.
518 */
519 if (pArgData->cbIn > 0 || pArgData->cbOut > 0)
520 {
521 cbBuf = max(pArgData->cbIn, pArgData->cbOut);
522 pvBuf = RTMemTmpAlloc(cbBuf);
523
524 if (pvBuf == NULL)
525 {
526 OSDBGPRINT(("VBoxDrvSolarisIOCtlSlow: failed to allocate buffer of %d bytes.\n", cbBuf));
527 return ENOMEM;
528 }
529
530 rc = ddi_copyin(pArgData->pvIn, pvBuf, pArgData->cbIn, Mode);
531
532 if (rc != 0)
533 {
534 OSDBGPRINT(("VBoxDrvSolarisIOCtlSlow: ddi_copyin(%p,%d) failed.\n", pArgData->pvIn, pArgData->cbIn));
535
536 RTMemTmpFree(pvBuf);
537 return EFAULT;
538 }
539 }
540
541 /*
542 * Process the IOCtl.
543 */
544 rc = supdrvIOCtl(Cmd, &g_DevExt, pSession, pvBuf, pArgData->cbIn, pvBuf, pArgData->cbOut, &cbOut);
545
546 /*
547 * Copy ioctl data and output buffer back to user space.
548 */
549 if (rc != 0)
550 rc = VBoxSupDrvErr2SolarisErr(rc);
551 else if (cbOut > 0)
552 {
553 if (pvBuf != NULL && cbOut <= cbBuf)
554 {
555 rc = ddi_copyout(pvBuf, pArgData->pvOut, cbOut, Mode);
556 if (rc != 0)
557 {
558 OSDBGPRINT(("VBoxDrvSolarisIOCtlSlow: ddi_copyout(,%p,%d) failed.\n", pArgData->pvOut, cbBuf));
559
560 /** @todo r=bird: why this extra return? setting rc = EFAULT; should do the trick, shouldn't it? */
561 RTMemTmpFree(pvBuf);
562 return EFAULT;
563 }
564 }
565 else
566 {
567 OSDBGPRINT(("WHAT!?! supdrvIOCtl messed up! cbOut=%d cbBuf=%d pvBuf=%p\n", cbOut, cbBuf, pvBuf));
568 rc = EPERM;
569 }
570 }
571
572 if (pvBuf)
573 RTMemTmpFree(pvBuf);
574
575 OSDBGPRINT(("VBoxDrvSolarisIOCtlSlow: returns %d cbOut=%d\n", rc, cbOut));
576 return rc;
577}
578
579
580/**
581 * Converts an supdrv error code to a solaris error code.
582 *
583 * @returns corresponding solaris error code.
584 * @param rc supdrv error code (SUPDRV_ERR_* defines).
585 */
586static int VBoxSupDrvErr2SolarisErr(int rc)
587{
588 switch (rc)
589 {
590 case 0: return 0;
591 case SUPDRV_ERR_GENERAL_FAILURE: return EACCES;
592 case SUPDRV_ERR_INVALID_PARAM: return EINVAL;
593 case SUPDRV_ERR_INVALID_MAGIC: return EILSEQ;
594 case SUPDRV_ERR_INVALID_HANDLE: return ENXIO;
595 case SUPDRV_ERR_INVALID_POINTER: return EFAULT;
596 case SUPDRV_ERR_LOCK_FAILED: return ENOLCK;
597 case SUPDRV_ERR_ALREADY_LOADED: return EEXIST;
598 case SUPDRV_ERR_PERMISSION_DENIED: return EPERM;
599 case SUPDRV_ERR_VERSION_MISMATCH: return ENOSYS;
600 }
601
602 return EPERM;
603}
604
605/**
606 * Initializes any OS specific object creator fields.
607 */
608void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
609{
610 NOREF(pObj);
611 NOREF(pSession);
612}
613
614
615/**
616 * Checks if the session can access the object.
617 *
618 * @returns true if a decision has been made.
619 * @returns false if the default access policy should be applied.
620 *
621 * @param pObj The object in question.
622 * @param pSession The session wanting to access the object.
623 * @param pszObjName The object name, can be NULL.
624 * @param prc Where to store the result when returning true.
625 */
626bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
627{
628 NOREF(pObj);
629 NOREF(pSession);
630 NOREF(pszObjName);
631 NOREF(prc);
632 return false;
633}
634
635RTDECL(int) SUPR0Printf(const char *pszFormat, ...)
636{
637 va_list args;
638 char szMsg[512];
639
640 va_start(args, pszFormat);
641 vsnprintf(szMsg, sizeof(szMsg) - 1, pszFormat, args);
642 va_end(args);
643
644 szMsg[sizeof(szMsg) - 1] = '\0';
645 printf("%s", szMsg);
646 return 0;
647}
Note: See TracBrowser for help on using the repository browser.

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