VirtualBox

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

Last change on this file since 76553 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.0 KB
Line 
1/* $Id: VBoxGuest-solaris.c 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions Driver for Solaris.
4 */
5
6/*
7 * Copyright (C) 2007-2019 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 fRequestor |= VMMDEV_REQUESTOR_NO_USER_DEVICE; /** @todo implement vboxuser device node. */
543
544 rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &pSession);
545 }
546 else
547 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
548 if (RT_SUCCESS(rc))
549 {
550 if (!(fFlags & FKLYR))
551 pState->pvProcRef = proc_ref();
552 else
553 pState->pvProcRef = NULL;
554 pState->pSession = pSession;
555 *pDev = makedevice(getmajor(*pDev), iOpenInstance);
556 Log(("vgdrvSolarisOpen: pSession=%p pState=%p pid=%d\n", pSession, pState, (int)RTProcSelf()));
557 return 0;
558 }
559
560 /* Failed, clean up. */
561 ddi_soft_state_free(g_pvgdrvSolarisState, iOpenInstance);
562
563 LogRel((DEVICE_NAME "::Open: VGDrvCommonCreateUserSession failed. rc=%d\n", rc));
564 return EFAULT;
565}
566
567
568static int vgdrvSolarisClose(dev_t Dev, int flag, int fType, cred_t *pCred)
569{
570 LogFlow(("vgdrvSolarisClose: pid=%d\n", (int)RTProcSelf()));
571
572 PVBOXGUESTSESSION pSession = NULL;
573 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
574 if (!pState)
575 {
576 Log(("vgdrvSolarisClose: failed to get pState.\n"));
577 return EFAULT;
578 }
579
580 if (pState->pvProcRef != NULL)
581 {
582 proc_unref(pState->pvProcRef);
583 pState->pvProcRef = NULL;
584 }
585 pSession = pState->pSession;
586 pState->pSession = NULL;
587 Log(("vgdrvSolarisClose: pSession=%p pState=%p\n", pSession, pState));
588 ddi_soft_state_free(g_pvgdrvSolarisState, getminor(Dev));
589 if (!pSession)
590 {
591 Log(("vgdrvSolarisClose: failed to get pSession.\n"));
592 return EFAULT;
593 }
594
595 /*
596 * Close the session.
597 */
598 if (pSession)
599 VGDrvCommonCloseSession(&g_DevExt, pSession);
600 return 0;
601}
602
603
604static int vgdrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
605{
606 LogFlow((DEVICE_NAME "::Read\n"));
607
608 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
609 if (!pState)
610 {
611 Log((DEVICE_NAME "::Close: failed to get pState.\n"));
612 return EFAULT;
613 }
614
615 PVBOXGUESTSESSION pSession = pState->pSession;
616 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
617 if (pSession->u32MousePosChangedSeq != u32CurSeq)
618 pSession->u32MousePosChangedSeq = u32CurSeq;
619
620 return 0;
621}
622
623
624static int vgdrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
625{
626 LogFlow(("vgdrvSolarisWrite:\n"));
627 return 0;
628}
629
630
631/** @def IOCPARM_LEN
632 * Gets the length from the ioctl number.
633 * This is normally defined by sys/ioccom.h on BSD systems...
634 */
635#ifndef IOCPARM_LEN
636# define IOCPARM_LEN(x) ( ((x) >> 16) & IOCPARM_MASK )
637#endif
638
639
640/**
641 * Driver ioctl, an alternate entry point for this character driver.
642 *
643 * @param Dev Device number
644 * @param iCmd Operation identifier
645 * @param iArgs Arguments from user to driver
646 * @param Mode Information bitfield (read/write, address space etc.)
647 * @param pCred User credentials
648 * @param pVal Return value for calling process.
649 *
650 * @return corresponding solaris error code.
651 */
652static int vgdrvSolarisIOCtl(dev_t Dev, int iCmd, intptr_t iArgs, int Mode, cred_t *pCred, int *pVal)
653{
654 /*
655 * Get the session from the soft state item.
656 */
657 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
658 if (!pState)
659 {
660 LogRel(("vgdrvSolarisIOCtl: no state data for %#x (%d)\n", Dev, getminor(Dev)));
661 return EINVAL;
662 }
663
664 PVBOXGUESTSESSION pSession = pState->pSession;
665 if (!pSession)
666 {
667 LogRel(("vgdrvSolarisIOCtl: no session in state data for %#x (%d)\n", Dev, getminor(Dev)));
668 return DDI_SUCCESS;
669 }
670
671 /*
672 * Deal with fast requests.
673 */
674 if (VBGL_IOCTL_IS_FAST(iCmd))
675 {
676 *pVal = VGDrvCommonIoCtlFast(iCmd, &g_DevExt, pSession);
677 return 0;
678 }
679
680 /*
681 * It's kind of simple if this is a kernel session, take slow path if user land.
682 */
683 if (pSession->R0Process == NIL_RTR0PROCESS)
684 {
685 if (IOCPARM_LEN(iCmd) == sizeof(VBGLREQHDR))
686 {
687 PVBGLREQHDR pHdr = (PVBGLREQHDR)iArgs;
688 int rc;
689 if (iCmd != VBGL_IOCTL_IDC_DISCONNECT)
690 rc =VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, RT_MAX(pHdr->cbIn, pHdr->cbOut));
691 else
692 {
693 pState->pSession = NULL;
694 rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, RT_MAX(pHdr->cbIn, pHdr->cbOut));
695 if (RT_FAILURE(rc))
696 pState->pSession = pSession;
697 }
698 return rc;
699 }
700 }
701
702 return vgdrvSolarisIOCtlSlow(pSession, iCmd, Mode, iArgs);
703}
704
705
706/**
707 * Worker for VBoxSupDrvIOCtl that takes the slow IOCtl functions.
708 *
709 * @returns Solaris errno.
710 *
711 * @param pSession The session.
712 * @param iCmd The IOCtl command.
713 * @param Mode Information bitfield (for specifying ownership of data)
714 * @param iArg User space address of the request buffer.
715 */
716static int vgdrvSolarisIOCtlSlow(PVBOXGUESTSESSION pSession, int iCmd, int Mode, intptr_t iArg)
717{
718 int rc;
719 uint32_t cbBuf = 0;
720 union
721 {
722 VBGLREQHDR Hdr;
723 uint8_t abBuf[64];
724 } StackBuf;
725 PVBGLREQHDR pHdr;
726
727
728 /*
729 * Read the header.
730 */
731 if (RT_UNLIKELY(IOCPARM_LEN(iCmd) != sizeof(StackBuf.Hdr)))
732 {
733 LogRel(("vgdrvSolarisIOCtlSlow: iCmd=%#x len %d expected %d\n", iCmd, IOCPARM_LEN(iCmd), sizeof(StackBuf.Hdr)));
734 return EINVAL;
735 }
736 rc = ddi_copyin((void *)iArg, &StackBuf.Hdr, sizeof(StackBuf.Hdr), Mode);
737 if (RT_UNLIKELY(rc))
738 {
739 LogRel(("vgdrvSolarisIOCtlSlow: ddi_copyin(,%#lx,) failed; iCmd=%#x. rc=%d\n", iArg, iCmd, rc));
740 return EFAULT;
741 }
742 if (RT_UNLIKELY(StackBuf.Hdr.uVersion != VBGLREQHDR_VERSION))
743 {
744 LogRel(("vgdrvSolarisIOCtlSlow: bad header version %#x; iCmd=%#x\n", StackBuf.Hdr.uVersion, iCmd));
745 return EINVAL;
746 }
747 cbBuf = RT_MAX(StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut);
748 if (RT_UNLIKELY( StackBuf.Hdr.cbIn < sizeof(StackBuf.Hdr)
749 || (StackBuf.Hdr.cbOut < sizeof(StackBuf.Hdr) && StackBuf.Hdr.cbOut != 0)
750 || cbBuf > _1M*16))
751 {
752 LogRel(("vgdrvSolarisIOCtlSlow: max(%#x,%#x); iCmd=%#x\n", StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut, iCmd));
753 return EINVAL;
754 }
755
756 /*
757 * Buffer the request.
758 *
759 * Note! Common code revalidates the header sizes and version. So it's
760 * fine to read it once more.
761 */
762 if (cbBuf <= sizeof(StackBuf))
763 pHdr = &StackBuf.Hdr;
764 else
765 {
766 pHdr = RTMemTmpAlloc(cbBuf);
767 if (RT_UNLIKELY(!pHdr))
768 {
769 LogRel(("vgdrvSolarisIOCtlSlow: failed to allocate buffer of %d bytes for iCmd=%#x.\n", cbBuf, iCmd));
770 return ENOMEM;
771 }
772 }
773 rc = ddi_copyin((void *)iArg, pHdr, cbBuf, Mode);
774 if (RT_UNLIKELY(rc))
775 {
776 LogRel(("vgdrvSolarisIOCtlSlow: copy_from_user(,%#lx, %#x) failed; iCmd=%#x. rc=%d\n", iArg, cbBuf, iCmd, rc));
777 if (pHdr != &StackBuf.Hdr)
778 RTMemFree(pHdr);
779 return EFAULT;
780 }
781
782 /*
783 * Process the IOCtl.
784 */
785 rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, cbBuf);
786
787 /*
788 * Copy ioctl data and output buffer back to user space.
789 */
790 if (RT_SUCCESS(rc))
791 {
792 uint32_t cbOut = pHdr->cbOut;
793 if (RT_UNLIKELY(cbOut > cbBuf))
794 {
795 LogRel(("vgdrvSolarisIOCtlSlow: too much output! %#x > %#x; iCmd=%#x!\n", cbOut, cbBuf, iCmd));
796 cbOut = cbBuf;
797 }
798 rc = ddi_copyout(pHdr, (void *)iArg, cbOut, Mode);
799 if (RT_UNLIKELY(rc != 0))
800 {
801 /* this is really bad */
802 LogRel(("vgdrvSolarisIOCtlSlow: ddi_copyout(,%p,%d) failed. rc=%d\n", (void *)iArg, cbBuf, rc));
803 rc = EFAULT;
804 }
805 }
806 else
807 rc = EINVAL;
808
809 if (pHdr != &StackBuf.Hdr)
810 RTMemTmpFree(pHdr);
811 return rc;
812}
813
814
815#if 0
816/**
817 * @note This code is duplicated on other platforms with variations, so please
818 * keep them all up to date when making changes!
819 */
820int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
821{
822 /*
823 * Simple request validation (common code does the rest).
824 */
825 int rc;
826 if ( RT_VALID_PTR(pReqHdr)
827 && cbReq >= sizeof(*pReqHdr))
828 {
829 /*
830 * All requests except the connect one requires a valid session.
831 */
832 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
833 if (pSession)
834 {
835 if ( RT_VALID_PTR(pSession)
836 && pSession->pDevExt == &g_DevExt)
837 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
838 else
839 rc = VERR_INVALID_HANDLE;
840 }
841 else if (uReq == VBGL_IOCTL_IDC_CONNECT)
842 {
843 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
844 if (RT_SUCCESS(rc))
845 {
846 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
847 if (RT_FAILURE(rc))
848 VGDrvCommonCloseSession(&g_DevExt, pSession);
849 }
850 }
851 else
852 rc = VERR_INVALID_HANDLE;
853 }
854 else
855 rc = VERR_INVALID_POINTER;
856 return rc;
857}
858#endif
859
860
861static int vgdrvSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead)
862{
863 LogFlow(("vgdrvSolarisPoll: fEvents=%d fAnyYet=%d\n", fEvents, fAnyYet));
864
865 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
866 if (RT_LIKELY(pState))
867 {
868 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pState->pSession;
869 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
870 if (pSession->u32MousePosChangedSeq != u32CurSeq)
871 {
872 *pReqEvents |= (POLLIN | POLLRDNORM);
873 pSession->u32MousePosChangedSeq = u32CurSeq;
874 }
875 else
876 {
877 *pReqEvents = 0;
878 if (!fAnyYet)
879 *ppPollHead = &g_PollHead;
880 }
881
882 return 0;
883 }
884
885 Log(("vgdrvSolarisPoll: no state data for %d\n", getminor(Dev)));
886 return EINVAL;
887}
888
889
890/**
891 * Sets IRQ for VMMDev.
892 *
893 * @returns Solaris error code.
894 * @param pDip Pointer to the device info structure.
895 */
896static int vgdrvSolarisAddIRQ(dev_info_t *pDip)
897{
898 LogFlow(("vgdrvSolarisAddIRQ: pDip=%p\n", pDip));
899
900 /* Get the types of interrupt supported for this hardware. */
901 int fIntrType = 0;
902 int rc = ddi_intr_get_supported_types(pDip, &fIntrType);
903 if (rc == DDI_SUCCESS)
904 {
905 /* We only support fixed interrupts at this point, not MSIs. */
906 if (fIntrType & DDI_INTR_TYPE_FIXED)
907 {
908 /* Verify the number of interrupts supported by this device. There can only be one fixed interrupt. */
909 int cIntrCount = 0;
910 rc = ddi_intr_get_nintrs(pDip, fIntrType, &cIntrCount);
911 if ( rc == DDI_SUCCESS
912 && cIntrCount == 1)
913 {
914 /* Allocated kernel memory for the interrupt handle. The allocation size is stored internally. */
915 g_pahIntrs = RTMemAllocZ(cIntrCount * sizeof(ddi_intr_handle_t));
916 if (g_pahIntrs)
917 {
918 /* Allocate the interrupt for this device and verify the allocation. */
919 int cIntrAllocated;
920 rc = ddi_intr_alloc(pDip, g_pahIntrs, fIntrType, 0 /* interrupt number */, cIntrCount, &cIntrAllocated,
921 DDI_INTR_ALLOC_NORMAL);
922 if ( rc == DDI_SUCCESS
923 && cIntrAllocated == 1)
924 {
925 /* Get the interrupt priority assigned by the system. */
926 uint_t uIntrPriority;
927 rc = ddi_intr_get_pri(g_pahIntrs[0], &uIntrPriority);
928 if (rc == DDI_SUCCESS)
929 {
930 /* Check if the interrupt priority is scheduler level or above, if so we need to use a high-level
931 and low-level interrupt handlers with corresponding mutexes. */
932 cmn_err(CE_CONT, "!vboxguest: uIntrPriority=%d hilevel_pri=%d\n", uIntrPriority, ddi_intr_get_hilevel_pri());
933 if (uIntrPriority >= ddi_intr_get_hilevel_pri())
934 {
935 /* Initialize the high-level mutex. */
936 mutex_init(&g_HighLevelIrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
937
938 /* Assign interrupt handler function to the interrupt handle. */
939 rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)&vgdrvSolarisHighLevelISR,
940 NULL /* pvArg1 */, NULL /* pvArg2 */);
941
942 if (rc == DDI_SUCCESS)
943 {
944 /* Add the low-level interrupt handler. */
945 rc = ddi_intr_add_softint(pDip, &g_hSoftIntr, DDI_INTR_SOFTPRI_MAX,
946 (ddi_intr_handler_t *)&vgdrvSolarisISR, NULL /* pvArg1 */);
947 if (rc == DDI_SUCCESS)
948 {
949 /* Initialize the low-level mutex at the corresponding level. */
950 mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER,
951 DDI_INTR_PRI(DDI_INTR_SOFTPRI_MAX));
952
953 g_fSoftIntRegistered = true;
954 /* Enable the high-level interrupt. */
955 rc = ddi_intr_enable(g_pahIntrs[0]);
956 if (rc == DDI_SUCCESS)
957 return rc;
958
959 LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc));
960 mutex_destroy(&g_IrqMtx);
961 }
962 else
963 LogRel((DEVICE_NAME "::AddIRQ: failed to add soft interrupt handler. rc=%d\n", rc));
964
965 ddi_intr_remove_handler(g_pahIntrs[0]);
966 }
967 else
968 LogRel((DEVICE_NAME "::AddIRQ: failed to add high-level interrupt handler. rc=%d\n", rc));
969
970 mutex_destroy(&g_HighLevelIrqMtx);
971 }
972 else
973 {
974 /* Interrupt handler runs at reschedulable level, initialize the mutex at the given priority. */
975 mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
976
977 /* Assign interrupt handler function to the interrupt handle. */
978 rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)vgdrvSolarisISR,
979 NULL /* pvArg1 */, NULL /* pvArg2 */);
980 if (rc == DDI_SUCCESS)
981 {
982 /* Enable the interrupt. */
983 rc = ddi_intr_enable(g_pahIntrs[0]);
984 if (rc == DDI_SUCCESS)
985 return rc;
986
987 LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc));
988 mutex_destroy(&g_IrqMtx);
989 }
990 }
991 }
992 else
993 LogRel((DEVICE_NAME "::AddIRQ: failed to get priority of interrupt. rc=%d\n", rc));
994
995 Assert(cIntrAllocated == 1);
996 ddi_intr_free(g_pahIntrs[0]);
997 }
998 else
999 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount));
1000 RTMemFree(g_pahIntrs);
1001 }
1002 else
1003 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount));
1004 }
1005 else
1006 LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient number of IRQs. rc=%d cIntrCount=%d\n", rc, cIntrCount));
1007 }
1008 else
1009 LogRel((DEVICE_NAME "::AddIRQ: fixed-type interrupts not supported. IntrType=%#x\n", fIntrType));
1010 }
1011 else
1012 LogRel((DEVICE_NAME "::AddIRQ: failed to get supported interrupt types. rc=%d\n", rc));
1013 return rc;
1014}
1015
1016
1017/**
1018 * Removes IRQ for VMMDev.
1019 *
1020 * @param pDip Pointer to the device info structure.
1021 */
1022static void vgdrvSolarisRemoveIRQ(dev_info_t *pDip)
1023{
1024 LogFlow(("vgdrvSolarisRemoveIRQ:\n"));
1025
1026 int rc = ddi_intr_disable(g_pahIntrs[0]);
1027 if (rc == DDI_SUCCESS)
1028 {
1029 rc = ddi_intr_remove_handler(g_pahIntrs[0]);
1030 if (rc == DDI_SUCCESS)
1031 ddi_intr_free(g_pahIntrs[0]);
1032 }
1033
1034 if (g_fSoftIntRegistered)
1035 {
1036 ddi_intr_remove_softint(g_hSoftIntr);
1037 mutex_destroy(&g_HighLevelIrqMtx);
1038 g_fSoftIntRegistered = false;
1039 }
1040
1041 mutex_destroy(&g_IrqMtx);
1042 RTMemFree(g_pahIntrs);
1043}
1044
1045
1046/**
1047 * High-level Interrupt Service Routine for VMMDev.
1048 *
1049 * This routine simply dispatches a soft-interrupt at an acceptable IPL as
1050 * VGDrvCommonISR() cannot be called at a high IPL (scheduler level or higher)
1051 * due to pollwakeup() in VGDrvNativeISRMousePollEvent().
1052 *
1053 * @param Arg Private data (unused, will be NULL).
1054 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
1055 */
1056static uint_t vgdrvSolarisHighLevelISR(caddr_t Arg)
1057{
1058 bool const fOurIrq = VGDrvCommonIsOurIRQ(&g_DevExt);
1059 if (fOurIrq)
1060 {
1061 ddi_intr_trigger_softint(g_hSoftIntr, NULL /* Arg */);
1062 return DDI_INTR_CLAIMED;
1063 }
1064 return DDI_INTR_UNCLAIMED;
1065}
1066
1067
1068/**
1069 * Interrupt Service Routine for VMMDev.
1070 *
1071 * @param Arg Private data (unused, will be NULL).
1072 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
1073 */
1074static uint_t vgdrvSolarisISR(caddr_t Arg)
1075{
1076 LogFlow(("vgdrvSolarisISR:\n"));
1077
1078 /* The mutex is required to protect against parallel executions (if possible?) and also the
1079 mouse notify registeration race between VGDrvNativeSetMouseNotifyCallback() and VGDrvCommonISR(). */
1080 mutex_enter(&g_IrqMtx);
1081 bool fOurIRQ = VGDrvCommonISR(&g_DevExt);
1082 mutex_exit(&g_IrqMtx);
1083
1084 return fOurIRQ ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED;
1085}
1086
1087
1088/**
1089 * Poll notifier for mouse poll events.
1090 *
1091 * @param pDevExt Pointer to the device extension.
1092 *
1093 * @remarks This must be called without holding any spinlocks.
1094 */
1095void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1096{
1097 LogFlow(("VGDrvNativeISRMousePollEvent:\n"));
1098
1099 /*
1100 * Wake up poll waiters.
1101 */
1102 pollwakeup(&g_PollHead, POLLIN | POLLRDNORM);
1103}
1104
1105
1106bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
1107{
1108 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
1109 return false;
1110}
1111
1112
1113/**
1114 * Sets the mouse notification callback.
1115 *
1116 * @returns VBox status code.
1117 * @param pDevExt Pointer to the device extension.
1118 * @param pNotify Pointer to the mouse notify struct.
1119 */
1120int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
1121{
1122 /* Take the mutex here so as to not race with VGDrvCommonISR() which invokes the mouse notify callback. */
1123 mutex_enter(&g_IrqMtx);
1124 pDevExt->pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
1125 pDevExt->pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
1126 mutex_exit(&g_IrqMtx);
1127 return VINF_SUCCESS;
1128}
1129
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use