VirtualBox

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

Last change on this file since 72352 was 70876, checked in by vboxsync, 7 years ago

VBoxGuest: Solaris root and wheel (equivalent) detection. bugref:9105

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