VirtualBox

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

Last change on this file since 75713 was 75584, checked in by vboxsync, 6 years ago

VBoxGuest-linux.c,VBoxGuest-solaris.c: Setup the IRQ after calling VGDrvCommonInitDevExt so that it's ready for it, like everyone else does it. Linux was also tearing down IRQs after common code terminated, this was also reverted (solaris got this right already).

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