VirtualBox

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

Last change on this file since 102990 was 102990, checked in by vboxsync, 10 months ago

Additions: Linux: Introduce initial support for kernel 6.8, bugref:10584.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.5 KB
Line 
1/* $Id: VBoxGuest-linux.c 102990 2024-01-22 17:13:50Z vboxsync $ */
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-2023 Oracle and/or its affiliates.
11 *
12 * This file is part of VirtualBox base platform packages, as
13 * available from https://www.virtualbox.org.
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation, in version 3 of the
18 * License.
19 *
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, see <https://www.gnu.org/licenses>.
27 *
28 * The contents of this file may alternatively be used under the terms
29 * of the Common Development and Distribution License Version 1.0
30 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
31 * in the VirtualBox distribution, in which case the provisions of the
32 * CDDL are applicable instead of those of the GPL.
33 *
34 * You may elect to license modified versions of this file under the
35 * terms and conditions of either the GPL or the CDDL or both.
36 *
37 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
38 */
39
40
41/*********************************************************************************************************************************
42* Header Files *
43*********************************************************************************************************************************/
44#define LOG_GROUP LOG_GROUP_SUP_DRV
45
46#include "the-linux-kernel.h"
47
48#if RTLNX_VER_MIN(2,6,15)
49# define VBOXGUEST_WITH_INPUT_DRIVER
50#endif
51
52#if RTLNX_VER_MIN(4,15,0)
53# define CONST_4_15 const
54#else
55# define CONST_4_15
56#endif
57
58#include "VBoxGuestInternal.h"
59#ifdef VBOXGUEST_WITH_INPUT_DRIVER
60# include <linux/input.h>
61#endif
62#include <linux/miscdevice.h>
63#include <linux/poll.h>
64#if RTLNX_VER_MIN(2,6,28)
65# include <linux/tty.h>
66#endif
67#include <VBox/version.h>
68#include "revision-generated.h"
69
70#include <iprt/assert.h>
71#include <iprt/asm.h>
72#include <iprt/ctype.h>
73#include <iprt/initterm.h>
74#include <iprt/mem.h>
75#include <iprt/mp.h>
76#include <iprt/process.h>
77#include <iprt/spinlock.h>
78#include <iprt/semaphore.h>
79#include <iprt/string.h>
80#include <VBox/err.h>
81#include <VBox/log.h>
82
83
84/*********************************************************************************************************************************
85* Defined Constants And Macros *
86*********************************************************************************************************************************/
87/** The device name. */
88#define DEVICE_NAME "vboxguest"
89/** The device name for the device node open to everyone. */
90#define DEVICE_NAME_USER "vboxuser"
91/** The name of the PCI driver */
92#define DRIVER_NAME DEVICE_NAME
93
94
95/* 2.4.x compatibility macros that may or may not be defined. */
96#ifndef IRQ_RETVAL
97# define irqreturn_t void
98# define IRQ_RETVAL(n)
99#endif
100
101/* uidgid.h was introduced in 3.5.0. */
102#if RTLNX_VER_MAX(3,5,0)
103# define kgid_t gid_t
104# define kuid_t uid_t
105#endif
106
107#ifdef VBOXGUEST_WITH_INPUT_DRIVER
108/** The name of input pointing device for mouse integration. */
109# define VBOX_INPUT_DEVICE_NAME "VirtualBox mouse integration"
110#endif
111
112
113/*********************************************************************************************************************************
114* Internal Functions *
115*********************************************************************************************************************************/
116static void vgdrvLinuxTermPci(struct pci_dev *pPciDev);
117static int vgdrvLinuxProbePci(struct pci_dev *pPciDev, const struct pci_device_id *id);
118static int __init vgdrvLinuxModInit(void);
119static void __exit vgdrvLinuxModExit(void);
120static int vgdrvLinuxOpen(struct inode *pInode, struct file *pFilp);
121static int vgdrvLinuxRelease(struct inode *pInode, struct file *pFilp);
122#ifdef HAVE_UNLOCKED_IOCTL
123static long vgdrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
124#else
125static int vgdrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
126#endif
127static int vgdrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PVBOXGUESTSESSION pSession);
128static int vgdrvLinuxFAsync(int fd, struct file *pFile, int fOn);
129static unsigned int vgdrvLinuxPoll(struct file *pFile, poll_table *pPt);
130static ssize_t vgdrvLinuxRead(struct file *pFile, char *pbBuf, size_t cbRead, loff_t *poff);
131
132
133/*********************************************************************************************************************************
134* Global Variables *
135*********************************************************************************************************************************/
136/**
137 * Device extention & session data association structure.
138 */
139static VBOXGUESTDEVEXT g_DevExt;
140/** The PCI device. */
141static struct pci_dev *g_pPciDev = NULL;
142/** The base of the I/O port range. */
143static RTIOPORT g_IOPortBase;
144/** The base of the MMIO range. */
145static RTHCPHYS g_MMIOPhysAddr = NIL_RTHCPHYS;
146/** The size of the MMIO range as seen by PCI. */
147static uint32_t g_cbMMIO;
148/** The pointer to the mapping of the MMIO range. */
149static void *g_pvMMIOBase;
150
151static RTHCPHYS g_MmioReqPhysAddr = NIL_RTHCPHYS;
152static uint32_t g_cbMmioReq;
153static void *g_pvMmioReq = NULL;
154
155/** Wait queue used by polling. */
156static wait_queue_head_t g_PollEventQueue;
157/** Asynchronous notification stuff. */
158static struct fasync_struct *g_pFAsyncQueue;
159#ifdef VBOXGUEST_WITH_INPUT_DRIVER
160/** Pre-allocated mouse status VMMDev requests for use in the IRQ handler. */
161static VMMDevReqMouseStatusEx *g_pMouseStatusReqEx;
162#endif
163#if RTLNX_VER_MIN(2,6,0)
164/** Whether we've create the logger or not. */
165static volatile bool g_fLoggerCreated;
166/** Release logger group settings. */
167static char g_szLogGrp[128];
168/** Release logger flags settings. */
169static char g_szLogFlags[128];
170/** Release logger destination settings. */
171static char g_szLogDst[128];
172# if 0
173/** Debug logger group settings. */
174static char g_szDbgLogGrp[128];
175/** Debug logger flags settings. */
176static char g_szDbgLogFlags[128];
177/** Debug logger destination settings. */
178static char g_szDbgLogDst[128];
179# endif
180#endif
181
182/** The input device handle */
183#ifdef VBOXGUEST_WITH_INPUT_DRIVER
184static struct input_dev *g_pInputDevice = NULL;
185#endif
186
187/** The file_operations structure. */
188static struct file_operations g_FileOps =
189{
190 owner: THIS_MODULE,
191 open: vgdrvLinuxOpen,
192 release: vgdrvLinuxRelease,
193#ifdef HAVE_UNLOCKED_IOCTL
194 unlocked_ioctl: vgdrvLinuxIOCtl,
195#else
196 ioctl: vgdrvLinuxIOCtl,
197#endif
198 fasync: vgdrvLinuxFAsync,
199 read: vgdrvLinuxRead,
200 poll: vgdrvLinuxPoll,
201 llseek: no_llseek,
202};
203
204/** The miscdevice structure. */
205static struct miscdevice g_MiscDevice =
206{
207 minor: MISC_DYNAMIC_MINOR,
208 name: DEVICE_NAME,
209 fops: &g_FileOps,
210};
211
212/** The file_operations structure for the user device.
213 * @remarks For the time being we'll be using the same implementation as
214 * /dev/vboxguest here. */
215static struct file_operations g_FileOpsUser =
216{
217 owner: THIS_MODULE,
218 open: vgdrvLinuxOpen,
219 release: vgdrvLinuxRelease,
220#ifdef HAVE_UNLOCKED_IOCTL
221 unlocked_ioctl: vgdrvLinuxIOCtl,
222#else
223 ioctl: vgdrvLinuxIOCtl,
224#endif
225};
226
227/** The miscdevice structure for the user device. */
228static struct miscdevice g_MiscDeviceUser =
229{
230 minor: MISC_DYNAMIC_MINOR,
231 name: DEVICE_NAME_USER,
232 fops: &g_FileOpsUser,
233};
234
235
236/** PCI hotplug structure. */
237static const struct pci_device_id g_VBoxGuestPciId[] =
238{
239 {
240 vendor: VMMDEV_VENDORID,
241 device: VMMDEV_DEVICEID
242 },
243 {
244 /* empty entry */
245 }
246};
247
248MODULE_DEVICE_TABLE(pci, g_VBoxGuestPciId);
249
250/** Structure for registering the PCI driver. */
251static struct pci_driver g_PciDriver =
252{
253 name: DRIVER_NAME,
254 id_table: g_VBoxGuestPciId,
255 probe: vgdrvLinuxProbePci,
256 remove: vgdrvLinuxTermPci
257};
258
259#ifdef VBOXGUEST_WITH_INPUT_DRIVER
260/** Kernel IDC session to ourselves for use with the mouse events. */
261static PVBOXGUESTSESSION g_pKernelSession = NULL;
262#endif
263
264
265
266/**
267 * Converts a VBox status code to a linux error code.
268 *
269 * @returns corresponding negative linux error code.
270 * @param rc supdrv error code (SUPDRV_ERR_* defines).
271 */
272static int vgdrvLinuxConvertToNegErrno(int rc)
273{
274 if ( rc > -1000
275 && rc < 1000)
276 return -RTErrConvertToErrno(rc);
277 switch (rc)
278 {
279 case VERR_HGCM_SERVICE_NOT_FOUND: return -ESRCH;
280 case VINF_HGCM_CLIENT_REJECTED: return 0;
281 case VERR_HGCM_INVALID_CMD_ADDRESS: return -EFAULT;
282 case VINF_HGCM_ASYNC_EXECUTE: return 0;
283 case VERR_HGCM_INTERNAL: return -EPROTO;
284 case VERR_HGCM_INVALID_CLIENT_ID: return -EINVAL;
285 case VINF_HGCM_SAVE_STATE: return 0;
286 /* No reason to return this to a guest */
287 // case VERR_HGCM_SERVICE_EXISTS: return -EEXIST;
288 default:
289 AssertMsgFailed(("Unhandled error code %Rrc\n", rc));
290 return -EPROTO;
291 }
292}
293
294
295#if defined(RT_ARCH_ARM64) || defined(RT_ARCH_ARM32)
296static int vgdrvLinuxMapMmio(struct pci_dev *pPciDev)
297{
298 g_MmioReqPhysAddr = pci_resource_start(pPciDev, 3);
299 g_cbMmioReq = pci_resource_len(pPciDev, 3);
300 if (request_mem_region(g_MmioReqPhysAddr, g_cbMmioReq, DEVICE_NAME) != NULL)
301 {
302 g_pvMmioReq = ioremap(g_MmioReqPhysAddr, g_cbMmioReq);
303 if (g_pvMmioReq)
304 return 0;
305
306 /* failure cleanup path */
307 LogRel((DEVICE_NAME ": ioremap failed; MMIO Addr=%RHp cb=%#x\n", g_MmioReqPhysAddr, g_cbMmioReq));
308 release_mem_region(g_MmioReqPhysAddr, g_cbMmioReq);
309 return -ENOMEM;
310 }
311 else
312 LogRel((DEVICE_NAME ": failed to obtain adapter memory\n"));
313
314 return -EBUSY;
315}
316#endif
317
318
319/**
320 * Does the PCI detection and init of the device.
321 *
322 * @returns 0 on success, negated errno on failure.
323 */
324static int vgdrvLinuxProbePci(struct pci_dev *pPciDev, const struct pci_device_id *id)
325{
326 int rc;
327
328 NOREF(id);
329 AssertReturn(!g_pPciDev, -EINVAL);
330 rc = pci_enable_device(pPciDev);
331 if (rc >= 0)
332 {
333#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
334 /* I/O Ports are mandatory, the MMIO bit is not. */
335 g_IOPortBase = pci_resource_start(pPciDev, 0);
336 if (g_IOPortBase != 0)
337#elif defined(RT_ARCH_ARM64) || defined(RT_ARCH_ARM32)
338 rc = vgdrvLinuxMapMmio(pPciDev);
339 if (!rc)
340#else
341# error "I have no memory of this arechitecture"
342#endif
343 {
344 /*
345 * Map the register address space.
346 */
347 g_MMIOPhysAddr = pci_resource_start(pPciDev, 1);
348 g_cbMMIO = pci_resource_len(pPciDev, 1);
349 if (request_mem_region(g_MMIOPhysAddr, g_cbMMIO, DEVICE_NAME) != NULL)
350 {
351 g_pvMMIOBase = ioremap(g_MMIOPhysAddr, g_cbMMIO);
352 if (g_pvMMIOBase)
353 {
354 /** @todo why aren't we requesting ownership of the I/O ports as well? */
355 g_pPciDev = pPciDev;
356 return 0;
357 }
358
359 /* failure cleanup path */
360 LogRel((DEVICE_NAME ": ioremap failed; MMIO Addr=%RHp cb=%#x\n", g_MMIOPhysAddr, g_cbMMIO));
361 rc = -ENOMEM;
362 release_mem_region(g_MMIOPhysAddr, g_cbMMIO);
363 }
364 else
365 {
366 LogRel((DEVICE_NAME ": failed to obtain adapter memory\n"));
367 rc = -EBUSY;
368 }
369 g_MMIOPhysAddr = NIL_RTHCPHYS;
370 g_cbMMIO = 0;
371 g_IOPortBase = 0;
372 }
373 else
374 {
375 LogRel((DEVICE_NAME ": did not find expected hardware resources\n"));
376 rc = -ENXIO;
377 }
378 pci_disable_device(pPciDev);
379 }
380 else
381 LogRel((DEVICE_NAME ": could not enable device: %d\n", rc));
382 return rc;
383}
384
385
386/**
387 * Clean up the usage of the PCI device.
388 */
389static void vgdrvLinuxTermPci(struct pci_dev *pPciDev)
390{
391 g_pPciDev = NULL;
392 if (pPciDev)
393 {
394 if (g_pvMmioReq)
395 {
396 iounmap(g_pvMmioReq);
397 g_pvMmioReq = NULL;
398
399 release_mem_region(g_MmioReqPhysAddr, g_cbMmioReq);
400 g_MmioReqPhysAddr = NIL_RTHCPHYS;
401 g_cbMmioReq = 0;
402 }
403
404 iounmap(g_pvMMIOBase);
405 g_pvMMIOBase = NULL;
406
407 release_mem_region(g_MMIOPhysAddr, g_cbMMIO);
408 g_MMIOPhysAddr = NIL_RTHCPHYS;
409 g_cbMMIO = 0;
410
411 pci_disable_device(pPciDev);
412 }
413}
414
415
416/**
417 * Interrupt service routine.
418 *
419 * @returns In 2.4 it returns void.
420 * In 2.6 we indicate whether we've handled the IRQ or not.
421 *
422 * @param iIrq The IRQ number.
423 * @param pvDevId The device ID, a pointer to g_DevExt.
424 * @param pRegs Register set. Removed in 2.6.19.
425 */
426#if RTLNX_VER_MIN(2,6,19) && !defined(DOXYGEN_RUNNING)
427static irqreturn_t vgdrvLinuxISR(int iIrq, void *pvDevId)
428#else
429static irqreturn_t vgdrvLinuxISR(int iIrq, void *pvDevId, struct pt_regs *pRegs)
430#endif
431{
432 bool fTaken = VGDrvCommonISR(&g_DevExt);
433 return IRQ_RETVAL(fTaken);
434}
435
436
437/**
438 * Registers the ISR and initializes the poll wait queue.
439 */
440static int __init vgdrvLinuxInitISR(void)
441{
442 int rc;
443
444 init_waitqueue_head(&g_PollEventQueue);
445 rc = request_irq(g_pPciDev->irq,
446 vgdrvLinuxISR,
447#if RTLNX_VER_MIN(2,6,20)
448 IRQF_SHARED,
449#else
450 SA_SHIRQ,
451#endif
452 DEVICE_NAME,
453 &g_DevExt);
454 if (rc)
455 {
456 LogRel((DEVICE_NAME ": could not request IRQ %d: err=%d\n", g_pPciDev->irq, rc));
457 return rc;
458 }
459 return 0;
460}
461
462
463/**
464 * Deregisters the ISR.
465 */
466static void vgdrvLinuxTermISR(void)
467{
468 free_irq(g_pPciDev->irq, &g_DevExt);
469}
470
471#ifdef VBOXGUEST_WITH_INPUT_DRIVER
472
473/**
474 * Check if extended mouse pointer state request protocol is currently used by driver.
475 *
476 * @returns True if extended protocol is used, False otherwise.
477 */
478static bool vgdrvLinuxUsesMouseStatusEx(void)
479{
480 return g_pMouseStatusReqEx->Core.header.requestType == VMMDevReq_GetMouseStatusEx;
481}
482
483/**
484 * Reports the mouse integration status to the host.
485 *
486 * Calls the kernel IOCtl to report mouse status to the host on behalf of
487 * our kernel session.
488 *
489 * @param fStatus The mouse status to report.
490 */
491static int vgdrvLinuxSetMouseStatus(uint32_t fStatus)
492{
493 int rc;
494 VBGLIOCSETMOUSESTATUS Req;
495 VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS);
496 Req.u.In.fStatus = fStatus;
497 rc = VGDrvCommonIoCtl(VBGL_IOCTL_SET_MOUSE_STATUS, &g_DevExt, g_pKernelSession, &Req.Hdr, sizeof(Req));
498 if (RT_SUCCESS(rc))
499 rc = Req.Hdr.rc;
500 return rc;
501}
502
503
504/**
505 * Called when the input device is first opened.
506 *
507 * Sets up absolute mouse reporting.
508 */
509static int vboxguestOpenInputDevice(struct input_dev *pDev)
510{
511 int rc = vgdrvLinuxSetMouseStatus( VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
512 | VMMDEV_MOUSE_NEW_PROTOCOL
513 | (vgdrvLinuxUsesMouseStatusEx() ? VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL : 0));
514 if (RT_FAILURE(rc))
515 return ENODEV;
516 NOREF(pDev);
517 return 0;
518}
519
520
521/**
522 * Called if all open handles to the input device are closed.
523 *
524 * Disables absolute reporting.
525 */
526static void vboxguestCloseInputDevice(struct input_dev *pDev)
527{
528 NOREF(pDev);
529 vgdrvLinuxSetMouseStatus(0);
530}
531
532
533/**
534 * Free corresponding mouse status request structure.
535 */
536static void vgdrvLinuxFreeMouseStatusReq(void)
537{
538 VbglR0GRFree(&g_pMouseStatusReqEx->Core.header);
539 g_pMouseStatusReqEx = NULL;
540}
541
542
543/**
544 * Creates the kernel input device.
545 */
546static int __init vgdrvLinuxCreateInputDevice(void)
547{
548 /* Try to allocate legacy request data first, and check if host supports extended protocol. */
549 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&g_pMouseStatusReqEx, sizeof(VMMDevReqMouseStatus), VMMDevReq_GetMouseStatus);
550 if (RT_SUCCESS(rc))
551 {
552 /* Check if host supports extended mouse state reporting. */
553 g_pMouseStatusReqEx->Core.mouseFeatures = 0;
554 rc = VbglR0GRPerform(&g_pMouseStatusReqEx->Core.header);
555 if (RT_SUCCESS(rc))
556 {
557 if (g_pMouseStatusReqEx->Core.mouseFeatures & VMMDEV_MOUSE_HOST_SUPPORTS_FULL_STATE_PROTOCOL)
558 {
559 VMMDevReqMouseStatusEx *pReqEx = NULL;
560 rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReqEx, sizeof(*pReqEx), VMMDevReq_GetMouseStatusEx);
561 if (RT_SUCCESS(rc))
562 {
563 /* De-allocate legacy request data, */
564 VbglR0GRFree(&g_pMouseStatusReqEx->Core.header);
565 /* ..and switch to extended requests. */
566 g_pMouseStatusReqEx = pReqEx;
567 LogRel(("Host supports full mouse state reporting, switching to extended mouse integration protocol\n"));
568 }
569 else
570 LogRel(("Host supports full mouse state reporting, but feature cannot be initialized, switching to legacy mouse integration protocol\n"));
571 }
572 else
573 LogRel(("Host does not support full mouse state reporting, switching to legacy mouse integration protocol\n"));
574 }
575 else
576 LogRel(("Unable to get host mouse capabilities, switching to legacy mouse integration protocol\n"));
577 }
578 else
579 rc = -ENOMEM;
580
581 if (RT_SUCCESS(rc))
582 {
583 g_pInputDevice = input_allocate_device();
584 if (g_pInputDevice)
585 {
586 g_pInputDevice->name = VBOX_INPUT_DEVICE_NAME;
587 g_pInputDevice->id.bustype = BUS_PCI;
588 g_pInputDevice->id.vendor = VMMDEV_VENDORID;
589 g_pInputDevice->id.product = VMMDEV_DEVICEID;
590 g_pInputDevice->id.version = VBOX_SHORT_VERSION;
591 g_pInputDevice->open = vboxguestOpenInputDevice;
592 g_pInputDevice->close = vboxguestCloseInputDevice;
593# if RTLNX_VER_MAX(2,6,22)
594 g_pInputDevice->cdev.dev = &g_pPciDev->dev;
595# else
596 g_pInputDevice->dev.parent = &g_pPciDev->dev;
597# endif
598 /* Set up input device capabilities. */
599 ASMBitSet(g_pInputDevice->evbit, EV_ABS);
600 ASMBitSet(g_pInputDevice->evbit, EV_KEY);
601# ifdef EV_SYN
602 ASMBitSet(g_pInputDevice->evbit, EV_SYN);
603# endif
604 ASMBitSet(g_pInputDevice->absbit, ABS_X);
605 ASMBitSet(g_pInputDevice->absbit, ABS_Y);
606
607 input_set_abs_params(g_pInputDevice, ABS_X, VMMDEV_MOUSE_RANGE_MIN, VMMDEV_MOUSE_RANGE_MAX, 0, 0);
608 input_set_abs_params(g_pInputDevice, ABS_Y, VMMDEV_MOUSE_RANGE_MIN, VMMDEV_MOUSE_RANGE_MAX, 0, 0);
609
610 ASMBitSet(g_pInputDevice->keybit, BTN_MOUSE);
611
612 /* Report extra capabilities to input layer if extended mouse state protocol
613 * will be used in communication with host. */
614 if (vgdrvLinuxUsesMouseStatusEx())
615 {
616 ASMBitSet(g_pInputDevice->evbit, EV_REL);
617 ASMBitSet(g_pInputDevice->evbit, EV_KEY);
618
619 ASMBitSet(g_pInputDevice->relbit, REL_WHEEL);
620 ASMBitSet(g_pInputDevice->relbit, REL_HWHEEL);
621
622 ASMBitSet(g_pInputDevice->keybit, BTN_LEFT);
623 ASMBitSet(g_pInputDevice->keybit, BTN_RIGHT);
624 ASMBitSet(g_pInputDevice->keybit, BTN_MIDDLE);
625 ASMBitSet(g_pInputDevice->keybit, BTN_SIDE);
626 ASMBitSet(g_pInputDevice->keybit, BTN_EXTRA);
627 }
628
629 rc = input_register_device(g_pInputDevice);
630 if (rc == 0)
631 return 0;
632
633 input_free_device(g_pInputDevice);
634 }
635 else
636 rc = -ENOMEM;
637
638 vgdrvLinuxFreeMouseStatusReq();
639 }
640
641 return rc;
642}
643
644
645/**
646 * Terminates the kernel input device.
647 */
648static void vgdrvLinuxTermInputDevice(void)
649{
650 /* Notify host that mouse integration is no longer available. */
651 vgdrvLinuxSetMouseStatus(0);
652
653 vgdrvLinuxFreeMouseStatusReq();
654
655 /* See documentation of input_register_device(): input_free_device()
656 * should not be called after a device has been registered. */
657 input_unregister_device(g_pInputDevice);
658}
659
660#endif /* VBOXGUEST_WITH_INPUT_DRIVER */
661
662/**
663 * Creates the device nodes.
664 *
665 * @returns 0 on success, negated errno on failure.
666 */
667static int __init vgdrvLinuxInitDeviceNodes(void)
668{
669 /*
670 * The full feature device node.
671 */
672 int rc = misc_register(&g_MiscDevice);
673 if (!rc)
674 {
675 /*
676 * The device node intended to be accessible by all users.
677 */
678 rc = misc_register(&g_MiscDeviceUser);
679 if (!rc)
680 return 0;
681 LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME_USER, rc));
682 misc_deregister(&g_MiscDevice);
683 }
684 else
685 LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME, rc));
686 return rc;
687}
688
689
690/**
691 * Deregisters the device nodes.
692 */
693static void vgdrvLinuxTermDeviceNodes(void)
694{
695 misc_deregister(&g_MiscDevice);
696 misc_deregister(&g_MiscDeviceUser);
697}
698
699
700/**
701 * Initialize module.
702 *
703 * @returns appropriate status code.
704 */
705static int __init vgdrvLinuxModInit(void)
706{
707 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
708 PRTLOGGER pRelLogger;
709 int rc;
710
711 /*
712 * Initialize IPRT first.
713 */
714 rc = RTR0Init(0);
715 if (RT_FAILURE(rc))
716 {
717 printk(KERN_ERR DEVICE_NAME ": RTR0Init failed, rc=%d.\n", rc);
718 return -EINVAL;
719 }
720
721 /*
722 * Create the release log.
723 * (We do that here instead of common code because we want to log
724 * early failures using the LogRel macro.)
725 */
726 rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all",
727 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
728 RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER | RTLOGDEST_USER, NULL);
729 if (RT_SUCCESS(rc))
730 {
731#if RTLNX_VER_MIN(2,6,0)
732 RTLogGroupSettings(pRelLogger, g_szLogGrp);
733 RTLogFlags(pRelLogger, g_szLogFlags);
734 RTLogDestinations(pRelLogger, g_szLogDst);
735#endif
736 RTLogRelSetDefaultInstance(pRelLogger);
737 }
738#if RTLNX_VER_MIN(2,6,0)
739 g_fLoggerCreated = true;
740#endif
741
742 /*
743 * Locate and initialize the PCI device.
744 */
745 rc = pci_register_driver(&g_PciDriver);
746 if (rc >= 0 && g_pPciDev)
747 {
748 /*
749 * Call the common device extension initializer.
750 */
751#if RTLNX_VER_MIN(2,6,0) && (defined(RT_ARCH_X86) || defined(RT_ARCH_ARM32))
752 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26;
753#elif RTLNX_VER_MIN(2,6,0) && (defined(RT_ARCH_AMD64) || defined(RT_ARCH_ARM64))
754 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26_x64;
755#elif RTLNX_VER_MIN(2,4,0) && defined(RT_ARCH_X86)
756 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24;
757#elif RTLNX_VER_MIN(2,4,0) && defined(RT_ARCH_AMD64)
758 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24_x64;
759#else
760# warning "huh? which arch + version is this?"
761 VBOXOSTYPE enmOsType = VBOXOSTYPE_Linux;
762#endif
763 rc = VGDrvCommonInitDevExt(&g_DevExt,
764 g_IOPortBase,
765 g_pvMmioReq,
766 g_pvMMIOBase,
767 g_cbMMIO,
768 enmOSType,
769 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
770 if (RT_SUCCESS(rc))
771 {
772 /*
773 * Register the interrupt service routine for it now that g_DevExt can handle IRQs.
774 */
775 rc = vgdrvLinuxInitISR();
776 if (rc >= 0) /** @todo r=bird: status check differs from that inside vgdrvLinuxInitISR. */
777 {
778#ifdef VBOXGUEST_WITH_INPUT_DRIVER
779 /*
780 * Create the kernel session for this driver.
781 */
782 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &g_pKernelSession);
783 if (RT_SUCCESS(rc))
784 {
785 /*
786 * Create the kernel input device.
787 */
788 rc = vgdrvLinuxCreateInputDevice();
789 if (rc >= 0)
790 {
791#endif
792 /*
793 * Read host configuration.
794 */
795 VGDrvCommonProcessOptionsFromHost(&g_DevExt);
796
797 /*
798 * Finally, create the device nodes.
799 */
800 rc = vgdrvLinuxInitDeviceNodes();
801 if (rc >= 0)
802 {
803 /* some useful information for the user but don't show this on the console */
804 LogRel((DEVICE_NAME ": Successfully loaded version " VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV) "\n"));
805 LogRel((DEVICE_NAME ": misc device minor %d, IRQ %d, I/O port %RTiop, MMIO at %RHp (size 0x%x)\n",
806 g_MiscDevice.minor, g_pPciDev->irq, g_IOPortBase, g_MMIOPhysAddr, g_cbMMIO));
807 printk(KERN_DEBUG DEVICE_NAME ": Successfully loaded version "
808 VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV) " (interface " RT_XSTR(VMMDEV_VERSION) ")\n");
809 return rc;
810 }
811
812 /* bail out */
813#ifdef VBOXGUEST_WITH_INPUT_DRIVER
814 vgdrvLinuxTermInputDevice();
815 }
816 else
817 {
818 LogRel((DEVICE_NAME ": vboxguestCreateInputDevice failed with rc=%Rrc\n", rc));
819 rc = RTErrConvertFromErrno(rc);
820 }
821 VGDrvCommonCloseSession(&g_DevExt, g_pKernelSession);
822 }
823#endif
824 vgdrvLinuxTermISR();
825 }
826 VGDrvCommonDeleteDevExt(&g_DevExt);
827 }
828 else
829 {
830 LogRel((DEVICE_NAME ": VGDrvCommonInitDevExt failed with rc=%Rrc\n", rc));
831 rc = RTErrConvertFromErrno(rc);
832 }
833 }
834 else
835 {
836 LogRel((DEVICE_NAME ": PCI device not found, probably running on physical hardware.\n"));
837 rc = -ENODEV;
838 }
839 pci_unregister_driver(&g_PciDriver);
840 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
841 RTLogDestroy(RTLogSetDefaultInstance(NULL));
842 RTR0Term();
843 return rc;
844}
845
846
847/**
848 * Unload the module.
849 */
850static void __exit vgdrvLinuxModExit(void)
851{
852 /*
853 * Inverse order of init.
854 */
855 vgdrvLinuxTermDeviceNodes();
856#ifdef VBOXGUEST_WITH_INPUT_DRIVER
857 vgdrvLinuxTermInputDevice();
858 VGDrvCommonCloseSession(&g_DevExt, g_pKernelSession);
859#endif
860 vgdrvLinuxTermISR();
861 VGDrvCommonDeleteDevExt(&g_DevExt);
862 pci_unregister_driver(&g_PciDriver);
863 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
864 RTLogDestroy(RTLogSetDefaultInstance(NULL));
865 RTR0Term();
866}
867
868
869/**
870 * Get the process user ID.
871 *
872 * @returns UID.
873 */
874DECLINLINE(RTUID) vgdrvLinuxGetUid(void)
875{
876#if RTLNX_VER_MIN(2,6,29)
877# if RTLNX_VER_MIN(3,5,0)
878 return from_kuid(current_user_ns(), current->cred->uid);
879# else
880 return current->cred->uid;
881# endif
882#else
883 return current->uid;
884#endif
885}
886
887
888/**
889 * Checks if the given group number is zero or not.
890 *
891 * @returns true / false.
892 * @param gid The group to check for.
893 */
894DECLINLINE(bool) vgdrvLinuxIsGroupZero(kgid_t gid)
895{
896#if RTLNX_VER_MIN(3,5,0)
897 return from_kgid(current_user_ns(), gid);
898#else
899 return gid == 0;
900#endif
901}
902
903
904/**
905 * Searches the effective group and supplementary groups for @a gid.
906 *
907 * @returns true if member, false if not.
908 * @param gid The group to check for.
909 */
910DECLINLINE(RTGID) vgdrvLinuxIsInGroupEff(kgid_t gid)
911{
912 return in_egroup_p(gid) != 0;
913}
914
915
916/**
917 * Check if we can positively or negatively determine that the process is
918 * running under a login on the physical machine console.
919 *
920 * Havne't found a good way to figure this out for graphical sessions, so this
921 * is mostly pointless. But let us try do what we can do.
922 *
923 * @returns VMMDEV_REQUESTOR_CON_XXX.
924 */
925static uint32_t vgdrvLinuxRequestorOnConsole(void)
926{
927 uint32_t fRet = VMMDEV_REQUESTOR_CON_DONT_KNOW;
928
929#if RTLNX_VER_MIN(2,6,28) /* First with tty_kref_put(). */
930 /*
931 * Check for tty0..63, ASSUMING that these are only used for the physical console.
932 */
933 struct tty_struct *pTty = get_current_tty();
934 if (pTty)
935 {
936# if RTLNX_VER_MIN(4,2,0)
937 const char *pszName = tty_name(pTty);
938# else
939 char szBuf[64];
940 const char *pszName = tty_name(pTty, szBuf);
941# endif
942 if ( pszName
943 && pszName[0] == 't'
944 && pszName[1] == 't'
945 && pszName[2] == 'y'
946 && RT_C_IS_DIGIT(pszName[3])
947 && ( pszName[4] == '\0'
948 || ( RT_C_IS_DIGIT(pszName[4])
949 && pszName[5] == '\0'
950 && (pszName[3] - '0') * 10 + (pszName[4] - '0') <= 63)) )
951 fRet = VMMDEV_REQUESTOR_CON_YES;
952 tty_kref_put(pTty);
953 }
954#endif
955
956 return fRet;
957}
958
959
960/**
961 * Device open. Called on open /dev/vboxdrv
962 *
963 * @param pInode Pointer to inode info structure.
964 * @param pFilp Associated file pointer.
965 */
966static int vgdrvLinuxOpen(struct inode *pInode, struct file *pFilp)
967{
968 int rc;
969 PVBOXGUESTSESSION pSession;
970 uint32_t fRequestor;
971 Log((DEVICE_NAME ": pFilp=%p pid=%d/%d %s\n", pFilp, RTProcSelf(), current->pid, current->comm));
972
973 /*
974 * Figure out the requestor flags.
975 * ASSUMES that the gid of /dev/vboxuser is what we should consider the special vbox group.
976 */
977 fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN;
978 if (vgdrvLinuxGetUid() == 0)
979 fRequestor |= VMMDEV_REQUESTOR_USR_ROOT;
980 else
981 fRequestor |= VMMDEV_REQUESTOR_USR_USER;
982 if (MINOR(pInode->i_rdev) == g_MiscDeviceUser.minor)
983 {
984 fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE;
985 if (!vgdrvLinuxIsGroupZero(pInode->i_gid) && vgdrvLinuxIsInGroupEff(pInode->i_gid))
986 fRequestor |= VMMDEV_REQUESTOR_GRP_VBOX;
987 }
988 fRequestor |= vgdrvLinuxRequestorOnConsole();
989
990 /*
991 * Call common code to create the user session. Associate it with
992 * the file so we can access it in the other methods.
993 */
994 rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &pSession);
995 if (RT_SUCCESS(rc))
996 pFilp->private_data = pSession;
997
998 Log(("vgdrvLinuxOpen: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n",
999 &g_DevExt, pSession, rc, vgdrvLinuxConvertToNegErrno(rc), RTProcSelf(), current->pid, current->comm));
1000 return vgdrvLinuxConvertToNegErrno(rc);
1001}
1002
1003
1004/**
1005 * Close device.
1006 *
1007 * @param pInode Pointer to inode info structure.
1008 * @param pFilp Associated file pointer.
1009 */
1010static int vgdrvLinuxRelease(struct inode *pInode, struct file *pFilp)
1011{
1012 Log(("vgdrvLinuxRelease: pFilp=%p pSession=%p pid=%d/%d %s\n",
1013 pFilp, pFilp->private_data, RTProcSelf(), current->pid, current->comm));
1014
1015#if RTLNX_VER_MAX(2,6,28)
1016 /* This housekeeping was needed in older kernel versions to ensure that
1017 * the file pointer didn't get left on the polling queue. */
1018 vgdrvLinuxFAsync(-1, pFilp, 0);
1019#endif
1020 VGDrvCommonCloseSession(&g_DevExt, (PVBOXGUESTSESSION)pFilp->private_data);
1021 pFilp->private_data = NULL;
1022 return 0;
1023}
1024
1025
1026/**
1027 * Device I/O Control entry point.
1028 *
1029 * @param pFilp Associated file pointer.
1030 * @param uCmd The function specified to ioctl().
1031 * @param ulArg The argument specified to ioctl().
1032 */
1033#if defined(HAVE_UNLOCKED_IOCTL) || defined(DOXYGEN_RUNNING)
1034static long vgdrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
1035#else
1036static int vgdrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
1037#endif
1038{
1039 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFilp->private_data;
1040 int rc;
1041#ifndef HAVE_UNLOCKED_IOCTL
1042 unlock_kernel();
1043#endif
1044
1045#if 0 /* no fast I/O controls defined atm. */
1046 if (RT_LIKELY( ( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN
1047 || uCmd == SUP_IOCTL_FAST_DO_HM_RUN
1048 || uCmd == SUP_IOCTL_FAST_DO_NOP)
1049 && pSession->fUnrestricted == true))
1050 rc = VGDrvCommonIoCtlFast(uCmd, ulArg, &g_DevExt, pSession);
1051 else
1052#endif
1053 rc = vgdrvLinuxIOCtlSlow(pFilp, uCmd, ulArg, pSession);
1054
1055#ifndef HAVE_UNLOCKED_IOCTL
1056 lock_kernel();
1057#endif
1058 return rc;
1059}
1060
1061
1062/**
1063 * Device I/O Control entry point, slow variant.
1064 *
1065 * @param pFilp Associated file pointer.
1066 * @param uCmd The function specified to ioctl().
1067 * @param ulArg The argument specified to ioctl().
1068 * @param pSession The session instance.
1069 */
1070static int vgdrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PVBOXGUESTSESSION pSession)
1071{
1072 int rc;
1073 VBGLREQHDR Hdr;
1074 PVBGLREQHDR pHdr;
1075 uint32_t cbBuf;
1076
1077 Log6(("vgdrvLinuxIOCtlSlow: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid));
1078
1079 /*
1080 * Read the header.
1081 */
1082 if (RT_FAILURE(RTR0MemUserCopyFrom(&Hdr, ulArg, sizeof(Hdr))))
1083 {
1084 Log(("vgdrvLinuxIOCtlSlow: copy_from_user(,%#lx,) failed; uCmd=%#x\n", ulArg, uCmd));
1085 return -EFAULT;
1086 }
1087 if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION))
1088 {
1089 Log(("vgdrvLinuxIOCtlSlow: bad header version %#x; uCmd=%#x\n", Hdr.uVersion, uCmd));
1090 return -EINVAL;
1091 }
1092
1093 /*
1094 * Buffer the request.
1095 * Note! The header is revalidated by the common code.
1096 */
1097 cbBuf = RT_MAX(Hdr.cbIn, Hdr.cbOut);
1098 if (RT_UNLIKELY(cbBuf > _1M*16))
1099 {
1100 Log(("vgdrvLinuxIOCtlSlow: too big cbBuf=%#x; uCmd=%#x\n", cbBuf, uCmd));
1101 return -E2BIG;
1102 }
1103 if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr)
1104 || (cbBuf != _IOC_SIZE(uCmd) && _IOC_SIZE(uCmd) != 0)))
1105 {
1106 Log(("vgdrvLinuxIOCtlSlow: bad ioctl cbBuf=%#x _IOC_SIZE=%#x; uCmd=%#x\n", cbBuf, _IOC_SIZE(uCmd), uCmd));
1107 return -EINVAL;
1108 }
1109 pHdr = RTMemAlloc(cbBuf);
1110 if (RT_UNLIKELY(!pHdr))
1111 {
1112 LogRel(("vgdrvLinuxIOCtlSlow: failed to allocate buffer of %d bytes for uCmd=%#x\n", cbBuf, uCmd));
1113 return -ENOMEM;
1114 }
1115 if (RT_FAILURE(RTR0MemUserCopyFrom(pHdr, ulArg, Hdr.cbIn)))
1116 {
1117 Log(("vgdrvLinuxIOCtlSlow: copy_from_user(,%#lx, %#x) failed; uCmd=%#x\n", ulArg, Hdr.cbIn, uCmd));
1118 RTMemFree(pHdr);
1119 return -EFAULT;
1120 }
1121 if (Hdr.cbIn < cbBuf)
1122 RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbBuf - Hdr.cbIn);
1123
1124 /*
1125 * Process the IOCtl.
1126 */
1127 rc = VGDrvCommonIoCtl(uCmd, &g_DevExt, pSession, pHdr, cbBuf);
1128
1129 /*
1130 * Copy ioctl data and output buffer back to user space.
1131 */
1132 if (RT_SUCCESS(rc))
1133 {
1134 uint32_t cbOut = pHdr->cbOut;
1135 if (RT_UNLIKELY(cbOut > cbBuf))
1136 {
1137 LogRel(("vgdrvLinuxIOCtlSlow: too much output! %#x > %#x; uCmd=%#x!\n", cbOut, cbBuf, uCmd));
1138 cbOut = cbBuf;
1139 }
1140 if (RT_FAILURE(RTR0MemUserCopyTo(ulArg, pHdr, cbOut)))
1141 {
1142 /* this is really bad! */
1143 LogRel(("vgdrvLinuxIOCtlSlow: copy_to_user(%#lx,,%#x); uCmd=%#x!\n", ulArg, cbOut, uCmd));
1144 rc = -EFAULT;
1145 }
1146 }
1147 else
1148 {
1149 Log(("vgdrvLinuxIOCtlSlow: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc));
1150 rc = -EINVAL;
1151 }
1152 RTMemFree(pHdr);
1153
1154 Log6(("vgdrvLinuxIOCtlSlow: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid));
1155 return rc;
1156}
1157
1158
1159/**
1160 * @note This code is duplicated on other platforms with variations, so please
1161 * keep them all up to date when making changes!
1162 */
1163int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
1164{
1165 /*
1166 * Simple request validation (common code does the rest).
1167 */
1168 int rc;
1169 if ( RT_VALID_PTR(pReqHdr)
1170 && cbReq >= sizeof(*pReqHdr))
1171 {
1172 /*
1173 * All requests except the connect one requires a valid session.
1174 */
1175 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
1176 if (pSession)
1177 {
1178 if ( RT_VALID_PTR(pSession)
1179 && pSession->pDevExt == &g_DevExt)
1180 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
1181 else
1182 rc = VERR_INVALID_HANDLE;
1183 }
1184 else if (uReq == VBGL_IOCTL_IDC_CONNECT)
1185 {
1186 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
1187 if (RT_SUCCESS(rc))
1188 {
1189 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
1190 if (RT_FAILURE(rc))
1191 VGDrvCommonCloseSession(&g_DevExt, pSession);
1192 }
1193 }
1194 else
1195 rc = VERR_INVALID_HANDLE;
1196 }
1197 else
1198 rc = VERR_INVALID_POINTER;
1199 return rc;
1200}
1201EXPORT_SYMBOL(VBoxGuestIDC);
1202
1203
1204/**
1205 * Asynchronous notification activation method.
1206 *
1207 * @returns 0 on success, negative errno on failure.
1208 *
1209 * @param fd The file descriptor.
1210 * @param pFile The file structure.
1211 * @param fOn On/off indicator.
1212 */
1213static int vgdrvLinuxFAsync(int fd, struct file *pFile, int fOn)
1214{
1215 return fasync_helper(fd, pFile, fOn, &g_pFAsyncQueue);
1216}
1217
1218
1219/**
1220 * Poll function.
1221 *
1222 * This returns ready to read if the mouse pointer mode or the pointer position
1223 * has changed since last call to read.
1224 *
1225 * @returns 0 if no changes, POLLIN | POLLRDNORM if there are unseen changes.
1226 *
1227 * @param pFile The file structure.
1228 * @param pPt The poll table.
1229 *
1230 * @remarks This is probably not really used, X11 is said to use the fasync
1231 * interface instead.
1232 */
1233static unsigned int vgdrvLinuxPoll(struct file *pFile, poll_table *pPt)
1234{
1235 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFile->private_data;
1236 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
1237 unsigned int fMask = pSession->u32MousePosChangedSeq != u32CurSeq
1238 ? POLLIN | POLLRDNORM
1239 : 0;
1240 poll_wait(pFile, &g_PollEventQueue, pPt);
1241 return fMask;
1242}
1243
1244
1245/**
1246 * Read to go with our poll/fasync response.
1247 *
1248 * @returns 1 or -EINVAL.
1249 *
1250 * @param pFile The file structure.
1251 * @param pbBuf The buffer to read into.
1252 * @param cbRead The max number of bytes to read.
1253 * @param poff The current file position.
1254 *
1255 * @remarks This is probably not really used as X11 lets the driver do its own
1256 * event reading. The poll condition is therefore also cleared when we
1257 * see VMMDevReq_GetMouseStatus in vgdrvIoCtl_VMMRequest.
1258 */
1259static ssize_t vgdrvLinuxRead(struct file *pFile, char *pbBuf, size_t cbRead, loff_t *poff)
1260{
1261 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFile->private_data;
1262 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
1263
1264 if (*poff != 0)
1265 return -EINVAL;
1266
1267 /*
1268 * Fake a single byte read if we're not up to date with the current mouse position.
1269 */
1270 if ( pSession->u32MousePosChangedSeq != u32CurSeq
1271 && cbRead > 0)
1272 {
1273 pSession->u32MousePosChangedSeq = u32CurSeq;
1274 pbBuf[0] = 0;
1275 return 1;
1276 }
1277 return 0;
1278}
1279
1280
1281#ifdef VBOXGUEST_WITH_INPUT_DRIVER
1282/**
1283 * Get host mouse state.
1284 *
1285 * @returns IPRT status code.
1286 * @param pfMouseFeatures Where to store host mouse capabilities.
1287 * @param pX Where to store X axis coordinate.
1288 * @param pY Where to store Y axis coordinate.
1289 * @param pDz Where to store vertical wheel movement offset (only set if in case of VMMDevReq_GetMouseStatusEx request).
1290 * @param pDw Where to store horizontal wheel movement offset (only set if in case of VMMDevReq_GetMouseStatusEx request).
1291 * @param pfButtons Where to store mouse buttons state (only set if in case of VMMDevReq_GetMouseStatusEx request).
1292 */
1293static int vgdrvLinuxGetHostMouseState(uint32_t *pfMouseFeatures, int32_t *pX, int32_t *pY, int32_t *pDz, int32_t *pDw, uint32_t *pfButtons)
1294{
1295 int rc = VERR_INVALID_PARAMETER;
1296
1297 Assert(pfMouseFeatures);
1298 Assert(pX);
1299 Assert(pY);
1300 Assert(pDz);
1301 Assert(pDw);
1302 Assert(pfButtons);
1303
1304 /* Initialize legacy request data. */
1305 g_pMouseStatusReqEx->Core.mouseFeatures = 0;
1306 g_pMouseStatusReqEx->Core.pointerXPos = 0;
1307 g_pMouseStatusReqEx->Core.pointerYPos = 0;
1308
1309 /* Initialize extended request data if VMMDevReq_GetMouseStatusEx is used. */
1310 if (vgdrvLinuxUsesMouseStatusEx())
1311 {
1312 g_pMouseStatusReqEx->dz = 0;
1313 g_pMouseStatusReqEx->dw = 0;
1314 g_pMouseStatusReqEx->fButtons = 0;
1315 }
1316
1317 /* Get host mouse state - either lagacy or extended version. */
1318 rc = VbglR0GRPerform(&g_pMouseStatusReqEx->Core.header);
1319 if (RT_SUCCESS(rc))
1320 {
1321 *pfMouseFeatures = g_pMouseStatusReqEx->Core.mouseFeatures;
1322 *pX = g_pMouseStatusReqEx->Core.pointerXPos;
1323 *pY = g_pMouseStatusReqEx->Core.pointerYPos;
1324
1325 /* Get extended mouse state data in case of VMMDevReq_GetMouseStatusEx. */
1326 if (vgdrvLinuxUsesMouseStatusEx())
1327 {
1328 *pDz = g_pMouseStatusReqEx->dz;
1329 *pDw = g_pMouseStatusReqEx->dw;
1330 *pfButtons = g_pMouseStatusReqEx->fButtons;
1331 }
1332 }
1333
1334 return rc;
1335}
1336#endif /* VBOXGUEST_WITH_INPUT_DRIVER */
1337
1338
1339void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1340{
1341#ifdef VBOXGUEST_WITH_INPUT_DRIVER
1342 int rc;
1343 uint32_t fMouseFeatures = 0;
1344 int32_t x = 0;
1345 int32_t y = 0;
1346 int32_t dz = 0;
1347 int32_t dw = 0;
1348 uint32_t fButtons = 0;
1349#endif
1350 NOREF(pDevExt);
1351
1352 /*
1353 * Wake up everyone that's in a poll() and post anyone that has
1354 * subscribed to async notifications.
1355 */
1356 Log3(("VGDrvNativeISRMousePollEvent: wake_up_all\n"));
1357 wake_up_all(&g_PollEventQueue);
1358 Log3(("VGDrvNativeISRMousePollEvent: kill_fasync\n"));
1359 kill_fasync(&g_pFAsyncQueue, SIGIO, POLL_IN);
1360#ifdef VBOXGUEST_WITH_INPUT_DRIVER
1361 rc = vgdrvLinuxGetHostMouseState(&fMouseFeatures, &x, &y, &dz, &dw, &fButtons);
1362 if (RT_SUCCESS(rc))
1363 {
1364 input_report_abs(g_pInputDevice, ABS_X, x);
1365 input_report_abs(g_pInputDevice, ABS_Y, y);
1366
1367 if ( vgdrvLinuxUsesMouseStatusEx()
1368 && ( fMouseFeatures
1369 & (VMMDEV_MOUSE_HOST_SUPPORTS_FULL_STATE_PROTOCOL | VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL))
1370 == ( VMMDEV_MOUSE_HOST_SUPPORTS_FULL_STATE_PROTOCOL | VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL ) )
1371 {
1372 /* Vertical and horizontal scroll values come as-is from GUI.
1373 * Invert values here as it is done in PS/2 mouse driver, so
1374 * scrolling direction will be exectly the same. */
1375 input_report_rel(g_pInputDevice, REL_WHEEL, -dz);
1376 input_report_rel(g_pInputDevice, REL_HWHEEL, -dw);
1377
1378 input_report_key(g_pInputDevice, BTN_LEFT, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_LEFT));
1379 input_report_key(g_pInputDevice, BTN_RIGHT, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_RIGHT));
1380 input_report_key(g_pInputDevice, BTN_MIDDLE, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_MIDDLE));
1381 input_report_key(g_pInputDevice, BTN_SIDE, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_X1));
1382 input_report_key(g_pInputDevice, BTN_EXTRA, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_X2));
1383 }
1384
1385# ifdef EV_SYN
1386 input_sync(g_pInputDevice);
1387# endif
1388 }
1389#endif
1390 Log3(("VGDrvNativeISRMousePollEvent: done\n"));
1391}
1392
1393
1394bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
1395{
1396 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
1397 return false;
1398}
1399
1400
1401#if RTLNX_VER_MIN(2,6,0)
1402
1403/** log and dbg_log parameter setter. */
1404static int vgdrvLinuxParamLogGrpSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam)
1405{
1406 if (g_fLoggerCreated)
1407 {
1408 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1409 if (pLogger)
1410 RTLogGroupSettings(pLogger, pszValue);
1411 }
1412 else if (pParam->name[0] != 'd')
1413 RT_STRSCPY(&g_szLogGrp[0], pszValue, sizeof(g_szLogGrp));
1414
1415 return 0;
1416}
1417
1418/** log and dbg_log parameter getter. */
1419static int vgdrvLinuxParamLogGrpGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam)
1420{
1421 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1422 *pszBuf = '\0';
1423 if (pLogger)
1424 RTLogQueryGroupSettings(pLogger, pszBuf, _4K);
1425 return strlen(pszBuf);
1426}
1427
1428
1429/** log and dbg_log_flags parameter setter. */
1430static int vgdrvLinuxParamLogFlagsSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam)
1431{
1432 if (g_fLoggerCreated)
1433 {
1434 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1435 if (pLogger)
1436 RTLogFlags(pLogger, pszValue);
1437 }
1438 else if (pParam->name[0] != 'd')
1439 RT_STRSCPY(&g_szLogFlags[0], pszValue, sizeof(g_szLogFlags));
1440 return 0;
1441}
1442
1443/** log and dbg_log_flags parameter getter. */
1444static int vgdrvLinuxParamLogFlagsGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam)
1445{
1446 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1447 *pszBuf = '\0';
1448 if (pLogger)
1449 RTLogQueryFlags(pLogger, pszBuf, _4K);
1450 return strlen(pszBuf);
1451}
1452
1453
1454/** log and dbg_log_dest parameter setter. */
1455static int vgdrvLinuxParamLogDstSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam)
1456{
1457 if (g_fLoggerCreated)
1458 {
1459 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1460 if (pLogger)
1461 RTLogDestinations(pLogger, pszValue);
1462 }
1463 else if (pParam->name[0] != 'd')
1464 RT_STRSCPY(&g_szLogDst[0], pszValue, sizeof(g_szLogDst));
1465 return 0;
1466}
1467
1468/** log and dbg_log_dest parameter getter. */
1469static int vgdrvLinuxParamLogDstGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam)
1470{
1471 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1472 *pszBuf = '\0';
1473 if (pLogger)
1474 RTLogQueryDestinations(pLogger, pszBuf, _4K);
1475 return strlen(pszBuf);
1476}
1477
1478
1479/** r3_log_to_host parameter setter. */
1480static int vgdrvLinuxParamR3LogToHostSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam)
1481{
1482 g_DevExt.fLoggingEnabled = VBDrvCommonIsOptionValueTrue(pszValue);
1483 return 0;
1484}
1485
1486/** r3_log_to_host parameter getter. */
1487static int vgdrvLinuxParamR3LogToHostGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam)
1488{
1489 strcpy(pszBuf, g_DevExt.fLoggingEnabled ? "enabled" : "disabled");
1490 return strlen(pszBuf);
1491}
1492
1493
1494/*
1495 * Define module parameters.
1496 */
1497module_param_call(log, vgdrvLinuxParamLogGrpSet, vgdrvLinuxParamLogGrpGet, NULL, 0664);
1498module_param_call(log_flags, vgdrvLinuxParamLogFlagsSet, vgdrvLinuxParamLogFlagsGet, NULL, 0664);
1499module_param_call(log_dest, vgdrvLinuxParamLogDstSet, vgdrvLinuxParamLogDstGet, NULL, 0664);
1500# ifdef LOG_ENABLED
1501module_param_call(dbg_log, vgdrvLinuxParamLogGrpSet, vgdrvLinuxParamLogGrpGet, NULL, 0664);
1502module_param_call(dbg_log_flags, vgdrvLinuxParamLogFlagsSet, vgdrvLinuxParamLogFlagsGet, NULL, 0664);
1503module_param_call(dbg_log_dest, vgdrvLinuxParamLogDstSet, vgdrvLinuxParamLogDstGet, NULL, 0664);
1504# endif
1505module_param_call(r3_log_to_host, vgdrvLinuxParamR3LogToHostSet, vgdrvLinuxParamR3LogToHostGet, NULL, 0664);
1506
1507#endif /* 2.6.0 and later */
1508
1509
1510module_init(vgdrvLinuxModInit);
1511module_exit(vgdrvLinuxModExit);
1512
1513MODULE_AUTHOR(VBOX_VENDOR);
1514MODULE_DESCRIPTION(VBOX_PRODUCT " Guest Additions for Linux Module");
1515MODULE_LICENSE("GPL");
1516#ifdef MODULE_VERSION
1517MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV));
1518#endif
1519
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