summaryrefslogtreecommitdiff
path: root/src/VBox/Devices/Storage/VBoxSCSI.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Storage/VBoxSCSI.cpp')
-rw-r--r--src/VBox/Devices/Storage/VBoxSCSI.cpp177
1 files changed, 107 insertions, 70 deletions
diff --git a/src/VBox/Devices/Storage/VBoxSCSI.cpp b/src/VBox/Devices/Storage/VBoxSCSI.cpp
index 6351a8d3..e077cf58 100644
--- a/src/VBox/Devices/Storage/VBoxSCSI.cpp
+++ b/src/VBox/Devices/Storage/VBoxSCSI.cpp
@@ -1,12 +1,10 @@
/* $Id: VBoxSCSI.cpp $ */
/** @file
- *
- * VBox storage devices:
- * Simple SCSI interface for BIOS access
+ * VBox storage devices - Simple SCSI interface for BIOS access.
*/
/*
- * Copyright (C) 2006-2009 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -21,10 +19,10 @@
* Header Files *
*******************************************************************************/
//#define DEBUG
-#define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC /* @todo: Create extra group. */
+#define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC /** @todo Create extra group. */
#if defined(IN_R0) || defined(IN_RC)
-# error This device has no R0 or GC components
+# error This device has no R0 or RC components
#endif
#include <VBox/vmm/pdmdev.h>
@@ -36,21 +34,26 @@
#include "VBoxSCSI.h"
+
+/**
+ * Resets the state.
+ */
static void vboxscsiReset(PVBOXSCSI pVBoxSCSI)
{
- pVBoxSCSI->regIdentify = 0;
- pVBoxSCSI->cbCDB = 0;
- memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
- pVBoxSCSI->iCDB = 0;
- pVBoxSCSI->fBusy = false;
- pVBoxSCSI->cbBuf = 0;
- pVBoxSCSI->iBuf = 0;
- if (pVBoxSCSI->pBuf)
- RTMemFree(pVBoxSCSI->pBuf);
-
- pVBoxSCSI->pBuf = NULL;
- pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
+ pVBoxSCSI->regIdentify = 0;
+ pVBoxSCSI->cbCDB = 0;
+ memset(pVBoxSCSI->abCDB, 0, sizeof(pVBoxSCSI->abCDB));
+ pVBoxSCSI->iCDB = 0;
+ pVBoxSCSI->fBusy = false;
+ pVBoxSCSI->rcCompletion = 0;
+ pVBoxSCSI->uTargetDevice = 0;
+ pVBoxSCSI->cbBuf = 0;
+ pVBoxSCSI->iBuf = 0;
+ if (pVBoxSCSI->pbBuf)
+ RTMemFree(pVBoxSCSI->pbBuf);
+ pVBoxSCSI->pbBuf = NULL;
+ pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
}
/**
@@ -61,7 +64,7 @@ static void vboxscsiReset(PVBOXSCSI pVBoxSCSI)
*/
int vboxscsiInitialize(PVBOXSCSI pVBoxSCSI)
{
- pVBoxSCSI->pBuf = NULL;
+ pVBoxSCSI->pbBuf = NULL;
vboxscsiReset(pVBoxSCSI);
return VINF_SUCCESS;
@@ -91,16 +94,19 @@ int vboxscsiReadRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint32_t *pu32V
*/
RTThreadYield();
}
- else
- uVal &= ~VBOX_SCSI_BUSY;
+ if (pVBoxSCSI->rcCompletion)
+ uVal |= VBOX_SCSI_ERROR;
break;
}
case 1:
{
- if (pVBoxSCSI->cbBuf > 0)
+ /* If we're not in the 'command ready' state, there may not even be a buffer yet. */
+ if ((pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY) && pVBoxSCSI->cbBuf > 0)
{
- AssertMsg(pVBoxSCSI->pBuf, ("pBuf is NULL\n"));
- uVal = pVBoxSCSI->pBuf[pVBoxSCSI->iBuf];
+ AssertMsg(pVBoxSCSI->pbBuf, ("pBuf is NULL\n"));
+ Assert(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY);
+ Assert(!pVBoxSCSI->fBusy);
+ uVal = pVBoxSCSI->pbBuf[pVBoxSCSI->iBuf];
pVBoxSCSI->iBuf++;
pVBoxSCSI->cbBuf--;
if (pVBoxSCSI->cbBuf == 0)
@@ -108,14 +114,15 @@ int vboxscsiReadRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint32_t *pu32V
/** The guest read the last byte from the data in buffer.
* Clear everything and reset command buffer.
*/
- RTMemFree(pVBoxSCSI->pBuf);
- pVBoxSCSI->pBuf = NULL;
+ RTMemFree(pVBoxSCSI->pbBuf);
+ pVBoxSCSI->pbBuf = NULL;
pVBoxSCSI->cbCDB = 0;
pVBoxSCSI->iCDB = 0;
pVBoxSCSI->iBuf = 0;
+ pVBoxSCSI->rcCompletion = 0;
pVBoxSCSI->uTargetDevice = 0;
pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
- memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
+ memset(pVBoxSCSI->abCDB, 0, sizeof(pVBoxSCSI->abCDB));
}
}
break;
@@ -125,6 +132,11 @@ int vboxscsiReadRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint32_t *pu32V
uVal = pVBoxSCSI->regIdentify;
break;
}
+ case 3:
+ {
+ uVal = pVBoxSCSI->rcCompletion;
+ break;
+ }
default:
AssertMsgFailed(("Invalid register to read from %u\n", iRegister));
}
@@ -162,33 +174,36 @@ int vboxscsiWriteRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint8_t uVal)
vboxscsiReset(pVBoxSCSI);
else
{
- pVBoxSCSI->enmState = VBOXSCSISTATE_READ_CDB_SIZE;
+ pVBoxSCSI->enmState = VBOXSCSISTATE_READ_CDB_SIZE_BUFHI;
pVBoxSCSI->uTxDir = uVal;
}
}
- else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_CDB_SIZE)
+ else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_CDB_SIZE_BUFHI)
{
- if (uVal > VBOXSCSI_CDB_SIZE_MAX)
+ uint8_t cbCDB = uVal & 0x0F;
+
+ if (cbCDB > VBOXSCSI_CDB_SIZE_MAX)
vboxscsiReset(pVBoxSCSI);
else
{
- pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_LOW;
- pVBoxSCSI->cbCDB = uVal;
+ pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_LSB;
+ pVBoxSCSI->cbCDB = cbCDB;
+ pVBoxSCSI->cbBuf = (uVal & 0xF0) << 12; /* Bits 16-19 of buffer size. */
}
}
- else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_LOW)
+ else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_LSB)
{
- pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_HIGH;
- pVBoxSCSI->cbBuf = uVal;
+ pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_MID;
+ pVBoxSCSI->cbBuf |= uVal; /* Bits 0-7 of buffer size. */
}
- else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_HIGH)
+ else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_MID)
{
pVBoxSCSI->enmState = VBOXSCSISTATE_READ_COMMAND;
- pVBoxSCSI->cbBuf |= (((uint16_t)uVal) << 8);
+ pVBoxSCSI->cbBuf |= (((uint16_t)uVal) << 8); /* Bits 8-15 of buffer size. */
}
else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_COMMAND)
{
- pVBoxSCSI->aCDB[pVBoxSCSI->iCDB] = uVal;
+ pVBoxSCSI->abCDB[pVBoxSCSI->iCDB] = uVal;
pVBoxSCSI->iCDB++;
/* Check if we have all necessary command data. */
@@ -199,8 +214,8 @@ int vboxscsiWriteRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint8_t uVal)
if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE)
{
/* This is a write allocate buffer. */
- pVBoxSCSI->pBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
- if (!pVBoxSCSI->pBuf)
+ pVBoxSCSI->pbBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
+ if (!pVBoxSCSI->pbBuf)
return VERR_NO_MEMORY;
}
else
@@ -225,7 +240,7 @@ int vboxscsiWriteRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint8_t uVal)
}
else
{
- pVBoxSCSI->pBuf[pVBoxSCSI->iBuf++] = uVal;
+ pVBoxSCSI->pbBuf[pVBoxSCSI->iBuf++] = uVal;
if (pVBoxSCSI->iBuf == pVBoxSCSI->cbBuf)
{
rc = VERR_MORE_DATA;
@@ -268,13 +283,16 @@ int vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, uint
AssertMsg(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY, ("Invalid state %u\n", pVBoxSCSI->enmState));
+ /* Clear any errors from a previous request. */
+ pVBoxSCSI->rcCompletion = 0;
+
if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_FROM_DEVICE)
{
- if (pVBoxSCSI->pBuf)
- RTMemFree(pVBoxSCSI->pBuf);
+ if (pVBoxSCSI->pbBuf)
+ RTMemFree(pVBoxSCSI->pbBuf);
- pVBoxSCSI->pBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
- if (!pVBoxSCSI->pBuf)
+ pVBoxSCSI->pbBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
+ if (!pVBoxSCSI->pbBuf)
return VERR_NO_MEMORY;
}
@@ -282,8 +300,8 @@ int vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, uint
pScsiRequest->paScatterGatherHead = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG) * 1); /* Only one element. */
if (!pScsiRequest->paScatterGatherHead)
{
- RTMemFree(pVBoxSCSI->pBuf);
- pVBoxSCSI->pBuf = NULL;
+ RTMemFree(pVBoxSCSI->pbBuf);
+ pVBoxSCSI->pbBuf = NULL;
return VERR_NO_MEMORY;
}
@@ -292,13 +310,13 @@ int vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, uint
pScsiRequest->pbSenseBuffer = (uint8_t *)RTMemAllocZ(pScsiRequest->cbSenseBuffer);
pScsiRequest->cbCDB = pVBoxSCSI->cbCDB;
- pScsiRequest->pbCDB = pVBoxSCSI->aCDB;
+ pScsiRequest->pbCDB = pVBoxSCSI->abCDB;
pScsiRequest->uLogicalUnit = 0;
pScsiRequest->cbScatterGather = pVBoxSCSI->cbBuf;
pScsiRequest->cScatterGatherEntries = 1;
pScsiRequest->paScatterGatherHead[0].cbSeg = pVBoxSCSI->cbBuf;
- pScsiRequest->paScatterGatherHead[0].pvSeg = pVBoxSCSI->pBuf;
+ pScsiRequest->paScatterGatherHead[0].pvSeg = pVBoxSCSI->pbBuf;
*puTargetDevice = pVBoxSCSI->uTargetDevice;
@@ -309,7 +327,7 @@ int vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, uint
* Notifies the device that a request finished and the incoming data
* is ready at the incoming data port.
*/
-int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest)
+int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, int rcCompletion)
{
LogFlowFunc(("pVBoxSCSI=%#p pScsiRequest=%#p\n", pVBoxSCSI, pScsiRequest));
RTMemFree(pScsiRequest->paScatterGatherHead);
@@ -317,18 +335,20 @@ int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest)
if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE)
{
- if (pVBoxSCSI->pBuf)
- RTMemFree(pVBoxSCSI->pBuf);
- pVBoxSCSI->pBuf = NULL;
+ if (pVBoxSCSI->pbBuf)
+ RTMemFree(pVBoxSCSI->pbBuf);
+ pVBoxSCSI->pbBuf = NULL;
pVBoxSCSI->cbBuf = 0;
pVBoxSCSI->cbCDB = 0;
pVBoxSCSI->iCDB = 0;
pVBoxSCSI->iBuf = 0;
pVBoxSCSI->uTargetDevice = 0;
pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
- memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
+ memset(pVBoxSCSI->abCDB, 0, sizeof(pVBoxSCSI->abCDB));
}
+ pVBoxSCSI->rcCompletion = rcCompletion;
+
ASMAtomicXchgBool(&pVBoxSCSI->fBusy, false);
return VINF_SUCCESS;
@@ -347,24 +367,38 @@ int vboxscsiReadString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegiste
AssertMsg(iRegister == 1, ("Hey only register 1 can be read from with string\n"));
/* Accesses without a valid buffer will be ignored. */
- if (!pVBoxSCSI->pBuf)
+ if (!pVBoxSCSI->pbBuf)
return VINF_SUCCESS;
- int rc = PGMPhysSimpleDirtyWriteGCPtr(PDMDevHlpGetVMCPU(pDevIns), GCDst, pVBoxSCSI->pBuf, cbTransfer);
+ /* Also ignore attempts to read more data than is available. */
+ Assert(cbTransfer <= pVBoxSCSI->cbBuf);
+ if (cbTransfer > pVBoxSCSI->cbBuf)
+ cbTransfer = pVBoxSCSI->cbBuf; /* Ignore excess data (not supposed to happen). */
+
+ int rc = PGMPhysSimpleDirtyWriteGCPtr(PDMDevHlpGetVMCPU(pDevIns), GCDst, pVBoxSCSI->pbBuf + pVBoxSCSI->iBuf, cbTransfer);
AssertRC(rc);
*pGCPtrDst = (RTGCPTR)((RTGCUINTPTR)GCDst + cbTransfer);
*pcTransfer = 0;
- RTMemFree(pVBoxSCSI->pBuf);
- pVBoxSCSI->pBuf = NULL;
- pVBoxSCSI->cbBuf = 0;
- pVBoxSCSI->cbCDB = 0;
- pVBoxSCSI->iCDB = 0;
- pVBoxSCSI->iBuf = 0;
- pVBoxSCSI->uTargetDevice = 0;
- pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
- memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
+ /* Advance current buffer position. */
+ pVBoxSCSI->iBuf += cbTransfer;
+ pVBoxSCSI->cbBuf -= cbTransfer;
+
+ if (pVBoxSCSI->cbBuf == 0)
+ {
+ /** The guest read the last byte from the data in buffer.
+ * Clear everything and reset command buffer.
+ */
+ RTMemFree(pVBoxSCSI->pbBuf);
+ pVBoxSCSI->pbBuf = NULL;
+ pVBoxSCSI->cbCDB = 0;
+ pVBoxSCSI->iCDB = 0;
+ pVBoxSCSI->iBuf = 0;
+ pVBoxSCSI->uTargetDevice = 0;
+ pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
+ memset(pVBoxSCSI->abCDB, 0, sizeof(pVBoxSCSI->abCDB));
+ }
return rc;
}
@@ -379,16 +413,20 @@ int vboxscsiWriteString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegist
AssertMsg(iRegister == 1, ("Hey only register 1 can be written to with string\n"));
/* Accesses without a valid buffer will be ignored. */
- if (!pVBoxSCSI->pBuf)
+ if (!pVBoxSCSI->pbBuf)
return VINF_SUCCESS;
- Assert(cbTransfer == pVBoxSCSI->cbBuf);
+ Assert(cbTransfer <= pVBoxSCSI->cbBuf);
if (cbTransfer > pVBoxSCSI->cbBuf)
cbTransfer = pVBoxSCSI->cbBuf; /* Ignore excess data (not supposed to happen). */
- int rc = PDMDevHlpPhysReadGCVirt(pDevIns, pVBoxSCSI->pBuf, GCSrc, cbTransfer);
+ int rc = PDMDevHlpPhysReadGCVirt(pDevIns, pVBoxSCSI->pbBuf + pVBoxSCSI->iBuf, GCSrc, cbTransfer);
AssertRC(rc);
+ /* Advance current buffer position. */
+ pVBoxSCSI->iBuf += cbTransfer;
+ pVBoxSCSI->cbBuf -= cbTransfer;
+
*pGCPtrSrc = (RTGCPTR)((RTGCUINTPTR)GCSrc + cbTransfer);
*pcTransfer = 0;
@@ -405,7 +443,6 @@ void vboxscsiSetRequestRedo(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest)
if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_FROM_DEVICE)
{
- AssertPtr(pVBoxSCSI->pBuf);
+ AssertPtr(pVBoxSCSI->pbBuf);
}
}
-