VirtualBox

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

Last change on this file since 99120 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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