SUPDrv-linux.c revision cf4b84bc96451ba1d379d78261dd750661e21cf0
/* $Rev$ */
/** @file
* VBoxDrv - The VirtualBox Support Driver - Linux specifics.
*/
/*
* Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SUP_DRV
#include "../SUPDrvInternal.h"
#include "the-linux-kernel.h"
#include "version-generated.h"
#include "product-generated.h"
#include <iprt/spinlock.h>
#include <iprt/semaphore.h>
#include <iprt/initterm.h>
/** @todo figure out the exact version number */
# define VBOX_WITH_SUSPEND_NOTIFICATION
#endif
#ifdef CONFIG_DEVFS_FS
# include <linux/devfs_fs_kernel.h>
#endif
#ifdef CONFIG_VBOXDRV_AS_MISC
# include <linux/miscdevice.h>
#endif
# include <linux/platform_device.h>
#endif
# define SUPDRV_LINUX_HAS_SAFE_MSR_API
# include <iprt/asm-amd64-x86.h>
#endif
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/* check kernel version */
# ifndef SUPDRV_AGNOSTIC
# endif
# endif
/* devfs defines */
#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC)
# ifdef VBOX_WITH_HARDENING
# else
# endif
#endif /* CONFIG_DEV_FS && !CONFIG_VBOXDEV_AS_MISC */
#ifdef CONFIG_X86_HIGH_ENTRY
# error "CONFIG_X86_HIGH_ENTRY is not supported by VBoxDrv at this time."
#endif
/* to include the version number of VirtualBox into kernel backtraces */
RT_CONCAT(VBOX_VERSION_MINOR, _), \
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int VBoxDrvLinuxInit(void);
static void VBoxDrvLinuxUnload(void);
#ifdef HAVE_UNLOCKED_IOCTL
#else
static int VBoxDrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
#endif
static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PSUPDRVSESSION pSession);
static int VBoxDrvLinuxErr2LinuxErr(int);
# else
# endif
#endif
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/**
* Device extention & session data association structure.
*/
static SUPDRVDEVEXT g_DevExt;
#ifndef CONFIG_VBOXDRV_AS_MISC
/** Module major number for vboxdrv. */
#define DEVICE_MAJOR_SYS 234
/** Saved major device number for vboxdrv. */
static int g_iModuleMajorSys;
/** Module major number for vboxdrvu. */
#define DEVICE_MAJOR_USR 235
/** Saved major device number for vboxdrvu. */
static int g_iModuleMajorUsr;
#endif /* !CONFIG_VBOXDRV_AS_MISC */
/** Module parameter.
* Not prefixed because the name is used by macros and the end of this file. */
static int force_async_tsc = 0;
/** The system device name. */
#define DEVICE_NAME_SYS "vboxdrv"
/** The user device name. */
#define DEVICE_NAME_USR "vboxdrvu"
#if (defined(RT_ARCH_AMD64) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)) || defined(VBOX_WITH_TEXT_MODMEM_HACK)
/**
* Memory for the executable memory heap (in IPRT).
*/
# ifdef DEBUG
# else
# endif
# ifndef VBOX_WITH_TEXT_MODMEM_HACK
__asm__(".section execmemory, \"awx\", @progbits\n\t"
".align 32\n\t"
".globl g_abExecMemory\n"
"g_abExecMemory:\n\t"
".type g_abExecMemory, @object\n\t"
".text\n\t");
# else
__asm__(".text\n\t"
".align 4096\n\t"
".globl g_abExecMemory\n"
"g_abExecMemory:\n\t"
".type g_abExecMemory, @object\n\t"
".text\n\t");
# endif
#endif
/** The file_operations structure. */
static struct file_operations gFileOpsVBoxDrvSys =
{
#ifdef HAVE_UNLOCKED_IOCTL
#else
#endif
};
/** The file_operations structure. */
static struct file_operations gFileOpsVBoxDrvUsr =
{
#ifdef HAVE_UNLOCKED_IOCTL
#else
#endif
};
#ifdef CONFIG_VBOXDRV_AS_MISC
/** The miscdevice structure for vboxdrv. */
static struct miscdevice gMiscDeviceSys =
{
# endif
};
/** The miscdevice structure for vboxdrvu. */
static struct miscdevice gMiscDeviceUsr =
{
# endif
};
#endif
static struct dev_pm_ops gPlatformPMOps =
{
};
# endif
static struct platform_driver gPlatformDriver =
{
.probe = VBoxDrvProbe,
.resume = VBoxDrvResume,
# endif
/** @todo .shutdown? */
.driver =
{
.name = "vboxdrv",
.pm = &gPlatformPMOps,
# endif
}
};
static struct platform_device gPlatformDevice =
{
.name = "vboxdrv",
.dev =
{
}
};
#endif /* VBOX_WITH_SUSPEND_NOTIFICATION */
{
# else
# endif
#else
#endif
}
{
# else
# endif
#else
#endif
}
{
# else
# endif
#else
#endif
}
/**
* Initialize module.
*
* @returns appropriate status code.
*/
static int __init VBoxDrvLinuxInit(void)
{
int rc;
/*
* Check for synchronous/asynchronous TSC mode.
*/
#ifdef CONFIG_VBOXDRV_AS_MISC
if (rc)
{
return rc;
}
if (rc)
{
return rc;
}
#else /* !CONFIG_VBOXDRV_AS_MISC */
/*
* Register character devices and save the returned major numbers.
*/
if (rc < 0)
{
return rc;
}
if (DEVICE_MAJOR_SYS != 0)
else
/** @todo Use a minor number of this bugger (not sure if this code is used
* though, so not bothering right now.) */
if (rc < 0)
{
return rc;
}
if (DEVICE_MAJOR_USR != 0)
else
rc = 0;
# ifdef CONFIG_DEVFS_FS
/*
* Register a device entry
*/
{
Log(("devfs_register failed!\n"));
}
# 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.
*/
if (RT_SUCCESS(rc))
{
#if (defined(RT_ARCH_AMD64) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)) || defined(VBOX_WITH_TEXT_MODMEM_HACK)
# ifdef VBOX_WITH_TEXT_MODMEM_HACK
# endif
#endif
Log(("VBoxDrv::ModuleInit\n"));
/*
* Initialize the device extension.
*/
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
{
if (rc == 0)
{
if (rc == 0)
#endif
{
return rc;
}
else
}
#endif
}
}
else
/*
* Failed, cleanup and return the error code.
*/
#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC)
#endif
}
#ifdef CONFIG_VBOXDRV_AS_MISC
Log(("VBoxDrv::ModuleInit returning %#x (minor:%d & %d)\n", rc, gMiscDeviceSys.minor, gMiscDeviceUsr.minor));
#else
Log(("VBoxDrv::ModuleInit returning %#x (major:%d & %d)\n", rc, g_iModuleMajorSys, g_iModuleMajorUsr));
#endif
return rc;
}
/**
* Unload the module.
*/
static void __exit VBoxDrvLinuxUnload(void)
{
int rc;
Log(("VBoxDrvLinuxUnload\n"));
#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
if (rc < 0)
{
}
if (rc < 0)
{
}
#else /* !CONFIG_VBOXDRV_AS_MISC */
# ifdef CONFIG_DEVFS_FS
/*
* Unregister a device entry
*/
# endif /* devfs */
#endif /* !CONFIG_VBOXDRV_AS_MISC */
/*
* Destroy GIP, delete the device extension and terminate IPRT.
*/
}
/**
* Common open code.
*
* @param pInode Pointer to inode info structure.
* @param pFilp Associated file pointer.
* @param fUnrestricted Indicates which device node which was opened.
*/
{
int rc;
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 unrestricted device, enforce it!
*/
if ( fUnrestricted
&& vboxdrvLinuxEuid() != 0 /* root */ )
{
return -EPERM;
}
#endif /* VBOX_WITH_HARDENING */
/*
* Call common code for the rest.
*/
if (!rc)
{
}
Log(("VBoxDrvLinuxCreate: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n",
return VBoxDrvLinuxErr2LinuxErr(rc);
}
{
}
{
}
/**
* Close device.
*
* @param pInode Pointer to inode info structure.
* @param pFilp Associated file pointer.
*/
{
Log(("VBoxDrvLinuxClose: pFilp=%p pSession=%p pid=%d/%d %s\n",
return 0;
}
/**
* Dummy device release function. We have to provide this function,
* otherwise the kernel will complain.
*
* @param pDev Pointer to the platform device.
*/
{
}
/**
* Dummy probe function.
*
* @param pDev Pointer to the platform device.
*/
{
return 0;
}
/**
* Suspend callback.
* @param pDev Pointer to the platform device.
* @param State message type, see Documentation/power/devices.txt.
*/
# else
# endif
{
return 0;
}
/**
* Resume callback.
*
* @param pDev Pointer to the platform device.
*/
# else
# endif
{
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
#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
|| uCmd == SUP_IOCTL_FAST_DO_HM_RUN
|| uCmd == SUP_IOCTL_FAST_DO_NOP)
&& pSession->fUnrestricted == true))
#else /* !HAVE_UNLOCKED_IOCTL */
int rc;
|| uCmd == SUP_IOCTL_FAST_DO_HM_RUN
|| uCmd == SUP_IOCTL_FAST_DO_NOP)
&& pSession->fUnrestricted == true))
else
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().
* @param pSession The session instance.
*/
static int VBoxDrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PSUPDRVSESSION pSession)
{
int rc;
Log6(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid));
/*
* Read the header.
*/
{
return -EFAULT;
}
{
Log(("VBoxDrvLinuxIOCtl: bad header magic %#x; uCmd=%#x\n", Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK, uCmd));
return -EINVAL;
}
/*
* Buffer the request.
*/
{
return -E2BIG;
}
{
Log(("VBoxDrvLinuxIOCtl: bad ioctl cbBuf=%#x _IOC_SIZE=%#x; uCmd=%#x.\n", cbBuf, _IOC_SIZE(uCmd), uCmd));
return -EINVAL;
}
if (RT_UNLIKELY(!pHdr))
{
OSDBGPRINT(("VBoxDrvLinuxIOCtl: failed to allocate buffer of %d bytes for uCmd=%#x.\n", cbBuf, uCmd));
return -ENOMEM;
}
{
return -EFAULT;
}
/*
* Process the IOCtl.
*/
/*
* Copy ioctl data and output buffer back to user space.
*/
{
{
}
{
/* this is really bad! */
}
}
else
{
Log(("VBoxDrvLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc));
}
return rc;
}
/**
* The SUPDRV IDC entry point.
*
* @returns VBox status code, see supdrvIDC.
* @param iReq The request code.
* @param pReq The request.
*/
{
/*
* Some quick validations.
*/
return VERR_INVALID_POINTER;
if (pSession)
{
return VERR_INVALID_PARAMETER;
return VERR_INVALID_PARAMETER;
}
return VERR_INVALID_PARAMETER;
/*
* Do the job.
*/
}
/**
* Initializes any OS specific object creator fields.
*/
{
}
/**
* 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)
{
return false;
}
{
return force_async_tsc != 0;
}
int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
{
return VERR_NOT_SUPPORTED;
}
{
}
int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv, const uint8_t *pbImageBits)
{
return VERR_NOT_SUPPORTED;
}
int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq)
{
return VERR_NOT_SUPPORTED;
}
{
}
#ifdef SUPDRV_WITH_MSR_PROBER
{
# ifdef SUPDRV_LINUX_HAS_SAFE_MSR_API
int rc;
if (idCpu == NIL_RTCPUID)
else if (RTMpIsCpuOnline(idCpu))
else
return VERR_CPU_OFFLINE;
if (rc == 0)
{
return VINF_SUCCESS;
}
return VERR_ACCESS_DENIED;
# else
return VERR_NOT_SUPPORTED;
# endif
}
{
# ifdef SUPDRV_LINUX_HAS_SAFE_MSR_API
int rc;
if (idCpu == NIL_RTCPUID)
else if (RTMpIsCpuOnline(idCpu))
else
return VERR_CPU_OFFLINE;
if (rc == 0)
return VINF_SUCCESS;
return VERR_ACCESS_DENIED;
# else
return VERR_NOT_SUPPORTED;
# endif
}
# ifdef SUPDRV_LINUX_HAS_SAFE_MSR_API
/**
* Worker for supdrvOSMsrProberModify.
*/
static DECLCALLBACK(void) supdrvLnxMsrProberModifyOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
{
/* Initialize result variables. */
/*
* Do the job.
*/
ASMCompilerBarrier(); /* paranoia */
if (!fFaster)
if (rcBefore >= 0)
{
if (!fFaster)
{
ASMReloadCR3();
ASMNopPause();
}
}
ASMCompilerBarrier(); /* paranoia */
/*
* Write out the results.
*/
}
# endif
{
# ifdef SUPDRV_LINUX_HAS_SAFE_MSR_API
if (idCpu == NIL_RTCPUID)
{
return VINF_SUCCESS;
}
# else
return VERR_NOT_SUPPORTED;
# endif
}
#endif /* SUPDRV_WITH_MSR_PROBER */
/**
* 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;
}
{
char szMsg[512];
return 0;
}
MODULE_LICENSE("GPL");
#ifdef MODULE_VERSION
#endif