SUPDrv-linux.c revision a466f24ecf94b453750f9791ad1140556114711b
/** @file
* The VirtualBox Support Driver - Linux hosts.
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* 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 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.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include "SUPDRV.h"
#include "version-generated.h"
#include <iprt/spinlock.h>
#include <iprt/semaphore.h>
#include <iprt/initterm.h>
#endif
#ifdef CONFIG_DEVFS_FS
# include <linux/devfs_fs_kernel.h>
#endif
#ifdef CONFIG_VBOXDRV_AS_MISC
# include <linux/miscdevice.h>
#endif
#ifdef CONFIG_X86_LOCAL_APIC
# endif
#endif
# ifndef page_to_pfn
# endif
# define global_flush_tlb __flush_tlb_global
#endif
/* devfs defines */
#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC)
# define VBOX_REGISTER_DEVFS() \
({ \
DEVICE_NAME) == 0) \
rc; \
})
# define VBOX_UNREGISTER_DEVFS(handle) \
# else /* < 2.6.0 */
# define VBOX_REGISTER_DEVFS() \
DEVICE_MAJOR, 0, \
# define VBOX_UNREGISTER_DEVFS(handle) \
# endif /* < 2.6.0 */
#endif /* CONFIG_DEV_FS && !CONFIG_VBOXDEV_AS_MISC */
#ifndef CONFIG_VBOXDRV_AS_MISC
# define VBOX_REGISTER_DEVICE(a,b,c) devfs_register_chrdev(a,b,c)
# define VBOX_UNREGISTER_DEVICE(a,b) devfs_unregister_chrdev(a,b)
# else
# define VBOX_REGISTER_DEVICE(a,b,c) register_chrdev(a,b,c)
# define VBOX_UNREGISTER_DEVICE(a,b) unregister_chrdev(a,b)
# endif
#endif /* !CONFIG_VBOXDRV_AS_MISC */
#ifdef CONFIG_X86_HIGH_ENTRY
# error "CONFIG_X86_HIGH_ENTRY is not supported by VBoxDrv at this time."
#endif
/*
* This sucks soooo badly on x86! Why don't they export __PAGE_KERNEL_EXEC so PAGE_KERNEL_EXEC would be usable?
*/
#if defined(__AMD64__)
# define MY_PAGE_KERNEL_EXEC PAGE_KERNEL_EXEC
# define MY_PAGE_KERNEL_EXEC __pgprot(cpu_has_pge ? _PAGE_KERNEL_EXEC | _PAGE_GLOBAL : _PAGE_KERNEL_EXEC)
#else
# define MY_PAGE_KERNEL_EXEC PAGE_KERNEL
#endif
/*
* The redhat hack section.
* - The current hacks are for 2.4.21-15.EL only.
*/
#ifndef NO_REDHAT_HACKS
/* accounting. */
# ifdef VM_ACCOUNT
# endif
# endif
/* backported remap_page_range. */
# ifdef tlb_vma /* probably not good enough... */
# define HAVE_26_STYLE_REMAP_PAGE_RANGE 1
# endif
# endif
# ifndef __AMD64__
/* In 2.6.9-22.ELsmp we have to call change_page_attr() twice when changing
* the page attributes from PAGE_KERNEL to something else, because there appears
* to be a bug in one of the many patches that redhat applied.
* It should be safe to do this on less buggy linux kernels too. ;-)
*/
do { \
} while (0)
# endif
#endif /* !NO_REDHAT_HACKS */
#ifndef MY_DO_MUNMAP
# define MY_DO_MUNMAP(a,b,c) do_munmap(a, b, c)
#endif
#ifndef MY_CHANGE_PAGE_ATTR
# ifdef __AMD64__ /** @todo This is a cheap hack, but it'll get around that 'else BUG();' in __change_page_attr(). */
do { \
} while (0)
# else
# endif
#endif
/** @def ONE_MSEC_IN_JIFFIES
* The number of jiffies that make up 1 millisecond. This is only actually used
* when HZ is > 1000. */
#if HZ <= 1000
# define ONE_MSEC_IN_JIFFIES 0
#else
# error "HZ is not a multiple of 1000, the GIP stuff won't work right!"
#endif
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/**
* Device extention & session data association structure.
*/
static SUPDRVDEVEXT g_DevExt;
/** Timer structure for the GIP update. */
static struct timer_list g_GipTimer;
/** Pointer to the page structure for the GIP. */
struct page *g_pGipPage;
/** Registered devfs device handle. */
#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC)
static void *g_hDevFsVBoxDrv = NULL;
# else
# endif
#endif
#ifndef CONFIG_VBOXDRV_AS_MISC
/** Module major number */
#define DEVICE_MAJOR 234
/** Saved major device number */
static int g_iModuleMajor;
#endif /* !CONFIG_VBOXDRV_AS_MISC */
/** The module name. */
#define DEVICE_NAME "vboxdrv"
#ifdef __AMD64__
/**
* Memory for the executable memory heap (in IPRT).
*/
__asm__(".section execmemory, \"awx\", @progbits\n\t"
".align 32\n\t"
".globl g_abExecMemory\n"
"g_abExecMemory:\n\t"
".zero 1572864\n\t"
".type g_abExecMemory, @object\n\t"
".size g_abExecMemory, 1572864\n\t"
".text\n\t");
#endif
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int VBoxSupDrvInit(void);
static void VBoxSupDrvUnload(void);
static RTR3PTR VBoxSupDrvMapUser(struct page **papPages, unsigned cPages, unsigned fProt, pgprot_t pgFlags);
static void VBoxSupGipTimer(unsigned long ulUser);
#ifdef CONFIG_SMP
static void VBoxSupGipTimerPerCpu(unsigned long ulUser);
static void VBoxSupGipResumePerCpu(void *pvUser);
#endif
static int VBoxSupDrvOrder(unsigned long size);
static int VBoxSupDrvErr2LinuxErr(int);
/** The file_operations structure. */
static struct file_operations gFileOpsVBoxDrv =
{
};
#ifdef CONFIG_VBOXDRV_AS_MISC
/** The miscdevice structure. */
static struct miscdevice gMiscDevice =
{
# endif
};
#endif
/**
* Initialize module.
*
* @returns appropriate status code.
*/
static int __init VBoxSupDrvInit(void)
{
int rc;
dprintf(("VBoxDrv::ModuleInit\n"));
#ifdef CONFIG_X86_LOCAL_APIC
/*
* If an NMI occurs while we are inside the world switcher the macine will crash.
* The Linux NMI watchdog generates periodic NMIs increasing a counter which is
* compared with another counter increased in the timer interrupt handler. Therefore
* we don't allow to setup an NMI watchdog.
*/
/*
* First test: NMI actiated? Works only works with Linux 2.6 -- 2.4 does not export
* the nmi_watchdog variable.
*/
/*
* Permanent IO_APIC mode active? No way to handle this!
*/
if (nmi_watchdog == NMI_IO_APIC)
{
": NMI watchdog in IO_APIC mode active -- refused to load the kernel module!\n"
": Please disable the NMI watchdog by specifying 'nmi_watchdog=0' at kernel\n"
": command line.\n");
return -EINVAL;
}
/*
*/
/*
* Now fall through and see if it actually was enabled before. If so, fail
* as we cannot deactivate it cleanly from here.
*/
# else /* < 2.6.19 */
/*
* Older 2.6 kernels: nmi_watchdog is not initalized by default
*/
if (nmi_watchdog != NMI_NONE)
goto nmi_activated;
# endif
# endif /* >= 2.6.0 */
/*
* Second test: Interrupt generated by performance counter not masked and can
* generate an NMI. Works also with Linux 2.4.
*/
{
ver = GET_APIC_VERSION(v);
/* 82489DXs do not report # of LVT entries. */
if (maxlvt >= 4)
{
/* Read status of performance counter IRQ vector */
v = apic_read(APIC_LVTPC);
/* performance counter generates NMI and is not masked? */
{
": NMI watchdog either active or at least initialized. Please disable the NMI\n"
": watchdog by specifying 'nmi_watchdog=0' at kernel command line.\n");
return -EINVAL;
# else /* < 2.6.19 */
# endif
": NMI watchdog active -- refused to load the kernel module! Please disable\n"
": the NMI watchdog by specifying 'nmi_watchdog=0' at kernel command line.\n");
return -EINVAL;
# endif /* >= 2.6.19 */
}
}
}
# endif /* >= 2.6.19 */
#endif /* CONFIG_X86_LOCAL_APIC */
#ifdef CONFIG_VBOXDRV_AS_MISC
if (rc)
{
return rc;
}
#else /* !CONFIG_VBOXDRV_AS_MISC */
/*
* Register character device.
*/
if (rc < 0)
{
return rc;
}
/*
* Save returned module major number
*/
if (DEVICE_MAJOR != 0)
else
g_iModuleMajor = rc;
rc = 0;
#ifdef CONFIG_DEVFS_FS
/*
* Register a device entry
*/
if (g_hDevFsVBoxDrv == NULL)
{
dprintf(("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))
{
#ifdef __AMD64__
#endif
/*
* Initialize the device extension.
*/
if (RT_SUCCESS(rc))
if (!rc)
{
/*
* Create the GIP page.
*/
if (!rc)
{
return rc;
}
}
else
RTR0Term();
}
else
/*
* Failed, cleanup and return the error code.
*/
#if defined(CONFIG_DEVFS_FS) && !defined(CONFIG_VBOXDRV_AS_MISC)
#endif
}
#ifdef CONFIG_VBOXDRV_AS_MISC
#else
#endif
return rc;
}
/**
* Unload the module.
*/
static void __exit VBoxSupDrvUnload(void)
{
int rc;
dprintf(("VBoxSupDrvUnload\n"));
/*
* 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)
{
}
#else /* !CONFIG_VBOXDRV_AS_MISC */
#ifdef CONFIG_DEVFS_FS
/*
* Unregister a device entry
*/
#endif // devfs
if (rc < 0)
{
}
#endif /* !CONFIG_VBOXDRV_AS_MISC */
/*
* Destroy GIP, delete the device extension and terminate IPRT.
*/
RTR0Term();
}
/**
*
* @param pInode Pointer to inode info structure.
* @param pFilp Associated file pointer.
*/
{
int rc;
/*
* Call common code for the rest.
*/
if (!rc)
{
}
return VBoxSupDrvErr2LinuxErr(rc);
}
/**
* Close device.
*
* @param pInode Pointer to inode info structure.
* @param pFilp Associated file pointer.
*/
{
return 0;
}
/**
* Device I/O Control entry point.
*
* @param pInode Pointer to inode info structure.
* @param pFilp Associated file pointer.
* @param IOCmd The function specified to ioctl().
* @param IOArg The argument specified to ioctl().
*/
{
int rc;
int cbBuf = 0;
unsigned cbOut = 0;
/*
* Copy ioctl data structure from user space.
*/
{
return -EINVAL;
}
{
dprintf(("VBoxSupDrvDeviceControl: copy_from_user(&Args) failed.\n"));
return -EFAULT;
}
/*
* Allocate and copy user space input data buffer to kernel space.
*/
{
{
return -ENOMEM;
}
{
dprintf(("VBoxSupDrvDeviceControl: copy_from_user(pvBuf) failed.\n"));
return -EFAULT;
}
}
/*
* Process the IOCtl.
*/
/*
* Copy ioctl data and output buffer back to user space.
*/
if (rc)
{
dprintf(("VBoxSupDrvDeviceControl: pFilp=%p IOCmd=%x IOArg=%p failed, rc=%d (linux rc=%d)\n",
}
else if (cbOut > 0)
{
{
{
dprintf(("copy_to_user failed.\n"));
}
}
else
{
}
}
if (pvBuf)
return rc;
}
/**
* 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;
}
/**
* Compute order. Some functions allocate 2^order pages.
*
* @returns order.
* @param cPages Number of pages.
*/
static int VBoxSupDrvOrder(unsigned long cPages)
{
int iOrder;
unsigned long cTmp;
;
++iOrder;
return iOrder;
}
/**
* OS Specific code for locking down memory.
*
* @returns 0 on success.
* @returns SUPDRV_ERR_* on failure.
* @param pMem Pointer to memory.
* This is not linked in anywhere.
* @param paPages Array which should be filled with the address of the physical pages.
*
* @remark See sgl_map_user_pages() for an example of an similar function.
*/
{
int rc;
unsigned iPage;
struct vm_area_struct **papVMAs;
/*
* Allocate page pointer array.
*/
if (!papPages)
return SUPDRV_ERR_NO_MEMORY;
/*
* Allocate the VMA pointer array.
*/
if (!papVMAs)
return SUPDRV_ERR_NO_MEMORY;
/*
* Get user pages.
*/
(unsigned long)pv, /* Where from. */
cPages, /* How many pages. */
1, /* Write to memory. */
0, /* force. */
papPages, /* Page array. */
papVMAs); /* vmas */
{
return SUPDRV_ERR_LOCK_FAILED;
}
/*
* Get addresses, protect against fork()
*/
{
}
dprintf2(("supdrvOSLockMemOne: pvR3=%p cb=%d papPages=%p\n",
return 0;
}
/**
* Unlocks the memory pointed to by pv.
*
* @param pMem Pointer to memory to unlock.
*
* @remark See sgl_unmap_user_pages() for an example of an similar function.
*/
{
unsigned iPage;
dprintf2(("supdrvOSUnlockMemOne: pvR3=%p cb=%d papPages=%p\n",
/*
* Loop thru the pages and release them.
*/
{
}
/* free the page array */
}
/**
* OS Specific code for allocating page aligned memory with continuous fixed
* physical paged backing.
*
* @returns 0 on success.
* @returns SUPDRV_ERR_* on failure.
* @param pMem Memory reference record of the memory to be allocated.
* (This is not linked in anywhere.)
* @param ppvR0 Where to store the virtual address of the ring-0 mapping. (optional)
* @param ppvR3 Where to store the virtual address of the ring-3 mapping.
* @param pHCPhys Where to store the physical address.
*/
int VBOXCALL supdrvOSContAllocOne(PSUPDRVMEMREF pMem, PRTR0PTR ppvR0, PRTR3PTR ppvR3, PRTHCPHYS pHCPhys)
{
unsigned iPage;
unsigned long ulAddr;
int rc = 0;
/*
* Allocate page pointer array.
*/
#ifdef __AMD64__ /** @todo check out if there is a correct way of getting memory below 4GB (physically). */
#else
#endif
if (!paPages)
return SUPDRV_ERR_NO_MEMORY;
/*
* Lock the pages.
*/
{
#ifdef DEBUG
if (iPage + 1 < cPages && (page_to_phys((&paPages[iPage])) + 0x1000) != page_to_phys((&paPages[iPage + 1])))
{
dprintf(("supdrvOSContAllocOne: Pages are not continuous!!!! iPage=%d phys=%llx physnext=%llx\n",
iPage, (long long)page_to_phys((&paPages[iPage])), (long long)page_to_phys((&paPages[iPage + 1]))));
BUG();
}
#endif
}
/*
* Allocate user space mapping and put the physical pages into it.
*/
ulAddr = do_mmap(NULL, 0, cbAligned, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_ANONYMOUS, 0);
{
#else
int rc2 = 0;
if (vma)
#else
#endif
else
{
}
#endif
if (rc2)
{
}
}
else
{
}
/*
* Success?
*/
if (!rc)
{
if (ppvR0)
dprintf2(("supdrvOSContAllocOne: pvR0=%p pvR3=%p cb=%d paPages=%p *pHCPhys=%lx *ppvR0=*ppvR3=%p\n",
return 0;
}
/*
* Failure, cleanup and be gone.
*/
{
}
return rc;
}
/**
* Frees contiguous memory.
*
* @param pMem Memory reference record of the memory to be freed.
*/
{
unsigned iPage;
dprintf2(("supdrvOSContFreeOne: pvR0=%p pvR3=%p cb=%d paPages=%p\n",
/*
* do_exit() destroys the mm before closing files.
* I really hope it cleans up our stuff properly...
*/
{
}
/*
* Change page attributes freeing the pages.
*/
{
if (!PageHighMem(&pMem->u.cont.paPages[iPage]) && pgprot_val(MY_PAGE_KERNEL_EXEC) != pgprot_val(PAGE_KERNEL))
}
}
/**
* Allocates memory which mapped into both kernel and user space.
* The returned memory is page aligned and so is the allocation.
*
* @returns 0 on success.
* @returns SUPDRV_ERR_* on failure.
* @param pMem Memory reference record of the memory to be allocated.
* (This is not linked in anywhere.)
* @param ppvR0 Where to store the address of the Ring-0 mapping.
* @param ppvR3 Where to store the address of the Ring-3 mapping.
*/
{
#endif
unsigned iPage;
/*
* Allocate array with page pointers.
*/
if (!papPages)
return SUPDRV_ERR_NO_MEMORY;
/*
* Allocate the pages.
*/
{
{
return SUPDRV_ERR_NO_MEMORY;
}
}
#else /* < 2.4.22 */
if (!paPages)
{
return SUPDRV_ERR_NO_MEMORY;
}
{
BUG();
}
#endif
/*
* Reserve the pages.
*/
/*
* Create the Ring-0 mapping.
*/
if (ppvR0)
{
# ifdef VM_MAP
# else
# endif
#else
#endif
}
{
/*
* Create the ring3 mapping.
*/
if (ppvR3)
*ppvR3 = pMem->pvR3 = VBoxSupDrvMapUser(papPages, cPages, PROT_READ | PROT_WRITE | PROT_EXEC, pgFlags);
return 0;
}
else
return SUPDRV_ERR_NO_MEMORY;
}
/**
* Get the physical addresses of the pages in the allocation.
* This is called while inside bundle the spinlock.
*
* @param pMem Memory reference record of the memory.
* @param paPages Where to store the page addresses.
*/
{
unsigned iPage;
{
}
}
/**
* Frees memory allocated by supdrvOSMemAllocOne().
*
* @param pMem Memory reference record of the memory to be free.
*/
{
dprintf2(("supdrvOSMemFreeOne: pvR0=%p pvR3=%p cb=%d cPages=%d papPages=%p\n",
/*
* Unmap the user mapping (if any).
* do_exit() destroys the mm before closing files.
*/
{
}
/*
* Unmap the kernel mapping (if any).
*/
{
#endif
}
/*
* Free the physical pages.
*/
{
unsigned iPage;
/* Restore the page flags. */
{
#endif
}
/* Free the pages. */
#else
if (cPages > 0)
#endif
/* Free the page pointer array. */
}
}
/**
* Maps a range of pages into user space.
*
* @returns Pointer to the user space mapping on success.
* @returns NULL on failure.
* @param papPages Array of the pages to map.
* @param cPages Number of pages to map.
* @param fProt The mapping protection.
* @param pgFlags The page level protection.
*/
static RTR3PTR VBoxSupDrvMapUser(struct page **papPages, unsigned cPages, unsigned fProt, pgprot_t pgFlags)
{
int rc = SUPDRV_ERR_NO_MEMORY;
unsigned long ulAddr;
/*
* Allocate user space mapping.
*/
{
/*
* Map page by page into the mmap area.
* This is generic, paranoid and not very efficient.
*/
int rc = 0;
unsigned iPage;
{
if (!vma)
break;
#endif
#else /* 2.4 */
#endif
if (rc)
break;
}
/*
* Successful?
*/
{
return ulAddr;
}
/* no, cleanup! */
if (rc)
else
dprintf(("VBoxSupDrvMapUser: find_vma failed!\n"));
}
else
{
}
return NIL_RTR3PTR;
}
/**
* Initializes the GIP.
*
* @returns negative errno.
* @param pDevExt Instance data. GIP stuff may be updated.
*/
{
#ifdef CONFIG_SMP
unsigned i;
#endif
dprintf(("VBoxSupDrvInitGip:\n"));
/*
* Allocate the page.
*/
if (!pPage)
{
dprintf(("VBoxSupDrvInitGip: failed to allocate the GIP page\n"));
return -ENOMEM;
}
/*
* Lock the page.
*/
g_pGipPage = pPage;
/*
* Call common initialization routine.
*/
#ifdef TICK_NSEC
dprintf(("VBoxSupDrvInitGIP: TICK_NSEC=%ld HZ=%d jiffies=%ld now=%lld\n",
#else
dprintf(("VBoxSupDrvInitGIP: TICK_NSEC=%d HZ=%d jiffies=%ld now=%lld\n",
#endif
/*
* Initialize the timer.
*/
#ifdef CONFIG_SMP
{
}
#endif
return 0;
}
/**
* Terminates the GIP.
*
* @returns negative errno.
* @param pDevExt Instance data. GIP stuff may be updated.
*/
{
#ifdef CONFIG_SMP
unsigned i;
#endif
dprintf(("VBoxSupDrvTermGip:\n"));
/*
* Delete the timer if it's pending.
*/
if (timer_pending(&g_GipTimer))
#ifdef CONFIG_SMP
#endif
/*
* Uninitialize the content.
*/
if (pGip)
/*
* Free the page.
*/
pPage = g_pGipPage;
g_pGipPage = NULL;
if (pPage)
{
__free_pages(pPage, 0);
}
return 0;
}
/**
* Timer callback function.
*
* In ASYNC TSC mode this is called on the primary CPU, and we're
* assuming that the CPU remains online.
*
* @param ulUser The device extension pointer.
*/
static void VBoxSupGipTimer(unsigned long ulUser)
{
unsigned long ulNow;
unsigned long ulDiff;
unsigned long SavedFlags;
#ifdef TICK_NSEC
#else
#endif
}
#ifdef CONFIG_SMP
/**
* Timer callback function for the other CPUs.
*
* @param iLnxCPU The APIC ID of this timer.
*/
static void VBoxSupGipTimerPerCpu(unsigned long iLnxCPU)
{
unsigned long SavedFlags;
iCPU = ASMGetApicId();
{
{
#ifdef TICK_NSEC
#else
#endif
}
else
printk("vboxdrv: error: GIP CPU update timer executing on the wrong CPU: apicid=%d != timer-apicid=%ld (cpuid=%d != timer-cpuid=%d)\n",
}
else
printk("vboxdrv: error: APIC ID is bogus (GIP CPU update): apicid=%d max=%d cpuid=%d\n",
}
#endif /* CONFIG_SMP */
/**
* Maps the GIP into user space.
*
* @returns negative errno.
* @param pDevExt Instance data.
*/
{
int rc = 0;
unsigned long ulAddr;
/*
* Allocate user space mapping and put the physical pages into it.
*/
{
#else
int rc2 = 0;
if (vma)
#else
#endif
else
{
}
#endif
if (rc2)
{
}
}
else
{
}
/*
* Success?
*/
if (!rc)
{
return 0;
}
/*
* Failure, cleanup and be gone.
*/
{
}
return rc;
}
/**
* Maps the GIP into user space.
*
* @returns negative errno.
* @param pDevExt Instance data.
*/
{
{
}
dprintf2(("supdrvOSGipUnmap: returns 0\n"));
return 0;
}
/**
* Resumes the GIP updating.
*
* @param pDevExt Instance data.
*/
{
dprintf2(("supdrvOSGipResume:\n"));
#ifdef CONFIG_SMP
#endif
#ifdef CONFIG_SMP
else
{
}
#endif
}
#ifdef CONFIG_SMP
/**
* Callback for resuming GIP updating on the other CPUs.
*
* This is only used when the GIP is in async tsc mode.
*
* @param pvUser Pointer to the device instance.
*/
static void VBoxSupGipResumePerCpu(void *pvUser)
{
{
printk("vboxdrv: error: apicid=%d max=%d cpuid=%d\n",
return;
}
}
#endif /* CONFIG_SMP */
/**
* Suspends the GIP updating.
*
* @param pDevExt Instance data.
*/
{
#ifdef CONFIG_SMP
unsigned i;
#endif
dprintf2(("supdrvOSGipSuspend:\n"));
if (timer_pending(&g_GipTimer))
#ifdef CONFIG_SMP
#endif
}
/**
* Get the current CPU count.
* @returns Number of cpus.
*/
unsigned VBOXCALL supdrvOSGetCPUCount(void)
{
#ifdef CONFIG_SMP
# ifdef num_present_cpus
return num_present_cpus();
# else
return smp_num_cpus;
# endif
#else
return 1;
#endif
}
/**
* Force async tsc mode.
* @todo add a module argument for this.
*/
bool VBOXCALL supdrvOSGetForcedAsyncTscMode(void)
{
return false;
}
/**
* Converts a supdrv error code to an linux error code.
*
* @returns corresponding linux error code.
* @param rc supdrv error code (SUPDRV_ERR_* defines).
*/
static int VBoxSupDrvErr2LinuxErr(int rc)
{
switch (rc)
{
case 0: return 0;
case SUPDRV_ERR_GENERAL_FAILURE: return -EACCES;
case SUPDRV_ERR_INVALID_PARAM: return -EINVAL;
case SUPDRV_ERR_INVALID_MAGIC: return -EILSEQ;
case SUPDRV_ERR_INVALID_HANDLE: return -ENXIO;
case SUPDRV_ERR_INVALID_POINTER: return -EFAULT;
case SUPDRV_ERR_LOCK_FAILED: return -ENOLCK;
case SUPDRV_ERR_ALREADY_LOADED: return -EEXIST;
case SUPDRV_ERR_PERMISSION_DENIED: return -EPERM;
case SUPDRV_ERR_VERSION_MISMATCH: return -ENOSYS;
}
return -EPERM;
}
{
#if 1
char szMsg[512];
#else
/* forward to printf - needs some more GCC hacking to fix ebp... */
"jmp %1\n\t",
"m" (printk));
#endif
return 0;
}
/** Runtime assert implementation for Linux Ring-0. */
RTDECL(void) AssertMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
{
printk("!!Assertion Failed!!\n"
"Expression: %s\n"
"Location : %s(%d) %s\n",
}
/** Runtime assert implementation for Linux Ring-0. */
{ /* forwarder. */
char msg[256];
}
/* GCC C++ hack. */
unsigned __gxx_personality_v0 = 0xcccccccc;
MODULE_AUTHOR("InnoTek Systemberatung GmbH");
MODULE_DESCRIPTION("VirtualBox Support Driver");
MODULE_LICENSE("GPL");
#ifdef MODULE_VERSION
#define str(s) #s
#endif