3f8dc16a69546ae938abc5b07b62693316cfb0cevboxsync * VBox storage devices - Simple SCSI interface for BIOS access.
3f8dc16a69546ae938abc5b07b62693316cfb0cevboxsync * Copyright (C) 2006-2013 Oracle Corporation
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * available from http://www.virtualbox.org. This file is free software;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * you can redistribute it and/or modify it under the terms of the GNU
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * General Public License (GPL) as published by the Free Software
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync/*******************************************************************************
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync* Header Files *
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync*******************************************************************************/
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync//#define DEBUG
3f8dc16a69546ae938abc5b07b62693316cfb0cevboxsync#define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC /** @todo Create extra group. */
3f8dc16a69546ae938abc5b07b62693316cfb0cevboxsync * Resets the state.
3f8dc16a69546ae938abc5b07b62693316cfb0cevboxsync memset(pVBoxSCSI->abCDB, 0, sizeof(pVBoxSCSI->abCDB));
060711075893fd64c1eaaefe41f318d83011e4e3vboxsync * Initializes the state for the SCSI interface.
060711075893fd64c1eaaefe41f318d83011e4e3vboxsync * @returns VBox status code.
060711075893fd64c1eaaefe41f318d83011e4e3vboxsync * @param pVBoxSCSI Pointer to the unitialized SCSI state.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * Reads a register value.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * @returns VBox status code.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * @param pVBoxSCSI Pointer to the SCSI state.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * @param iRegister Index of the register to read.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * @param pu32Value Where to store the content of the register.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncint vboxscsiReadRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint32_t *pu32Value)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /* There is an I/O operation in progress.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * Yield the execution thread to let the I/O thread make progress.
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync /* If we're not in the 'command ready' state, there may not even be a buffer yet. */
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync if ((pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY) && pVBoxSCSI->cbBuf > 0)
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync Assert(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /** The guest read the last byte from the data in buffer.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * Clear everything and reset command buffer.
3f8dc16a69546ae938abc5b07b62693316cfb0cevboxsync memset(pVBoxSCSI->abCDB, 0, sizeof(pVBoxSCSI->abCDB));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync AssertMsgFailed(("Invalid register to read from %u\n", iRegister));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * Writes to a register.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * @returns VBox status code.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * VERR_MORE_DATA if a command is ready to be sent to the SCSI driver.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * @param pVBoxSCSI Pointer to the SCSI state.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * @param iRegister Index of the register to write to.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * @param uVal Value to write.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncint vboxscsiWriteRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint8_t uVal)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync if (pVBoxSCSI->enmState == VBOXSCSISTATE_NO_COMMAND)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_TXDIR)
060711075893fd64c1eaaefe41f318d83011e4e3vboxsync if (uVal != VBOXSCSI_TXDIR_FROM_DEVICE && uVal != VBOXSCSI_TXDIR_TO_DEVICE)
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync pVBoxSCSI->enmState = VBOXSCSISTATE_READ_CDB_SIZE_BUFHI;
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_CDB_SIZE_BUFHI)
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_LSB;
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync pVBoxSCSI->cbBuf = (uVal & 0xF0) << 12; /* Bits 16-19 of buffer size. */
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_LSB)
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_MID;
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync pVBoxSCSI->cbBuf |= uVal; /* Bits 0-7 of buffer size. */
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_MID)
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync pVBoxSCSI->cbBuf |= (((uint16_t)uVal) << 8); /* Bits 8-15 of buffer size. */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_COMMAND)
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync /* Check if we have all necessary command data. */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync Log(("%s: Command ready for processing\n", __FUNCTION__));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /* This is a write allocate buffer. */
3f8dc16a69546ae938abc5b07b62693316cfb0cevboxsync pVBoxSCSI->pbBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /* This is a read from the device. */
8b90eb0585fa16024709ca374c69f1eb5d5a5a7cvboxsync rc = VERR_MORE_DATA; /** @todo Better return value to indicate ready command? */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync AssertMsgFailed(("Invalid state %d\n", pVBoxSCSI->enmState));
060711075893fd64c1eaaefe41f318d83011e4e3vboxsync if ( pVBoxSCSI->enmState != VBOXSCSISTATE_COMMAND_READY
060711075893fd64c1eaaefe41f318d83011e4e3vboxsync /* Reset the state */
060711075893fd64c1eaaefe41f318d83011e4e3vboxsync /* Reset */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync AssertMsgFailed(("Invalid register to write to %u\n", iRegister));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * Sets up a SCSI request which the owning SCSI device can process.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * @returns VBox status code.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * @param pVBoxSCSI Pointer to the SCSI state.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * @param pScsiRequest Pointer to a scsi request to setup.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * @param puTargetDevice Where to store the target device ID.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncint vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, uint32_t *puTargetDevice)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync LogFlowFunc(("pVBoxSCSI=%#p pScsiRequest=%#p puTargetDevice=%#p\n", pVBoxSCSI, pScsiRequest, puTargetDevice));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync AssertMsg(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY, ("Invalid state %u\n", pVBoxSCSI->enmState));
b7a04f3d42ada4fd6106ef61c28f7ef0a64af878vboxsync /* Clear any errors from a previous request. */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_FROM_DEVICE)
3f8dc16a69546ae938abc5b07b62693316cfb0cevboxsync pVBoxSCSI->pbBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync /* Allocate scatter gather element. */
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync pScsiRequest->paScatterGatherHead = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG) * 1); /* Only one element. */
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync /* Allocate sense buffer. */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync pScsiRequest->pbSenseBuffer = (uint8_t *)RTMemAllocZ(pScsiRequest->cbSenseBuffer);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync pScsiRequest->paScatterGatherHead[0].cbSeg = pVBoxSCSI->cbBuf;
3f8dc16a69546ae938abc5b07b62693316cfb0cevboxsync pScsiRequest->paScatterGatherHead[0].pvSeg = pVBoxSCSI->pbBuf;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * Notifies the device that a request finished and the incoming data
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * is ready at the incoming data port.
b7a04f3d42ada4fd6106ef61c28f7ef0a64af878vboxsyncint vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, int rcCompletion)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync LogFlowFunc(("pVBoxSCSI=%#p pScsiRequest=%#p\n", pVBoxSCSI, pScsiRequest));
3f8dc16a69546ae938abc5b07b62693316cfb0cevboxsync memset(pVBoxSCSI->abCDB, 0, sizeof(pVBoxSCSI->abCDB));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncint vboxscsiReadString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync LogFlowFunc(("pDevIns=%#p pVBoxSCSI=%#p iRegister=%d cTransfer=%u cb=%u\n",
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /* Read string only valid for data in register. */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync AssertMsg(iRegister == 1, ("Hey only register 1 can be read from with string\n"));
3864d226a840ec3ae21abc27459c3cbbc7ef21a3vboxsync /* Accesses without a valid buffer will be ignored. */
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync /* Also ignore attempts to read more data than is available. */
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync cbTransfer = pVBoxSCSI->cbBuf; /* Ignore excess data (not supposed to happen). */
3f8dc16a69546ae938abc5b07b62693316cfb0cevboxsync int rc = PGMPhysSimpleDirtyWriteGCPtr(PDMDevHlpGetVMCPU(pDevIns), GCDst, pVBoxSCSI->pbBuf + pVBoxSCSI->iBuf, cbTransfer);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync *pGCPtrDst = (RTGCPTR)((RTGCUINTPTR)GCDst + cbTransfer);
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync /* Advance current buffer position. */
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync /** The guest read the last byte from the data in buffer.
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync * Clear everything and reset command buffer.
3f8dc16a69546ae938abc5b07b62693316cfb0cevboxsync memset(pVBoxSCSI->abCDB, 0, sizeof(pVBoxSCSI->abCDB));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncint vboxscsiWriteString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
8b90eb0585fa16024709ca374c69f1eb5d5a5a7cvboxsync RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
a898eb6a3ace2594099a60b478ae5cc164bfe3dcvboxsync /* Write string only valid for data in/out register. */
a898eb6a3ace2594099a60b478ae5cc164bfe3dcvboxsync AssertMsg(iRegister == 1, ("Hey only register 1 can be written to with string\n"));
3864d226a840ec3ae21abc27459c3cbbc7ef21a3vboxsync /* Accesses without a valid buffer will be ignored. */
a898eb6a3ace2594099a60b478ae5cc164bfe3dcvboxsync cbTransfer = pVBoxSCSI->cbBuf; /* Ignore excess data (not supposed to happen). */
3f8dc16a69546ae938abc5b07b62693316cfb0cevboxsync int rc = PDMDevHlpPhysReadGCVirt(pDevIns, pVBoxSCSI->pbBuf + pVBoxSCSI->iBuf, GCSrc, cbTransfer);
370a24407d54e42d100a4b9d314a805ed82d0840vboxsync /* Advance current buffer position. */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync *pGCPtrSrc = (RTGCPTR)((RTGCUINTPTR)GCSrc + cbTransfer);
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsyncvoid vboxscsiSetRequestRedo(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest)
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync AssertMsg(pVBoxSCSI->fBusy, ("No request to redo\n"));