VirtualBox

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

Last change on this file since 70582 was 70088, checked in by vboxsync, 7 years ago

VBoxGuest: Enabled option reading from the host on all platforms.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.1 KB
Line 
1/* $Id: VBoxGuest-solaris.c 70088 2017-12-12 17:38:54Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions Driver for Solaris.
4 */
5
6/*
7 * Copyright (C) 2007-2017 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/** Additional IPRT function we need to drag in for vboxfs. */
196PFNRT g_Deps[] =
197{
198 (PFNRT)RTErrConvertToErrno,
199};
200
201
202/**
203 * Kernel entry points
204 */
205int _init(void)
206{
207 /*
208 * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
209 */
210 int rc = RTR0Init(0);
211 if (RT_SUCCESS(rc))
212 {
213 PRTLOGGER pRelLogger;
214 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
215 rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all",
216 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
217 RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER, NULL);
218 if (RT_SUCCESS(rc))
219 RTLogRelSetDefaultInstance(pRelLogger);
220 else
221 cmn_err(CE_NOTE, "failed to initialize driver logging rc=%d!\n", rc);
222
223 /*
224 * Prevent module autounloading.
225 */
226 modctl_t *pModCtl = mod_getctl(&g_vgdrvSolarisModLinkage);
227 if (pModCtl)
228 pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
229 else
230 LogRel((DEVICE_NAME ": failed to disable autounloading!\n"));
231
232 rc = ddi_soft_state_init(&g_pvgdrvSolarisState, sizeof(vboxguest_state_t), 1);
233 if (!rc)
234 {
235 rc = mod_install(&g_vgdrvSolarisModLinkage);
236 if (rc)
237 ddi_soft_state_fini(&g_pvgdrvSolarisState);
238 }
239 }
240 else
241 {
242 cmn_err(CE_NOTE, "_init: RTR0Init failed. rc=%d\n", rc);
243 return EINVAL;
244 }
245
246 return rc;
247}
248
249
250int _fini(void)
251{
252 LogFlow((DEVICE_NAME ":_fini\n"));
253 int rc = mod_remove(&g_vgdrvSolarisModLinkage);
254 if (!rc)
255 ddi_soft_state_fini(&g_pvgdrvSolarisState);
256
257 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
258 RTLogDestroy(RTLogSetDefaultInstance(NULL));
259
260 if (!rc)
261 RTR0Term();
262 return rc;
263}
264
265
266int _info(struct modinfo *pModInfo)
267{
268 /* LogFlow((DEVICE_NAME ":_info\n")); - Called too early, causing RTThreadPreemtIsEnabled warning. */
269 return mod_info(&g_vgdrvSolarisModLinkage, pModInfo);
270}
271
272
273/**
274 * Attach entry point, to attach a device to the system or resume it.
275 *
276 * @param pDip The module structure instance.
277 * @param enmCmd Attach type (ddi_attach_cmd_t)
278 *
279 * @return corresponding solaris error code.
280 */
281static int vgdrvSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
282{
283 LogFlow(("vgdrvSolarisAttach:\n"));
284 switch (enmCmd)
285 {
286 case DDI_ATTACH:
287 {
288 if (g_pDip)
289 {
290 LogRel(("vgdrvSolarisAttach: Only one instance supported.\n"));
291 return DDI_FAILURE;
292 }
293
294 int instance = ddi_get_instance(pDip);
295
296 /*
297 * Enable resources for PCI access.
298 */
299 ddi_acc_handle_t PciHandle;
300 int rc = pci_config_setup(pDip, &PciHandle);
301 if (rc == DDI_SUCCESS)
302 {
303 /*
304 * Map the register address space.
305 */
306 caddr_t baseAddr;
307 ddi_device_acc_attr_t deviceAttr;
308 deviceAttr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
309 deviceAttr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
310 deviceAttr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
311 deviceAttr.devacc_attr_access = DDI_DEFAULT_ACC;
312 rc = ddi_regs_map_setup(pDip, 1, &baseAddr, 0, 0, &deviceAttr, &g_PciIOHandle);
313 if (rc == DDI_SUCCESS)
314 {
315 /*
316 * Read size of the MMIO region.
317 */
318 g_uIOPortBase = (uintptr_t)baseAddr;
319 rc = ddi_dev_regsize(pDip, 2, &g_cbMMIO);
320 if (rc == DDI_SUCCESS)
321 {
322 rc = ddi_regs_map_setup(pDip, 2, &g_pMMIOBase, 0, g_cbMMIO, &deviceAttr, &g_PciMMIOHandle);
323 if (rc == DDI_SUCCESS)
324 {
325 /*
326 * Add IRQ of VMMDev.
327 */
328 rc = vgdrvSolarisAddIRQ(pDip);
329 if (rc == DDI_SUCCESS)
330 {
331 /*
332 * Call the common device extension initializer.
333 */
334 rc = VGDrvCommonInitDevExt(&g_DevExt, g_uIOPortBase, g_pMMIOBase, g_cbMMIO,
335#if ARCH_BITS == 64
336 VBOXOSTYPE_Solaris_x64,
337#else
338 VBOXOSTYPE_Solaris,
339#endif
340 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
341 if (RT_SUCCESS(rc))
342 {
343 /*
344 * Read host configuration.
345 */
346 VGDrvCommonProcessOptionsFromHost(&g_DevExt);
347
348
349 rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, 0 /* fFlags */);
350 if (rc == DDI_SUCCESS)
351 {
352 g_pDip = pDip;
353 pci_config_teardown(&PciHandle);
354 return DDI_SUCCESS;
355 }
356
357 LogRel((DEVICE_NAME "::Attach: ddi_create_minor_node failed.\n"));
358 VGDrvCommonDeleteDevExt(&g_DevExt);
359 }
360 else
361 LogRel((DEVICE_NAME "::Attach: VGDrvCommonInitDevExt failed.\n"));
362
363 vgdrvSolarisRemoveIRQ(pDip);
364 }
365 else
366 LogRel((DEVICE_NAME "::Attach: vgdrvSolarisAddIRQ failed.\n"));
367 ddi_regs_map_free(&g_PciMMIOHandle);
368 }
369 else
370 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for MMIO region failed.\n"));
371 }
372 else
373 LogRel((DEVICE_NAME "::Attach: ddi_dev_regsize for MMIO region failed.\n"));
374 ddi_regs_map_free(&g_PciIOHandle);
375 }
376 else
377 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for IOport failed.\n"));
378 pci_config_teardown(&PciHandle);
379 }
380 else
381 LogRel((DEVICE_NAME "::Attach: pci_config_setup failed rc=%d.\n", rc));
382 return DDI_FAILURE;
383 }
384
385 case DDI_RESUME:
386 {
387 /** @todo implement resume for guest driver. */
388 return DDI_SUCCESS;
389 }
390
391 default:
392 return DDI_FAILURE;
393 }
394}
395
396
397/**
398 * Detach entry point, to detach a device to the system or suspend it.
399 *
400 * @param pDip The module structure instance.
401 * @param enmCmd Attach type (ddi_attach_cmd_t)
402 *
403 * @return corresponding solaris error code.
404 */
405static int vgdrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
406{
407 LogFlow(("vgdrvSolarisDetach:\n"));
408 switch (enmCmd)
409 {
410 case DDI_DETACH:
411 {
412 vgdrvSolarisRemoveIRQ(pDip);
413 ddi_regs_map_free(&g_PciIOHandle);
414 ddi_regs_map_free(&g_PciMMIOHandle);
415 ddi_remove_minor_node(pDip, NULL);
416 VGDrvCommonDeleteDevExt(&g_DevExt);
417 g_pDip = NULL;
418 return DDI_SUCCESS;
419 }
420
421 case DDI_SUSPEND:
422 {
423 /** @todo implement suspend for guest driver. */
424 return DDI_SUCCESS;
425 }
426
427 default:
428 return DDI_FAILURE;
429 }
430}
431
432
433/**
434 * Quiesce entry point, called by solaris kernel for disabling the device from
435 * generating any interrupts or doing in-bound DMA.
436 *
437 * @param pDip The module structure instance.
438 *
439 * @return corresponding solaris error code.
440 */
441static int vgdrvSolarisQuiesce(dev_info_t *pDip)
442{
443 int rc = ddi_intr_disable(g_pahIntrs[0]);
444 if (rc != DDI_SUCCESS)
445 return DDI_FAILURE;
446
447 /** @todo What about HGCM/HGSMI touching guest-memory? */
448
449 return DDI_SUCCESS;
450}
451
452
453/**
454 * Info entry point, called by solaris kernel for obtaining driver info.
455 *
456 * @param pDip The module structure instance (do not use).
457 * @param enmCmd Information request type.
458 * @param pvArg Type specific argument.
459 * @param ppvResult Where to store the requested info.
460 *
461 * @return corresponding solaris error code.
462 */
463static int vgdrvSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
464{
465 LogFlow(("vgdrvSolarisGetInfo:\n"));
466
467 int rc = DDI_SUCCESS;
468 switch (enmCmd)
469 {
470 case DDI_INFO_DEVT2DEVINFO:
471 *ppvResult = (void *)g_pDip;
472 break;
473
474 case DDI_INFO_DEVT2INSTANCE:
475 *ppvResult = (void *)(uintptr_t)ddi_get_instance(g_pDip);
476 break;
477
478 default:
479 rc = DDI_FAILURE;
480 break;
481 }
482
483 NOREF(pvArg);
484 return rc;
485}
486
487
488/**
489 * User context entry points
490 *
491 * @remarks fFlags are the flags passed to open() or to ldi_open_by_name. In
492 * the latter case the FKLYR flag is added to indicate that the caller
493 * is a kernel component rather than user land.
494 */
495static int vgdrvSolarisOpen(dev_t *pDev, int fFlags, int fType, cred_t *pCred)
496{
497 int rc;
498 PVBOXGUESTSESSION pSession = NULL;
499
500 LogFlow(("vgdrvSolarisOpen:\n"));
501
502 /*
503 * Verify we are being opened as a character device.
504 */
505 if (fType != OTYP_CHR)
506 return EINVAL;
507
508 vboxguest_state_t *pState = NULL;
509 unsigned iOpenInstance;
510 for (iOpenInstance = 0; iOpenInstance < 4096; iOpenInstance++)
511 {
512 if ( !ddi_get_soft_state(g_pvgdrvSolarisState, iOpenInstance) /* faster */
513 && ddi_soft_state_zalloc(g_pvgdrvSolarisState, iOpenInstance) == DDI_SUCCESS)
514 {
515 pState = ddi_get_soft_state(g_pvgdrvSolarisState, iOpenInstance);
516 break;
517 }
518 }
519 if (!pState)
520 {
521 Log(("vgdrvSolarisOpen: too many open instances."));
522 return ENXIO;
523 }
524
525 /*
526 * Create a new session.
527 */
528 if (!(fFlags & FKLYR))
529 rc = VGDrvCommonCreateUserSession(&g_DevExt, &pSession);
530 else
531 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
532 if (RT_SUCCESS(rc))
533 {
534 if (!(fFlags & FKLYR))
535 pState->pvProcRef = proc_ref();
536 else
537 pState->pvProcRef = NULL;
538 pState->pSession = pSession;
539 *pDev = makedevice(getmajor(*pDev), iOpenInstance);
540 Log(("vgdrvSolarisOpen: pSession=%p pState=%p pid=%d\n", pSession, pState, (int)RTProcSelf()));
541 return 0;
542 }
543
544 /* Failed, clean up. */
545 ddi_soft_state_free(g_pvgdrvSolarisState, iOpenInstance);
546
547 LogRel((DEVICE_NAME "::Open: VGDrvCommonCreateUserSession failed. rc=%d\n", rc));
548 return EFAULT;
549}
550
551
552static int vgdrvSolarisClose(dev_t Dev, int flag, int fType, cred_t *pCred)
553{
554 LogFlow(("vgdrvSolarisClose: pid=%d\n", (int)RTProcSelf()));
555
556 PVBOXGUESTSESSION pSession = NULL;
557 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
558 if (!pState)
559 {
560 Log(("vgdrvSolarisClose: failed to get pState.\n"));
561 return EFAULT;
562 }
563
564 if (pState->pvProcRef != NULL)
565 {
566 proc_unref(pState->pvProcRef);
567 pState->pvProcRef = NULL;
568 }
569 pSession = pState->pSession;
570 pState->pSession = NULL;
571 Log(("vgdrvSolarisClose: pSession=%p pState=%p\n", pSession, pState));
572 ddi_soft_state_free(g_pvgdrvSolarisState, getminor(Dev));
573 if (!pSession)
574 {
575 Log(("vgdrvSolarisClose: failed to get pSession.\n"));
576 return EFAULT;
577 }
578
579 /*
580 * Close the session.
581 */
582 if (pSession)
583 VGDrvCommonCloseSession(&g_DevExt, pSession);
584 return 0;
585}
586
587
588static int vgdrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
589{
590 LogFlow((DEVICE_NAME "::Read\n"));
591
592 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
593 if (!pState)
594 {
595 Log((DEVICE_NAME "::Close: failed to get pState.\n"));
596 return EFAULT;
597 }
598
599 PVBOXGUESTSESSION pSession = pState->pSession;
600 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
601 if (pSession->u32MousePosChangedSeq != u32CurSeq)
602 pSession->u32MousePosChangedSeq = u32CurSeq;
603
604 return 0;
605}
606
607
608static int vgdrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
609{
610 LogFlow(("vgdrvSolarisWrite:\n"));
611 return 0;
612}
613
614
615/** @def IOCPARM_LEN
616 * Gets the length from the ioctl number.
617 * This is normally defined by sys/ioccom.h on BSD systems...
618 */
619#ifndef IOCPARM_LEN
620# define IOCPARM_LEN(x) ( ((x) >> 16) & IOCPARM_MASK )
621#endif
622
623
624/**
625 * Driver ioctl, an alternate entry point for this character driver.
626 *
627 * @param Dev Device number
628 * @param iCmd Operation identifier
629 * @param iArgs Arguments from user to driver
630 * @param Mode Information bitfield (read/write, address space etc.)
631 * @param pCred User credentials
632 * @param pVal Return value for calling process.
633 *
634 * @return corresponding solaris error code.
635 */
636static int vgdrvSolarisIOCtl(dev_t Dev, int iCmd, intptr_t iArgs, int Mode, cred_t *pCred, int *pVal)
637{
638 /*
639 * Get the session from the soft state item.
640 */
641 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
642 if (!pState)
643 {
644 LogRel(("vgdrvSolarisIOCtl: no state data for %#x (%d)\n", Dev, getminor(Dev)));
645 return EINVAL;
646 }
647
648 PVBOXGUESTSESSION pSession = pState->pSession;
649 if (!pSession)
650 {
651 LogRel(("vgdrvSolarisIOCtl: no session in state data for %#x (%d)\n", Dev, getminor(Dev)));
652 return DDI_SUCCESS;
653 }
654
655 /*
656 * Deal with fast requests.
657 */
658 if (VBGL_IOCTL_IS_FAST(iCmd))
659 {
660 *pVal = VGDrvCommonIoCtlFast(iCmd, &g_DevExt, pSession);
661 return 0;
662 }
663
664 /*
665 * It's kind of simple if this is a kernel session, take slow path if user land.
666 */
667 if (pSession->R0Process == NIL_RTR0PROCESS)
668 {
669 if (IOCPARM_LEN(iCmd) == sizeof(VBGLREQHDR))
670 {
671 PVBGLREQHDR pHdr = (PVBGLREQHDR)iArgs;
672 int rc;
673 if (iCmd != VBGL_IOCTL_IDC_DISCONNECT)
674 rc =VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, RT_MAX(pHdr->cbIn, pHdr->cbOut));
675 else
676 {
677 pState->pSession = NULL;
678 rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, RT_MAX(pHdr->cbIn, pHdr->cbOut));
679 if (RT_FAILURE(rc))
680 pState->pSession = pSession;
681 }
682 return rc;
683 }
684 }
685
686 return vgdrvSolarisIOCtlSlow(pSession, iCmd, Mode, iArgs);
687}
688
689
690/**
691 * Worker for VBoxSupDrvIOCtl that takes the slow IOCtl functions.
692 *
693 * @returns Solaris errno.
694 *
695 * @param pSession The session.
696 * @param iCmd The IOCtl command.
697 * @param Mode Information bitfield (for specifying ownership of data)
698 * @param iArg User space address of the request buffer.
699 */
700static int vgdrvSolarisIOCtlSlow(PVBOXGUESTSESSION pSession, int iCmd, int Mode, intptr_t iArg)
701{
702 int rc;
703 uint32_t cbBuf = 0;
704 union
705 {
706 VBGLREQHDR Hdr;
707 uint8_t abBuf[64];
708 } StackBuf;
709 PVBGLREQHDR pHdr;
710
711
712 /*
713 * Read the header.
714 */
715 if (RT_UNLIKELY(IOCPARM_LEN(iCmd) != sizeof(StackBuf.Hdr)))
716 {
717 LogRel(("vgdrvSolarisIOCtlSlow: iCmd=%#x len %d expected %d\n", iCmd, IOCPARM_LEN(iCmd), sizeof(StackBuf.Hdr)));
718 return EINVAL;
719 }
720 rc = ddi_copyin((void *)iArg, &StackBuf.Hdr, sizeof(StackBuf.Hdr), Mode);
721 if (RT_UNLIKELY(rc))
722 {
723 LogRel(("vgdrvSolarisIOCtlSlow: ddi_copyin(,%#lx,) failed; iCmd=%#x. rc=%d\n", iArg, iCmd, rc));
724 return EFAULT;
725 }
726 if (RT_UNLIKELY(StackBuf.Hdr.uVersion != VBGLREQHDR_VERSION))
727 {
728 LogRel(("vgdrvSolarisIOCtlSlow: bad header version %#x; iCmd=%#x\n", StackBuf.Hdr.uVersion, iCmd));
729 return EINVAL;
730 }
731 cbBuf = RT_MAX(StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut);
732 if (RT_UNLIKELY( StackBuf.Hdr.cbIn < sizeof(StackBuf.Hdr)
733 || (StackBuf.Hdr.cbOut < sizeof(StackBuf.Hdr) && StackBuf.Hdr.cbOut != 0)
734 || cbBuf > _1M*16))
735 {
736 LogRel(("vgdrvSolarisIOCtlSlow: max(%#x,%#x); iCmd=%#x\n", StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut, iCmd));
737 return EINVAL;
738 }
739
740 /*
741 * Buffer the request.
742 *
743 * Note! Common code revalidates the header sizes and version. So it's
744 * fine to read it once more.
745 */
746 if (cbBuf <= sizeof(StackBuf))
747 pHdr = &StackBuf.Hdr;
748 else
749 {
750 pHdr = RTMemTmpAlloc(cbBuf);
751 if (RT_UNLIKELY(!pHdr))
752 {
753 LogRel(("vgdrvSolarisIOCtlSlow: failed to allocate buffer of %d bytes for iCmd=%#x.\n", cbBuf, iCmd));
754 return ENOMEM;
755 }
756 }
757 rc = ddi_copyin((void *)iArg, pHdr, cbBuf, Mode);
758 if (RT_UNLIKELY(rc))
759 {
760 LogRel(("vgdrvSolarisIOCtlSlow: copy_from_user(,%#lx, %#x) failed; iCmd=%#x. rc=%d\n", iArg, cbBuf, iCmd, rc));
761 if (pHdr != &StackBuf.Hdr)
762 RTMemFree(pHdr);
763 return EFAULT;
764 }
765
766 /*
767 * Process the IOCtl.
768 */
769 rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, cbBuf);
770
771 /*
772 * Copy ioctl data and output buffer back to user space.
773 */
774 if (RT_SUCCESS(rc))
775 {
776 uint32_t cbOut = pHdr->cbOut;
777 if (RT_UNLIKELY(cbOut > cbBuf))
778 {
779 LogRel(("vgdrvSolarisIOCtlSlow: too much output! %#x > %#x; iCmd=%#x!\n", cbOut, cbBuf, iCmd));
780 cbOut = cbBuf;
781 }
782 rc = ddi_copyout(pHdr, (void *)iArg, cbOut, Mode);
783 if (RT_UNLIKELY(rc != 0))
784 {
785 /* this is really bad */
786 LogRel(("vgdrvSolarisIOCtlSlow: ddi_copyout(,%p,%d) failed. rc=%d\n", (void *)iArg, cbBuf, rc));
787 rc = EFAULT;
788 }
789 }
790 else
791 rc = EINVAL;
792
793 if (pHdr != &StackBuf.Hdr)
794 RTMemTmpFree(pHdr);
795 return rc;
796}
797
798
799#if 0
800/**
801 * @note This code is duplicated on other platforms with variations, so please
802 * keep them all up to date when making changes!
803 */
804int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
805{
806 /*
807 * Simple request validation (common code does the rest).
808 */
809 int rc;
810 if ( RT_VALID_PTR(pReqHdr)
811 && cbReq >= sizeof(*pReqHdr))
812 {
813 /*
814 * All requests except the connect one requires a valid session.
815 */
816 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
817 if (pSession)
818 {
819 if ( RT_VALID_PTR(pSession)
820 && pSession->pDevExt == &g_DevExt)
821 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
822 else
823 rc = VERR_INVALID_HANDLE;
824 }
825 else if (uReq == VBGL_IOCTL_IDC_CONNECT)
826 {
827 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
828 if (RT_SUCCESS(rc))
829 {
830 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
831 if (RT_FAILURE(rc))
832 VGDrvCommonCloseSession(&g_DevExt, pSession);
833 }
834 }
835 else
836 rc = VERR_INVALID_HANDLE;
837 }
838 else
839 rc = VERR_INVALID_POINTER;
840 return rc;
841}
842#endif
843
844
845static int vgdrvSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead)
846{
847 LogFlow(("vgdrvSolarisPoll: fEvents=%d fAnyYet=%d\n", fEvents, fAnyYet));
848
849 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
850 if (RT_LIKELY(pState))
851 {
852 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pState->pSession;
853 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
854 if (pSession->u32MousePosChangedSeq != u32CurSeq)
855 {
856 *pReqEvents |= (POLLIN | POLLRDNORM);
857 pSession->u32MousePosChangedSeq = u32CurSeq;
858 }
859 else
860 {
861 *pReqEvents = 0;
862 if (!fAnyYet)
863 *ppPollHead = &g_PollHead;
864 }
865
866 return 0;
867 }
868
869 Log(("vgdrvSolarisPoll: no state data for %d\n", getminor(Dev)));
870 return EINVAL;
871}
872
873
874/**
875 * Sets IRQ for VMMDev.
876 *
877 * @returns Solaris error code.
878 * @param pDip Pointer to the device info structure.
879 */
880static int vgdrvSolarisAddIRQ(dev_info_t *pDip)
881{
882 LogFlow(("vgdrvSolarisAddIRQ: pDip=%p\n", pDip));
883
884 /* Get the types of interrupt supported for this hardware. */
885 int fIntrType = 0;
886 int rc = ddi_intr_get_supported_types(pDip, &fIntrType);
887 if (rc == DDI_SUCCESS)
888 {
889 /* We only support fixed interrupts at this point, not MSIs. */
890 if (fIntrType & DDI_INTR_TYPE_FIXED)
891 {
892 /* Verify the number of interrupts supported by this device. There can only be one fixed interrupt. */
893 int cIntrCount = 0;
894 rc = ddi_intr_get_nintrs(pDip, fIntrType, &cIntrCount);
895 if ( rc == DDI_SUCCESS
896 && cIntrCount == 1)
897 {
898 /* Allocated kernel memory for the interrupt handle. The allocation size is stored internally. */
899 g_pahIntrs = RTMemAllocZ(cIntrCount * sizeof(ddi_intr_handle_t));
900 if (g_pahIntrs)
901 {
902 /* Allocate the interrupt for this device and verify the allocation. */
903 int cIntrAllocated;
904 rc = ddi_intr_alloc(pDip, g_pahIntrs, fIntrType, 0 /* interrupt number */, cIntrCount, &cIntrAllocated,
905 DDI_INTR_ALLOC_NORMAL);
906 if ( rc == DDI_SUCCESS
907 && cIntrAllocated == 1)
908 {
909 /* Get the interrupt priority assigned by the system. */
910 uint_t uIntrPriority;
911 rc = ddi_intr_get_pri(g_pahIntrs[0], &uIntrPriority);
912 if (rc == DDI_SUCCESS)
913 {
914 /* Check if the interrupt priority is scheduler level or above, if so we need to use a high-level
915 and low-level interrupt handlers with corresponding mutexes. */
916 cmn_err(CE_CONT, "!vboxguest: uIntrPriority=%d hilevel_pri=%d\n", uIntrPriority, ddi_intr_get_hilevel_pri());
917 if (uIntrPriority >= ddi_intr_get_hilevel_pri())
918 {
919 /* Initialize the high-level mutex. */
920 mutex_init(&g_HighLevelIrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
921
922 /* Assign interrupt handler function to the interrupt handle. */
923 rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)&vgdrvSolarisHighLevelISR,
924 NULL /* pvArg1 */, NULL /* pvArg2 */);
925
926 if (rc == DDI_SUCCESS)
927 {
928 /* Add the low-level interrupt handler. */
929 rc = ddi_intr_add_softint(pDip, &g_hSoftIntr, DDI_INTR_SOFTPRI_MAX,
930 (ddi_intr_handler_t *)&vgdrvSolarisISR, NULL /* pvArg1 */);
931 if (rc == DDI_SUCCESS)
932 {
933 /* Initialize the low-level mutex at the corresponding level. */
934 mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER,
935 DDI_INTR_PRI(DDI_INTR_SOFTPRI_MAX));
936
937 g_fSoftIntRegistered = true;
938 /* Enable the high-level interrupt. */
939 rc = ddi_intr_enable(g_pahIntrs[0]);
940 if (rc == DDI_SUCCESS)
941 return rc;
942
943 LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc));
944 mutex_destroy(&g_IrqMtx);
945 }
946 else
947 LogRel((DEVICE_NAME "::AddIRQ: failed to add soft interrupt handler. rc=%d\n", rc));
948
949 ddi_intr_remove_handler(g_pahIntrs[0]);
950 }
951 else
952 LogRel((DEVICE_NAME "::AddIRQ: failed to add high-level interrupt handler. rc=%d\n", rc));
953
954 mutex_destroy(&g_HighLevelIrqMtx);
955 }
956 else
957 {
958 /* Interrupt handler runs at reschedulable level, initialize the mutex at the given priority. */
959 mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
960
961 /* Assign interrupt handler function to the interrupt handle. */
962 rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)vgdrvSolarisISR,
963 NULL /* pvArg1 */, NULL /* pvArg2 */);
964 if (rc == DDI_SUCCESS)
965 {
966 /* Enable the interrupt. */
967 rc = ddi_intr_enable(g_pahIntrs[0]);
968 if (rc == DDI_SUCCESS)
969 return rc;
970
971 LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc));
972 mutex_destroy(&g_IrqMtx);
973 }
974 }
975 }
976 else
977 LogRel((DEVICE_NAME "::AddIRQ: failed to get priority of interrupt. rc=%d\n", rc));
978
979 Assert(cIntrAllocated == 1);
980 ddi_intr_free(g_pahIntrs[0]);
981 }
982 else
983 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount));
984 RTMemFree(g_pahIntrs);
985 }
986 else
987 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount));
988 }
989 else
990 LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient number of IRQs. rc=%d cIntrCount=%d\n", rc, cIntrCount));
991 }
992 else
993 LogRel((DEVICE_NAME "::AddIRQ: fixed-type interrupts not supported. IntrType=%#x\n", fIntrType));
994 }
995 else
996 LogRel((DEVICE_NAME "::AddIRQ: failed to get supported interrupt types. rc=%d\n", rc));
997 return rc;
998}
999
1000
1001/**
1002 * Removes IRQ for VMMDev.
1003 *
1004 * @param pDip Pointer to the device info structure.
1005 */
1006static void vgdrvSolarisRemoveIRQ(dev_info_t *pDip)
1007{
1008 LogFlow(("vgdrvSolarisRemoveIRQ:\n"));
1009
1010 int rc = ddi_intr_disable(g_pahIntrs[0]);
1011 if (rc == DDI_SUCCESS)
1012 {
1013 rc = ddi_intr_remove_handler(g_pahIntrs[0]);
1014 if (rc == DDI_SUCCESS)
1015 ddi_intr_free(g_pahIntrs[0]);
1016 }
1017
1018 if (g_fSoftIntRegistered)
1019 {
1020 ddi_intr_remove_softint(g_hSoftIntr);
1021 mutex_destroy(&g_HighLevelIrqMtx);
1022 g_fSoftIntRegistered = false;
1023 }
1024
1025 mutex_destroy(&g_IrqMtx);
1026 RTMemFree(g_pahIntrs);
1027}
1028
1029
1030/**
1031 * High-level Interrupt Service Routine for VMMDev.
1032 *
1033 * This routine simply dispatches a soft-interrupt at an acceptable IPL as
1034 * VGDrvCommonISR() cannot be called at a high IPL (scheduler level or higher)
1035 * due to pollwakeup() in VGDrvNativeISRMousePollEvent().
1036 *
1037 * @param Arg Private data (unused, will be NULL).
1038 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
1039 */
1040static uint_t vgdrvSolarisHighLevelISR(caddr_t Arg)
1041{
1042 bool const fOurIrq = VGDrvCommonIsOurIRQ(&g_DevExt);
1043 if (fOurIrq)
1044 {
1045 ddi_intr_trigger_softint(g_hSoftIntr, NULL /* Arg */);
1046 return DDI_INTR_CLAIMED;
1047 }
1048 return DDI_INTR_UNCLAIMED;
1049}
1050
1051
1052/**
1053 * Interrupt Service Routine for VMMDev.
1054 *
1055 * @param Arg Private data (unused, will be NULL).
1056 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
1057 */
1058static uint_t vgdrvSolarisISR(caddr_t Arg)
1059{
1060 LogFlow(("vgdrvSolarisISR:\n"));
1061
1062 /* The mutex is required to protect against parallel executions (if possible?) and also the
1063 mouse notify registeration race between VGDrvNativeSetMouseNotifyCallback() and VGDrvCommonISR(). */
1064 mutex_enter(&g_IrqMtx);
1065 bool fOurIRQ = VGDrvCommonISR(&g_DevExt);
1066 mutex_exit(&g_IrqMtx);
1067
1068 return fOurIRQ ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED;
1069}
1070
1071
1072/**
1073 * Poll notifier for mouse poll events.
1074 *
1075 * @param pDevExt Pointer to the device extension.
1076 *
1077 * @remarks This must be called without holding any spinlocks.
1078 */
1079void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1080{
1081 LogFlow(("VGDrvNativeISRMousePollEvent:\n"));
1082
1083 /*
1084 * Wake up poll waiters.
1085 */
1086 pollwakeup(&g_PollHead, POLLIN | POLLRDNORM);
1087}
1088
1089
1090bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
1091{
1092 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
1093 return false;
1094}
1095
1096
1097/**
1098 * Sets the mouse notification callback.
1099 *
1100 * @returns VBox status code.
1101 * @param pDevExt Pointer to the device extension.
1102 * @param pNotify Pointer to the mouse notify struct.
1103 */
1104int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
1105{
1106 /* Take the mutex here so as to not race with VGDrvCommonISR() which invokes the mouse notify callback. */
1107 mutex_enter(&g_IrqMtx);
1108 pDevExt->pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
1109 pDevExt->pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
1110 mutex_exit(&g_IrqMtx);
1111 return VINF_SUCCESS;
1112}
1113
Note: See TracBrowser for help on using the repository browser.

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