VirtualBox

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

Last change on this file since 65124 was 64436, checked in by vboxsync, 8 years ago

Additions/VBoxGuest: Fix typo in r111564.

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