VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/VBoxGuest-linux.c@ 68643

Last change on this file since 68643 was 68589, checked in by vboxsync, 7 years ago

VBoxGuest-linux.c: build fix in vgdrvLinuxSetMouseStatus.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.9 KB
Line 
1/* $Rev: 68589 $ */
2/** @file
3 * VBoxGuest - Linux specifics.
4 *
5 * Note. Unfortunately, the difference between this and SUPDrv-linux.c is
6 * a little bit too big to be helpful.
7 */
8
9/*
10 * Copyright (C) 2006-2016 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21
22/*********************************************************************************************************************************
23* Header Files *
24*********************************************************************************************************************************/
25#define LOG_GROUP LOG_GROUP_SUP_DRV
26
27#include "the-linux-kernel.h"
28
29#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 15)
30# define VBOXGUEST_WITH_INPUT_DRIVER
31#endif
32
33#include "VBoxGuestInternal.h"
34#ifdef VBOXGUEST_WITH_INPUT_DRIVER
35# include <linux/input.h>
36#endif
37#include <linux/miscdevice.h>
38#include <linux/poll.h>
39#include <VBox/version.h>
40#include "revision-generated.h"
41
42#include <iprt/assert.h>
43#include <iprt/asm.h>
44#include <iprt/err.h>
45#include <iprt/initterm.h>
46#include <iprt/mem.h>
47#include <iprt/mp.h>
48#include <iprt/process.h>
49#include <iprt/spinlock.h>
50#include <iprt/semaphore.h>
51#include <iprt/string.h>
52#include <VBox/log.h>
53
54
55/*********************************************************************************************************************************
56* Defined Constants And Macros *
57*********************************************************************************************************************************/
58/** The device name. */
59#define DEVICE_NAME "vboxguest"
60/** The device name for the device node open to everyone. */
61#define DEVICE_NAME_USER "vboxuser"
62/** The name of the PCI driver */
63#define DRIVER_NAME DEVICE_NAME
64
65
66/* 2.4.x compatibility macros that may or may not be defined. */
67#ifndef IRQ_RETVAL
68# define irqreturn_t void
69# define IRQ_RETVAL(n)
70#endif
71
72
73/*********************************************************************************************************************************
74* Internal Functions *
75*********************************************************************************************************************************/
76static void vgdrvLinuxTermPci(struct pci_dev *pPciDev);
77static int vgdrvLinuxProbePci(struct pci_dev *pPciDev, const struct pci_device_id *id);
78static int vgdrvLinuxModInit(void);
79static void vgdrvLinuxModExit(void);
80static int vgdrvLinuxOpen(struct inode *pInode, struct file *pFilp);
81static int vgdrvLinuxRelease(struct inode *pInode, struct file *pFilp);
82#ifdef HAVE_UNLOCKED_IOCTL
83static long vgdrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
84#else
85static int vgdrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
86#endif
87static int vgdrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PVBOXGUESTSESSION pSession);
88static int vgdrvLinuxFAsync(int fd, struct file *pFile, int fOn);
89static unsigned int vgdrvLinuxPoll(struct file *pFile, poll_table *pPt);
90static ssize_t vgdrvLinuxRead(struct file *pFile, char *pbBuf, size_t cbRead, loff_t *poff);
91
92
93/*********************************************************************************************************************************
94* Global Variables *
95*********************************************************************************************************************************/
96/**
97 * Device extention & session data association structure.
98 */
99static VBOXGUESTDEVEXT g_DevExt;
100/** The PCI device. */
101static struct pci_dev *g_pPciDev = NULL;
102/** The base of the I/O port range. */
103static RTIOPORT g_IOPortBase;
104/** The base of the MMIO range. */
105static RTHCPHYS g_MMIOPhysAddr = NIL_RTHCPHYS;
106/** The size of the MMIO range as seen by PCI. */
107static uint32_t g_cbMMIO;
108/** The pointer to the mapping of the MMIO range. */
109static void *g_pvMMIOBase;
110/** Wait queue used by polling. */
111static wait_queue_head_t g_PollEventQueue;
112/** Asynchronous notification stuff. */
113static struct fasync_struct *g_pFAsyncQueue;
114#ifdef VBOXGUEST_WITH_INPUT_DRIVER
115/** Pre-allocated mouse status VMMDev request for use in the IRQ
116 * handler. */
117static VMMDevReqMouseStatus *g_pMouseStatusReq;
118#endif
119#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
120/** Whether we've create the logger or not. */
121static volatile bool g_fLoggerCreated;
122/** Release logger group settings. */
123static char g_szLogGrp[128];
124/** Release logger flags settings. */
125static char g_szLogFlags[128];
126/** Release logger destination settings. */
127static char g_szLogDst[128];
128# if 0
129/** Debug logger group settings. */
130static char g_szDbgLogGrp[128];
131/** Debug logger flags settings. */
132static char g_szDbgLogFlags[128];
133/** Debug logger destination settings. */
134static char g_szDbgLogDst[128];
135# endif
136#endif
137
138/** The input device handle */
139#ifdef VBOXGUEST_WITH_INPUT_DRIVER
140static struct input_dev *g_pInputDevice = NULL;
141#endif
142
143/** The file_operations structure. */
144static struct file_operations g_FileOps =
145{
146 owner: THIS_MODULE,
147 open: vgdrvLinuxOpen,
148 release: vgdrvLinuxRelease,
149#ifdef HAVE_UNLOCKED_IOCTL
150 unlocked_ioctl: vgdrvLinuxIOCtl,
151#else
152 ioctl: vgdrvLinuxIOCtl,
153#endif
154 fasync: vgdrvLinuxFAsync,
155 read: vgdrvLinuxRead,
156 poll: vgdrvLinuxPoll,
157 llseek: no_llseek,
158};
159
160/** The miscdevice structure. */
161static struct miscdevice g_MiscDevice =
162{
163 minor: MISC_DYNAMIC_MINOR,
164 name: DEVICE_NAME,
165 fops: &g_FileOps,
166};
167
168/** The file_operations structure for the user device.
169 * @remarks For the time being we'll be using the same implementation as
170 * /dev/vboxguest here. */
171static struct file_operations g_FileOpsUser =
172{
173 owner: THIS_MODULE,
174 open: vgdrvLinuxOpen,
175 release: vgdrvLinuxRelease,
176#ifdef HAVE_UNLOCKED_IOCTL
177 unlocked_ioctl: vgdrvLinuxIOCtl,
178#else
179 ioctl: vgdrvLinuxIOCtl,
180#endif
181};
182
183/** The miscdevice structure for the user device. */
184static struct miscdevice g_MiscDeviceUser =
185{
186 minor: MISC_DYNAMIC_MINOR,
187 name: DEVICE_NAME_USER,
188 fops: &g_FileOpsUser,
189};
190
191
192/** PCI hotplug structure. */
193static const struct pci_device_id
194#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
195__devinitdata
196#endif
197g_VBoxGuestPciId[] =
198{
199 {
200 vendor: VMMDEV_VENDORID,
201 device: VMMDEV_DEVICEID
202 },
203 {
204 /* empty entry */
205 }
206};
207
208MODULE_DEVICE_TABLE(pci, g_VBoxGuestPciId);
209
210/** Structure for registering the PCI driver. */
211static struct pci_driver g_PciDriver =
212{
213 name: DRIVER_NAME,
214 id_table: g_VBoxGuestPciId,
215 probe: vgdrvLinuxProbePci,
216 remove: vgdrvLinuxTermPci
217};
218
219static PVBOXGUESTSESSION g_pKernelSession = NULL;
220
221
222
223/**
224 * Converts a VBox status code to a linux error code.
225 *
226 * @returns corresponding negative linux error code.
227 * @param rc supdrv error code (SUPDRV_ERR_* defines).
228 */
229static int vgdrvLinuxConvertToNegErrno(int rc)
230{
231 if ( rc > -1000
232 && rc < 1000)
233 return -RTErrConvertToErrno(rc);
234 switch (rc)
235 {
236 case VERR_HGCM_SERVICE_NOT_FOUND: return -ESRCH;
237 case VINF_HGCM_CLIENT_REJECTED: return 0;
238 case VERR_HGCM_INVALID_CMD_ADDRESS: return -EFAULT;
239 case VINF_HGCM_ASYNC_EXECUTE: return 0;
240 case VERR_HGCM_INTERNAL: return -EPROTO;
241 case VERR_HGCM_INVALID_CLIENT_ID: return -EINVAL;
242 case VINF_HGCM_SAVE_STATE: return 0;
243 /* No reason to return this to a guest */
244 // case VERR_HGCM_SERVICE_EXISTS: return -EEXIST;
245 default:
246 AssertMsgFailed(("Unhandled error code %Rrc\n", rc));
247 return -EPROTO;
248 }
249}
250
251
252/**
253 * Does the PCI detection and init of the device.
254 *
255 * @returns 0 on success, negated errno on failure.
256 */
257static int vgdrvLinuxProbePci(struct pci_dev *pPciDev, const struct pci_device_id *id)
258{
259 int rc;
260
261 NOREF(id);
262 AssertReturn(!g_pPciDev, -EINVAL);
263 rc = pci_enable_device(pPciDev);
264 if (rc >= 0)
265 {
266 /* I/O Ports are mandatory, the MMIO bit is not. */
267 g_IOPortBase = pci_resource_start(pPciDev, 0);
268 if (g_IOPortBase != 0)
269 {
270 /*
271 * Map the register address space.
272 */
273 g_MMIOPhysAddr = pci_resource_start(pPciDev, 1);
274 g_cbMMIO = pci_resource_len(pPciDev, 1);
275 if (request_mem_region(g_MMIOPhysAddr, g_cbMMIO, DEVICE_NAME) != NULL)
276 {
277 g_pvMMIOBase = ioremap(g_MMIOPhysAddr, g_cbMMIO);
278 if (g_pvMMIOBase)
279 {
280 /** @todo why aren't we requesting ownership of the I/O ports as well? */
281 g_pPciDev = pPciDev;
282 return 0;
283 }
284
285 /* failure cleanup path */
286 LogRel((DEVICE_NAME ": ioremap failed; MMIO Addr=%RHp cb=%#x\n", g_MMIOPhysAddr, g_cbMMIO));
287 rc = -ENOMEM;
288 release_mem_region(g_MMIOPhysAddr, g_cbMMIO);
289 }
290 else
291 {
292 LogRel((DEVICE_NAME ": failed to obtain adapter memory\n"));
293 rc = -EBUSY;
294 }
295 g_MMIOPhysAddr = NIL_RTHCPHYS;
296 g_cbMMIO = 0;
297 g_IOPortBase = 0;
298 }
299 else
300 {
301 LogRel((DEVICE_NAME ": did not find expected hardware resources\n"));
302 rc = -ENXIO;
303 }
304 pci_disable_device(pPciDev);
305 }
306 else
307 LogRel((DEVICE_NAME ": could not enable device: %d\n", rc));
308 return rc;
309}
310
311
312/**
313 * Clean up the usage of the PCI device.
314 */
315static void vgdrvLinuxTermPci(struct pci_dev *pPciDev)
316{
317 g_pPciDev = NULL;
318 if (pPciDev)
319 {
320 iounmap(g_pvMMIOBase);
321 g_pvMMIOBase = NULL;
322
323 release_mem_region(g_MMIOPhysAddr, g_cbMMIO);
324 g_MMIOPhysAddr = NIL_RTHCPHYS;
325 g_cbMMIO = 0;
326
327 pci_disable_device(pPciDev);
328 }
329}
330
331
332/**
333 * Interrupt service routine.
334 *
335 * @returns In 2.4 it returns void.
336 * In 2.6 we indicate whether we've handled the IRQ or not.
337 *
338 * @param iIrq The IRQ number.
339 * @param pvDevId The device ID, a pointer to g_DevExt.
340 * @param pRegs Register set. Removed in 2.6.19.
341 */
342#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) && !defined(DOXYGEN_RUNNING)
343static irqreturn_t vgdrvLinuxISR(int iIrq, void *pvDevId)
344#else
345static irqreturn_t vgdrvLinuxISR(int iIrq, void *pvDevId, struct pt_regs *pRegs)
346#endif
347{
348 bool fTaken = VGDrvCommonISR(&g_DevExt);
349 return IRQ_RETVAL(fTaken);
350}
351
352
353/**
354 * Registers the ISR and initializes the poll wait queue.
355 */
356static int __init vgdrvLinuxInitISR(void)
357{
358 int rc;
359
360 init_waitqueue_head(&g_PollEventQueue);
361 rc = request_irq(g_pPciDev->irq,
362 vgdrvLinuxISR,
363#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
364 IRQF_SHARED,
365#else
366 SA_SHIRQ,
367#endif
368 DEVICE_NAME,
369 &g_DevExt);
370 if (rc)
371 {
372 LogRel((DEVICE_NAME ": could not request IRQ %d: err=%d\n", g_pPciDev->irq, rc));
373 return rc;
374 }
375 return 0;
376}
377
378
379/**
380 * Deregisters the ISR.
381 */
382static void vgdrvLinuxTermISR(void)
383{
384 free_irq(g_pPciDev->irq, &g_DevExt);
385}
386
387
388#ifdef VBOXGUEST_WITH_INPUT_DRIVER
389
390/**
391 * Reports the mouse integration status to the host.
392 *
393 * Calls the kernel IOCtl to report mouse status to the host on behalf of
394 * our kernel session.
395 *
396 * @param fStatus The mouse status to report.
397 */
398static int vgdrvLinuxSetMouseStatus(uint32_t fStatus)
399{
400 int rc;
401 VBGLIOCSETMOUSESTATUS Req;
402 VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS);
403 Req.u.In.fStatus = fStatus;
404 rc = VGDrvCommonIoCtl(VBGL_IOCTL_SET_MOUSE_STATUS, &g_DevExt, g_pKernelSession, &Req.Hdr, sizeof(Req));
405 if (RT_SUCCESS(rc))
406 rc = Req.Hdr.rc;
407 return rc;
408}
409
410
411/**
412 * Called when the input device is first opened.
413 *
414 * Sets up absolute mouse reporting.
415 */
416static int vboxguestOpenInputDevice(struct input_dev *pDev)
417{
418 int rc = vgdrvLinuxSetMouseStatus(VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE | VMMDEV_MOUSE_NEW_PROTOCOL);
419 if (RT_FAILURE(rc))
420 return ENODEV;
421 NOREF(pDev);
422 return 0;
423}
424
425
426/**
427 * Called if all open handles to the input device are closed.
428 *
429 * Disables absolute reporting.
430 */
431static void vboxguestCloseInputDevice(struct input_dev *pDev)
432{
433 NOREF(pDev);
434 vgdrvLinuxSetMouseStatus(0);
435}
436
437
438/**
439 * Creates the kernel input device.
440 */
441static int __init vgdrvLinuxCreateInputDevice(void)
442{
443 int rc = VbglGRAlloc((VMMDevRequestHeader **)&g_pMouseStatusReq, sizeof(*g_pMouseStatusReq), VMMDevReq_GetMouseStatus);
444 if (RT_SUCCESS(rc))
445 {
446 g_pInputDevice = input_allocate_device();
447 if (g_pInputDevice)
448 {
449 g_pInputDevice->id.bustype = BUS_PCI;
450 g_pInputDevice->id.vendor = VMMDEV_VENDORID;
451 g_pInputDevice->id.product = VMMDEV_DEVICEID;
452 g_pInputDevice->id.version = VBOX_SHORT_VERSION;
453 g_pInputDevice->open = vboxguestOpenInputDevice;
454 g_pInputDevice->close = vboxguestCloseInputDevice;
455# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
456 g_pInputDevice->cdev.dev = &g_pPciDev->dev;
457# else
458 g_pInputDevice->dev.parent = &g_pPciDev->dev;
459# endif
460 rc = input_register_device(g_pInputDevice);
461 if (rc == 0)
462 {
463 /* Do what one of our competitors apparently does as that works. */
464 ASMBitSet(g_pInputDevice->evbit, EV_ABS);
465 ASMBitSet(g_pInputDevice->evbit, EV_KEY);
466# ifdef EV_SYN
467 ASMBitSet(g_pInputDevice->evbit, EV_SYN);
468# endif
469 input_set_abs_params(g_pInputDevice, ABS_X, VMMDEV_MOUSE_RANGE_MIN, VMMDEV_MOUSE_RANGE_MAX, 0, 0);
470 input_set_abs_params(g_pInputDevice, ABS_Y, VMMDEV_MOUSE_RANGE_MIN, VMMDEV_MOUSE_RANGE_MAX, 0, 0);
471 ASMBitSet(g_pInputDevice->keybit, BTN_MOUSE);
472 /** @todo this string should be in a header file somewhere. */
473 g_pInputDevice->name = "VirtualBox mouse integration";
474 return 0;
475 }
476
477 input_free_device(g_pInputDevice);
478 }
479 else
480 rc = -ENOMEM;
481 VbglGRFree(&g_pMouseStatusReq->header);
482 g_pMouseStatusReq = NULL;
483 }
484 else
485 rc = -ENOMEM;
486 return rc;
487}
488
489
490/**
491 * Terminates the kernel input device.
492 */
493static void vgdrvLinuxTermInputDevice(void)
494{
495 VbglGRFree(&g_pMouseStatusReq->header);
496 g_pMouseStatusReq = NULL;
497
498 /* See documentation of input_register_device(): input_free_device()
499 * should not be called after a device has been registered. */
500 input_unregister_device(g_pInputDevice);
501}
502
503#endif /* VBOXGUEST_WITH_INPUT_DRIVER */
504
505
506/**
507 * Creates the device nodes.
508 *
509 * @returns 0 on success, negated errno on failure.
510 */
511static int __init vgdrvLinuxInitDeviceNodes(void)
512{
513 /*
514 * The full feature device node.
515 */
516 int rc = misc_register(&g_MiscDevice);
517 if (!rc)
518 {
519 /*
520 * The device node intended to be accessible by all users.
521 */
522 rc = misc_register(&g_MiscDeviceUser);
523 if (!rc)
524 return 0;
525 LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME_USER, rc));
526 misc_deregister(&g_MiscDevice);
527 }
528 else
529 LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME, rc));
530 return rc;
531}
532
533
534/**
535 * Deregisters the device nodes.
536 */
537static void vgdrvLinuxTermDeviceNodes(void)
538{
539 misc_deregister(&g_MiscDevice);
540 misc_deregister(&g_MiscDeviceUser);
541}
542
543
544/**
545 * Initialize module.
546 *
547 * @returns appropriate status code.
548 */
549static int __init vgdrvLinuxModInit(void)
550{
551 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
552 PRTLOGGER pRelLogger;
553 int rc;
554
555 /*
556 * Initialize IPRT first.
557 */
558 rc = RTR0Init(0);
559 if (RT_FAILURE(rc))
560 {
561 printk(KERN_ERR DEVICE_NAME ": RTR0Init failed, rc=%d.\n", rc);
562 return -EINVAL;
563 }
564
565 /*
566 * Create the release log.
567 * (We do that here instead of common code because we want to log
568 * early failures using the LogRel macro.)
569 */
570 rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all",
571 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
572 RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER | RTLOGDEST_USER, NULL);
573 if (RT_SUCCESS(rc))
574 {
575#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
576 RTLogGroupSettings(pRelLogger, g_szLogGrp);
577 RTLogFlags(pRelLogger, g_szLogFlags);
578 RTLogDestinations(pRelLogger, g_szLogDst);
579#endif
580 RTLogRelSetDefaultInstance(pRelLogger);
581 }
582#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
583 g_fLoggerCreated = true;
584#endif
585
586 /*
587 * Locate and initialize the PCI device.
588 */
589 rc = pci_register_driver(&g_PciDriver);
590 if (rc >= 0 && g_pPciDev)
591 {
592 /*
593 * Register the interrupt service routine for it.
594 */
595 rc = vgdrvLinuxInitISR();
596 if (rc >= 0)
597 {
598 /*
599 * Call the common device extension initializer.
600 */
601#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && defined(RT_ARCH_X86)
602 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26;
603#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && defined(RT_ARCH_AMD64)
604 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26_x64;
605#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) && defined(RT_ARCH_X86)
606 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24;
607#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) && defined(RT_ARCH_AMD64)
608 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24_x64;
609#else
610# warning "huh? which arch + version is this?"
611 VBOXOSTYPE enmOsType = VBOXOSTYPE_Linux;
612#endif
613 rc = VGDrvCommonInitDevExt(&g_DevExt,
614 g_IOPortBase,
615 g_pvMMIOBase,
616 g_cbMMIO,
617 enmOSType,
618 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
619 if (RT_SUCCESS(rc))
620 {
621 /*
622 * Create the kernel session for this driver.
623 */
624 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &g_pKernelSession);
625 if (RT_SUCCESS(rc))
626 {
627 /*
628 * Create the kernel input device.
629 */
630#ifdef VBOXGUEST_WITH_INPUT_DRIVER
631 rc = vgdrvLinuxCreateInputDevice();
632 if (rc >= 0)
633 {
634#endif
635 /*
636 * Finally, create the device nodes.
637 */
638 rc = vgdrvLinuxInitDeviceNodes();
639 if (rc >= 0)
640 {
641 /* some useful information for the user but don't show this on the console */
642 LogRel((DEVICE_NAME ": misc device minor %d, IRQ %d, I/O port %RTiop, MMIO at %RHp (size 0x%x)\n",
643 g_MiscDevice.minor, g_pPciDev->irq, g_IOPortBase, g_MMIOPhysAddr, g_cbMMIO));
644 printk(KERN_DEBUG DEVICE_NAME ": Successfully loaded version "
645 VBOX_VERSION_STRING " (interface " RT_XSTR(VMMDEV_VERSION) ")\n");
646 return rc;
647 }
648
649 /* bail out */
650#ifdef VBOXGUEST_WITH_INPUT_DRIVER
651 vgdrvLinuxTermInputDevice();
652 }
653 else
654 {
655 LogRel((DEVICE_NAME ": vboxguestCreateInputDevice failed with rc=%Rrc\n", rc));
656 rc = RTErrConvertFromErrno(rc);
657 }
658#endif
659 VGDrvCommonCloseSession(&g_DevExt, g_pKernelSession);
660 }
661 VGDrvCommonDeleteDevExt(&g_DevExt);
662 }
663 else
664 {
665 LogRel((DEVICE_NAME ": VGDrvCommonInitDevExt failed with rc=%Rrc\n", rc));
666 rc = RTErrConvertFromErrno(rc);
667 }
668 vgdrvLinuxTermISR();
669 }
670 }
671 else
672 {
673 LogRel((DEVICE_NAME ": PCI device not found, probably running on physical hardware.\n"));
674 rc = -ENODEV;
675 }
676 pci_unregister_driver(&g_PciDriver);
677 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
678 RTLogDestroy(RTLogSetDefaultInstance(NULL));
679 RTR0Term();
680 return rc;
681}
682
683
684/**
685 * Unload the module.
686 */
687static void __exit vgdrvLinuxModExit(void)
688{
689 /*
690 * Inverse order of init.
691 */
692 vgdrvLinuxTermDeviceNodes();
693#ifdef VBOXGUEST_WITH_INPUT_DRIVER
694 vgdrvLinuxTermInputDevice();
695#endif
696 VGDrvCommonCloseSession(&g_DevExt, g_pKernelSession);
697 VGDrvCommonDeleteDevExt(&g_DevExt);
698 vgdrvLinuxTermISR();
699 pci_unregister_driver(&g_PciDriver);
700 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
701 RTLogDestroy(RTLogSetDefaultInstance(NULL));
702 RTR0Term();
703}
704
705
706/**
707 * Device open. Called on open /dev/vboxdrv
708 *
709 * @param pInode Pointer to inode info structure.
710 * @param pFilp Associated file pointer.
711 */
712static int vgdrvLinuxOpen(struct inode *pInode, struct file *pFilp)
713{
714 int rc;
715 PVBOXGUESTSESSION pSession;
716 Log((DEVICE_NAME ": pFilp=%p pid=%d/%d %s\n", pFilp, RTProcSelf(), current->pid, current->comm));
717
718 /*
719 * Call common code to create the user session. Associate it with
720 * the file so we can access it in the other methods.
721 */
722 rc = VGDrvCommonCreateUserSession(&g_DevExt, &pSession);
723 if (RT_SUCCESS(rc))
724 {
725 pFilp->private_data = pSession;
726 if (MINOR(pInode->i_rdev) == g_MiscDeviceUser.minor)
727 pSession->fUserSession = true;
728 }
729
730 Log(("vgdrvLinuxOpen: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n",
731 &g_DevExt, pSession, rc, vgdrvLinuxConvertToNegErrno(rc), RTProcSelf(), current->pid, current->comm));
732 return vgdrvLinuxConvertToNegErrno(rc);
733}
734
735
736/**
737 * Close device.
738 *
739 * @param pInode Pointer to inode info structure.
740 * @param pFilp Associated file pointer.
741 */
742static int vgdrvLinuxRelease(struct inode *pInode, struct file *pFilp)
743{
744 Log(("vgdrvLinuxRelease: pFilp=%p pSession=%p pid=%d/%d %s\n",
745 pFilp, pFilp->private_data, RTProcSelf(), current->pid, current->comm));
746
747#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
748 /* This housekeeping was needed in older kernel versions to ensure that
749 * the file pointer didn't get left on the polling queue. */
750 vgdrvLinuxFAsync(-1, pFilp, 0);
751#endif
752 VGDrvCommonCloseSession(&g_DevExt, (PVBOXGUESTSESSION)pFilp->private_data);
753 pFilp->private_data = NULL;
754 return 0;
755}
756
757
758/**
759 * Device I/O Control entry point.
760 *
761 * @param pFilp Associated file pointer.
762 * @param uCmd The function specified to ioctl().
763 * @param ulArg The argument specified to ioctl().
764 */
765#if defined(HAVE_UNLOCKED_IOCTL) || defined(DOXYGEN_RUNNING)
766static long vgdrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
767#else
768static int vgdrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
769#endif
770{
771 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFilp->private_data;
772 int rc;
773#ifndef HAVE_UNLOCKED_IOCTL
774 unlock_kernel();
775#endif
776
777#if 0 /* no fast I/O controls defined atm. */
778 if (RT_LIKELY( ( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN
779 || uCmd == SUP_IOCTL_FAST_DO_HM_RUN
780 || uCmd == SUP_IOCTL_FAST_DO_NOP)
781 && pSession->fUnrestricted == true))
782 rc = VGDrvCommonIoCtlFast(uCmd, ulArg, &g_DevExt, pSession);
783 else
784#endif
785 rc = vgdrvLinuxIOCtlSlow(pFilp, uCmd, ulArg, pSession);
786
787#ifndef HAVE_UNLOCKED_IOCTL
788 lock_kernel();
789#endif
790 return rc;
791}
792
793
794/**
795 * Device I/O Control entry point, slow variant.
796 *
797 * @param pFilp Associated file pointer.
798 * @param uCmd The function specified to ioctl().
799 * @param ulArg The argument specified to ioctl().
800 * @param pSession The session instance.
801 */
802static int vgdrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PVBOXGUESTSESSION pSession)
803{
804 int rc;
805 VBGLREQHDR Hdr;
806 PVBGLREQHDR pHdr;
807 uint32_t cbBuf;
808
809 Log6(("vgdrvLinuxIOCtlSlow: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid));
810
811 /*
812 * Read the header.
813 */
814 if (RT_FAILURE(RTR0MemUserCopyFrom(&Hdr, ulArg, sizeof(Hdr))))
815 {
816 Log(("vgdrvLinuxIOCtlSlow: copy_from_user(,%#lx,) failed; uCmd=%#x\n", ulArg, uCmd));
817 return -EFAULT;
818 }
819 if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION))
820 {
821 Log(("vgdrvLinuxIOCtlSlow: bad header version %#x; uCmd=%#x\n", Hdr.uVersion, uCmd));
822 return -EINVAL;
823 }
824
825 /*
826 * Buffer the request.
827 * Note! The header is revalidated by the common code.
828 */
829 cbBuf = RT_MAX(Hdr.cbIn, Hdr.cbOut);
830 if (RT_UNLIKELY(cbBuf > _1M*16))
831 {
832 Log(("vgdrvLinuxIOCtlSlow: too big cbBuf=%#x; uCmd=%#x\n", cbBuf, uCmd));
833 return -E2BIG;
834 }
835 if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr)
836 || (cbBuf != _IOC_SIZE(uCmd) && _IOC_SIZE(uCmd) != 0)))
837 {
838 Log(("vgdrvLinuxIOCtlSlow: bad ioctl cbBuf=%#x _IOC_SIZE=%#x; uCmd=%#x\n", cbBuf, _IOC_SIZE(uCmd), uCmd));
839 return -EINVAL;
840 }
841 pHdr = RTMemAlloc(cbBuf);
842 if (RT_UNLIKELY(!pHdr))
843 {
844 LogRel(("vgdrvLinuxIOCtlSlow: failed to allocate buffer of %d bytes for uCmd=%#x\n", cbBuf, uCmd));
845 return -ENOMEM;
846 }
847 if (RT_FAILURE(RTR0MemUserCopyFrom(pHdr, ulArg, Hdr.cbIn)))
848 {
849 Log(("vgdrvLinuxIOCtlSlow: copy_from_user(,%#lx, %#x) failed; uCmd=%#x\n", ulArg, Hdr.cbIn, uCmd));
850 RTMemFree(pHdr);
851 return -EFAULT;
852 }
853 if (Hdr.cbIn < cbBuf)
854 RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbBuf - Hdr.cbIn);
855
856 /*
857 * Process the IOCtl.
858 */
859 rc = VGDrvCommonIoCtl(uCmd, &g_DevExt, pSession, pHdr, cbBuf);
860
861 /*
862 * Copy ioctl data and output buffer back to user space.
863 */
864 if (RT_SUCCESS(rc))
865 {
866 uint32_t cbOut = pHdr->cbOut;
867 if (RT_UNLIKELY(cbOut > cbBuf))
868 {
869 LogRel(("vgdrvLinuxIOCtlSlow: too much output! %#x > %#x; uCmd=%#x!\n", cbOut, cbBuf, uCmd));
870 cbOut = cbBuf;
871 }
872 if (RT_FAILURE(RTR0MemUserCopyTo(ulArg, pHdr, cbOut)))
873 {
874 /* this is really bad! */
875 LogRel(("vgdrvLinuxIOCtlSlow: copy_to_user(%#lx,,%#x); uCmd=%#x!\n", ulArg, cbOut, uCmd));
876 rc = -EFAULT;
877 }
878 }
879 else
880 {
881 Log(("vgdrvLinuxIOCtlSlow: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc));
882 rc = -EINVAL;
883 }
884 RTMemFree(pHdr);
885
886 Log6(("vgdrvLinuxIOCtlSlow: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid));
887 return rc;
888}
889
890
891/**
892 * @note This code is duplicated on other platforms with variations, so please
893 * keep them all up to date when making changes!
894 */
895int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
896{
897 /*
898 * Simple request validation (common code does the rest).
899 */
900 int rc;
901 if ( RT_VALID_PTR(pReqHdr)
902 && cbReq >= sizeof(*pReqHdr))
903 {
904 /*
905 * All requests except the connect one requires a valid session.
906 */
907 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
908 if (pSession)
909 {
910 if ( RT_VALID_PTR(pSession)
911 && pSession->pDevExt == &g_DevExt)
912 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
913 else
914 rc = VERR_INVALID_HANDLE;
915 }
916 else if (uReq == VBGL_IOCTL_IDC_CONNECT)
917 {
918 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
919 if (RT_SUCCESS(rc))
920 {
921 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
922 if (RT_FAILURE(rc))
923 VGDrvCommonCloseSession(&g_DevExt, pSession);
924 }
925 }
926 else
927 rc = VERR_INVALID_HANDLE;
928 }
929 else
930 rc = VERR_INVALID_POINTER;
931 return rc;
932}
933EXPORT_SYMBOL(VBoxGuestIDC);
934
935
936/**
937 * Asynchronous notification activation method.
938 *
939 * @returns 0 on success, negative errno on failure.
940 *
941 * @param fd The file descriptor.
942 * @param pFile The file structure.
943 * @param fOn On/off indicator.
944 */
945static int vgdrvLinuxFAsync(int fd, struct file *pFile, int fOn)
946{
947 return fasync_helper(fd, pFile, fOn, &g_pFAsyncQueue);
948}
949
950
951/**
952 * Poll function.
953 *
954 * This returns ready to read if the mouse pointer mode or the pointer position
955 * has changed since last call to read.
956 *
957 * @returns 0 if no changes, POLLIN | POLLRDNORM if there are unseen changes.
958 *
959 * @param pFile The file structure.
960 * @param pPt The poll table.
961 *
962 * @remarks This is probably not really used, X11 is said to use the fasync
963 * interface instead.
964 */
965static unsigned int vgdrvLinuxPoll(struct file *pFile, poll_table *pPt)
966{
967 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFile->private_data;
968 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
969 unsigned int fMask = pSession->u32MousePosChangedSeq != u32CurSeq
970 ? POLLIN | POLLRDNORM
971 : 0;
972 poll_wait(pFile, &g_PollEventQueue, pPt);
973 return fMask;
974}
975
976
977/**
978 * Read to go with our poll/fasync response.
979 *
980 * @returns 1 or -EINVAL.
981 *
982 * @param pFile The file structure.
983 * @param pbBuf The buffer to read into.
984 * @param cbRead The max number of bytes to read.
985 * @param poff The current file position.
986 *
987 * @remarks This is probably not really used as X11 lets the driver do its own
988 * event reading. The poll condition is therefore also cleared when we
989 * see VMMDevReq_GetMouseStatus in vgdrvIoCtl_VMMRequest.
990 */
991static ssize_t vgdrvLinuxRead(struct file *pFile, char *pbBuf, size_t cbRead, loff_t *poff)
992{
993 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFile->private_data;
994 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
995
996 if (*poff != 0)
997 return -EINVAL;
998
999 /*
1000 * Fake a single byte read if we're not up to date with the current mouse position.
1001 */
1002 if ( pSession->u32MousePosChangedSeq != u32CurSeq
1003 && cbRead > 0)
1004 {
1005 pSession->u32MousePosChangedSeq = u32CurSeq;
1006 pbBuf[0] = 0;
1007 return 1;
1008 }
1009 return 0;
1010}
1011
1012
1013void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1014{
1015#ifdef VBOXGUEST_WITH_INPUT_DRIVER
1016 int rc;
1017#endif
1018 NOREF(pDevExt);
1019
1020 /*
1021 * Wake up everyone that's in a poll() and post anyone that has
1022 * subscribed to async notifications.
1023 */
1024 Log3(("VGDrvNativeISRMousePollEvent: wake_up_all\n"));
1025 wake_up_all(&g_PollEventQueue);
1026 Log3(("VGDrvNativeISRMousePollEvent: kill_fasync\n"));
1027 kill_fasync(&g_pFAsyncQueue, SIGIO, POLL_IN);
1028#ifdef VBOXGUEST_WITH_INPUT_DRIVER
1029 /* Report events to the kernel input device */
1030 g_pMouseStatusReq->mouseFeatures = 0;
1031 g_pMouseStatusReq->pointerXPos = 0;
1032 g_pMouseStatusReq->pointerYPos = 0;
1033 rc = VbglGRPerform(&g_pMouseStatusReq->header);
1034 if (RT_SUCCESS(rc))
1035 {
1036 input_report_abs(g_pInputDevice, ABS_X,
1037 g_pMouseStatusReq->pointerXPos);
1038 input_report_abs(g_pInputDevice, ABS_Y,
1039 g_pMouseStatusReq->pointerYPos);
1040# ifdef EV_SYN
1041 input_sync(g_pInputDevice);
1042# endif
1043 }
1044#endif
1045 Log3(("VGDrvNativeISRMousePollEvent: done\n"));
1046}
1047
1048
1049#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
1050
1051/** log and dbg_log parameter setter. */
1052static int vgdrvLinuxParamLogGrpSet(const char *pszValue, struct kernel_param *pParam)
1053{
1054 if (g_fLoggerCreated)
1055 {
1056 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1057 if (pLogger)
1058 RTLogGroupSettings(pLogger, pszValue);
1059 }
1060 else if (pParam->name[0] != 'd')
1061 strlcpy(&g_szLogGrp[0], pszValue, sizeof(g_szLogGrp));
1062
1063 return 0;
1064}
1065
1066/** log and dbg_log parameter getter. */
1067static int vgdrvLinuxParamLogGrpGet(char *pszBuf, struct kernel_param *pParam)
1068{
1069 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1070 *pszBuf = '\0';
1071 if (pLogger)
1072 RTLogGetGroupSettings(pLogger, pszBuf, _4K);
1073 return strlen(pszBuf);
1074}
1075
1076
1077/** log and dbg_log_flags parameter setter. */
1078static int vgdrvLinuxParamLogFlagsSet(const char *pszValue, struct kernel_param *pParam)
1079{
1080 if (g_fLoggerCreated)
1081 {
1082 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1083 if (pLogger)
1084 RTLogFlags(pLogger, pszValue);
1085 }
1086 else if (pParam->name[0] != 'd')
1087 strlcpy(&g_szLogFlags[0], pszValue, sizeof(g_szLogFlags));
1088 return 0;
1089}
1090
1091/** log and dbg_log_flags parameter getter. */
1092static int vgdrvLinuxParamLogFlagsGet(char *pszBuf, struct kernel_param *pParam)
1093{
1094 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1095 *pszBuf = '\0';
1096 if (pLogger)
1097 RTLogGetFlags(pLogger, pszBuf, _4K);
1098 return strlen(pszBuf);
1099}
1100
1101
1102/** log and dbg_log_dest parameter setter. */
1103static int vgdrvLinuxParamLogDstSet(const char *pszValue, struct kernel_param *pParam)
1104{
1105 if (g_fLoggerCreated)
1106 {
1107 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1108 if (pLogger)
1109 RTLogDestinations(pLogger, pszValue);
1110 }
1111 else if (pParam->name[0] != 'd')
1112 strlcpy(&g_szLogDst[0], pszValue, sizeof(g_szLogDst));
1113 return 0;
1114}
1115
1116/** log and dbg_log_dest parameter getter. */
1117static int vgdrvLinuxParamLogDstGet(char *pszBuf, struct kernel_param *pParam)
1118{
1119 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1120 *pszBuf = '\0';
1121 if (pLogger)
1122 RTLogGetDestinations(pLogger, pszBuf, _4K);
1123 return strlen(pszBuf);
1124}
1125
1126
1127/** r3_log_to_host parameter setter. */
1128static int vgdrvLinuxParamR3LogToHostSet(const char *pszValue, struct kernel_param *pParam)
1129{
1130 if ( pszValue == NULL
1131 || *pszValue == '\0'
1132 || *pszValue == 'n'
1133 || *pszValue == 'N'
1134 || *pszValue == 'd'
1135 || *pszValue == 'D'
1136 || ( (*pszValue == 'o' || *pszValue == 'O')
1137 && (*pszValue == 'f' || *pszValue == 'F') )
1138 )
1139 g_DevExt.fLoggingEnabled = false;
1140 else
1141 g_DevExt.fLoggingEnabled = true;
1142 return 0;
1143}
1144
1145/** r3_log_to_host parameter getter. */
1146static int vgdrvLinuxParamR3LogToHostGet(char *pszBuf, struct kernel_param *pParam)
1147{
1148 strcpy(pszBuf, g_DevExt.fLoggingEnabled ? "enabled" : "disabled");
1149 return strlen(pszBuf);
1150}
1151
1152
1153/*
1154 * Define module parameters.
1155 */
1156module_param_call(log, vgdrvLinuxParamLogGrpSet, vgdrvLinuxParamLogGrpGet, NULL, 0664);
1157module_param_call(log_flags, vgdrvLinuxParamLogFlagsSet, vgdrvLinuxParamLogFlagsGet, NULL, 0664);
1158module_param_call(log_dest, vgdrvLinuxParamLogDstSet, vgdrvLinuxParamLogDstGet, NULL, 0664);
1159# ifdef LOG_ENABLED
1160module_param_call(dbg_log, vgdrvLinuxParamLogGrpSet, vgdrvLinuxParamLogGrpGet, NULL, 0664);
1161module_param_call(dbg_log_flags, vgdrvLinuxParamLogFlagsSet, vgdrvLinuxParamLogFlagsGet, NULL, 0664);
1162module_param_call(dbg_log_dest, vgdrvLinuxParamLogDstSet, vgdrvLinuxParamLogDstGet, NULL, 0664);
1163# endif
1164module_param_call(r3_log_to_host, vgdrvLinuxParamR3LogToHostSet, vgdrvLinuxParamR3LogToHostGet, NULL, 0664);
1165
1166#endif /* 2.6.0 and later */
1167
1168
1169module_init(vgdrvLinuxModInit);
1170module_exit(vgdrvLinuxModExit);
1171
1172MODULE_AUTHOR(VBOX_VENDOR);
1173MODULE_DESCRIPTION(VBOX_PRODUCT " Guest Additions for Linux Module");
1174MODULE_LICENSE("GPL");
1175#ifdef MODULE_VERSION
1176MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV));
1177#endif
1178
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