VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c@ 57585

Last change on this file since 57585 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.3 KB
Line 
1/* $Rev: 57358 $ */
2/** @file
3 * VBoxDrv - The VirtualBox Support Driver - Linux specifics.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP LOG_GROUP_SUP_DRV
32#include "../SUPDrvInternal.h"
33#include "the-linux-kernel.h"
34#include "version-generated.h"
35#include "product-generated.h"
36
37#include <iprt/assert.h>
38#include <iprt/spinlock.h>
39#include <iprt/semaphore.h>
40#include <iprt/initterm.h>
41#include <iprt/process.h>
42#include <VBox/err.h>
43#include <iprt/mem.h>
44#include <VBox/log.h>
45#include <iprt/mp.h>
46
47/** @todo figure out the exact version number */
48#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16)
49# include <iprt/power.h>
50# define VBOX_WITH_SUSPEND_NOTIFICATION
51#endif
52
53#include <linux/sched.h>
54#ifdef CONFIG_DEVFS_FS
55# include <linux/devfs_fs_kernel.h>
56#endif
57#ifdef CONFIG_VBOXDRV_AS_MISC
58# include <linux/miscdevice.h>
59#endif
60#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
61# include <linux/platform_device.h>
62#endif
63#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) && defined(SUPDRV_WITH_MSR_PROBER)
64# define SUPDRV_LINUX_HAS_SAFE_MSR_API
65# include <asm/msr.h>
66#endif
67#include <iprt/asm-amd64-x86.h>
68
69
70/*********************************************************************************************************************************
71* Defined Constants And Macros *
72*********************************************************************************************************************************/
73/* check kernel version */
74# ifndef SUPDRV_AGNOSTIC
75# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
76# error Unsupported kernel version!
77# endif
78# endif
79
80/* devfs defines */
81#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC)
82# ifdef VBOX_WITH_HARDENING
83# define VBOX_DEV_FMASK (S_IWUSR | S_IRUSR)
84# else
85# define VBOX_DEV_FMASK (S_IRUGO | S_IWUGO)
86# endif
87#endif /* CONFIG_DEV_FS && !CONFIG_VBOXDEV_AS_MISC */
88
89#ifdef CONFIG_X86_HIGH_ENTRY
90# error "CONFIG_X86_HIGH_ENTRY is not supported by VBoxDrv at this time."
91#endif
92
93/* We cannot include x86.h, so we copy the defines we need here: */
94#define X86_EFL_IF RT_BIT(9)
95#define X86_EFL_AC RT_BIT(18)
96#define X86_EFL_DF RT_BIT(10)
97#define X86_EFL_IOPL (RT_BIT(12) | RT_BIT(13))
98
99/* To include the version number of VirtualBox into kernel backtraces: */
100#define VBoxDrvLinuxVersion RT_CONCAT3(RT_CONCAT(VBOX_VERSION_MAJOR, _), \
101 RT_CONCAT(VBOX_VERSION_MINOR, _), \
102 VBOX_VERSION_BUILD)
103#define VBoxDrvLinuxIOCtl RT_CONCAT(VBoxDrvLinuxIOCtl_,VBoxDrvLinuxVersion)
104
105
106
107/*********************************************************************************************************************************
108* Internal Functions *
109*********************************************************************************************************************************/
110static int VBoxDrvLinuxInit(void);
111static void VBoxDrvLinuxUnload(void);
112static int VBoxDrvLinuxCreateSys(struct inode *pInode, struct file *pFilp);
113static int VBoxDrvLinuxCreateUsr(struct inode *pInode, struct file *pFilp);
114static int VBoxDrvLinuxClose(struct inode *pInode, struct file *pFilp);
115#ifdef HAVE_UNLOCKED_IOCTL
116static long VBoxDrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
117#else
118static int VBoxDrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
119#endif
120static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PSUPDRVSESSION pSession);
121static int VBoxDrvLinuxErr2LinuxErr(int);
122#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
123static int VBoxDrvProbe(struct platform_device *pDev);
124# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
125static int VBoxDrvSuspend(struct device *pDev);
126static int VBoxDrvResume(struct device *pDev);
127# else
128static int VBoxDrvSuspend(struct platform_device *pDev, pm_message_t State);
129static int VBoxDrvResume(struct platform_device *pDev);
130# endif
131static void VBoxDevRelease(struct device *pDev);
132#endif
133
134
135/*********************************************************************************************************************************
136* Global Variables *
137*********************************************************************************************************************************/
138/**
139 * Device extention & session data association structure.
140 */
141static SUPDRVDEVEXT g_DevExt;
142
143#ifndef CONFIG_VBOXDRV_AS_MISC
144/** Module major number for vboxdrv. */
145#define DEVICE_MAJOR_SYS 234
146/** Saved major device number for vboxdrv. */
147static int g_iModuleMajorSys;
148/** Module major number for vboxdrvu. */
149#define DEVICE_MAJOR_USR 235
150/** Saved major device number for vboxdrvu. */
151static int g_iModuleMajorUsr;
152#endif /* !CONFIG_VBOXDRV_AS_MISC */
153
154/** Module parameter.
155 * Not prefixed because the name is used by macros and the end of this file. */
156static int force_async_tsc = 0;
157
158/** The system device name. */
159#define DEVICE_NAME_SYS "vboxdrv"
160/** The user device name. */
161#define DEVICE_NAME_USR "vboxdrvu"
162
163#if (defined(RT_ARCH_AMD64) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)) || defined(VBOX_WITH_TEXT_MODMEM_HACK)
164/**
165 * Memory for the executable memory heap (in IPRT).
166 */
167# ifdef DEBUG
168# define EXEC_MEMORY_SIZE 6291456 /* 6 MB */
169# else
170# define EXEC_MEMORY_SIZE 1572864 /* 1.5 MB */
171# endif
172extern uint8_t g_abExecMemory[EXEC_MEMORY_SIZE];
173# ifndef VBOX_WITH_TEXT_MODMEM_HACK
174__asm__(".section execmemory, \"awx\", @progbits\n\t"
175 ".align 32\n\t"
176 ".globl g_abExecMemory\n"
177 "g_abExecMemory:\n\t"
178 ".zero " RT_XSTR(EXEC_MEMORY_SIZE) "\n\t"
179 ".type g_abExecMemory, @object\n\t"
180 ".size g_abExecMemory, " RT_XSTR(EXEC_MEMORY_SIZE) "\n\t"
181 ".text\n\t");
182# else
183__asm__(".text\n\t"
184 ".align 4096\n\t"
185 ".globl g_abExecMemory\n"
186 "g_abExecMemory:\n\t"
187 ".zero " RT_XSTR(EXEC_MEMORY_SIZE) "\n\t"
188 ".type g_abExecMemory, @object\n\t"
189 ".size g_abExecMemory, " RT_XSTR(EXEC_MEMORY_SIZE) "\n\t"
190 ".text\n\t");
191# endif
192#endif
193
194/** The file_operations structure. */
195static struct file_operations gFileOpsVBoxDrvSys =
196{
197 owner: THIS_MODULE,
198 open: VBoxDrvLinuxCreateSys,
199 release: VBoxDrvLinuxClose,
200#ifdef HAVE_UNLOCKED_IOCTL
201 unlocked_ioctl: VBoxDrvLinuxIOCtl,
202#else
203 ioctl: VBoxDrvLinuxIOCtl,
204#endif
205};
206
207/** The file_operations structure. */
208static struct file_operations gFileOpsVBoxDrvUsr =
209{
210 owner: THIS_MODULE,
211 open: VBoxDrvLinuxCreateUsr,
212 release: VBoxDrvLinuxClose,
213#ifdef HAVE_UNLOCKED_IOCTL
214 unlocked_ioctl: VBoxDrvLinuxIOCtl,
215#else
216 ioctl: VBoxDrvLinuxIOCtl,
217#endif
218};
219
220#ifdef CONFIG_VBOXDRV_AS_MISC
221/** The miscdevice structure for vboxdrv. */
222static struct miscdevice gMiscDeviceSys =
223{
224 minor: MISC_DYNAMIC_MINOR,
225 name: DEVICE_NAME_SYS,
226 fops: &gFileOpsVBoxDrvSys,
227# if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 17)
228 devfs_name: DEVICE_NAME_SYS,
229# endif
230};
231/** The miscdevice structure for vboxdrvu. */
232static struct miscdevice gMiscDeviceUsr =
233{
234 minor: MISC_DYNAMIC_MINOR,
235 name: DEVICE_NAME_USR,
236 fops: &gFileOpsVBoxDrvUsr,
237# if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 17)
238 devfs_name: DEVICE_NAME_USR,
239# endif
240};
241#endif
242
243
244#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
245# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
246static struct dev_pm_ops gPlatformPMOps =
247{
248 .suspend = VBoxDrvSuspend, /* before entering deep sleep */
249 .resume = VBoxDrvResume, /* after wakeup from deep sleep */
250 .freeze = VBoxDrvSuspend, /* before creating hibernation image */
251 .restore = VBoxDrvResume, /* after waking up from hibernation */
252};
253# endif
254
255static struct platform_driver gPlatformDriver =
256{
257 .probe = VBoxDrvProbe,
258# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
259 .suspend = VBoxDrvSuspend,
260 .resume = VBoxDrvResume,
261# endif
262 /** @todo .shutdown? */
263 .driver =
264 {
265 .name = "vboxdrv",
266# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
267 .pm = &gPlatformPMOps,
268# endif
269 }
270};
271
272static struct platform_device gPlatformDevice =
273{
274 .name = "vboxdrv",
275 .dev =
276 {
277 .release = VBoxDevRelease
278 }
279};
280#endif /* VBOX_WITH_SUSPEND_NOTIFICATION */
281
282
283DECLINLINE(RTUID) vboxdrvLinuxUid(void)
284{
285#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
286# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
287 return from_kuid(current_user_ns(), current->cred->uid);
288# else
289 return current->cred->uid;
290# endif
291#else
292 return current->uid;
293#endif
294}
295
296DECLINLINE(RTGID) vboxdrvLinuxGid(void)
297{
298#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
299# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
300 return from_kgid(current_user_ns(), current->cred->gid);
301# else
302 return current->cred->gid;
303# endif
304#else
305 return current->gid;
306#endif
307}
308
309DECLINLINE(RTUID) vboxdrvLinuxEuid(void)
310{
311#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
312# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
313 return from_kuid(current_user_ns(), current->cred->euid);
314# else
315 return current->cred->euid;
316# endif
317#else
318 return current->euid;
319#endif
320}
321
322/**
323 * Initialize module.
324 *
325 * @returns appropriate status code.
326 */
327static int __init VBoxDrvLinuxInit(void)
328{
329 int rc;
330
331 /*
332 * Check for synchronous/asynchronous TSC mode.
333 */
334 printk(KERN_DEBUG "vboxdrv: Found %u processor cores\n", (unsigned)RTMpGetOnlineCount());
335#ifdef CONFIG_VBOXDRV_AS_MISC
336 rc = misc_register(&gMiscDeviceSys);
337 if (rc)
338 {
339 printk(KERN_ERR "vboxdrv: Can't register system misc device! rc=%d\n", rc);
340 return rc;
341 }
342 rc = misc_register(&gMiscDeviceUsr);
343 if (rc)
344 {
345 printk(KERN_ERR "vboxdrv: Can't register user misc device! rc=%d\n", rc);
346 misc_deregister(&gMiscDeviceSys);
347 return rc;
348 }
349#else /* !CONFIG_VBOXDRV_AS_MISC */
350 /*
351 * Register character devices and save the returned major numbers.
352 */
353 /* /dev/vboxdrv */
354 g_iModuleMajorSys = DEVICE_MAJOR_SYS;
355 rc = register_chrdev((dev_t)g_iModuleMajorSys, DEVICE_NAME_SYS, &gFileOpsVBoxDrvSys);
356 if (rc < 0)
357 {
358 Log(("register_chrdev() failed with rc=%#x for vboxdrv!\n", rc));
359 return rc;
360 }
361 if (DEVICE_MAJOR_SYS != 0)
362 g_iModuleMajorSys = DEVICE_MAJOR_SYS;
363 else
364 g_iModuleMajorSys = rc;
365
366 /* /dev/vboxdrvu */
367 /** @todo Use a minor number of this bugger (not sure if this code is used
368 * though, so not bothering right now.) */
369 g_iModuleMajorUsr = DEVICE_MAJOR_USR;
370 rc = register_chrdev((dev_t)g_iModuleMajorUsr, DEVICE_NAME_USR, &gFileOpsVBoxDrvUsr);
371 if (rc < 0)
372 {
373 Log(("register_chrdev() failed with rc=%#x for vboxdrv!\n", rc));
374 return rc;
375 }
376 if (DEVICE_MAJOR_USR != 0)
377 g_iModuleMajorUsr = DEVICE_MAJOR_USR;
378 else
379 g_iModuleMajorUsr = rc;
380 rc = 0;
381
382# ifdef CONFIG_DEVFS_FS
383 /*
384 * Register a device entry
385 */
386 if ( devfs_mk_cdev(MKDEV(DEVICE_MAJOR_SYS, 0), S_IFCHR | VBOX_DEV_FMASK, DEVICE_NAME_SYS) != 0
387 || devfs_mk_cdev(MKDEV(DEVICE_MAJOR_USR, 0), S_IFCHR | VBOX_DEV_FMASK, DEVICE_NAME_USR) != 0)
388 {
389 Log(("devfs_register failed!\n"));
390 rc = -EINVAL;
391 }
392# endif
393#endif /* !CONFIG_VBOXDRV_AS_MISC */
394 if (!rc)
395 {
396 /*
397 * Initialize the runtime.
398 * On AMD64 we'll have to donate the high rwx memory block to the exec allocator.
399 */
400 rc = RTR0Init(0);
401 if (RT_SUCCESS(rc))
402 {
403#if (defined(RT_ARCH_AMD64) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)) || defined(VBOX_WITH_TEXT_MODMEM_HACK)
404# ifdef VBOX_WITH_TEXT_MODMEM_HACK
405 set_memory_x(&g_abExecMemory[0], sizeof(g_abExecMemory) / PAGE_SIZE);
406 set_memory_rw(&g_abExecMemory[0], sizeof(g_abExecMemory) / PAGE_SIZE);
407# endif
408 rc = RTR0MemExecDonate(&g_abExecMemory[0], sizeof(g_abExecMemory));
409 printk(KERN_DEBUG "VBoxDrv: dbg - g_abExecMemory=%p\n", (void *)&g_abExecMemory[0]);
410#endif
411 Log(("VBoxDrv::ModuleInit\n"));
412
413 /*
414 * Initialize the device extension.
415 */
416 if (RT_SUCCESS(rc))
417 rc = supdrvInitDevExt(&g_DevExt, sizeof(SUPDRVSESSION));
418 if (RT_SUCCESS(rc))
419 {
420#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
421 rc = platform_driver_register(&gPlatformDriver);
422 if (rc == 0)
423 {
424 rc = platform_device_register(&gPlatformDevice);
425 if (rc == 0)
426#endif
427 {
428 printk(KERN_INFO "vboxdrv: TSC mode is %s, tentative frequency %llu Hz\n",
429 SUPGetGIPModeName(g_DevExt.pGip), g_DevExt.pGip->u64CpuHz);
430 LogFlow(("VBoxDrv::ModuleInit returning %#x\n", rc));
431 printk(KERN_DEBUG "vboxdrv: Successfully loaded version "
432 VBOX_VERSION_STRING " (interface " RT_XSTR(SUPDRV_IOC_VERSION) ")\n");
433 return rc;
434 }
435#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
436 else
437 platform_driver_unregister(&gPlatformDriver);
438 }
439#endif
440 }
441
442 rc = -EINVAL;
443 RTR0TermForced();
444 }
445 else
446 rc = -EINVAL;
447
448 /*
449 * Failed, cleanup and return the error code.
450 */
451#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC)
452 devfs_remove(DEVICE_NAME_SYS);
453 devfs_remove(DEVICE_NAME_USR);
454#endif
455 }
456#ifdef CONFIG_VBOXDRV_AS_MISC
457 misc_deregister(&gMiscDeviceSys);
458 misc_deregister(&gMiscDeviceUsr);
459 Log(("VBoxDrv::ModuleInit returning %#x (minor:%d & %d)\n", rc, gMiscDeviceSys.minor, gMiscDeviceUsr.minor));
460#else
461 unregister_chrdev(g_iModuleMajorUsr, DEVICE_NAME_USR);
462 unregister_chrdev(g_iModuleMajorSys, DEVICE_NAME_SYS);
463 Log(("VBoxDrv::ModuleInit returning %#x (major:%d & %d)\n", rc, g_iModuleMajorSys, g_iModuleMajorUsr));
464#endif
465 return rc;
466}
467
468
469/**
470 * Unload the module.
471 */
472static void __exit VBoxDrvLinuxUnload(void)
473{
474 int rc;
475 Log(("VBoxDrvLinuxUnload\n"));
476 NOREF(rc);
477
478#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
479 platform_device_unregister(&gPlatformDevice);
480 platform_driver_unregister(&gPlatformDriver);
481#endif
482
483 /*
484 * I Don't think it's possible to unload a driver which processes have
485 * opened, at least we'll blindly assume that here.
486 */
487#ifdef CONFIG_VBOXDRV_AS_MISC
488 rc = misc_deregister(&gMiscDeviceUsr);
489 if (rc < 0)
490 {
491 Log(("misc_deregister failed with rc=%#x on vboxdrvu\n", rc));
492 }
493 rc = misc_deregister(&gMiscDeviceSys);
494 if (rc < 0)
495 {
496 Log(("misc_deregister failed with rc=%#x on vboxdrv\n", rc));
497 }
498#else /* !CONFIG_VBOXDRV_AS_MISC */
499# ifdef CONFIG_DEVFS_FS
500 /*
501 * Unregister a device entry
502 */
503 devfs_remove(DEVICE_NAME_USR);
504 devfs_remove(DEVICE_NAME_SYS);
505# endif /* devfs */
506 unregister_chrdev(g_iModuleMajorUsr, DEVICE_NAME_USR);
507 unregister_chrdev(g_iModuleMajorSys, DEVICE_NAME_SYS);
508#endif /* !CONFIG_VBOXDRV_AS_MISC */
509
510 /*
511 * Destroy GIP, delete the device extension and terminate IPRT.
512 */
513 supdrvDeleteDevExt(&g_DevExt);
514 RTR0TermForced();
515}
516
517
518/**
519 * Common open code.
520 *
521 * @param pInode Pointer to inode info structure.
522 * @param pFilp Associated file pointer.
523 * @param fUnrestricted Indicates which device node which was opened.
524 */
525static int vboxdrvLinuxCreateCommon(struct inode *pInode, struct file *pFilp, bool fUnrestricted)
526{
527 int rc;
528 PSUPDRVSESSION pSession;
529 Log(("VBoxDrvLinuxCreate: pFilp=%p pid=%d/%d %s\n", pFilp, RTProcSelf(), current->pid, current->comm));
530
531#ifdef VBOX_WITH_HARDENING
532 /*
533 * Only root is allowed to access the unrestricted device, enforce it!
534 */
535 if ( fUnrestricted
536 && vboxdrvLinuxEuid() != 0 /* root */ )
537 {
538 Log(("VBoxDrvLinuxCreate: euid=%d, expected 0 (root)\n", vboxdrvLinuxEuid()));
539 return -EPERM;
540 }
541#endif /* VBOX_WITH_HARDENING */
542
543 /*
544 * Call common code for the rest.
545 */
546 rc = supdrvCreateSession(&g_DevExt, true /* fUser */, fUnrestricted, &pSession);
547 if (!rc)
548 {
549 pSession->Uid = vboxdrvLinuxUid();
550 pSession->Gid = vboxdrvLinuxGid();
551 }
552
553 pFilp->private_data = pSession;
554
555 Log(("VBoxDrvLinuxCreate: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n",
556 &g_DevExt, pSession, rc, VBoxDrvLinuxErr2LinuxErr(rc),
557 RTProcSelf(), current->pid, current->comm));
558 return VBoxDrvLinuxErr2LinuxErr(rc);
559}
560
561
562/** /dev/vboxdrv. */
563static int VBoxDrvLinuxCreateSys(struct inode *pInode, struct file *pFilp)
564{
565 return vboxdrvLinuxCreateCommon(pInode, pFilp, true);
566}
567
568
569/** /dev/vboxdrvu. */
570static int VBoxDrvLinuxCreateUsr(struct inode *pInode, struct file *pFilp)
571{
572 return vboxdrvLinuxCreateCommon(pInode, pFilp, false);
573}
574
575
576/**
577 * Close device.
578 *
579 * @param pInode Pointer to inode info structure.
580 * @param pFilp Associated file pointer.
581 */
582static int VBoxDrvLinuxClose(struct inode *pInode, struct file *pFilp)
583{
584 Log(("VBoxDrvLinuxClose: pFilp=%p pSession=%p pid=%d/%d %s\n",
585 pFilp, pFilp->private_data, RTProcSelf(), current->pid, current->comm));
586 supdrvSessionRelease((PSUPDRVSESSION)pFilp->private_data);
587 pFilp->private_data = NULL;
588 return 0;
589}
590
591
592#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
593/**
594 * Dummy device release function. We have to provide this function,
595 * otherwise the kernel will complain.
596 *
597 * @param pDev Pointer to the platform device.
598 */
599static void VBoxDevRelease(struct device *pDev)
600{
601}
602
603/**
604 * Dummy probe function.
605 *
606 * @param pDev Pointer to the platform device.
607 */
608static int VBoxDrvProbe(struct platform_device *pDev)
609{
610 return 0;
611}
612
613/**
614 * Suspend callback.
615 * @param pDev Pointer to the platform device.
616 * @param State message type, see Documentation/power/devices.txt.
617 */
618# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
619static int VBoxDrvSuspend(struct device *pDev)
620# else
621static int VBoxDrvSuspend(struct platform_device *pDev, pm_message_t State)
622# endif
623{
624 RTPowerSignalEvent(RTPOWEREVENT_SUSPEND);
625 return 0;
626}
627
628/**
629 * Resume callback.
630 *
631 * @param pDev Pointer to the platform device.
632 */
633# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
634static int VBoxDrvResume(struct device *pDev)
635# else
636static int VBoxDrvResume(struct platform_device *pDev)
637# endif
638{
639 RTPowerSignalEvent(RTPOWEREVENT_RESUME);
640 return 0;
641}
642#endif /* VBOX_WITH_SUSPEND_NOTIFICATION */
643
644
645/**
646 * Device I/O Control entry point.
647 *
648 * @param pFilp Associated file pointer.
649 * @param uCmd The function specified to ioctl().
650 * @param ulArg The argument specified to ioctl().
651 */
652#ifdef HAVE_UNLOCKED_IOCTL
653static long VBoxDrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
654#else
655static int VBoxDrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
656#endif
657{
658 PSUPDRVSESSION pSession = (PSUPDRVSESSION)pFilp->private_data;
659 int rc;
660#if defined(VBOX_STRICT) || defined(VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV)
661 RTCCUINTREG fSavedEfl;
662
663 /*
664 * Refuse all I/O control calls if we've ever detected EFLAGS.AC being cleared.
665 *
666 * This isn't a problem, as there is absolutely nothing in the kernel context that
667 * depend on user context triggering cleanups. That would be pretty wild, right?
668 */
669 if (RT_UNLIKELY(g_DevExt.cBadContextCalls > 0))
670 {
671 SUPR0Printf("VBoxDrvLinuxIOCtl: EFLAGS.AC=0 detected %u times, refusing all I/O controls!\n", g_DevExt.cBadContextCalls);
672 return ESPIPE;
673 }
674
675 fSavedEfl = ASMAddFlags(X86_EFL_AC);
676# else
677 stac();
678# endif
679
680 /*
681 * Deal with the two high-speed IOCtl that takes it's arguments from
682 * the session and iCmd, and only returns a VBox status code.
683 */
684#ifdef HAVE_UNLOCKED_IOCTL
685 if (RT_LIKELY( ( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN
686 || uCmd == SUP_IOCTL_FAST_DO_HM_RUN
687 || uCmd == SUP_IOCTL_FAST_DO_NOP)
688 && pSession->fUnrestricted == true))
689 rc = supdrvIOCtlFast(uCmd, ulArg, &g_DevExt, pSession);
690 else
691 rc = VBoxDrvLinuxIOCtlSlow(pFilp, uCmd, ulArg, pSession);
692#else /* !HAVE_UNLOCKED_IOCTL */
693 unlock_kernel();
694 if (RT_LIKELY( ( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN
695 || uCmd == SUP_IOCTL_FAST_DO_HM_RUN
696 || uCmd == SUP_IOCTL_FAST_DO_NOP)
697 && pSession->fUnrestricted == true))
698 rc = supdrvIOCtlFast(uCmd, ulArg, &g_DevExt, pSession);
699 else
700 rc = VBoxDrvLinuxIOCtlSlow(pFilp, uCmd, ulArg, pSession);
701 lock_kernel();
702#endif /* !HAVE_UNLOCKED_IOCTL */
703
704#if defined(VBOX_STRICT) || defined(VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV)
705 /*
706 * Before we restore AC and the rest of EFLAGS, check if the IOCtl handler code
707 * accidentially modified it or some other important flag.
708 */
709 if (RT_UNLIKELY( (ASMGetFlags() & (X86_EFL_AC | X86_EFL_IF | X86_EFL_DF | X86_EFL_IOPL))
710 != ((fSavedEfl & (X86_EFL_AC | X86_EFL_IF | X86_EFL_DF | X86_EFL_IOPL)) | X86_EFL_AC) ))
711 {
712 char szTmp[48];
713 RTStrPrintf(szTmp, sizeof(szTmp), "uCmd=%#x: %#x->%#x!", _IOC_NR(uCmd), (uint32_t)fSavedEfl, (uint32_t)ASMGetFlags());
714 supdrvBadContext(&g_DevExt, "SUPDrv-linux.c", __LINE__, szTmp);
715 }
716 ASMSetFlags(fSavedEfl);
717#else
718 clac();
719#endif
720 return rc;
721}
722
723
724/**
725 * Device I/O Control entry point.
726 *
727 * @param pFilp Associated file pointer.
728 * @param uCmd The function specified to ioctl().
729 * @param ulArg The argument specified to ioctl().
730 * @param pSession The session instance.
731 */
732static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PSUPDRVSESSION pSession)
733{
734 int rc;
735 SUPREQHDR Hdr;
736 PSUPREQHDR pHdr;
737 uint32_t cbBuf;
738
739 Log6(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid));
740
741 /*
742 * Read the header.
743 */
744 if (RT_FAILURE(RTR0MemUserCopyFrom(&Hdr, ulArg, sizeof(Hdr))))
745 {
746 Log(("VBoxDrvLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x\n", ulArg, uCmd));
747 return -EFAULT;
748 }
749 if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
750 {
751 Log(("VBoxDrvLinuxIOCtl: bad header magic %#x; uCmd=%#x\n", Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK, uCmd));
752 return -EINVAL;
753 }
754
755 /*
756 * Buffer the request.
757 */
758 cbBuf = RT_MAX(Hdr.cbIn, Hdr.cbOut);
759 if (RT_UNLIKELY(cbBuf > _1M*16))
760 {
761 Log(("VBoxDrvLinuxIOCtl: too big cbBuf=%#x; uCmd=%#x\n", cbBuf, uCmd));
762 return -E2BIG;
763 }
764 if (RT_UNLIKELY(_IOC_SIZE(uCmd) ? cbBuf != _IOC_SIZE(uCmd) : Hdr.cbIn < sizeof(Hdr)))
765 {
766 Log(("VBoxDrvLinuxIOCtl: bad ioctl cbBuf=%#x _IOC_SIZE=%#x; uCmd=%#x\n", cbBuf, _IOC_SIZE(uCmd), uCmd));
767 return -EINVAL;
768 }
769 pHdr = RTMemAlloc(cbBuf);
770 if (RT_UNLIKELY(!pHdr))
771 {
772 OSDBGPRINT(("VBoxDrvLinuxIOCtl: failed to allocate buffer of %d bytes for uCmd=%#x\n", cbBuf, uCmd));
773 return -ENOMEM;
774 }
775 if (RT_FAILURE(RTR0MemUserCopyFrom(pHdr, ulArg, Hdr.cbIn)))
776 {
777 Log(("VBoxDrvLinuxIOCtl: copy_from_user(,%#lx, %#x) failed; uCmd=%#x\n", ulArg, Hdr.cbIn, uCmd));
778 RTMemFree(pHdr);
779 return -EFAULT;
780 }
781 if (Hdr.cbIn < cbBuf)
782 RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbBuf - Hdr.cbIn);
783
784 /*
785 * Process the IOCtl.
786 */
787 rc = supdrvIOCtl(uCmd, &g_DevExt, pSession, pHdr, cbBuf);
788
789 /*
790 * Copy ioctl data and output buffer back to user space.
791 */
792 if (RT_LIKELY(!rc))
793 {
794 uint32_t cbOut = pHdr->cbOut;
795 if (RT_UNLIKELY(cbOut > cbBuf))
796 {
797 OSDBGPRINT(("VBoxDrvLinuxIOCtl: too much output! %#x > %#x; uCmd=%#x!\n", cbOut, cbBuf, uCmd));
798 cbOut = cbBuf;
799 }
800 if (RT_FAILURE(RTR0MemUserCopyTo(ulArg, pHdr, cbOut)))
801 {
802 /* this is really bad! */
803 OSDBGPRINT(("VBoxDrvLinuxIOCtl: copy_to_user(%#lx,,%#x); uCmd=%#x!\n", ulArg, cbOut, uCmd));
804 rc = -EFAULT;
805 }
806 }
807 else
808 {
809 Log(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc));
810 rc = -EINVAL;
811 }
812 RTMemFree(pHdr);
813
814 Log6(("VBoxDrvLinuxIOCtl: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid));
815 return rc;
816}
817
818
819/**
820 * The SUPDRV IDC entry point.
821 *
822 * @returns VBox status code, see supdrvIDC.
823 * @param iReq The request code.
824 * @param pReq The request.
825 */
826int VBOXCALL SUPDrvLinuxIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq)
827{
828 PSUPDRVSESSION pSession;
829
830 /*
831 * Some quick validations.
832 */
833 if (RT_UNLIKELY(!VALID_PTR(pReq)))
834 return VERR_INVALID_POINTER;
835
836 pSession = pReq->pSession;
837 if (pSession)
838 {
839 if (RT_UNLIKELY(!VALID_PTR(pSession)))
840 return VERR_INVALID_PARAMETER;
841 if (RT_UNLIKELY(pSession->pDevExt != &g_DevExt))
842 return VERR_INVALID_PARAMETER;
843 }
844 else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT))
845 return VERR_INVALID_PARAMETER;
846
847 /*
848 * Do the job.
849 */
850 return supdrvIDC(uReq, &g_DevExt, pSession, pReq);
851}
852
853EXPORT_SYMBOL(SUPDrvLinuxIDC);
854
855
856RTCCUINTREG VBOXCALL supdrvOSChangeCR4(RTCCUINTREG fOrMask, RTCCUINTREG fAndMask)
857{
858#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 20, 0)
859 RTCCUINTREG uOld = this_cpu_read(cpu_tlbstate.cr4);
860 RTCCUINTREG uNew = (uOld & fAndMask) | fOrMask;
861 if (uNew != uOld)
862 {
863 this_cpu_write(cpu_tlbstate.cr4, uNew);
864 __write_cr4(uNew);
865 }
866#else
867 RTCCUINTREG uOld = ASMGetCR4();
868 RTCCUINTREG uNew = (uOld & fAndMask) | fOrMask;
869 if (uNew != uOld)
870 ASMSetCR4(uNew);
871#endif
872 return uOld;
873}
874
875
876void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
877{
878 NOREF(pDevExt);
879 NOREF(pSession);
880}
881
882
883void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
884{
885 NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
886}
887
888
889void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
890{
891 NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
892}
893
894
895/**
896 * Initializes any OS specific object creator fields.
897 */
898void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
899{
900 NOREF(pObj);
901 NOREF(pSession);
902}
903
904
905/**
906 * Checks if the session can access the object.
907 *
908 * @returns true if a decision has been made.
909 * @returns false if the default access policy should be applied.
910 *
911 * @param pObj The object in question.
912 * @param pSession The session wanting to access the object.
913 * @param pszObjName The object name, can be NULL.
914 * @param prc Where to store the result when returning true.
915 */
916bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
917{
918 NOREF(pObj);
919 NOREF(pSession);
920 NOREF(pszObjName);
921 NOREF(prc);
922 return false;
923}
924
925
926bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt)
927{
928 return force_async_tsc != 0;
929}
930
931
932bool VBOXCALL supdrvOSAreCpusOfflinedOnSuspend(void)
933{
934 return true;
935}
936
937
938bool VBOXCALL supdrvOSAreTscDeltasInSync(void)
939{
940 return false;
941}
942
943
944int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
945{
946 NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
947 return VERR_NOT_SUPPORTED;
948}
949
950
951void VBOXCALL supdrvOSLdrNotifyOpened(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
952{
953 NOREF(pDevExt); NOREF(pImage);
954}
955
956
957int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv, const uint8_t *pbImageBits)
958{
959 NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits);
960 return VERR_NOT_SUPPORTED;
961}
962
963
964int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq)
965{
966 NOREF(pDevExt); NOREF(pImage); NOREF(pbImageBits); NOREF(pReq);
967 return VERR_NOT_SUPPORTED;
968}
969
970
971void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
972{
973 NOREF(pDevExt); NOREF(pImage);
974}
975
976
977#ifdef SUPDRV_WITH_MSR_PROBER
978
979int VBOXCALL supdrvOSMsrProberRead(uint32_t uMsr, RTCPUID idCpu, uint64_t *puValue)
980{
981# ifdef SUPDRV_LINUX_HAS_SAFE_MSR_API
982 uint32_t u32Low, u32High;
983 int rc;
984
985 IPRT_LINUX_SAVE_EFL_AC();
986 if (idCpu == NIL_RTCPUID)
987 rc = rdmsr_safe(uMsr, &u32Low, &u32High);
988 else if (RTMpIsCpuOnline(idCpu))
989 rc = rdmsr_safe_on_cpu(idCpu, uMsr, &u32Low, &u32High);
990 else
991 return VERR_CPU_OFFLINE;
992 IPRT_LINUX_RESTORE_EFL_AC();
993 if (rc == 0)
994 {
995 *puValue = RT_MAKE_U64(u32Low, u32High);
996 return VINF_SUCCESS;
997 }
998 return VERR_ACCESS_DENIED;
999# else
1000 return VERR_NOT_SUPPORTED;
1001# endif
1002}
1003
1004
1005int VBOXCALL supdrvOSMsrProberWrite(uint32_t uMsr, RTCPUID idCpu, uint64_t uValue)
1006{
1007# ifdef SUPDRV_LINUX_HAS_SAFE_MSR_API
1008 int rc;
1009
1010 IPRT_LINUX_SAVE_EFL_AC();
1011 if (idCpu == NIL_RTCPUID)
1012 rc = wrmsr_safe(uMsr, RT_LODWORD(uValue), RT_HIDWORD(uValue));
1013 else if (RTMpIsCpuOnline(idCpu))
1014 rc = wrmsr_safe_on_cpu(idCpu, uMsr, RT_LODWORD(uValue), RT_HIDWORD(uValue));
1015 else
1016 return VERR_CPU_OFFLINE;
1017 IPRT_LINUX_RESTORE_EFL_AC();
1018
1019 if (rc == 0)
1020 return VINF_SUCCESS;
1021 return VERR_ACCESS_DENIED;
1022# else
1023 return VERR_NOT_SUPPORTED;
1024# endif
1025}
1026
1027# ifdef SUPDRV_LINUX_HAS_SAFE_MSR_API
1028/**
1029 * Worker for supdrvOSMsrProberModify.
1030 */
1031static DECLCALLBACK(void) supdrvLnxMsrProberModifyOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
1032{
1033 PSUPMSRPROBER pReq = (PSUPMSRPROBER)pvUser1;
1034 register uint32_t uMsr = pReq->u.In.uMsr;
1035 bool const fFaster = pReq->u.In.enmOp == SUPMSRPROBEROP_MODIFY_FASTER;
1036 uint64_t uBefore;
1037 uint64_t uWritten;
1038 uint64_t uAfter;
1039 int rcBefore, rcWrite, rcAfter, rcRestore;
1040 RTCCUINTREG fOldFlags;
1041
1042 /* Initialize result variables. */
1043 uBefore = uWritten = uAfter = 0;
1044 rcWrite = rcAfter = rcRestore = -EIO;
1045
1046 /*
1047 * Do the job.
1048 */
1049 fOldFlags = ASMIntDisableFlags();
1050 ASMCompilerBarrier(); /* paranoia */
1051 if (!fFaster)
1052 ASMWriteBackAndInvalidateCaches();
1053
1054 rcBefore = rdmsrl_safe(uMsr, &uBefore);
1055 if (rcBefore >= 0)
1056 {
1057 register uint64_t uRestore = uBefore;
1058 uWritten = uRestore;
1059 uWritten &= pReq->u.In.uArgs.Modify.fAndMask;
1060 uWritten |= pReq->u.In.uArgs.Modify.fOrMask;
1061
1062 rcWrite = wrmsr_safe(uMsr, RT_LODWORD(uWritten), RT_HIDWORD(uWritten));
1063 rcAfter = rdmsrl_safe(uMsr, &uAfter);
1064 rcRestore = wrmsr_safe(uMsr, RT_LODWORD(uRestore), RT_HIDWORD(uRestore));
1065
1066 if (!fFaster)
1067 {
1068 ASMWriteBackAndInvalidateCaches();
1069 ASMReloadCR3();
1070 ASMNopPause();
1071 }
1072 }
1073
1074 ASMCompilerBarrier(); /* paranoia */
1075 ASMSetFlags(fOldFlags);
1076
1077 /*
1078 * Write out the results.
1079 */
1080 pReq->u.Out.uResults.Modify.uBefore = uBefore;
1081 pReq->u.Out.uResults.Modify.uWritten = uWritten;
1082 pReq->u.Out.uResults.Modify.uAfter = uAfter;
1083 pReq->u.Out.uResults.Modify.fBeforeGp = rcBefore != 0;
1084 pReq->u.Out.uResults.Modify.fModifyGp = rcWrite != 0;
1085 pReq->u.Out.uResults.Modify.fAfterGp = rcAfter != 0;
1086 pReq->u.Out.uResults.Modify.fRestoreGp = rcRestore != 0;
1087 RT_ZERO(pReq->u.Out.uResults.Modify.afReserved);
1088}
1089# endif
1090
1091
1092int VBOXCALL supdrvOSMsrProberModify(RTCPUID idCpu, PSUPMSRPROBER pReq)
1093{
1094# ifdef SUPDRV_LINUX_HAS_SAFE_MSR_API
1095 if (idCpu == NIL_RTCPUID)
1096 {
1097 supdrvLnxMsrProberModifyOnCpu(idCpu, pReq, NULL);
1098 return VINF_SUCCESS;
1099 }
1100 return RTMpOnSpecific(idCpu, supdrvLnxMsrProberModifyOnCpu, pReq, NULL);
1101# else
1102 return VERR_NOT_SUPPORTED;
1103# endif
1104}
1105
1106#endif /* SUPDRV_WITH_MSR_PROBER */
1107
1108
1109/**
1110 * Converts a supdrv error code to an linux error code.
1111 *
1112 * @returns corresponding linux error code.
1113 * @param rc IPRT status code.
1114 */
1115static int VBoxDrvLinuxErr2LinuxErr(int rc)
1116{
1117 switch (rc)
1118 {
1119 case VINF_SUCCESS: return 0;
1120 case VERR_GENERAL_FAILURE: return -EACCES;
1121 case VERR_INVALID_PARAMETER: return -EINVAL;
1122 case VERR_INVALID_MAGIC: return -EILSEQ;
1123 case VERR_INVALID_HANDLE: return -ENXIO;
1124 case VERR_INVALID_POINTER: return -EFAULT;
1125 case VERR_LOCK_FAILED: return -ENOLCK;
1126 case VERR_ALREADY_LOADED: return -EEXIST;
1127 case VERR_PERMISSION_DENIED: return -EPERM;
1128 case VERR_VERSION_MISMATCH: return -ENOSYS;
1129 case VERR_IDT_FAILED: return -1000;
1130 }
1131
1132 return -EPERM;
1133}
1134
1135
1136RTDECL(int) SUPR0Printf(const char *pszFormat, ...)
1137{
1138 va_list va;
1139 char szMsg[512];
1140 IPRT_LINUX_SAVE_EFL_AC();
1141
1142 va_start(va, pszFormat);
1143 RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
1144 va_end(va);
1145 szMsg[sizeof(szMsg) - 1] = '\0';
1146
1147 printk("%s", szMsg);
1148
1149 IPRT_LINUX_RESTORE_EFL_AC();
1150 return 0;
1151}
1152
1153
1154SUPR0DECL(uint32_t) SUPR0GetKernelFeatures(void)
1155{
1156 uint32_t fFlags = 0;
1157#ifdef CONFIG_PAX_KERNEXEC
1158 fFlags |= SUPKERNELFEATURES_GDT_READ_ONLY;
1159#endif
1160#if defined(VBOX_STRICT) || defined(VBOX_WITH_EFLAGS_AC_SET_IN_VBOXDRV)
1161 fFlags |= SUPKERNELFEATURES_SMAP;
1162#elif defined(CONFIG_X86_SMAP)
1163 if (ASMGetCR4() & X86_CR4_SMAP)
1164 fFlags |= SUPKERNELFEATURES_SMAP;
1165#endif
1166 return fFlags;
1167}
1168
1169
1170module_init(VBoxDrvLinuxInit);
1171module_exit(VBoxDrvLinuxUnload);
1172
1173MODULE_AUTHOR(VBOX_VENDOR);
1174MODULE_DESCRIPTION(VBOX_PRODUCT " Support Driver");
1175MODULE_LICENSE("GPL");
1176#ifdef MODULE_VERSION
1177MODULE_VERSION(VBOX_VERSION_STRING " (" RT_XSTR(SUPDRV_IOC_VERSION) ")");
1178#endif
1179
1180module_param(force_async_tsc, int, 0444);
1181MODULE_PARM_DESC(force_async_tsc, "force the asynchronous TSC mode");
1182
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