VirtualBox

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

Last change on this file since 46052 was 46052, checked in by vboxsync, 12 years ago

Additions/solaris/vboxguest: convert to non-fatal error code on ioctl() failure.

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