VirtualBox

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

Last change on this file since 10682 was 10680, checked in by vboxsync, 17 years ago

Increase minor version, needed some timestamp functions.

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