Virtio-solaris.c revision 38b0f04a36431d57eed1de01653dd0a015eae4c3
/* $Id$ */
/** @file
* VirtualBox Guest Additions: Virtio Driver for Solaris.
*/
/*
* Copyright (C) 2010 Oracle Corporation
*
* Oracle Corporation confidential
* All rights reserved
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include "Virtio-solaris.h"
#include <iprt/assert.h>
#include <iprt/mem.h>
#include <VBox/log.h>
#if defined(DEBUG_ramshankar)
# undef LogFlowFunc
# define LogFlowFunc LogRel
# undef Log
# define Log LogRel
# undef LogFlow
# define LogFlow LogRel
#endif
/**
* Virtio Attach routine that should be called from all Virtio drivers' attach
* routines.
*
* @param pDip The module structure instance.
* @param enmCmd Operation type (attach/resume).
* @param pDeviceOps Pointer to device ops structure.
* @param pHyperOps Pointer to hypervisor ops structure.
*
* @return Solaris DDI error code. DDI_SUCCESS or DDI_FAILURE.
*/
int VirtioAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd, PVIRTIODEVICEOPS pDeviceOps, PVIRTIOHYPEROPS pHyperOps)
{
LogFlowFunc((VIRTIOLOGNAME ":VirtioAttach: pDip=%p enmCmd=%d pDeviceOps=%p pHyperOps=%p\n", pDip, enmCmd, pDeviceOps, pHyperOps));
AssertReturn(pDip, DDI_EINVAL);
AssertReturn(pDeviceOps, DDI_EINVAL);
AssertReturn(pHyperOps, DDI_EINVAL);
if (enmCmd != DDI_ATTACH)
{
LogRel((VIRTIOLOGNAME ":VirtioAttach: Invalid enmCmd=%#x expected DDI_ATTACH\n", enmCmd));
return DDI_FAILURE;
}
int rc = DDI_FAILURE;
PVIRTIODEVICE pDevice = RTMemAllocZ(sizeof(VIRTIODEVICE));
if (RT_LIKELY(pDevice))
{
pDevice->pDip = pDip;
pDevice->pDeviceOps = pDeviceOps;
pDevice->pHyperOps = pHyperOps;
pDevice->pvDevice = pDevice->pDeviceOps->pfnAlloc(pDevice);
if (RT_LIKELY(pDevice->pvDevice))
{
pDevice->pvHyper = pDevice->pHyperOps->pfnAlloc(pDevice);
if (RT_LIKELY(pDevice->pvHyper))
{
/*
* Attach hypervisor interface and obtain features supported by host.
*/
rc = pDevice->pHyperOps->pfnAttach(pDevice);
if (rc == DDI_SUCCESS)
{
pDevice->fHostFeatures = pDevice->pHyperOps->pfnGetFeatures(pDevice);
LogFlow((VIRTIOLOGNAME ":VirtioAttach: Host features=%#x\n", pDevice->fHostFeatures));
/*
* Attach the device type interface.
*/
rc = pDevice->pDeviceOps->pfnAttach(pDevice);
if (rc == DDI_SUCCESS)
{
ddi_set_driver_private(pDip, pDevice);
return DDI_SUCCESS;
}
else
LogRel((VIRTIOLOGNAME ":VirtioAttach: DeviceOps pfnAttach failed. rc=%d\n", rc));
pDevice->pHyperOps->pfnDetach(pDevice);
}
else
LogRel((VIRTIOLOGNAME ":VirtioAttach: HyperOps pfnAttach failed. rc=%d\n", rc));
pDevice->pHyperOps->pfnFree(pDevice);
}
else
LogRel((VIRTIOLOGNAME ":VirtioAttach: HyperOps->pfnAlloc failed!\n"));
pDevice->pDeviceOps->pfnFree(pDevice);
}
else
LogRel((VIRTIOLOGNAME ":VirtioAttach: DeviceOps->pfnAlloc failed!\n"));
RTMemFree(pDevice);
}
else
LogRel((VIRTIOLOGNAME ":VirtioAttach: failed to alloc %u bytes for device structure.\n", sizeof(VIRTIODEVICE)));
return DDI_FAILURE;
}
/**
* Virtio Detach routine that should be called from all Virtio drivers' detach
* routines.
*
* @param pDip The module structure instance.
* @param enmCmd Operation type (detach/suspend).
*
* @return Solaris DDI error code. DDI_SUCCESS or DDI_FAILURE.
*/
int VirtioDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
{
LogFlowFunc((VIRTIOLOGNAME ":VirtioDetach pDip=%p enmCmd=%d\n", pDip, enmCmd));
PVIRTIODEVICE pDevice = ddi_get_driver_private(pDip);
if (RT_UNLIKELY(!pDevice))
return DDI_FAILURE;
if (enmCmd != DDI_DETACH)
{
LogRel((VIRTIOLOGNAME ":VirtioDetach: Invalid enmCmd=%#x expected DDI_DETACH.\n", enmCmd));
return DDI_FAILURE;
}
int rc = pDevice->pDeviceOps->pfnDetach(pDevice);
if (rc == DDI_SUCCESS)
{
pDevice->pHyperOps->pfnDetach(pDevice);
pDevice->pDeviceOps->pfnFree(pDevice);
pDevice->pvDevice = NULL;
pDevice->pHyperOps->pfnFree(pDevice);
pDevice->pvHyper = NULL;
ddi_set_driver_private(pDevice->pDip, NULL);
RTMemFree(pDevice);
return DDI_SUCCESS;
}
else
LogRel((VIRTIOLOGNAME ":VirtioDetach: DeviceOps pfnDetach failed. rc=%d\n", rc));
return DDI_FAILURE;
}
/**
* Allocates a Virtio Queue object and assigns it an index.
*
* @param pDevice Pointer to the Virtio device instance.
*
* @return A pointer to a Virtio Queue instance.
*/
PVIRTIOQUEUE VirtioGetQueue(PVIRTIODEVICE pDevice, uint16_t Index)
{
PVIRTIOQUEUE pQueue = RTMemAllocZ(sizeof(VIRTIOQUEUE));
if (RT_UNLIKELY(!pQueue))
{
LogRel((VIRTIOLOGNAME ":VirtioGetQueue: failed to alloc memory for %u bytes.\n", sizeof(VIRTIOQUEUE)));
return NULL;
}
pQueue->QueueIndex = Index;
pQueue->pvData = pDevice->pHyperOps->pfnGetQueue(pDevice, pQueue);
if (RT_UNLIKELY(!pQueue->pvData))
{
LogRel((VIRTIOLOGNAME ":VirtioGetQueue: HyperOps GetQueue failed!\n"));
RTMemFree(pQueue);
return NULL;
}
AssertReturn(pQueue->pQueue, NULL);
AssertReturn(pQueue->Ring.cDesc > 0, NULL);
/* @todo enable interrupt. */
return pQueue;
}
/**
* Puts a queue and destroys the instance.
*
* @param pDevice Pointer to the Virtio device instance.
* @param pQueue Pointer to the Virtio queue.
*/
void VirtioPutQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue)
{
AssertReturnVoid(pDevice);
AssertReturnVoid(pQueue);
pDevice->pHyperOps->pfnPutQueue(pDevice, pQueue);
RTMemFree(pQueue);
}