VirtualBox

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

Last change on this file since 49966 was 49767, checked in by vboxsync, 11 years ago

SUPDrv/darwin: MSR prober support for darwin.

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