VirtualBox

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

Last change on this file since 10297 was 10263, checked in by vboxsync, 17 years ago

Inter-Driver Communication (IDC) interface for the support driver. The OS specific bits in SUPDrv.

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