VirtualBox

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

Last change on this file since 56294 was 56219, checked in by vboxsync, 9 years ago

Additions/solaris: revert r92087, though not strictly necessary due to order of driver hooks and guarantees, this makes the code *much* more readable and explicit.

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