DrvSCSI.cpp revision 9dca051a5f8ff457ef1692990f6ecfa280daf265
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * VBox storage drivers:
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Generic SCSI command parser and execution driver
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Copyright (C) 2006-2009 Sun Microsystems, Inc.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * available from http://www.virtualbox.org. This file is free software;
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * you can redistribute it and/or modify it under the terms of the GNU
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * General Public License (GPL) as published by the Free Software
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * additional information or have any questions.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync/*******************************************************************************
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync* Header Files *
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync*******************************************************************************/
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync//#define DEBUG
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * SCSI driver instance data.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsynctypedef struct DRVSCSI
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** Pointer driver instance. */
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync /** Pointer to the attached driver's base interface. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** Pointer to the attached driver's block interface. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** Pointer to the attached driver's async block interface. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** Pointer to the attached driver's block bios interface. */
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync /** Pointer to the attached driver's mount interface. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** Pointer to the SCSI port interface of the device above. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** pointer to the Led port interface of the dveice above. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** The scsi connector interface .*/
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync /** The block port interface. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** The optional block async port interface. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** The mount notify interface. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** The status LED state for this drive.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * used in case the device doesn't has a Led interface
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * so we can use this to avoid if checks later on. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** pointer to the Led to use. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Device type. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** BIOS PCHS Geometry. */
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync /** BIOS LCHS Geometry. */
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync /** Number of sectors this device has. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** The dedicated I/O thread for the non async approach. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Queue for passing the requests to the thread. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Release statistics: number of bytes written. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Release statistics: number of bytes read. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync/** Converts a pointer to DRVSCSI::ISCSIConnecotr to a PDRVSCSI. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync#define PDMISCSICONNECTOR_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, ISCSIConnector)) )
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Dumps a SCSI request structure for debugging purposes.
0368e9c310393e82ef37c480b6acbd0f107cf0edvboxsync * @returns nothing.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * @param pRequest Pointer to the request to dump.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsyncstatic void drvscsiDumpScsiRequest(PPDMSCSIREQUEST pRequest)
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync Log(("Dump for pRequest=%#p Command: %s\n", pRequest, SCSICmdText(pRequest->pbCDB[0])));
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync Log(("cbScatterGather=%u\n", pRequest->cbScatterGather));
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync Log(("cScatterGatherEntries=%u\n", pRequest->cScatterGatherEntries));
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /* Print all scatter gather entries. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync for (uint32_t i = 0; i < pRequest->cScatterGatherEntries; i++)
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync Log(("ScatterGatherEntry[%u].cbSeg=%u\n", i, pRequest->paScatterGatherHead[i].cbSeg));
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync Log(("ScatterGatherEntry[%u].pvSeg=%#p\n", i, pRequest->paScatterGatherHead[i].pvSeg));
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * Copy the content of a buffer to a scatter gather list only
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * copying only the amount of data which fits into the
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * scatter gather list.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * @returns VBox status code.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * @param pRequest Pointer to the request which contains the S/G list entries.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * @param pvBuf Pointer to the buffer which should be copied.
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * @param cbBuf Size of the buffer.
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsyncstatic int drvscsiScatterGatherListCopyFromBuffer(PPDMSCSIREQUEST pRequest, void *pvBuf, size_t cbBuf)
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync unsigned cSGEntry = 0;
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync PPDMDATASEG pSGEntry = &pRequest->paScatterGatherHead[cSGEntry];
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync LogFlowFunc(("pRequest=%#p pvBuf=%#p cbBuf=%u\n", pRequest, pvBuf, cbBuf));
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync for (unsigned i = 0; i < cbBuf; i++)
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync Log(("%s: pvBuf[%u]=%#x\n", __FUNCTION__, i, pu8Buf[i]));
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync size_t cbToCopy = (cbBuf < pSGEntry->cbSeg) ? cbBuf : pSGEntry->cbSeg;
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync /* We finished. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /* Advance the buffer. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /* Go to the next entry in the list. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsyncstatic void drvscsiPadStr(int8_t *pbDst, const char *pbSrc, uint32_t cbSize)
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * Set the sense and advanced sense key in the buffer for error conditions.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * @returns SCSI status code.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * @param pRequest Pointer to the request which contains the sense buffer.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * @param uSCSISenseKey The sense key to set.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * @param uSCSIASC The advanced sense key to set.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsyncDECLINLINE(int) drvscsiCmdError(PPDMSCSIREQUEST pRequest, uint8_t uSCSISenseKey, uint8_t uSCSIASC)
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync AssertMsgReturn(pRequest->cbSenseBuffer >= 18, ("Sense buffer is not big enough\n"), SCSI_STATUS_OK);
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync AssertMsgReturn(pRequest->pbSenseBuffer, ("Sense buffer pointer is NULL\n"), SCSI_STATUS_OK);
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync memset(pRequest->pbSenseBuffer, 0, pRequest->cbSenseBuffer);
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync pRequest->pbSenseBuffer[0] = (1 << 7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED; /* Fixed format */
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync pRequest->pbSenseBuffer[13] = 0x00; /** @todo: Provide more info. */
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * Sets the sense key for a status good condition.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * @returns SCSI status code.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * @param pRequest Pointer to the request which contains the sense buffer.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsyncDECLINLINE(int) drvscsiCmdOk(PPDMSCSIREQUEST pRequest)
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync AssertMsgReturn(pRequest->cbSenseBuffer >= 18, ("Sense buffer is not big enough\n"), SCSI_STATUS_OK);
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync AssertMsgReturn(pRequest->pbSenseBuffer, ("Sense buffer pointer is NULL\n"), SCSI_STATUS_OK);
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync memset(pRequest->pbSenseBuffer, 0, pRequest->cbSenseBuffer);
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * Setting this breaks Linux guests on the BusLogic controller.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * According to the SCSI SPC spec sense data is returned after a
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * CHECK CONDITION status or a REQUEST SENSE command.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * Both SCSI controllers have a feature called Auto Sense which
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * fetches the sense data automatically from the device
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * with REQUEST SENSE. So the SCSI subsystem in Linux should
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * find this sense data even if the command finishes successfully
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * but if it finds valid sense data it will let the command fail
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * and it doesn't detect attached disks anymore.
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * Disabling makes it work again and no other guest shows errors
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * so I will leave it disabled for now.
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * On the other hand it is possible that the devices fetch the sense data
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * only after a command failed so the content is really invalid if
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * the command succeeds.
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync pRequest->pbSenseBuffer[0] = (1 << 7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED; /* Fixed format */
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync pRequest->pbSenseBuffer[13] = SCSI_ASC_NONE; /* Should be ASCQ but it has the same value for success. */
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsyncDECLINLINE(void) drvscsiH2BE_U16(uint8_t *pbBuf, uint16_t val)
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsyncDECLINLINE(void) drvscsiH2BE_U24(uint8_t *pbBuf, uint32_t val)
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsyncDECLINLINE(void) drvscsiH2BE_U32(uint8_t *pbBuf, uint32_t val)
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsyncDECLINLINE(uint16_t) drvscsiBE2H_U16(const uint8_t *pbBuf)
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsyncDECLINLINE(uint32_t) drvscsiBE2H_U24(const uint8_t *pbBuf)
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync return (pbBuf[0] << 16) | (pbBuf[1] << 8) | pbBuf[2];
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsyncDECLINLINE(uint32_t) drvscsiBE2H_U32(const uint8_t *pbBuf)
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync return (pbBuf[0] << 24) | (pbBuf[1] << 16) | (pbBuf[2] << 8) | pbBuf[3];
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsyncDECLINLINE(uint64_t) drvscsiBE2H_U64(const uint8_t *pbBuf)
static int drvscsiProcessCDB(PDRVSCSI pThis, PPDMSCSIREQUEST pRequest, uint64_t *puOffset, uint32_t *pcbToTransfer, int *piTxDir)
case SCSI_INQUIRY:
ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
case PDMBLOCKTYPE_HARD_DISK:
case SCSI_REPORT_LUNS:
case SCSI_TEST_UNIT_READY:
case SCSI_READ_CAPACITY:
case SCSI_MODE_SENSE_6:
case SCSI_READ_6:
case SCSI_READ_10:
case SCSI_READ_12:
case SCSI_READ_16:
case SCSI_WRITE_6:
case SCSI_WRITE_10:
case SCSI_WRITE_12:
case SCSI_WRITE_16:
case SCSI_SYNCHRONIZE_CACHE:
case SCSI_READ_BUFFER:
switch (uDataMode)
case SCSI_START_STOP_UNIT:
case SCSI_LOG_SENSE:
switch (uPageCode)
if (uSubPageCode == 0)
aReply[0] = 0;
//AssertMsgFailed(("Command %#x [%s] not implemented\n", pRequest->pbCDB[0], SCSICmdText(pRequest->pbCDB[0])));
rc = drvscsiCmdError(pRequest, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_UNIT_DOES_NOT_RESPOND_TO_SELECTION);
return rc;
int iTxDir;
int rcCompletion;
#ifdef DEBUG
uint32_t cbProcess = (cbToTransfer < pSegActual->cbSeg) ? cbToTransfer : (uint32_t)pSegActual->cbSeg;
pSegActual++;
return rc;
static int drvscsiAsyncIOLoopWakeupFunc(void)
return VWRN_STATE_CHANGED;
return VINF_SUCCESS;
AssertMsg(rc == VWRN_STATE_CHANGED, ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n", rc));
return VINF_SUCCESS;
int rc;
rc = RTReqCall(pThis->pQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsiAsyncIOLoopWakeupFunc, 0);
return rc;
static DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
int rc;
rc = RTReqCallEx(pThis->pQueueRequests, &pReq, 0, RTREQFLAGS_NO_WAIT, (PFNRT)drvscsiProcessRequestOne, 2, pThis, pSCSIRequest);
return VINF_SUCCESS;
switch (enmInterface)
case PDMINTERFACE_BASE:
case PDMINTERFACE_BLOCK_PORT:
return NULL;
int rc;
pThis->pDrvBlock = (PDMIBLOCK *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_BLOCK);
return VERR_PDM_MISSING_INTERFACE;
pThis->pDrvBlockBios = (PDMIBLOCKBIOS *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_BLOCK_BIOS);
return VERR_PDM_MISSING_INTERFACE;
pThis->pDevScsiPort = (PPDMISCSIPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_SCSI_PORT);
AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
pThis->pDrvMount = (PDMIMOUNT *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_MOUNT);
pThis->pLedPort = (PPDMILEDPORTS)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_LED_PORTS);
pThis->pDrvBlockAsync = (PDMIBLOCKASYNC *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_BLOCK_ASYNC);
return VERR_PDM_UNSUPPORTED_BLOCK_TYPE;
PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
return VINF_SUCCESS;
sizeof(DRVSCSI),
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,