VirtualBox

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

Last change on this file since 56284 was 55436, checked in by vboxsync, 10 years ago

Reverted bad commit r99781.

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