VirtualBox

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

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

Linux support driver: adapted the CONFIG_PERF_* heuristics to 2.6.32 (CONFIG_PERF_COUNTERS was renamed to CONFIG_PERF_EVENTS)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.3 KB
Line 
1/* $Rev: 25365 $ */
2/** @file
3 * VBoxDrv - The VirtualBox Support Driver - Linux specifics.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 * Some lines of code to disable the local APIC on x86_64 machines taken
30 * from a Mandriva patch by Gwenole Beauchesne <[email protected]>.
31 */
32
33/*******************************************************************************
34* Header Files *
35*******************************************************************************/
36#define LOG_GROUP LOG_GROUP_SUP_DRV
37#include "../SUPDrvInternal.h"
38#include "the-linux-kernel.h"
39#include "version-generated.h"
40
41#include <iprt/assert.h>
42#include <iprt/spinlock.h>
43#include <iprt/semaphore.h>
44#include <iprt/initterm.h>
45#include <iprt/process.h>
46#include <VBox/err.h>
47#include <iprt/mem.h>
48#include <VBox/log.h>
49#include <iprt/mp.h>
50
51/** @todo figure out the exact version number */
52#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16)
53# include <iprt/power.h>
54# define VBOX_WITH_SUSPEND_NOTIFICATION
55#endif
56
57#include <linux/sched.h>
58#ifdef CONFIG_DEVFS_FS
59# include <linux/devfs_fs_kernel.h>
60#endif
61#ifdef CONFIG_VBOXDRV_AS_MISC
62# include <linux/miscdevice.h>
63#endif
64#ifdef CONFIG_X86_LOCAL_APIC
65# include <asm/apic.h>
66# include <asm/nmi.h>
67#endif
68#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
69# include <linux/platform_device.h>
70#endif
71
72#include <iprt/mem.h>
73
74
75/* devfs defines */
76#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC)
77# ifdef VBOX_WITH_HARDENING
78# define VBOX_DEV_FMASK (S_IWUSR | S_IRUSR)
79# else
80# define VBOX_DEV_FMASK (S_IRUGO | S_IWUGO)
81# endif
82#endif /* CONFIG_DEV_FS && !CONFIG_VBOXDEV_AS_MISC */
83
84#ifdef CONFIG_X86_HIGH_ENTRY
85# error "CONFIG_X86_HIGH_ENTRY is not supported by VBoxDrv at this time."
86#endif
87
88#ifdef CONFIG_X86_LOCAL_APIC
89
90/* If an NMI occurs while we are inside the world switcher the machine will
91 * crash. The Linux NMI watchdog generates periodic NMIs increasing a counter
92 * which is compared with another counter increased in the timer interrupt
93 * handler. We disable the NMI watchdog.
94 *
95 * - Linux >= 2.6.21: The watchdog is disabled by default on i386 and x86_64.
96 * - Linux < 2.6.21: The watchdog is normally enabled by default on x86_64
97 * and disabled on i386.
98 */
99# if defined(RT_ARCH_AMD64)
100# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21) && !defined(VBOX_REDHAT_KABI)
101# define DO_DISABLE_NMI 1
102# endif
103# endif
104
105# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
106extern int nmi_active;
107# define nmi_atomic_read(P) *(P)
108# define nmi_atomic_set(P, V) *(P) = (V)
109# define nmi_atomic_dec(P) nmi_atomic_set(P, 0)
110# else
111# define nmi_atomic_read(P) atomic_read(P)
112# define nmi_atomic_set(P, V) atomic_set(P, V)
113# define nmi_atomic_dec(P) atomic_dec(P)
114# endif
115
116# ifndef X86_FEATURE_ARCH_PERFMON
117# define X86_FEATURE_ARCH_PERFMON (3*32+9) /* Intel Architectural PerfMon */
118# endif
119# ifndef MSR_ARCH_PERFMON_EVENTSEL0
120# define MSR_ARCH_PERFMON_EVENTSEL0 0x186
121# endif
122# ifndef ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT
123# define ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT (1 << 0)
124# endif
125
126#endif /* CONFIG_X86_LOCAL_APIC */
127
128
129/*******************************************************************************
130* Global Variables *
131*******************************************************************************/
132/**
133 * Device extention & session data association structure.
134 */
135static SUPDRVDEVEXT g_DevExt;
136
137#ifndef CONFIG_VBOXDRV_AS_MISC
138/** Module major number */
139#define DEVICE_MAJOR 234
140/** Saved major device number */
141static int g_iModuleMajor;
142#endif /* !CONFIG_VBOXDRV_AS_MISC */
143
144/** Module parameter.
145 * Not prefixed because the name is used by macros and the end of this file. */
146static int force_async_tsc = 0;
147
148/** The module name. */
149#define DEVICE_NAME "vboxdrv"
150
151#ifdef RT_ARCH_AMD64
152/**
153 * Memory for the executable memory heap (in IPRT).
154 */
155extern uint8_t g_abExecMemory[1572864]; /* 1.5 MB */
156__asm__(".section execmemory, \"awx\", @progbits\n\t"
157 ".align 32\n\t"
158 ".globl g_abExecMemory\n"
159 "g_abExecMemory:\n\t"
160 ".zero 1572864\n\t"
161 ".type g_abExecMemory, @object\n\t"
162 ".size g_abExecMemory, 1572864\n\t"
163 ".text\n\t");
164#endif
165
166
167/*******************************************************************************
168* Internal Functions *
169*******************************************************************************/
170static int VBoxDrvLinuxInit(void);
171static void VBoxDrvLinuxUnload(void);
172static int VBoxDrvLinuxCreate(struct inode *pInode, struct file *pFilp);
173static int VBoxDrvLinuxClose(struct inode *pInode, struct file *pFilp);
174#ifdef HAVE_UNLOCKED_IOCTL
175static long VBoxDrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
176#else
177static int VBoxDrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
178#endif
179static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
180static int VBoxDrvLinuxErr2LinuxErr(int);
181#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
182static int VBoxDrvProbe(struct platform_device *pDev);
183# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
184static int VBoxDrvSuspend(struct device *pDev);
185static int VBoxDrvResume(struct device *pDev);
186# else
187static int VBoxDrvSuspend(struct platform_device *pDev, pm_message_t State);
188static int VBoxDrvResume(struct platform_device *pDev);
189# endif
190static void VBoxDevRelease(struct device *pDev);
191#endif
192
193/** The file_operations structure. */
194static struct file_operations gFileOpsVBoxDrv =
195{
196 owner: THIS_MODULE,
197 open: VBoxDrvLinuxCreate,
198 release: VBoxDrvLinuxClose,
199#ifdef HAVE_UNLOCKED_IOCTL
200 unlocked_ioctl: VBoxDrvLinuxIOCtl,
201#else
202 ioctl: VBoxDrvLinuxIOCtl,
203#endif
204};
205
206#ifdef CONFIG_VBOXDRV_AS_MISC
207/** The miscdevice structure. */
208static struct miscdevice gMiscDevice =
209{
210 minor: MISC_DYNAMIC_MINOR,
211 name: DEVICE_NAME,
212 fops: &gFileOpsVBoxDrv,
213# if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 17)
214 devfs_name: DEVICE_NAME,
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,
225 .resume = VBoxDrvResume,
226};
227# endif
228
229static struct platform_driver gPlatformDriver =
230{
231 .probe = VBoxDrvProbe,
232# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
233 .suspend = VBoxDrvSuspend,
234 .resume = VBoxDrvResume,
235# endif
236 /** @todo .shutdown? */
237 .driver =
238 {
239 .name = "vboxdrv",
240# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
241 .pm = &gPlatformPMOps,
242# endif
243 }
244};
245
246static struct platform_device gPlatformDevice =
247{
248 .name = "vboxdrv",
249 .dev =
250 {
251 .release = VBoxDevRelease
252 }
253};
254#endif /* VBOX_WITH_SUSPEND_NOTIFICATION */
255
256
257#ifdef CONFIG_X86_LOCAL_APIC
258# ifdef DO_DISABLE_NMI
259/** Stop AMD NMI watchdog (x86_64 only). */
260static int vboxdrvStopK7Watchdog(void)
261{
262 wrmsr(MSR_K7_EVNTSEL0, 0, 0);
263 return 1;
264}
265
266/** Stop Intel P4 NMI watchdog (x86_64 only). */
267static int vboxdrvStopP4Watchdog(void)
268{
269 wrmsr(MSR_P4_IQ_CCCR0, 0, 0);
270 wrmsr(MSR_P4_IQ_CCCR1, 0, 0);
271 wrmsr(MSR_P4_CRU_ESCR0, 0, 0);
272 return 1;
273}
274
275/** The new method of detecting the event counter */
276static int vboxdrvStopIntelArchWatchdog(void)
277{
278 unsigned ebx;
279
280 ebx = cpuid_ebx(10);
281 if (!(ebx & ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT))
282 wrmsr(MSR_ARCH_PERFMON_EVENTSEL0, 0, 0);
283 return 1;
284}
285
286/** Stop NMI watchdog. */
287static void vboxdrvStopApicNmiWatchdog(void *unused)
288{
289 int stopped = 0;
290
291 /* only support LOCAL and IO APICs for now */
292 if ((nmi_watchdog != NMI_LOCAL_APIC) &&
293 (nmi_watchdog != NMI_IO_APIC))
294 return;
295
296 if (nmi_watchdog == NMI_LOCAL_APIC)
297 {
298 switch (boot_cpu_data.x86_vendor)
299 {
300 case X86_VENDOR_AMD:
301 if (strstr(boot_cpu_data.x86_model_id, "Screwdriver"))
302 return;
303 stopped = vboxdrvStopK7Watchdog();
304 break;
305 case X86_VENDOR_INTEL:
306 if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON))
307 {
308 stopped = vboxdrvStopIntelArchWatchdog();
309 break;
310 }
311 stopped = vboxdrvStopP4Watchdog();
312 break;
313 default:
314 return;
315 }
316 }
317
318 if (stopped)
319 nmi_atomic_dec(&nmi_active);
320}
321
322/** Disable LAPIC NMI watchdog. */
323static void DisableLapicNmiWatchdog(void)
324{
325 BUG_ON(nmi_watchdog != NMI_LOCAL_APIC);
326
327 if (nmi_atomic_read(&nmi_active) <= 0)
328 return;
329
330 on_each_cpu(vboxdrvStopApicNmiWatchdog, NULL, 1, 1);
331
332 BUG_ON(nmi_atomic_read(&nmi_active) != 0);
333
334 /* tell do_nmi() and others that we're not active any more */
335 nmi_watchdog = NMI_NONE;
336}
337
338/** Shutdown NMI. */
339static void vboxdrvNmiCpuShutdown(void * dummy)
340{
341 unsigned int vERR, vPC;
342
343 vPC = apic_read(APIC_LVTPC);
344
345 if ((GET_APIC_DELIVERY_MODE(vPC) == APIC_MODE_NMI) && !(vPC & APIC_LVT_MASKED))
346 {
347 vERR = apic_read(APIC_LVTERR);
348 apic_write(APIC_LVTERR, vERR | APIC_LVT_MASKED);
349 apic_write(APIC_LVTPC, vPC | APIC_LVT_MASKED);
350 apic_write(APIC_LVTERR, vERR);
351 }
352}
353
354static void vboxdrvNmiShutdown(void)
355{
356 on_each_cpu(vboxdrvNmiCpuShutdown, NULL, 0, 1);
357}
358# endif /* DO_DISABLE_NMI */
359#endif /* CONFIG_X86_LOCAL_APIC */
360
361
362DECLINLINE(RTUID) vboxdrvLinuxUid(void)
363{
364#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
365 return current->cred->uid;
366#else
367 return current->uid;
368#endif
369}
370
371DECLINLINE(RTGID) vboxdrvLinuxGid(void)
372{
373#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
374 return current->cred->gid;
375#else
376 return current->gid;
377#endif
378}
379
380DECLINLINE(RTUID) vboxdrvLinuxEuid(void)
381{
382#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
383 return current->cred->euid;
384#else
385 return current->euid;
386#endif
387}
388
389/**
390 * Initialize module.
391 *
392 * @returns appropriate status code.
393 */
394static int __init VBoxDrvLinuxInit(void)
395{
396 int rc;
397
398 dprintf(("VBoxDrv::ModuleInit\n"));
399
400#ifdef CONFIG_X86_LOCAL_APIC
401 /*
402 * If an NMI occurs while we are inside the world switcher the macine will crash.
403 * The Linux NMI watchdog generates periodic NMIs increasing a counter which is
404 * compared with another counter increased in the timer interrupt handler. Therefore
405 * we don't allow to setup an NMI watchdog.
406 */
407# if !defined(VBOX_REDHAT_KABI)
408 /*
409 * First test: NMI actiated? Works only works with Linux 2.6 -- 2.4 does not export
410 * the nmi_watchdog variable.
411 */
412# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) || defined CONFIG_X86_64
413# ifdef DO_DISABLE_NMI
414 if (nmi_atomic_read(&nmi_active) > 0)
415 {
416 printk(KERN_DEBUG DEVICE_NAME ": Trying to deactivate the NMI watchdog...\n");
417
418 switch (nmi_watchdog)
419 {
420 case NMI_LOCAL_APIC:
421 DisableLapicNmiWatchdog();
422 break;
423 case NMI_NONE:
424 nmi_atomic_dec(&nmi_active);
425 break;
426 }
427
428 if (nmi_atomic_read(&nmi_active) == 0)
429 {
430 vboxdrvNmiShutdown();
431 printk(KERN_DEBUG DEVICE_NAME ": Successfully done.\n");
432 }
433 else
434 printk(KERN_DEBUG DEVICE_NAME ": Failed!\n");
435 }
436# endif /* DO_DISABLE_NMI */
437
438 /*
439 * Permanent IO_APIC mode active? No way to handle this!
440 */
441 if (nmi_watchdog == NMI_IO_APIC)
442 {
443 printk(KERN_ERR DEVICE_NAME
444 ": NMI watchdog in IO_APIC mode active -- refused to load the kernel module!\n"
445 DEVICE_NAME
446 ": Please disable the NMI watchdog by specifying 'nmi_watchdog=0' at kernel\n"
447 DEVICE_NAME
448 ": command line.\n");
449 return -EINVAL;
450 }
451
452 /*
453 * See arch/i386/kernel/nmi.c on >= 2.6.19: -1 means it can never enabled again
454 */
455 nmi_atomic_set(&nmi_active, -1);
456 printk(KERN_DEBUG DEVICE_NAME ": Trying to deactivate the NMI watchdog permanently...\n");
457
458 /*
459 * Now fall through and see if it actually was enabled before. If so, fail
460 * as we cannot deactivate it cleanly from here.
461 */
462# else /* < 2.6.19 */
463 /*
464 * Older 2.6 kernels: nmi_watchdog is not initalized by default
465 */
466 if (nmi_watchdog != NMI_NONE)
467 goto nmi_activated;
468# endif
469# endif /* >= 2.6.0 && !defined(VBOX_REDHAT_KABI) */
470
471 /*
472 * Second test: Interrupt generated by performance counter not masked and can
473 * generate an NMI. Works also with Linux 2.4.
474 */
475 {
476 unsigned int v, ver, maxlvt;
477
478 v = apic_read(APIC_LVR);
479 ver = GET_APIC_VERSION(v);
480 /* 82489DXs do not report # of LVT entries. */
481 maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(v) : 2;
482 if (maxlvt >= 4)
483 {
484 /* Read status of performance counter IRQ vector */
485 v = apic_read(APIC_LVTPC);
486
487 /* performance counter generates NMI and is not masked? */
488 if ((GET_APIC_DELIVERY_MODE(v) == APIC_MODE_NMI) && !(v & APIC_LVT_MASKED))
489 {
490# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) \
491 && (defined(CONFIG_PERF_COUNTERS) || defined(CONFIG_PERF_EVENTS))
492 /* 2.6.31+: The performance counter framework will initialize the LVTPC
493 * vector as NMI. We can't disable the framework but the kernel loader
494 * script will do 'echo 2 > /proc/sys/kernel/perf_counter_paranoid'
495 * which hopefilly prevents any usage of hardware performance counters
496 * and therefore triggering of NMIs.
497 * 2.6.32+: CONFIG_PERF_COUNTERS => CONFIG_PERF_EVENTS */
498 printk(KERN_ERR DEVICE_NAME
499 ": Warning: 2.6.31+ kernel detected. Most likely the hardware performance\n"
500 DEVICE_NAME
501 ": counter framework which can generate NMIs is active. You have to prevent\n"
502 DEVICE_NAME
503 ": the usage of hardware performance counters by\n"
504 DEVICE_NAME
505 ": echo 2 > /proc/sys/kernel/perf_counter_paranoid\n");
506 /* We can't do more here :-( */
507 goto no_error;
508# endif
509
510# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) || defined CONFIG_X86_64
511 printk(KERN_ERR DEVICE_NAME
512 ": NMI watchdog either active or at least initialized. Please disable the NMI\n"
513 DEVICE_NAME
514 ": watchdog by specifying 'nmi_watchdog=0' at kernel command line.\n");
515 return -EINVAL;
516# else /* < 2.6.19 */
517# if !defined(VBOX_REDHAT_KABI)
518nmi_activated:
519# endif
520 printk(KERN_ERR DEVICE_NAME
521 ": NMI watchdog active -- refused to load the kernel module! Please disable\n"
522 DEVICE_NAME
523 ": the NMI watchdog by specifying 'nmi_watchdog=0' at kernel command line.\n");
524 return -EINVAL;
525# endif /* >= 2.6.19 */
526 }
527 }
528 }
529# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
530 printk(KERN_DEBUG DEVICE_NAME ": Successfully done.\n");
531# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) && defined(CONFIG_PERF_COUNTERS)
532no_error:
533# endif
534# endif /* >= 2.6.19 */
535#endif /* CONFIG_X86_LOCAL_APIC */
536
537 /*
538 * Check for synchronous/asynchronous TSC mode.
539 */
540 printk(KERN_DEBUG DEVICE_NAME ": Found %u processor cores.\n", (unsigned)RTMpGetOnlineCount());
541#ifdef CONFIG_VBOXDRV_AS_MISC
542 rc = misc_register(&gMiscDevice);
543 if (rc)
544 {
545 printk(KERN_ERR DEVICE_NAME ": Can't register misc device! rc=%d\n", rc);
546 return rc;
547 }
548#else /* !CONFIG_VBOXDRV_AS_MISC */
549 /*
550 * Register character device.
551 */
552 g_iModuleMajor = DEVICE_MAJOR;
553 rc = register_chrdev((dev_t)g_iModuleMajor, DEVICE_NAME, &gFileOpsVBoxDrv);
554 if (rc < 0)
555 {
556 dprintf(("register_chrdev() failed with rc=%#x!\n", rc));
557 return rc;
558 }
559
560 /*
561 * Save returned module major number
562 */
563 if (DEVICE_MAJOR != 0)
564 g_iModuleMajor = DEVICE_MAJOR;
565 else
566 g_iModuleMajor = rc;
567 rc = 0;
568
569# ifdef CONFIG_DEVFS_FS
570 /*
571 * Register a device entry
572 */
573 if (devfs_mk_cdev(MKDEV(DEVICE_MAJOR, 0), S_IFCHR | VBOX_DEV_FMASK, DEVICE_NAME) != 0)
574 {
575 dprintf(("devfs_register failed!\n"));
576 rc = -EINVAL;
577 }
578# endif
579#endif /* !CONFIG_VBOXDRV_AS_MISC */
580 if (!rc)
581 {
582 /*
583 * Initialize the runtime.
584 * On AMD64 we'll have to donate the high rwx memory block to the exec allocator.
585 */
586 rc = RTR0Init(0);
587 if (RT_SUCCESS(rc))
588 {
589#ifdef RT_ARCH_AMD64
590 rc = RTR0MemExecDonate(&g_abExecMemory[0], sizeof(g_abExecMemory));
591 printk("VBoxDrv: dbg - g_abExecMemory=%p\n", (void *)&g_abExecMemory[0]);
592#endif
593 /*
594 * Initialize the device extension.
595 */
596 if (RT_SUCCESS(rc))
597 rc = supdrvInitDevExt(&g_DevExt);
598 if (RT_SUCCESS(rc))
599 {
600#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
601 rc = platform_driver_register(&gPlatformDriver);
602 if (rc == 0)
603 {
604 rc = platform_device_register(&gPlatformDevice);
605 if (rc == 0)
606#endif
607 {
608 printk(KERN_INFO DEVICE_NAME ": TSC mode is %s, kernel timer mode is "
609#ifdef VBOX_HRTIMER
610 "'high-res'"
611#else
612 "'normal'"
613#endif
614 ".\n",
615 g_DevExt.pGip->u32Mode == SUPGIPMODE_SYNC_TSC ? "'synchronous'" : "'asynchronous'");
616 LogFlow(("VBoxDrv::ModuleInit returning %#x\n", rc));
617 printk(KERN_DEBUG DEVICE_NAME ": Successfully loaded version "
618 VBOX_VERSION_STRING " (interface " RT_XSTR(SUPDRV_IOC_VERSION) ").\n");
619 return rc;
620 }
621#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
622 else
623 platform_driver_unregister(&gPlatformDriver);
624 }
625#endif
626 }
627
628 rc = -EINVAL;
629 RTR0TermForced();
630 }
631 else
632 rc = -EINVAL;
633
634 /*
635 * Failed, cleanup and return the error code.
636 */
637#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC)
638 devfs_remove(DEVICE_NAME);
639#endif
640 }
641#ifdef CONFIG_VBOXDRV_AS_MISC
642 misc_deregister(&gMiscDevice);
643 dprintf(("VBoxDrv::ModuleInit returning %#x (minor:%d)\n", rc, gMiscDevice.minor));
644#else
645 unregister_chrdev(g_iModuleMajor, DEVICE_NAME);
646 dprintf(("VBoxDrv::ModuleInit returning %#x (major:%d)\n", rc, g_iModuleMajor));
647#endif
648 return rc;
649}
650
651
652/**
653 * Unload the module.
654 */
655static void __exit VBoxDrvLinuxUnload(void)
656{
657 int rc;
658 dprintf(("VBoxDrvLinuxUnload\n"));
659 NOREF(rc);
660
661#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
662 platform_device_unregister(&gPlatformDevice);
663 platform_driver_unregister(&gPlatformDriver);
664#endif
665
666 /*
667 * I Don't think it's possible to unload a driver which processes have
668 * opened, at least we'll blindly assume that here.
669 */
670#ifdef CONFIG_VBOXDRV_AS_MISC
671 rc = misc_deregister(&gMiscDevice);
672 if (rc < 0)
673 {
674 dprintf(("misc_deregister failed with rc=%#x\n", rc));
675 }
676#else /* !CONFIG_VBOXDRV_AS_MISC */
677# ifdef CONFIG_DEVFS_FS
678 /*
679 * Unregister a device entry
680 */
681 devfs_remove(DEVICE_NAME);
682# endif /* devfs */
683 unregister_chrdev(g_iModuleMajor, DEVICE_NAME);
684#endif /* !CONFIG_VBOXDRV_AS_MISC */
685
686 /*
687 * Destroy GIP, delete the device extension and terminate IPRT.
688 */
689 supdrvDeleteDevExt(&g_DevExt);
690 RTR0TermForced();
691}
692
693
694/**
695 * Device open. Called on open /dev/vboxdrv
696 *
697 * @param pInode Pointer to inode info structure.
698 * @param pFilp Associated file pointer.
699 */
700static int VBoxDrvLinuxCreate(struct inode *pInode, struct file *pFilp)
701{
702 int rc;
703 PSUPDRVSESSION pSession;
704 Log(("VBoxDrvLinuxCreate: pFilp=%p pid=%d/%d %s\n", pFilp, RTProcSelf(), current->pid, current->comm));
705
706#ifdef VBOX_WITH_HARDENING
707 /*
708 * Only root is allowed to access the device, enforce it!
709 */
710 if (vboxdrvLinuxEuid() != 0 /* root */ )
711 {
712 Log(("VBoxDrvLinuxCreate: euid=%d, expected 0 (root)\n", vboxdrvLinuxEuid()));
713 return -EPERM;
714 }
715#endif /* VBOX_WITH_HARDENING */
716
717 /*
718 * Call common code for the rest.
719 */
720 rc = supdrvCreateSession(&g_DevExt, true /* fUser */, (PSUPDRVSESSION *)&pSession);
721 if (!rc)
722 {
723 pSession->Uid = vboxdrvLinuxUid();
724 pSession->Gid = vboxdrvLinuxGid();
725 }
726
727 pFilp->private_data = pSession;
728
729 Log(("VBoxDrvLinuxCreate: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n",
730 &g_DevExt, pSession, rc, VBoxDrvLinuxErr2LinuxErr(rc),
731 RTProcSelf(), current->pid, current->comm));
732 return VBoxDrvLinuxErr2LinuxErr(rc);
733}
734
735
736/**
737 * Close device.
738 *
739 * @param pInode Pointer to inode info structure.
740 * @param pFilp Associated file pointer.
741 */
742static int VBoxDrvLinuxClose(struct inode *pInode, struct file *pFilp)
743{
744 Log(("VBoxDrvLinuxClose: pFilp=%p pSession=%p pid=%d/%d %s\n",
745 pFilp, pFilp->private_data, RTProcSelf(), current->pid, current->comm));
746 supdrvCloseSession(&g_DevExt, (PSUPDRVSESSION)pFilp->private_data);
747 pFilp->private_data = NULL;
748 return 0;
749}
750
751
752#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
753/**
754 * Dummy device release function. We have to provide this function,
755 * otherwise the kernel will complain.
756 *
757 * @param pDev Pointer to the platform device.
758 */
759static void VBoxDevRelease(struct device *pDev)
760{
761}
762
763/**
764 * Dummy probe function.
765 *
766 * @param pDev Pointer to the platform device.
767 */
768static int VBoxDrvProbe(struct platform_device *pDev)
769{
770 return 0;
771}
772
773/**
774 * Suspend callback.
775 * @param pDev Pointer to the platform device.
776 * @param State message type, see Documentation/power/devices.txt.
777 */
778# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
779static int VBoxDrvSuspend(struct device *pDev)
780# else
781static int VBoxDrvSuspend(struct platform_device *pDev, pm_message_t State)
782# endif
783{
784 RTPowerSignalEvent(RTPOWEREVENT_SUSPEND);
785 return 0;
786}
787
788/**
789 * Resume callback.
790 *
791 * @param pDev Pointer to the platform device.
792 */
793# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
794static int VBoxDrvResume(struct device *pDev)
795# else
796static int VBoxDrvResume(struct platform_device *pDev)
797# endif
798{
799 RTPowerSignalEvent(RTPOWEREVENT_RESUME);
800 return 0;
801}
802#endif /* VBOX_WITH_SUSPEND_NOTIFICATION */
803
804
805/**
806 * Device I/O Control entry point.
807 *
808 * @param pFilp Associated file pointer.
809 * @param uCmd The function specified to ioctl().
810 * @param ulArg The argument specified to ioctl().
811 */
812#ifdef HAVE_UNLOCKED_IOCTL
813static long VBoxDrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
814#else
815static int VBoxDrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
816#endif
817{
818 /*
819 * Deal with the two high-speed IOCtl that takes it's arguments from
820 * the session and iCmd, and only returns a VBox status code.
821 */
822#ifdef HAVE_UNLOCKED_IOCTL
823 if (RT_LIKELY( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN
824 || uCmd == SUP_IOCTL_FAST_DO_HWACC_RUN
825 || uCmd == SUP_IOCTL_FAST_DO_NOP))
826 return supdrvIOCtlFast(uCmd, ulArg, &g_DevExt, (PSUPDRVSESSION)pFilp->private_data);
827 return VBoxDrvLinuxIOCtlSlow(pFilp, uCmd, ulArg);
828
829#else /* !HAVE_UNLOCKED_IOCTL */
830
831 int rc;
832 unlock_kernel();
833 if (RT_LIKELY( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN
834 || uCmd == SUP_IOCTL_FAST_DO_HWACC_RUN
835 || uCmd == SUP_IOCTL_FAST_DO_NOP))
836 rc = supdrvIOCtlFast(uCmd, ulArg, &g_DevExt, (PSUPDRVSESSION)pFilp->private_data);
837 else
838 rc = VBoxDrvLinuxIOCtlSlow(pFilp, uCmd, ulArg);
839 lock_kernel();
840 return rc;
841#endif /* !HAVE_UNLOCKED_IOCTL */
842}
843
844
845/**
846 * Device I/O Control entry point.
847 *
848 * @param pFilp Associated file pointer.
849 * @param uCmd The function specified to ioctl().
850 * @param ulArg The argument specified to ioctl().
851 */
852static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
853{
854 int rc;
855 SUPREQHDR Hdr;
856 PSUPREQHDR pHdr;
857 uint32_t cbBuf;
858
859 Log6(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid));
860
861 /*
862 * Read the header.
863 */
864 if (RT_UNLIKELY(copy_from_user(&Hdr, (void *)ulArg, sizeof(Hdr))))
865 {
866 Log(("VBoxDrvLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd));
867 return -EFAULT;
868 }
869 if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
870 {
871 Log(("VBoxDrvLinuxIOCtl: bad header magic %#x; uCmd=%#x\n", Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK, uCmd));
872 return -EINVAL;
873 }
874
875 /*
876 * Buffer the request.
877 */
878 cbBuf = RT_MAX(Hdr.cbIn, Hdr.cbOut);
879 if (RT_UNLIKELY(cbBuf > _1M*16))
880 {
881 Log(("VBoxDrvLinuxIOCtl: too big cbBuf=%#x; uCmd=%#x\n", cbBuf, uCmd));
882 return -E2BIG;
883 }
884 if (RT_UNLIKELY(cbBuf != _IOC_SIZE(uCmd) && _IOC_SIZE(uCmd)))
885 {
886 Log(("VBoxDrvLinuxIOCtl: bad ioctl cbBuf=%#x _IOC_SIZE=%#x; uCmd=%#x.\n", cbBuf, _IOC_SIZE(uCmd), uCmd));
887 return -EINVAL;
888 }
889 pHdr = RTMemAlloc(cbBuf);
890 if (RT_UNLIKELY(!pHdr))
891 {
892 OSDBGPRINT(("VBoxDrvLinuxIOCtl: failed to allocate buffer of %d bytes for uCmd=%#x.\n", cbBuf, uCmd));
893 return -ENOMEM;
894 }
895 if (RT_UNLIKELY(copy_from_user(pHdr, (void *)ulArg, Hdr.cbIn)))
896 {
897 Log(("VBoxDrvLinuxIOCtl: copy_from_user(,%#lx, %#x) failed; uCmd=%#x.\n", ulArg, Hdr.cbIn, uCmd));
898 RTMemFree(pHdr);
899 return -EFAULT;
900 }
901
902 /*
903 * Process the IOCtl.
904 */
905 rc = supdrvIOCtl(uCmd, &g_DevExt, (PSUPDRVSESSION)pFilp->private_data, pHdr);
906
907 /*
908 * Copy ioctl data and output buffer back to user space.
909 */
910 if (RT_LIKELY(!rc))
911 {
912 uint32_t cbOut = pHdr->cbOut;
913 if (RT_UNLIKELY(cbOut > cbBuf))
914 {
915 OSDBGPRINT(("VBoxDrvLinuxIOCtl: too much output! %#x > %#x; uCmd=%#x!\n", cbOut, cbBuf, uCmd));
916 cbOut = cbBuf;
917 }
918 if (RT_UNLIKELY(copy_to_user((void *)ulArg, pHdr, cbOut)))
919 {
920 /* this is really bad! */
921 OSDBGPRINT(("VBoxDrvLinuxIOCtl: copy_to_user(%#lx,,%#x); uCmd=%#x!\n", ulArg, cbOut, uCmd));
922 rc = -EFAULT;
923 }
924 }
925 else
926 {
927 Log(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc));
928 rc = -EINVAL;
929 }
930 RTMemFree(pHdr);
931
932 Log6(("VBoxDrvLinuxIOCtl: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid));
933 return rc;
934}
935
936
937/**
938 * The SUPDRV IDC entry point.
939 *
940 * @returns VBox status code, see supdrvIDC.
941 * @param iReq The request code.
942 * @param pReq The request.
943 */
944int VBOXCALL SUPDrvLinuxIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq)
945{
946 PSUPDRVSESSION pSession;
947
948 /*
949 * Some quick validations.
950 */
951 if (RT_UNLIKELY(!VALID_PTR(pReq)))
952 return VERR_INVALID_POINTER;
953
954 pSession = pReq->pSession;
955 if (pSession)
956 {
957 if (RT_UNLIKELY(!VALID_PTR(pSession)))
958 return VERR_INVALID_PARAMETER;
959 if (RT_UNLIKELY(pSession->pDevExt != &g_DevExt))
960 return VERR_INVALID_PARAMETER;
961 }
962 else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT))
963 return VERR_INVALID_PARAMETER;
964
965 /*
966 * Do the job.
967 */
968 return supdrvIDC(uReq, &g_DevExt, pSession, pReq);
969}
970
971EXPORT_SYMBOL(SUPDrvLinuxIDC);
972
973
974/**
975 * Initializes any OS specific object creator fields.
976 */
977void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
978{
979 NOREF(pObj);
980 NOREF(pSession);
981}
982
983
984/**
985 * Checks if the session can access the object.
986 *
987 * @returns true if a decision has been made.
988 * @returns false if the default access policy should be applied.
989 *
990 * @param pObj The object in question.
991 * @param pSession The session wanting to access the object.
992 * @param pszObjName The object name, can be NULL.
993 * @param prc Where to store the result when returning true.
994 */
995bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
996{
997 NOREF(pObj);
998 NOREF(pSession);
999 NOREF(pszObjName);
1000 NOREF(prc);
1001 return false;
1002}
1003
1004
1005bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt)
1006{
1007 return force_async_tsc != 0;
1008}
1009
1010
1011int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
1012{
1013 NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
1014 return VERR_NOT_SUPPORTED;
1015}
1016
1017
1018int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv, const uint8_t *pbImageBits)
1019{
1020 NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits);
1021 return VERR_NOT_SUPPORTED;
1022}
1023
1024
1025int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits)
1026{
1027 NOREF(pDevExt); NOREF(pImage); NOREF(pbImageBits);
1028 return VERR_NOT_SUPPORTED;
1029}
1030
1031
1032void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
1033{
1034 NOREF(pDevExt); NOREF(pImage);
1035}
1036
1037
1038/**
1039 * Converts a supdrv error code to an linux error code.
1040 *
1041 * @returns corresponding linux error code.
1042 * @param rc supdrv error code (SUPDRV_ERR_* defines).
1043 */
1044static int VBoxDrvLinuxErr2LinuxErr(int rc)
1045{
1046 switch (rc)
1047 {
1048 case 0: return 0;
1049 case SUPDRV_ERR_GENERAL_FAILURE: return -EACCES;
1050 case SUPDRV_ERR_INVALID_PARAM: return -EINVAL;
1051 case SUPDRV_ERR_INVALID_MAGIC: return -EILSEQ;
1052 case SUPDRV_ERR_INVALID_HANDLE: return -ENXIO;
1053 case SUPDRV_ERR_INVALID_POINTER: return -EFAULT;
1054 case SUPDRV_ERR_LOCK_FAILED: return -ENOLCK;
1055 case SUPDRV_ERR_ALREADY_LOADED: return -EEXIST;
1056 case SUPDRV_ERR_PERMISSION_DENIED: return -EPERM;
1057 case SUPDRV_ERR_VERSION_MISMATCH: return -ENOSYS;
1058 case SUPDRV_ERR_IDT_FAILED: return -1000;
1059 }
1060
1061 return -EPERM;
1062}
1063
1064
1065RTDECL(int) SUPR0Printf(const char *pszFormat, ...)
1066{
1067#if 1
1068 va_list args;
1069 char szMsg[512];
1070
1071 va_start(args, pszFormat);
1072 vsnprintf(szMsg, sizeof(szMsg) - 1, pszFormat, args);
1073 szMsg[sizeof(szMsg) - 1] = '\0';
1074 printk("%s", szMsg);
1075 va_end(args);
1076#else
1077 /* forward to printf - needs some more GCC hacking to fix ebp... */
1078 __asm__ __volatile__ ("mov %0, %esp\n\t"
1079 "jmp %1\n\t",
1080 :: "r" ((uintptr_t)&pszFormat - 4),
1081 "m" (printk));
1082#endif
1083 return 0;
1084}
1085
1086module_init(VBoxDrvLinuxInit);
1087module_exit(VBoxDrvLinuxUnload);
1088
1089MODULE_AUTHOR("Sun Microsystems, Inc.");
1090MODULE_DESCRIPTION("VirtualBox Support Driver");
1091MODULE_LICENSE("GPL");
1092#ifdef MODULE_VERSION
1093MODULE_VERSION(VBOX_VERSION_STRING " (" RT_XSTR(SUPDRV_IOC_VERSION) ")");
1094#endif
1095
1096module_param(force_async_tsc, int, 0444);
1097MODULE_PARM_DESC(force_async_tsc, "force the asynchronous TSC mode");
1098
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette