vboxmod.c revision 9d805e5fdc5c2254d518988f5c12532b8cd00d14
/** @file
*
* vboxadd -- VirtualBox Guest Additions for Linux
*/
/*
* Copyright (C) 2006-2007 innotek 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.
*/
#include "the-linux-kernel.h"
#include "version-generated.h"
/* #define IRQ_DEBUG */
#include "vboxmod.h"
#include "waitcompat.h"
MODULE_DESCRIPTION("VirtualBox Guest Additions for Linux Module");
MODULE_AUTHOR("innotek GmbH");
MODULE_LICENSE("GPL");
#ifdef MODULE_VERSION
#endif
/* This is called by our assert macros to find out whether we want
to insert a breakpoint after the assertion. In kernel modules we
do not of course. */
RTDECL(bool) RTAssertDoBreakpoint(void)
{
return false;
}
/** device extension structure (we only support one device instance) */
/** our file node major id (set dynamically) */
#ifdef CONFIG_VBOXADD_MAJOR
static unsigned int vbox_major = CONFIG_VBOXADD_MAJOR;
#else
static unsigned int vbox_major = 0;
#endif
DECLVBGL (void *) vboxadd_cmc_open (void)
{
return vboxDev;
}
{
(void) opaque;
}
#define MAX_HGCM_CONNECTIONS 1024
/**
* Structure for keeping track of HGCM connections owned by user space processes, so that
* we can close the connection if a process does not clean up properly (for example if it
* was terminated too abruptly).
*/
/* We just define a fixed number of these so far. This can be changed if it ever becomes
a problem. */
static struct {
/** Open file structure that this connection handle is associated with */
/** HGCM connection ID */
} hgcm_connections[MAX_HGCM_CONNECTIONS] = { { 0 } };
/**
* Register an HGCM connection as being connected with a given file descriptor, so that it
* will be closed automatically when that file descriptor is.
*
* @returns 0 on success or Linux kernel error number
* @param clientID the client ID of the HGCM connection
* @param filep the file structure that the connection is to be associated with
*/
{
int i;
bool found = false;
for (i = 0; i < MAX_HGCM_CONNECTIONS; ++i) {
}
for (i = 0; (i < MAX_HGCM_CONNECTIONS) && (false == found); ++i) {
found = true;
}
}
}
/**
* Unregister an HGCM connection associated with a given file descriptor without closing
* the connection.
*
* @returns 0 on success or Linux kernel error number
* @param clientID the client ID of the HGCM connection
*/
{
int i;
bool found = false;
for (i = 0; (i < MAX_HGCM_CONNECTIONS) && (false == found); ++i) {
hgcm_connections[i].client_id = 0;
found = true;
}
}
for (i = 0; i < MAX_HGCM_CONNECTIONS; ++i) {
}
}
/**
* Unregister all HGCM connections associated with a given file descriptor, closing
* the connections in the process. This should be called when a file descriptor is
* closed.
*
* @returns 0 on success or Linux kernel error number
* @param clientID the client ID of the HGCM connection
*/
{
int i;
for (i = 0; i < MAX_HGCM_CONNECTIONS; ++i) {
hgcm_connections[i].client_id = 0;
}
}
return 0;
}
/**
* File open handler
*
*/
{
/* no checks required */
return 0;
}
/**
* File close handler. Clean up any HGCM connections associated with the open file
* which might still be open.
*/
{
return 0;
}
/**
* Wait for event
*
*/
static void
{
BUG ();
}
static void
{
long timeout;
&info->u32EventFlagsOut);
}
/**
* IOCTL handler. Initiate an HGCM connection for a user space application. If the connection
* succeeds, it will be associated with the file structure used to open it, so that it will be
* automatically shut down again if the file descriptor is closed.
*
* @returns 0 on success, or a Linux kernel errno value
* @param filp the file structure with which the application opened the driver
* @param userspace_info userspace pointer to the hgcm connection information
* (VBoxGuestHGCMConnectInfo structure)
* @retval userspace_info userspace pointer to the hgcm connection information
*/
{
LogRelFunc (("IOCTL_VBOXGUEST_HGCM_CONNECT: can not get connection info\n"));
return -EFAULT;
}
LogRelFunc(("IOCTL_VBOXGUEST_HGCM_CONNECT: hgcm connection failed. internal ioctl result %Vrc, hgcm result %Vrc\n", rcVBox, info.result));
} else {
/* Register that the connection is associated with this file pointer. */
if (0 != rc) {
LogRelFunc(("IOCTL_VBOXGUEST_HGCM_CONNECT: failed to register the HGCM connection\n"));
} else {
sizeof(info))) {
LogRelFunc (("IOCTL_VBOXGUEST_HGCM_CONNECT: failed to return the connection structure\n"));
} else {
return 0;
}
/* Unregister again, as we didn't get as far as informing userspace. */
}
/* And disconnect the hgcm connection again, as we told userspace it failed. */
}
return rc;
}
/**
* IOCTL handler
*
*/
{
switch (cmd)
{
{
{
LogRelFunc (("IOCTL_VBOXGUEST_WAITEVENT: can not get event info\n"));
return -EFAULT;
}
{
LogRelFunc (("IOCTL_VBOXGUEST_WAITEVENT: can not put out_mask\n"));
return -EFAULT;
}
return 0;
}
{
int rc;
{
LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: invalid VMM request structure size: %d\n",
return -EINVAL;
}
{
LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: copy_from_user failed for vmm request!\n"));
return -EFAULT;
}
/* get the request size */
if (!cbVanillaRequestSize)
{
LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: invalid request type: %d\n",
return -EINVAL;
}
{
LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: invalid request size: %d min: %d type: %d\n",
return -EINVAL;
}
/* request storage for the full request */
if (VBOX_FAILURE(rc))
{
return -EFAULT;
}
/* now get the full request */
{
LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: failed to fetch full request from user space!\n"));
return -EFAULT;
}
/* now issue the request */
/* asynchronous processing? */
if (rc == VINF_HGCM_ASYNC_EXECUTE)
{
}
/* failed? */
{
LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: request execution failed!\n"));
}
else
{
/* success, copy the result data to user space */
{
LogRelFunc(("IOCTL_VBOXGUEST_VMMREQUEST: error copying request result to user space!\n"));
return -EFAULT;
}
}
break;
}
{
/* This IOCTL allows the guest to make an HGCM call from user space. The
OS-independant part of the Guest Additions already contain code for making an
HGCM call from the guest, but this code assumes that the call is made from the
kernel's address space. So before calling it, we have to copy all parameters
to the HGCM call from user space to kernel space and reconstruct the structures
passed to the call (which include pointers to other memory) inside the kernel's
address space. */
}
{
}
default:
{
return -EINVAL;
}
}
return 0;
}
#ifdef DEBUG
static ssize_t
{
{
return -EINVAL;
}
*loff += 8;
return 8;
}
#endif
/** strategy handlers (file operations) */
static struct file_operations vbox_fops =
{
.owner = THIS_MODULE,
.open = vboxadd_open,
.ioctl = vboxadd_ioctl,
#ifdef DEBUG
.read = vboxadd_read,
#endif
};
#ifndef IRQ_RETVAL
/* interrupt handlers in 2.4 kernels don't return anything */
# define irqreturn_t void
# define IRQ_RETVAL(n)
#endif
/**
* vboxadd_irq_handler
*
* Interrupt handler
*
* @returns scsi error code
* @param irq Irq number
* @param dev_id Irq handler parameter
* @param regs Regs
*
*/
#else
#endif
{
int fIRQTaken = 0;
int rcVBox;
#ifdef IRQ_DEBUG
printk ("%s: vboxDev->pVMMDevMemory=%p vboxDev->pVMMDevMemory->fHaveEvents=%d\n",
#endif
/* check if IRQ was asserted by VBox */
{
#ifdef IRQ_DEBUG
#endif
/* make a copy of the event mask */
{
{
}
}
else
{
/* impossible... */
LogRelFunc(("IRQ was not acknowledged! rc = %Vrc, header.rc = %Vrc\n",
BUG ();
}
/* it was ours! */
fIRQTaken = 1;
}
#ifdef IRQ_DEBUG
else
{
printk ("vboxadd: stale IRQ mem=%p events=%d devevents=%#x\n",
}
#endif
/* it was ours */
return IRQ_RETVAL(fIRQTaken);
}
/**
* Helper function to reserve a fixed kernel address space window
* and tell the VMM that it can safely put its hypervisor there.
* This function might fail which is not a critical error.
*/
static int vboxadd_reserve_hypervisor(void)
{
int rcVBox;
/* allocate request structure */
(VMMDevRequestHeader**)&req,
sizeof(VMMDevReqHypervisorInfo),
);
if (VBOX_FAILURE(rcVBox))
{
goto bail_out;
}
/* query the hypervisor information */
{
/* are we supposed to make a reservation? */
if (req->hypervisorSize)
{
/** @todo repeat this several times until we get an address the host likes */
void *hypervisorArea;
/* reserve another 4MB because the start needs to be 4MB aligned */
/* perform a fictive IO space mapping */
if (hypervisorArea)
{
/* communicate result to VMM, align at 4MB */
{
/* store mapping for future unmapping */
}
else
{
LogRelFunc(("failed to set hypervisor region! rc = %Vrc, header.rc = %Vrc\n",
goto bail_out;
}
}
else
{
goto bail_out;
}
}
}
else
{
LogRelFunc(("failed to query hypervisor info! rc = %Vrc, header.rc = %Vrc\n",
goto bail_out;
}
/* successful return */
return 0;
/* error return */
if (req)
return 1;
}
/**
* Helper function to free the hypervisor address window
*
*/
static int vboxadd_free_hypervisor(void)
{
int rcVBox;
/* allocate request structure */
(VMMDevRequestHeader**)&req,
sizeof(VMMDevReqHypervisorInfo),
);
if (VBOX_FAILURE(rcVBox))
{
goto bail_out;
}
/* reset the hypervisor information */
req->hypervisorStart = 0;
req->hypervisorSize = 0;
{
/* now we can free the associated IO space mapping */
vboxDev->hypervisorStart = 0;
}
else
{
LogRelFunc(("failed to reset hypervisor info! rc = %Vrc, header.rc = %Vrc\n",
goto bail_out;
}
return 0;
if (req)
return 1;
}
/**
* Helper to free resources
*
*/
static void free_resources(void)
{
if (vboxDev)
{
if (vboxDev->hypervisorStart)
{
}
if (vboxDev->irqAckRequest)
{
}
if (vboxDev->pVMMDevMemory)
}
}
#define PCI_DEV_GET(v,d,p) pci_get_device(v,d,p)
#define PCI_DEV_PUT(x) pci_dev_put(x)
#else
#define PCI_DEV_GET(v,d,p) pci_find_device(v,d,p)
#define PCI_DEV_PUT(x)
#endif
/**
* Module initialization
*
*/
{
int err;
int rcVBox;
if (vboxadd_cmc_init ())
{
return -ENODEV;
}
/*
* Detect PCI device
*/
if (!pcidev)
{
return -ENODEV;
}
if (err)
{
return -ENODEV;
}
LogRel(("Starting VirtualBox version %s Guest Additions\n",
/* register a character device */
{
vbox_major, err);
LogRelFunc(("register_chrdev failed: vbox_major: %d, err = %d\n",
vbox_major, err));
return -ENODEV;
}
/* if no major code was set, take the return value */
if (!vbox_major)
vbox_major = err;
/* allocate and initialize device extension */
if (!vboxDev)
{
LogRelFunc(("cannot allocate device!\n"));
goto fail;
}
/* get the IO port region */
/* get the memory region */
/* all resources found? */
{
LogRelFunc(("did not find expected hardware resources!\n"));
goto fail;
}
/* request ownership of adapter memory */
{
LogRelFunc(("failed to request adapter memory!\n"));
goto fail;
}
/* map adapter memory into kernel address space and check version */
if (!vboxDev->pVMMDevMemory)
{
LogRelFunc(("ioremap failed\n"));
goto fail;
}
{
"vboxadd: invalid VMM device memory version! (got 0x%x, expected 0x%x)\n",
LogRelFunc(("invalid VMM device memory version! (got 0x%x, expected 0x%x)\n",
goto fail;
}
/* initialize VBGL subsystem */
if (VBOX_FAILURE(rcVBox))
{
goto fail;
}
/* report guest information to host, this must be done as the very first request */
sizeof(VMMDevReportGuestInfo), VMMDevReq_ReportGuestInfo);
if (VBOX_FAILURE(rcVBox))
{
goto fail;
}
/* report guest version to host, the VMMDev requires that to be done first */
#else
#endif
{
"vboxadd: error reporting guest info to host! rc = %d, header.rc = %d\n",
LogRelFunc(("error reporting guest info to host! rc = %Vrc, header.rc = %Vrc\n",
goto fail;
}
/* perform hypervisor address space reservation */
if (vboxadd_reserve_hypervisor())
{
/* we just ignore the error, no address window reservation, non fatal */
}
/* allocate a VMM request structure for use in the ISR */
sizeof(VMMDevEvents), VMMDevReq_AcknowledgeEvents);
if (VBOX_FAILURE(rcVBox))
{
goto fail;
}
/* get ISR */
#else
#endif
"vboxadd", vboxDev);
if (err)
{
goto fail;
}
/* some useful information for the user */
"vboxadd: major code: %d, using irq %d, "
"io port 0x%x, memory at 0x%x (size %d bytes), "
"hypervisor window at 0x%p (size 0x%x bytes)\n",
LogRelFunc(("major code: %d, using irq %d, "
"io port 0x%x, memory at 0x%x (size %d bytes), "
"hypervisor window at 0x%p (size 0x%x bytes)\n",
/* successful return */
return 0;
fail:
return err;
}
/**
* Module termination
*
*/
{
LogRelFunc(("unloading...\n"));
vboxadd_cmc_fini ();
LogRelFunc(("unloaded\n"));
}
/* PCI hotplug structure */
{
{
},
{
/* empty entry */
}
};
int __gxx_personality_v0 = 0xdeadbeef;
/*
* Local Variables:
* c-mode: bsd
* indent-tabs-mode: nil
* c-plusplus: evil
* End:
*/