SUPDrv-linux.c revision 833e61de8bb4cce493af884821fdd51d01c75048
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/* $Rev$ */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/** @file
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * VBoxDrv - The VirtualBox Support Driver - Linux specifics.
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/*
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * Copyright (C) 2006-2007 Oracle Corporation
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync *
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * available from http://www.virtualbox.org. This file is free software;
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * you can redistribute it and/or modify it under the terms of the GNU
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * General Public License (GPL) as published by the Free Software
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync *
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * The contents of this file may alternatively be used under the terms
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * of the Common Development and Distribution License Version 1.0
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * VirtualBox OSE distribution, in which case the provisions of the
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * CDDL are applicable instead of those of the GPL.
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync *
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * You may elect to license modified versions of this file under the
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * terms and conditions of either the GPL or the CDDL or both.
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/*******************************************************************************
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync* Header Files *
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync*******************************************************************************/
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#define LOG_GROUP LOG_GROUP_SUP_DRV
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include "../SUPDrvInternal.h"
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include "the-linux-kernel.h"
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include "version-generated.h"
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include "product-generated.h"
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include <iprt/assert.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include <iprt/spinlock.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include <iprt/semaphore.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include <iprt/initterm.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include <iprt/process.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include <VBox/err.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include <iprt/mem.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include <VBox/log.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include <iprt/mp.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/** @todo figure out the exact version number */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# include <iprt/power.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# define VBOX_WITH_SUSPEND_NOTIFICATION
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include <linux/sched.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#ifdef CONFIG_DEVFS_FS
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# include <linux/devfs_fs_kernel.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#ifdef CONFIG_VBOXDRV_AS_MISC
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# include <linux/miscdevice.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# include <linux/platform_device.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#include <iprt/mem.h>
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/*******************************************************************************
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync* Defined Constants And Macros *
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync*******************************************************************************/
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/* check kernel version */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# ifndef SUPDRV_AGNOSTIC
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# error Unsupported kernel version!
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/* devfs defines */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# ifdef VBOX_WITH_HARDENING
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# define VBOX_DEV_FMASK (S_IWUSR | S_IRUSR)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# else
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# define VBOX_DEV_FMASK (S_IRUGO | S_IWUGO)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif /* CONFIG_DEV_FS && !CONFIG_VBOXDEV_AS_MISC */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#ifdef CONFIG_X86_HIGH_ENTRY
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# error "CONFIG_X86_HIGH_ENTRY is not supported by VBoxDrv at this time."
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/*******************************************************************************
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync* Internal Functions *
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync*******************************************************************************/
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int VBoxDrvLinuxInit(void);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic void VBoxDrvLinuxUnload(void);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int VBoxDrvLinuxCreate(struct inode *pInode, struct file *pFilp);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int VBoxDrvLinuxClose(struct inode *pInode, struct file *pFilp);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#ifdef HAVE_UNLOCKED_IOCTL
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic long VBoxDrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#else
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int VBoxDrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int VBoxDrvLinuxErr2LinuxErr(int);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int VBoxDrvProbe(struct platform_device *pDev);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int VBoxDrvSuspend(struct device *pDev);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int VBoxDrvResume(struct device *pDev);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# else
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int VBoxDrvSuspend(struct platform_device *pDev, pm_message_t State);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int VBoxDrvResume(struct platform_device *pDev);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic void VBoxDevRelease(struct device *pDev);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/*******************************************************************************
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync* Global Variables *
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync*******************************************************************************/
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/**
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * Device extention & session data association structure.
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic SUPDRVDEVEXT g_DevExt;
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#ifndef CONFIG_VBOXDRV_AS_MISC
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/** Module major number */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#define DEVICE_MAJOR 234
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/** Saved major device number */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int g_iModuleMajor;
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif /* !CONFIG_VBOXDRV_AS_MISC */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/** Module parameter.
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * Not prefixed because the name is used by macros and the end of this file. */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int force_async_tsc = 0;
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/** The module name. */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#define DEVICE_NAME "vboxdrv"
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#ifdef RT_ARCH_AMD64
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/**
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * Memory for the executable memory heap (in IPRT).
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncextern uint8_t g_abExecMemory[1572864]; /* 1.5 MB */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync__asm__(".section execmemory, \"awx\", @progbits\n\t"
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync ".align 32\n\t"
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync ".globl g_abExecMemory\n"
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync "g_abExecMemory:\n\t"
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync ".zero 1572864\n\t"
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync ".type g_abExecMemory, @object\n\t"
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync ".size g_abExecMemory, 1572864\n\t"
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync ".text\n\t");
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/** The file_operations structure. */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic struct file_operations gFileOpsVBoxDrv =
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync{
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync owner: THIS_MODULE,
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync open: VBoxDrvLinuxCreate,
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync release: VBoxDrvLinuxClose,
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#ifdef HAVE_UNLOCKED_IOCTL
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync unlocked_ioctl: VBoxDrvLinuxIOCtl,
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#else
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync ioctl: VBoxDrvLinuxIOCtl,
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync};
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#ifdef CONFIG_VBOXDRV_AS_MISC
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/** The miscdevice structure. */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic struct miscdevice gMiscDevice =
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync{
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync minor: MISC_DYNAMIC_MINOR,
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync name: DEVICE_NAME,
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync fops: &gFileOpsVBoxDrv,
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 17)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync devfs_name: DEVICE_NAME,
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync};
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic struct dev_pm_ops gPlatformPMOps =
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync{
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync .suspend = VBoxDrvSuspend, /* before entering deep sleep */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync .resume = VBoxDrvResume, /* after wakeup from deep sleep */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync .freeze = VBoxDrvSuspend, /* before creating hibernation image */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync .restore = VBoxDrvResume, /* after waking up from hibernation */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync};
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic struct platform_driver gPlatformDriver =
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync{
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync .probe = VBoxDrvProbe,
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync .suspend = VBoxDrvSuspend,
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync .resume = VBoxDrvResume,
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync /** @todo .shutdown? */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync .driver =
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync {
a0a9f39e8864357c2e1e61106958411240f5bf6bvboxsync .name = "vboxdrv",
a0a9f39e8864357c2e1e61106958411240f5bf6bvboxsync# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync .pm = &gPlatformPMOps,
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync# endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync }
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync};
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic struct platform_device gPlatformDevice =
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync{
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync .name = "vboxdrv",
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync .dev =
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync {
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync .release = VBoxDevRelease
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync }
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync};
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif /* VBOX_WITH_SUSPEND_NOTIFICATION */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncDECLINLINE(RTUID) vboxdrvLinuxUid(void)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync{
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync return current->cred->uid;
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#else
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync return current->uid;
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync}
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncDECLINLINE(RTGID) vboxdrvLinuxGid(void)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync{
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync return current->cred->gid;
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#else
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync return current->gid;
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync}
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncDECLINLINE(RTUID) vboxdrvLinuxEuid(void)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync{
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync return current->cred->euid;
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#else
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync return current->euid;
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#endif
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync}
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync/**
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * Initialize module.
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync *
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * @returns appropriate status code.
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsyncstatic int __init VBoxDrvLinuxInit(void)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync{
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync int rc;
a0a9f39e8864357c2e1e61106958411240f5bf6bvboxsync
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync /*
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * Check for synchronous/asynchronous TSC mode.
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync printk(KERN_DEBUG DEVICE_NAME ": Found %u processor cores.\n", (unsigned)RTMpGetOnlineCount());
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#ifdef CONFIG_VBOXDRV_AS_MISC
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync rc = misc_register(&gMiscDevice);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync if (rc)
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync {
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync printk(KERN_ERR DEVICE_NAME ": Can't register misc device! rc=%d\n", rc);
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync return rc;
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync }
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync#else /* !CONFIG_VBOXDRV_AS_MISC */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync /*
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync * Register character device.
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync */
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync g_iModuleMajor = DEVICE_MAJOR;
19a258565a4d24e5a0af62f626943ac9b898d957vboxsync rc = register_chrdev((dev_t)g_iModuleMajor, DEVICE_NAME, &gFileOpsVBoxDrv);
if (rc < 0)
{
Log(("register_chrdev() failed with rc=%#x!\n", rc));
return rc;
}
/*
* Save returned module major number
*/
if (DEVICE_MAJOR != 0)
g_iModuleMajor = DEVICE_MAJOR;
else
g_iModuleMajor = rc;
rc = 0;
# ifdef CONFIG_DEVFS_FS
/*
* Register a device entry
*/
if (devfs_mk_cdev(MKDEV(DEVICE_MAJOR, 0), S_IFCHR | VBOX_DEV_FMASK, DEVICE_NAME) != 0)
{
Log(("devfs_register failed!\n"));
rc = -EINVAL;
}
# endif
#endif /* !CONFIG_VBOXDRV_AS_MISC */
if (!rc)
{
/*
* Initialize the runtime.
* On AMD64 we'll have to donate the high rwx memory block to the exec allocator.
*/
rc = RTR0Init(0);
if (RT_SUCCESS(rc))
{
#ifdef RT_ARCH_AMD64
rc = RTR0MemExecDonate(&g_abExecMemory[0], sizeof(g_abExecMemory));
printk("VBoxDrv: dbg - g_abExecMemory=%p\n", (void *)&g_abExecMemory[0]);
#endif
Log(("VBoxDrv::ModuleInit\n"));
/*
* Initialize the device extension.
*/
if (RT_SUCCESS(rc))
rc = supdrvInitDevExt(&g_DevExt, sizeof(SUPDRVSESSION));
if (RT_SUCCESS(rc))
{
#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
rc = platform_driver_register(&gPlatformDriver);
if (rc == 0)
{
rc = platform_device_register(&gPlatformDevice);
if (rc == 0)
#endif
{
printk(KERN_INFO DEVICE_NAME ": TSC mode is %s, kernel timer mode is "
#ifdef VBOX_HRTIMER
"'high-res'"
#else
"'normal'"
#endif
".\n",
g_DevExt.pGip->u32Mode == SUPGIPMODE_SYNC_TSC ? "'synchronous'" : "'asynchronous'");
LogFlow(("VBoxDrv::ModuleInit returning %#x\n", rc));
printk(KERN_DEBUG DEVICE_NAME ": Successfully loaded version "
VBOX_VERSION_STRING " (interface " RT_XSTR(SUPDRV_IOC_VERSION) ").\n");
return rc;
}
#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
else
platform_driver_unregister(&gPlatformDriver);
}
#endif
}
rc = -EINVAL;
RTR0TermForced();
}
else
rc = -EINVAL;
/*
* Failed, cleanup and return the error code.
*/
#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC)
devfs_remove(DEVICE_NAME);
#endif
}
#ifdef CONFIG_VBOXDRV_AS_MISC
misc_deregister(&gMiscDevice);
Log(("VBoxDrv::ModuleInit returning %#x (minor:%d)\n", rc, gMiscDevice.minor));
#else
unregister_chrdev(g_iModuleMajor, DEVICE_NAME);
Log(("VBoxDrv::ModuleInit returning %#x (major:%d)\n", rc, g_iModuleMajor));
#endif
return rc;
}
/**
* Unload the module.
*/
static void __exit VBoxDrvLinuxUnload(void)
{
int rc;
Log(("VBoxDrvLinuxUnload\n"));
NOREF(rc);
#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
platform_device_unregister(&gPlatformDevice);
platform_driver_unregister(&gPlatformDriver);
#endif
/*
* I Don't think it's possible to unload a driver which processes have
* opened, at least we'll blindly assume that here.
*/
#ifdef CONFIG_VBOXDRV_AS_MISC
rc = misc_deregister(&gMiscDevice);
if (rc < 0)
{
Log(("misc_deregister failed with rc=%#x\n", rc));
}
#else /* !CONFIG_VBOXDRV_AS_MISC */
# ifdef CONFIG_DEVFS_FS
/*
* Unregister a device entry
*/
devfs_remove(DEVICE_NAME);
# endif /* devfs */
unregister_chrdev(g_iModuleMajor, DEVICE_NAME);
#endif /* !CONFIG_VBOXDRV_AS_MISC */
/*
* Destroy GIP, delete the device extension and terminate IPRT.
*/
supdrvDeleteDevExt(&g_DevExt);
RTR0TermForced();
}
/**
* Device open. Called on open /dev/vboxdrv
*
* @param pInode Pointer to inode info structure.
* @param pFilp Associated file pointer.
*/
static int VBoxDrvLinuxCreate(struct inode *pInode, struct file *pFilp)
{
int rc;
PSUPDRVSESSION pSession;
Log(("VBoxDrvLinuxCreate: pFilp=%p pid=%d/%d %s\n", pFilp, RTProcSelf(), current->pid, current->comm));
#ifdef VBOX_WITH_HARDENING
/*
* Only root is allowed to access the device, enforce it!
*/
if (vboxdrvLinuxEuid() != 0 /* root */ )
{
Log(("VBoxDrvLinuxCreate: euid=%d, expected 0 (root)\n", vboxdrvLinuxEuid()));
return -EPERM;
}
#endif /* VBOX_WITH_HARDENING */
/*
* Call common code for the rest.
*/
rc = supdrvCreateSession(&g_DevExt, true /* fUser */, &pSession);
if (!rc)
{
pSession->Uid = vboxdrvLinuxUid();
pSession->Gid = vboxdrvLinuxGid();
}
pFilp->private_data = pSession;
Log(("VBoxDrvLinuxCreate: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n",
&g_DevExt, pSession, rc, VBoxDrvLinuxErr2LinuxErr(rc),
RTProcSelf(), current->pid, current->comm));
return VBoxDrvLinuxErr2LinuxErr(rc);
}
/**
* Close device.
*
* @param pInode Pointer to inode info structure.
* @param pFilp Associated file pointer.
*/
static int VBoxDrvLinuxClose(struct inode *pInode, struct file *pFilp)
{
Log(("VBoxDrvLinuxClose: pFilp=%p pSession=%p pid=%d/%d %s\n",
pFilp, pFilp->private_data, RTProcSelf(), current->pid, current->comm));
supdrvCloseSession(&g_DevExt, (PSUPDRVSESSION)pFilp->private_data);
pFilp->private_data = NULL;
return 0;
}
#ifdef VBOX_WITH_SUSPEND_NOTIFICATION
/**
* Dummy device release function. We have to provide this function,
* otherwise the kernel will complain.
*
* @param pDev Pointer to the platform device.
*/
static void VBoxDevRelease(struct device *pDev)
{
}
/**
* Dummy probe function.
*
* @param pDev Pointer to the platform device.
*/
static int VBoxDrvProbe(struct platform_device *pDev)
{
return 0;
}
/**
* Suspend callback.
* @param pDev Pointer to the platform device.
* @param State message type, see Documentation/power/devices.txt.
*/
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
static int VBoxDrvSuspend(struct device *pDev)
# else
static int VBoxDrvSuspend(struct platform_device *pDev, pm_message_t State)
# endif
{
RTPowerSignalEvent(RTPOWEREVENT_SUSPEND);
return 0;
}
/**
* Resume callback.
*
* @param pDev Pointer to the platform device.
*/
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
static int VBoxDrvResume(struct device *pDev)
# else
static int VBoxDrvResume(struct platform_device *pDev)
# endif
{
RTPowerSignalEvent(RTPOWEREVENT_RESUME);
return 0;
}
#endif /* VBOX_WITH_SUSPEND_NOTIFICATION */
/**
* Device I/O Control entry point.
*
* @param pFilp Associated file pointer.
* @param uCmd The function specified to ioctl().
* @param ulArg The argument specified to ioctl().
*/
#ifdef HAVE_UNLOCKED_IOCTL
static long VBoxDrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
#else
static int VBoxDrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
#endif
{
/*
* Deal with the two high-speed IOCtl that takes it's arguments from
* the session and iCmd, and only returns a VBox status code.
*/
#ifdef HAVE_UNLOCKED_IOCTL
if (RT_LIKELY( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN
|| uCmd == SUP_IOCTL_FAST_DO_HWACC_RUN
|| uCmd == SUP_IOCTL_FAST_DO_NOP))
return supdrvIOCtlFast(uCmd, ulArg, &g_DevExt, (PSUPDRVSESSION)pFilp->private_data);
return VBoxDrvLinuxIOCtlSlow(pFilp, uCmd, ulArg);
#else /* !HAVE_UNLOCKED_IOCTL */
int rc;
unlock_kernel();
if (RT_LIKELY( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN
|| uCmd == SUP_IOCTL_FAST_DO_HWACC_RUN
|| uCmd == SUP_IOCTL_FAST_DO_NOP))
rc = supdrvIOCtlFast(uCmd, ulArg, &g_DevExt, (PSUPDRVSESSION)pFilp->private_data);
else
rc = VBoxDrvLinuxIOCtlSlow(pFilp, uCmd, ulArg);
lock_kernel();
return rc;
#endif /* !HAVE_UNLOCKED_IOCTL */
}
/**
* Device I/O Control entry point.
*
* @param pFilp Associated file pointer.
* @param uCmd The function specified to ioctl().
* @param ulArg The argument specified to ioctl().
*/
static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
{
int rc;
SUPREQHDR Hdr;
PSUPREQHDR pHdr;
uint32_t cbBuf;
Log6(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid));
/*
* Read the header.
*/
if (RT_UNLIKELY(copy_from_user(&Hdr, (void *)ulArg, sizeof(Hdr))))
{
Log(("VBoxDrvLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd));
return -EFAULT;
}
if (RT_UNLIKELY((Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK) != SUPREQHDR_FLAGS_MAGIC))
{
Log(("VBoxDrvLinuxIOCtl: bad header magic %#x; uCmd=%#x\n", Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK, uCmd));
return -EINVAL;
}
/*
* Buffer the request.
*/
cbBuf = RT_MAX(Hdr.cbIn, Hdr.cbOut);
if (RT_UNLIKELY(cbBuf > _1M*16))
{
Log(("VBoxDrvLinuxIOCtl: too big cbBuf=%#x; uCmd=%#x\n", cbBuf, uCmd));
return -E2BIG;
}
if (RT_UNLIKELY(cbBuf != _IOC_SIZE(uCmd) && _IOC_SIZE(uCmd)))
{
Log(("VBoxDrvLinuxIOCtl: bad ioctl cbBuf=%#x _IOC_SIZE=%#x; uCmd=%#x.\n", cbBuf, _IOC_SIZE(uCmd), uCmd));
return -EINVAL;
}
pHdr = RTMemAlloc(cbBuf);
if (RT_UNLIKELY(!pHdr))
{
OSDBGPRINT(("VBoxDrvLinuxIOCtl: failed to allocate buffer of %d bytes for uCmd=%#x.\n", cbBuf, uCmd));
return -ENOMEM;
}
if (RT_UNLIKELY(copy_from_user(pHdr, (void *)ulArg, Hdr.cbIn)))
{
Log(("VBoxDrvLinuxIOCtl: copy_from_user(,%#lx, %#x) failed; uCmd=%#x.\n", ulArg, Hdr.cbIn, uCmd));
RTMemFree(pHdr);
return -EFAULT;
}
/*
* Process the IOCtl.
*/
rc = supdrvIOCtl(uCmd, &g_DevExt, (PSUPDRVSESSION)pFilp->private_data, pHdr);
/*
* Copy ioctl data and output buffer back to user space.
*/
if (RT_LIKELY(!rc))
{
uint32_t cbOut = pHdr->cbOut;
if (RT_UNLIKELY(cbOut > cbBuf))
{
OSDBGPRINT(("VBoxDrvLinuxIOCtl: too much output! %#x > %#x; uCmd=%#x!\n", cbOut, cbBuf, uCmd));
cbOut = cbBuf;
}
if (RT_UNLIKELY(copy_to_user((void *)ulArg, pHdr, cbOut)))
{
/* this is really bad! */
OSDBGPRINT(("VBoxDrvLinuxIOCtl: copy_to_user(%#lx,,%#x); uCmd=%#x!\n", ulArg, cbOut, uCmd));
rc = -EFAULT;
}
}
else
{
Log(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc));
rc = -EINVAL;
}
RTMemFree(pHdr);
Log6(("VBoxDrvLinuxIOCtl: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid));
return rc;
}
/**
* The SUPDRV IDC entry point.
*
* @returns VBox status code, see supdrvIDC.
* @param iReq The request code.
* @param pReq The request.
*/
int VBOXCALL SUPDrvLinuxIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq)
{
PSUPDRVSESSION pSession;
/*
* Some quick validations.
*/
if (RT_UNLIKELY(!VALID_PTR(pReq)))
return VERR_INVALID_POINTER;
pSession = pReq->pSession;
if (pSession)
{
if (RT_UNLIKELY(!VALID_PTR(pSession)))
return VERR_INVALID_PARAMETER;
if (RT_UNLIKELY(pSession->pDevExt != &g_DevExt))
return VERR_INVALID_PARAMETER;
}
else if (RT_UNLIKELY(uReq != SUPDRV_IDC_REQ_CONNECT))
return VERR_INVALID_PARAMETER;
/*
* Do the job.
*/
return supdrvIDC(uReq, &g_DevExt, pSession, pReq);
}
EXPORT_SYMBOL(SUPDrvLinuxIDC);
/**
* Initializes any OS specific object creator fields.
*/
void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
{
NOREF(pObj);
NOREF(pSession);
}
/**
* Checks if the session can access the object.
*
* @returns true if a decision has been made.
* @returns false if the default access policy should be applied.
*
* @param pObj The object in question.
* @param pSession The session wanting to access the object.
* @param pszObjName The object name, can be NULL.
* @param prc Where to store the result when returning true.
*/
bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
{
NOREF(pObj);
NOREF(pSession);
NOREF(pszObjName);
NOREF(prc);
return false;
}
bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt)
{
return force_async_tsc != 0;
}
int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
{
NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
return VERR_NOT_SUPPORTED;
}
int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv, const uint8_t *pbImageBits)
{
NOREF(pDevExt); NOREF(pImage); NOREF(pv); NOREF(pbImageBits);
return VERR_NOT_SUPPORTED;
}
int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits)
{
NOREF(pDevExt); NOREF(pImage); NOREF(pbImageBits);
return VERR_NOT_SUPPORTED;
}
void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
{
NOREF(pDevExt); NOREF(pImage);
}
/**
* Converts a supdrv error code to an linux error code.
*
* @returns corresponding linux error code.
* @param rc IPRT status code.
*/
static int VBoxDrvLinuxErr2LinuxErr(int rc)
{
switch (rc)
{
case VINF_SUCCESS: return 0;
case VERR_GENERAL_FAILURE: return -EACCES;
case VERR_INVALID_PARAMETER: return -EINVAL;
case VERR_INVALID_MAGIC: return -EILSEQ;
case VERR_INVALID_HANDLE: return -ENXIO;
case VERR_INVALID_POINTER: return -EFAULT;
case VERR_LOCK_FAILED: return -ENOLCK;
case VERR_ALREADY_LOADED: return -EEXIST;
case VERR_PERMISSION_DENIED: return -EPERM;
case VERR_VERSION_MISMATCH: return -ENOSYS;
case VERR_IDT_FAILED: return -1000;
}
return -EPERM;
}
RTDECL(int) SUPR0Printf(const char *pszFormat, ...)
{
#if 1
va_list args;
char szMsg[512];
va_start(args, pszFormat);
vsnprintf(szMsg, sizeof(szMsg) - 1, pszFormat, args);
szMsg[sizeof(szMsg) - 1] = '\0';
printk("%s", szMsg);
va_end(args);
#else
/* forward to printf - needs some more GCC hacking to fix ebp... */
__asm__ __volatile__ ("mov %0, %esp\n\t"
"jmp %1\n\t",
:: "r" ((uintptr_t)&pszFormat - 4),
"m" (printk));
#endif
return 0;
}
module_init(VBoxDrvLinuxInit);
module_exit(VBoxDrvLinuxUnload);
MODULE_AUTHOR(VBOX_VENDOR);
MODULE_DESCRIPTION(VBOX_PRODUCT " Support Driver");
MODULE_LICENSE("GPL");
#ifdef MODULE_VERSION
MODULE_VERSION(VBOX_VERSION_STRING " (" RT_XSTR(SUPDRV_IOC_VERSION) ")");
#endif
module_param(force_async_tsc, int, 0444);
MODULE_PARM_DESC(force_async_tsc, "force the asynchronous TSC mode");