VirtualBox

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

Last change on this file since 60337 was 58113, checked in by vboxsync, 9 years ago

VBoxGuest: Vbgd -> VGDrv, cleanups - will probably not build cleanly everywhere. :)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.2 KB
Line 
1/* $Id: VBoxGuest-solaris.c 58113 2015-10-08 10:13:54Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions Driver for Solaris.
4 */
5
6/*
7 * Copyright (C) 2007-2015 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 vgdrvSolarisISR(caddr_t Arg);
82
83
84/*********************************************************************************************************************************
85* Structures and Typedefs *
86*********************************************************************************************************************************/
87/**
88 * cb_ops: for drivers that support char/block entry points
89 */
90static struct cb_ops g_vgdrvSolarisCbOps =
91{
92 vgdrvSolarisOpen,
93 vgdrvSolarisClose,
94 nodev, /* b strategy */
95 nodev, /* b dump */
96 nodev, /* b print */
97 vgdrvSolarisRead,
98 vgdrvSolarisWrite,
99 vgdrvSolarisIOCtl,
100 nodev, /* c devmap */
101 nodev, /* c mmap */
102 nodev, /* c segmap */
103 vgdrvSolarisPoll,
104 ddi_prop_op, /* property ops */
105 NULL, /* streamtab */
106 D_NEW | D_MP, /* compat. flag */
107 CB_REV /* revision */
108};
109
110/**
111 * dev_ops: for driver device operations
112 */
113static struct dev_ops g_vgdrvSolarisDevOps =
114{
115 DEVO_REV, /* driver build revision */
116 0, /* ref count */
117 vgdrvSolarisGetInfo,
118 nulldev, /* identify */
119 nulldev, /* probe */
120 vgdrvSolarisAttach,
121 vgdrvSolarisDetach,
122 nodev, /* reset */
123 &g_vgdrvSolarisCbOps,
124 (struct bus_ops *)0,
125 nodev, /* power */
126 vgdrvSolarisQuiesce
127};
128
129/**
130 * modldrv: export driver specifics to the kernel
131 */
132static struct modldrv g_vgdrvSolarisModule =
133{
134 &mod_driverops, /* extern from kernel */
135 DEVICE_DESC " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
136 &g_vgdrvSolarisDevOps
137};
138
139/**
140 * modlinkage: export install/remove/info to the kernel
141 */
142static struct modlinkage g_vgdrvSolarisModLinkage =
143{
144 MODREV_1, /* loadable module system revision */
145 &g_vgdrvSolarisModule,
146 NULL /* terminate array of linkage structures */
147};
148
149/**
150 * State info for each open file handle.
151 */
152typedef struct
153{
154 /** Pointer to the session handle. */
155 PVBOXGUESTSESSION pSession;
156 /** The process reference for posting signals */
157 void *pvProcRef;
158} vboxguest_state_t;
159
160
161/*********************************************************************************************************************************
162* Global Variables *
163*********************************************************************************************************************************/
164/** Device handle (we support only one instance). */
165static dev_info_t *g_pDip = NULL;
166/** Opaque pointer to file-descriptor states */
167static void *g_pvgdrvSolarisState = NULL;
168/** Device extention & session data association structure. */
169static VBOXGUESTDEVEXT g_DevExt;
170/** IO port handle. */
171static ddi_acc_handle_t g_PciIOHandle;
172/** MMIO handle. */
173static ddi_acc_handle_t g_PciMMIOHandle;
174/** IO Port. */
175static uint16_t g_uIOPortBase;
176/** Address of the MMIO region.*/
177static caddr_t g_pMMIOBase;
178/** Size of the MMIO region. */
179static off_t g_cbMMIO;
180/** Pointer to the interrupt handle vector */
181static ddi_intr_handle_t *g_pIntr;
182/** Number of actually allocated interrupt handles */
183static size_t g_cIntrAllocated;
184/** The pollhead structure */
185static pollhead_t g_PollHead;
186/** The IRQ Mutex */
187static kmutex_t g_IrqMtx;
188/** Layered device handle for kernel keep-attached opens */
189static ldi_handle_t g_LdiHandle = NULL;
190/** Ref counting for IDCOpen calls */
191static uint64_t g_cLdiOpens = 0;
192/** The Mutex protecting the LDI handle in IDC opens */
193static kmutex_t g_LdiMtx;
194
195/**
196 * Kernel entry points
197 */
198int _init(void)
199{
200 /*
201 * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
202 */
203 int rc = RTR0Init(0);
204 if (RT_SUCCESS(rc))
205 {
206 PRTLOGGER pRelLogger;
207 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
208 rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all",
209 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
210 RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER, NULL);
211 if (RT_SUCCESS(rc))
212 RTLogRelSetDefaultInstance(pRelLogger);
213 else
214 cmn_err(CE_NOTE, "failed to initialize driver logging rc=%d!\n", rc);
215
216 mutex_init(&g_LdiMtx, NULL, MUTEX_DRIVER, NULL);
217
218 /*
219 * Prevent module autounloading.
220 */
221 modctl_t *pModCtl = mod_getctl(&g_vgdrvSolarisModLinkage);
222 if (pModCtl)
223 pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
224 else
225 LogRel((DEVICE_NAME ": failed to disable autounloading!\n"));
226
227 rc = ddi_soft_state_init(&g_pvgdrvSolarisState, sizeof(vboxguest_state_t), 1);
228 if (!rc)
229 {
230 rc = mod_install(&g_vgdrvSolarisModLinkage);
231 if (rc)
232 ddi_soft_state_fini(&g_pvgdrvSolarisState);
233 }
234 }
235 else
236 {
237 cmn_err(CE_NOTE, "_init: RTR0Init failed. rc=%d\n", rc);
238 return EINVAL;
239 }
240
241 return rc;
242}
243
244
245int _fini(void)
246{
247 LogFlow((DEVICE_NAME ":_fini\n"));
248 int rc = mod_remove(&g_vgdrvSolarisModLinkage);
249 if (!rc)
250 ddi_soft_state_fini(&g_pvgdrvSolarisState);
251
252 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
253 RTLogDestroy(RTLogSetDefaultInstance(NULL));
254
255 if (!rc)
256 {
257 mutex_destroy(&g_LdiMtx);
258 RTR0Term();
259 }
260 return rc;
261}
262
263
264int _info(struct modinfo *pModInfo)
265{
266 LogFlow((DEVICE_NAME ":_info\n"));
267 return mod_info(&g_vgdrvSolarisModLinkage, pModInfo);
268}
269
270
271/**
272 * Attach entry point, to attach a device to the system or resume it.
273 *
274 * @param pDip The module structure instance.
275 * @param enmCmd Attach type (ddi_attach_cmd_t)
276 *
277 * @return corresponding solaris error code.
278 */
279static int vgdrvSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
280{
281 LogFlow(("vgdrvSolarisAttach:\n"));
282 switch (enmCmd)
283 {
284 case DDI_ATTACH:
285 {
286 if (g_pDip)
287 {
288 LogRel(("vgdrvSolarisAttach: Only one instance supported.\n"));
289 return DDI_FAILURE;
290 }
291
292 int instance = ddi_get_instance(pDip);
293
294 /*
295 * Enable resources for PCI access.
296 */
297 ddi_acc_handle_t PciHandle;
298 int rc = pci_config_setup(pDip, &PciHandle);
299 if (rc == DDI_SUCCESS)
300 {
301 /*
302 * Map the register address space.
303 */
304 caddr_t baseAddr;
305 ddi_device_acc_attr_t deviceAttr;
306 deviceAttr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
307 deviceAttr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
308 deviceAttr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
309 deviceAttr.devacc_attr_access = DDI_DEFAULT_ACC;
310 rc = ddi_regs_map_setup(pDip, 1, &baseAddr, 0, 0, &deviceAttr, &g_PciIOHandle);
311 if (rc == DDI_SUCCESS)
312 {
313 /*
314 * Read size of the MMIO region.
315 */
316 g_uIOPortBase = (uintptr_t)baseAddr;
317 rc = ddi_dev_regsize(pDip, 2, &g_cbMMIO);
318 if (rc == DDI_SUCCESS)
319 {
320 rc = ddi_regs_map_setup(pDip, 2, &g_pMMIOBase, 0, g_cbMMIO, &deviceAttr,
321 &g_PciMMIOHandle);
322 if (rc == DDI_SUCCESS)
323 {
324 /*
325 * Add IRQ of VMMDev.
326 */
327 rc = vgdrvSolarisAddIRQ(pDip);
328 if (rc == DDI_SUCCESS)
329 {
330 /*
331 * Call the common device extension initializer.
332 */
333 rc = VGDrvCommonInitDevExt(&g_DevExt, g_uIOPortBase, g_pMMIOBase, g_cbMMIO,
334#if ARCH_BITS == 64
335 VBOXOSTYPE_Solaris_x64,
336#else
337 VBOXOSTYPE_Solaris,
338#endif
339 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
340 if (RT_SUCCESS(rc))
341 {
342 rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, 0 /* fFlags */);
343 if (rc == DDI_SUCCESS)
344 {
345 g_pDip = pDip;
346 pci_config_teardown(&PciHandle);
347 return DDI_SUCCESS;
348 }
349
350 LogRel((DEVICE_NAME "::Attach: ddi_create_minor_node failed.\n"));
351 VGDrvCommonDeleteDevExt(&g_DevExt);
352 }
353 else
354 LogRel((DEVICE_NAME "::Attach: VGDrvCommonInitDevExt failed.\n"));
355 vgdrvSolarisRemoveIRQ(pDip);
356 }
357 else
358 LogRel((DEVICE_NAME "::Attach: vgdrvSolarisAddIRQ failed.\n"));
359 ddi_regs_map_free(&g_PciMMIOHandle);
360 }
361 else
362 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for MMIO region failed.\n"));
363 }
364 else
365 LogRel((DEVICE_NAME "::Attach: ddi_dev_regsize for MMIO region failed.\n"));
366 ddi_regs_map_free(&g_PciIOHandle);
367 }
368 else
369 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for IOport failed.\n"));
370 pci_config_teardown(&PciHandle);
371 }
372 else
373 LogRel((DEVICE_NAME "::Attach: pci_config_setup failed rc=%d.\n", rc));
374 return DDI_FAILURE;
375 }
376
377 case DDI_RESUME:
378 {
379 /** @todo implement resume for guest driver. */
380 return DDI_SUCCESS;
381 }
382
383 default:
384 return DDI_FAILURE;
385 }
386}
387
388
389/**
390 * Detach entry point, to detach a device to the system or suspend it.
391 *
392 * @param pDip The module structure instance.
393 * @param enmCmd Attach type (ddi_attach_cmd_t)
394 *
395 * @return corresponding solaris error code.
396 */
397static int vgdrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
398{
399 LogFlow(("vgdrvSolarisDetach:\n"));
400 switch (enmCmd)
401 {
402 case DDI_DETACH:
403 {
404 vgdrvSolarisRemoveIRQ(pDip);
405 ddi_regs_map_free(&g_PciIOHandle);
406 ddi_regs_map_free(&g_PciMMIOHandle);
407 ddi_remove_minor_node(pDip, NULL);
408 VGDrvCommonDeleteDevExt(&g_DevExt);
409 g_pDip = NULL;
410 return DDI_SUCCESS;
411 }
412
413 case DDI_SUSPEND:
414 {
415 /** @todo implement suspend for guest driver. */
416 return DDI_SUCCESS;
417 }
418
419 default:
420 return DDI_FAILURE;
421 }
422}
423
424
425/**
426 * Quiesce entry point, called by solaris kernel for disabling the device from
427 * generating any interrupts or doing in-bound DMA.
428 *
429 * @param pDip The module structure instance.
430 *
431 * @return corresponding solaris error code.
432 */
433static int vgdrvSolarisQuiesce(dev_info_t *pDip)
434{
435 for (int i = 0; i < g_cIntrAllocated; i++)
436 {
437 int rc = ddi_intr_disable(g_pIntr[i]);
438 if (rc != DDI_SUCCESS)
439 return DDI_FAILURE;
440 }
441
442 /** @todo What about HGCM/HGSMI touching guest-memory? */
443
444 return DDI_SUCCESS;
445}
446
447
448/**
449 * Info entry point, called by solaris kernel for obtaining driver info.
450 *
451 * @param pDip The module structure instance (do not use).
452 * @param enmCmd Information request type.
453 * @param pvArg Type specific argument.
454 * @param ppvResult Where to store the requested info.
455 *
456 * @return corresponding solaris error code.
457 */
458static int vgdrvSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
459{
460 LogFlow(("vgdrvSolarisGetInfo:\n"));
461
462 int rc = DDI_SUCCESS;
463 switch (enmCmd)
464 {
465 case DDI_INFO_DEVT2DEVINFO:
466 *ppvResult = (void *)g_pDip;
467 break;
468
469 case DDI_INFO_DEVT2INSTANCE:
470 *ppvResult = (void *)(uintptr_t)ddi_get_instance(g_pDip);
471 break;
472
473 default:
474 rc = DDI_FAILURE;
475 break;
476 }
477
478 NOREF(pvArg);
479 return rc;
480}
481
482
483/**
484 * User context entry points
485 */
486static int vgdrvSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred)
487{
488 int rc;
489 PVBOXGUESTSESSION pSession = NULL;
490
491 LogFlow(("vgdrvSolarisOpen:\n"));
492
493 /*
494 * Verify we are being opened as a character device.
495 */
496 if (fType != OTYP_CHR)
497 return EINVAL;
498
499 vboxguest_state_t *pState = NULL;
500 unsigned iOpenInstance;
501 for (iOpenInstance = 0; iOpenInstance < 4096; iOpenInstance++)
502 {
503 if ( !ddi_get_soft_state(g_pvgdrvSolarisState, iOpenInstance) /* faster */
504 && ddi_soft_state_zalloc(g_pvgdrvSolarisState, iOpenInstance) == DDI_SUCCESS)
505 {
506 pState = ddi_get_soft_state(g_pvgdrvSolarisState, iOpenInstance);
507 break;
508 }
509 }
510 if (!pState)
511 {
512 Log(("vgdrvSolarisOpen: too many open instances."));
513 return ENXIO;
514 }
515
516 /*
517 * Create a new session.
518 */
519 rc = VGDrvCommonCreateUserSession(&g_DevExt, &pSession);
520 if (RT_SUCCESS(rc))
521 {
522 pState->pvProcRef = proc_ref();
523 pState->pSession = pSession;
524 *pDev = makedevice(getmajor(*pDev), iOpenInstance);
525 Log(("vgdrvSolarisOpen: pSession=%p pState=%p pid=%d\n", pSession, pState, (int)RTProcSelf()));
526 return 0;
527 }
528
529 /* Failed, clean up. */
530 ddi_soft_state_free(g_pvgdrvSolarisState, iOpenInstance);
531
532 LogRel((DEVICE_NAME "::Open: VGDrvCommonCreateUserSession failed. rc=%d\n", rc));
533 return EFAULT;
534}
535
536
537static int vgdrvSolarisClose(dev_t Dev, int flag, int fType, cred_t *pCred)
538{
539 LogFlow(("vgdrvSolarisClose: pid=%d\n", (int)RTProcSelf()));
540
541 PVBOXGUESTSESSION pSession = NULL;
542 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
543 if (!pState)
544 {
545 Log(("vgdrvSolarisClose: failed to get pState.\n"));
546 return EFAULT;
547 }
548
549 proc_unref(pState->pvProcRef);
550 pSession = pState->pSession;
551 pState->pSession = NULL;
552 Log(("vgdrvSolarisClose: pSession=%p pState=%p\n", pSession, pState));
553 ddi_soft_state_free(g_pvgdrvSolarisState, getminor(Dev));
554 if (!pSession)
555 {
556 Log(("vgdrvSolarisClose: failed to get pSession.\n"));
557 return EFAULT;
558 }
559
560 /*
561 * Close the session.
562 */
563 VGDrvCommonCloseSession(&g_DevExt, pSession);
564 return 0;
565}
566
567
568static int vgdrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
569{
570 LogFlow((DEVICE_NAME "::Read\n"));
571
572 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
573 if (!pState)
574 {
575 Log((DEVICE_NAME "::Close: failed to get pState.\n"));
576 return EFAULT;
577 }
578
579 PVBOXGUESTSESSION pSession = pState->pSession;
580 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
581 if (pSession->u32MousePosChangedSeq != u32CurSeq)
582 pSession->u32MousePosChangedSeq = u32CurSeq;
583
584 return 0;
585}
586
587
588static int vgdrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
589{
590 LogFlow(("vgdrvSolarisWrite:\n"));
591 return 0;
592}
593
594
595/** @def IOCPARM_LEN
596 * Gets the length from the ioctl number.
597 * This is normally defined by sys/ioccom.h on BSD systems...
598 */
599#ifndef IOCPARM_LEN
600# define IOCPARM_LEN(Code) (((Code) >> 16) & IOCPARM_MASK)
601#endif
602
603
604/**
605 * Driver ioctl, an alternate entry point for this character driver.
606 *
607 * @param Dev Device number
608 * @param Cmd Operation identifier
609 * @param pArg Arguments from user to driver
610 * @param Mode Information bitfield (read/write, address space etc.)
611 * @param pCred User credentials
612 * @param pVal Return value for calling process.
613 *
614 * @return corresponding solaris error code.
615 */
616static int vgdrvSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal)
617{
618 LogFlow(("vgdrvSolarisIOCtl: iCmd=%#x\n", Cmd));
619
620 /*
621 * Get the session from the soft state item.
622 */
623 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
624 if (!pState)
625 {
626 LogRel((DEVICE_NAME "::IOCtl: no state data for %d\n", getminor(Dev)));
627 return EINVAL;
628 }
629
630 PVBOXGUESTSESSION pSession = pState->pSession;
631 if (!pSession)
632 {
633 LogRel((DEVICE_NAME "::IOCtl: no session data for %d\n", getminor(Dev)));
634 return EINVAL;
635 }
636
637 /*
638 * Read and validate the request wrapper.
639 */
640 VBGLBIGREQ ReqWrap;
641 if (IOCPARM_LEN(Cmd) != sizeof(ReqWrap))
642 {
643 LogRel((DEVICE_NAME "::IOCtl: bad request %#x size=%d expected=%d\n", Cmd, IOCPARM_LEN(Cmd), sizeof(ReqWrap)));
644 return ENOTTY;
645 }
646
647 int rc = ddi_copyin((void *)pArg, &ReqWrap, sizeof(ReqWrap), Mode);
648 if (RT_UNLIKELY(rc))
649 {
650 LogRel((DEVICE_NAME "::IOCtl: ddi_copyin failed to read header pArg=%p Cmd=%d. rc=%#x.\n", pArg, Cmd, rc));
651 return EINVAL;
652 }
653
654 if (ReqWrap.u32Magic != VBGLBIGREQ_MAGIC)
655 {
656 LogRel((DEVICE_NAME "::IOCtl: bad magic %#x; pArg=%p Cmd=%#x.\n", ReqWrap.u32Magic, pArg, Cmd));
657 return EINVAL;
658 }
659 if (RT_UNLIKELY(ReqWrap.cbData > _1M*16))
660 {
661 LogRel((DEVICE_NAME "::IOCtl: bad size %#x; pArg=%p Cmd=%#x.\n", ReqWrap.cbData, pArg, Cmd));
662 return EINVAL;
663 }
664
665 /*
666 * Read the request payload if any; requests like VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS have no data payload.
667 */
668 void *pvBuf = NULL;
669 if (RT_LIKELY(ReqWrap.cbData > 0))
670 {
671 pvBuf = RTMemTmpAlloc(ReqWrap.cbData);
672 if (RT_UNLIKELY(!pvBuf))
673 {
674 LogRel((DEVICE_NAME "::IOCtl: RTMemTmpAlloc failed to alloc %d bytes.\n", ReqWrap.cbData));
675 return ENOMEM;
676 }
677
678 rc = ddi_copyin((void *)(uintptr_t)ReqWrap.pvDataR3, pvBuf, ReqWrap.cbData, Mode);
679 if (RT_UNLIKELY(rc))
680 {
681 RTMemTmpFree(pvBuf);
682 LogRel((DEVICE_NAME "::IOCtl: ddi_copyin failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc));
683 return EFAULT;
684 }
685 if (RT_UNLIKELY(!VALID_PTR(pvBuf)))
686 {
687 RTMemTmpFree(pvBuf);
688 LogRel((DEVICE_NAME "::IOCtl: pvBuf invalid pointer %p\n", pvBuf));
689 return EINVAL;
690 }
691 }
692 Log(("vgdrvSolarisIOCtl: pSession=%p pid=%d.\n", pSession, (int)RTProcSelf()));
693
694 /*
695 * Process the IOCtl.
696 */
697 size_t cbDataReturned = 0;
698 rc = VGDrvCommonIoCtl(Cmd, &g_DevExt, pSession, pvBuf, ReqWrap.cbData, &cbDataReturned);
699 if (RT_SUCCESS(rc))
700 {
701 rc = 0;
702 if (RT_UNLIKELY(cbDataReturned > ReqWrap.cbData))
703 {
704 LogRel((DEVICE_NAME "::IOCtl: too much output data %d expected %d\n", cbDataReturned, ReqWrap.cbData));
705 cbDataReturned = ReqWrap.cbData;
706 }
707 if (cbDataReturned > 0)
708 {
709 rc = ddi_copyout(pvBuf, (void *)(uintptr_t)ReqWrap.pvDataR3, cbDataReturned, Mode);
710 if (RT_UNLIKELY(rc))
711 {
712 LogRel((DEVICE_NAME "::IOCtl: ddi_copyout failed; pvBuf=%p pArg=%p cbDataReturned=%u Cmd=%d. rc=%d\n",
713 pvBuf, pArg, cbDataReturned, Cmd, rc));
714 rc = EFAULT;
715 }
716 }
717 }
718 else
719 {
720 /*
721 * We Log() instead of LogRel() here because VBOXGUEST_IOCTL_WAITEVENT can return VERR_TIMEOUT,
722 * VBOXGUEST_IOCTL_CANCEL_ALL_EVENTS can return VERR_INTERRUPTED and possibly more in the future;
723 * which are not really failures that require logging.
724 */
725 Log(("vgdrvSolarisIOCtl: VGDrvCommonIoCtl failed. Cmd=%#x rc=%d\n", Cmd, rc));
726 if (rc == VERR_PERMISSION_DENIED) /* RTErrConvertToErrno() below will ring-0 debug assert if we don't do this. */
727 rc = VERR_ACCESS_DENIED;
728 rc = RTErrConvertToErrno(rc);
729 }
730 *pVal = rc;
731 if (pvBuf)
732 RTMemTmpFree(pvBuf);
733 return rc;
734}
735
736
737static int vgdrvSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead)
738{
739 LogFlow(("vgdrvSolarisPoll: fEvents=%d fAnyYet=%d\n", fEvents, fAnyYet));
740
741 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
742 if (RT_LIKELY(pState))
743 {
744 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pState->pSession;
745 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
746 if (pSession->u32MousePosChangedSeq != u32CurSeq)
747 {
748 *pReqEvents |= (POLLIN | POLLRDNORM);
749 pSession->u32MousePosChangedSeq = u32CurSeq;
750 }
751 else
752 {
753 *pReqEvents = 0;
754 if (!fAnyYet)
755 *ppPollHead = &g_PollHead;
756 }
757
758 return 0;
759 }
760 else
761 {
762 Log(("vgdrvSolarisPoll: no state data for %d\n", getminor(Dev)));
763 return EINVAL;
764 }
765}
766
767
768/**
769 * Sets IRQ for VMMDev.
770 *
771 * @returns Solaris error code.
772 * @param pDip Pointer to the device info structure.
773 */
774static int vgdrvSolarisAddIRQ(dev_info_t *pDip)
775{
776 LogFlow(("vgdrvSolarisAddIRQ: pDip=%p\n", pDip));
777
778 int IntrType = 0;
779 int rc = ddi_intr_get_supported_types(pDip, &IntrType);
780 if (rc == DDI_SUCCESS)
781 {
782 /* We won't need to bother about MSIs. */
783 if (IntrType & DDI_INTR_TYPE_FIXED)
784 {
785 int IntrCount = 0;
786 rc = ddi_intr_get_nintrs(pDip, IntrType, &IntrCount);
787 if ( rc == DDI_SUCCESS
788 && IntrCount > 0)
789 {
790 int IntrAvail = 0;
791 rc = ddi_intr_get_navail(pDip, IntrType, &IntrAvail);
792 if ( rc == DDI_SUCCESS
793 && IntrAvail > 0)
794 {
795 /* Allocated kernel memory for the interrupt handles. The allocation size is stored internally. */
796 g_pIntr = RTMemAlloc(IntrCount * sizeof(ddi_intr_handle_t));
797 if (g_pIntr)
798 {
799 int IntrAllocated;
800 rc = ddi_intr_alloc(pDip, g_pIntr, IntrType, 0, IntrCount, &IntrAllocated, DDI_INTR_ALLOC_NORMAL);
801 if ( rc == DDI_SUCCESS
802 && IntrAllocated > 0)
803 {
804 g_cIntrAllocated = IntrAllocated;
805 uint_t uIntrPriority;
806 rc = ddi_intr_get_pri(g_pIntr[0], &uIntrPriority);
807 if (rc == DDI_SUCCESS)
808 {
809 /* Initialize the mutex. */
810 mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
811
812 /* Assign interrupt handler functions and enable interrupts. */
813 for (int i = 0; i < IntrAllocated; i++)
814 {
815 rc = ddi_intr_add_handler(g_pIntr[i], (ddi_intr_handler_t *)vgdrvSolarisISR,
816 NULL /* pvArg1 */, NULL /* pvArg2 */);
817 if (rc == DDI_SUCCESS)
818 rc = ddi_intr_enable(g_pIntr[i]);
819 if (rc != DDI_SUCCESS)
820 {
821 /* Changing local IntrAllocated to hold so-far allocated handles for freeing. */
822 IntrAllocated = i;
823 break;
824 }
825 }
826 if (rc == DDI_SUCCESS)
827 return rc;
828
829 /* Remove any assigned handlers */
830 LogRel((DEVICE_NAME ":failed to assign IRQs allocated=%d\n", IntrAllocated));
831 for (int x = 0; x < IntrAllocated; x++)
832 ddi_intr_remove_handler(g_pIntr[x]);
833 }
834 else
835 LogRel((DEVICE_NAME "::AddIRQ: failed to get priority of interrupt. rc=%d\n", rc));
836
837 /* Remove allocated IRQs, too bad we can free only one handle at a time. */
838 for (int k = 0; k < g_cIntrAllocated; k++)
839 ddi_intr_free(g_pIntr[k]);
840 g_cIntrAllocated = 0;
841 }
842 else
843 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
844 RTMemFree(g_pIntr);
845 }
846 else
847 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
848 }
849 else
850 LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient available IRQs. rc=%d IntrAvail=%d\n", rc, IntrAvail));
851 }
852 else
853 LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient number of IRQs. rc=%d IntrCount=%d\n", rc, IntrCount));
854 }
855 else
856 LogRel((DEVICE_NAME "::AddIRQ: invalid irq type. IntrType=%#x\n", IntrType));
857 }
858 else
859 LogRel((DEVICE_NAME "::AddIRQ: failed to get supported interrupt types\n"));
860 return rc;
861}
862
863
864/**
865 * Removes IRQ for VMMDev.
866 *
867 * @param pDip Pointer to the device info structure.
868 */
869static void vgdrvSolarisRemoveIRQ(dev_info_t *pDip)
870{
871 LogFlow(("vgdrvSolarisRemoveIRQ:\n"));
872
873 for (int i = 0; i < g_cIntrAllocated; i++)
874 {
875 int rc = ddi_intr_disable(g_pIntr[i]);
876 if (rc == DDI_SUCCESS)
877 {
878 rc = ddi_intr_remove_handler(g_pIntr[i]);
879 if (rc == DDI_SUCCESS)
880 ddi_intr_free(g_pIntr[i]);
881 }
882 }
883 g_cIntrAllocated = 0;
884 RTMemFree(g_pIntr);
885 mutex_destroy(&g_IrqMtx);
886}
887
888
889/**
890 * Interrupt Service Routine for VMMDev.
891 *
892 * @param Arg Private data (unused, will be NULL).
893 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
894 */
895static uint_t vgdrvSolarisISR(caddr_t Arg)
896{
897 LogFlow(("vgdrvSolarisISR:\n"));
898
899 mutex_enter(&g_IrqMtx);
900 bool fOurIRQ = VGDrvCommonISR(&g_DevExt);
901 mutex_exit(&g_IrqMtx);
902
903 return fOurIRQ ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED;
904}
905
906
907void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
908{
909 LogFlow(("VGDrvNativeISRMousePollEvent:\n"));
910
911 /*
912 * Wake up poll waiters.
913 */
914 pollwakeup(&g_PollHead, POLLIN | POLLRDNORM);
915}
916
917
918/* Common code that depend on g_DevExt. */
919#include "VBoxGuestIDC-unix.c.h"
920
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