VirtualBox

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

Last change on this file since 40552 was 40552, checked in by vboxsync, 13 years ago

Additions/VBoxGuest: Solaris STREAMS driver: cleanups, fixes and more tests.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.6 KB
Line 
1/* $Id: VBoxGuest-solaris-streams.c 40552 2012-03-20 15:00:41Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions Driver for Solaris.
4 */
5
6/*
7 * Copyright (C) 2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/******************************************************************************
20* Header Files *
21******************************************************************************/
22
23#ifndef TESTCASE
24# include <sys/conf.h>
25# include <sys/modctl.h>
26# include <sys/mutex.h>
27# include <sys/pci.h>
28# include <sys/stat.h>
29# include <sys/ddi.h>
30# include <sys/ddi_intr.h>
31# include <sys/sunddi.h>
32# include <sys/open.h>
33# include <sys/sunldi.h>
34# include <sys/file.h>
35#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
36#else /* TESTCASE */
37# undef IN_RING3
38# define IN_RING0
39#endif /* TESTCASE */
40
41#include "VBoxGuestInternal.h"
42#include <VBox/log.h>
43#include <VBox/version.h>
44#include <iprt/assert.h>
45#include <iprt/initterm.h>
46#include <iprt/process.h>
47#include <iprt/mem.h>
48#include <iprt/cdefs.h>
49#include <iprt/asm.h>
50
51#ifdef TESTCASE /* Include this last as we . */
52# include "testcase/solaris.h"
53# include <iprt/test.h>
54#endif /* TESTCASE */
55
56
57/******************************************************************************
58* Defined Constants And Macros *
59******************************************************************************/
60
61/** The module name. */
62#define DEVICE_NAME "vboxguest"
63/** The module description as seen in 'modinfo'. */
64#define DEVICE_DESC "VirtualBox GstDrv"
65/** The maximum number of open device nodes we support. */
66#define MAX_OPEN_NODES 4096
67
68
69/******************************************************************************
70* Internal functions used in global structures *
71******************************************************************************/
72
73static int vbgr0SolOpen(queue_t *pReadQueue, dev_t *pDev, int fFlag,
74 int fMode, cred_t *pCred);
75static int vbgr0SolClose(queue_t *pReadQueue, int fFlag, cred_t *pCred);
76static int vbgr0SolWPut(queue_t *pWriteQueue, mblk_t *pMBlk);
77
78static int vbgr0SolGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
79static int vbgr0SolAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
80static int vbgr0SolDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
81
82
83/******************************************************************************
84* Driver global structures *
85******************************************************************************/
86
87#ifndef TESTCASE /* I see no value in including these in the test. */
88
89/*
90 * mod_info: STREAMS module information.
91 */
92static struct module_info g_vbgr0SolModInfo =
93{
94 0x0ffff, /* module id number */
95 "vboxguest",
96 0, /* minimum packet size */
97 INFPSZ, /* maximum packet size accepted */
98 512, /* high water mark for data flow control */
99 128 /* low water mark */
100};
101
102/*
103 * rinit: read queue structure for handling messages coming from below. In
104 * our case this means the host and the virtual hardware, so we do not need
105 * the put and service procedures.
106 */
107static struct qinit g_vbgr0SolRInit =
108{
109 NULL, /* put */
110 NULL, /* service thread procedure */
111 vbgr0SolOpen,
112 vbgr0SolClose,
113 NULL, /* reserved */
114 &g_vbgr0SolModInfo,
115 NULL /* module statistics structure */
116};
117
118/*
119 * winit: write queue structure for handling messages coming from above. Above
120 * means user space applications: either Guest Additions user space tools or
121 * applications reading pointer input. Messages from the last most likely pass
122 * through at least the "consms" console mouse streams module which multiplexes
123 * hardware pointer drivers to a single virtual pointer.
124 */
125static struct qinit g_vbgr0SolWInit =
126{
127 vbgr0SolWPut,
128 NULL, /* service thread procedure */
129 NULL, /* open */
130 NULL, /* close */
131 NULL, /* reserved */
132 &g_vbgr0SolModInfo,
133 NULL /* module statistics structure */
134};
135
136/**
137 * streamtab: for drivers that support char/block entry points.
138 */
139static struct streamtab g_vbgr0SolStreamTab =
140{
141 &g_vbgr0SolRInit,
142 &g_vbgr0SolWInit,
143 NULL, /* MUX rinit */
144 NULL /* MUX winit */
145};
146
147/**
148 * cb_ops: for drivers that support char/block entry points.
149 */
150static struct cb_ops g_vbgr0SolCbOps =
151{
152 nulldev, /* open */
153 nulldev, /* close */
154 nulldev, /* b strategy */
155 nulldev, /* b dump */
156 nulldev, /* b print */
157 nulldev, /* c read */
158 nulldev, /* c write */
159 nulldev, /* c ioctl */
160 nulldev, /* c devmap */
161 nulldev, /* c mmap */
162 nulldev, /* c segmap */
163 nochpoll, /* c poll */
164 ddi_prop_op, /* property ops */
165 g_vbgr0SolStreamTab,
166 D_NEW | D_MP, /* compat. flag */
167};
168
169/**
170 * dev_ops: for driver device operations.
171 */
172static struct dev_ops g_vbgr0SolDevOps =
173{
174 DEVO_REV, /* driver build revision */
175 0, /* ref count */
176 vbgr0SolGetInfo,
177 nulldev, /* identify */
178 nulldev, /* probe */
179 vbgr0SolAttach,
180 vbgr0SolDetach,
181 nodev, /* reset */
182 &g_vbgr0SolCbOps,
183 (struct bus_ops *)0,
184 nodev /* power */
185};
186
187/**
188 * modldrv: export driver specifics to the kernel.
189 */
190static struct modldrv g_vbgr0SolModule =
191{
192 &mod_driverops, /* extern from kernel */
193 DEVICE_DESC " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
194 &g_vbgr0SolDevOps
195};
196
197/**
198 * modlinkage: export install/remove/info to the kernel.
199 */
200static struct modlinkage g_vbgr0SolModLinkage =
201{
202 MODREV_1, /* loadable module system revision */
203 &g_vbgr0SolModule,
204 NULL /* terminate array of linkage structures */
205};
206
207#else /* TESTCASE */
208static void *g_vbgr0SolModLinkage;
209#endif /* TESTCASE */
210
211/**
212 * State info for each open file handle.
213 */
214typedef struct
215{
216 /** Pointer to the session handle. */
217 PVBOXGUESTSESSION pSession;
218 /** The STREAMS write queue which we need for sending messages up to
219 * user-space. */
220 queue_t *pWriteQueue;
221 /* The current greatest horizontal pixel offset on the screen, used for
222 * absolute mouse position reporting.
223 */
224 int cMaxScreenX;
225 /* The current greatest vertical pixel offset on the screen, used for
226 * absolute mouse position reporting.
227 */
228 int cMaxScreenY;
229} VBGR0STATE, *PVBGR0STATE;
230
231
232/******************************************************************************
233* Global Variables *
234******************************************************************************/
235
236/** Device handle (we support only one instance). */
237static dev_info_t *g_pDip = NULL;
238/** Array of state structures for open device nodes. I don't care about
239 * wasting a few K of memory. */
240static VBGR0STATE g_aOpenNodeStates[MAX_OPEN_NODES] /* = { 0 } */;
241/** Mutex to protect the queue pointers in the node states from being unset
242 * during an IRQ. */
243static kmutex_t g_StateMutex;
244/** Device extention & session data association structure. */
245static VBOXGUESTDEVEXT g_DevExt;
246/** IO port handle. */
247static ddi_acc_handle_t g_PciIOHandle;
248/** MMIO handle. */
249static ddi_acc_handle_t g_PciMMIOHandle;
250/** IO Port. */
251static uint16_t g_uIOPortBase;
252/** Address of the MMIO region.*/
253static char *g_pMMIOBase; /* Actually caddr_t. */
254/** Size of the MMIO region. */
255static off_t g_cbMMIO;
256/** Pointer to the interrupt handle vector */
257static ddi_intr_handle_t *g_pIntr;
258/** Number of actually allocated interrupt handles */
259static size_t g_cIntrAllocated;
260/** The IRQ Mutex */
261static kmutex_t g_IrqMutex;
262
263
264/******************************************************************************
265* Kernel entry points *
266******************************************************************************/
267
268/** Driver initialisation. */
269int _init(void)
270{
271 /*
272 * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
273 */
274 int rc = RTR0Init(0);
275 if (RT_SUCCESS(rc))
276 {
277 PRTLOGGER pRelLogger;
278 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
279 modctl_t *pModCtl;
280
281 rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all",
282 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
283 RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER, NULL);
284 if (RT_SUCCESS(rc))
285 RTLogRelSetDefaultInstance(pRelLogger);
286 else
287 cmn_err(CE_NOTE, "failed to initialize driver logging rc=%d!\n", rc);
288
289 /*
290 * Prevent module autounloading.
291 */
292 pModCtl = mod_getctl(&g_vbgr0SolModLinkage);
293 if (pModCtl)
294 pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
295 else
296 LogRel((DEVICE_NAME ":failed to disable autounloading!\n"));
297 /* Initialise the node state mutex. This will be taken in the ISR. */
298 mutex_init(&g_StateMutex, NULL, MUTEX_DRIVER,
299 DDI_INTR_PRI(uIntrPriority));
300 rc = mod_install(&g_vbgr0SolModLinkage);
301 }
302 else
303 {
304 cmn_err(CE_NOTE, "_init: RTR0Init failed. rc=%d\n", rc);
305 return EINVAL;
306 }
307
308 return rc;
309}
310
311
312#ifdef TESTCASE
313/** Simple test of the flow through _init. */
314static void test_init(RTTEST hTest)
315{
316 RTTestSub(hTest, "Testing _init");
317 RTTEST_CHECK(hTest, _init() == 0);
318}
319#endif
320
321
322/** Driver cleanup. */
323int _fini(void)
324{
325 int rc;
326
327 LogFlow((DEVICE_NAME ":_fini\n"));
328 rc = mod_remove(&g_vbgr0SolModLinkage);
329 mutex_destroy(&g_StateMutex);
330
331 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
332 RTLogDestroy(RTLogSetDefaultInstance(NULL));
333
334 RTR0Term();
335 return rc;
336}
337
338
339/** Driver identification. */
340int _info(struct modinfo *pModInfo)
341{
342 LogFlow((DEVICE_NAME ":_info\n"));
343 return mod_info(&g_vbgr0SolModLinkage, pModInfo);
344}
345
346
347/******************************************************************************
348* Helper routines *
349******************************************************************************/
350
351/** Calls the kernel IOCtl to report mouse status to the host on behalf of
352 * an open session. */
353static int vbgr0SolSetMouseStatus(PVBOXGUESTSESSION pSession, uint32_t fStatus)
354{
355 return VBoxGuestCommonIOCtl(VBOXGUEST_IOCTL_SET_MOUSE_STATUS, &g_DevExt,
356 pSession, &fStatus, sizeof(fStatus), NULL);
357}
358
359/** Resets (zeroes) a member in our open node state array in an IRQ-safe way.
360 */
361static void vbgr0SolResetSoftState(PVBGR0STATE pState)
362{
363 mutex_enter(&g_StateMutex);
364 RT_ZERO(*pState);
365 mutex_exit(&g_StateMutex);
366}
367
368/******************************************************************************
369* Main code *
370******************************************************************************/
371
372/**
373 * Open callback for the read queue, which we use as a generic device open
374 * handler.
375 */
376int vbgr0SolOpen(queue_t *pReadQueue, dev_t *pDev, int fFlag, int fMode,
377 cred_t *pCred)
378{
379 int rc;
380 PVBOXGUESTSESSION pSession = NULL;
381 PVBGR0STATE pState = NULL;
382 unsigned cInstance;
383
384 NOREF(fFlag);
385 NOREF(pCred);
386 LogFlow((DEVICE_NAME "::Open\n"));
387
388 /*
389 * Sanity check on the mode parameter - only open as a driver, not a
390 * module, and we do cloning ourselves. Note that we start at 1, as minor
391 * zero was allocated to the file system device node in vbgr0SolAttach
392 * (see https://blogs.oracle.com/timatworkhomeandinbetween/entry/using_makedevice_in_a_drivers).
393 */
394 if (fMode)
395 return EINVAL;
396
397 for (cInstance = 1; cInstance < MAX_OPEN_NODES; cInstance++)
398 {
399 if (ASMAtomicCmpXchgPtr(&g_aOpenNodeStates[cInstance].pWriteQueue,
400 WR(pReadQueue), NULL))
401 {
402 pState = &g_aOpenNodeStates[cInstance];
403 break;
404 }
405 }
406 if (!pState)
407 {
408 Log((DEVICE_NAME "::Open: too many open instances."));
409 return ENXIO;
410 }
411
412 /*
413 * Create a new session.
414 */
415 rc = VBoxGuestCreateUserSession(&g_DevExt, &pSession);
416 if (RT_SUCCESS(rc))
417 {
418 pState->pSession = pSession;
419 *pDev = makedevice(getmajor(*pDev), cInstance);
420 /* Initialise user data for the queues to our state and vice-versa. */
421 WR(pReadQueue)->q_ptr = (char *)pState;
422 pReadQueue->q_ptr = (char *)pState;
423 qprocson(pState->pWriteQueue);
424 Log((DEVICE_NAME "::Open: pSession=%p pState=%p pid=%d\n", pSession, pState, (int)RTProcSelf()));
425 return 0;
426 }
427
428 /* Failed, clean up. */
429 vbgr0SolResetSoftState(pState);
430
431 LogRel((DEVICE_NAME "::Open: VBoxGuestCreateUserSession failed. rc=%d\n", rc));
432 return EFAULT;
433}
434
435
436/**
437 * Close callback for the read queue, which we use as a generic device close
438 * handler.
439 */
440int vbgr0SolClose(queue_t *pReadQueue, int fFlag, cred_t *pCred)
441{
442 PVBOXGUESTSESSION pSession = NULL;
443 PVBGR0STATE pState = (PVBGR0STATE)pReadQueue->q_ptr;
444
445 LogFlow((DEVICE_NAME "::Close pid=%d\n", (int)RTProcSelf()));
446 NOREF(fFlag);
447 NOREF(pCred);
448
449 if (!pState)
450 {
451 Log((DEVICE_NAME "::Close: failed to get pState.\n"));
452 return EFAULT;
453 }
454 qprocsoff(pState->pWriteQueue);
455 pSession = pState->pSession;
456 vbgr0SolResetSoftState(pState);
457 pReadQueue->q_ptr = NULL;
458
459 Log((DEVICE_NAME "::Close: pSession=%p pState=%p\n", pSession, pState));
460 if (!pSession)
461 {
462 Log((DEVICE_NAME "::Close: failed to get pSession.\n"));
463 return EFAULT;
464 }
465
466 /*
467 * Close the session.
468 */
469 VBoxGuestCloseSession(&g_DevExt, pSession);
470 return 0;
471}
472
473
474#ifdef TESTCASE
475/** Simple test of vbgr0SolOpen and vbgr0SolClose. */
476static void testOpenClose(RTTEST hTest)
477{
478 queue_t aQueues[4];
479 dev_t device = 0;
480 int rc;
481
482 RTTestSub(hTest, "Testing vbgr0SolOpen and vbgr0SolClose");
483 RT_ZERO(g_aOpenNodeStates);
484 RT_ZERO(aQueues);
485 doInitQueues(&aQueues[0]);
486 doInitQueues(&aQueues[2]);
487 rc = vbgr0SolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
488 RTTEST_CHECK(hTest, rc == 0);
489 RTTEST_CHECK(hTest, g_aOpenNodeStates[1].pWriteQueue == WR(&aQueues[0]));
490 rc = vbgr0SolOpen(RD(&aQueues[2]), &device, 0, 0, NULL);
491 RTTEST_CHECK(hTest, rc == 0);
492 RTTEST_CHECK(hTest, g_aOpenNodeStates[2].pWriteQueue == WR(&aQueues[2]));
493 vbgr0SolClose(RD(&aQueues[0]), 0, NULL);
494 vbgr0SolClose(RD(&aQueues[1]), 0, NULL);
495}
496#endif
497
498
499/* Helper for vbgr0SolWPut. */
500static int vbgr0SolDispatchIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk);
501
502/**
503 * Handler for messages sent from above (user-space and upper modules) which
504 * land in our write queue.
505 */
506int vbgr0SolWPut(queue_t *pWriteQueue, mblk_t *pMBlk)
507{
508 PVBGR0STATE pState = (PVBGR0STATE)pWriteQueue->q_ptr;
509
510 LogFlowFunc((DEVICE_NAME "::\n"));
511 switch (pMBlk->b_datap->db_type)
512 {
513 case M_FLUSH:
514 /* Flush the write queue if so requested. */
515 if (*pMBlk->b_rptr & FLUSHW)
516 flushq(pWriteQueue, FLUSHDATA);
517
518 /* Flush the read queue if so requested. */
519 if (*pMBlk->b_rptr & FLUSHR)
520 flushq(RD(pWriteQueue), FLUSHDATA);
521
522 /* We have no one below us to pass the message on to. */
523 return 0;
524 /* M_IOCDATA is additional data attached to (at least) transparent
525 * IOCtls. We handle the two together here and separate them further
526 * down. */
527 case M_IOCTL:
528 case M_IOCDATA:
529 {
530 int err = vbgr0SolDispatchIOCtl(pWriteQueue, pMBlk);
531 if (!err)
532 qreply(pWriteQueue, pMBlk);
533 else
534 miocnak(pWriteQueue, pMBlk, 0, err);
535 break;
536 }
537 }
538 return 0;
539}
540
541
542#ifdef TESTCASE
543/* Constants, definitions and test functions for testWPut. */
544static const int g_ccTestWPutFirmEvent = VUID_FIRM_EVENT;
545# define PVGFORMAT (&g_ccTestWPutFirmEvent)
546# define CBGFORMAT (sizeof(g_ccTestWPutFirmEvent))
547static const Ms_screen_resolution g_TestResolution = { 640, 480 };
548# define PMSIOSRES (&g_TestResolution)
549# define CBMSIOSRES (sizeof(g_TestResolution))
550
551static inline void testSetResolution(RTTEST hTest, queue_t *pWriteQueue,
552 struct msgb *pMBlk)
553{
554 PVBGR0STATE pState = (PVBGR0STATE)pWriteQueue->q_ptr;
555 RTTEST_CHECK_MSG(hTest, pState->cMaxScreenX
556 == g_TestResolution.width - 1,
557 (hTest, "pState->cMaxScreenX=%d\n", pState->cMaxScreenX));
558 RTTEST_CHECK_MSG(hTest, pState->cMaxScreenY
559 == g_TestResolution.height - 1,
560 (hTest, "pState->cMaxScreenY=%d\n", pState->cMaxScreenY));
561}
562
563/** Data table for testWPut. */
564static struct
565{
566 unsigned char iDBType;
567 int iIOCCmd;
568 size_t cbData;
569 const void *pvDataIn;
570 size_t cbDataIn;
571 const void *pvDataOut;
572 size_t cbDataOut;
573 int rcExp;
574 void (*pfnExtra)(RTTEST hTest, queue_t *pWriteQueue, struct msgb *pMBlk);
575} g_asTestWPut[] =
576{
577 /* iDBType iIOCCmd cbData pvDataIn cbDataIn
578 pvDataOut cbDataOut rcExp pfnExtra */
579 { M_IOCTL, VUIDGFORMAT, sizeof(int), NULL, 0,
580 PVGFORMAT, CBGFORMAT, 0, NULL },
581 { M_IOCTL, VUIDGFORMAT, sizeof(int) - 1, NULL, 0,
582 NULL, 0, EINVAL, NULL },
583 { M_IOCTL, VUIDGFORMAT, sizeof(int) + 1, NULL, 0,
584 PVGFORMAT, CBGFORMAT, 0, NULL },
585 { M_IOCTL, VUIDSFORMAT, sizeof(int), PVGFORMAT, CBGFORMAT,
586 NULL, 0, 0, NULL },
587 { M_IOCTL, MSIOSRESOLUTION, CBMSIOSRES, PMSIOSRES, CBMSIOSRES,
588 NULL, 0, 0, testSetResolution },
589 { M_IOCTL, VUIDGWHEELINFO, 0, NULL, 0,
590 NULL, 0, EINVAL, NULL }
591};
592
593# undef PVGFORMAT
594# undef CBGFORMAT
595# undef PMSIOSRES
596# undef CBMSIOSRES
597
598/** Test WPut's handling of different IOCtls, which is bulk of the logic in
599 * this file. */
600static void testWPut(RTTEST hTest)
601{
602 queue_t aQueues[2];
603 dev_t device = 0;
604 struct msgb MBlk, MBlkCont;
605 struct datab DBlk;
606 struct iocblk IOCBlk;
607 int rc, cFormat = 0;
608 unsigned i;
609
610 /* Single simple test to start with. We can try to make it more systematic
611 * next. */
612 RTTestSub(hTest, "Testing vbgr0WPut");
613 for (i = 0; i < RT_ELEMENTS(g_asTestWPut); ++i)
614 {
615 unsigned char acData[1024];
616
617 RT_ZERO(aQueues);
618 doInitQueues(&aQueues[0]);
619 rc = vbgr0SolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
620 RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
621 RTTEST_CHECK_MSG(hTest, g_aOpenNodeStates[1].pWriteQueue
622 == WR(&aQueues[0]), (hTest, "i=%u\n", i));
623 RT_ZERO(MBlk);
624 RT_ZERO(DBlk);
625 RT_ZERO(IOCBlk);
626 RT_ZERO(MBlkCont);
627 RT_ZERO(acData);
628 DBlk.db_type = g_asTestWPut[i].iDBType;
629 IOCBlk.ioc_cmd = g_asTestWPut[i].iIOCCmd;
630 IOCBlk.ioc_count = g_asTestWPut[i].cbData;
631 AssertReturnVoid(g_asTestWPut[i].cbData <= sizeof(acData));
632 AssertReturnVoid(g_asTestWPut[i].cbDataIn <= g_asTestWPut[i].cbData);
633 AssertReturnVoid(g_asTestWPut[i].cbDataOut <= g_asTestWPut[i].cbData);
634 memcpy(acData, g_asTestWPut[i].pvDataIn, g_asTestWPut[i].cbDataIn);
635 MBlkCont.b_rptr = acData;
636 MBlkCont.b_wptr = acData + g_asTestWPut[i].cbData;
637 MBlk.b_cont = &MBlkCont;
638 MBlk.b_rptr = (unsigned char *)&IOCBlk;
639 MBlk.b_wptr = (unsigned char *)&IOCBlk + sizeof(IOCBlk);
640 MBlk.b_datap = &DBlk;
641 rc = vbgr0SolWPut(WR(&aQueues[0]), &MBlk);
642 RTTEST_CHECK_MSG(hTest, IOCBlk.ioc_error == g_asTestWPut[i].rcExp,
643 (hTest, "i=%u, IOCBlk.ioc_error=%d\n", i,
644 IOCBlk.ioc_error));
645 RTTEST_CHECK_MSG(hTest, IOCBlk.ioc_count == g_asTestWPut[i].cbDataOut,
646 (hTest, "i=%u, ioc_count=%u\n", i, IOCBlk.ioc_count));
647 RTTEST_CHECK_MSG(hTest, !memcmp(acData, g_asTestWPut[i].pvDataOut,
648 g_asTestWPut[i].cbDataOut),
649 (hTest, "i=%u\n", i));
650 /* Hack to ensure that miocpullup() gets called when needed. */
651 if ( g_asTestWPut[i].iDBType == M_IOCTL
652 && g_asTestWPut[i].cbData > 0) /* TRANSPARENT == -1 */
653 RTTEST_CHECK_MSG(hTest, MBlk.b_flag == 1, (hTest, "i=%u\n", i));
654 if (g_asTestWPut[i].pfnExtra)
655 g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), &MBlk);
656 vbgr0SolClose(RD(&aQueues[1]), 0, NULL);
657 }
658}
659#endif
660
661
662/** Data transfer direction of an IOCtl. This is used for describing
663 * transparent IOCtls, and @a UNSPECIFIED is not a valid value for them. */
664enum IOCTLDIRECTION
665{
666 /** This IOCtl transfers no data. */
667 NONE,
668 /** This IOCtl only transfers data from user to kernel. */
669 IN,
670 /** This IOCtl only transfers data from kernel to user. */
671 OUT,
672 /** This IOCtl transfers data from user to kernel and back. */
673 BOTH,
674 /** We aren't saying anything about how the IOCtl transfers data. */
675 UNSPECIFIED
676};
677
678/**
679 * IOCtl handler function.
680 * @returns 0 on success, error code on failure.
681 * @param iCmd The IOCtl command number.
682 * @param pvData Buffer for the user data.
683 * @param cbBuffer Size of the buffer in @a pvData or zero.
684 * @param pcbData Where to set the size of the data returned. Required for
685 * handlers which return data.
686 * @param prc Where to store the return code. Default is zero. Only
687 * used for IOCtls without data for convenience of
688 * implemention.
689 */
690typedef int FNVBGR0SOLIOCTL(PVBGR0STATE pState, int iCmd, void *pvData,
691 size_t cbBuffer, size_t *pcbData, int *prc);
692typedef FNVBGR0SOLIOCTL *PFNVBGR0SOLIOCTL;
693
694/* Helpers for vbgr0SolDispatchIOCtl. */
695static int vbgr0SolHandleIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk,
696 PFNVBGR0SOLIOCTL pfnHandler,
697 int iCmd, size_t cbTransparent,
698 enum IOCTLDIRECTION enmDirection);
699static int vbgr0SolVUIDIOCtl(PVBGR0STATE pState, int iCmd, void *pvData,
700 size_t cbBuffer, size_t *pcbData, int *prc);
701static int vbgr0SolGuestIOCtl(PVBGR0STATE pState, int iCmd, void *pvData,
702 size_t cbBuffer, size_t *pcbData, int *prc);
703
704/** Table of supported VUID IOCtls. */
705struct
706{
707 /** The IOCtl number. */
708 int iCmd;
709 /** The size of the buffer which needs to be copied between user and kernel
710 * space, or zero if unknown (must be known for tranparent IOCtls). */
711 size_t cbBuffer;
712 /** The direction the buffer data needs to be copied. This must be
713 * specified for transparent IOCtls. */
714 enum IOCTLDIRECTION enmDirection;
715} g_aVUIDIOCtlDescriptions[] =
716{
717 { VUIDGFORMAT, sizeof(int), OUT },
718 { VUIDSFORMAT, 0, NONE },
719 { VUIDGADDR, 0, UNSPECIFIED },
720 { MSIOGETPARMS, sizeof(Ms_parms), OUT },
721 { MSIOSETPARMS, 0, NONE },
722 { MSIOSRESOLUTION, sizeof(Ms_screen_resolution), IN },
723 { MSIOBUTTONS, sizeof(int), OUT },
724 { VUIDGWHEELCOUNT, sizeof(int), OUT },
725 { VUIDGWHEELINFO, 0, UNSPECIFIED },
726 { VUIDGWHEELSTATE, 0, UNSPECIFIED },
727 { VUIDSWHEELSTATE, 0, UNSPECIFIED }
728};
729
730/**
731 * Handle a STREAMS IOCtl message for our driver on the write stream. This
732 * function takes care of the IOCtl logic only and does not call qreply() or
733 * miocnak() at all - the caller must call these on success or failure
734 * respectively.
735 * @returns 0 on success or the IOCtl error code on failure.
736 * @param pWriteQueue pointer to the STREAMS write queue structure.
737 * @param pMBlk pointer to the STREAMS message block structure.
738 */
739static int vbgr0SolDispatchIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk)
740{
741 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
742 int iCmd = pIOCBlk->ioc_cmd, iCmdType = iCmd & ~0xff;
743 size_t cbBuffer;
744 enum IOCTLDIRECTION enmDirection;
745
746 LogFlowFunc((DEVICE_NAME "::iCmdType=%c, iCmd=%d\n", iCmdType, iCmd));
747 switch (iCmdType)
748 {
749 case MSIOC:
750 case VUIOC:
751 {
752 unsigned i;
753
754 for (i = 0; i < RT_ELEMENTS(g_aVUIDIOCtlDescriptions); ++i)
755 if (g_aVUIDIOCtlDescriptions[i].iCmd == iCmd)
756 {
757 cbBuffer = g_aVUIDIOCtlDescriptions[i].cbBuffer;
758 enmDirection = g_aVUIDIOCtlDescriptions[i].enmDirection;
759 return vbgr0SolHandleIOCtl(pWriteQueue, pMBlk,
760 vbgr0SolVUIDIOCtl, iCmd,
761 cbBuffer, enmDirection);
762 }
763 return EINVAL;
764 }
765 case 'V' << 8:
766 return vbgr0SolHandleIOCtl(pWriteQueue, pMBlk, vbgr0SolGuestIOCtl,
767 iCmd, 0, UNSPECIFIED);
768 default:
769 return ENOTTY;
770 }
771}
772
773
774/* Helpers for vbgr0SolHandleIOCtl. */
775static int vbgr0SolHandleIOCtlData(queue_t *pWriteQueue, mblk_t *pMBlk,
776 PFNVBGR0SOLIOCTL pfnHandler, int iCmd,
777 size_t cbTransparent,
778 enum IOCTLDIRECTION enmDirection);
779
780static int vbgr0SolHandleTransparentIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk,
781 PFNVBGR0SOLIOCTL pfnHandler,
782 int iCmd, size_t cbTransparent,
783 enum IOCTLDIRECTION enmDirection);
784
785static int vbgr0SolHandleIStrIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk,
786 PFNVBGR0SOLIOCTL pfnHandler, int iCmd);
787
788/**
789 * Generic code for handling STREAMS-specific IOCtl logic and boilerplate. It
790 * calls the IOCtl handler passed to it without the handler having to be aware
791 * of STREAMS structures, or whether this is a transparent (traditional) or an
792 * I_STR (using a STREAMS structure to describe the data) IOCtl. With the
793 * caveat that we only support transparent IOCtls which pass all data in a
794 * single buffer of a fixed size (I_STR IOCtls are restricted to a single
795 * buffer anyway, but the caller can choose the buffer size).
796 * @returns 0 on success or the IOCtl error code on failure.
797 * @param pWriteQueue pointer to the STREAMS write queue structure.
798 * @param pMBlk pointer to the STREAMS message block structure.
799 * @param pfnHandler pointer to the right IOCtl handler function for this
800 * IOCtl number.
801 * @param iCmd IOCtl command number.
802 * @param cbTransparent size of the user space buffer for this IOCtl number,
803 * used for processing transparent IOCtls. Pass zero
804 * for IOCtls with no maximum buffer size (which will
805 * not be able to be handled as transparent) or with
806 * no argument.
807 * @param enmDirection data transfer direction of the IOCtl.
808 */
809static int vbgr0SolHandleIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk,
810 PFNVBGR0SOLIOCTL pfnHandler, int iCmd,
811 size_t cbTransparent,
812 enum IOCTLDIRECTION enmDirection)
813{
814 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
815 PVBGR0STATE pState = (PVBGR0STATE)pWriteQueue->q_ptr;
816
817 if (pMBlk->b_datap->db_type == M_IOCDATA)
818 return vbgr0SolHandleIOCtlData(pWriteQueue, pMBlk, pfnHandler, iCmd,
819 cbTransparent, enmDirection);
820 else if ( pMBlk->b_datap->db_type == M_IOCTL
821 && pIOCBlk->ioc_count == TRANSPARENT)
822 return vbgr0SolHandleTransparentIOCtl(pWriteQueue, pMBlk, pfnHandler,
823 iCmd, cbTransparent,
824 enmDirection);
825 else if (pMBlk->b_datap->db_type == M_IOCTL)
826 return vbgr0SolHandleIStrIOCtl(pWriteQueue, pMBlk, pfnHandler, iCmd);
827 return EINVAL;
828}
829
830
831/**
832 * Helper for vbgr0SolHandleIOCtl. This rather complicated-looking
833 * code is basically the standard boilerplate for handling any streams IOCtl
834 * additional data, which we currently only use for transparent IOCtls.
835 * @copydoc vbgr0SolHandleIOCtl
836 */
837static int vbgr0SolHandleIOCtlData(queue_t *pWriteQueue, mblk_t *pMBlk,
838 PFNVBGR0SOLIOCTL pfnHandler, int iCmd,
839 size_t cbTransparent,
840 enum IOCTLDIRECTION enmDirection)
841{
842 struct copyresp *pCopyResp = (struct copyresp *)pMBlk->b_rptr;
843 PVBGR0STATE pState = (PVBGR0STATE)pWriteQueue->q_ptr;
844
845 if (pCopyResp->cp_rval) /* cp_rval is a pointer used as a boolean. */
846 {
847 freemsg(pMBlk);
848 return EAGAIN;
849 }
850 if ((pCopyResp->cp_private && enmDirection == BOTH) || enmDirection == IN)
851 {
852 size_t cbData = 0;
853 void *pvData = NULL;
854 int err;
855
856 if (cbData < cbTransparent)
857 return EINVAL;
858 if (!pMBlk->b_cont)
859 return EINVAL;
860 if (enmDirection == BOTH && !pCopyResp->cp_private)
861 return EINVAL;
862 pvData = pMBlk->b_cont->b_rptr;
863 err = pfnHandler(pState, iCmd, pvData, cbTransparent, &cbData, NULL);
864 if (!err && enmDirection == BOTH)
865 mcopyout(pMBlk, NULL, cbData, pCopyResp->cp_private, NULL);
866 else if (!err && enmDirection == IN)
867 miocack(pWriteQueue, pMBlk, 0, 0);
868 return err;
869 }
870 else
871 {
872 AssertReturn(enmDirection == OUT || enmDirection == BOTH, EINVAL);
873 miocack(pWriteQueue, pMBlk, 0, 0);
874 return 0;
875 }
876}
877
878/**
879 * Helper for vbgr0SolHandleIOCtl. This rather complicated-looking
880 * code is basically the standard boilerplate for handling transparent IOCtls,
881 * that is, IOCtls which are not re-packed inside STREAMS IOCtls.
882 * @copydoc vbgr0SolHandleIOCtl
883 */
884int vbgr0SolHandleTransparentIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk,
885 PFNVBGR0SOLIOCTL pfnHandler, int iCmd,
886 size_t cbTransparent,
887 enum IOCTLDIRECTION enmDirection)
888{
889 int err = 0, rc = 0;
890 size_t cbData = 0;
891 PVBGR0STATE pState = (PVBGR0STATE)pWriteQueue->q_ptr;
892
893 if ( (enmDirection != NONE && !pMBlk->b_cont)
894 || enmDirection == UNSPECIFIED)
895 return EINVAL;
896 if (enmDirection == IN || enmDirection == BOTH)
897 {
898 void *pUserAddr = NULL;
899 /* We only need state data if there is something to copy back. */
900 if (enmDirection == BOTH)
901 pUserAddr = *(void **)pMBlk->b_cont->b_rptr;
902 mcopyin(pMBlk, pUserAddr /* state data */, cbTransparent, NULL);
903 }
904 else if (enmDirection == OUT)
905 {
906 mblk_t *pMBlkOut = allocb(cbOut, BPRI_MED);
907 void *pvData;
908
909 if (!pMBlkOut)
910 return EAGAIN;
911 pvData = pMBlkOut->b_rptr;
912 err = pfnHandler(pState, iCmd, pvData, cbTransparent, &cbData, NULL);
913 if (!err)
914 mcopyout(pMBlk, NULL, cbData, NULL, pMBlkOut);
915 else
916 freemsg(pMBlkOut);
917 }
918 else
919 {
920 AssertReturn(enmDirection == NONE, EINVAL);
921 err = pfnHandler(pState, iCmd, NULL, 0, NULL, &rc);
922 if (!err)
923 miocack(pWriteQueue, pMBlk, 0, rc);
924 }
925 return err;
926}
927
928/**
929 * Helper for vbgr0SolHandleIOCtl. This rather complicated-looking
930 * code is basically the standard boilerplate for handling any streams IOCtl.
931 * @copydoc vbgr0SolHandleIOCtl
932 */
933static int vbgr0SolHandleIStrIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk,
934 PFNVBGR0SOLIOCTL pfnHandler, int iCmd)
935{
936 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
937 PVBGR0STATE pState = (PVBGR0STATE)pWriteQueue->q_ptr;
938 uint_t cbBuffer = pIOCBlk->ioc_count;
939 void *pvData = NULL;
940 int err, rc = 0;
941 size_t cbData = 0;
942
943 if (cbBuffer && !pMBlk->b_cont)
944 return EINVAL;
945 /* Repack the whole buffer into a single message block if needed. */
946 if (cbBuffer)
947 {
948 err = miocpullup(pMBlk, cbBuffer);
949 if (err)
950 return err;
951 pvData = pMBlk->b_cont->b_rptr;
952 }
953 err = pfnHandler(pState, iCmd, pvData, cbBuffer, &cbData, &rc);
954 if (!err)
955 miocack(pWriteQueue, pMBlk, cbData, rc);
956 return err;
957}
958
959
960/**
961 * Handle a VUID input device IOCtl.
962 * @copydoc FNVBGR0SOLIOCTL
963 */
964static int vbgr0SolVUIDIOCtl(PVBGR0STATE pState, int iCmd, void *pvData,
965 size_t cbBuffer, size_t *pcbData, int *prc)
966{
967 LogFlowFunc((DEVICE_NAME ":: " /* no '\n' */));
968 switch (iCmd)
969 {
970 case VUIDGFORMAT:
971 {
972 LogFlowFunc(("VUIDGFORMAT\n"));
973 if (cbBuffer < sizeof(int))
974 return EINVAL;
975 *(int *)pvData = VUID_FIRM_EVENT;
976 *pcbData = sizeof(int);
977 return 0;
978 }
979 case VUIDSFORMAT:
980 LogFlowFunc(("VUIDSFORMAT\n"));
981 /* We define our native format to be VUID_FIRM_EVENT, so there
982 * is nothing more to do and we exit here on success or on
983 * failure. */
984 return 0;
985 case VUIDGADDR:
986 case VUIDSADDR:
987 LogFlowFunc(("VUIDGADDR/VUIDSADDR\n"));
988 return ENOTTY;
989 case MSIOGETPARMS:
990 {
991 Ms_parms parms = { 0 };
992
993 LogFlowFunc(("MSIOGETPARMS\n"));
994 if (cbBuffer < sizeof(Ms_parms))
995 return EINVAL;
996 *(Ms_parms *)pvData = parms;
997 *pcbData = sizeof(Ms_parms);
998 return 0;
999 }
1000 case MSIOSETPARMS:
1001 LogFlowFunc(("MSIOSETPARMS\n"));
1002 return 0;
1003 case MSIOSRESOLUTION:
1004 {
1005 Ms_screen_resolution *pResolution = (Ms_screen_resolution *)pvData;
1006 int rc;
1007
1008 LogFlowFunc(("MSIOSRESOLUTION\n"));
1009 if (cbBuffer < sizeof(Ms_screen_resolution))
1010 return EINVAL;
1011 pState->cMaxScreenX = pResolution->width - 1;
1012 pState->cMaxScreenY = pResolution->height - 1;
1013 /* Note: we don't disable this again until session close. */
1014 rc = vbgr0SolSetMouseStatus(pState->pSession,
1015 VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
1016 | VMMDEV_MOUSE_NEW_PROTOCOL);
1017 if (RT_SUCCESS(rc))
1018 return 0;
1019 pState->cMaxScreenX = 0;
1020 pState->cMaxScreenY = 0;
1021 return ENODEV;
1022 }
1023 case MSIOBUTTONS:
1024 {
1025 LogFlowFunc(("MSIOBUTTONS\n"));
1026 if (cbBuffer < sizeof(int))
1027 return EINVAL;
1028 *(int *)pvData = 0;
1029 *pcbData = sizeof(int);
1030 return 0;
1031 }
1032 case VUIDGWHEELCOUNT:
1033 {
1034 LogFlowFunc(("VUIDGWHEELCOUNT\n"));
1035 if (cbBuffer < sizeof(int))
1036 return EINVAL;
1037 *(int *)pvData = 0;
1038 *pcbData = sizeof(int);
1039 return 0;
1040 }
1041 case VUIDGWHEELINFO:
1042 case VUIDGWHEELSTATE:
1043 case VUIDSWHEELSTATE:
1044 LogFlowFunc(("VUIDGWHEELINFO/VUIDGWHEELSTATE/VUIDSWHEELSTATE\n"));
1045 return EINVAL;
1046 default:
1047 LogFlowFunc(("Invalid IOCtl command %x\n", iCmd));
1048 return EINVAL;
1049 }
1050}
1051
1052
1053/**
1054 * Handle a VBoxGuest IOCtl.
1055 * @copydoc FNVBGR0SOLIOCTL
1056 */
1057static int vbgr0SolGuestIOCtl(PVBGR0STATE pState, int iCmd, void *pvData,
1058 size_t cbBuffer, size_t *pcbData, int *prc)
1059{
1060 int rc = VBoxGuestCommonIOCtl(iCmd, &g_DevExt, pState->pSession, pvData, cbBuffer, pcbData);
1061 if (RT_SUCCESS(rc))
1062 {
1063 *prc = rc;
1064 return 0;
1065 }
1066 else
1067 {
1068 /*
1069 * We Log() instead of LogRel() here because VBOXGUEST_IOCTL_WAITEVENT can return VERR_TIMEOUT,
1070 * VBOXGUEST_IOCTL_CANCEL_ALL_EVENTS can return VERR_INTERRUPTED and possibly more in the future;
1071 * which are not really failures that require logging.
1072 */
1073 Log((DEVICE_NAME "::IOCtl: VBoxGuestCommonIOCtl failed. Cmd=%#x rc=%d\n", iCmd, rc));
1074 rc = RTErrConvertToErrno(rc);
1075 return rc;
1076 }
1077}
1078
1079
1080/**
1081 * Info entry point, called by solaris kernel for obtaining driver info.
1082 *
1083 * @param pDip The module structure instance (do not use).
1084 * @param enmCmd Information request type.
1085 * @param pvArg Type specific argument.
1086 * @param ppvResult Where to store the requested info.
1087 *
1088 * @return corresponding solaris error code.
1089 */
1090int vbgr0SolGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg,
1091 void **ppvResult)
1092{
1093 int rc = DDI_SUCCESS;
1094
1095 LogFlow((DEVICE_NAME "::GetInfo\n"));
1096 switch (enmCmd)
1097 {
1098 case DDI_INFO_DEVT2DEVINFO:
1099 *ppvResult = (void *)g_pDip;
1100 break;
1101
1102 case DDI_INFO_DEVT2INSTANCE:
1103 *ppvResult = (void *)(uintptr_t)ddi_get_instance(g_pDip);
1104 break;
1105
1106 default:
1107 rc = DDI_FAILURE;
1108 break;
1109 }
1110
1111 NOREF(pvArg);
1112 return rc;
1113}
1114
1115
1116/* Helpers for vbgr0SolAttach and vbgr0SolDetach. */
1117static int vbgr0SolAddIRQ(dev_info_t *pDip);
1118static void vbgr0SolRemoveIRQ(dev_info_t *pDip);
1119
1120/**
1121 * Attach entry point, to attach a device to the system or resume it.
1122 *
1123 * @param pDip The module structure instance.
1124 * @param enmCmd Attach type (ddi_attach_cmd_t)
1125 *
1126 * @return corresponding solaris error code.
1127 */
1128int vbgr0SolAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
1129{
1130 LogFlow((DEVICE_NAME "::Attach\n"));
1131 switch (enmCmd)
1132 {
1133 case DDI_ATTACH:
1134 {
1135 int instance, rc;
1136 ddi_acc_handle_t PciHandle;
1137
1138 if (g_pDip)
1139 {
1140 LogRel((DEVICE_NAME "::Attach: Only one instance supported.\n"));
1141 return DDI_FAILURE;
1142 }
1143 instance = ddi_get_instance(pDip);
1144
1145 /*
1146 * Enable resources for PCI access.
1147 */
1148 rc = pci_config_setup(pDip, &PciHandle);
1149 if (rc == DDI_SUCCESS)
1150 {
1151 /*
1152 * Map the register address space.
1153 */
1154 char *baseAddr; /* Actually caddr_t. */
1155 ddi_device_acc_attr_t deviceAttr;
1156 deviceAttr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1157 deviceAttr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
1158 deviceAttr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1159 deviceAttr.devacc_attr_access = DDI_DEFAULT_ACC;
1160 rc = ddi_regs_map_setup(pDip, 1, &baseAddr, 0, 0, &deviceAttr, &g_PciIOHandle);
1161 if (rc == DDI_SUCCESS)
1162 {
1163 /*
1164 * Read size of the MMIO region.
1165 */
1166 g_uIOPortBase = (uintptr_t)baseAddr;
1167 rc = ddi_dev_regsize(pDip, 2, &g_cbMMIO);
1168 if (rc == DDI_SUCCESS)
1169 {
1170 rc = ddi_regs_map_setup(pDip, 2, &g_pMMIOBase, 0, g_cbMMIO, &deviceAttr,
1171 &g_PciMMIOHandle);
1172 if (rc == DDI_SUCCESS)
1173 {
1174 /*
1175 * Add IRQ of VMMDev.
1176 */
1177 rc = vbgr0SolAddIRQ(pDip);
1178 if (rc == DDI_SUCCESS)
1179 {
1180 /*
1181 * Call the common device extension initializer.
1182 */
1183#if ARCH_BITS == 64
1184# define VBOXGUEST_OS_TYPE VBOXOSTYPE_Solaris_x64
1185#else
1186# define VBOXGUEST_OS_TYPE VBOXOSTYPE_Solaris
1187#endif
1188 rc = VBoxGuestInitDevExt(&g_DevExt,
1189 g_uIOPortBase,
1190 g_pMMIOBase, g_cbMMIO,
1191 VBOXGUEST_OS_TYPE,
1192 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
1193#undef VBOXGUEST_OS_TYPE
1194 if (RT_SUCCESS(rc))
1195 {
1196 rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, 0);
1197 if (rc == DDI_SUCCESS)
1198 {
1199 g_pDip = pDip;
1200 pci_config_teardown(&PciHandle);
1201 return DDI_SUCCESS;
1202 }
1203
1204 LogRel((DEVICE_NAME "::Attach: ddi_create_minor_node failed.\n"));
1205 VBoxGuestDeleteDevExt(&g_DevExt);
1206 }
1207 else
1208 LogRel((DEVICE_NAME "::Attach: VBoxGuestInitDevExt failed.\n"));
1209 vbgr0SolRemoveIRQ(pDip);
1210 }
1211 else
1212 LogRel((DEVICE_NAME "::Attach: vbgr0SolAddIRQ failed.\n"));
1213 ddi_regs_map_free(&g_PciMMIOHandle);
1214 }
1215 else
1216 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for MMIO region failed.\n"));
1217 }
1218 else
1219 LogRel((DEVICE_NAME "::Attach: ddi_dev_regsize for MMIO region failed.\n"));
1220 ddi_regs_map_free(&g_PciIOHandle);
1221 }
1222 else
1223 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for IOport failed.\n"));
1224 pci_config_teardown(&PciHandle);
1225 }
1226 else
1227 LogRel((DEVICE_NAME "::Attach: pci_config_setup failed rc=%d.\n", rc));
1228 return DDI_FAILURE;
1229 }
1230
1231 case DDI_RESUME:
1232 {
1233 /** @todo implement resume for guest driver. */
1234 return DDI_SUCCESS;
1235 }
1236
1237 default:
1238 return DDI_FAILURE;
1239 }
1240}
1241
1242
1243/**
1244 * Detach entry point, to detach a device to the system or suspend it.
1245 *
1246 * @param pDip The module structure instance.
1247 * @param enmCmd Attach type (ddi_attach_cmd_t)
1248 *
1249 * @return corresponding solaris error code.
1250 */
1251int vbgr0SolDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
1252{
1253 LogFlow((DEVICE_NAME "::Detach\n"));
1254 switch (enmCmd)
1255 {
1256 case DDI_DETACH:
1257 {
1258 vbgr0SolRemoveIRQ(pDip);
1259 ddi_regs_map_free(&g_PciIOHandle);
1260 ddi_regs_map_free(&g_PciMMIOHandle);
1261 ddi_remove_minor_node(pDip, NULL);
1262 VBoxGuestDeleteDevExt(&g_DevExt);
1263 g_pDip = NULL;
1264 return DDI_SUCCESS;
1265 }
1266
1267 case DDI_SUSPEND:
1268 {
1269 /** @todo implement suspend for guest driver. */
1270 return DDI_SUCCESS;
1271 }
1272
1273 default:
1274 return DDI_FAILURE;
1275 }
1276}
1277
1278
1279/* Interrupt service routine installed by vbgr0SolAddIRQ. */
1280static uint_t vbgr0SolISR(char *Arg /* Actually caddr_t. */);
1281
1282/**
1283 * Sets IRQ for VMMDev.
1284 *
1285 * @returns Solaris error code.
1286 * @param pDip Pointer to the device info structure.
1287 */
1288static int vbgr0SolAddIRQ(dev_info_t *pDip)
1289{
1290 int IntrType = 0, rc;
1291
1292 LogFlow((DEVICE_NAME "::AddIRQ: pDip=%p\n", pDip));
1293 rc = ddi_intr_get_supported_types(pDip, &IntrType);
1294 if (rc == DDI_SUCCESS)
1295 {
1296 /* We won't need to bother about MSIs. */
1297 if (IntrType & DDI_INTR_TYPE_FIXED)
1298 {
1299 int IntrCount = 0;
1300 rc = ddi_intr_get_nintrs(pDip, IntrType, &IntrCount);
1301 if ( rc == DDI_SUCCESS
1302 && IntrCount > 0)
1303 {
1304 int IntrAvail = 0;
1305 rc = ddi_intr_get_navail(pDip, IntrType, &IntrAvail);
1306 if ( rc == DDI_SUCCESS
1307 && IntrAvail > 0)
1308 {
1309 /* Allocated kernel memory for the interrupt handles. The allocation size is stored internally. */
1310 g_pIntr = RTMemAlloc(IntrCount * sizeof(ddi_intr_handle_t));
1311 if (g_pIntr)
1312 {
1313 size_t IntrAllocated;
1314 unsigned i;
1315 rc = ddi_intr_alloc(pDip, g_pIntr, IntrType, 0, IntrCount, &IntrAllocated, DDI_INTR_ALLOC_NORMAL);
1316 if ( rc == DDI_SUCCESS
1317 && IntrAllocated > 0)
1318 {
1319 uint_t uIntrPriority;
1320 g_cIntrAllocated = IntrAllocated;
1321 rc = ddi_intr_get_pri(g_pIntr[0], &uIntrPriority);
1322 if (rc == DDI_SUCCESS)
1323 {
1324 /* Initialize the mutex. */
1325 mutex_init(&g_IrqMutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
1326
1327 /* Assign interrupt handler functions and enable interrupts. */
1328 for (i = 0; i < IntrAllocated; i++)
1329 {
1330 rc = ddi_intr_add_handler(g_pIntr[i], (ddi_intr_handler_t *)vbgr0SolISR,
1331 NULL /* No Private Data */, NULL);
1332 if (rc == DDI_SUCCESS)
1333 rc = ddi_intr_enable(g_pIntr[i]);
1334 if (rc != DDI_SUCCESS)
1335 {
1336 /* Changing local IntrAllocated to hold so-far allocated handles for freeing. */
1337 IntrAllocated = i;
1338 break;
1339 }
1340 }
1341 if (rc == DDI_SUCCESS)
1342 return rc;
1343
1344 /* Remove any assigned handlers */
1345 LogRel((DEVICE_NAME ":failed to assign IRQs allocated=%d\n", IntrAllocated));
1346 for (i = 0; i < IntrAllocated; i++)
1347 ddi_intr_remove_handler(g_pIntr[i]);
1348 }
1349 else
1350 LogRel((DEVICE_NAME "::AddIRQ: failed to get priority of interrupt. rc=%d\n", rc));
1351
1352 /* Remove allocated IRQs, too bad we can free only one handle at a time. */
1353 for (i = 0; i < g_cIntrAllocated; i++)
1354 ddi_intr_free(g_pIntr[i]);
1355 }
1356 else
1357 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
1358 RTMemFree(g_pIntr);
1359 }
1360 else
1361 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
1362 }
1363 else
1364 LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient available IRQs. rc=%d IntrAvail=%d\n", rc, IntrAvail));
1365 }
1366 else
1367 LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient number of IRQs. rc=%d IntrCount=%d\n", rc, IntrCount));
1368 }
1369 else
1370 LogRel((DEVICE_NAME "::AddIRQ: invalid irq type. IntrType=%#x\n", IntrType));
1371 }
1372 else
1373 LogRel((DEVICE_NAME "::AddIRQ: failed to get supported interrupt types\n"));
1374 return rc;
1375}
1376
1377
1378/**
1379 * Removes IRQ for VMMDev.
1380 *
1381 * @param pDip Pointer to the device info structure.
1382 */
1383static void vbgr0SolRemoveIRQ(dev_info_t *pDip)
1384{
1385 unsigned i;
1386
1387 LogFlow((DEVICE_NAME "::RemoveIRQ:\n"));
1388 for (i = 0; i < g_cIntrAllocated; i++)
1389 {
1390 int rc = ddi_intr_disable(g_pIntr[i]);
1391 if (rc == DDI_SUCCESS)
1392 {
1393 rc = ddi_intr_remove_handler(g_pIntr[i]);
1394 if (rc == DDI_SUCCESS)
1395 ddi_intr_free(g_pIntr[i]);
1396 }
1397 }
1398 RTMemFree(g_pIntr);
1399 mutex_destroy(&g_IrqMutex);
1400}
1401
1402
1403/**
1404 * Interrupt Service Routine for VMMDev.
1405 *
1406 * @param Arg Private data (unused, will be NULL).
1407 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
1408 */
1409static uint_t vbgr0SolISR(char *Arg /* Actually caddr_t. */)
1410{
1411 bool fOurIRQ;
1412
1413 LogFlow((DEVICE_NAME "::ISR:\n"));
1414 mutex_enter(&g_IrqMutex);
1415 fOurIRQ = VBoxGuestCommonISR(&g_DevExt);
1416 mutex_exit(&g_IrqMutex);
1417 return fOurIRQ ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED;
1418}
1419
1420
1421/* Helper for VBoxGuestNativeISRMousePollEvent. */
1422static void vbgr0SolVUIDPutAbsEvent(PVBGR0STATE pState, ushort_t cEvent,
1423 int cValue);
1424
1425/**
1426 * Native part of the IRQ service routine, called when the VBoxGuest mouse
1427 * pointer is moved. We send a VUID event up to user space.
1428 */
1429void VBoxGuestNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1430{
1431 VMMDevReqMouseStatus *pReq;
1432 int rc;
1433 LogFlow((DEVICE_NAME "::NativeISRMousePollEvent:\n"));
1434
1435 rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq),
1436 VMMDevReq_GetMouseStatus);
1437 if (RT_FAILURE(rc))
1438 return; /* If kernel memory is short a missed event is acceptable! */
1439 pReq->mouseFeatures = 0;
1440 pReq->pointerXPos = 0;
1441 pReq->pointerYPos = 0;
1442 rc = VbglGRPerform(&pReq->header);
1443 if (RT_SUCCESS(rc))
1444 {
1445 unsigned i;
1446
1447 mutex_enter(&g_StateMutex);
1448 for (i = 1; i < MAX_OPEN_NODES; ++i)
1449 {
1450 int cMaxScreenX = g_aOpenNodeStates[i].cMaxScreenX;
1451 int cMaxScreenY = g_aOpenNodeStates[i].cMaxScreenY;
1452
1453 if (!cMaxScreenX || !cMaxScreenY)
1454 continue;
1455 vbgr0SolVUIDPutAbsEvent(&g_aOpenNodeStates[i], LOC_X_ABSOLUTE,
1456 pReq->pointerXPos * cMaxScreenX
1457 / VMMDEV_MOUSE_RANGE_MAX);
1458 vbgr0SolVUIDPutAbsEvent(&g_aOpenNodeStates[i], LOC_Y_ABSOLUTE,
1459 pReq->pointerYPos * cMaxScreenY
1460 / VMMDEV_MOUSE_RANGE_MAX);
1461 }
1462 mutex_exit(&g_StateMutex);
1463 }
1464 VbglGRFree(&pReq->header);
1465}
1466
1467
1468void vbgr0SolVUIDPutAbsEvent(PVBGR0STATE pState, ushort_t cEvent,
1469 int cValue)
1470{
1471 queue_t *pReadQueue = RD(pState->pWriteQueue);
1472 mblk_t *pMBlk = allocb(sizeof(Firm_event, BPRI_HI));
1473 Firm_event *pEvent;
1474 AssertReturnVoid(cEvent == LOC_X_ABSOLUTE || cEvent == LOC_Y_ABSOLUTE);
1475 if (!pMBlk)
1476 return; /* If kernel memory is short a missed event is acceptable! */
1477 pEvent = (Firm_event *)pMBlk->b_wptr;
1478 pEvent->id = cEvent;
1479 pEvent->pair_type = FE_PAIR_DELTA;
1480 pEvent->pair = cEvent == LOC_X_ABSOLUTE ? LOC_X_DELTA : LOC_Y_DELTA;
1481 pEvent->value = cValue;
1482 uniqtime32(&pEvent->time);
1483 pMBlk->b_wptr += sizeof(Firm_event);
1484 /* Put the message on the queue immediately if it is not blocked. */
1485 if (canput(pReadQueue->q_next))
1486 putnext(pReadQueue, pMBlk);
1487 else
1488 putbq(pReadQueue, pMBlk);
1489}
1490
1491
1492/* Common code that depends on g_DevExt. */
1493#ifndef TESTCASE
1494# include "VBoxGuestIDC-unix.c.h"
1495#endif
1496
1497#ifdef TESTCASE
1498int main(void)
1499{
1500 RTTEST hTest;
1501 int rc = RTTestInitAndCreate("tstVBoxGuest-solaris", &hTest);
1502 if (rc)
1503 return rc;
1504 RTTestBanner(hTest);
1505 test_init(hTest);
1506 testOpenClose(hTest);
1507 testWPut(hTest);
1508
1509 /*
1510 * Summary.
1511 */
1512 return RTTestSummaryAndDestroy(hTest);
1513}
1514#endif
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