VirtualBox

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

Last change on this file was 103788, checked in by vboxsync, 2 months ago

Linux Additions: Add possiblity to prevent kernel modules from loading in kernel command line, bugref:8409.

Add to kernel command line either vboxguest.disabled=1 and/or vboxsf.disabled=1
and/or vboxvideo.disabled=1 in order to prevent certain module or all of them
from loading during system boot.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use