/* $Id$ */
/** @file
* VirtualBox Guest Additions - Virtio Driver for Solaris.
*/
/*
* Copyright (C) 2010-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* 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.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#ifdef DEBUG_ramshankar
# define LOG_INSTANCE RTLogRelDefaultInstance()
#endif
#include "Virtio-solaris.h"
#include <iprt/assert.h>
#include <iprt/mem.h>
#include <VBox/log.h>
/**
* 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);
}