VirtualBox

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

最後變更 在這個檔案是 107251,由 vboxsync 提交於 3 月 前

Add,HostDrivers: More parfait/solaris kludges. [fix]

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette