VBoxGuest-freebsd.c revision 1c94c0a63ba68be1a7b2c640e70d7a06464e4fca
/* $Id$ */
/** @file
* VirtualBox Guest Additions Driver for FreeBSD.
*/
/*
* Copyright (C) 2007 Sun Microsystems, Inc.
*
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/** @todo r=bird: This must merge with SUPDrv-freebsd.c before long. The two
* source files should only differ on prefixes and the extra bits wrt to the
* pci device. I.e. it should be diffable so that fixes to one can easily be
* applied to the other. */
#include <machine/resource.h>
#include "VBoxGuestInternal.h"
#include <iprt/initterm.h>
/** The module name. */
#define DEVICE_NAME "vboxguest"
struct VBoxGuestDeviceState
{
/** file node minor code */
unsigned minor;
/** first IO port */
int io_port_resid;
/** IO Port. */
/** device memory resources */
int vmmdevmem_resid;
/** physical address of adapter memory */
/** Mapping of the register space */
void *pMMIOBase;
/** IRQ number */
int irq_resid;
void *irq_handler;
/** VMMDev version */
};
#if FreeBSD >= 700
static int VBoxGuestFreeBSDOpen(struct cdev *dev, int flags, int fmt, struct thread *td, struct file *pFd);
#else
#endif
int VBoxGuestFreeBSDISR(void *pvState);
DECLVBGL(int) VBoxGuestFreeBSDServiceCall(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned);
/*
* Device node entry points.
*/
static struct cdevsw g_VBoxAddFreeBSDChrDevSW =
{
.d_flags = D_TRACKCLOSE,
};
/** The make_dev result. */
static struct cdev *g_pVBoxAddFreeBSDChrDev;
/** Device extention & session data association structure. */
static VBOXGUESTDEVEXT g_DevExt;
/** Spinlock protecting g_apSessionHashTab. */
/** Hash table */
/** Calculates the index into g_apSessionHashTab.*/
/** @todo r=bird: fork() and session hash table didn't work well for solaris, so I doubt it works for
* FreeBSD... A different solution is needed unless the current can be improved. */
/**
* VBoxGuest Common ioctl wrapper from VBoxGuestLib.
*
* @returns VBox error code.
* @param pvSession Opaque pointer to the session.
* @param iCmd Requested function.
* @param pvData IO data buffer.
* @param cbData Size of the data buffer.
* @param pcbDataReturned Where to store the amount of returned data.
*/
DECLVBGL(int) VBoxGuestFreeBSDServiceCall(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned)
{
LogFlow((DEVICE_NAME ":VBoxGuestSolarisServiceCall %pvSesssion=%p Cmd=%u pvData=%p cbData=%d\n", pvSession, iCmd, pvData, cbData));
}
/**
* FreeBSD Guest service open.
*
* @returns Opaque pointer to session object.
* @param pu32Version Where to store VMMDev version.
*/
{
if (RT_SUCCESS(rc))
{
return pSession;
}
return NULL;
}
/**
* FreeBSD Guest service close.
*
* @returns VBox error code.
* @param pvState Opaque pointer to the session object.
*/
{
if (pSession)
{
return VINF_SUCCESS;
}
return VERR_INVALID_HANDLE;
}
/**
* File open handler
*
*/
#if FreeBSD >= 700
static int VBoxGuestFreeBSDOpen(struct cdev *dev, int flags, int fmt, struct thread *td, struct file *pFd)
#else
#endif
{
int rc;
/*
* Create a new session.
*/
if (RT_SUCCESS(rc))
{
/*
* Insert it into the hash table.
*/
Log((DEVICE_NAME ":VBoxGuestFreeBSDOpen success: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, (int)RTProcSelf()));
return 0;
}
return EFAULT;
}
/**
* File close handler
*
*/
{
/*
* Remove from the hash table.
*/
if (pSession)
{
{
}
else
{
while (pSession)
{
{
break;
}
/* next */
}
}
}
if (!pSession)
{
Log((DEVICE_NAME ":VBoxGuestFreeBSDClose: WHUT?!? pSession == NULL! This must be a mistake... pid=%d", (int)Process));
return EFAULT;
}
/*
* Close the session.
*/
return 0;
}
/**
* IOCTL handler
*
*/
{
int rc = 0;
/*
* Find the session.
*/
{
}
if (!pSession)
{
Log((DEVICE_NAME ":VBoxGuestFreeBSDIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#lx\n", (int)Process, cmd));
return EINVAL;
}
/*
* Validate the request wrapper.
*/
{
Log((DEVICE_NAME ": VBoxGuestFreeBSDIOCtl: bad request %lu size=%lu expected=%d\n", cmd, IOCPARM_LEN(cmd), sizeof(VBGLBIGREQ)));
return ENOTTY;
}
{
Log((DEVICE_NAME ": VBoxGuestFreeBSDIOCtl: bad magic %#x; pArg=%p Cmd=%lu.\n", ReqWrap->u32Magic, data, cmd));
return EINVAL;
}
{
printf(DEVICE_NAME ": VBoxGuestFreeBSDIOCtl: bad size %#x; pArg=%p Cmd=%lu.\n", ReqWrap->cbData, data, cmd);
return EINVAL;
}
/*
* Read the request.
*/
if (RT_UNLIKELY(!pvBuf))
{
Log((DEVICE_NAME ":VBoxGuestFreeBSDIOCtl: RTMemTmpAlloc failed to alloc %d bytes.\n", ReqWrap->cbData));
return ENOMEM;
}
if (RT_UNLIKELY(rc))
{
Log((DEVICE_NAME ":VBoxGuestFreeBSDIOCtl: copyin failed; pvBuf=%p pArg=%p Cmd=%lu. rc=%d\n", pvBuf, data, cmd, rc));
return EFAULT;
}
{
return EINVAL;
}
/*
* Process the IOCtl.
*/
if (RT_SUCCESS(rc))
{
rc = 0;
{
Log((DEVICE_NAME ":VBoxGuestFreeBSDIOCtl: too much output data %d expected %d\n", cbDataReturned, ReqWrap->cbData));
}
if (cbDataReturned > 0)
{
if (RT_UNLIKELY(rc))
{
Log((DEVICE_NAME ":VBoxGuestFreeBSDIOCtl: copyout failed; pvBuf=%p pArg=%p Cmd=%lu. rc=%d\n", pvBuf, data, cmd, rc));
}
}
}
else
{
}
return rc;
}
{
return 0;
}
{
return 0;
}
{
RTR0Term();
return 0;
}
/**
* Interrupt service routine.
*
* @returns Whether the interrupt was from VMMDev.
* @param pvState Opaque pointer to the device state.
*/
int VBoxGuestFreeBSDISR(void *pvState)
{
return fOurIRQ ? 0 : 1;
}
/**
* Sets IRQ for VMMDev.
*
* @returns FreeBSD error code.
* @param self Pointer to the device info structure.
* @param pvState Pointer to the state info structure.
*/
{
int res_id = 0;
int rc = 0;
#if __FreeBSD_version >= 700000
rc = bus_setup_intr(self, pState->irq, INTR_TYPE_BIO, NULL, (driver_intr_t *)VBoxGuestFreeBSDISR, pState, &pState->irq_handler);
#else
rc = bus_setup_intr(self, pState->irq, INTR_TYPE_BIO, (driver_intr_t *)VBoxGuestFreeBSDISR, pState, &pState->irq_handler);
#endif
if (rc)
{
return VERR_DEV_IO_ERROR;
}
return VINF_SUCCESS;
}
/**
* Removes IRQ for VMMDev.
*
* @param self Pointer to the device info structure.
* @param pvState Opaque pointer to the state info structure.
*/
{
{
}
}
{
int rc = VINF_SUCCESS;
int res_id = 0;
/*
* Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
*/
if (RT_FAILURE(rc))
{
LogFunc(("RTR0Init failed.\n"));
return ENXIO;
}
if (!pState)
{
if (!pState)
return ENOMEM;
}
/*
* Initialize the session hash table.
*/
if (RT_SUCCESS(rc))
{
/*
* Map the register address space.
*/
if (pState->uIOPortBase)
{
/*
* Map the MMIO region.
*/
{
/*
* Add IRQ of VMMDev.
*/
if (RT_SUCCESS(rc))
{
/*
* Call the common device extension initializer.
*/
if (RT_SUCCESS(rc))
{
/*
* Create device node.
*/
0,
0666,
return 0;
}
else
}
else
}
else
}
else
}
else
RTR0Term();
return ENXIO;
}
{
{
Log(("Found VBox device\n"));
return 0;
}
return ENXIO;
}
static device_method_t VBoxGuestFreeBSDMethods[] =
{
/* Device interface. */
{0,0}
};
static driver_t VBoxGuestFreeBSDDriver =
{
sizeof(struct VBoxGuestDeviceState),
};
static devclass_t VBoxGuestFreeBSDClass;
int __gxx_personality_v0 = 0xdeadbeef;