VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.c@ 21102

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

VBoxGuest (common): porting to linux. some cleanup. currently untested.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 33.3 KB
Line 
1/* $Id: VBoxGuest-solaris.c 21069 2009-06-30 14:23:40Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions Driver for Solaris.
4 */
5
6/*
7 * Copyright (C) 2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#include <sys/conf.h>
27#include <sys/modctl.h>
28#include <sys/mutex.h>
29#include <sys/pci.h>
30#include <sys/stat.h>
31#include <sys/ddi.h>
32#include <sys/ddi_intr.h>
33#include <sys/sunddi.h>
34#include <sys/open.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 "VBoxGuestInternal.h"
38#include <VBox/log.h>
39#include <VBox/version.h>
40#include <iprt/assert.h>
41#include <iprt/initterm.h>
42#include <iprt/process.h>
43#include <iprt/mem.h>
44
45
46/*******************************************************************************
47* Defined Constants And Macros *
48*******************************************************************************/
49#define VBOXSOLQUOTE2(x) #x
50#define VBOXSOLQUOTE(x) VBOXSOLQUOTE2(x)
51/** The module name. */
52#define DEVICE_NAME "vboxguest"
53/** The module description as seen in 'modinfo'. */
54#define DEVICE_DESC "VirtualBox GstDrv"
55
56
57/*******************************************************************************
58* Internal Functions *
59*******************************************************************************/
60static int VBoxGuestSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred);
61static int VBoxGuestSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred);
62static int VBoxGuestSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred);
63static int VBoxGuestSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred);
64static int VBoxGuestSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal);
65
66static int VBoxGuestSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
67static int VBoxGuestSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
68static int VBoxGuestSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
69
70static int VBoxGuestSolarisAddIRQ(dev_info_t *pDip, void *pvState);
71static void VBoxGuestSolarisRemoveIRQ(dev_info_t *pDip, void *pvState);
72static uint_t VBoxGuestSolarisISR(caddr_t Arg);
73
74
75/*******************************************************************************
76* Structures and Typedefs *
77*******************************************************************************/
78/**
79 * cb_ops: for drivers that support char/block entry points
80 */
81static struct cb_ops g_VBoxGuestSolarisCbOps =
82{
83 VBoxGuestSolarisOpen,
84 VBoxGuestSolarisClose,
85 nodev, /* b strategy */
86 nodev, /* b dump */
87 nodev, /* b print */
88 VBoxGuestSolarisRead,
89 VBoxGuestSolarisWrite,
90 VBoxGuestSolarisIOCtl,
91 nodev, /* c devmap */
92 nodev, /* c mmap */
93 nodev, /* c segmap */
94 nochpoll, /* c poll */
95 ddi_prop_op, /* property ops */
96 NULL, /* streamtab */
97 D_NEW | D_MP, /* compat. flag */
98 CB_REV /* revision */
99};
100
101/**
102 * dev_ops: for driver device operations
103 */
104static struct dev_ops g_VBoxGuestSolarisDevOps =
105{
106 DEVO_REV, /* driver build revision */
107 0, /* ref count */
108 VBoxGuestSolarisGetInfo,
109 nulldev, /* identify */
110 nulldev, /* probe */
111 VBoxGuestSolarisAttach,
112 VBoxGuestSolarisDetach,
113 nodev, /* reset */
114 &g_VBoxGuestSolarisCbOps,
115 (struct bus_ops *)0,
116 nodev /* power */
117};
118
119/**
120 * modldrv: export driver specifics to the kernel
121 */
122static struct modldrv g_VBoxGuestSolarisModule =
123{
124 &mod_driverops, /* extern from kernel */
125 DEVICE_DESC " " VBOX_VERSION_STRING "r" VBOXSOLQUOTE(VBOX_SVN_REV),
126 &g_VBoxGuestSolarisDevOps
127};
128
129/**
130 * modlinkage: export install/remove/info to the kernel
131 */
132static struct modlinkage g_VBoxGuestSolarisModLinkage =
133{
134 MODREV_1, /* loadable module system revision */
135 &g_VBoxGuestSolarisModule,
136 NULL /* terminate array of linkage structures */
137};
138
139/**
140 * State info for each open file handle.
141 */
142typedef struct
143{
144 /** IO port handle. */
145 ddi_acc_handle_t PciIOHandle;
146 /** MMIO handle. */
147 ddi_acc_handle_t PciMMIOHandle;
148#if 0
149 /** Interrupt block cookie. */
150 ddi_iblock_cookie_t BlockCookie;
151#endif
152 /** Driver Mutex. */
153 kmutex_t Mtx;
154 /** IO Port. */
155 uint16_t uIOPortBase;
156 /** Address of the MMIO region.*/
157 caddr_t pMMIOBase;
158 /** Size of the MMIO region. */
159 off_t cbMMIO;
160 /** VMMDev Version. */
161 uint32_t u32Version;
162 /** Pointer to the interrupt handle vector */
163 ddi_intr_handle_t *pIntr;
164 /** Number of actually allocated interrupt handles */
165 size_t cIntrAllocated;
166#ifndef USE_SESSION_HASH
167 /** Pointer to the session handle. */
168 PVBOXGUESTSESSION pSession;
169#endif
170} vboxguest_state_t;
171
172
173/*******************************************************************************
174* Global Variables *
175*******************************************************************************/
176/** Device handle (we support only one instance). */
177static dev_info_t *g_pDip;
178
179/** Opaque pointer to state */
180static void *g_pVBoxGuestSolarisState;
181
182/** Device extention & session data association structure. */
183static VBOXGUESTDEVEXT g_DevExt;
184/** Spinlock protecting g_apSessionHashTab. */
185static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
186#ifdef USE_SESSION_HASH
187/** Hash table */
188static PVBOXGUESTSESSION g_apSessionHashTab[19];
189/** Calculates the index into g_apSessionHashTab.*/
190#define SESSION_HASH(sfn) ((sfn) % RT_ELEMENTS(g_apSessionHashTab))
191#endif /* USE_SESSION_HASH */
192
193#if 0/** @todo This shouldn't be needed. if it is, that means exceptions hasn't been disabled correctly. */
194/** GCC C++ hack. */
195unsigned __gxx_personality_v0 = 0xdecea5ed;
196#endif
197
198/**
199 * Kernel entry points
200 */
201int _init(void)
202{
203 LogFlow((DEVICE_NAME ":_init\n"));
204 int rc = ddi_soft_state_init(&g_pVBoxGuestSolarisState, sizeof(vboxguest_state_t), 1);
205 if (!rc)
206 {
207 rc = mod_install(&g_VBoxGuestSolarisModLinkage);
208 if (rc)
209 ddi_soft_state_fini(&g_pVBoxGuestSolarisState);
210 }
211 return rc;
212}
213
214
215int _fini(void)
216{
217 LogFlow((DEVICE_NAME ":_fini\n"));
218 int rc = mod_remove(&g_VBoxGuestSolarisModLinkage);
219 if (!rc)
220 ddi_soft_state_fini(&g_pVBoxGuestSolarisState);
221 return rc;
222}
223
224
225int _info(struct modinfo *pModInfo)
226{
227 LogFlow((DEVICE_NAME ":_info\n"));
228 return mod_info(&g_VBoxGuestSolarisModLinkage, pModInfo);
229}
230
231
232/**
233 * Attach entry point, to attach a device to the system or resume it.
234 *
235 * @param pDip The module structure instance.
236 * @param enmCmd Attach type (ddi_attach_cmd_t)
237 *
238 * @return corresponding solaris error code.
239 */
240static int VBoxGuestSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
241{
242 LogFlow((DEVICE_NAME "::Attach\n"));
243 switch (enmCmd)
244 {
245 case DDI_ATTACH:
246 {
247 int rc;
248 int instance;
249 vboxguest_state_t *pState;
250
251 instance = ddi_get_instance(pDip);
252#ifdef USE_SESSION_HASH
253 rc = ddi_soft_state_zalloc(g_pVBoxGuestSolarisState, instance);
254 if (rc != DDI_SUCCESS)
255 {
256 Log((DEVICE_NAME ":ddi_soft_state_zalloc failed.\n"));
257 return DDI_FAILURE;
258 }
259
260 pState = ddi_get_soft_state(g_pVBoxGuestSolarisState, instance);
261 if (!pState)
262 {
263 ddi_soft_state_free(g_pVBoxGuestSolarisState, instance);
264 Log((DEVICE_NAME ":ddi_get_soft_state for instance %d failed\n", instance));
265 return DDI_FAILURE;
266 }
267#else
268 pState = RTMemAllocZ(sizeof(vboxguest_state_t));
269 if (!pState)
270 {
271 Log((DEVICE_NAME ":RTMemAllocZ failed to allocate %d bytes\n", sizeof(vboxguest_state_t)));
272 return DDI_FAILURE;
273 }
274#endif
275
276 /*
277 * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
278 */
279 rc = RTR0Init(0);
280 if (RT_FAILURE(rc))
281 {
282 Log((DEVICE_NAME ":RTR0Init failed.\n"));
283 return DDI_FAILURE;
284 }
285
286 /*
287 * Initialize the session hash table.
288 */
289 rc = RTSpinlockCreate(&g_Spinlock);
290 if (RT_SUCCESS(rc))
291 {
292 /*
293 * Enable resources for PCI access.
294 */
295 ddi_acc_handle_t PciHandle;
296 rc = pci_config_setup(pDip, &PciHandle);
297 if (rc == DDI_SUCCESS)
298 {
299 /*
300 * Map the register address space.
301 */
302 caddr_t baseAddr;
303 ddi_device_acc_attr_t deviceAttr;
304 deviceAttr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
305 deviceAttr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
306 deviceAttr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
307 deviceAttr.devacc_attr_access = DDI_DEFAULT_ACC;
308 rc = ddi_regs_map_setup(pDip, 1, &baseAddr, 0, 0, &deviceAttr, &pState->PciIOHandle);
309 if (rc == DDI_SUCCESS)
310 {
311 /*
312 * Read size of the MMIO region.
313 */
314 pState->uIOPortBase = (uintptr_t)baseAddr;
315 rc = ddi_dev_regsize(pDip, 2, &pState->cbMMIO);
316 if (rc == DDI_SUCCESS)
317 {
318 rc = ddi_regs_map_setup(pDip, 2, &pState->pMMIOBase, 0, pState->cbMMIO, &deviceAttr,
319 &pState->PciMMIOHandle);
320 if (rc == DDI_SUCCESS)
321 {
322 /*
323 * Add IRQ of VMMDev.
324 */
325 rc = VBoxGuestSolarisAddIRQ(pDip, pState);
326 if (rc == DDI_SUCCESS)
327 {
328 /*
329 * Call the common device extension initializer.
330 */
331 rc = VBoxGuestInitDevExt(&g_DevExt, pState->uIOPortBase, pState->pMMIOBase,
332 pState->cbMMIO, VBOXOSTYPE_Solaris);
333 if (RT_SUCCESS(rc))
334 {
335 rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, 0);
336 if (rc == DDI_SUCCESS)
337 {
338 g_pDip = pDip;
339 ddi_set_driver_private(pDip, pState);
340 pci_config_teardown(&PciHandle);
341 ddi_report_dev(pDip);
342 return DDI_SUCCESS;
343 }
344
345 LogRel((DEVICE_NAME ": ddi_create_minor_node failed.\n"));
346 VBoxGuestDeleteDevExt(&g_DevExt);
347 }
348 else
349 LogRel((DEVICE_NAME ": VBoxGuestInitDevExt failed.\n"));
350 VBoxGuestSolarisRemoveIRQ(pDip, pState);
351 }
352 else
353 LogRel((DEVICE_NAME ": VBoxGuestSolarisAddIRQ failed.\n"));
354 ddi_regs_map_free(&pState->PciMMIOHandle);
355 }
356 else
357 LogRel((DEVICE_NAME ": ddi_regs_map_setup for MMIO region failed.\n"));
358 }
359 else
360 LogRel((DEVICE_NAME ": ddi_dev_regsize for MMIO region failed.\n"));
361 ddi_regs_map_free(&pState->PciIOHandle);
362 }
363 else
364 LogRel((DEVICE_NAME ": ddi_regs_map_setup for IOport failed.\n"));
365 pci_config_teardown(&PciHandle);
366 }
367 else
368 LogRel((DEVICE_NAME ": pci_config_setup failed rc=%d.\n", rc));
369 RTSpinlockDestroy(g_Spinlock);
370 g_Spinlock = NIL_RTSPINLOCK;
371 }
372 else
373 LogRel((DEVICE_NAME ": RTSpinlockCreate failed.\n"));
374
375 RTR0Term();
376 return DDI_FAILURE;
377 }
378
379 case DDI_RESUME:
380 {
381 /** @todo implement resume for guest driver. */
382 return DDI_SUCCESS;
383 }
384
385 default:
386 return DDI_FAILURE;
387 }
388}
389
390
391/**
392 * Detach entry point, to detach a device to the system or suspend it.
393 *
394 * @param pDip The module structure instance.
395 * @param enmCmd Attach type (ddi_attach_cmd_t)
396 *
397 * @return corresponding solaris error code.
398 */
399static int VBoxGuestSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
400{
401 LogFlow((DEVICE_NAME "::Detach\n"));
402 switch (enmCmd)
403 {
404 case DDI_DETACH:
405 {
406 int rc;
407 int instance = ddi_get_instance(pDip);
408#ifdef USE_SESSION_HASH
409 vboxguest_state_t *pState = ddi_get_soft_state(g_pVBoxGuestSolarisState, instance);
410#else
411 vboxguest_state_t *pState = ddi_get_driver_private(g_pDip);
412#endif
413 if (pState)
414 {
415 VBoxGuestSolarisRemoveIRQ(pDip, pState);
416 ddi_regs_map_free(&pState->PciIOHandle);
417 ddi_regs_map_free(&pState->PciMMIOHandle);
418 ddi_remove_minor_node(pDip, NULL);
419#ifdef USE_SESSION_HASH
420 ddi_soft_state_free(g_pVBoxGuestSolarisState, instance);
421#else
422 RTMemFree(pState);
423#endif
424
425 rc = RTSpinlockDestroy(g_Spinlock);
426 AssertRC(rc);
427 g_Spinlock = NIL_RTSPINLOCK;
428
429 RTR0Term();
430 return DDI_SUCCESS;
431 }
432 Log((DEVICE_NAME ":ddi_get_soft_state failed. Cannot detach instance %d\n", instance));
433 return DDI_FAILURE;
434 }
435
436 case DDI_SUSPEND:
437 {
438 /** @todo implement suspend for guest driver. */
439 return DDI_SUCCESS;
440 }
441
442 default:
443 return DDI_FAILURE;
444 }
445}
446
447
448/**
449 * Info entry point, called by solaris kernel for obtaining driver info.
450 *
451 * @param pDip The module structure instance (do not use).
452 * @param enmCmd Information request type.
453 * @param pvArg Type specific argument.
454 * @param ppvResult Where to store the requested info.
455 *
456 * @return corresponding solaris error code.
457 */
458static int VBoxGuestSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
459{
460 LogFlow((DEVICE_NAME "::GetInfo\n"));
461
462 int rc = DDI_SUCCESS;
463 switch (enmCmd)
464 {
465 case DDI_INFO_DEVT2DEVINFO:
466 *ppvResult = (void *)g_pDip;
467 break;
468
469 case DDI_INFO_DEVT2INSTANCE:
470 *ppvResult = (void *)(uintptr_t)ddi_get_instance(g_pDip);
471 break;
472
473 default:
474 rc = DDI_FAILURE;
475 break;
476 }
477
478 NOREF(pvArg);
479 return rc;
480}
481
482
483/**
484 * User context entry points
485 */
486static int VBoxGuestSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred)
487{
488 int rc;
489 PVBOXGUESTSESSION pSession;
490
491 LogFlow((DEVICE_NAME "::Open\n"));
492
493 /*
494 * Verify we are being opened as a character device.
495 */
496 if (fType != OTYP_CHR)
497 return EINVAL;
498
499#ifndef USE_SESSION_HASH
500 vboxguest_state_t *pState = NULL;
501 unsigned iOpenInstance;
502 for (iOpenInstance = 0; iOpenInstance < 4096; iOpenInstance++)
503 {
504 if ( !ddi_get_soft_state(g_pVBoxGuestSolarisState, iOpenInstance) /* faster */
505 && ddi_soft_state_zalloc(g_pVBoxGuestSolarisState, iOpenInstance) == DDI_SUCCESS)
506 {
507 pState = ddi_get_soft_state(g_pVBoxGuestSolarisState, iOpenInstance);
508 break;
509 }
510 }
511 if (!pState)
512 {
513 Log((DEVICE_NAME "::Open: too many open instances."));
514 return ENXIO;
515 }
516
517 /*
518 * Create a new session.
519 */
520 rc = VBoxGuestCreateUserSession(&g_DevExt, &pSession);
521 if (RT_SUCCESS(rc))
522 {
523 pState->pSession = pSession;
524 *pDev = makedevice(getmajor(*pDev), iOpenInstance);
525 Log((DEVICE_NAME "::Open: pSession=%p pState=%p pid=%d\n", pSession, pState, (int)RTProcSelf()));
526 return 0;
527 }
528
529 /* Failed, clean up. */
530 ddi_soft_state_free(g_pVBoxGuestSolarisState, iOpenInstance);
531#else
532 /*
533 * Create a new session.
534 */
535 rc = VBoxGuestCreateUserSession(&g_DevExt, &pSession);
536 if (RT_SUCCESS(rc))
537 {
538 /*
539 * Insert it into the hash table.
540 */
541 unsigned iHash = SESSION_HASH(pSession->Process);
542 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
543 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
544 pSession->pNextHash = g_apSessionHashTab[iHash];
545 g_apSessionHashTab[iHash] = pSession;
546 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
547
548 int instance;
549 for (instance = 0; instance < 4096; instance++)
550 {
551 vboxguest_state_t *pState = ddi_get_soft_state(g_pVBoxGuestSolarisState, instance);
552 if (pState)
553 break;
554 }
555 if (instance >= 4096)
556 {
557 Log((DEVICE_NAME "::Open: All instances exhausted\n"));
558 return ENXIO;
559 }
560 *pDev = makedevice(getmajor(*pDev), instance);
561 Log((DEVICE_NAME "::Open success: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, (int)RTProcSelf()));
562 return 0;
563 }
564#endif
565 LogRel((DEVICE_NAME "::Open: VBoxGuestCreateUserSession failed. rc=%d\n", rc));
566 return EFAULT;
567}
568
569
570static int VBoxGuestSolarisClose(dev_t Dev, int flag, int fType, cred_t *pCred)
571{
572 LogFlow((DEVICE_NAME "::Close pid=%d\n", (int)RTProcSelf()));
573
574#ifndef USE_SESSION_HASH
575 PVBOXGUESTSESSION pSession;
576 vboxguest_state_t *pState = ddi_get_soft_state(g_pVBoxGuestSolarisState, getminor(Dev));
577 if (!pState)
578 {
579 Log((DEVICE_NAME "::Close: failed to get pState.\n"));
580 return EFAULT;
581 }
582
583 pSession = pState->pSession;
584 pState->pSession = NULL;
585 Log((DEVICE_NAME "::Close: pSession=%p pState=%p\n", pSession, pState));
586 ddi_soft_state_free(g_pVBoxGuestSolarisState, getminor(Dev));
587 if (!pSession)
588 {
589 Log((DEVICE_NAME "::Close: failed to get pSession.\n"));
590 return EFAULT;
591 }
592
593#else /* USE_SESSION_HASH */
594 /*
595 * Remove from the hash table.
596 */
597 PVBOXGUESTSESSION pSession;
598 const RTPROCESS Process = RTProcSelf();
599 const unsigned iHash = SESSION_HASH(Process);
600 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
601 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
602
603 pSession = g_apSessionHashTab[iHash];
604 if (pSession)
605 {
606 if (pSession->Process == Process)
607 {
608 g_apSessionHashTab[iHash] = pSession->pNextHash;
609 pSession->pNextHash = NULL;
610 }
611 else
612 {
613 PVBOXGUESTSESSION pPrev = pSession;
614 pSession = pSession->pNextHash;
615 while (pSession)
616 {
617 if (pSession->Process == Process)
618 {
619 pPrev->pNextHash = pSession->pNextHash;
620 pSession->pNextHash = NULL;
621 break;
622 }
623
624 /* next */
625 pPrev = pSession;
626 pSession = pSession->pNextHash;
627 }
628 }
629 }
630 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
631 if (!pSession)
632 {
633 Log((DEVICE_NAME "::Close: WHUT?!? pSession == NULL! This must be a mistake... pid=%d", (int)Process));
634 return EFAULT;
635 }
636 Log((DEVICE_NAME "::Close: pid=%d\n", (int)Process));
637#endif /* USE_SESSION_HASH */
638
639 /*
640 * Close the session.
641 */
642 VBoxGuestCloseSession(&g_DevExt, pSession);
643 return 0;
644}
645
646
647static int VBoxGuestSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
648{
649 LogFlow((DEVICE_NAME "::Read\n"));
650 return 0;
651}
652
653
654static int VBoxGuestSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
655{
656 LogFlow((DEVICE_NAME "::Write\n"));
657 return 0;
658}
659
660
661/** @def IOCPARM_LEN
662 * Gets the length from the ioctl number.
663 * This is normally defined by sys/ioccom.h on BSD systems...
664 */
665#ifndef IOCPARM_LEN
666# define IOCPARM_LEN(Code) (((Code) >> 16) & IOCPARM_MASK)
667#endif
668
669
670/**
671 * Driver ioctl, an alternate entry point for this character driver.
672 *
673 * @param Dev Device number
674 * @param Cmd Operation identifier
675 * @param pArg Arguments from user to driver
676 * @param Mode Information bitfield (read/write, address space etc.)
677 * @param pCred User credentials
678 * @param pVal Return value for calling process.
679 *
680 * @return corresponding solaris error code.
681 */
682static int VBoxGuestSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal)
683{
684 LogFlow((DEVICE_NAME ":VBoxGuestSolarisIOCtl\n"));
685
686#ifndef USE_SESSION_HASH
687 /*
688 * Get the session from the soft state item.
689 */
690 vboxguest_state_t *pState = ddi_get_soft_state(g_pVBoxGuestSolarisState, getminor(Dev));
691 if (!pState)
692 {
693 Log((DEVICE_NAME "::IOCtl: no state data for %d\n", getminor(Dev)));
694 return EINVAL;
695 }
696
697 PVBOXGUESTSESSION pSession = pState->pSession;
698 if (!pSession)
699 {
700 Log((DEVICE_NAME "::IOCtl: no session data for %d\n", getminor(Dev)));
701 return EINVAL;
702 }
703
704#else /* USE_SESSION_HASH */
705 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
706 const RTPROCESS Process = RTProcSelf();
707 const unsigned iHash = SESSION_HASH(Process);
708 PVBOXGUESTSESSION pSession;
709
710 /*
711 * Find the session.
712 */
713 RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
714 pSession = g_apSessionHashTab[iHash];
715 if (pSession && pSession->Process != Process)
716 {
717 do pSession = pSession->pNextHash;
718 while (pSession && pSession->Process != Process);
719 }
720 RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
721 if (!pSession)
722 {
723 Log((DEVICE_NAME "::IOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#x\n", (int)Process, Cmd));
724 return EINVAL;
725 }
726#endif /* USE_SESSION_HASH */
727
728 /*
729 * Read and validate the request wrapper.
730 */
731 VBGLBIGREQ ReqWrap;
732 if (IOCPARM_LEN(Cmd) != sizeof(ReqWrap))
733 {
734 LogRel((DEVICE_NAME "::IOCtl: bad request %#x size=%d expected=%d\n", Cmd, IOCPARM_LEN(Cmd), sizeof(ReqWrap)));
735 return ENOTTY;
736 }
737
738 int rc = ddi_copyin((void *)pArg, &ReqWrap, sizeof(ReqWrap), Mode);
739 if (RT_UNLIKELY(rc))
740 {
741 LogRel((DEVICE_NAME "::IOCtl: ddi_copyin failed to read header pArg=%p Cmd=%d. rc=%d.\n", pArg, Cmd, rc));
742 return EINVAL;
743 }
744
745 if (ReqWrap.u32Magic != VBGLBIGREQ_MAGIC)
746 {
747 LogRel((DEVICE_NAME "::IOCtl: bad magic %#x; pArg=%p Cmd=%d.\n", ReqWrap.u32Magic, pArg, Cmd));
748 return EINVAL;
749 }
750 if (RT_UNLIKELY( ReqWrap.cbData == 0
751 || ReqWrap.cbData > _1M*16))
752 {
753 Log((DEVICE_NAME "::IOCtl: bad size %#x; pArg=%p Cmd=%d.\n", ReqWrap.cbData, pArg, Cmd));
754 return EINVAL;
755 }
756
757 /*
758 * Read the request.
759 */
760 void *pvBuf = RTMemTmpAlloc(ReqWrap.cbData);
761 if (RT_UNLIKELY(!pvBuf))
762 {
763 LogRel((DEVICE_NAME "::IOCtl: RTMemTmpAlloc failed to alloc %d bytes.\n", ReqWrap.cbData));
764 return ENOMEM;
765 }
766
767 rc = ddi_copyin((void *)(uintptr_t)ReqWrap.pvDataR3, pvBuf, ReqWrap.cbData, Mode);
768 if (RT_UNLIKELY(rc))
769 {
770 RTMemTmpFree(pvBuf);
771 LogRel((DEVICE_NAME "::IOCtl: ddi_copyin failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc));
772 return EFAULT;
773 }
774 if (RT_UNLIKELY( ReqWrap.cbData != 0
775 && !VALID_PTR(pvBuf)))
776 {
777 RTMemTmpFree(pvBuf);
778 LogRel((DEVICE_NAME "::IOCtl: pvBuf invalid pointer %p\n", pvBuf));
779 return EINVAL;
780 }
781 Log((DEVICE_NAME "::IOCtl: pSession=%p pid=%d.\n", pSession, (int)RTProcSelf()));
782
783 /*
784 * Process the IOCtl.
785 */
786 size_t cbDataReturned;
787 rc = VBoxGuestCommonIOCtl(Cmd, &g_DevExt, pSession, pvBuf, ReqWrap.cbData, &cbDataReturned);
788 if (RT_SUCCESS(rc))
789 {
790 rc = 0;
791 if (RT_UNLIKELY(cbDataReturned > ReqWrap.cbData))
792 {
793 LogRel((DEVICE_NAME "::IOCtl: too much output data %d expected %d\n", cbDataReturned, ReqWrap.cbData));
794 cbDataReturned = ReqWrap.cbData;
795 }
796 if (cbDataReturned > 0)
797 {
798 rc = ddi_copyout(pvBuf, (void *)(uintptr_t)ReqWrap.pvDataR3, cbDataReturned, Mode);
799 if (RT_UNLIKELY(rc))
800 {
801 LogRel((DEVICE_NAME "::IOCtl: ddi_copyout failed; pvBuf=%p pArg=%p cbDataReturned=%u Cmd=%d. rc=%d\n",
802 pvBuf, pArg, cbDataReturned, Cmd, rc));
803 rc = EFAULT;
804 }
805 }
806 }
807 else
808 {
809 LogRel((DEVICE_NAME "::IOCtl: VBoxGuestCommonIOCtl failed. rc=%d\n", rc));
810 rc = RTErrConvertToErrno(rc);
811 }
812 *pVal = rc;
813 RTMemTmpFree(pvBuf);
814 return rc;
815}
816
817
818/**
819 * Sets IRQ for VMMDev.
820 *
821 * @returns Solaris error code.
822 * @param pDip Pointer to the device info structure.
823 * @param pvState Pointer to the state info structure.
824 */
825static int VBoxGuestSolarisAddIRQ(dev_info_t *pDip, void *pvState)
826{
827 LogFlow((DEVICE_NAME "::AddIRQ: %p\n", pvState));
828
829 vboxguest_state_t *pState = (vboxguest_state_t *)pvState;
830#if 0
831 /*
832 * These calls are supposedly deprecated. But Sun seems to use them all over
833 * the place. Anyway, once this works we will switch to the highly elaborate
834 * and non-obsolete way of setting up IRQs.
835 */
836 int rc = ddi_get_iblock_cookie(pDip, 0, &pState->BlockCookie);
837 if (rc == DDI_SUCCESS)
838 {
839 mutex_init(&pState->Mtx, "VBoxGuest Driver Mutex", MUTEX_DRIVER, (void *)pState->BlockCookie);
840 rc = ddi_add_intr(pDip, 0, &pState->BlockCookie, NULL, VBoxGuestSolarisISR, (caddr_t)pState);
841 if (rc != DDI_SUCCESS)
842 Log((DEVICE_NAME ":ddi_add_intr failed. Cannot set IRQ for VMMDev.\n"));
843 }
844 else
845 Log((DEVICE_NAME ":ddi_get_iblock_cookie failed. Cannot set IRQ for VMMDev.\n"));
846 return rc;
847#else
848 int IntrType = 0;
849 int rc = ddi_intr_get_supported_types(pDip, &IntrType);
850 if (rc == DDI_SUCCESS)
851 {
852 /* We won't need to bother about MSIs. */
853 if (IntrType & DDI_INTR_TYPE_FIXED)
854 {
855 int IntrCount = 0;
856 rc = ddi_intr_get_nintrs(pDip, IntrType, &IntrCount);
857 if ( rc == DDI_SUCCESS
858 && IntrCount > 0)
859 {
860 int IntrAvail = 0;
861 rc = ddi_intr_get_navail(pDip, IntrType, &IntrAvail);
862 if ( rc == DDI_SUCCESS
863 && IntrAvail > 0)
864 {
865 /* Allocated kernel memory for the interrupt handles. The allocation size is stored internally. */
866 pState->pIntr = RTMemAlloc(IntrCount * sizeof(ddi_intr_handle_t));
867 if (pState->pIntr)
868 {
869 int IntrAllocated;
870 rc = ddi_intr_alloc(pDip, pState->pIntr, IntrType, 0, IntrCount, &IntrAllocated, DDI_INTR_ALLOC_NORMAL);
871 if ( rc == DDI_SUCCESS
872 && IntrAllocated > 0)
873 {
874 pState->cIntrAllocated = IntrAllocated;
875 uint_t uIntrPriority;
876 rc = ddi_intr_get_pri(pState->pIntr[0], &uIntrPriority);
877 if (rc == DDI_SUCCESS)
878 {
879 /* Initialize the mutex. */
880 mutex_init(&pState->Mtx, "VBoxGuestMtx", MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
881
882 /* Assign interrupt handler functions and enable interrupts. */
883 for (int i = 0; i < IntrAllocated; i++)
884 {
885 rc = ddi_intr_add_handler(pState->pIntr[i], (ddi_intr_handler_t *)VBoxGuestSolarisISR,
886 (caddr_t)pState, NULL);
887 if (rc == DDI_SUCCESS)
888 rc = ddi_intr_enable(pState->pIntr[i]);
889 if (rc != DDI_SUCCESS)
890 {
891 /* Changing local IntrAllocated to hold so-far allocated handles for freeing. */
892 IntrAllocated = i;
893 break;
894 }
895 }
896 if (rc == DDI_SUCCESS)
897 return rc;
898
899 /* Remove any assigned handlers */
900 LogRel((DEVICE_NAME ":failed to assign IRQs allocated=%d\n", IntrAllocated));
901 for (int x = 0; x < IntrAllocated; x++)
902 ddi_intr_remove_handler(pState->pIntr[x]);
903 }
904 else
905 LogRel((DEVICE_NAME "::AddIRQ: failed to get priority of interrupt. rc=%d\n", rc));
906
907 /* Remove allocated IRQs, too bad we can free only one handle at a time. */
908 for (int k = 0; k < pState->cIntrAllocated; k++)
909 ddi_intr_free(pState->pIntr[k]);
910 }
911 else
912 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
913 RTMemFree(pState->pIntr);
914 }
915 else
916 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
917 }
918 else
919 LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient available IRQs. rc=%d IntrAvail=%d\n", rc, IntrAvail));
920 }
921 else
922 LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient number of IRQs. rc=%d IntrCount=%d\n", rc, IntrCount));
923 }
924 else
925 LogRel((DEVICE_NAME "::AddIRQ: invalid irq type. IntrType=%#x\n", IntrType));
926 }
927 else
928 LogRel((DEVICE_NAME "::AddIRQ: failed to get supported interrupt types\n"));
929 return rc;
930#endif
931}
932
933
934/**
935 * Removes IRQ for VMMDev.
936 *
937 * @param pDip Pointer to the device info structure.
938 * @param pvState Opaque pointer to the state info structure.
939 */
940static void VBoxGuestSolarisRemoveIRQ(dev_info_t *pDip, void *pvState)
941{
942 vboxguest_state_t *pState = (vboxguest_state_t *)pvState;
943 LogFlow((DEVICE_NAME "::RemoveIRQ: pvState=%p\n"));
944
945#if 0
946 ddi_remove_intr(pDip, 0, pState->BlockCookie);
947 mutex_destroy(&pState->Mtx);
948#else
949 for (int i = 0; i < pState->cIntrAllocated; i++)
950 {
951 int rc = ddi_intr_disable(pState->pIntr[i]);
952 if (rc == DDI_SUCCESS)
953 {
954 rc = ddi_intr_remove_handler(pState->pIntr[i]);
955 if (rc == DDI_SUCCESS)
956 ddi_intr_free(pState->pIntr[i]);
957 }
958 }
959 RTMemFree(pState->pIntr);
960 mutex_destroy(&pState->Mtx);
961#endif
962}
963
964
965/**
966 * Interrupt Service Routine for VMMDev.
967 *
968 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
969 */
970static uint_t VBoxGuestSolarisISR(caddr_t Arg)
971{
972 LogFlow((DEVICE_NAME "::ISR: Arg=%p\n", Arg));
973
974 vboxguest_state_t *pState = (vboxguest_state_t *)Arg;
975 mutex_enter(&pState->Mtx);
976 bool fOurIRQ = VBoxGuestCommonISR(&g_DevExt);
977 mutex_exit(&pState->Mtx);
978
979 return fOurIRQ ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED;
980}
981
982
983/* Common code that depend on g_DevExt. */
984#include "VBoxGuestIDC-unix.c.h"
985
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