VirtualBox

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

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

Solaris vboxguest: added proper checks while setting up IRQs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 36.9 KB
Line 
1/* $Id: VBoxGuest-solaris.c 6803 2008-02-05 07:41:33Z 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 "vboxguest"
48/** The module description as seen in 'modinfo'. */
49#define DEVICE_DESC "VirtualBox Guest Driver"
50
51
52/*******************************************************************************
53* Internal Functions *
54*******************************************************************************/
55static int VBoxGuestSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred);
56static int VBoxGuestSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred);
57static int VBoxGuestSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred);
58static int VBoxGuestSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred);
59static int VBoxGuestSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int mode, cred_t *pCred, int *pVal);
60
61static int VBoxGuestSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
62static int VBoxGuestSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
63static int VBoxGuestSolarisDetach(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_VBoxGuestSolarisCbOps =
81{
82 VBoxGuestSolarisOpen,
83 VBoxGuestSolarisClose,
84 nodev, /* b strategy */
85 nodev, /* b dump */
86 nodev, /* b print */
87 VBoxGuestSolarisRead,
88 VBoxGuestSolarisWrite,
89 VBoxGuestSolarisIOCtl,
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_VBoxGuestSolarisDevOps =
104{
105 DEVO_REV, /* driver build revision */
106 0, /* ref count */
107 VBoxGuestSolarisGetInfo,
108 nulldev, /* identify */
109 nulldev, /* probe */
110 VBoxGuestSolarisAttach,
111 VBoxGuestSolarisDetach,
112 nodev, /* reset */
113 &g_VBoxGuestSolarisCbOps,
114 (struct bus_ops *)0,
115 nodev /* power */
116};
117
118/**
119 * modldrv: export driver specifics to the kernel
120 */
121static struct modldrv g_VBoxGuestSolarisModule =
122{
123 &mod_driverops, /* extern from kernel */
124 DEVICE_DESC,
125 &g_VBoxGuestSolarisDevOps
126};
127
128/**
129 * modlinkage: export install/remove/info to the kernel
130 */
131static struct modlinkage g_VBoxGuestSolarisModLinkage =
132{
133 MODREV_1, /* loadable module system revision */
134 &g_VBoxGuestSolarisModule,
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 /** Pointer to the interrupt handle vector */
160 ddi_intr_handle_t *pIntr;
161 /** Number of actually allocated interrupt handles */
162 size_t cIntrAllocated;
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_pVBoxGuestSolarisState;
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_pVBoxGuestSolarisState, sizeof(VBoxAddDevState), 1);
196 if (!rc)
197 {
198 rc = mod_install(&g_VBoxGuestSolarisModLinkage);
199 if (rc)
200 ddi_soft_state_fini(&g_pVBoxGuestSolarisState);
201 }
202 return rc;
203}
204
205
206int _fini(void)
207{
208 LogFlow((DEVICE_NAME ":_fini\n"));
209 int rc = mod_remove(&g_VBoxGuestSolarisModLinkage);
210 if (!rc)
211 ddi_soft_state_fini(&g_pVBoxGuestSolarisState);
212 return rc;
213}
214
215
216int _info(struct modinfo *pModInfo)
217{
218 LogFlow((DEVICE_NAME ":_info\n"));
219 return mod_info(&g_VBoxGuestSolarisModLinkage, 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 VBoxGuestSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
232{
233 LogFlow((DEVICE_NAME ":VBoxGuestSolarisAttach\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_pVBoxGuestSolarisState, 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_pVBoxGuestSolarisState, instance);
252 if (!pState)
253 {
254 ddi_soft_state_free(g_pVBoxGuestSolarisState, 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 VBoxGuestSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
412{
413 LogFlow((DEVICE_NAME ":VBoxGuestSolarisDetach\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_pVBoxGuestSolarisState, 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_pVBoxGuestSolarisState, 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 VBoxGuestSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult)
471{
472 LogFlow((DEVICE_NAME ":VBoxGuestSolarisGetInfo\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 VBoxGuestSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred)
497{
498 int rc;
499 PVBOXGUESTSESSION pSession;
500
501 LogFlow((DEVICE_NAME ":VBoxGuestSolarisOpen\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_pVBoxGuestSolarisState, iOpenInstance) /* faster */
515 && ddi_soft_state_zalloc(g_pVBoxGuestSolarisState, iOpenInstance) == DDI_SUCCESS)
516 {
517 pState = ddi_get_soft_state(g_pVBoxGuestSolarisState, iOpenInstance);
518 break;
519 }
520 }
521 if (!pState)
522 {
523 Log((DEVICE_NAME ":VBoxGuestSolarisOpen: 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 "VBoxGuestSolarisOpen: 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_pVBoxGuestSolarisState, 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_pVBoxGuestSolarisState, instance);
562 if (pState)
563 break;
564 }
565 if (instance >= 4096)
566 {
567 Log((DEVICE_NAME ":VBoxGuestSolarisOpen: All instances exhausted\n"));
568 return ENXIO;
569 }
570 *pDev = makedevice(getmajor(*pDev), instance);
571 Log((DEVICE_NAME ":VBoxGuestSolarisOpen 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 ":VBoxGuestSolarisOpen: VBoxGuestCreateUserSession failed. rc=%d\n", rc));
576 return EFAULT;
577}
578
579
580static int VBoxGuestSolarisClose(dev_t Dev, int flag, int fType, cred_t *pCred)
581{
582 LogFlow((DEVICE_NAME ":VBoxGuestSolarisClose pid=%d\n", (int)RTProcSelf()));
583
584#ifndef USE_SESSION_HASH
585 PVBOXGUESTSESSION pSession;
586 VBoxAddDevState *pState = ddi_get_soft_state(g_pVBoxGuestSolarisState, getminor(Dev));
587 if (!pState)
588 {
589 Log((DEVICE_NAME ":VBoxGuestSolarisClose: failed to get pState.\n"));
590 return EFAULT;
591 }
592
593 pSession = pState->pSession;
594 pState->pSession = NULL;
595 Log((DEVICE_NAME ":VBoxGuestSolarisClose: pSession=%p pState=%p\n", pSession, pState));
596 ddi_soft_state_free(g_pVBoxGuestSolarisState, getminor(Dev));
597 if (!pSession)
598 {
599 Log((DEVICE_NAME ":VBoxGuestSolarisClose: 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 ":VBoxGuestSolarisClose: WHUT?!? pSession == NULL! This must be a mistake... pid=%d", (int)Process));
644 return EFAULT;
645 }
646 Log((DEVICE_NAME ":VBoxGuestSolarisClose: 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 VBoxGuestSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
658{
659 LogFlow((DEVICE_NAME ":VBoxGuestSolarisRead\n"));
660 return 0;
661}
662
663
664static int VBoxGuestSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
665{
666 LogFlow((DEVICE_NAME ":VBoxGuestSolarisWrite\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 VBoxGuestSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal)
693{
694 LogFlow((DEVICE_NAME ":VBoxGuestSolarisIOCtl\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_pVBoxGuestSolarisState, getminor(Dev));
701 if (!pState)
702 {
703 Log((DEVICE_NAME ":VBoxGuestSolarisIOCtl: 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 ":VBoxGuestSolarisIOCtl: 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 ":VBoxGuestSolarisIOCtl: 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 ": VBoxGuestSolarisIOCtl: 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 ": VBoxGuestSolarisIOCtl: 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 ": VBoxGuestSolarisIOCtl: 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 ": VBoxGuestSolarisIOCtl: 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 ":VBoxGuestSolarisIOCtl: 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 ":VBoxGuestSolarisIOCtl: 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 ":VBoxGuestSolarisIOCtl: pvBuf invalid pointer %p\n", pvBuf));
789 return EINVAL;
790 }
791 Log((DEVICE_NAME ":VBoxGuestSolarisIOCtl: 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 rc = 0;
801 if (RT_UNLIKELY(cbDataReturned > ReqWrap.cbData))
802 {
803 Log((DEVICE_NAME ":VBoxGuestSolarisIOCtl: too much output data %d expected %d\n", cbDataReturned, ReqWrap.cbData));
804 cbDataReturned = ReqWrap.cbData;
805 }
806 if (cbDataReturned > 0)
807 {
808 rc = ddi_copyout(pvBuf, (void *)(uintptr_t)ReqWrap.pvDataR3, cbDataReturned, Mode);
809 if (RT_UNLIKELY(rc))
810 {
811 Log((DEVICE_NAME ":VBoxGuestSolarisIOCtl: ddi_copyout failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc));
812 rc = EFAULT;
813 }
814 }
815 }
816 else
817 {
818 LogRel((DEVICE_NAME ":VBoxGuestSolarisIOCtl: VBoxGuestCommonIOCtl failed. rc=%d\n", rc));
819 rc = EFAULT;
820 }
821 *pVal = rc;
822 RTMemTmpFree(pvBuf);
823 return rc;
824}
825
826
827/**
828 * Sets IRQ for VMMDev.
829 *
830 * @returns Solaris error code.
831 * @param pDip Pointer to the device info structure.
832 * @param pvState Pointer to the state info structure.
833 */
834static int VBoxGuestSolarisAddIRQ(dev_info_t *pDip, void *pvState)
835{
836 LogFlow((DEVICE_NAME ":VBoxGuestSolarisAddIRQ %p\n", pvState));
837
838 VBoxAddDevState *pState = (VBoxAddDevState *)pvState;
839#if 0
840 /*
841 * These calls are supposedly deprecated. But Sun seems to use them all over
842 * the place. Anyway, once this works we will switch to the highly elaborate
843 * and non-obsolete way of setting up IRQs.
844 */
845 int rc = ddi_get_iblock_cookie(pDip, 0, &pState->BlockCookie);
846 if (rc == DDI_SUCCESS)
847 {
848 mutex_init(&pState->Mtx, "VBoxGuest Driver Mutex", MUTEX_DRIVER, (void *)pState->BlockCookie);
849 rc = ddi_add_intr(pDip, 0, &pState->BlockCookie, NULL, VBoxGuestSolarisISR, (caddr_t)pState);
850 if (rc != DDI_SUCCESS)
851 Log((DEVICE_NAME ":ddi_add_intr failed. Cannot set IRQ for VMMDev.\n"));
852 }
853 else
854 Log((DEVICE_NAME ":ddi_get_iblock_cookie failed. Cannot set IRQ for VMMDev.\n"));
855 return rc;
856#else
857 int IntrType = 0;
858 int 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 = 0;
865 rc = ddi_intr_get_nintrs(pDip, IntrType, &IntrCount);
866 if ( rc == DDI_SUCCESS
867 && IntrCount > 0)
868 {
869 int IntrAvail = 0;
870 rc = ddi_intr_get_navail(pDip, IntrType, &IntrAvail);
871 if ( rc == DDI_SUCCESS
872 && IntrAvail > 0)
873 {
874 /* Allocated kernel memory for the interrupt handles. The allocation size is stored internally. */
875 pState->pIntr = RTMemAlloc(IntrCount * sizeof(ddi_intr_handle_t));
876 if (pState->pIntr)
877 {
878 int IntrAllocated;
879 rc = ddi_intr_alloc(pDip, pState->pIntr, IntrType, 0, IntrCount, &IntrAllocated, DDI_INTR_ALLOC_NORMAL);
880 if ( rc == DDI_SUCCESS
881 && IntrAllocated > 0)
882 {
883 pState->cIntrAllocated = IntrAllocated;
884 uint_t uIntrPriority;
885 rc = ddi_intr_get_pri(pState->pIntr[0], &uIntrPriority);
886 if (rc == DDI_SUCCESS)
887 {
888 /* Initialize the mutex. */
889 mutex_init(&pState->Mtx, "VBoxGuestMtx", MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
890
891 /* Assign interrupt handler functions and enable interrupts. */
892 for (int i = 0; i < IntrAllocated; i++)
893 {
894 rc = ddi_intr_add_handler(pState->pIntr[i], (ddi_intr_handler_t *)VBoxGuestSolarisISR,
895 (caddr_t)pState, NULL);
896 if (rc == DDI_SUCCESS)
897 rc = ddi_intr_enable(pState->pIntr[i]);
898 if (rc != DDI_SUCCESS)
899 {
900 /* Changing local IntrAllocated to hold so-far allocated handles for freeing. */
901 IntrAllocated = i;
902 break;
903 }
904 }
905 if (rc == DDI_SUCCESS)
906 return rc;
907
908 /* Remove any assigned handlers */
909 LogRel((DEVICE_NAME ":failed to assign IRQs allocated=%d\n", IntrAllocated));
910 for (int x = 0; x < IntrAllocated; x++)
911 ddi_intr_remove_handler(pState->pIntr[x]);
912 }
913 else
914 LogRel((DEVICE_NAME ":VBoxGuestSolarisAddIRQ: failed to get priority of interrupt. rc=%d\n", rc));
915
916 /* Remove allocated IRQs, too bad we can free only one handle at a time. */
917 for (int k = 0; k < pState->cIntrAllocated; k++)
918 ddi_intr_free(pState->pIntr[k]);
919 }
920 else
921 LogRel((DEVICE_NAME ":VBoxGuestSolarisAddIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
922 RTMemFree(pState->pIntr);
923 }
924 else
925 LogRel((DEVICE_NAME ":VBoxGuestSolarisAddIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
926 }
927 else
928 LogRel((DEVICE_NAME ":VBoxGuestSolarisAddIRQ: failed to get or insufficient available IRQs. rc=%d IntrAvail=%d\n", rc, IntrAvail));
929 }
930 else
931 LogRel((DEVICE_NAME ":VBoxGuestSolarisAddIRQ: failed to get or insufficient number of IRQs. rc=%d IntrCount=%d\n", rc, IntrCount));
932 }
933 else
934 LogRel((DEVICE_NAME ":VBoxGuestSolarisAddIRQ: invalid irq type. IntrType=%#x\n", IntrType));
935 }
936 else
937 LogRel((DEVICE_NAME ":VBoxGuestSolarisAddIRQ: failed to get supported interrupt types\n"));
938 return rc;
939#endif
940}
941
942
943/**
944 * Removes IRQ for VMMDev.
945 *
946 * @param pDip Pointer to the device info structure.
947 * @param pvState Opaque pointer to the state info structure.
948 */
949static void VBoxGuestSolarisRemoveIRQ(dev_info_t *pDip, void *pvState)
950{
951 VBoxAddDevState *pState = (VBoxAddDevState *)pvState;
952 LogFlow((DEVICE_NAME ":VBoxGuestSolarisRemoveIRQ pvState=%p\n"));
953
954#if 0
955 ddi_remove_intr(pDip, 0, pState->BlockCookie);
956 mutex_destroy(&pState->Mtx);
957#else
958 for (int i = 0; i < pState->cIntrAllocated; i++)
959 {
960 int rc = ddi_intr_disable(pState->pIntr[i]);
961 if (rc == DDI_SUCCESS)
962 {
963 rc = ddi_intr_remove_handler(pState->pIntr[i]);
964 if (rc == DDI_SUCCESS)
965 ddi_intr_free(pState->pIntr[i]);
966 }
967 }
968 RTMemFree(pState->pIntr);
969 mutex_destroy(&pState->Mtx);
970#endif
971}
972
973
974/**
975 * Interrupt Service Routine for VMMDev.
976 *
977 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
978 */
979static uint_t VBoxGuestSolarisISR(caddr_t Arg)
980{
981 LogFlow((DEVICE_NAME ":VBoxGuestSolarisISR Arg=%p\n", Arg));
982
983 VBoxAddDevState *pState = (VBoxAddDevState *)Arg;
984 mutex_enter(&pState->Mtx);
985 bool fOurIRQ = VBoxGuestCommonISR(&g_DevExt);
986 mutex_exit(&pState->Mtx);
987
988 return fOurIRQ ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED;
989}
990
991
992/**
993 * VBoxGuest Common ioctl wrapper from VBoxGuestLib.
994 *
995 * @returns VBox error code.
996 * @param pvSession Opaque pointer to the session.
997 * @param iCmd Requested function.
998 * @param pvData IO data buffer.
999 * @param cbData Size of the data buffer.
1000 * @param pcbDataReturned Where to store the amount of returned data.
1001 */
1002DECLVBGL(int) VBoxGuestSolarisServiceCall(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned)
1003{
1004 LogFlow((DEVICE_NAME ":VBoxGuestSolarisServiceCall %pvSesssion=%p Cmd=%u pvData=%p cbData=%d\n", pvSession, iCmd, pvData, cbData));
1005
1006 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
1007 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1008 AssertMsgReturn(pSession->pDevExt == &g_DevExt,
1009 ("SC: %p != %p\n", pSession->pDevExt, &g_DevExt), VERR_INVALID_HANDLE);
1010
1011 return VBoxGuestCommonIOCtl(iCmd, &g_DevExt, pSession, pvData, cbData, pcbDataReturned);
1012}
1013
1014
1015/**
1016 * Solaris Guest service open.
1017 *
1018 * @returns Opaque pointer to session object.
1019 * @param pu32Version Where to store VMMDev version.
1020 */
1021DECLVBGL(void *) VBoxGuestSolarisServiceOpen(uint32_t *pu32Version)
1022{
1023 LogFlow((DEVICE_NAME ":VBoxGuestSolarisServiceOpen\n"));
1024
1025 AssertPtrReturn(pu32Version, NULL);
1026 PVBOXGUESTSESSION pSession;
1027 int rc = VBoxGuestCreateKernelSession(&g_DevExt, &pSession);
1028 if (RT_SUCCESS(rc))
1029 {
1030 *pu32Version = VMMDEV_VERSION;
1031 return pSession;
1032 }
1033 LogRel((DEVICE_NAME ":VBoxGuestCreateKernelSession failed. rc=%d\n", rc));
1034 return NULL;
1035}
1036
1037
1038/**
1039 * Solaris Guest service close.
1040 *
1041 * @returns VBox error code.
1042 * @param pvState Opaque pointer to the session object.
1043 */
1044DECLVBGL(int) VBoxGuestSolarisServiceClose(void *pvSession)
1045{
1046 LogFlow((DEVICE_NAME ":VBoxGuestSolarisServiceClose\n"));
1047
1048 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
1049 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1050 if (pSession)
1051 {
1052 VBoxGuestCloseSession(&g_DevExt, pSession);
1053 return VINF_SUCCESS;
1054 }
1055 LogRel((DEVICE_NAME ":Invalid pSession.\n"));
1056 return VERR_INVALID_HANDLE;
1057}
1058
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