VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 8 weeks 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.4 KB
Line 
1/* $Id: VBoxGuest-solaris.c 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions Driver for Solaris.
4 */
5
6/*
7 * Copyright (C) 2007-2024 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,
338 NULL /*pvMmioReq*/,
339 g_pMMIOBase, g_cbMMIO,
340#if ARCH_BITS == 64
341 VBOXOSTYPE_Solaris_x64,
342#else
343 VBOXOSTYPE_Solaris,
344#endif
345 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
346 if (RT_SUCCESS(rc))
347 {
348 /*
349 * Add IRQ of VMMDev.
350 */
351 rc = vgdrvSolarisAddIRQ(pDip);
352 if (rc == DDI_SUCCESS)
353 {
354 /*
355 * Read host configuration.
356 */
357 VGDrvCommonProcessOptionsFromHost(&g_DevExt);
358
359 rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, 0 /* instance */, DDI_PSEUDO,
360 0 /* fFlags */);
361 if (rc == DDI_SUCCESS)
362 {
363 g_pDip = pDip;
364 pci_config_teardown(&PciHandle);
365 return DDI_SUCCESS;
366 }
367
368 LogRel((DEVICE_NAME "::Attach: ddi_create_minor_node failed.\n"));
369 vgdrvSolarisRemoveIRQ(pDip);
370 }
371 else
372 LogRel((DEVICE_NAME "::Attach: vgdrvSolarisAddIRQ failed.\n"));
373 VGDrvCommonDeleteDevExt(&g_DevExt);
374 }
375 else
376 LogRel((DEVICE_NAME "::Attach: VGDrvCommonInitDevExt failed.\n"));
377 ddi_regs_map_free(&g_PciMMIOHandle);
378 }
379 else
380 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for MMIO region failed.\n"));
381 }
382 else
383 LogRel((DEVICE_NAME "::Attach: ddi_dev_regsize for MMIO region failed.\n"));
384 ddi_regs_map_free(&g_PciIOHandle);
385 }
386 else
387 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for IOport failed.\n"));
388 pci_config_teardown(&PciHandle);
389 }
390 else
391 LogRel((DEVICE_NAME "::Attach: pci_config_setup failed rc=%d.\n", rc));
392 return DDI_FAILURE;
393 }
394
395 case DDI_RESUME:
396 {
397 /** @todo implement resume for guest driver. */
398 return DDI_SUCCESS;
399 }
400
401 default:
402 return DDI_FAILURE;
403 }
404}
405
406
407/**
408 * Detach entry point, to detach a device to the system or suspend it.
409 *
410 * @param pDip The module structure instance.
411 * @param enmCmd Attach type (ddi_attach_cmd_t)
412 *
413 * @return corresponding solaris error code.
414 */
415static int vgdrvSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
416{
417 LogFlow(("vgdrvSolarisDetach:\n"));
418 switch (enmCmd)
419 {
420 case DDI_DETACH:
421 {
422 vgdrvSolarisRemoveIRQ(pDip);
423 ddi_regs_map_free(&g_PciIOHandle);
424 ddi_regs_map_free(&g_PciMMIOHandle);
425 ddi_remove_minor_node(pDip, NULL);
426 VGDrvCommonDeleteDevExt(&g_DevExt);
427 g_pDip = NULL;
428 return DDI_SUCCESS;
429 }
430
431 case DDI_SUSPEND:
432 {
433 /** @todo implement suspend for guest driver. */
434 return DDI_SUCCESS;
435 }
436
437 default:
438 return DDI_FAILURE;
439 }
440}
441
442
443/**
444 * Quiesce entry point, called by solaris kernel for disabling the device from
445 * generating any interrupts or doing in-bound DMA.
446 *
447 * @param pDip The module structure instance.
448 *
449 * @return corresponding solaris error code.
450 */
451static int vgdrvSolarisQuiesce(dev_info_t *pDip)
452{
453 int rc = ddi_intr_disable(g_pahIntrs[0]);
454 if (rc != DDI_SUCCESS)
455 return DDI_FAILURE;
456
457 /** @todo What about HGCM/HGSMI touching guest-memory? */
458
459 return DDI_SUCCESS;
460}
461
462
463/**
464 * Info entry point, called by solaris kernel for obtaining driver info.
465 *
466 * @param pDip The module structure instance (do not use).
467 * @param enmCmd Information request type.
468 * @param pvArg Type specific argument.
469 * @param ppvResult Where to store the requested info.
470 *
471 * @return corresponding solaris error code.
472 */
473static int vgdrvSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
474{
475 LogFlow(("vgdrvSolarisGetInfo:\n"));
476
477 int rc = DDI_SUCCESS;
478 switch (enmCmd)
479 {
480 case DDI_INFO_DEVT2DEVINFO:
481 {
482 *ppvResult = (void *)g_pDip;
483 if (!*ppvResult)
484 rc = DDI_FAILURE;
485 break;
486 }
487
488 case DDI_INFO_DEVT2INSTANCE:
489 {
490 /* There can only be a single-instance of this driver and thus its instance number is 0. */
491 *ppvResult = (void *)0;
492 break;
493 }
494
495 default:
496 rc = DDI_FAILURE;
497 break;
498 }
499
500 NOREF(pvArg);
501 return rc;
502}
503
504
505/**
506 * User context entry points
507 *
508 * @remarks fFlags are the flags passed to open() or to ldi_open_by_name. In
509 * the latter case the FKLYR flag is added to indicate that the caller
510 * is a kernel component rather than user land.
511 */
512static int vgdrvSolarisOpen(dev_t *pDev, int fFlags, int fType, cred_t *pCred)
513{
514 int rc;
515 PVBOXGUESTSESSION pSession = NULL;
516
517 LogFlow(("vgdrvSolarisOpen:\n"));
518
519 /*
520 * Verify we are being opened as a character device.
521 */
522 if (fType != OTYP_CHR)
523 return EINVAL;
524
525 vboxguest_state_t *pState = NULL;
526 unsigned iOpenInstance;
527 for (iOpenInstance = 0; iOpenInstance < 4096; iOpenInstance++)
528 {
529 if ( !ddi_get_soft_state(g_pvgdrvSolarisState, iOpenInstance) /* faster */
530 && ddi_soft_state_zalloc(g_pvgdrvSolarisState, iOpenInstance) == DDI_SUCCESS)
531 {
532 pState = ddi_get_soft_state(g_pvgdrvSolarisState, iOpenInstance);
533 break;
534 }
535 }
536 if (!pState)
537 {
538 Log(("vgdrvSolarisOpen: too many open instances."));
539 return ENXIO;
540 }
541
542 /*
543 * Create a new session.
544 *
545 * Note! The devfs inode with the gid isn't readily available here, so we cannot easily
546 * to the vbox group detection like on linux. Read config instead?
547 */
548 if (!(fFlags & FKLYR))
549 {
550 uint32_t fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN;
551 if (crgetruid(pCred) == 0)
552 fRequestor |= VMMDEV_REQUESTOR_USR_ROOT;
553 else
554 fRequestor |= VMMDEV_REQUESTOR_USR_USER;
555 if (secpolicy_coreadm(pCred) == 0)
556 fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL;
557 /** @todo is there any way of detecting that the process belongs to someone on the physical console?
558 * secpolicy_console() [== PRIV_SYS_DEVICES] doesn't look quite right, or does it? */
559 fRequestor |= VMMDEV_REQUESTOR_CON_DONT_KNOW;
560 fRequestor |= VMMDEV_REQUESTOR_NO_USER_DEVICE; /** @todo implement vboxuser device node. */
561
562 rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &pSession);
563 }
564 else
565 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
566 if (RT_SUCCESS(rc))
567 {
568 if (!(fFlags & FKLYR))
569 pState->pvProcRef = proc_ref();
570 else
571 pState->pvProcRef = NULL;
572 pState->pSession = pSession;
573 *pDev = makedevice(getmajor(*pDev), iOpenInstance);
574 Log(("vgdrvSolarisOpen: pSession=%p pState=%p pid=%d\n", pSession, pState, (int)RTProcSelf()));
575 return 0;
576 }
577
578 /* Failed, clean up. */
579 ddi_soft_state_free(g_pvgdrvSolarisState, iOpenInstance);
580
581 LogRel((DEVICE_NAME "::Open: VGDrvCommonCreateUserSession failed. rc=%d\n", rc));
582 return EFAULT;
583}
584
585
586static int vgdrvSolarisClose(dev_t Dev, int flag, int fType, cred_t *pCred)
587{
588 LogFlow(("vgdrvSolarisClose: pid=%d\n", (int)RTProcSelf()));
589
590 PVBOXGUESTSESSION pSession = NULL;
591 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
592 if (!pState)
593 {
594 Log(("vgdrvSolarisClose: failed to get pState.\n"));
595 return EFAULT;
596 }
597
598 if (pState->pvProcRef != NULL)
599 {
600 proc_unref(pState->pvProcRef);
601 pState->pvProcRef = NULL;
602 }
603 pSession = pState->pSession;
604 pState->pSession = NULL;
605 Log(("vgdrvSolarisClose: pSession=%p pState=%p\n", pSession, pState));
606 ddi_soft_state_free(g_pvgdrvSolarisState, getminor(Dev));
607 if (!pSession)
608 {
609 Log(("vgdrvSolarisClose: failed to get pSession.\n"));
610 return EFAULT;
611 }
612
613 /*
614 * Close the session.
615 */
616 if (pSession)
617 VGDrvCommonCloseSession(&g_DevExt, pSession);
618 return 0;
619}
620
621
622static int vgdrvSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
623{
624 LogFlow((DEVICE_NAME "::Read\n"));
625
626 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
627 if (!pState)
628 {
629 Log((DEVICE_NAME "::Close: failed to get pState.\n"));
630 return EFAULT;
631 }
632
633 PVBOXGUESTSESSION pSession = pState->pSession;
634 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
635 if (pSession->u32MousePosChangedSeq != u32CurSeq)
636 pSession->u32MousePosChangedSeq = u32CurSeq;
637
638 return 0;
639}
640
641
642static int vgdrvSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
643{
644 LogFlow(("vgdrvSolarisWrite:\n"));
645 return 0;
646}
647
648
649/** @def IOCPARM_LEN
650 * Gets the length from the ioctl number.
651 * This is normally defined by sys/ioccom.h on BSD systems...
652 */
653#ifndef IOCPARM_LEN
654# define IOCPARM_LEN(x) ( ((x) >> 16) & IOCPARM_MASK )
655#endif
656
657
658/**
659 * Driver ioctl, an alternate entry point for this character driver.
660 *
661 * @param Dev Device number
662 * @param iCmd Operation identifier
663 * @param iArgs Arguments from user to driver
664 * @param Mode Information bitfield (read/write, address space etc.)
665 * @param pCred User credentials
666 * @param pVal Return value for calling process.
667 *
668 * @return corresponding solaris error code.
669 */
670static int vgdrvSolarisIOCtl(dev_t Dev, int iCmd, intptr_t iArgs, int Mode, cred_t *pCred, int *pVal)
671{
672 /*
673 * Get the session from the soft state item.
674 */
675 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
676 if (!pState)
677 {
678 LogRel(("vgdrvSolarisIOCtl: no state data for %#x (%d)\n", Dev, getminor(Dev)));
679 return EINVAL;
680 }
681
682 PVBOXGUESTSESSION pSession = pState->pSession;
683 if (!pSession)
684 {
685 LogRel(("vgdrvSolarisIOCtl: no session in state data for %#x (%d)\n", Dev, getminor(Dev)));
686 return DDI_SUCCESS;
687 }
688
689 /*
690 * Deal with fast requests.
691 */
692 if (VBGL_IOCTL_IS_FAST(iCmd))
693 {
694 *pVal = VGDrvCommonIoCtlFast(iCmd, &g_DevExt, pSession);
695 return 0;
696 }
697
698 /*
699 * It's kind of simple if this is a kernel session, take slow path if user land.
700 */
701 if (pSession->R0Process == NIL_RTR0PROCESS)
702 {
703 if (IOCPARM_LEN(iCmd) == sizeof(VBGLREQHDR))
704 {
705 PVBGLREQHDR pHdr = (PVBGLREQHDR)iArgs;
706 int rc;
707 if (iCmd != VBGL_IOCTL_IDC_DISCONNECT)
708 rc =VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, RT_MAX(pHdr->cbIn, pHdr->cbOut));
709 else
710 {
711 pState->pSession = NULL;
712 rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, RT_MAX(pHdr->cbIn, pHdr->cbOut));
713 if (RT_FAILURE(rc))
714 pState->pSession = pSession;
715 }
716 return rc;
717 }
718 }
719
720 return vgdrvSolarisIOCtlSlow(pSession, iCmd, Mode, iArgs);
721}
722
723
724/**
725 * Worker for VBoxSupDrvIOCtl that takes the slow IOCtl functions.
726 *
727 * @returns Solaris errno.
728 *
729 * @param pSession The session.
730 * @param iCmd The IOCtl command.
731 * @param Mode Information bitfield (for specifying ownership of data)
732 * @param iArg User space address of the request buffer.
733 */
734static int vgdrvSolarisIOCtlSlow(PVBOXGUESTSESSION pSession, int iCmd, int Mode, intptr_t iArg)
735{
736 int rc;
737 uint32_t cbBuf = 0;
738 union
739 {
740 VBGLREQHDR Hdr;
741 uint8_t abBuf[64];
742 } StackBuf;
743 PVBGLREQHDR pHdr;
744
745
746 /*
747 * Read the header.
748 */
749 if (RT_UNLIKELY(IOCPARM_LEN(iCmd) != sizeof(StackBuf.Hdr)))
750 {
751 LogRel(("vgdrvSolarisIOCtlSlow: iCmd=%#x len %d expected %d\n", iCmd, IOCPARM_LEN(iCmd), sizeof(StackBuf.Hdr)));
752 return EINVAL;
753 }
754 rc = ddi_copyin((void *)iArg, &StackBuf.Hdr, sizeof(StackBuf.Hdr), Mode);
755 if (RT_UNLIKELY(rc))
756 {
757 LogRel(("vgdrvSolarisIOCtlSlow: ddi_copyin(,%#lx,) failed; iCmd=%#x. rc=%d\n", iArg, iCmd, rc));
758 return EFAULT;
759 }
760 if (RT_UNLIKELY(StackBuf.Hdr.uVersion != VBGLREQHDR_VERSION))
761 {
762 LogRel(("vgdrvSolarisIOCtlSlow: bad header version %#x; iCmd=%#x\n", StackBuf.Hdr.uVersion, iCmd));
763 return EINVAL;
764 }
765 cbBuf = RT_MAX(StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut);
766 if (RT_UNLIKELY( StackBuf.Hdr.cbIn < sizeof(StackBuf.Hdr)
767 || (StackBuf.Hdr.cbOut < sizeof(StackBuf.Hdr) && StackBuf.Hdr.cbOut != 0)
768 || cbBuf > _1M*16))
769 {
770 LogRel(("vgdrvSolarisIOCtlSlow: max(%#x,%#x); iCmd=%#x\n", StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut, iCmd));
771 return EINVAL;
772 }
773
774 /*
775 * Buffer the request.
776 *
777 * Note! Common code revalidates the header sizes and version. So it's
778 * fine to read it once more.
779 */
780 if (cbBuf <= sizeof(StackBuf))
781 pHdr = &StackBuf.Hdr;
782 else
783 {
784 pHdr = RTMemTmpAlloc(cbBuf);
785 if (RT_UNLIKELY(!pHdr))
786 {
787 LogRel(("vgdrvSolarisIOCtlSlow: failed to allocate buffer of %d bytes for iCmd=%#x.\n", cbBuf, iCmd));
788 return ENOMEM;
789 }
790 }
791 rc = ddi_copyin((void *)iArg, pHdr, cbBuf, Mode);
792 if (RT_UNLIKELY(rc))
793 {
794 LogRel(("vgdrvSolarisIOCtlSlow: copy_from_user(,%#lx, %#x) failed; iCmd=%#x. rc=%d\n", iArg, cbBuf, iCmd, rc));
795 if (pHdr != &StackBuf.Hdr)
796 RTMemFree(pHdr);
797 return EFAULT;
798 }
799
800 /*
801 * Process the IOCtl.
802 */
803 rc = VGDrvCommonIoCtl(iCmd, &g_DevExt, pSession, pHdr, cbBuf);
804
805 /*
806 * Copy ioctl data and output buffer back to user space.
807 */
808 if (RT_SUCCESS(rc))
809 {
810 uint32_t cbOut = pHdr->cbOut;
811 if (RT_UNLIKELY(cbOut > cbBuf))
812 {
813 LogRel(("vgdrvSolarisIOCtlSlow: too much output! %#x > %#x; iCmd=%#x!\n", cbOut, cbBuf, iCmd));
814 cbOut = cbBuf;
815 }
816 rc = ddi_copyout(pHdr, (void *)iArg, cbOut, Mode);
817 if (RT_UNLIKELY(rc != 0))
818 {
819 /* this is really bad */
820 LogRel(("vgdrvSolarisIOCtlSlow: ddi_copyout(,%p,%d) failed. rc=%d\n", (void *)iArg, cbBuf, rc));
821 rc = EFAULT;
822 }
823 }
824 else
825 rc = EINVAL;
826
827 if (pHdr != &StackBuf.Hdr)
828 RTMemTmpFree(pHdr);
829 return rc;
830}
831
832
833#if 0
834/**
835 * @note This code is duplicated on other platforms with variations, so please
836 * keep them all up to date when making changes!
837 */
838int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
839{
840 /*
841 * Simple request validation (common code does the rest).
842 */
843 int rc;
844 if ( RT_VALID_PTR(pReqHdr)
845 && cbReq >= sizeof(*pReqHdr))
846 {
847 /*
848 * All requests except the connect one requires a valid session.
849 */
850 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
851 if (pSession)
852 {
853 if ( RT_VALID_PTR(pSession)
854 && pSession->pDevExt == &g_DevExt)
855 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
856 else
857 rc = VERR_INVALID_HANDLE;
858 }
859 else if (uReq == VBGL_IOCTL_IDC_CONNECT)
860 {
861 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
862 if (RT_SUCCESS(rc))
863 {
864 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
865 if (RT_FAILURE(rc))
866 VGDrvCommonCloseSession(&g_DevExt, pSession);
867 }
868 }
869 else
870 rc = VERR_INVALID_HANDLE;
871 }
872 else
873 rc = VERR_INVALID_POINTER;
874 return rc;
875}
876#endif
877
878
879static int vgdrvSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead)
880{
881 LogFlow(("vgdrvSolarisPoll: fEvents=%d fAnyYet=%d\n", fEvents, fAnyYet));
882
883 vboxguest_state_t *pState = ddi_get_soft_state(g_pvgdrvSolarisState, getminor(Dev));
884 if (RT_LIKELY(pState))
885 {
886 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pState->pSession;
887 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
888 if (pSession->u32MousePosChangedSeq != u32CurSeq)
889 {
890 *pReqEvents |= (POLLIN | POLLRDNORM);
891 pSession->u32MousePosChangedSeq = u32CurSeq;
892 }
893 else
894 {
895 *pReqEvents = 0;
896 if (!fAnyYet)
897 *ppPollHead = &g_PollHead;
898 }
899
900 return 0;
901 }
902
903 Log(("vgdrvSolarisPoll: no state data for %d\n", getminor(Dev)));
904 return EINVAL;
905}
906
907
908/**
909 * Sets IRQ for VMMDev.
910 *
911 * @returns Solaris error code.
912 * @param pDip Pointer to the device info structure.
913 */
914static int vgdrvSolarisAddIRQ(dev_info_t *pDip)
915{
916 LogFlow(("vgdrvSolarisAddIRQ: pDip=%p\n", pDip));
917
918 /* Get the types of interrupt supported for this hardware. */
919 int fIntrType = 0;
920 int rc = ddi_intr_get_supported_types(pDip, &fIntrType);
921 if (rc == DDI_SUCCESS)
922 {
923 /* We only support fixed interrupts at this point, not MSIs. */
924 if (fIntrType & DDI_INTR_TYPE_FIXED)
925 {
926 /* Verify the number of interrupts supported by this device. There can only be one fixed interrupt. */
927 int cIntrCount = 0;
928 rc = ddi_intr_get_nintrs(pDip, fIntrType, &cIntrCount);
929 if ( rc == DDI_SUCCESS
930 && cIntrCount == 1)
931 {
932 /* Allocated kernel memory for the interrupt handle. The allocation size is stored internally. */
933 g_pahIntrs = RTMemAllocZ(cIntrCount * sizeof(ddi_intr_handle_t));
934 if (g_pahIntrs)
935 {
936 /* Allocate the interrupt for this device and verify the allocation. */
937 int cIntrAllocated;
938 rc = ddi_intr_alloc(pDip, g_pahIntrs, fIntrType, 0 /* interrupt number */, cIntrCount, &cIntrAllocated,
939 DDI_INTR_ALLOC_NORMAL);
940 if ( rc == DDI_SUCCESS
941 && cIntrAllocated == 1)
942 {
943 /* Get the interrupt priority assigned by the system. */
944 uint_t uIntrPriority;
945 rc = ddi_intr_get_pri(g_pahIntrs[0], &uIntrPriority);
946 if (rc == DDI_SUCCESS)
947 {
948 /* Check if the interrupt priority is scheduler level or above, if so we need to use a high-level
949 and low-level interrupt handlers with corresponding mutexes. */
950 cmn_err(CE_CONT, "!vboxguest: uIntrPriority=%d hilevel_pri=%d\n", uIntrPriority, ddi_intr_get_hilevel_pri());
951 if (uIntrPriority >= ddi_intr_get_hilevel_pri())
952 {
953 /* Initialize the high-level mutex. */
954 mutex_init(&g_HighLevelIrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
955
956 /* Assign interrupt handler function to the interrupt handle. */
957 rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)&vgdrvSolarisHighLevelISR,
958 NULL /* pvArg1 */, NULL /* pvArg2 */);
959
960 if (rc == DDI_SUCCESS)
961 {
962 /* Add the low-level interrupt handler. */
963 rc = ddi_intr_add_softint(pDip, &g_hSoftIntr, DDI_INTR_SOFTPRI_MAX,
964 (ddi_intr_handler_t *)&vgdrvSolarisISR, NULL /* pvArg1 */);
965 if (rc == DDI_SUCCESS)
966 {
967 /* Initialize the low-level mutex at the corresponding level. */
968 mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER,
969 DDI_INTR_PRI(DDI_INTR_SOFTPRI_MAX));
970
971 g_fSoftIntRegistered = true;
972 /* Enable the high-level interrupt. */
973 rc = ddi_intr_enable(g_pahIntrs[0]);
974 if (rc == DDI_SUCCESS)
975 return rc;
976
977 LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc));
978 mutex_destroy(&g_IrqMtx);
979 }
980 else
981 LogRel((DEVICE_NAME "::AddIRQ: failed to add soft interrupt handler. rc=%d\n", rc));
982
983 ddi_intr_remove_handler(g_pahIntrs[0]);
984 }
985 else
986 LogRel((DEVICE_NAME "::AddIRQ: failed to add high-level interrupt handler. rc=%d\n", rc));
987
988 mutex_destroy(&g_HighLevelIrqMtx);
989 }
990 else
991 {
992 /* Interrupt handler runs at reschedulable level, initialize the mutex at the given priority. */
993 mutex_init(&g_IrqMtx, NULL /* pszDesc */, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
994
995 /* Assign interrupt handler function to the interrupt handle. */
996 rc = ddi_intr_add_handler(g_pahIntrs[0], (ddi_intr_handler_t *)vgdrvSolarisISR,
997 NULL /* pvArg1 */, NULL /* pvArg2 */);
998 if (rc == DDI_SUCCESS)
999 {
1000 /* Enable the interrupt. */
1001 rc = ddi_intr_enable(g_pahIntrs[0]);
1002 if (rc == DDI_SUCCESS)
1003 return rc;
1004
1005 LogRel((DEVICE_NAME "::AddIRQ: failed to enable interrupt. rc=%d\n", rc));
1006 mutex_destroy(&g_IrqMtx);
1007 }
1008 }
1009 }
1010 else
1011 LogRel((DEVICE_NAME "::AddIRQ: failed to get priority of interrupt. rc=%d\n", rc));
1012
1013 Assert(cIntrAllocated == 1);
1014 ddi_intr_free(g_pahIntrs[0]);
1015 }
1016 else
1017 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount));
1018 RTMemFree(g_pahIntrs);
1019 }
1020 else
1021 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", cIntrCount));
1022 }
1023 else
1024 LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient number of IRQs. rc=%d cIntrCount=%d\n", rc, cIntrCount));
1025 }
1026 else
1027 LogRel((DEVICE_NAME "::AddIRQ: fixed-type interrupts not supported. IntrType=%#x\n", fIntrType));
1028 }
1029 else
1030 LogRel((DEVICE_NAME "::AddIRQ: failed to get supported interrupt types. rc=%d\n", rc));
1031 return rc;
1032}
1033
1034
1035/**
1036 * Removes IRQ for VMMDev.
1037 *
1038 * @param pDip Pointer to the device info structure.
1039 */
1040static void vgdrvSolarisRemoveIRQ(dev_info_t *pDip)
1041{
1042 LogFlow(("vgdrvSolarisRemoveIRQ:\n"));
1043
1044 int rc = ddi_intr_disable(g_pahIntrs[0]);
1045 if (rc == DDI_SUCCESS)
1046 {
1047 rc = ddi_intr_remove_handler(g_pahIntrs[0]);
1048 if (rc == DDI_SUCCESS)
1049 ddi_intr_free(g_pahIntrs[0]);
1050 }
1051
1052 if (g_fSoftIntRegistered)
1053 {
1054 ddi_intr_remove_softint(g_hSoftIntr);
1055 mutex_destroy(&g_HighLevelIrqMtx);
1056 g_fSoftIntRegistered = false;
1057 }
1058
1059 mutex_destroy(&g_IrqMtx);
1060 RTMemFree(g_pahIntrs);
1061}
1062
1063
1064/**
1065 * High-level Interrupt Service Routine for VMMDev.
1066 *
1067 * This routine simply dispatches a soft-interrupt at an acceptable IPL as
1068 * VGDrvCommonISR() cannot be called at a high IPL (scheduler level or higher)
1069 * due to pollwakeup() in VGDrvNativeISRMousePollEvent().
1070 *
1071 * @param Arg Private data (unused, will be NULL).
1072 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
1073 */
1074static uint_t vgdrvSolarisHighLevelISR(caddr_t Arg)
1075{
1076 bool const fOurIrq = VGDrvCommonIsOurIRQ(&g_DevExt);
1077 if (fOurIrq)
1078 {
1079 ddi_intr_trigger_softint(g_hSoftIntr, NULL /* Arg */);
1080 return DDI_INTR_CLAIMED;
1081 }
1082 return DDI_INTR_UNCLAIMED;
1083}
1084
1085
1086/**
1087 * Interrupt Service Routine for VMMDev.
1088 *
1089 * @param Arg Private data (unused, will be NULL).
1090 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
1091 */
1092static uint_t vgdrvSolarisISR(caddr_t Arg)
1093{
1094 LogFlow(("vgdrvSolarisISR:\n"));
1095
1096 /* The mutex is required to protect against parallel executions (if possible?) and also the
1097 mouse notify registeration race between VGDrvNativeSetMouseNotifyCallback() and VGDrvCommonISR(). */
1098 mutex_enter(&g_IrqMtx);
1099 bool fOurIRQ = VGDrvCommonISR(&g_DevExt);
1100 mutex_exit(&g_IrqMtx);
1101
1102 return fOurIRQ ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED;
1103}
1104
1105
1106void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1107{
1108 LogFlow(("VGDrvNativeISRMousePollEvent:\n"));
1109
1110 /*
1111 * Wake up poll waiters.
1112 */
1113 pollwakeup(&g_PollHead, POLLIN | POLLRDNORM);
1114}
1115
1116
1117bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
1118{
1119 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
1120 return false;
1121}
1122
1123
1124/**
1125 * Sets the mouse notification callback.
1126 *
1127 * @returns VBox status code.
1128 * @param pDevExt Pointer to the device extension.
1129 * @param pNotify Pointer to the mouse notify struct.
1130 */
1131int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
1132{
1133 /* Take the mutex here so as to not race with VGDrvCommonISR() which invokes the mouse notify callback. */
1134 mutex_enter(&g_IrqMtx);
1135 pDevExt->pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
1136 pDevExt->pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
1137 mutex_exit(&g_IrqMtx);
1138 return VINF_SUCCESS;
1139}
1140
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