VirtualBox

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

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

vboxadd: The preferred way to setup interrupts in Solaris.

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