VBoxGuest-solaris.c revision 38a8f7dc49d1dff47a643e2bac0305b48fb25a1e
/* $Id$ */
/** @file
* VirtualBox Guest Additions Driver for Solaris.
*/
/*
* Copyright (C) 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
#if defined(DEBUG_ramshankar) && !defined(LOG_ENABLED)
#define LOG_ENABLED
#endif
#include "VBoxGuestInternal.h"
#include <iprt/initterm.h>
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The module name. */
#define DEVICE_NAME "vboxadd"
/** The module description as seen in 'modinfo'. */
#define DEVICE_DESC "VirtualBox Guest Driver"
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int VBoxAddSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int mode, cred_t *pCred, int *pVal);
static int VBoxAddSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
DECLVBGL(int) VBoxGuestSolarisServiceCall(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned);
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/**
*/
static struct cb_ops g_VBoxAddSolarisCbOps =
{
nodev, /* b strategy */
nodev, /* b dump */
nodev, /* b print */
nodev, /* c devmap */
nodev, /* c mmap */
nodev, /* c segmap */
nochpoll, /* c poll */
ddi_prop_op, /* property ops */
NULL, /* streamtab */
CB_REV /* revision */
};
/**
* dev_ops: for driver device operations
*/
static struct dev_ops g_VBoxAddSolarisDevOps =
{
DEVO_REV, /* driver build revision */
0, /* ref count */
nulldev, /* identify */
nulldev, /* probe */
nodev, /* reset */
(struct bus_ops *)0,
nodev /* power */
};
/**
* modldrv: export driver specifics to the kernel
*/
static struct modldrv g_VBoxAddSolarisModule =
{
&mod_driverops, /* extern from kernel */
};
/**
*/
static struct modlinkage g_VBoxAddSolarisModLinkage =
{
MODREV_1, /* loadable module system revision */
NULL /* terminate array of linkage structures */
};
/**
* State info for each open file handle.
*/
typedef struct
{
/** PCI handle of VMMDev. */
/** IO port handle. */
/** MMIO handle. */
/** Interrupt block cookie. */
/** Driver Mutex. */
/** IO Port. */
/** Address of the MMIO region.*/
/** Size of the MMIO region. */
/** VMMDev Version. */
#ifndef USE_SESSION_HASH
/** Pointer to the session handle. */
#endif
/** Device handle (we support only one instance). */
static dev_info_t *g_pDip;
/** Opaque pointer to state */
static void *g_pVBoxAddSolarisState;
/** Device extention & session data association structure. */
static VBOXGUESTDEVEXT g_DevExt;
/** Spinlock protecting g_apSessionHashTab. */
#ifdef USE_SESSION_HASH
/** Hash table */
/** Calculates the index into g_apSessionHashTab.*/
#endif /* USE_SESSION_HASH */
/** GCC C++ hack. */
unsigned __gxx_personality_v0 = 0xdecea5ed;
/**
* Kernel entry points
*/
int _init(void)
{
if (!rc)
{
if (rc)
}
return rc;
}
int _fini(void)
{
if (!rc)
return rc;
}
{
}
/**
* Attach entry point, to attach a device to the system or resume it.
*
* @param pDip The module structure instance.
* @param enmCmd Attach type (ddi_attach_cmd_t)
*
* @return corresponding solaris error code.
*/
{
switch (enmCmd)
{
case DDI_ATTACH:
{
int rc;
int instance;
#ifdef USE_SESSION_HASH
if (rc != DDI_SUCCESS)
{
return DDI_FAILURE;
}
if (!pState)
{
return DDI_FAILURE;
}
#else
if (!pState)
{
return DDI_FAILURE;
}
#endif
/*
* Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
*/
if (RT_FAILURE(rc))
{
return DDI_FAILURE;
}
/*
* Initialize the session hash table.
*/
if (RT_SUCCESS(rc))
{
/*
* Enable resources for PCI access.
*/
if (rc == DDI_SUCCESS)
{
/*
* Check vendor and device ID.
*/
if ( uVendorID == VMMDEV_VENDORID
&& uDeviceID == VMMDEV_DEVICEID)
{
/*
* Verify PCI class of the device (a bit paranoid).
*/
if ( uClass == PCI_CLASS_PERIPH
&& uSubClass == PCI_PERIPH_OTHER)
{
/*
* Map the register address space.
*/
if (rc == DDI_SUCCESS)
{
/*
* Read size of the MMIO region.
*/
if (rc == DDI_SUCCESS)
{
&pState->PciMMIOHandle);
if (rc == DDI_SUCCESS)
{
/*
* Add IRQ of VMMDev.
*/
if (rc == DDI_SUCCESS)
{
/*
* Call the common device extension initializer.
*/
if (RT_SUCCESS(rc))
{
if (rc == DDI_SUCCESS)
{
return DDI_SUCCESS;
}
}
else
}
else
}
else
}
else
}
else
}
else
}
else
}
else
}
else
RTR0Term();
return DDI_FAILURE;
}
case DDI_RESUME:
{
/** @todo implement resume for guest driver. */
return DDI_SUCCESS;
}
default:
return DDI_FAILURE;
}
}
/**
* Detach entry point, to detach a device to the system or suspend it.
*
* @param pDip The module structure instance.
* @param enmCmd Attach type (ddi_attach_cmd_t)
*
* @return corresponding solaris error code.
*/
{
switch (enmCmd)
{
case DDI_DETACH:
{
int rc;
#ifdef USE_SESSION_HASH
#else
#endif
if (pState)
{
#ifdef USE_SESSION_HASH
#else
#endif
RTR0Term();
return DDI_SUCCESS;
}
return DDI_FAILURE;
}
case DDI_SUSPEND:
{
/** @todo implement suspend for guest driver. */
return DDI_SUCCESS;
}
default:
return DDI_FAILURE;
}
}
/**
* Info entry point, called by solaris kernel for obtaining driver info.
*
* @param pDip The module structure instance (do not use).
* @param enmCmd Information request type.
* @param pArg Type specific argument.
* @param ppResult Where to store the requested info.
*
* @return corresponding solaris error code.
*/
static int VBoxAddSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult)
{
int rc = DDI_SUCCESS;
switch (enmCmd)
{
case DDI_INFO_DEVT2DEVINFO:
break;
case DDI_INFO_DEVT2INSTANCE:
break;
default:
rc = DDI_FAILURE;
break;
}
return rc;
}
/**
* User context entry points
*/
{
int rc;
/*
* Verify we are being opened as a character device
*/
return EINVAL;
#ifndef USE_SESSION_HASH
unsigned iOpenInstance;
{
{
break;
}
}
if (!pState)
{
return ENXIO;
}
/*
* Create a new session.
*/
if (RT_SUCCESS(rc))
{
Log((DEVICE_NAME "VBoxAddSolarisOpen: pSession=%p pState=%p pid=%d\n", pSession, pState, (int)RTProcSelf()));
return 0;
}
/* Failed, clean up. */
#else
/*
* Create a new session.
*/
if (RT_SUCCESS(rc))
{
/*
* Insert it into the hash table.
*/
int instance;
{
if (pState)
break;
}
if (instance >= 4096)
{
return ENXIO;
}
Log((DEVICE_NAME "VBoxAddSolarisOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, (int)RTProcSelf()));
return 0;
}
#endif
return rc;
}
{
#ifdef USE_SESSION_HASH
/*
* Remove from the hash table.
*/
if (pSession)
{
{
}
else
{
while (pSession)
{
{
break;
}
/* next */
}
}
}
if (!pSession)
{
Log((DEVICE_NAME ":VBoxAddSolarisClose: WHUT?!? pSession == NULL! This must be a mistake... pid=%d", (int)Process));
return EFAULT;
}
#else
if (!pState)
{
return EFAULT;
}
if (!pSession)
{
return EFAULT;
}
#endif
/*
* Close the session.
*/
return 0;
}
{
return 0;
}
{
return 0;
}
/** @def IOCPARM_LEN
* Gets the length from the ioctl number.
*/
#ifndef IOCPARM_LEN
#endif
/**
* Driver ioctl, an alternate entry point for this character driver.
*
* @param Dev Device number
* @param Cmd Operation identifier
* @param pArg Arguments from user to driver
* @param pCred User credentials
* @param pVal Return value for calling process.
*
* @return corresponding solaris error code.
*/
static int VBoxAddSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal)
{
#ifdef USE_SESSION_HASH
/*
* Find the session.
*/
{
}
if (!pSession)
{
Log((DEVICE_NAME ":VBoxAddSolarisIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#x\n", (int)Process, Cmd));
return EINVAL;
}
#else
/*
* Get the session from the soft state item.
*/
if (!pState)
{
return EINVAL;
}
if (!pSession)
{
return EINVAL;
}
#endif
/** @todo I'll remove this size check after testing. */
if ( Cmd >= VBOXGUEST_IOCTL_VMMREQUEST(0)
{
cbBuf = sizeof(VMMDevRequestHeader);
}
#ifdef VBOX_HGCM
else if ( Cmd >= VBOXGUEST_IOCTL_HGCM_CALL(0)
{
cbBuf = sizeof(VBoxGuestHGCMCallInfo);
}
#endif /* VBOX_HGCM */
else
{
switch (Cmd)
{
cbBuf = sizeof(VBoxGuestPortInfo);
break;
cbBuf = sizeof(VBoxGuestWaitEventInfo);
break;
cbBuf = sizeof(VBoxGuestFilterMaskInfo);
break;
#ifdef VBOX_HGCM
cbBuf = sizeof(VBoxGuestHGCMConnectInfo);
break;
cbBuf = sizeof(VBoxGuestHGCMDisconnectInfo);
break;
break;
#endif /* VBOX_HGCM */
default:
{
return VERR_NOT_SUPPORTED;
}
}
}
#if 0
/* cbBuf must actually get the size based on the VMM request type.
* Anyway, this obtaining cbBuf businesss will be removed eventually.
*/
{
LogRel((DEVICE_NAME ":VBoxAddSolarisIOCtl: buffer size mismatch. size=%d expected=%d.\n", IOCPARM_LEN(Cmd), cbBuf));
return EINVAL;
}
#endif
if (RT_UNLIKELY(!pvBuf))
{
return ENOMEM;
}
if (RT_UNLIKELY(rc))
{
Log((DEVICE_NAME ":VBoxAddSolarisIOCtl: ddi_copyin failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc));
return EFAULT;
}
{
}
if (RT_SUCCESS(rc))
{
{
Log((DEVICE_NAME ":VBoxAddSolarisIOCtl: too much output data %d expected %d\n", cbDataReturned, cbBuf));
}
if (RT_UNLIKELY(rc))
{
Log((DEVICE_NAME ":VBoxAddSolarisIOCtl: ddi_copyout failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc));
}
}
else
{
}
return rc;
}
/**
* Sets IRQ for VMMDev.
*
* @returns Solaris error code.
* @param pDip Pointer to the device info structure.
* @param pvState Pointer to the state info structure.
*/
{
int rc;
/*
* These calls are supposedly deprecated. But Sun seems to use them all over
* the place. Anyway, once this works we will switch to the highly elaborate
* and non-obsolete way of setting up IRQs.
*/
if (rc == DDI_SUCCESS)
{
if (rc != DDI_SUCCESS)
}
else
return rc;
}
/**
* Removes IRQ for VMMDev.
*
* @param pDip Pointer to the device info structure.
* @param pvState Opaque pointer to the state info structure.
*/
{
}
/**
* Interrupt Service Routine for VMMDev.
*
* @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
*/
{
}
/**
* 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) VBoxGuestSolarisServiceCall(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned)
{
}
/**
* Solaris Guest service open.
*
* @returns Opaque pointer to session object.
* @param pu32Version Where to store VMMDev version.
*/
{
if (RT_SUCCESS(rc))
{
return pSession;
}
return NULL;
}
/**
* Solaris Guest service close.
*
* @returns VBox error code.
* @param pvState Opaque pointer to the session object.
*/
{
if (pSession)
{
return VINF_SUCCESS;
}
return VERR_INVALID_HANDLE;
}