VirtualBox

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

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

VBoxGuest-linux.c: fix for r119567/public r70033 for builds without VBOXGUEST_WITH_INPUT_DRIVER.

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