VirtualBox

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

Last change on this file since 28244 was 27158, checked in by vboxsync, 15 years ago

Additions/VBoxGuest-solaris: logging.

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