VirtualBox

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

Last change on this file since 93516 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.0 KB
Line 
1/* $Id: VBoxGuest-solaris.c 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions Driver for Solaris.
4 */
5
6/*
7 * Copyright (C) 2007-2022 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/policy.h>
42#include <sys/file.h>
43#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
44
45#include "VBoxGuestInternal.h"
46#include <VBox/log.h>
47#include <VBox/version.h>
48#include <iprt/assert.h>
49#include <iprt/initterm.h>
50#include <iprt/process.h>
51#include <iprt/mem.h>
52#include <iprt/cdefs.h>
53#include <iprt/asm.h>
54
55
56/*********************************************************************************************************************************
57* Defined Constants And Macros *
58*********************************************************************************************************************************/
59/** The module name. */
60#define DEVICE_NAME "vboxguest"
61/** The module description as seen in 'modinfo'. */
62#define DEVICE_DESC "VirtualBox GstDrv"
63
64
65/*********************************************************************************************************************************
66* Internal Functions *
67*********************************************************************************************************************************/
68static int vgdrvSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred);
69static int vgdrvSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred);
70static int vgdrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred);
71static int vgdrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred);
72static int vgdrvSolarisIOCtl(dev_t Dev, int iCmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal);
73static int vgdrvSolarisIOCtlSlow(PVBOXGUESTSESSION pSession, int iCmd, int Mode, intptr_t iArgs);
74static int vgdrvSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead);
75
76static int vgdrvSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
77static int vgdrvSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
78static int vgdrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
79static int vgdrvSolarisQuiesce(dev_info_t *pDip);
80
81static int vgdrvSolarisAddIRQ(dev_info_t *pDip);
82static void vgdrvSolarisRemoveIRQ(dev_info_t *pDip);
83static uint_t vgdrvSolarisHighLevelISR(caddr_t Arg);
84static uint_t vgdrvSolarisISR(caddr_t Arg);
85
86
87/*********************************************************************************************************************************
88* Structures and Typedefs *
89*********************************************************************************************************************************/
90/**
91 * cb_ops: for drivers that support char/block entry points
92 */
93static struct cb_ops g_vgdrvSolarisCbOps =
94{
95 vgdrvSolarisOpen,
96 vgdrvSolarisClose,
97 nodev, /* b strategy */
98 nodev, /* b dump */
99 nodev, /* b print */
100 vgdrvSolarisRead,
101 vgdrvSolarisWrite,
102 vgdrvSolarisIOCtl,
103 nodev, /* c devmap */
104 nodev, /* c mmap */
105 nodev, /* c segmap */
106 vgdrvSolarisPoll,
107 ddi_prop_op, /* property ops */
108 NULL, /* streamtab */
109 D_NEW | D_MP, /* compat. flag */
110 CB_REV /* revision */
111};
112
113/**
114 * dev_ops: for driver device operations
115 */
116static struct dev_ops g_vgdrvSolarisDevOps =
117{
118 DEVO_REV, /* driver build revision */
119 0, /* ref count */
120 vgdrvSolarisGetInfo,
121 nulldev, /* identify */
122 nulldev, /* probe */
123 vgdrvSolarisAttach,
124 vgdrvSolarisDetach,
125 nodev, /* reset */
126 &g_vgdrvSolarisCbOps,
127 (struct bus_ops *)0,
128 nodev, /* power */
129 vgdrvSolarisQuiesce
130};
131
132/**
133 * modldrv: export driver specifics to the kernel
134 */
135static struct modldrv g_vgdrvSolarisModule =
136{
137 &mod_driverops, /* extern from kernel */
138 DEVICE_DESC " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
139 &g_vgdrvSolarisDevOps
140};
141
142/**
143 * modlinkage: export install/remove/info to the kernel
144 */
145static struct modlinkage g_vgdrvSolarisModLinkage =
146{
147 MODREV_1, /* loadable module system revision */
148 &g_vgdrvSolarisModule,
149 NULL /* terminate array of linkage structures */
150};
151
152/**
153 * State info for each open file handle.
154 */
155typedef struct
156{
157 /** Pointer to the session handle. */
158 PVBOXGUESTSESSION pSession;
159 /** The process reference for posting signals */
160 void *pvProcRef;
161} vboxguest_state_t;
162
163
164/*********************************************************************************************************************************
165* Global Variables *
166*********************************************************************************************************************************/
167/** Device handle (we support only one instance). */
168static dev_info_t *g_pDip = NULL;
169/** Opaque pointer to file-descriptor states */
170static void *g_pvgdrvSolarisState = NULL;
171/** Device extention & session data association structure. */
172static VBOXGUESTDEVEXT g_DevExt;
173/** IO port handle. */
174static ddi_acc_handle_t g_PciIOHandle;
175/** MMIO handle. */
176static ddi_acc_handle_t g_PciMMIOHandle;
177/** IO Port. */
178static uint16_t g_uIOPortBase;
179/** Address of the MMIO region.*/
180static caddr_t g_pMMIOBase;
181/** Size of the MMIO region. */
182static off_t g_cbMMIO;
183/** Pointer to an array of interrupt handles. */
184static ddi_intr_handle_t *g_pahIntrs;
185/** Handle to the soft interrupt. */
186static ddi_softint_handle_t g_hSoftIntr;
187/** The pollhead structure */
188static pollhead_t g_PollHead;
189/** The IRQ Mutex */
190static kmutex_t g_IrqMtx;
191/** The IRQ high-level Mutex. */
192static kmutex_t g_HighLevelIrqMtx;
193/** Whether soft-ints are setup. */
194static bool g_fSoftIntRegistered = false;
195
196/** Additional IPRT function we need to drag in for vboxfs. */
197PFNRT g_Deps[] =
198{
199 (PFNRT)RTErrConvertToErrno,
200};
201
202
203/**
204 * Kernel entry points
205 */
206int _init(void)
207{
208 /*
209 * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
210 */
211 int rc = RTR0Init(0);
212 if (RT_SUCCESS(rc))
213 {
214 PRTLOGGER pRelLogger;
215 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
216 rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all",
217 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
218 RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER, NULL);
219 if (RT_SUCCESS(rc))
220 RTLogRelSetDefaultInstance(pRelLogger);
221 else
222 cmn_err(CE_NOTE, "failed to initialize driver logging rc=%d!\n", rc);
223
224 /*
225 * Prevent module autounloading.
226 */
227 modctl_t *pModCtl = mod_getctl(&g_vgdrvSolarisModLinkage);
228 if (pModCtl)
229 pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
230 else
231 LogRel((DEVICE_NAME ": failed to disable autounloading!\n"));
232
233 rc = ddi_soft_state_init(&g_pvgdrvSolarisState, sizeof(vboxguest_state_t), 1);
234 if (!rc)
235 {
236 rc = mod_install(&g_vgdrvSolarisModLinkage);
237 if (rc)
238 ddi_soft_state_fini(&g_pvgdrvSolarisState);
239 }
240 }
241 else
242 {
243 cmn_err(CE_NOTE, "_init: RTR0Init failed. rc=%d\n", rc);
244 return EINVAL;
245 }
246
247 return rc;
248}
249
250
251int _fini(void)
252{
253 LogFlow((DEVICE_NAME ":_fini\n"));
254 int rc = mod_remove(&g_vgdrvSolarisModLinkage);
255 if (!rc)
256 ddi_soft_state_fini(&g_pvgdrvSolarisState);
257
258 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
259 RTLogDestroy(RTLogSetDefaultInstance(NULL));
260
261 if (!rc)
262 RTR0Term();
263 return rc;
264}
265
266
267int _info(struct modinfo *pModInfo)
268{
269 /* LogFlow((DEVICE_NAME ":_info\n")); - Called too early, causing RTThreadPreemtIsEnabled warning. */
270 return mod_info(&g_vgdrvSolarisModLinkage, pModInfo);
271}
272
273
274/**
275 * Attach entry point, to attach a device to the system or resume it.
276 *
277 * @param pDip The module structure instance.
278 * @param enmCmd Attach type (ddi_attach_cmd_t)
279 *
280 * @return corresponding solaris error code.
281 */
282static int vgdrvSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
283{
284 LogFlow(("vgdrvSolarisAttach:\n"));
285 switch (enmCmd)
286 {
287 case DDI_ATTACH:
288 {
289 if (g_pDip)
290 {
291 LogRel(("vgdrvSolarisAttach: Only one instance supported.\n"));
292 return DDI_FAILURE;
293 }
294
295 /*
296 * Enable resources for PCI access.
297 */
298 ddi_acc_handle_t PciHandle;
299 int rc = pci_config_setup(pDip, &PciHandle);
300 if (rc == DDI_SUCCESS)
301 {
302 /*
303 * Map the register address space.
304 */
305 caddr_t baseAddr;
306 ddi_device_acc_attr_t deviceAttr;
307 deviceAttr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
308 deviceAttr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
309 deviceAttr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
310 deviceAttr.devacc_attr_access = DDI_DEFAULT_ACC;
311 rc = ddi_regs_map_setup(pDip, 1, &baseAddr, 0, 0, &deviceAttr, &g_PciIOHandle);
312 if (rc == DDI_SUCCESS)
313 {
314 /*
315 * Read size of the MMIO region.
316 */
317 g_uIOPortBase = (uintptr_t)baseAddr;
318 rc = ddi_dev_regsize(pDip, 2, &g_cbMMIO);
319 if (rc == DDI_SUCCESS)
320 {
321 rc = ddi_regs_map_setup(pDip, 2, &g_pMMIOBase, 0, g_cbMMIO, &deviceAttr, &g_PciMMIOHandle);
322 if (rc == DDI_SUCCESS)
323 {
324 /*
325 * Call the common device extension initializer.
326 */
327 rc = VGDrvCommonInitDevExt(&g_DevExt, g_uIOPortBase, g_pMMIOBase, g_cbMMIO,
328#if ARCH_BITS == 64
329 VBOXOSTYPE_Solaris_x64,
330#else
331 VBOXOSTYPE_Solaris,
332#endif
333 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
334 if (RT_SUCCESS(rc))
335 {
336 /*
337 * Add IRQ of VMMDev.
338 */
339 rc = vgdrvSolarisAddIRQ(pDip);
340 if (rc == DDI_SUCCESS)
341 {
342 /*
343 * Read host configuration.
344 */
345 VGDrvCommonProcessOptionsFromHost(&g_DevExt);
346
347 rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, 0 /* instance */, DDI_PSEUDO,
348 0 /* fFlags */);
349 if (rc == DDI_SUCCESS)
350 {
351 g_pDip = pDip;
352 pci_config_teardown(&PciHandle);
353 return DDI_SUCCESS;
354 }
355
356 LogRel((DEVICE_NAME "::Attach: ddi_create_minor_node failed.\n"));
357 vgdrvSolarisRemoveIRQ(pDip);
358 }
359 else
360 LogRel((DEVICE_NAME "::Attach: vgdrvSolarisAddIRQ failed.\n"));
361 VGDrvCommonDeleteDevExt(&g_DevExt);
362 }
363 else
364 LogRel((DEVICE_NAME "::Attach: VGDrvCommonInitDevExt failed.\n"));
365 ddi_regs_map_free(&g_PciMMIOHandle);
366 }
367 else
368 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for MMIO region failed.\n"));
369 }
370 else
371 LogRel((DEVICE_NAME "::Attach: ddi_dev_regsize for MMIO region failed.\n"));
372 ddi_regs_map_free(&g_PciIOHandle);
373 }
374 else
375 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for IOport failed.\n"));
376 pci_config_teardown(&PciHandle);
377 }
378 else
379 LogRel((DEVICE_NAME "::Attach: pci_config_setup failed rc=%d.\n", rc));
380 return DDI_FAILURE;
381 }
382
383 case DDI_RESUME:
384 {
385 /** @todo implement resume for guest driver. */
386 return DDI_SUCCESS;
387 }
388
389 default:
390 return DDI_FAILURE;
391 }
392}
393
394
395/**
396 * Detach entry point, to detach a device to the system or suspend it.
397 *
398 * @param pDip The module structure instance.
399 * @param enmCmd Attach type (ddi_attach_cmd_t)
400 *
401 * @return corresponding solaris error code.
402 */
403static int vgdrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
404{
405 LogFlow(("vgdrvSolarisDetach:\n"));
406 switch (enmCmd)
407 {
408 case DDI_DETACH:
409 {
410 vgdrvSolarisRemoveIRQ(pDip);
411 ddi_regs_map_free(&g_PciIOHandle);
412 ddi_regs_map_free(&g_PciMMIOHandle);
413 ddi_remove_minor_node(pDip, NULL);
414 VGDrvCommonDeleteDevExt(&g_DevExt);
415 g_pDip = NULL;
416 return DDI_SUCCESS;
417 }
418
419 case DDI_SUSPEND:
420 {
421 /** @todo implement suspend for guest driver. */
422 return DDI_SUCCESS;
423 }
424
425 default:
426 return DDI_FAILURE;
427 }
428}
429
430
431/**
432 * Quiesce entry point, called by solaris kernel for disabling the device from
433 * generating any interrupts or doing in-bound DMA.
434 *
435 * @param pDip The module structure instance.
436 *
437 * @return corresponding solaris error code.
438 */
439static int vgdrvSolarisQuiesce(dev_info_t *pDip)
440{
441 int rc = ddi_intr_disable(g_pahIntrs[0]);
442 if (rc != DDI_SUCCESS)
443 return DDI_FAILURE;
444
445 /** @todo What about HGCM/HGSMI touching guest-memory? */
446
447 return DDI_SUCCESS;
448}
449
450
451/**
452 * Info entry point, called by solaris kernel for obtaining driver info.
453 *
454 * @param pDip The module structure instance (do not use).
455 * @param enmCmd Information request type.
456 * @param pvArg Type specific argument.
457 * @param ppvResult Where to store the requested info.
458 *
459 * @return corresponding solaris error code.
460 */
461static int vgdrvSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
462{
463 LogFlow(("vgdrvSolarisGetInfo:\n"));
464
465 int rc = DDI_SUCCESS;
466 switch (enmCmd)
467 {
468 case DDI_INFO_DEVT2DEVINFO:
469 {
470 *ppvResult = (void *)g_pDip;
471 if (!*ppvResult)
472 rc = DDI_FAILURE;
473 break;
474 }
475
476 case DDI_INFO_DEVT2INSTANCE:
477 {
478 /* There can only be a single-instance of this driver and thus its instance number is 0. */
479 *ppvResult = (void *)0;
480 break;
481 }
482
483 default:
484 rc = DDI_FAILURE;
485 break;
486 }
487
488 NOREF(pvArg);
489 return rc;
490}
491
492
493/**
494 * User context entry points
495 *
496 * @remarks fFlags are the flags passed to open() or to ldi_open_by_name. In
497 * the latter case the FKLYR flag is added to indicate that the caller
498 * is a kernel component rather than user land.
499 */
500static int vgdrvSolarisOpen(dev_t *pDev, int fFlags, int fType, cred_t *pCred)
501{
502 int rc;
503 PVBOXGUESTSESSION pSession = NULL;
504
505 LogFlow(("vgdrvSolarisOpen:\n"));
506
507 /*
508 * Verify we are being opened as a character device.
509 */
510 if (fType != OTYP_CHR)
511 return EINVAL;
512
513 vboxguest_state_t *pState = NULL;
514 unsigned iOpenInstance;
515 for (iOpenInstance = 0; iOpenInstance < 4096; iOpenInstance++)
516 {
517 if ( !ddi_get_soft_state(g_pvgdrvSolarisState, iOpenInstance) /* faster */
518 && ddi_soft_state_zalloc(g_pvgdrvSolarisState, iOpenInstance) == DDI_SUCCESS)
519 {
520 pState = ddi_get_soft_state(g_pvgdrvSolarisState, iOpenInstance);
521 break;
522 }
523 }
524 if (!pState)
525 {
526 Log(("vgdrvSolarisOpen: too many open instances."));
527 return ENXIO;
528 }
529
530 /*
531 * Create a new session.
532 *
533 * Note! The devfs inode with the gid isn't readily available here, so we cannot easily
534 * to the vbox group detection like on linux. Read config instead?
535 */
536 if (!(fFlags & FKLYR))
537 {
538 uint32_t fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN;
539 if (crgetruid(pCred) == 0)
540 fRequestor |= VMMDEV_REQUESTOR_USR_ROOT;
541 else
542 fRequestor |= VMMDEV_REQUESTOR_USR_USER;
543 if (secpolicy_coreadm(pCred) == 0)
544 fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL;
545 /** @todo is there any way of detecting that the process belongs to someone on the physical console?
546 * secpolicy_console() [== PRIV_SYS_DEVICES] doesn't look quite right, or does it? */
547 fRequestor |= VMMDEV_REQUESTOR_CON_DONT_KNOW;
548 fRequestor |= VMMDEV_REQUESTOR_NO_USER_DEVICE; /** @todo implement vboxuser device node. */
549
550 rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &pSession);
551 }
552 else
553 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
554 if (RT_SUCCESS(rc))
555 {
556 if (!(fFlags & FKLYR))
557 pState->pvProcRef = proc_ref();
558 else
559 pState->pvProcRef = NULL;
560 pState->pSession = pSession;
561 *pDev = makedevice(getmajor(*pDev), iOpenInstance);
562 Log(("vgdrvSolarisOpen: pSession=%p pState=%p pid=%d\n", pSession, pState, (int)RTProcSelf()));
563 return 0;
564 }
565
566 /* Failed, clean up. */
567 ddi_soft_state_free(g_pvgdrvSolarisState, iOpenInstance);
568
569 LogRel((DEVICE_NAME "::Open: VGDrvCommonCreateUserSession failed. rc=%d\n", rc));
570 return EFAULT;
571}
572
573
574static int vgdrvSolarisClose(dev_t Dev, int flag, int fType, cred_t *pCred)
575{
576 LogFlow(("vgdrvSolarisClose: pid=%d\n", (int)RTProcSelf()));
577
578 PVBOXGUESTSESSION pSession = NULL;
579 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
580 if (!pState)
581 {
582 Log(("vgdrvSolarisClose: failed to get pState.\n"));
583 return EFAULT;
584 }
585
586 if (pState->pvProcRef != NULL)
587 {
588 proc_unref(pState->pvProcRef);
589 pState->pvProcRef = NULL;
590 }
591 pSession = pState->pSession;
592 pState->pSession = NULL;
593 Log(("vgdrvSolarisClose: pSession=%p pState=%p\n", pSession, pState));
594 ddi_soft_state_free(g_pvgdrvSolarisState, getminor(Dev));
595 if (!pSession)
596 {
597 Log(("vgdrvSolarisClose: failed to get pSession.\n"));
598 return EFAULT;
599 }
600
601 /*
602 * Close the session.
603 */
604 if (pSession)
605 VGDrvCommonCloseSession(&g_DevExt, pSession);
606 return 0;
607}
608
609
610static int vgdrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
611{
612 LogFlow((DEVICE_NAME "::Read\n"));
613
614 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
615 if (!pState)
616 {
617 Log((DEVICE_NAME "::Close: failed to get pState.\n"));
618 return EFAULT;
619 }
620
621 PVBOXGUESTSESSION pSession = pState->pSession;
622 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
623 if (pSession->u32MousePosChangedSeq != u32CurSeq)
624 pSession->u32MousePosChangedSeq = u32CurSeq;
625
626 return 0;
627}
628
629
630static int vgdrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
631{
632 LogFlow(("vgdrvSolarisWrite:\n"));
633 return 0;
634}
635
636
637/** @def IOCPARM_LEN
638 * Gets the length from the ioctl number.
639 * This is normally defined by sys/ioccom.h on BSD systems...
640 */
641#ifndef IOCPARM_LEN
642# define IOCPARM_LEN(x) ( ((x) >> 16) & IOCPARM_MASK )
643#endif
644
645
646/**
647 * Driver ioctl, an alternate entry point for this character driver.
648 *
649 * @param Dev Device number
650 * @param iCmd Operation identifier
651 * @param iArgs Arguments from user to driver
652 * @param Mode Information bitfield (read/write, address space etc.)
653 * @param pCred User credentials
654 * @param pVal Return value for calling process.
655 *
656 * @return corresponding solaris error code.
657 */
658static int vgdrvSolarisIOCtl(dev_t Dev, int iCmd, intptr_t iArgs, int Mode, cred_t *pCred, int *pVal)
659{
660 /*
661 * Get the session from the soft state item.
662 */
663 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
664 if (!pState)
665 {
666 LogRel(("vgdrvSolarisIOCtl: no state data for %#x (%d)\n", Dev, getminor(Dev)));
667 return EINVAL;
668 }
669
670 PVBOXGUESTSESSION pSession = pState->pSession;
671 if (!pSession)
672 {
673 LogRel(("vgdrvSolarisIOCtl: no session in state data for %#x (%d)\n", Dev, getminor(Dev)));
674 return DDI_SUCCESS;
675 }
676
677 /*
678 * Deal with fast requests.
679 */
680 if (VBGL_IOCTL_IS_FAST(iCmd))
681 {
682 *pVal = VGDrvCommonIoCtlFast(iCmd, &g_DevExt, pSession);
683 return 0;
684 }
685
686 /*
687 * It's kind of simple if this is a kernel session, take slow path if user land.
688 */
689 if (pSession->R0Process == NIL_RTR0PROCESS)
690 {
691 if (IOCPARM_LEN(iCmd) == sizeof(VBGLREQHDR))
692 {
693 PVBGLREQHDR pHdr = (PVBGLREQHDR)iArgs;
694 int rc;
695 if (iCmd != VBGL_IOCTL_IDC_DISCONNECT)
696 rc =VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, RT_MAX(pHdr->cbIn, pHdr->cbOut));
697 else
698 {
699 pState->pSession = NULL;
700 rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, RT_MAX(pHdr->cbIn, pHdr->cbOut));
701 if (RT_FAILURE(rc))
702 pState->pSession = pSession;
703 }
704 return rc;
705 }
706 }
707
708 return vgdrvSolarisIOCtlSlow(pSession, iCmd, Mode, iArgs);
709}
710
711
712/**
713 * Worker for VBoxSupDrvIOCtl that takes the slow IOCtl functions.
714 *
715 * @returns Solaris errno.
716 *
717 * @param pSession The session.
718 * @param iCmd The IOCtl command.
719 * @param Mode Information bitfield (for specifying ownership of data)
720 * @param iArg User space address of the request buffer.
721 */
722static int vgdrvSolarisIOCtlSlow(PVBOXGUESTSESSION pSession, int iCmd, int Mode, intptr_t iArg)
723{
724 int rc;
725 uint32_t cbBuf = 0;
726 union
727 {
728 VBGLREQHDR Hdr;
729 uint8_t abBuf[64];
730 } StackBuf;
731 PVBGLREQHDR pHdr;
732
733
734 /*
735 * Read the header.
736 */
737 if (RT_UNLIKELY(IOCPARM_LEN(iCmd) != sizeof(StackBuf.Hdr)))
738 {
739 LogRel(("vgdrvSolarisIOCtlSlow: iCmd=%#x len %d expected %d\n", iCmd, IOCPARM_LEN(iCmd), sizeof(StackBuf.Hdr)));
740 return EINVAL;
741 }
742 rc = ddi_copyin((void *)iArg, &StackBuf.Hdr, sizeof(StackBuf.Hdr), Mode);
743 if (RT_UNLIKELY(rc))
744 {
745 LogRel(("vgdrvSolarisIOCtlSlow: ddi_copyin(,%#lx,) failed; iCmd=%#x. rc=%d\n", iArg, iCmd, rc));
746 return EFAULT;
747 }
748 if (RT_UNLIKELY(StackBuf.Hdr.uVersion != VBGLREQHDR_VERSION))
749 {
750 LogRel(("vgdrvSolarisIOCtlSlow: bad header version %#x; iCmd=%#x\n", StackBuf.Hdr.uVersion, iCmd));
751 return EINVAL;
752 }
753 cbBuf = RT_MAX(StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut);
754 if (RT_UNLIKELY( StackBuf.Hdr.cbIn < sizeof(StackBuf.Hdr)
755 || (StackBuf.Hdr.cbOut < sizeof(StackBuf.Hdr) && StackBuf.Hdr.cbOut != 0)
756 || cbBuf > _1M*16))
757 {
758 LogRel(("vgdrvSolarisIOCtlSlow: max(%#x,%#x); iCmd=%#x\n", StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut, iCmd));
759 return EINVAL;
760 }
761
762 /*
763 * Buffer the request.
764 *
765 * Note! Common code revalidates the header sizes and version. So it's
766 * fine to read it once more.
767 */
768 if (cbBuf <= sizeof(StackBuf))
769 pHdr = &StackBuf.Hdr;
770 else
771 {
772 pHdr = RTMemTmpAlloc(cbBuf);
773 if (RT_UNLIKELY(!pHdr))
774 {
775 LogRel(("vgdrvSolarisIOCtlSlow: failed to allocate buffer of %d bytes for iCmd=%#x.\n", cbBuf, iCmd));
776 return ENOMEM;
777 }
778 }
779 rc = ddi_copyin((void *)iArg, pHdr, cbBuf, Mode);
780 if (RT_UNLIKELY(rc))
781 {
782 LogRel(("vgdrvSolarisIOCtlSlow: copy_from_user(,%#lx, %#x) failed; iCmd=%#x. rc=%d\n", iArg, cbBuf, iCmd, rc));
783 if (pHdr != &StackBuf.Hdr)
784 RTMemFree(pHdr);
785 return EFAULT;
786 }
787
788 /*
789 * Process the IOCtl.
790 */
791 rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, cbBuf);
792
793 /*
794 * Copy ioctl data and output buffer back to user space.
795 */
796 if (RT_SUCCESS(rc))
797 {
798 uint32_t cbOut = pHdr->cbOut;
799 if (RT_UNLIKELY(cbOut > cbBuf))
800 {
801 LogRel(("vgdrvSolarisIOCtlSlow: too much output! %#x > %#x; iCmd=%#x!\n", cbOut, cbBuf, iCmd));
802 cbOut = cbBuf;
803 }
804 rc = ddi_copyout(pHdr, (void *)iArg, cbOut, Mode);
805 if (RT_UNLIKELY(rc != 0))
806 {
807 /* this is really bad */
808 LogRel(("vgdrvSolarisIOCtlSlow: ddi_copyout(,%p,%d) failed. rc=%d\n", (void *)iArg, cbBuf, rc));
809 rc = EFAULT;
810 }
811 }
812 else
813 rc = EINVAL;
814
815 if (pHdr != &StackBuf.Hdr)
816 RTMemTmpFree(pHdr);
817 return rc;
818}
819
820
821#if 0
822/**
823 * @note This code is duplicated on other platforms with variations, so please
824 * keep them all up to date when making changes!
825 */
826int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
827{
828 /*
829 * Simple request validation (common code does the rest).
830 */
831 int rc;
832 if ( RT_VALID_PTR(pReqHdr)
833 && cbReq >= sizeof(*pReqHdr))
834 {
835 /*
836 * All requests except the connect one requires a valid session.
837 */
838 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
839 if (pSession)
840 {
841 if ( RT_VALID_PTR(pSession)
842 && pSession->pDevExt == &g_DevExt)
843 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
844 else
845 rc = VERR_INVALID_HANDLE;
846 }
847 else if (uReq == VBGL_IOCTL_IDC_CONNECT)
848 {
849 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
850 if (RT_SUCCESS(rc))
851 {
852 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
853 if (RT_FAILURE(rc))
854 VGDrvCommonCloseSession(&g_DevExt, pSession);
855 }
856 }
857 else
858 rc = VERR_INVALID_HANDLE;
859 }
860 else
861 rc = VERR_INVALID_POINTER;
862 return rc;
863}
864#endif
865
866
867static int vgdrvSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead)
868{
869 LogFlow(("vgdrvSolarisPoll: fEvents=%d fAnyYet=%d\n", fEvents, fAnyYet));
870
871 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
872 if (RT_LIKELY(pState))
873 {
874 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pState->pSession;
875 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
876 if (pSession->u32MousePosChangedSeq != u32CurSeq)
877 {
878 *pReqEvents |= (POLLIN | POLLRDNORM);
879 pSession->u32MousePosChangedSeq = u32CurSeq;
880 }
881 else
882 {
883 *pReqEvents = 0;
884 if (!fAnyYet)
885 *ppPollHead = &g_PollHead;
886 }
887
888 return 0;
889 }
890
891 Log(("vgdrvSolarisPoll: no state data for %d\n", getminor(Dev)));
892 return EINVAL;
893}
894
895
896/**
897 * Sets IRQ for VMMDev.
898 *
899 * @returns Solaris error code.
900 * @param pDip Pointer to the device info structure.
901 */
902static int vgdrvSolarisAddIRQ(dev_info_t *pDip)
903{
904 LogFlow(("vgdrvSolarisAddIRQ: pDip=%p\n", pDip));
905
906 /* Get the types of interrupt supported for this hardware. */
907 int fIntrType = 0;
908 int rc = ddi_intr_get_supported_types(pDip, &fIntrType);
909 if (rc == DDI_SUCCESS)
910 {
911 /* We only support fixed interrupts at this point, not MSIs. */
912 if (fIntrType & DDI_INTR_TYPE_FIXED)
913 {
914 /* Verify the number of interrupts supported by this device. There can only be one fixed interrupt. */
915 int cIntrCount = 0;
916 rc = ddi_intr_get_nintrs(pDip, fIntrType, &cIntrCount);
917 if ( rc == DDI_SUCCESS
918 && cIntrCount == 1)
919 {
920 /* Allocated kernel memory for the interrupt handle. The allocation size is stored internally. */
921 g_pahIntrs = RTMemAllocZ(cIntrCount * sizeof(ddi_intr_handle_t));
922 if (g_pahIntrs)
923 {
924 /* Allocate the interrupt for this device and verify the allocation. */
925 int cIntrAllocated;
926 rc = ddi_intr_alloc(pDip, g_pahIntrs, fIntrType, 0 /* interrupt number */, cIntrCount, &cIntrAllocated,
927 DDI_INTR_ALLOC_NORMAL);
928 if ( rc == DDI_SUCCESS
929 && cIntrAllocated == 1)
930 {
931 /* Get the interrupt priority assigned by the system. */
932 uint_t uIntrPriority;
933 rc = ddi_intr_get_pri(g_pahIntrs[0], &uIntrPriority);
934 if (rc == DDI_SUCCESS)
935 {
936 /* Check if the interrupt priority is scheduler level or above, if so we need to use a high-level
937 and low-level interrupt handlers with corresponding mutexes. */
938 cmn_err(CE_CONT, "!vboxguest: uIntrPriority=%d hilevel_pri=%d\n", uIntrPriority, ddi_intr_get_hilevel_pri());
939 if (uIntrPriority >= ddi_intr_get_hilevel_pri())
940 {
941 /* Initialize the high-level mutex. */
942 mutex_init(&g_HighLevelIrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
943
944 /* Assign interrupt handler function to the interrupt handle. */
945 rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)&vgdrvSolarisHighLevelISR,
946 NULL /* pvArg1 */, NULL /* pvArg2 */);
947
948 if (rc == DDI_SUCCESS)
949 {
950 /* Add the low-level interrupt handler. */
951 rc = ddi_intr_add_softint(pDip, &g_hSoftIntr, DDI_INTR_SOFTPRI_MAX,
952 (ddi_intr_handler_t *)&vgdrvSolarisISR, NULL /* pvArg1 */);
953 if (rc == DDI_SUCCESS)
954 {
955 /* Initialize the low-level mutex at the corresponding level. */
956 mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER,
957 DDI_INTR_PRI(DDI_INTR_SOFTPRI_MAX));
958
959 g_fSoftIntRegistered = true;
960 /* Enable the high-level interrupt. */
961 rc = ddi_intr_enable(g_pahIntrs[0]);
962 if (rc == DDI_SUCCESS)
963 return rc;
964
965 LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc));
966 mutex_destroy(&g_IrqMtx);
967 }
968 else
969 LogRel((DEVICE_NAME "::AddIRQ: failed to add soft interrupt handler. rc=%d\n", rc));
970
971 ddi_intr_remove_handler(g_pahIntrs[0]);
972 }
973 else
974 LogRel((DEVICE_NAME "::AddIRQ: failed to add high-level interrupt handler. rc=%d\n", rc));
975
976 mutex_destroy(&g_HighLevelIrqMtx);
977 }
978 else
979 {
980 /* Interrupt handler runs at reschedulable level, initialize the mutex at the given priority. */
981 mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
982
983 /* Assign interrupt handler function to the interrupt handle. */
984 rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)vgdrvSolarisISR,
985 NULL /* pvArg1 */, NULL /* pvArg2 */);
986 if (rc == DDI_SUCCESS)
987 {
988 /* Enable the interrupt. */
989 rc = ddi_intr_enable(g_pahIntrs[0]);
990 if (rc == DDI_SUCCESS)
991 return rc;
992
993 LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc));
994 mutex_destroy(&g_IrqMtx);
995 }
996 }
997 }
998 else
999 LogRel((DEVICE_NAME "::AddIRQ: failed to get priority of interrupt. rc=%d\n", rc));
1000
1001 Assert(cIntrAllocated == 1);
1002 ddi_intr_free(g_pahIntrs[0]);
1003 }
1004 else
1005 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount));
1006 RTMemFree(g_pahIntrs);
1007 }
1008 else
1009 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount));
1010 }
1011 else
1012 LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient number of IRQs. rc=%d cIntrCount=%d\n", rc, cIntrCount));
1013 }
1014 else
1015 LogRel((DEVICE_NAME "::AddIRQ: fixed-type interrupts not supported. IntrType=%#x\n", fIntrType));
1016 }
1017 else
1018 LogRel((DEVICE_NAME "::AddIRQ: failed to get supported interrupt types. rc=%d\n", rc));
1019 return rc;
1020}
1021
1022
1023/**
1024 * Removes IRQ for VMMDev.
1025 *
1026 * @param pDip Pointer to the device info structure.
1027 */
1028static void vgdrvSolarisRemoveIRQ(dev_info_t *pDip)
1029{
1030 LogFlow(("vgdrvSolarisRemoveIRQ:\n"));
1031
1032 int rc = ddi_intr_disable(g_pahIntrs[0]);
1033 if (rc == DDI_SUCCESS)
1034 {
1035 rc = ddi_intr_remove_handler(g_pahIntrs[0]);
1036 if (rc == DDI_SUCCESS)
1037 ddi_intr_free(g_pahIntrs[0]);
1038 }
1039
1040 if (g_fSoftIntRegistered)
1041 {
1042 ddi_intr_remove_softint(g_hSoftIntr);
1043 mutex_destroy(&g_HighLevelIrqMtx);
1044 g_fSoftIntRegistered = false;
1045 }
1046
1047 mutex_destroy(&g_IrqMtx);
1048 RTMemFree(g_pahIntrs);
1049}
1050
1051
1052/**
1053 * High-level Interrupt Service Routine for VMMDev.
1054 *
1055 * This routine simply dispatches a soft-interrupt at an acceptable IPL as
1056 * VGDrvCommonISR() cannot be called at a high IPL (scheduler level or higher)
1057 * due to pollwakeup() in VGDrvNativeISRMousePollEvent().
1058 *
1059 * @param Arg Private data (unused, will be NULL).
1060 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
1061 */
1062static uint_t vgdrvSolarisHighLevelISR(caddr_t Arg)
1063{
1064 bool const fOurIrq = VGDrvCommonIsOurIRQ(&g_DevExt);
1065 if (fOurIrq)
1066 {
1067 ddi_intr_trigger_softint(g_hSoftIntr, NULL /* Arg */);
1068 return DDI_INTR_CLAIMED;
1069 }
1070 return DDI_INTR_UNCLAIMED;
1071}
1072
1073
1074/**
1075 * Interrupt Service Routine for VMMDev.
1076 *
1077 * @param Arg Private data (unused, will be NULL).
1078 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
1079 */
1080static uint_t vgdrvSolarisISR(caddr_t Arg)
1081{
1082 LogFlow(("vgdrvSolarisISR:\n"));
1083
1084 /* The mutex is required to protect against parallel executions (if possible?) and also the
1085 mouse notify registeration race between VGDrvNativeSetMouseNotifyCallback() and VGDrvCommonISR(). */
1086 mutex_enter(&g_IrqMtx);
1087 bool fOurIRQ = VGDrvCommonISR(&g_DevExt);
1088 mutex_exit(&g_IrqMtx);
1089
1090 return fOurIRQ ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED;
1091}
1092
1093
1094void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1095{
1096 LogFlow(("VGDrvNativeISRMousePollEvent:\n"));
1097
1098 /*
1099 * Wake up poll waiters.
1100 */
1101 pollwakeup(&g_PollHead, POLLIN | POLLRDNORM);
1102}
1103
1104
1105bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
1106{
1107 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
1108 return false;
1109}
1110
1111
1112/**
1113 * Sets the mouse notification callback.
1114 *
1115 * @returns VBox status code.
1116 * @param pDevExt Pointer to the device extension.
1117 * @param pNotify Pointer to the mouse notify struct.
1118 */
1119int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
1120{
1121 /* Take the mutex here so as to not race with VGDrvCommonISR() which invokes the mouse notify callback. */
1122 mutex_enter(&g_IrqMtx);
1123 pDevExt->pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
1124 pDevExt->pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
1125 mutex_exit(&g_IrqMtx);
1126 return VINF_SUCCESS;
1127}
1128
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