VirtualBox

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

Last change on this file since 36347 was 36347, checked in by vboxsync, 14 years ago

Runtime/log: adapt more places

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