VirtualBox

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

Last change on this file since 68704 was 68638, checked in by vboxsync, 7 years ago

more doxygen fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.3 KB
Line 
1/* $Id: VBoxGuest-solaris.c 68638 2017-09-05 13:30:52Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions Driver for Solaris.
4 */
5
6/*
7 * Copyright (C) 2007-2016 Oracle Corporation
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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <sys/conf.h>
32#include <sys/modctl.h>
33#include <sys/mutex.h>
34#include <sys/pci.h>
35#include <sys/stat.h>
36#include <sys/ddi.h>
37#include <sys/ddi_intr.h>
38#include <sys/sunddi.h>
39#include <sys/open.h>
40#include <sys/sunldi.h>
41#include <sys/file.h>
42#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
43
44#include "VBoxGuestInternal.h"
45#include <VBox/log.h>
46#include <VBox/version.h>
47#include <iprt/assert.h>
48#include <iprt/initterm.h>
49#include <iprt/process.h>
50#include <iprt/mem.h>
51#include <iprt/cdefs.h>
52#include <iprt/asm.h>
53
54
55/*********************************************************************************************************************************
56* Defined Constants And Macros *
57*********************************************************************************************************************************/
58/** The module name. */
59#define DEVICE_NAME "vboxguest"
60/** The module description as seen in 'modinfo'. */
61#define DEVICE_DESC "VirtualBox GstDrv"
62
63
64/*********************************************************************************************************************************
65* Internal Functions *
66*********************************************************************************************************************************/
67static int vgdrvSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred);
68static int vgdrvSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred);
69static int vgdrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred);
70static int vgdrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred);
71static int vgdrvSolarisIOCtl(dev_t Dev, int iCmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal);
72static int vgdrvSolarisIOCtlSlow(PVBOXGUESTSESSION pSession, int iCmd, int Mode, intptr_t iArgs);
73static int vgdrvSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead);
74
75static int vgdrvSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
76static int vgdrvSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
77static int vgdrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
78static int vgdrvSolarisQuiesce(dev_info_t *pDip);
79
80static int vgdrvSolarisAddIRQ(dev_info_t *pDip);
81static void vgdrvSolarisRemoveIRQ(dev_info_t *pDip);
82static uint_t vgdrvSolarisHighLevelISR(caddr_t Arg);
83static uint_t vgdrvSolarisISR(caddr_t Arg);
84
85
86/*********************************************************************************************************************************
87* Structures and Typedefs *
88*********************************************************************************************************************************/
89/**
90 * cb_ops: for drivers that support char/block entry points
91 */
92static struct cb_ops g_vgdrvSolarisCbOps =
93{
94 vgdrvSolarisOpen,
95 vgdrvSolarisClose,
96 nodev, /* b strategy */
97 nodev, /* b dump */
98 nodev, /* b print */
99 vgdrvSolarisRead,
100 vgdrvSolarisWrite,
101 vgdrvSolarisIOCtl,
102 nodev, /* c devmap */
103 nodev, /* c mmap */
104 nodev, /* c segmap */
105 vgdrvSolarisPoll,
106 ddi_prop_op, /* property ops */
107 NULL, /* streamtab */
108 D_NEW | D_MP, /* compat. flag */
109 CB_REV /* revision */
110};
111
112/**
113 * dev_ops: for driver device operations
114 */
115static struct dev_ops g_vgdrvSolarisDevOps =
116{
117 DEVO_REV, /* driver build revision */
118 0, /* ref count */
119 vgdrvSolarisGetInfo,
120 nulldev, /* identify */
121 nulldev, /* probe */
122 vgdrvSolarisAttach,
123 vgdrvSolarisDetach,
124 nodev, /* reset */
125 &g_vgdrvSolarisCbOps,
126 (struct bus_ops *)0,
127 nodev, /* power */
128 vgdrvSolarisQuiesce
129};
130
131/**
132 * modldrv: export driver specifics to the kernel
133 */
134static struct modldrv g_vgdrvSolarisModule =
135{
136 &mod_driverops, /* extern from kernel */
137 DEVICE_DESC " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
138 &g_vgdrvSolarisDevOps
139};
140
141/**
142 * modlinkage: export install/remove/info to the kernel
143 */
144static struct modlinkage g_vgdrvSolarisModLinkage =
145{
146 MODREV_1, /* loadable module system revision */
147 &g_vgdrvSolarisModule,
148 NULL /* terminate array of linkage structures */
149};
150
151/**
152 * State info for each open file handle.
153 */
154typedef struct
155{
156 /** Pointer to the session handle. */
157 PVBOXGUESTSESSION pSession;
158 /** The process reference for posting signals */
159 void *pvProcRef;
160} vboxguest_state_t;
161
162
163/*********************************************************************************************************************************
164* Global Variables *
165*********************************************************************************************************************************/
166/** Device handle (we support only one instance). */
167static dev_info_t *g_pDip = NULL;
168/** Opaque pointer to file-descriptor states */
169static void *g_pvgdrvSolarisState = NULL;
170/** Device extention & session data association structure. */
171static VBOXGUESTDEVEXT g_DevExt;
172/** IO port handle. */
173static ddi_acc_handle_t g_PciIOHandle;
174/** MMIO handle. */
175static ddi_acc_handle_t g_PciMMIOHandle;
176/** IO Port. */
177static uint16_t g_uIOPortBase;
178/** Address of the MMIO region.*/
179static caddr_t g_pMMIOBase;
180/** Size of the MMIO region. */
181static off_t g_cbMMIO;
182/** Pointer to an array of interrupt handles. */
183static ddi_intr_handle_t *g_pahIntrs;
184/** Handle to the soft interrupt. */
185static ddi_softint_handle_t g_hSoftIntr;
186/** The pollhead structure */
187static pollhead_t g_PollHead;
188/** The IRQ Mutex */
189static kmutex_t g_IrqMtx;
190/** The IRQ high-level Mutex. */
191static kmutex_t g_HighLevelIrqMtx;
192/** Whether soft-ints are setup. */
193static bool g_fSoftIntRegistered = false;
194
195/**
196 * Kernel entry points
197 */
198int _init(void)
199{
200 /*
201 * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
202 */
203 int rc = RTR0Init(0);
204 if (RT_SUCCESS(rc))
205 {
206 PRTLOGGER pRelLogger;
207 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
208 rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all",
209 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
210 RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER, NULL);
211 if (RT_SUCCESS(rc))
212 RTLogRelSetDefaultInstance(pRelLogger);
213 else
214 cmn_err(CE_NOTE, "failed to initialize driver logging rc=%d!\n", rc);
215
216 /*
217 * Prevent module autounloading.
218 */
219 modctl_t *pModCtl = mod_getctl(&g_vgdrvSolarisModLinkage);
220 if (pModCtl)
221 pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
222 else
223 LogRel((DEVICE_NAME ": failed to disable autounloading!\n"));
224
225 rc = ddi_soft_state_init(&g_pvgdrvSolarisState, sizeof(vboxguest_state_t), 1);
226 if (!rc)
227 {
228 rc = mod_install(&g_vgdrvSolarisModLinkage);
229 if (rc)
230 ddi_soft_state_fini(&g_pvgdrvSolarisState);
231 }
232 }
233 else
234 {
235 cmn_err(CE_NOTE, "_init: RTR0Init failed. rc=%d\n", rc);
236 return EINVAL;
237 }
238
239 return rc;
240}
241
242
243int _fini(void)
244{
245 LogFlow((DEVICE_NAME ":_fini\n"));
246 int rc = mod_remove(&g_vgdrvSolarisModLinkage);
247 if (!rc)
248 ddi_soft_state_fini(&g_pvgdrvSolarisState);
249
250 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
251 RTLogDestroy(RTLogSetDefaultInstance(NULL));
252
253 if (!rc)
254 RTR0Term();
255 return rc;
256}
257
258
259int _info(struct modinfo *pModInfo)
260{
261 LogFlow((DEVICE_NAME ":_info\n"));
262 return mod_info(&g_vgdrvSolarisModLinkage, pModInfo);
263}
264
265
266/**
267 * Attach entry point, to attach a device to the system or resume it.
268 *
269 * @param pDip The module structure instance.
270 * @param enmCmd Attach type (ddi_attach_cmd_t)
271 *
272 * @return corresponding solaris error code.
273 */
274static int vgdrvSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
275{
276 LogFlow(("vgdrvSolarisAttach:\n"));
277 switch (enmCmd)
278 {
279 case DDI_ATTACH:
280 {
281 if (g_pDip)
282 {
283 LogRel(("vgdrvSolarisAttach: Only one instance supported.\n"));
284 return DDI_FAILURE;
285 }
286
287 int instance = ddi_get_instance(pDip);
288
289 /*
290 * Enable resources for PCI access.
291 */
292 ddi_acc_handle_t PciHandle;
293 int rc = pci_config_setup(pDip, &PciHandle);
294 if (rc == DDI_SUCCESS)
295 {
296 /*
297 * Map the register address space.
298 */
299 caddr_t baseAddr;
300 ddi_device_acc_attr_t deviceAttr;
301 deviceAttr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
302 deviceAttr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
303 deviceAttr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
304 deviceAttr.devacc_attr_access = DDI_DEFAULT_ACC;
305 rc = ddi_regs_map_setup(pDip, 1, &baseAddr, 0, 0, &deviceAttr, &g_PciIOHandle);
306 if (rc == DDI_SUCCESS)
307 {
308 /*
309 * Read size of the MMIO region.
310 */
311 g_uIOPortBase = (uintptr_t)baseAddr;
312 rc = ddi_dev_regsize(pDip, 2, &g_cbMMIO);
313 if (rc == DDI_SUCCESS)
314 {
315 rc = ddi_regs_map_setup(pDip, 2, &g_pMMIOBase, 0, g_cbMMIO, &deviceAttr,
316 &g_PciMMIOHandle);
317 if (rc == DDI_SUCCESS)
318 {
319 /*
320 * Add IRQ of VMMDev.
321 */
322 rc = vgdrvSolarisAddIRQ(pDip);
323 if (rc == DDI_SUCCESS)
324 {
325 /*
326 * Call the common device extension initializer.
327 */
328 rc = VGDrvCommonInitDevExt(&g_DevExt, g_uIOPortBase, g_pMMIOBase, g_cbMMIO,
329#if ARCH_BITS == 64
330 VBOXOSTYPE_Solaris_x64,
331#else
332 VBOXOSTYPE_Solaris,
333#endif
334 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
335 if (RT_SUCCESS(rc))
336 {
337 rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, 0 /* fFlags */);
338 if (rc == DDI_SUCCESS)
339 {
340 g_pDip = pDip;
341 pci_config_teardown(&PciHandle);
342 return DDI_SUCCESS;
343 }
344
345 LogRel((DEVICE_NAME "::Attach: ddi_create_minor_node failed.\n"));
346 VGDrvCommonDeleteDevExt(&g_DevExt);
347 }
348 else
349 LogRel((DEVICE_NAME "::Attach: VGDrvCommonInitDevExt failed.\n"));
350
351 vgdrvSolarisRemoveIRQ(pDip);
352 }
353 else
354 LogRel((DEVICE_NAME "::Attach: vgdrvSolarisAddIRQ failed.\n"));
355 ddi_regs_map_free(&g_PciMMIOHandle);
356 }
357 else
358 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for MMIO region failed.\n"));
359 }
360 else
361 LogRel((DEVICE_NAME "::Attach: ddi_dev_regsize for MMIO region failed.\n"));
362 ddi_regs_map_free(&g_PciIOHandle);
363 }
364 else
365 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for IOport failed.\n"));
366 pci_config_teardown(&PciHandle);
367 }
368 else
369 LogRel((DEVICE_NAME "::Attach: pci_config_setup failed rc=%d.\n", rc));
370 return DDI_FAILURE;
371 }
372
373 case DDI_RESUME:
374 {
375 /** @todo implement resume for guest driver. */
376 return DDI_SUCCESS;
377 }
378
379 default:
380 return DDI_FAILURE;
381 }
382}
383
384
385/**
386 * Detach entry point, to detach a device to the system or suspend it.
387 *
388 * @param pDip The module structure instance.
389 * @param enmCmd Attach type (ddi_attach_cmd_t)
390 *
391 * @return corresponding solaris error code.
392 */
393static int vgdrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
394{
395 LogFlow(("vgdrvSolarisDetach:\n"));
396 switch (enmCmd)
397 {
398 case DDI_DETACH:
399 {
400 vgdrvSolarisRemoveIRQ(pDip);
401 ddi_regs_map_free(&g_PciIOHandle);
402 ddi_regs_map_free(&g_PciMMIOHandle);
403 ddi_remove_minor_node(pDip, NULL);
404 VGDrvCommonDeleteDevExt(&g_DevExt);
405 g_pDip = NULL;
406 return DDI_SUCCESS;
407 }
408
409 case DDI_SUSPEND:
410 {
411 /** @todo implement suspend for guest driver. */
412 return DDI_SUCCESS;
413 }
414
415 default:
416 return DDI_FAILURE;
417 }
418}
419
420
421/**
422 * Quiesce entry point, called by solaris kernel for disabling the device from
423 * generating any interrupts or doing in-bound DMA.
424 *
425 * @param pDip The module structure instance.
426 *
427 * @return corresponding solaris error code.
428 */
429static int vgdrvSolarisQuiesce(dev_info_t *pDip)
430{
431 int rc = ddi_intr_disable(g_pahIntrs[0]);
432 if (rc != DDI_SUCCESS)
433 return DDI_FAILURE;
434
435 /** @todo What about HGCM/HGSMI touching guest-memory? */
436
437 return DDI_SUCCESS;
438}
439
440
441/**
442 * Info entry point, called by solaris kernel for obtaining driver info.
443 *
444 * @param pDip The module structure instance (do not use).
445 * @param enmCmd Information request type.
446 * @param pvArg Type specific argument.
447 * @param ppvResult Where to store the requested info.
448 *
449 * @return corresponding solaris error code.
450 */
451static int vgdrvSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
452{
453 LogFlow(("vgdrvSolarisGetInfo:\n"));
454
455 int rc = DDI_SUCCESS;
456 switch (enmCmd)
457 {
458 case DDI_INFO_DEVT2DEVINFO:
459 *ppvResult = (void *)g_pDip;
460 break;
461
462 case DDI_INFO_DEVT2INSTANCE:
463 *ppvResult = (void *)(uintptr_t)ddi_get_instance(g_pDip);
464 break;
465
466 default:
467 rc = DDI_FAILURE;
468 break;
469 }
470
471 NOREF(pvArg);
472 return rc;
473}
474
475
476/**
477 * User context entry points
478 */
479static int vgdrvSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred)
480{
481 int rc;
482 PVBOXGUESTSESSION pSession = NULL;
483
484 LogFlow(("vgdrvSolarisOpen:\n"));
485
486 /*
487 * Verify we are being opened as a character device.
488 */
489 if (fType != OTYP_CHR)
490 return EINVAL;
491
492 vboxguest_state_t *pState = NULL;
493 unsigned iOpenInstance;
494 for (iOpenInstance = 0; iOpenInstance < 4096; iOpenInstance++)
495 {
496 if ( !ddi_get_soft_state(g_pvgdrvSolarisState, iOpenInstance) /* faster */
497 && ddi_soft_state_zalloc(g_pvgdrvSolarisState, iOpenInstance) == DDI_SUCCESS)
498 {
499 pState = ddi_get_soft_state(g_pvgdrvSolarisState, iOpenInstance);
500 break;
501 }
502 }
503 if (!pState)
504 {
505 Log(("vgdrvSolarisOpen: too many open instances."));
506 return ENXIO;
507 }
508
509 /*
510 * Create a new session.
511 */
512 rc = VGDrvCommonCreateUserSession(&g_DevExt, &pSession);
513 if (RT_SUCCESS(rc))
514 {
515 pState->pvProcRef = proc_ref();
516 pState->pSession = pSession;
517 *pDev = makedevice(getmajor(*pDev), iOpenInstance);
518 Log(("vgdrvSolarisOpen: pSession=%p pState=%p pid=%d\n", pSession, pState, (int)RTProcSelf()));
519 return 0;
520 }
521
522 /* Failed, clean up. */
523 ddi_soft_state_free(g_pvgdrvSolarisState, iOpenInstance);
524
525 LogRel((DEVICE_NAME "::Open: VGDrvCommonCreateUserSession failed. rc=%d\n", rc));
526 return EFAULT;
527}
528
529
530static int vgdrvSolarisClose(dev_t Dev, int flag, int fType, cred_t *pCred)
531{
532 LogFlow(("vgdrvSolarisClose: pid=%d\n", (int)RTProcSelf()));
533
534 PVBOXGUESTSESSION pSession = NULL;
535 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
536 if (!pState)
537 {
538 Log(("vgdrvSolarisClose: failed to get pState.\n"));
539 return EFAULT;
540 }
541
542 proc_unref(pState->pvProcRef);
543 pSession = pState->pSession;
544 pState->pSession = NULL;
545 Log(("vgdrvSolarisClose: pSession=%p pState=%p\n", pSession, pState));
546 ddi_soft_state_free(g_pvgdrvSolarisState, getminor(Dev));
547 if (!pSession)
548 {
549 Log(("vgdrvSolarisClose: failed to get pSession.\n"));
550 return EFAULT;
551 }
552
553 /*
554 * Close the session.
555 */
556 VGDrvCommonCloseSession(&g_DevExt, pSession);
557 return 0;
558}
559
560
561static int vgdrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
562{
563 LogFlow((DEVICE_NAME "::Read\n"));
564
565 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
566 if (!pState)
567 {
568 Log((DEVICE_NAME "::Close: failed to get pState.\n"));
569 return EFAULT;
570 }
571
572 PVBOXGUESTSESSION pSession = pState->pSession;
573 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
574 if (pSession->u32MousePosChangedSeq != u32CurSeq)
575 pSession->u32MousePosChangedSeq = u32CurSeq;
576
577 return 0;
578}
579
580
581static int vgdrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
582{
583 LogFlow(("vgdrvSolarisWrite:\n"));
584 return 0;
585}
586
587
588/**
589 * Driver ioctl, an alternate entry point for this character driver.
590 *
591 * @param Dev Device number
592 * @param iCmd Operation identifier
593 * @param iArgs Arguments from user to driver
594 * @param Mode Information bitfield (read/write, address space etc.)
595 * @param pCred User credentials
596 * @param pVal Return value for calling process.
597 *
598 * @return corresponding solaris error code.
599 */
600static int vgdrvSolarisIOCtl(dev_t Dev, int iCmd, intptr_t iArgs, int Mode, cred_t *pCred, int *pVal)
601{
602 /*
603 * Get the session from the soft state item.
604 */
605 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
606 if (!pState)
607 {
608 LogRel(("vgdrvSolarisIOCtl: no state data for %#x (%d)\n", Dev, getminor(Dev)));
609 return EINVAL;
610 }
611
612 PVBOXGUESTSESSION pSession = pState->pSession;
613 if (!pSession)
614 {
615 LogRel(("vgdrvSolarisIOCtl: no session in state data for %#x (%d)\n", Dev, getminor(Dev)));
616 return DDI_SUCCESS;
617 }
618
619 /*
620 * Deal with fast requests.
621 */
622 if (VBGL_IOCTL_IS_FAST(iCmd))
623 {
624 *pVal = VGDrvCommonIoCtlFast(iCmd, &g_DevExt, pSession);
625 return 0;
626 }
627
628 return vgdrvSolarisIOCtlSlow(pSession, iCmd, Mode, iArgs);
629}
630
631
632/** @def IOCPARM_LEN
633 * Gets the length from the ioctl number.
634 * This is normally defined by sys/ioccom.h on BSD systems...
635 */
636#ifndef IOCPARM_LEN
637# define IOCPARM_LEN(x) ( ((x) >> 16) & IOCPARM_MASK )
638#endif
639
640
641/**
642 * Worker for VBoxSupDrvIOCtl that takes the slow IOCtl functions.
643 *
644 * @returns Solaris errno.
645 *
646 * @param pSession The session.
647 * @param iCmd The IOCtl command.
648 * @param Mode Information bitfield (for specifying ownership of data)
649 * @param iArg User space address of the request buffer.
650 */
651static int vgdrvSolarisIOCtlSlow(PVBOXGUESTSESSION pSession, int iCmd, int Mode, intptr_t iArg)
652{
653 int rc;
654 uint32_t cbBuf = 0;
655 union
656 {
657 VBGLREQHDR Hdr;
658 uint8_t abBuf[64];
659 } StackBuf;
660 PVBGLREQHDR pHdr;
661
662
663 /*
664 * Read the header.
665 */
666 if (RT_UNLIKELY(IOCPARM_LEN(iCmd) != sizeof(StackBuf.Hdr)))
667 {
668 LogRel(("vgdrvSolarisIOCtlSlow: iCmd=%#x len %d expected %d\n", iCmd, IOCPARM_LEN(iCmd), sizeof(StackBuf.Hdr)));
669 return EINVAL;
670 }
671 rc = ddi_copyin((void *)iArg, &StackBuf.Hdr, sizeof(StackBuf.Hdr), Mode);
672 if (RT_UNLIKELY(rc))
673 {
674 LogRel(("vgdrvSolarisIOCtlSlow: ddi_copyin(,%#lx,) failed; iCmd=%#x. rc=%d\n", iArg, iCmd, rc));
675 return EFAULT;
676 }
677 if (RT_UNLIKELY(StackBuf.Hdr.uVersion != VBGLREQHDR_VERSION))
678 {
679 LogRel(("vgdrvSolarisIOCtlSlow: bad header version %#x; iCmd=%#x\n", StackBuf.Hdr.uVersion, iCmd));
680 return EINVAL;
681 }
682 cbBuf = RT_MAX(StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut);
683 if (RT_UNLIKELY( StackBuf.Hdr.cbIn < sizeof(StackBuf.Hdr)
684 || (StackBuf.Hdr.cbOut < sizeof(StackBuf.Hdr) && StackBuf.Hdr.cbOut != 0)
685 || cbBuf > _1M*16))
686 {
687 LogRel(("vgdrvSolarisIOCtlSlow: max(%#x,%#x); iCmd=%#x\n", StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut, iCmd));
688 return EINVAL;
689 }
690
691 /*
692 * Buffer the request.
693 *
694 * Note! Common code revalidates the header sizes and version. So it's
695 * fine to read it once more.
696 */
697 if (cbBuf <= sizeof(StackBuf))
698 pHdr = &StackBuf.Hdr;
699 else
700 {
701 pHdr = RTMemTmpAlloc(cbBuf);
702 if (RT_UNLIKELY(!pHdr))
703 {
704 LogRel(("vgdrvSolarisIOCtlSlow: failed to allocate buffer of %d bytes for iCmd=%#x.\n", cbBuf, iCmd));
705 return ENOMEM;
706 }
707 }
708 rc = ddi_copyin((void *)iArg, pHdr, cbBuf, Mode);
709 if (RT_UNLIKELY(rc))
710 {
711 LogRel(("vgdrvSolarisIOCtlSlow: copy_from_user(,%#lx, %#x) failed; iCmd=%#x. rc=%d\n", iArg, cbBuf, iCmd, rc));
712 if (pHdr != &StackBuf.Hdr)
713 RTMemFree(pHdr);
714 return EFAULT;
715 }
716
717 /*
718 * Process the IOCtl.
719 */
720 rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, cbBuf);
721
722 /*
723 * Copy ioctl data and output buffer back to user space.
724 */
725 if (RT_SUCCESS(rc))
726 {
727 uint32_t cbOut = pHdr->cbOut;
728 if (RT_UNLIKELY(cbOut > cbBuf))
729 {
730 LogRel(("vgdrvSolarisIOCtlSlow: too much output! %#x > %#x; iCmd=%#x!\n", cbOut, cbBuf, iCmd));
731 cbOut = cbBuf;
732 }
733 rc = ddi_copyout(pHdr, (void *)iArg, cbOut, Mode);
734 if (RT_UNLIKELY(rc != 0))
735 {
736 /* this is really bad */
737 LogRel(("vgdrvSolarisIOCtlSlow: ddi_copyout(,%p,%d) failed. rc=%d\n", (void *)iArg, cbBuf, rc));
738 rc = EFAULT;
739 }
740 }
741 else
742 rc = EINVAL;
743
744 if (pHdr != &StackBuf.Hdr)
745 RTMemTmpFree(pHdr);
746 return rc;
747}
748
749
750/**
751 * @note This code is duplicated on other platforms with variations, so please
752 * keep them all up to date when making changes!
753 */
754int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
755{
756 /*
757 * Simple request validation (common code does the rest).
758 */
759 int rc;
760 if ( RT_VALID_PTR(pReqHdr)
761 && cbReq >= sizeof(*pReqHdr))
762 {
763 /*
764 * All requests except the connect one requires a valid session.
765 */
766 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
767 if (pSession)
768 {
769 if ( RT_VALID_PTR(pSession)
770 && pSession->pDevExt == &g_DevExt)
771 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
772 else
773 rc = VERR_INVALID_HANDLE;
774 }
775 else if (uReq == VBGL_IOCTL_IDC_CONNECT)
776 {
777 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
778 if (RT_SUCCESS(rc))
779 {
780 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
781 if (RT_FAILURE(rc))
782 VGDrvCommonCloseSession(&g_DevExt, pSession);
783 }
784 }
785 else
786 rc = VERR_INVALID_HANDLE;
787 }
788 else
789 rc = VERR_INVALID_POINTER;
790 return rc;
791}
792
793
794
795static int vgdrvSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead)
796{
797 LogFlow(("vgdrvSolarisPoll: fEvents=%d fAnyYet=%d\n", fEvents, fAnyYet));
798
799 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
800 if (RT_LIKELY(pState))
801 {
802 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pState->pSession;
803 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
804 if (pSession->u32MousePosChangedSeq != u32CurSeq)
805 {
806 *pReqEvents |= (POLLIN | POLLRDNORM);
807 pSession->u32MousePosChangedSeq = u32CurSeq;
808 }
809 else
810 {
811 *pReqEvents = 0;
812 if (!fAnyYet)
813 *ppPollHead = &g_PollHead;
814 }
815
816 return 0;
817 }
818
819 Log(("vgdrvSolarisPoll: no state data for %d\n", getminor(Dev)));
820 return EINVAL;
821}
822
823
824/**
825 * Sets IRQ for VMMDev.
826 *
827 * @returns Solaris error code.
828 * @param pDip Pointer to the device info structure.
829 */
830static int vgdrvSolarisAddIRQ(dev_info_t *pDip)
831{
832 LogFlow(("vgdrvSolarisAddIRQ: pDip=%p\n", pDip));
833
834 /* Get the types of interrupt supported for this hardware. */
835 int fIntrType = 0;
836 int rc = ddi_intr_get_supported_types(pDip, &fIntrType);
837 if (rc == DDI_SUCCESS)
838 {
839 /* We only support fixed interrupts at this point, not MSIs. */
840 if (fIntrType & DDI_INTR_TYPE_FIXED)
841 {
842 /* Verify the number of interrupts supported by this device. There can only be one fixed interrupt. */
843 int cIntrCount = 0;
844 rc = ddi_intr_get_nintrs(pDip, fIntrType, &cIntrCount);
845 if ( rc == DDI_SUCCESS
846 && cIntrCount == 1)
847 {
848 /* Allocated kernel memory for the interrupt handle. The allocation size is stored internally. */
849 g_pahIntrs = RTMemAllocZ(cIntrCount * sizeof(ddi_intr_handle_t));
850 if (g_pahIntrs)
851 {
852 /* Allocate the interrupt for this device and verify the allocation. */
853 int cIntrAllocated;
854 rc = ddi_intr_alloc(pDip, g_pahIntrs, fIntrType, 0 /* interrupt number */, cIntrCount, &cIntrAllocated,
855 DDI_INTR_ALLOC_NORMAL);
856 if ( rc == DDI_SUCCESS
857 && cIntrAllocated == 1)
858 {
859 /* Get the interrupt priority assigned by the system. */
860 uint_t uIntrPriority;
861 rc = ddi_intr_get_pri(g_pahIntrs[0], &uIntrPriority);
862 if (rc == DDI_SUCCESS)
863 {
864 /* Check if the interrupt priority is scheduler level or above, if so we need to use a high-level
865 and low-level interrupt handlers with corresponding mutexes. */
866 cmn_err(CE_CONT, "!vboxguest: uIntrPriority=%d hilevel_pri=%d\n", uIntrPriority, ddi_intr_get_hilevel_pri());
867 if (uIntrPriority >= ddi_intr_get_hilevel_pri())
868 {
869 /* Initialize the high-level mutex. */
870 mutex_init(&g_HighLevelIrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
871
872 /* Assign interrupt handler function to the interrupt handle. */
873 rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)&vgdrvSolarisHighLevelISR,
874 NULL /* pvArg1 */, NULL /* pvArg2 */);
875
876 if (rc == DDI_SUCCESS)
877 {
878 /* Add the low-level interrupt handler. */
879 rc = ddi_intr_add_softint(pDip, &g_hSoftIntr, DDI_INTR_SOFTPRI_MAX,
880 (ddi_intr_handler_t *)&vgdrvSolarisISR, NULL /* pvArg1 */);
881 if (rc == DDI_SUCCESS)
882 {
883 /* Initialize the low-level mutex at the corresponding level. */
884 mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER,
885 DDI_INTR_PRI(DDI_INTR_SOFTPRI_MAX));
886
887 g_fSoftIntRegistered = true;
888 /* Enable the high-level interrupt. */
889 rc = ddi_intr_enable(g_pahIntrs[0]);
890 if (rc == DDI_SUCCESS)
891 return rc;
892
893 LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc));
894 mutex_destroy(&g_IrqMtx);
895 }
896 else
897 LogRel((DEVICE_NAME "::AddIRQ: failed to add soft interrupt handler. rc=%d\n", rc));
898
899 ddi_intr_remove_handler(g_pahIntrs[0]);
900 }
901 else
902 LogRel((DEVICE_NAME "::AddIRQ: failed to add high-level interrupt handler. rc=%d\n", rc));
903
904 mutex_destroy(&g_HighLevelIrqMtx);
905 }
906 else
907 {
908 /* Interrupt handler runs at reschedulable level, initialize the mutex at the given priority. */
909 mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
910
911 /* Assign interrupt handler function to the interrupt handle. */
912 rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)vgdrvSolarisISR,
913 NULL /* pvArg1 */, NULL /* pvArg2 */);
914 if (rc == DDI_SUCCESS)
915 {
916 /* Enable the interrupt. */
917 rc = ddi_intr_enable(g_pahIntrs[0]);
918 if (rc == DDI_SUCCESS)
919 return rc;
920
921 LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc));
922 mutex_destroy(&g_IrqMtx);
923 }
924 }
925 }
926 else
927 LogRel((DEVICE_NAME "::AddIRQ: failed to get priority of interrupt. rc=%d\n", rc));
928
929 Assert(cIntrAllocated == 1);
930 ddi_intr_free(g_pahIntrs[0]);
931 }
932 else
933 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount));
934 RTMemFree(g_pahIntrs);
935 }
936 else
937 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount));
938 }
939 else
940 LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient number of IRQs. rc=%d cIntrCount=%d\n", rc, cIntrCount));
941 }
942 else
943 LogRel((DEVICE_NAME "::AddIRQ: fixed-type interrupts not supported. IntrType=%#x\n", fIntrType));
944 }
945 else
946 LogRel((DEVICE_NAME "::AddIRQ: failed to get supported interrupt types. rc=%d\n", rc));
947 return rc;
948}
949
950
951/**
952 * Removes IRQ for VMMDev.
953 *
954 * @param pDip Pointer to the device info structure.
955 */
956static void vgdrvSolarisRemoveIRQ(dev_info_t *pDip)
957{
958 LogFlow(("vgdrvSolarisRemoveIRQ:\n"));
959
960 int rc = ddi_intr_disable(g_pahIntrs[0]);
961 if (rc == DDI_SUCCESS)
962 {
963 rc = ddi_intr_remove_handler(g_pahIntrs[0]);
964 if (rc == DDI_SUCCESS)
965 ddi_intr_free(g_pahIntrs[0]);
966 }
967
968 if (g_fSoftIntRegistered)
969 {
970 ddi_intr_remove_softint(g_hSoftIntr);
971 mutex_destroy(&g_HighLevelIrqMtx);
972 g_fSoftIntRegistered = false;
973 }
974
975 mutex_destroy(&g_IrqMtx);
976 RTMemFree(g_pahIntrs);
977}
978
979
980/**
981 * High-level Interrupt Service Routine for VMMDev.
982 *
983 * This routine simply dispatches a soft-interrupt at an acceptable IPL as
984 * VGDrvCommonISR() cannot be called at a high IPL (scheduler level or higher)
985 * due to pollwakeup() in VGDrvNativeISRMousePollEvent().
986 *
987 * @param Arg Private data (unused, will be NULL).
988 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
989 */
990static uint_t vgdrvSolarisHighLevelISR(caddr_t Arg)
991{
992 bool const fOurIrq = VGDrvCommonIsOurIRQ(&g_DevExt);
993 if (fOurIrq)
994 {
995 ddi_intr_trigger_softint(g_hSoftIntr, NULL /* Arg */);
996 return DDI_INTR_CLAIMED;
997 }
998 return DDI_INTR_UNCLAIMED;
999}
1000
1001
1002/**
1003 * Interrupt Service Routine for VMMDev.
1004 *
1005 * @param Arg Private data (unused, will be NULL).
1006 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
1007 */
1008static uint_t vgdrvSolarisISR(caddr_t Arg)
1009{
1010 LogFlow(("vgdrvSolarisISR:\n"));
1011
1012 /* The mutex is required to protect against parallel executions (if possible?) and also the
1013 mouse notify registeration race between VGDrvNativeSetMouseNotifyCallback() and VGDrvCommonISR(). */
1014 mutex_enter(&g_IrqMtx);
1015 bool fOurIRQ = VGDrvCommonISR(&g_DevExt);
1016 mutex_exit(&g_IrqMtx);
1017
1018 return fOurIRQ ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED;
1019}
1020
1021
1022/**
1023 * Poll notifier for mouse poll events.
1024 *
1025 * @param pDevExt Pointer to the device extension.
1026 *
1027 * @remarks This must be called without holding any spinlocks.
1028 */
1029void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1030{
1031 LogFlow(("VGDrvNativeISRMousePollEvent:\n"));
1032
1033 /*
1034 * Wake up poll waiters.
1035 */
1036 pollwakeup(&g_PollHead, POLLIN | POLLRDNORM);
1037}
1038
1039
1040/**
1041 * Sets the mouse notification callback.
1042 *
1043 * @returns VBox status code.
1044 * @param pDevExt Pointer to the device extension.
1045 * @param pNotify Pointer to the mouse notify struct.
1046 */
1047int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
1048{
1049 /* Take the mutex here so as to not race with VGDrvCommonISR() which invokes the mouse notify callback. */
1050 mutex_enter(&g_IrqMtx);
1051 pDevExt->pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
1052 pDevExt->pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
1053 mutex_exit(&g_IrqMtx);
1054 return VINF_SUCCESS;
1055}
1056
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