VirtualBox

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

Last change on this file since 21069 was 21069, checked in by vboxsync, 15 years ago

VBoxGuest (common): porting to linux. some cleanup. currently untested.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.0 KB
Line 
1/* $Rev: 21069 $ */
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-2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
30 * Clara, CA 95054 USA or visit http://www.sun.com if you need
31 * additional information or have any questions.
32 * Some lines of code to disable the local APIC on x86_64 machines taken
33 * from a Mandriva patch by Gwenole Beauchesne <gbeauchesne@mandriva.com>.
34 */
35
36/*******************************************************************************
37* Header Files *
38*******************************************************************************/
39#define LOG_GROUP LOG_GROUP_SUP_DRV
40#include "VBoxGuestInternal.h"
41#include "the-linux-kernel.h"
42#include <linux/miscdevice.h>
43#include "version-generated.h"
44
45#include <iprt/assert.h>
46#include <iprt/spinlock.h>
47#include <iprt/semaphore.h>
48#include <iprt/initterm.h>
49#include <iprt/process.h>
50#include <iprt/err.h>
51#include <iprt/mem.h>
52#include <VBox/log.h>
53#include <iprt/mp.h>
54#include <iprt/mem.h>
55
56
57#define xstr(s) str(s)
58#define str(s) #s
59
60/** The device name. */
61#define DEVICE_NAME "vboxguest"
62/** The device name for the device node open to everyone.. */
63#define DEVICE_NAME_USER "vboxuser"
64
65
66#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
67# define PCI_DEV_GET(v,d,p) pci_get_device(v,d,p)
68# define PCI_DEV_PUT(x) pci_dev_put(x)
69#else
70# define PCI_DEV_GET(v,d,p) pci_find_device(v,d,p)
71# define PCI_DEV_PUT(x) do {} while(0)
72#endif
73
74/* 2.4.x compatability macros that may or may not be defined. */
75#ifndef IRQ_RETVAL
76# define irqreturn_t void
77# define IRQ_RETVAL(n)
78#endif
79
80
81/*******************************************************************************
82* Internal Functions *
83*******************************************************************************/
84static int vboxguestLinuxModInit(void);
85static void vboxguestLinuxModExit(void);
86static int vboxguestLinuxOpen(struct inode *pInode, struct file *pFilp);
87static int vboxguestLinuxRelease(struct inode *pInode, struct file *pFilp);
88#ifdef HAVE_UNLOCKED_IOCTL
89static long vboxguestLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
90#else
91static int vboxguestLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
92#endif
93
94
95/*******************************************************************************
96* Global Variables *
97*******************************************************************************/
98/**
99 * Device extention & session data association structure.
100 */
101static VBOXGUESTDEVEXT g_DevExt;
102/** The PCI device. */
103static struct pci_dev *g_pPciDev;
104/** The base of the I/O port range. */
105static RTIOPORT g_IOPortBase;
106/** The base of the MMIO range. */
107static RTHCPHYS g_MMIOPhysAddr = NIL_RTHCPHYS;
108/** The size of the MMIO range as seen by PCI. */
109static uint32_t g_cbMMIO;
110/** The pointer to the mapping of the MMIO range. */
111static void *g_pvMMIOBase;
112
113/** Our file node major id.
114 * Either set dynamically at run time or statically at compile time. */
115#ifdef CONFIG_VBOXADD_MAJOR
116static unsigned int g_iModuleMajor = CONFIG_VBOXADD_MAJOR;
117#else
118static unsigned int g_iModuleMajor = 0;
119#endif
120
121
122/** The file_operations structure. */
123static struct file_operations g_FileOps =
124{
125 owner: THIS_MODULE,
126 open: vboxguestLinuxOpen,
127 release: vboxguestLinuxRelease,
128#ifdef HAVE_UNLOCKED_IOCTL
129 unlocked_ioctl: vboxguestLinuxIOCtl,
130#else
131 ioctl: vboxguestLinuxIOCtl,
132#endif
133};
134
135/** The miscdevice structure. */
136static struct miscdevice g_MiscDevice =
137{
138 minor: MISC_DYNAMIC_MINOR,
139 name: DEVICE_NAME,
140 fops: &g_FileOps,
141#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 17)
142 devfs_name: DEVICE_NAME,
143#endif
144};
145
146/** The file_operations structure for the user device.
147 * @remarks For the time being we'll be using the same implementation as
148 * /dev/vboxguest here. */
149static struct file_operations g_FileOpsUser =
150{
151 owner: THIS_MODULE,
152 open: vboxguestLinuxOpen,
153 release: vboxguestLinuxRelease,
154#ifdef HAVE_UNLOCKED_IOCTL
155 unlocked_ioctl: vboxguestLinuxIOCtl,
156#else
157 ioctl: vboxguestLinuxIOCtl,
158#endif
159};
160
161/** The miscdevice structure for the user device. */
162static struct miscdevice g_MiscDeviceUser =
163{
164 minor: MISC_DYNAMIC_MINOR,
165 name: DEVICE_NAME_USER,
166 fops: &g_FileOpsUser,
167#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 17)
168 devfs_name: DEVICE_NAME_USER,
169#endif
170};
171
172
173/** PCI hotplug structure. */
174static const struct pci_device_id __devinitdata g_VBoxGuestPciId[] =
175{
176 {
177 vendor: VMMDEV_VENDORID,
178 device: VMMDEV_DEVICEID
179 },
180 {
181 /* empty entry */
182 }
183};
184MODULE_DEVICE_TABLE(pci, g_VBoxGuestPciId);
185
186
187/**
188 * Converts a VBox status code to a linux error code.
189 *
190 * @returns corresponding negative linux error code.
191 * @param rc supdrv error code (SUPDRV_ERR_* defines).
192 */
193static int vboxguestLinuxConvertToNegErrno(int rc)
194{
195 if ( rc > -1000
196 && rc < 1000)
197 return -RTErrConvertToErrno(rc);
198 switch (rc)
199 {
200 case VERR_HGCM_SERVICE_NOT_FOUND: return -ESRCH;
201 case VINF_HGCM_CLIENT_REJECTED: return 0;
202 case VERR_HGCM_INVALID_CMD_ADDRESS: return -EFAULT;
203 case VINF_HGCM_ASYNC_EXECUTE: return 0;
204 case VERR_HGCM_INTERNAL: return -EPROTO;
205 case VERR_HGCM_INVALID_CLIENT_ID: return -EINVAL;
206 case VINF_HGCM_SAVE_STATE: return 0;
207 /* No reason to return this to a guest */
208 // case VERR_HGCM_SERVICE_EXISTS: return -EEXIST;
209 default:
210 AssertMsgFailed(("Unhandled error code %Rrc\n", rc));
211 return -EPROTO;
212 }
213}
214
215
216
217/**
218 * Does the PCI detection and init of the device.
219 *
220 * @returns 0 on success, negated errno on failure.
221 */
222static int __init vboxguestLinuxInitPci(void)
223{
224 struct pci_dev *pPciDev;
225 int rc;
226
227 pPciDev = PCI_DEV_GET(VMMDEV_VENDORID, VMMDEV_DEVICEID, NULL);
228 if (pPciDev)
229 {
230 rc = pci_enable_device(pPciDev);
231 if (rc >= 0)
232 {
233 /* I/O Ports are mandatory, the MMIO bit is not. */
234 if (pci_resource_start(pPciDev, 0) != 0)
235 {
236 /*
237 * Map the register address space.
238 */
239 g_MMIOPhysAddr = pci_resource_start(pPciDev, 1);
240 g_cbMMIO = pci_resource_len(pPciDev, 1);
241 g_IOPortBase = pci_resource_start(pPciDev, 0);
242 if (request_mem_region(g_MMIOPhysAddr, g_cbMMIO, DEVICE_NAME) != NULL)
243 {
244 g_pvMMIOBase = ioremap(g_MMIOPhysAddr, g_cbMMIO);
245 if (g_DevExt.pVMMDevMemory)
246 {
247 /** @todo why aren't we requesting ownership of the I/O ports as well? */
248 g_pPciDev = pPciDev;
249 return 0;
250 }
251
252 /* failure cleanup path */
253 LogRel((DEVICE_NAME ": ioremap failed\n"));
254 rc = -ENOMEM;
255 release_mem_region(g_MMIOPhysAddr, g_cbMMIO);
256 }
257 else
258 {
259 LogRel((DEVICE_NAME ": failed to obtain adapter memory\n"));
260 rc = -EBUSY;
261 }
262 g_MMIOPhysAddr = NIL_RTHCPHYS;
263 g_cbMMIO = 0;
264 g_IOPortBase = 0;
265 }
266 else
267 {
268 LogRel((DEVICE_NAME ": did not find expected hardware resources\n"));
269 rc = -ENXIO;
270 }
271 pci_disable_device(pPciDev);
272 }
273 else
274 LogRel((DEVICE_NAME ": could not enable device: %d\n", rc));
275 PCI_DEV_PUT(pPciDev);
276 }
277 else
278 {
279 printk(KERN_ERR DEVICE_NAME ": VirtualBox Guest PCI device not found.\n");
280 rc = -ENODEV;
281 }
282 return rc;
283}
284
285
286/**
287 * Clean up the usage of the PCI device.
288 */
289static void __exit vboxguestLinuxTermPci(void)
290{
291 struct pci_dev *pPciDev = g_pPciDev;
292 g_pPciDev = NULL;
293 if (pPciDev)
294 {
295 iounmap(g_pvMMIOBase);
296 g_pvMMIOBase = NULL;
297
298 release_mem_region(g_MMIOPhysAddr, g_cbMMIO);
299 g_MMIOPhysAddr = NIL_RTHCPHYS;
300 g_cbMMIO = 0;
301
302 pci_disable_device(pPciDev);
303 }
304}
305
306
307/**
308 * Interrupt service routine.
309 *
310 * @returns In 2.4 it returns void.
311 * In 2.6 we indicate whether we've handled the IRQ or not.
312 *
313 * @param iIrq The IRQ number.
314 * @param pvDevId The device ID, a pointer to g_DevExt.
315 * @param pvRegs Register set. Removed in 2.6.19.
316 */
317#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
318static irqreturn_t vboxguestLinuxISR(int iIrrq, void *pvDevId)
319#else
320static irqreturn_t vboxguestLinuxISR(int iIrrq, void *pvDevId, struct pt_regs *pRegs)
321#endif
322{
323 bool fTaken = VBoxGuestCommonISR(&g_DevExt);
324 /** @todo if (vboxDev->irqAckRequest->events &
325 * VMMDEV_EVENT_MOUSE_POSITION_CHANGED)
326 * kill_fasync(&vboxDev->async_queue, SIGIO, POLL_IN);
327 */
328 return IRQ_RETVAL(fTaken);
329}
330
331
332/**
333 * Registers the ISR.
334 */
335static int __init vboxguestLinuxInitISR(void)
336{
337 int rc;
338
339 rc = request_irq(g_pPciDev->irq,
340 vboxguestLinuxISR,
341#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
342 IRQF_SHARED,
343#else
344 SA_SHIRQ,
345#endif
346 DEVICE_NAME,
347 &g_DevExt);
348 if (rc)
349 {
350 LogRel((DEVICE_NAME ": could not request IRQ %d: err=%d\n", g_pPciDev->irq, rc));
351 return rc;
352 }
353 return 0;
354}
355
356
357/**
358 * Deregisters the ISR.
359 */
360static void __exit vboxguestLinuxTermISR(void)
361{
362 free_irq(g_pPciDev->irq, &g_DevExt);
363}
364
365
366/**
367 * Creates the device nodes.
368 *
369 * @returns 0 on success, negated errno on failure.
370 */
371static int __init vboxguestLinuxInitDeviceNodes(void)
372{
373 int rc;
374
375 /*
376 * The full feature device node.
377 */
378 if (g_iModuleMajor > 0)
379 {
380 rc = register_chrdev(g_iModuleMajor, DEVICE_NAME, &g_FileOps);
381 if (rc < 0)
382 {
383 LogRel((DEVICE_NAME ": register_chrdev failed: g_iModuleMajor: %d, rc: %d\n", g_iModuleMajor, rc));
384 return rc;
385 }
386 }
387 else
388 {
389 rc = misc_register(&g_MiscDevice);
390 if (rc)
391 {
392 LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME, rc));
393 return rc;
394 }
395 }
396
397 /*
398 * The device node intended to be accessible by all users.
399 */
400 rc = misc_register(&g_MiscDeviceUser);
401 if (rc)
402 {
403 LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME_USER, rc));
404 if (g_iModuleMajor > 0)
405 unregister_chrdev(g_iModuleMajor, DEVICE_NAME);
406 else
407 misc_deregister(&g_MiscDevice);
408 return rc;
409 }
410
411 return 0;
412}
413
414
415/**
416 * Deregisters the device nodes.
417 */
418static void __exit vboxguestLinuxTermDeviceNodes(void)
419{
420 if (g_iModuleMajor > 0)
421 unregister_chrdev(g_iModuleMajor, DEVICE_NAME);
422 else
423 misc_deregister(&g_MiscDevice);
424 misc_deregister(&g_MiscDeviceUser);
425}
426
427
428
429/**
430 * Initialize module.
431 *
432 * @returns appropriate status code.
433 */
434static int __init vboxguestLinuxModInit(void)
435{
436 int rc;
437
438 /*
439 * Initialize IPRT first.
440 */
441 rc = RTR0Init(0);
442 if (RT_FAILURE(rc))
443 {
444 Log((DEVICE_NAME ": RTR0Init failed.\n"));
445 return -EINVAL;
446 }
447
448 /*
449 * Locate and initialize the PCI device.
450 */
451 rc = vboxguestLinuxInitPci();
452 if (rc >= 0)
453 {
454 /*
455 * Register the interrupt service routine for it.
456 */
457 rc = vboxguestLinuxInitISR();
458 if (rc >= 0)
459 {
460 /*
461 * Call the common device extension initializer.
462 */
463#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && defined(RT_ARCH_X86)
464 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26;
465#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && defined(RT_ARCH_AMD64)
466 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26_x64;
467#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) && defined(RT_ARCH_X86)
468 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24;
469#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) && defined(RT_ARCH_AMD64)
470 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24_x64;
471#else
472# warning "huh? which arch + version is this?"
473 VBOXOSTYPE enmOsType = VBOXOSTYPE_Linux;
474#endif
475 rc = VBoxGuestInitDevExt(&g_DevExt,
476 g_IOPortBase,
477 g_pvMMIOBase,
478 g_cbMMIO,
479 enmOSType);
480 if (RT_SUCCESS(rc))
481 {
482 /*
483 * Finally, create the device nodes.
484 */
485 rc = vboxguestLinuxInitDeviceNodes();
486 if (rc >= 0)
487 {
488 /* some useful information for the user but don't show this on the console */
489 LogRel(("VirtualBox device settings: major %d, IRQ %d, I/O port 0x%x, MMIO at 0x%x (size 0x%x)\n",
490 g_iModuleMajor, g_pPciDev->irq, g_IOPortBase, g_MMIOPhysAddr, g_cbMMIO));
491 printk(KERN_DEBUG DEVICE_NAME ": Successfully loaded version "
492 VBOX_VERSION_STRING " (interface " xstr(VMMDEV_VERSION) ")\n");
493 return rc;
494 }
495
496 /* bail out */
497 VBoxGuestDeleteDevExt(&g_DevExt);
498 }
499 else
500 {
501 LogRel(( DEVICE_NAME ": VBoxGuestInitDevExt failed with rc=%Rrc\n", rc));
502 rc = RTErrConvertFromErrno(rc);
503 }
504 vboxguestLinuxTermISR();
505 }
506 vboxguestLinuxTermPci();
507 }
508 RTR0Term();
509 return rc;
510}
511
512
513/**
514 * Unload the module.
515 */
516static void __exit vboxguestLinuxModExit(void)
517{
518 /*
519 * Inverse order of init.
520 */
521 vboxguestLinuxTermDeviceNodes();
522 VBoxGuestDeleteDevExt(&g_DevExt);
523 vboxguestLinuxTermISR();
524 vboxguestLinuxTermPci();
525 RTR0Term();
526}
527
528
529/**
530 * Device open. Called on open /dev/vboxdrv
531 *
532 * @param pInode Pointer to inode info structure.
533 * @param pFilp Associated file pointer.
534 */
535static int vboxguestLinuxOpen(struct inode *pInode, struct file *pFilp)
536{
537 int rc;
538 PVBOXGUESTSESSION pSession;
539 Log((DEVICE_NAME ": pFilp=%p pid=%d/%d %s\n", pFilp, RTProcSelf(), current->pid, current->comm));
540
541 /*
542 * Call common code to create the user session. Associate it with
543 * the file so we can access it in the other methods.
544 */
545 rc = VBoxGuestCreateUserSession(&g_DevExt, &pSession);
546 if (RT_SUCCESS(rc))
547 pFilp->private_data = pSession;
548
549 Log(("vboxguestLinuxOpen: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n",
550 &g_DevExt, pSession, rc, vboxguestLinuxConvertToNegErrno(rc),
551 RTProcSelf(), current->pid, current->comm));
552 return vboxguestLinuxConvertToNegErrno(rc);
553}
554
555
556/**
557 * Close device.
558 *
559 * @param pInode Pointer to inode info structure.
560 * @param pFilp Associated file pointer.
561 */
562static int vboxguestLinuxRelease(struct inode *pInode, struct file *pFilp)
563{
564 Log(("vboxguestLinuxRelease: pFilp=%p pSession=%p pid=%d/%d %s\n",
565 pFilp, pFilp->private_data, RTProcSelf(), current->pid, current->comm));
566
567 VBoxGuestCloseSession(&g_DevExt, (PVBOXGUESTSESSION)pFilp->private_data);
568 pFilp->private_data = NULL;
569 return 0;
570}
571
572
573/**
574 * Device I/O Control entry point.
575 *
576 * @param pFilp Associated file pointer.
577 * @param uCmd The function specified to ioctl().
578 * @param ulArg The argument specified to ioctl().
579 */
580#ifdef HAVE_UNLOCKED_IOCTL
581static long vboxguestLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
582#else
583static int vboxguestLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
584#endif
585{
586 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFilp->private_data;
587 uint32_t cbData = _IOC_SIZE(uCmd);
588 void *pvBufFree;
589 void *pvBuf;
590 int rc;
591 uint64_t au64Buf[32/sizeof(uint64_t)];
592
593 Log6(("vboxguestLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid));
594
595 /*
596 * Buffer the request.
597 */
598 if (cbData <= sizeof(au64Buf))
599 {
600 pvBufFree = NULL;
601 pvBuf = &au64Buf[0];
602 }
603 else
604 {
605 pvBufFree = pvBuf = RTMemTmpAlloc(cbData);
606 if (RT_UNLIKELY(!pvBuf))
607 {
608 LogRel((DEVICE_NAME "::IOCtl: RTMemTmpAlloc failed to alloc %u bytes.\n", cbData));
609 return -ENOMEM;
610 }
611 }
612 if (RT_LIKELY(copy_from_user(pvBuf, (void *)ulArg, cbData)))
613 {
614 /*
615 * Process the IOCtl.
616 */
617 size_t cbDataReturned;
618 rc = VBoxGuestCommonIOCtl(uCmd, &g_DevExt, pSession, pvBuf, cbData, &cbDataReturned);
619
620 /*
621 * Copy ioctl data and output buffer back to user space.
622 */
623 if (RT_LIKELY(!rc))
624 {
625 rc = 0;
626 if (RT_UNLIKELY(cbDataReturned > cbData))
627 {
628 LogRel((DEVICE_NAME "::IOCtl: too much output data %u expected %u\n", cbDataReturned, cbData));
629 cbDataReturned = cbData;
630 }
631 if (cbDataReturned > 0)
632 {
633 if (RT_UNLIKELY(copy_to_user((void *)ulArg, pvBuf, cbDataReturned)))
634 {
635 LogRel((DEVICE_NAME "::IOCtl: copy_to_user failed; pvBuf=%p ulArg=%p cbDataReturned=%u uCmd=%d\n",
636 pvBuf, (void *)ulArg, cbDataReturned, uCmd, rc));
637 rc = -EFAULT;
638 }
639 }
640 }
641 else
642 {
643 Log(("vboxguestLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc));
644 rc = vboxguestLinuxConvertToNegErrno(rc);
645 }
646 }
647 else
648 {
649 Log((DEVICE_NAME "::IOCtl: copy_from_user(,%#lx, %#x) failed; uCmd=%#x.\n", ulArg, cbData, uCmd));
650 rc = -EFAULT;
651 }
652 if (pvBufFree)
653 RTMemFree(pvBufFree);
654
655 Log6(("vboxguestLinuxIOCtl: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid));
656 return rc;
657}
658
659/* Common code that depend on g_DevExt. */
660#include "VBoxGuestIDC-unix.c.h"
661
662EXPORT_SYMBOL(VBoxGuestIDCOpen);
663EXPORT_SYMBOL(VBoxGuestIDCClose);
664EXPORT_SYMBOL(VBoxGuestIDCCall);
665
666module_init(vboxguestLinuxModInit);
667module_exit(vboxguestLinuxModExit);
668
669MODULE_AUTHOR("Sun Microsystems, Inc.");
670MODULE_DESCRIPTION("VirtualBox Guest Additions for Linux Module");
671MODULE_LICENSE("GPL");
672#ifdef MODULE_VERSION
673MODULE_VERSION(VBOX_VERSION_STRING " (" xstr(SUPDRV_IOC_VERSION) ")");
674#endif
675
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