VBoxGuest-solaris.c revision 564daef48aa05d858dcfd13959f107d1388e770e
/* $Id$ */
/** @file
* VirtualBox Guest Additions Driver for Solaris.
*/
/*
* 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <sys/ddi_intr.h>
#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
#include "VBoxGuestInternal.h"
#include <iprt/initterm.h>
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The module name. */
#define DEVICE_NAME "vboxguest"
/** The module description as seen in 'modinfo'. */
#define DEVICE_DESC "VirtualBox GstDrv"
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int VBoxGuestSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal);
static int VBoxGuestSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead);
static int VBoxGuestSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
*/
static struct cb_ops g_VBoxGuestSolarisCbOps =
{
nodev, /* b strategy */
nodev, /* b dump */
nodev, /* b print */
nodev, /* c devmap */
nodev, /* c mmap */
nodev, /* c segmap */
ddi_prop_op, /* property ops */
NULL, /* streamtab */
CB_REV /* revision */
};
/**
* dev_ops: for driver device operations
*/
static struct dev_ops g_VBoxGuestSolarisDevOps =
{
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_VBoxGuestSolarisModule =
{
&mod_driverops, /* extern from kernel */
};
/**
*/
static struct modlinkage g_VBoxGuestSolarisModLinkage =
{
MODREV_1, /* loadable module system revision */
NULL /* terminate array of linkage structures */
};
/**
* State info for each open file handle.
*/
typedef struct
{
/** Pointer to the session handle. */
/** The process reference for posting signals */
void *pvProcRef;
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Device handle (we support only one instance). */
/** Opaque pointer to file-descriptor states */
static void *g_pVBoxGuestSolarisState = NULL;
/** Device extention & session data association structure. */
static VBOXGUESTDEVEXT g_DevExt;
/** IO port handle. */
static ddi_acc_handle_t g_PciIOHandle;
/** MMIO handle. */
static ddi_acc_handle_t g_PciMMIOHandle;
/** IO Port. */
static uint16_t g_uIOPortBase;
/** Address of the MMIO region.*/
static caddr_t g_pMMIOBase;
/** Size of the MMIO region. */
/** Pointer to the interrupt handle vector */
static ddi_intr_handle_t *g_pIntr;
/** Number of actually allocated interrupt handles */
static size_t g_cIntrAllocated;
/** The pollhead structure */
static pollhead_t g_PollHead;
/** The IRQ Mutex */
/**
* Kernel entry points
*/
int _init(void)
{
/*
* Prevent module autounloading.
*/
if (pModCtl)
else
static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
if (RT_SUCCESS(rc))
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:
{
if (g_pDip)
{
return DDI_FAILURE;
}
int rc;
int instance;
/*
* Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
*/
if (RT_FAILURE(rc))
{
return DDI_FAILURE;
}
/*
* Enable resources for PCI access.
*/
if (rc == DDI_SUCCESS)
{
/*
* Map the register address space.
*/
if (rc == DDI_SUCCESS)
{
/*
* Read size of the MMIO region.
*/
if (rc == DDI_SUCCESS)
{
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
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:
{
RTR0Term();
return DDI_SUCCESS;
}
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 pvArg Type specific argument.
* @param ppvResult Where to store the requested info.
*
* @return corresponding solaris error code.
*/
static int VBoxGuestSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
{
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;
unsigned iOpenInstance;
{
{
break;
}
}
if (!pState)
{
return ENXIO;
}
/*
* Create a new session.
*/
if (RT_SUCCESS(rc))
{
return 0;
}
/* Failed, clean up. */
return EFAULT;
}
{
if (!pState)
{
return EFAULT;
}
if (!pSession)
{
return EFAULT;
}
/*
* Close the session.
*/
return 0;
}
{
if (!pState)
{
return EFAULT;
}
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 VBoxGuestSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal)
{
/*
* Get the session from the soft state item.
*/
if (!pState)
{
return EINVAL;
}
if (!pSession)
{
return EINVAL;
}
/*
* Read and validate the request wrapper.
*/
{
LogRel((DEVICE_NAME "::IOCtl: bad request %#x size=%d expected=%d\n", Cmd, IOCPARM_LEN(Cmd), sizeof(ReqWrap)));
return ENOTTY;
}
if (RT_UNLIKELY(rc))
{
LogRel((DEVICE_NAME "::IOCtl: ddi_copyin failed to read header pArg=%p Cmd=%d. rc=%#x.\n", pArg, Cmd, rc));
return EINVAL;
}
{
return EINVAL;
}
{
return EINVAL;
}
/*
* Read the request payload if any; requests like VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS have no data payload.
*/
{
if (RT_UNLIKELY(!pvBuf))
{
return ENOMEM;
}
if (RT_UNLIKELY(rc))
{
LogRel((DEVICE_NAME "::IOCtl: ddi_copyin failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc));
return EFAULT;
}
{
return EINVAL;
}
}
/*
* Process the IOCtl.
*/
size_t cbDataReturned = 0;
if (RT_SUCCESS(rc))
{
rc = 0;
{
LogRel((DEVICE_NAME "::IOCtl: too much output data %d expected %d\n", cbDataReturned, ReqWrap.cbData));
}
if (cbDataReturned > 0)
{
if (RT_UNLIKELY(rc))
{
LogRel((DEVICE_NAME "::IOCtl: ddi_copyout failed; pvBuf=%p pArg=%p cbDataReturned=%u Cmd=%d. rc=%d\n",
}
}
}
else
{
if (rc != VERR_INTERRUPTED)
else
}
if (pvBuf)
return rc;
}
static int VBoxGuestSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead)
{
{
{
}
else
{
*pReqEvents = 0;
if (!fAnyYet)
*ppPollHead = &g_PollHead;
}
return 0;
}
else
{
return EINVAL;
}
}
/**
* Sets IRQ for VMMDev.
*
* @returns Solaris error code.
* @param pDip Pointer to the device info structure.
*/
{
int IntrType = 0;
if (rc == DDI_SUCCESS)
{
/* We won't need to bother about MSIs. */
if (IntrType & DDI_INTR_TYPE_FIXED)
{
int IntrCount = 0;
if ( rc == DDI_SUCCESS
&& IntrCount > 0)
{
int IntrAvail = 0;
if ( rc == DDI_SUCCESS
&& IntrAvail > 0)
{
/* Allocated kernel memory for the interrupt handles. The allocation size is stored internally. */
if (g_pIntr)
{
int IntrAllocated;
if ( rc == DDI_SUCCESS
&& IntrAllocated > 0)
{
if (rc == DDI_SUCCESS)
{
/* Initialize the mutex. */
/* Assign interrupt handler functions and enable interrupts. */
for (int i = 0; i < IntrAllocated; i++)
{
if (rc == DDI_SUCCESS)
if (rc != DDI_SUCCESS)
{
/* Changing local IntrAllocated to hold so-far allocated handles for freeing. */
IntrAllocated = i;
break;
}
}
if (rc == DDI_SUCCESS)
return rc;
/* Remove any assigned handlers */
for (int x = 0; x < IntrAllocated; x++)
}
else
/* Remove allocated IRQs, too bad we can free only one handle at a time. */
for (int k = 0; k < g_cIntrAllocated; k++)
ddi_intr_free(g_pIntr[k]);
}
else
}
else
}
else
LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient available IRQs. rc=%d IntrAvail=%d\n", rc, IntrAvail));
}
else
LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient number of IRQs. rc=%d IntrCount=%d\n", rc, IntrCount));
}
else
}
else
return rc;
}
/**
* Removes IRQ for VMMDev.
*
* @param pDip Pointer to the device info structure.
*/
{
for (int i = 0; i < g_cIntrAllocated; i++)
{
if (rc == DDI_SUCCESS)
{
if (rc == DDI_SUCCESS)
ddi_intr_free(g_pIntr[i]);
}
}
}
/**
* Interrupt Service Routine for VMMDev.
*
* @param Arg Private data (unused, will be NULL).
* @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
*/
{
}
{
/*
* Wake up poll waiters.
*/
}
/* Common code that depend on g_DevExt. */
#include "VBoxGuestIDC-unix.c.h"