VirtualBox

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

Last change on this file since 48952 was 47537, checked in by vboxsync, 11 years ago

HostDrivers/Support: Introduce reference counting for the session object to keep it alive while there is someone accessing it, fixes R0 assertion on OS X when killing the VM process

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.8 KB
Line 
1/* $Rev: 47537 $ */
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
63
64/*******************************************************************************
65* Defined Constants And Macros *
66*******************************************************************************/
67/* check kernel version */
68# ifndef SUPDRV_AGNOSTIC
69# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
70# error Unsupported kernel version!
71# endif
72# endif
73
74/* devfs defines */
75#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC)
76# ifdef VBOX_WITH_HARDENING
77# define VBOX_DEV_FMASK (S_IWUSR | S_IRUSR)
78# else
79# define VBOX_DEV_FMASK (S_IRUGO | S_IWUGO)
80# endif
81#endif /* CONFIG_DEV_FS && !CONFIG_VBOXDEV_AS_MISC */
82
83#ifdef CONFIG_X86_HIGH_ENTRY
84# error "CONFIG_X86_HIGH_ENTRY is not supported by VBoxDrv at this time."
85#endif
86
87/* to include the version number of VirtualBox into kernel backtraces */
88#define VBoxDrvLinuxVersion RT_CONCAT3(RT_CONCAT(VBOX_VERSION_MAJOR, _), \
89 RT_CONCAT(VBOX_VERSION_MINOR, _), \
90 VBOX_VERSION_BUILD)
91#define VBoxDrvLinuxIOCtl RT_CONCAT(VBoxDrvLinuxIOCtl_,VBoxDrvLinuxVersion)
92
93/*******************************************************************************
94* Internal Functions *
95*******************************************************************************/
96static int VBoxDrvLinuxInit(void);
97static void VBoxDrvLinuxUnload(void);
98static int VBoxDrvLinuxCreateSys(struct inode *pInode, struct file *pFilp);
99static int VBoxDrvLinuxCreateUsr(struct inode *pInode, struct file *pFilp);
100static int VBoxDrvLinuxClose(struct inode *pInode, struct file *pFilp);
101#ifdef HAVE_UNLOCKED_IOCTL
102static long VBoxDrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
103#else
104static int VBoxDrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
105#endif
106static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PSUPDRVSESSION pSession);
107static int VBoxDrvLinuxErr2LinuxErr(int);
108#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
109static int VBoxDrvProbe(struct platform_device *pDev);
110# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
111static int VBoxDrvSuspend(struct device *pDev);
112static int VBoxDrvResume(struct device *pDev);
113# else
114static int VBoxDrvSuspend(struct platform_device *pDev, pm_message_t State);
115static int VBoxDrvResume(struct platform_device *pDev);
116# endif
117static void VBoxDevRelease(struct device *pDev);
118#endif
119
120
121/*******************************************************************************
122* Global Variables *
123*******************************************************************************/
124/**
125 * Device extention & session data association structure.
126 */
127static SUPDRVDEVEXT g_DevExt;
128
129#ifndef CONFIG_VBOXDRV_AS_MISC
130/** Module major number for vboxdrv. */
131#define DEVICE_MAJOR_SYS 234
132/** Saved major device number for vboxdrv. */
133static int g_iModuleMajorSys;
134/** Module major number for vboxdrvu. */
135#define DEVICE_MAJOR_USR 235
136/** Saved major device number for vboxdrvu. */
137static int g_iModuleMajorUsr;
138#endif /* !CONFIG_VBOXDRV_AS_MISC */
139
140/** Module parameter.
141 * Not prefixed because the name is used by macros and the end of this file. */
142static int force_async_tsc = 0;
143
144/** The system device name. */
145#define DEVICE_NAME_SYS "vboxdrv"
146/** The user device name. */
147#define DEVICE_NAME_USR "vboxdrvu"
148
149#if defined(RT_ARCH_AMD64) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
150/**
151 * Memory for the executable memory heap (in IPRT).
152 */
153extern uint8_t g_abExecMemory[1572864]; /* 1.5 MB */
154__asm__(".section execmemory, \"awx\", @progbits\n\t"
155 ".align 32\n\t"
156 ".globl g_abExecMemory\n"
157 "g_abExecMemory:\n\t"
158 ".zero 1572864\n\t"
159 ".type g_abExecMemory, @object\n\t"
160 ".size g_abExecMemory, 1572864\n\t"
161 ".text\n\t");
162#endif
163
164/** The file_operations structure. */
165static struct file_operations gFileOpsVBoxDrvSys =
166{
167 owner: THIS_MODULE,
168 open: VBoxDrvLinuxCreateSys,
169 release: VBoxDrvLinuxClose,
170#ifdef HAVE_UNLOCKED_IOCTL
171 unlocked_ioctl: VBoxDrvLinuxIOCtl,
172#else
173 ioctl: VBoxDrvLinuxIOCtl,
174#endif
175};
176
177/** The file_operations structure. */
178static struct file_operations gFileOpsVBoxDrvUsr =
179{
180 owner: THIS_MODULE,
181 open: VBoxDrvLinuxCreateUsr,
182 release: VBoxDrvLinuxClose,
183#ifdef HAVE_UNLOCKED_IOCTL
184 unlocked_ioctl: VBoxDrvLinuxIOCtl,
185#else
186 ioctl: VBoxDrvLinuxIOCtl,
187#endif
188};
189
190#ifdef CONFIG_VBOXDRV_AS_MISC
191/** The miscdevice structure for vboxdrv. */
192static struct miscdevice gMiscDeviceSys =
193{
194 minor: MISC_DYNAMIC_MINOR,
195 name: DEVICE_NAME_SYS,
196 fops: &gFileOpsVBoxDrvSys,
197# if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 17)
198 devfs_name: DEVICE_NAME_SYS,
199# endif
200};
201/** The miscdevice structure for vboxdrvu. */
202static struct miscdevice gMiscDeviceUsr =
203{
204 minor: MISC_DYNAMIC_MINOR,
205 name: DEVICE_NAME_USR,
206 fops: &gFileOpsVBoxDrvUsr,
207# if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 17)
208 devfs_name: DEVICE_NAME_USR,
209# endif
210};
211#endif
212
213
214#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
215# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
216static struct dev_pm_ops gPlatformPMOps =
217{
218 .suspend = VBoxDrvSuspend, /* before entering deep sleep */
219 .resume = VBoxDrvResume, /* after wakeup from deep sleep */
220 .freeze = VBoxDrvSuspend, /* before creating hibernation image */
221 .restore = VBoxDrvResume, /* after waking up from hibernation */
222};
223# endif
224
225static struct platform_driver gPlatformDriver =
226{
227 .probe = VBoxDrvProbe,
228# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
229 .suspend = VBoxDrvSuspend,
230 .resume = VBoxDrvResume,
231# endif
232 /** @todo .shutdown? */
233 .driver =
234 {
235 .name = "vboxdrv",
236# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
237 .pm = &gPlatformPMOps,
238# endif
239 }
240};
241
242static struct platform_device gPlatformDevice =
243{
244 .name = "vboxdrv",
245 .dev =
246 {
247 .release = VBoxDevRelease
248 }
249};
250#endif /* VBOX_WITH_SUSPEND_NOTIFICATION */
251
252
253DECLINLINE(RTUID) vboxdrvLinuxUid(void)
254{
255#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
256# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
257 return from_kuid(current_user_ns(), current->cred->uid);
258# else
259 return current->cred->uid;
260# endif
261#else
262 return current->uid;
263#endif
264}
265
266DECLINLINE(RTGID) vboxdrvLinuxGid(void)
267{
268#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
269# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
270 return from_kgid(current_user_ns(), current->cred->gid);
271# else
272 return current->cred->gid;
273# endif
274#else
275 return current->gid;
276#endif
277}
278
279DECLINLINE(RTUID) vboxdrvLinuxEuid(void)
280{
281#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
282# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
283 return from_kuid(current_user_ns(), current->cred->euid);
284# else
285 return current->cred->euid;
286# endif
287#else
288 return current->euid;
289#endif
290}
291
292/**
293 * Initialize module.
294 *
295 * @returns appropriate status code.
296 */
297static int __init VBoxDrvLinuxInit(void)
298{
299 int rc;
300
301 /*
302 * Check for synchronous/asynchronous TSC mode.
303 */
304 printk(KERN_DEBUG "vboxdrv: Found %u processor cores.\n", (unsigned)RTMpGetOnlineCount());
305#ifdef CONFIG_VBOXDRV_AS_MISC
306 rc = misc_register(&gMiscDeviceSys);
307 if (rc)
308 {
309 printk(KERN_ERR "vboxdrv: Can't register system misc device! rc=%d\n", rc);
310 return rc;
311 }
312 rc = misc_register(&gMiscDeviceUsr);
313 if (rc)
314 {
315 printk(KERN_ERR "vboxdrv: Can't register user misc device! rc=%d\n", rc);
316 misc_deregister(&gMiscDeviceSys);
317 return rc;
318 }
319#else /* !CONFIG_VBOXDRV_AS_MISC */
320 /*
321 * Register character devices and save the returned major numbers.
322 */
323 /* /dev/vboxdrv */
324 g_iModuleMajorSys = DEVICE_MAJOR_SYS;
325 rc = register_chrdev((dev_t)g_iModuleMajorSys, DEVICE_NAME_SYS, &gFileOpsVBoxDrvSys);
326 if (rc < 0)
327 {
328 Log(("register_chrdev() failed with rc=%#x for vboxdrv!\n", rc));
329 return rc;
330 }
331 if (DEVICE_MAJOR_SYS != 0)
332 g_iModuleMajorSys = DEVICE_MAJOR_SYS;
333 else
334 g_iModuleMajorSys = rc;
335
336 /* /dev/vboxdrvu */
337 /** @todo Use a minor number of this bugger (not sure if this code is used
338 * though, so not bothering right now.) */
339 g_iModuleMajorUsr = DEVICE_MAJOR_USR;
340 rc = register_chrdev((dev_t)g_iModuleMajorUsr, DEVICE_NAME_USR, &gFileOpsVBoxDrvUsr);
341 if (rc < 0)
342 {
343 Log(("register_chrdev() failed with rc=%#x for vboxdrv!\n", rc));
344 return rc;
345 }
346 if (DEVICE_MAJOR_USR != 0)
347 g_iModuleMajorUsr = DEVICE_MAJOR_USR;
348 else
349 g_iModuleMajorUsr = rc;
350 rc = 0;
351
352# ifdef CONFIG_DEVFS_FS
353 /*
354 * Register a device entry
355 */
356 if ( devfs_mk_cdev(MKDEV(DEVICE_MAJOR_SYS, 0), S_IFCHR | VBOX_DEV_FMASK, DEVICE_NAME_SYS) != 0
357 || devfs_mk_cdev(MKDEV(DEVICE_MAJOR_USR, 0), S_IFCHR | VBOX_DEV_FMASK, DEVICE_NAME_USR) != 0)
358 {
359 Log(("devfs_register failed!\n"));
360 rc = -EINVAL;
361 }
362# endif
363#endif /* !CONFIG_VBOXDRV_AS_MISC */
364 if (!rc)
365 {
366 /*
367 * Initialize the runtime.
368 * On AMD64 we'll have to donate the high rwx memory block to the exec allocator.
369 */
370 rc = RTR0Init(0);
371 if (RT_SUCCESS(rc))
372 {
373#if defined(RT_ARCH_AMD64) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
374 rc = RTR0MemExecDonate(&g_abExecMemory[0], sizeof(g_abExecMemory));
375 printk(KERN_DEBUG "VBoxDrv: dbg - g_abExecMemory=%p\n", (void *)&g_abExecMemory[0]);
376#endif
377 Log(("VBoxDrv::ModuleInit\n"));
378
379 /*
380 * Initialize the device extension.
381 */
382 if (RT_SUCCESS(rc))
383 rc = supdrvInitDevExt(&g_DevExt, sizeof(SUPDRVSESSION));
384 if (RT_SUCCESS(rc))
385 {
386#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
387 rc = platform_driver_register(&gPlatformDriver);
388 if (rc == 0)
389 {
390 rc = platform_device_register(&gPlatformDevice);
391 if (rc == 0)
392#endif
393 {
394 printk(KERN_INFO "vboxdrv: TSC mode is %s, kernel timer mode is 'normal'.\n",
395 g_DevExt.pGip->u32Mode == SUPGIPMODE_SYNC_TSC ? "'synchronous'" : "'asynchronous'");
396 LogFlow(("VBoxDrv::ModuleInit returning %#x\n", rc));
397 printk(KERN_DEBUG "vboxdrv: Successfully loaded version "
398 VBOX_VERSION_STRING " (interface " RT_XSTR(SUPDRV_IOC_VERSION) ").\n");
399 return rc;
400 }
401#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
402 else
403 platform_driver_unregister(&gPlatformDriver);
404 }
405#endif
406 }
407
408 rc = -EINVAL;
409 RTR0TermForced();
410 }
411 else
412 rc = -EINVAL;
413
414 /*
415 * Failed, cleanup and return the error code.
416 */
417#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC)
418 devfs_remove(DEVICE_NAME_SYS);
419 devfs_remove(DEVICE_NAME_USR);
420#endif
421 }
422#ifdef CONFIG_VBOXDRV_AS_MISC
423 misc_deregister(&gMiscDeviceSys);
424 misc_deregister(&gMiscDeviceUsr);
425 Log(("VBoxDrv::ModuleInit returning %#x (minor:%d & %d)\n", rc, gMiscDeviceSys.minor, gMiscDeviceUsr.minor));
426#else
427 unregister_chrdev(g_iModuleMajorUsr, DEVICE_NAME_USR);
428 unregister_chrdev(g_iModuleMajorSys, DEVICE_NAME_SYS);
429 Log(("VBoxDrv::ModuleInit returning %#x (major:%d & %d)\n", rc, g_iModuleMajorSys, g_iModuleMajorUsr));
430#endif
431 return rc;
432}
433
434
435/**
436 * Unload the module.
437 */
438static void __exit VBoxDrvLinuxUnload(void)
439{
440 int rc;
441 Log(("VBoxDrvLinuxUnload\n"));
442 NOREF(rc);
443
444#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
445 platform_device_unregister(&gPlatformDevice);
446 platform_driver_unregister(&gPlatformDriver);
447#endif
448
449 /*
450 * I Don't think it's possible to unload a driver which processes have
451 * opened, at least we'll blindly assume that here.
452 */
453#ifdef CONFIG_VBOXDRV_AS_MISC
454 rc = misc_deregister(&gMiscDeviceUsr);
455 if (rc < 0)
456 {
457 Log(("misc_deregister failed with rc=%#x on vboxdrvu\n", rc));
458 }
459 rc = misc_deregister(&gMiscDeviceSys);
460 if (rc < 0)
461 {
462 Log(("misc_deregister failed with rc=%#x on vboxdrv\n", rc));
463 }
464#else /* !CONFIG_VBOXDRV_AS_MISC */
465# ifdef CONFIG_DEVFS_FS
466 /*
467 * Unregister a device entry
468 */
469 devfs_remove(DEVICE_NAME_USR);
470 devfs_remove(DEVICE_NAME_SYS);
471# endif /* devfs */
472 unregister_chrdev(g_iModuleMajorUsr, DEVICE_NAME_USR);
473 unregister_chrdev(g_iModuleMajorSys, DEVICE_NAME_SYS);
474#endif /* !CONFIG_VBOXDRV_AS_MISC */
475
476 /*
477 * Destroy GIP, delete the device extension and terminate IPRT.
478 */
479 supdrvDeleteDevExt(&g_DevExt);
480 RTR0TermForced();
481}
482
483
484/**
485 * Common open code.
486 *
487 * @param pInode Pointer to inode info structure.
488 * @param pFilp Associated file pointer.
489 * @param fUnrestricted Indicates which device node which was opened.
490 */
491static int vboxdrvLinuxCreateCommon(struct inode *pInode, struct file *pFilp, bool fUnrestricted)
492{
493 int rc;
494 PSUPDRVSESSION pSession;
495 Log(("VBoxDrvLinuxCreate: pFilp=%p pid=%d/%d %s\n", pFilp, RTProcSelf(), current->pid, current->comm));
496
497#ifdef VBOX_WITH_HARDENING
498 /*
499 * Only root is allowed to access the unrestricted device, enforce it!
500 */
501 if ( fUnrestricted
502 && vboxdrvLinuxEuid() != 0 /* root */ )
503 {
504 Log(("VBoxDrvLinuxCreate: euid=%d, expected 0 (root)\n", vboxdrvLinuxEuid()));
505 return -EPERM;
506 }
507#endif /* VBOX_WITH_HARDENING */
508
509 /*
510 * Call common code for the rest.
511 */
512 rc = supdrvCreateSession(&g_DevExt, true /* fUser */, fUnrestricted, &pSession);
513 if (!rc)
514 {
515 pSession->Uid = vboxdrvLinuxUid();
516 pSession->Gid = vboxdrvLinuxGid();
517 }
518
519 pFilp->private_data = pSession;
520
521 Log(("VBoxDrvLinuxCreate: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n",
522 &g_DevExt, pSession, rc, VBoxDrvLinuxErr2LinuxErr(rc),
523 RTProcSelf(), current->pid, current->comm));
524 return VBoxDrvLinuxErr2LinuxErr(rc);
525}
526
527
528/** /dev/vboxdrv. */
529static int VBoxDrvLinuxCreateSys(struct inode *pInode, struct file *pFilp)
530{
531 return vboxdrvLinuxCreateCommon(pInode, pFilp, true);
532}
533
534
535/** /dev/vboxdrvu. */
536static int VBoxDrvLinuxCreateUsr(struct inode *pInode, struct file *pFilp)
537{
538 return vboxdrvLinuxCreateCommon(pInode, pFilp, false);
539}
540
541
542/**
543 * Close device.
544 *
545 * @param pInode Pointer to inode info structure.
546 * @param pFilp Associated file pointer.
547 */
548static int VBoxDrvLinuxClose(struct inode *pInode, struct file *pFilp)
549{
550 Log(("VBoxDrvLinuxClose: pFilp=%p pSession=%p pid=%d/%d %s\n",
551 pFilp, pFilp->private_data, RTProcSelf(), current->pid, current->comm));
552 supdrvSessionRelease((PSUPDRVSESSION)pFilp->private_data);
553 pFilp->private_data = NULL;
554 return 0;
555}
556
557
558#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
559/**
560 * Dummy device release function. We have to provide this function,
561 * otherwise the kernel will complain.
562 *
563 * @param pDev Pointer to the platform device.
564 */
565static void VBoxDevRelease(struct device *pDev)
566{
567}
568
569/**
570 * Dummy probe function.
571 *
572 * @param pDev Pointer to the platform device.
573 */
574static int VBoxDrvProbe(struct platform_device *pDev)
575{
576 return 0;
577}
578
579/**
580 * Suspend callback.
581 * @param pDev Pointer to the platform device.
582 * @param State message type, see Documentation/power/devices.txt.
583 */
584# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
585static int VBoxDrvSuspend(struct device *pDev)
586# else
587static int VBoxDrvSuspend(struct platform_device *pDev, pm_message_t State)
588# endif
589{
590 RTPowerSignalEvent(RTPOWEREVENT_SUSPEND);
591 return 0;
592}
593
594/**
595 * Resume callback.
596 *
597 * @param pDev Pointer to the platform device.
598 */
599# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
600static int VBoxDrvResume(struct device *pDev)
601# else
602static int VBoxDrvResume(struct platform_device *pDev)
603# endif
604{
605 RTPowerSignalEvent(RTPOWEREVENT_RESUME);
606 return 0;
607}
608#endif /* VBOX_WITH_SUSPEND_NOTIFICATION */
609
610
611/**
612 * Device I/O Control entry point.
613 *
614 * @param pFilp Associated file pointer.
615 * @param uCmd The function specified to ioctl().
616 * @param ulArg The argument specified to ioctl().
617 */
618#ifdef HAVE_UNLOCKED_IOCTL
619static long VBoxDrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
620#else
621static int VBoxDrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
622#endif
623{
624 PSUPDRVSESSION pSession = (PSUPDRVSESSION)pFilp->private_data;
625
626 /*
627 * Deal with the two high-speed IOCtl that takes it's arguments from
628 * the session and iCmd, and only returns a VBox status code.
629 */
630#ifdef HAVE_UNLOCKED_IOCTL
631 if (RT_LIKELY( ( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN
632 || uCmd == SUP_IOCTL_FAST_DO_HM_RUN
633 || uCmd == SUP_IOCTL_FAST_DO_NOP)
634 && pSession->fUnrestricted == true))
635 return supdrvIOCtlFast(uCmd, ulArg, &g_DevExt, pSession);
636 return VBoxDrvLinuxIOCtlSlow(pFilp, uCmd, ulArg, pSession);
637
638#else /* !HAVE_UNLOCKED_IOCTL */
639
640 int rc;
641 unlock_kernel();
642 if (RT_LIKELY( ( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN
643 || uCmd == SUP_IOCTL_FAST_DO_HM_RUN
644 || uCmd == SUP_IOCTL_FAST_DO_NOP)
645 && pSession->fUnrestricted == true))
646 rc = supdrvIOCtlFast(uCmd, ulArg, &g_DevExt, pSession);
647 else
648 rc = VBoxDrvLinuxIOCtlSlow(pFilp, uCmd, ulArg, pSession);
649 lock_kernel();
650 return rc;
651#endif /* !HAVE_UNLOCKED_IOCTL */
652}
653
654
655/**
656 * Device I/O Control entry point.
657 *
658 * @param pFilp Associated file pointer.
659 * @param uCmd The function specified to ioctl().
660 * @param ulArg The argument specified to ioctl().
661 * @param pSession The session instance.
662 */
663static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PSUPDRVSESSION pSession)
664{
665 int rc;
666 SUPREQHDR Hdr;
667 PSUPREQHDR pHdr;
668 uint32_t cbBuf;
669
670 Log6(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid));
671
672 /*
673 * Read the header.
674 */
675 if (RT_UNLIKELY(copy_from_user(&Hdr, (void *)ulArg, sizeof(Hdr))))
676 {
677 Log(("VBoxDrvLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd));
678 return -EFAULT;
679 }
680 if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
681 {
682 Log(("VBoxDrvLinuxIOCtl: bad header magic %#x; uCmd=%#x\n", Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK, uCmd));
683 return -EINVAL;
684 }
685
686 /*
687 * Buffer the request.
688 */
689 cbBuf = RT_MAX(Hdr.cbIn, Hdr.cbOut);
690 if (RT_UNLIKELY(cbBuf > _1M*16))
691 {
692 Log(("VBoxDrvLinuxIOCtl: too big cbBuf=%#x; uCmd=%#x\n", cbBuf, uCmd));
693 return -E2BIG;
694 }
695 if (RT_UNLIKELY(cbBuf != _IOC_SIZE(uCmd) && _IOC_SIZE(uCmd)))
696 {
697 Log(("VBoxDrvLinuxIOCtl: bad ioctl cbBuf=%#x _IOC_SIZE=%#x; uCmd=%#x.\n", cbBuf, _IOC_SIZE(uCmd), uCmd));
698 return -EINVAL;
699 }
700 pHdr = RTMemAlloc(cbBuf);
701 if (RT_UNLIKELY(!pHdr))
702 {
703 OSDBGPRINT(("VBoxDrvLinuxIOCtl: failed to allocate buffer of %d bytes for uCmd=%#x.\n", cbBuf, uCmd));
704 return -ENOMEM;
705 }
706 if (RT_UNLIKELY(copy_from_user(pHdr, (void *)ulArg, Hdr.cbIn)))
707 {
708 Log(("VBoxDrvLinuxIOCtl: copy_from_user(,%#lx, %#x) failed; uCmd=%#x.\n", ulArg, Hdr.cbIn, uCmd));
709 RTMemFree(pHdr);
710 return -EFAULT;
711 }
712
713 /*
714 * Process the IOCtl.
715 */
716 rc = supdrvIOCtl(uCmd, &g_DevExt, pSession, pHdr);
717
718 /*
719 * Copy ioctl data and output buffer back to user space.
720 */
721 if (RT_LIKELY(!rc))
722 {
723 uint32_t cbOut = pHdr->cbOut;
724 if (RT_UNLIKELY(cbOut > cbBuf))
725 {
726 OSDBGPRINT(("VBoxDrvLinuxIOCtl: too much output! %#x > %#x; uCmd=%#x!\n", cbOut, cbBuf, uCmd));
727 cbOut = cbBuf;
728 }
729 if (RT_UNLIKELY(copy_to_user((void *)ulArg, pHdr, cbOut)))
730 {
731 /* this is really bad! */
732 OSDBGPRINT(("VBoxDrvLinuxIOCtl: copy_to_user(%#lx,,%#x); uCmd=%#x!\n", ulArg, cbOut, uCmd));
733 rc = -EFAULT;
734 }
735 }
736 else
737 {
738 Log(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc));
739 rc = -EINVAL;
740 }
741 RTMemFree(pHdr);
742
743 Log6(("VBoxDrvLinuxIOCtl: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid));
744 return rc;
745}
746
747
748/**
749 * The SUPDRV IDC entry point.
750 *
751 * @returns VBox status code, see supdrvIDC.
752 * @param iReq The request code.
753 * @param pReq The request.
754 */
755int VBOXCALL SUPDrvLinuxIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq)
756{
757 PSUPDRVSESSION pSession;
758
759 /*
760 * Some quick validations.
761 */
762 if (RT_UNLIKELY(!VALID_PTR(pReq)))
763 return VERR_INVALID_POINTER;
764
765 pSession = pReq->pSession;
766 if (pSession)
767 {
768 if (RT_UNLIKELY(!VALID_PTR(pSession)))
769 return VERR_INVALID_PARAMETER;
770 if (RT_UNLIKELY(pSession->pDevExt != &g_DevExt))
771 return VERR_INVALID_PARAMETER;
772 }
773 else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT))
774 return VERR_INVALID_PARAMETER;
775
776 /*
777 * Do the job.
778 */
779 return supdrvIDC(uReq, &g_DevExt, pSession, pReq);
780}
781
782EXPORT_SYMBOL(SUPDrvLinuxIDC);
783
784
785/**
786 * Initializes any OS specific object creator fields.
787 */
788void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
789{
790 NOREF(pObj);
791 NOREF(pSession);
792}
793
794
795/**
796 * Checks if the session can access the object.
797 *
798 * @returns true if a decision has been made.
799 * @returns false if the default access policy should be applied.
800 *
801 * @param pObj The object in question.
802 * @param pSession The session wanting to access the object.
803 * @param pszObjName The object name, can be NULL.
804 * @param prc Where to store the result when returning true.
805 */
806bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
807{
808 NOREF(pObj);
809 NOREF(pSession);
810 NOREF(pszObjName);
811 NOREF(prc);
812 return false;
813}
814
815
816bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt)
817{
818 return force_async_tsc != 0;
819}
820
821
822int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
823{
824 NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
825 return VERR_NOT_SUPPORTED;
826}
827
828
829void VBOXCALL supdrvOSLdrNotifyOpened(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
830{
831 NOREF(pDevExt); NOREF(pImage);
832}
833
834
835int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv, const uint8_t *pbImageBits)
836{
837 NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits);
838 return VERR_NOT_SUPPORTED;
839}
840
841
842int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq)
843{
844 NOREF(pDevExt); NOREF(pImage); NOREF(pbImageBits); NOREF(pReq);
845 return VERR_NOT_SUPPORTED;
846}
847
848
849void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
850{
851 NOREF(pDevExt); NOREF(pImage);
852}
853
854
855/**
856 * Converts a supdrv error code to an linux error code.
857 *
858 * @returns corresponding linux error code.
859 * @param rc IPRT status code.
860 */
861static int VBoxDrvLinuxErr2LinuxErr(int rc)
862{
863 switch (rc)
864 {
865 case VINF_SUCCESS: return 0;
866 case VERR_GENERAL_FAILURE: return -EACCES;
867 case VERR_INVALID_PARAMETER: return -EINVAL;
868 case VERR_INVALID_MAGIC: return -EILSEQ;
869 case VERR_INVALID_HANDLE: return -ENXIO;
870 case VERR_INVALID_POINTER: return -EFAULT;
871 case VERR_LOCK_FAILED: return -ENOLCK;
872 case VERR_ALREADY_LOADED: return -EEXIST;
873 case VERR_PERMISSION_DENIED: return -EPERM;
874 case VERR_VERSION_MISMATCH: return -ENOSYS;
875 case VERR_IDT_FAILED: return -1000;
876 }
877
878 return -EPERM;
879}
880
881
882RTDECL(int) SUPR0Printf(const char *pszFormat, ...)
883{
884 va_list va;
885 char szMsg[512];
886
887 va_start(va, pszFormat);
888 RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
889 va_end(va);
890 szMsg[sizeof(szMsg) - 1] = '\0';
891
892 printk("%s", szMsg);
893 return 0;
894}
895
896module_init(VBoxDrvLinuxInit);
897module_exit(VBoxDrvLinuxUnload);
898
899MODULE_AUTHOR(VBOX_VENDOR);
900MODULE_DESCRIPTION(VBOX_PRODUCT " Support Driver");
901MODULE_LICENSE("GPL");
902#ifdef MODULE_VERSION
903MODULE_VERSION(VBOX_VERSION_STRING " (" RT_XSTR(SUPDRV_IOC_VERSION) ")");
904#endif
905
906module_param(force_async_tsc, int, 0444);
907MODULE_PARM_DESC(force_async_tsc, "force the asynchronous TSC mode");
908
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