DrvBlock.cpp revision ad77e3ec3cde24263bc7537575f5cae442bee3b1
/** @file
*
* VBox storage devices:
* Generic block driver
*/
/*
* Copyright (C) 2006-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 *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DRV_BLOCK
#include <string.h>
#include "Builtins.h"
/** @def VBOX_PERIODIC_FLUSH
* Enable support for periodically flushing the VDI to disk. This may prove
* useful for those nasty problems with the ultra-slow host filesystems.
* If this is enabled, it can be configured via the CFGM key
* "VBoxInternal/Devices/piix3ide/0/LUN#<x>/Config/FlushInterval". <x>
* must be replaced with the correct LUN number of the disk that should
* do the periodic flushes. The value of the key is the number of bytes
* written between flushes. A value of 0 (the default) denotes no flushes. */
#define VBOX_PERIODIC_FLUSH
/** @def VBOX_IGNORE_FLUSH
* Enable support for ignoring VDI flush requests. This can be useful for
* filesystems that show bad guest IDE write performance (especially with
* Windows guests). NOTE that this does not disable the flushes caused by
* the periodic flush cache feature above.
* If this feature is enabled, it can be configured via the CFGM key
* "VBoxInternal/Devices/piix3ide/0/LUN#<x>/Config/IgnoreFlush". <x>
* must be replaced with the correct LUN number of the disk that should
* ignore flush requests. The value of the key is a boolean. The default
* is to ignore flushes, i.e. true. */
#define VBOX_IGNORE_FLUSH
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Block driver instance data.
*/
typedef struct DRVBLOCK
{
/** Pointer driver instance. */
/** Drive type. */
/** Locked indicator. */
bool fLocked;
/** Mountable indicator. */
bool fMountable;
/** Visible to the BIOS. */
bool fBiosVisible;
#ifdef VBOX_PERIODIC_FLUSH
/** HACK: Configuration value for number of bytes written after which to flush. */
/** HACK: Current count for the number of bytes written since the last flush. */
#endif /* VBOX_PERIODIC_FLUSH */
#ifdef VBOX_IGNORE_FLUSH
/** HACK: Disable flushes for this drive. */
bool fIgnoreFlush;
#endif /* VBOX_IGNORE_FLUSH */
/** Pointer to the media driver below us.
* This is NULL if the media is not mounted. */
/** Pointer to the block port interface above us. */
/** Pointer to the mount notify interface above us. */
/** Our block interface. */
/** Our block interface. */
/** Our mountable interface. */
/** Pointer to the async media driver below us.
* This is NULL if the media is not mounted. */
/** Our media async port. */
/** Pointer to the async block port interface above us. */
/** Our async block interface. */
/** Uuid of the drive. */
/** BIOS PCHS Geometry. */
/** BIOS LCHS Geometry. */
/* -=-=-=-=- IBlock -=-=-=-=- */
/** Makes a PDRVBLOCK out of a PPDMIBLOCK. */
#define PDMIBLOCK_2_DRVBLOCK(pInterface) ( (PDRVBLOCK)((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IBlock)) )
/** @copydoc PDMIBLOCK::pfnRead */
static DECLCALLBACK(int) drvblockRead(PPDMIBLOCK pInterface, uint64_t off, void *pvBuf, size_t cbRead)
{
/*
* Check the state.
*/
{
AssertMsgFailed(("Invalid state! Not mounted!\n"));
return VERR_PDM_MEDIA_NOT_MOUNTED;
}
return rc;
}
/** @copydoc PDMIBLOCK::pfnWrite */
static DECLCALLBACK(int) drvblockWrite(PPDMIBLOCK pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
{
/*
* Check the state.
*/
{
AssertMsgFailed(("Invalid state! Not mounted!\n"));
return VERR_PDM_MEDIA_NOT_MOUNTED;
}
#ifdef VBOX_PERIODIC_FLUSH
if (pThis->cbFlushInterval)
{
{
pThis->cbDataWritten = 0;
}
}
#endif /* VBOX_PERIODIC_FLUSH */
return rc;
}
/** @copydoc PDMIBLOCK::pfnFlush */
{
/*
* Check the state.
*/
{
AssertMsgFailed(("Invalid state! Not mounted!\n"));
return VERR_PDM_MEDIA_NOT_MOUNTED;
}
#ifdef VBOX_IGNORE_FLUSH
if (pThis->fIgnoreFlush)
return VINF_SUCCESS;
#endif /* VBOX_IGNORE_FLUSH */
if (rc == VERR_NOT_IMPLEMENTED)
rc = VINF_SUCCESS;
return rc;
}
/** @copydoc PDMIBLOCK::pfnIsReadOnly */
{
/*
* Check the state.
*/
return false;
return fRc;
}
/** @copydoc PDMIBLOCK::pfnGetSize */
{
/*
* Check the state.
*/
return 0;
return cb;
}
/** @copydoc PDMIBLOCK::pfnGetType */
{
}
/** @copydoc PDMIBLOCK::pfnGetUuid */
{
/*
* Copy the uuid.
*/
return VINF_SUCCESS;
}
/* -=-=-=-=- IBlockAsync -=-=-=-=- */
/** Makes a PDRVBLOCK out of a PPDMIBLOCKASYNC. */
#define PDMIBLOCKASYNC_2_DRVBLOCK(pInterface) ( (PDRVBLOCK)((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IBlockAsync)) )
/** @copydoc PDMIBLOCKASYNC::pfnStartRead */
static DECLCALLBACK(int) drvblockAsyncReadStart(PPDMIBLOCKASYNC pInterface, uint64_t off, PPDMDATASEG pSeg, unsigned cSeg, size_t cbRead, void *pvUser)
{
/*
* Check the state.
*/
if (!pThis->pDrvMediaAsync)
{
AssertMsgFailed(("Invalid state! Not mounted!\n"));
return VERR_PDM_MEDIA_NOT_MOUNTED;
}
int rc = pThis->pDrvMediaAsync->pfnStartRead(pThis->pDrvMediaAsync, off, pSeg, cSeg, cbRead, pvUser);
return rc;
}
/** @copydoc PDMIBLOCKASYNC::pfnStartWrite */
static DECLCALLBACK(int) drvblockAsyncWriteStart(PPDMIBLOCKASYNC pInterface, uint64_t off, PPDMDATASEG pSeg, unsigned cSeg, size_t cbWrite, void *pvUser)
{
/*
* Check the state.
*/
if (!pThis->pDrvMediaAsync)
{
AssertMsgFailed(("Invalid state! Not mounted!\n"));
return VERR_PDM_MEDIA_NOT_MOUNTED;
}
int rc = pThis->pDrvMediaAsync->pfnStartWrite(pThis->pDrvMediaAsync, off, pSeg, cSeg, cbWrite, pvUser);
return rc;
}
/* -=-=-=-=- IMediaAsyncPort -=-=-=-=- */
/** Makes a PDRVBLOCKASYNC out of a PPDMIMEDIAASYNCPORT. */
#define PDMIMEDIAASYNCPORT_2_DRVBLOCK(pInterface) ( (PDRVBLOCK((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IMediaAsyncPort))) )
static DECLCALLBACK(int) drvblockAsyncTransferCompleteNotify(PPDMIMEDIAASYNCPORT pInterface, void *pvUser)
{
}
/* -=-=-=-=- IBlockBios -=-=-=-=- */
/** Makes a PDRVBLOCK out of a PPDMIBLOCKBIOS. */
#define PDMIBLOCKBIOS_2_DRVBLOCK(pInterface) ( (PDRVBLOCK((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IBlockBios))) )
/** @copydoc PDMIBLOCKBIOS::pfnGetPCHSGeometry */
static DECLCALLBACK(int) drvblockGetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pPCHSGeometry)
{
/*
* Check the state.
*/
return VERR_PDM_MEDIA_NOT_MOUNTED;
/*
* Use configured/cached values if present.
*/
{
LogFlow(("%s: returns VINF_SUCCESS {%d,%d,%d}\n", __FUNCTION__, pThis->PCHSGeometry.cCylinders, pThis->PCHSGeometry.cHeads, pThis->PCHSGeometry.cSectors));
return VINF_SUCCESS;
}
/*
* Call media.
*/
if (RT_SUCCESS(rc))
{
LogFlow(("%s: returns %Rrc {%d,%d,%d}\n", __FUNCTION__, rc, pThis->PCHSGeometry.cCylinders, pThis->PCHSGeometry.cHeads, pThis->PCHSGeometry.cSectors));
}
else if (rc == VERR_NOT_IMPLEMENTED)
{
}
return rc;
}
/** @copydoc PDMIBLOCKBIOS::pfnSetPCHSGeometry */
static DECLCALLBACK(int) drvblockSetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pPCHSGeometry)
{
LogFlow(("%s: cCylinders=%d cHeads=%d cSectors=%d\n", __FUNCTION__, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
/*
* Check the state.
*/
{
AssertMsgFailed(("Invalid state! Not mounted!\n"));
return VERR_PDM_MEDIA_NOT_MOUNTED;
}
/*
* Call media. Ignore the not implemented return code.
*/
if ( RT_SUCCESS(rc)
|| rc == VERR_NOT_IMPLEMENTED)
{
rc = VINF_SUCCESS;
}
return rc;
}
/** @copydoc PDMIBLOCKBIOS::pfnGetLCHSGeometry */
static DECLCALLBACK(int) drvblockGetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pLCHSGeometry)
{
/*
* Check the state.
*/
return VERR_PDM_MEDIA_NOT_MOUNTED;
/*
* Use configured/cached values if present.
*/
{
LogFlow(("%s: returns VINF_SUCCESS {%d,%d,%d}\n", __FUNCTION__, pThis->LCHSGeometry.cCylinders, pThis->LCHSGeometry.cHeads, pThis->LCHSGeometry.cSectors));
return VINF_SUCCESS;
}
/*
* Call media.
*/
if (RT_SUCCESS(rc))
{
LogFlow(("%s: returns %Rrc {%d,%d,%d}\n", __FUNCTION__, rc, pThis->LCHSGeometry.cCylinders, pThis->LCHSGeometry.cHeads, pThis->LCHSGeometry.cSectors));
}
else if (rc == VERR_NOT_IMPLEMENTED)
{
}
return rc;
}
/** @copydoc PDMIBLOCKBIOS::pfnSetLCHSGeometry */
static DECLCALLBACK(int) drvblockSetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pLCHSGeometry)
{
LogFlow(("%s: cCylinders=%d cHeads=%d cSectors=%d\n", __FUNCTION__, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
/*
* Check the state.
*/
{
AssertMsgFailed(("Invalid state! Not mounted!\n"));
return VERR_PDM_MEDIA_NOT_MOUNTED;
}
/*
* Call media. Ignore the not implemented return code.
*/
if ( RT_SUCCESS(rc)
|| rc == VERR_NOT_IMPLEMENTED)
{
rc = VINF_SUCCESS;
}
return rc;
}
/** @copydoc PDMIBLOCKBIOS::pfnIsVisible */
{
return pThis->fBiosVisible;
}
/** @copydoc PDMIBLOCKBIOS::pfnGetType */
{
}
/* -=-=-=-=- IMount -=-=-=-=- */
/** Makes a PDRVBLOCK out of a PPDMIMOUNT. */
#define PDMIMOUNT_2_DRVBLOCK(pInterface) ( (PDRVBLOCK)((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IMount)) )
/** @copydoc PDMIMOUNT::pfnMount */
static DECLCALLBACK(int) drvblockMount(PPDMIMOUNT pInterface, const char *pszFilename, const char *pszCoreDriver)
{
LogFlow(("drvblockMount: pszFilename=%p:{%s} pszCoreDriver=%p:{%s}\n", pszFilename, pszFilename, pszCoreDriver, pszCoreDriver));
/*
* Validate state.
*/
{
AssertMsgFailed(("Already mounted\n"));
return VERR_PDM_MEDIA_MOUNTED;
}
/*
* Prepare configuration.
*/
if (pszFilename)
{
if (RT_FAILURE(rc))
{
return rc;
}
}
/*
* Attach the media driver and query it's interface.
*/
if (RT_FAILURE(rc))
{
return rc;
}
{
/** @todo r=klaus missing async handling, this is just a band aid to
* avoid using stale information */
/*
* Initialize state.
*/
#ifdef VBOX_PERIODIC_FLUSH
pThis->cbDataWritten = 0;
#endif /* VBOX_PERIODIC_FLUSH */
/*
*/
if (pThis->pDrvMountNotify)
Log(("drvblockMount: Success\n"));
return VINF_SUCCESS;
}
else
/*
* Failed, detatch the media driver.
*/
AssertMsgFailed(("No media interface!\n"));
return rc;
}
/** @copydoc PDMIMOUNT::pfnUnmount */
{
/*
* Validate state.
*/
{
Log(("drvblockUmount: Not mounted\n"));
return VERR_PDM_MEDIA_NOT_MOUNTED;
}
{
Log(("drvblockUmount: Locked\n"));
return VERR_PDM_MEDIA_LOCKED;
}
/* Media is no longer locked even if it was previously. */
/*
* Detach the media driver and query it's interface.
*/
if (RT_FAILURE(rc))
{
return rc;
}
/*
*/
if (pThis->pDrvMountNotify)
Log(("drvblockUnmount: success\n"));
return VINF_SUCCESS;
}
/** @copydoc PDMIMOUNT::pfnIsMounted */
{
}
/** @copydoc PDMIMOUNT::pfnLock */
{
return VINF_SUCCESS;
}
/** @copydoc PDMIMOUNT::pfnUnlock */
{
return VINF_SUCCESS;
}
/** @copydoc PDMIMOUNT::pfnIsLocked */
{
}
/* -=-=-=-=- IBase -=-=-=-=- */
/** @copydoc PDMIBASE::pfnQueryInterface. */
{
switch (enmInterface)
{
case PDMINTERFACE_BASE:
case PDMINTERFACE_BLOCK:
case PDMINTERFACE_BLOCK_BIOS:
case PDMINTERFACE_MOUNT:
case PDMINTERFACE_BLOCK_ASYNC:
default:
return NULL;
}
}
/* -=-=-=-=- driver interface -=-=-=-=- */
/** @copydoc FNPDMDRVDETACH. */
{
}
/**
* Reset notification.
*
* @returns VBox status.
* @param pDevIns The driver instance data.
*/
{
}
/**
* Construct a block driver instance.
*
* @copydoc FNPDMDRVCONSTRUCT
*/
static DECLCALLBACK(int) drvblockConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
{
/*
* Validate configuration.
*/
#if defined(VBOX_PERIODIC_FLUSH) || defined(VBOX_IGNORE_FLUSH)
if (!CFGMR3AreValuesValid(pCfgHandle, "Type\0Locked\0BIOSVisible\0AttachFailError\0Cylinders\0Heads\0Sectors\0Mountable\0FlushInterval\0IgnoreFlush\0"))
#else /* !(VBOX_PERIODIC_FLUSH || VBOX_IGNORE_FLUSH) */
if (!CFGMR3AreValuesValid(pCfgHandle, "Type\0Locked\0BIOSVisible\0AttachFailError\0Cylinders\0Heads\0Sectors\0Mountable\0"))
#endif /* !(VBOX_PERIODIC_FLUSH || VBOX_IGNORE_FLUSH) */
/*
* Initialize most of the data members.
*/
/* IBase. */
/* IBlock. */
/* IBlockBios. */
/* IMount. */
/* IBlockAsync. */
/* IMediaAsyncPort. */
/*
*/
pThis->pDrvBlockPort = (PPDMIBLOCKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_BLOCK_PORT);
if (!pThis->pDrvBlockPort)
N_("No block port interface above"));
/* Try to get the optional async block port interface above. */
pThis->pDrvBlockAsyncPort = (PPDMIBLOCKASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_BLOCK_ASYNC_PORT);
pThis->pDrvMountNotify = (PPDMIMOUNTNOTIFY)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MOUNT_NOTIFY);
/*
* Query configuration.
*/
/* type */
char *psz;
if (RT_FAILURE(rc))
else
{
return VERR_PDM_BLOCK_UNKNOWN_TYPE;
}
/* Mountable */
if (RT_FAILURE(rc))
/* Locked */
if (RT_FAILURE(rc))
/* BIOS visible */
if (RT_FAILURE(rc))
/** @todo AttachFailError is currently completely ignored. */
/* Cylinders */
if (RT_FAILURE(rc))
/* Heads */
if (RT_FAILURE(rc))
/* Sectors */
if (RT_FAILURE(rc))
/* Uuid */
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
else if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
{
return rc;
}
}
else
#ifdef VBOX_PERIODIC_FLUSH
if (RT_FAILURE(rc))
#endif /* VBOX_PERIODIC_FLUSH */
#ifdef VBOX_IGNORE_FLUSH
if (RT_FAILURE(rc))
#endif /* VBOX_IGNORE_FLUSH */
/*
* Try attach driver below and query it's media interface.
*/
if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
return VINF_SUCCESS;
if (RT_FAILURE(rc))
N_("No media or async media interface below"));
/* Try to get the optional async interface. */
{
}
return VINF_SUCCESS;
}
/**
* Block driver registration record.
*/
const PDMDRVREG g_DrvBlock =
{
/* u32Version */
/* szDriverName */
"Block",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"Generic block driver.",
/* fFlags */
/* fClass. */
/* cMaxInstances */
~0,
/* cbInstance */
sizeof(DRVBLOCK),
/* pfnConstruct */
/* pfnDestruct */
NULL,
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32EndVersion */
};