diff options
Diffstat (limited to 'src/VBox/Devices/Storage')
33 files changed, 7712 insertions, 10908 deletions
diff --git a/src/VBox/Devices/Storage/ATAController.cpp b/src/VBox/Devices/Storage/ATAController.cpp deleted file mode 100644 index ca1ee31b..00000000 --- a/src/VBox/Devices/Storage/ATAController.cpp +++ /dev/null @@ -1,5789 +0,0 @@ -/* $Id: ATAController.cpp $ */ -/** @file - * DevATA, DevAHCI - Shared ATA/ATAPI controller code (disk and cdrom). - * - * @todo Actually share this code? - */ - -/* - * Copyright (C) 2006-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. - */ - -/******************************************************************************* -* Header Files * -*******************************************************************************/ -#define LOG_GROUP LOG_GROUP_DEV_IDE -#include <VBox/vmm/pdmdev.h> -#include <iprt/assert.h> -#include <iprt/string.h> -#ifdef IN_RING3 -# include <iprt/uuid.h> -# include <iprt/semaphore.h> -# include <iprt/thread.h> -# include <iprt/time.h> -# include <iprt/alloc.h> -#endif /* IN_RING3 */ -#include <iprt/critsect.h> -#include <iprt/asm.h> -#include <VBox/vmm/stam.h> -#include <VBox/vmm/mm.h> -#include <VBox/vmm/pgm.h> - -#include <VBox/scsi.h> - -#include "ATAController.h" -#include "PIIX3ATABmDma.h" -#include "ide.h" - -/** - * The SSM saved state version. - */ -#define ATA_CTL_SAVED_STATE_VERSION 3 -#define ATA_CTL_SAVED_STATE_VERSION_WITHOUT_FULL_SENSE 1 -#define ATA_CTL_SAVED_STATE_VERSION_WITHOUT_EVENT_STATUS 2 - -#ifndef VBOX_DEVICE_STRUCT_TESTCASE - -DECLINLINE(void) ataSetStatusValue(AHCIATADevState *s, uint8_t stat) -{ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - - /* Freeze status register contents while processing RESET. */ - if (!pCtl->fReset) - { - s->uATARegStatus = stat; - Log2(("%s: LUN#%d status %#04x\n", __FUNCTION__, s->iLUN, s->uATARegStatus)); - } -} - - -DECLINLINE(void) ataSetStatus(AHCIATADevState *s, uint8_t stat) -{ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - - /* Freeze status register contents while processing RESET. */ - if (!pCtl->fReset) - { - s->uATARegStatus |= stat; - Log2(("%s: LUN#%d status %#04x\n", __FUNCTION__, s->iLUN, s->uATARegStatus)); - } -} - - -DECLINLINE(void) ataUnsetStatus(AHCIATADevState *s, uint8_t stat) -{ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - - /* Freeze status register contents while processing RESET. */ - if (!pCtl->fReset) - { - s->uATARegStatus &= ~stat; - Log2(("%s: LUN#%d status %#04x\n", __FUNCTION__, s->iLUN, s->uATARegStatus)); - } -} - -#ifdef IN_RING3 - -typedef void (*PBeginTransferFunc)(AHCIATADevState *); -typedef bool (*PSourceSinkFunc)(AHCIATADevState *); - -static void ataReadWriteSectorsBT(AHCIATADevState *); -static void ataPacketBT(AHCIATADevState *); -static void atapiCmdBT(AHCIATADevState *); -static void atapiPassthroughCmdBT(AHCIATADevState *); - -static bool ataIdentifySS(AHCIATADevState *); -static bool ataFlushSS(AHCIATADevState *); -static bool ataReadSectorsSS(AHCIATADevState *); -static bool ataWriteSectorsSS(AHCIATADevState *); -static bool ataExecuteDeviceDiagnosticSS(AHCIATADevState *); -static bool ataTrimSS(AHCIATADevState *); -static bool ataPacketSS(AHCIATADevState *); -static bool atapiGetConfigurationSS(AHCIATADevState *); -static bool atapiGetEventStatusNotificationSS(AHCIATADevState *); -static bool atapiIdentifySS(AHCIATADevState *); -static bool atapiInquirySS(AHCIATADevState *); -static bool atapiMechanismStatusSS(AHCIATADevState *); -static bool atapiModeSenseErrorRecoverySS(AHCIATADevState *); -static bool atapiModeSenseCDStatusSS(AHCIATADevState *); -static bool atapiReadSS(AHCIATADevState *); -static bool atapiReadCapacitySS(AHCIATADevState *); -static bool atapiReadDiscInformationSS(AHCIATADevState *); -static bool atapiReadTOCNormalSS(AHCIATADevState *); -static bool atapiReadTOCMultiSS(AHCIATADevState *); -static bool atapiReadTOCRawSS(AHCIATADevState *); -static bool atapiReadTrackInformationSS(AHCIATADevState *); -static bool atapiRequestSenseSS(AHCIATADevState *); -static bool atapiPassthroughSS(AHCIATADevState *); - -/** - * Begin of transfer function indexes for g_apfnBeginTransFuncs. - */ -typedef enum ATAFNBT -{ - ATAFN_BT_NULL = 0, - ATAFN_BT_READ_WRITE_SECTORS, - ATAFN_BT_PACKET, - ATAFN_BT_ATAPI_CMD, - ATAFN_BT_ATAPI_PASSTHROUGH_CMD, - ATAFN_BT_MAX -} ATAFNBT; - -/** - * Array of end transfer functions, the index is ATAFNET. - * Make sure ATAFNET and this array match! - */ -static const PBeginTransferFunc g_apfnBeginTransFuncs[ATAFN_BT_MAX] = -{ - NULL, - ataReadWriteSectorsBT, - ataPacketBT, - atapiCmdBT, - atapiPassthroughCmdBT, -}; - -/** - * Source/sink function indexes for g_apfnSourceSinkFuncs. - */ -typedef enum ATAFNSS -{ - ATAFN_SS_NULL = 0, - ATAFN_SS_IDENTIFY, - ATAFN_SS_FLUSH, - ATAFN_SS_READ_SECTORS, - ATAFN_SS_WRITE_SECTORS, - ATAFN_SS_EXECUTE_DEVICE_DIAGNOSTIC, - ATAFN_SS_TRIM, - ATAFN_SS_PACKET, - ATAFN_SS_ATAPI_GET_CONFIGURATION, - ATAFN_SS_ATAPI_GET_EVENT_STATUS_NOTIFICATION, - ATAFN_SS_ATAPI_IDENTIFY, - ATAFN_SS_ATAPI_INQUIRY, - ATAFN_SS_ATAPI_MECHANISM_STATUS, - ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY, - ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS, - ATAFN_SS_ATAPI_READ, - ATAFN_SS_ATAPI_READ_CAPACITY, - ATAFN_SS_ATAPI_READ_DISC_INFORMATION, - ATAFN_SS_ATAPI_READ_TOC_NORMAL, - ATAFN_SS_ATAPI_READ_TOC_MULTI, - ATAFN_SS_ATAPI_READ_TOC_RAW, - ATAFN_SS_ATAPI_READ_TRACK_INFORMATION, - ATAFN_SS_ATAPI_REQUEST_SENSE, - ATAFN_SS_ATAPI_PASSTHROUGH, - ATAFN_SS_MAX -} ATAFNSS; - -/** - * Array of source/sink functions, the index is ATAFNSS. - * Make sure ATAFNSS and this array match! - */ -static const PSourceSinkFunc g_apfnSourceSinkFuncs[ATAFN_SS_MAX] = -{ - NULL, - ataIdentifySS, - ataFlushSS, - ataReadSectorsSS, - ataWriteSectorsSS, - ataExecuteDeviceDiagnosticSS, - ataTrimSS, - ataPacketSS, - atapiGetConfigurationSS, - atapiGetEventStatusNotificationSS, - atapiIdentifySS, - atapiInquirySS, - atapiMechanismStatusSS, - atapiModeSenseErrorRecoverySS, - atapiModeSenseCDStatusSS, - atapiReadSS, - atapiReadCapacitySS, - atapiReadDiscInformationSS, - atapiReadTOCNormalSS, - atapiReadTOCMultiSS, - atapiReadTOCRawSS, - atapiReadTrackInformationSS, - atapiRequestSenseSS, - atapiPassthroughSS -}; - - -static const AHCIATARequest ataDMARequest = { AHCIATA_AIO_DMA, }; -static const AHCIATARequest ataPIORequest = { AHCIATA_AIO_PIO, }; -static const AHCIATARequest ataResetARequest = { AHCIATA_AIO_RESET_ASSERTED, }; -static const AHCIATARequest ataResetCRequest = { AHCIATA_AIO_RESET_CLEARED, }; - - -static void ataAsyncIOClearRequests(PAHCIATACONTROLLER pCtl) -{ - int rc; - - rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT); - AssertRC(rc); - pCtl->AsyncIOReqHead = 0; - pCtl->AsyncIOReqTail = 0; - rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex); - AssertRC(rc); -} - - -static void ataAsyncIOPutRequest(PAHCIATACONTROLLER pCtl, const AHCIATARequest *pReq) -{ - int rc; - - rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT); - AssertRC(rc); - Assert((pCtl->AsyncIOReqHead + 1) % RT_ELEMENTS(pCtl->aAsyncIORequests) != pCtl->AsyncIOReqTail); - memcpy(&pCtl->aAsyncIORequests[pCtl->AsyncIOReqHead], pReq, sizeof(*pReq)); - pCtl->AsyncIOReqHead++; - pCtl->AsyncIOReqHead %= RT_ELEMENTS(pCtl->aAsyncIORequests); - rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex); - AssertRC(rc); - rc = PDMR3CritSectScheduleExitEvent(&pCtl->lock, pCtl->AsyncIOSem); - if (RT_FAILURE(rc)) - { - rc = RTSemEventSignal(pCtl->AsyncIOSem); - AssertRC(rc); - } -} - - -static const AHCIATARequest *ataAsyncIOGetCurrentRequest(PAHCIATACONTROLLER pCtl) -{ - int rc; - const AHCIATARequest *pReq; - - rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT); - AssertRC(rc); - if (pCtl->AsyncIOReqHead != pCtl->AsyncIOReqTail) - pReq = &pCtl->aAsyncIORequests[pCtl->AsyncIOReqTail]; - else - pReq = NULL; - rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex); - AssertRC(rc); - return pReq; -} - - -/** - * Remove the request with the given type, as it's finished. The request - * is not removed blindly, as this could mean a RESET request that is not - * yet processed (but has cleared the request queue) is lost. - * - * @param pCtl Controller for which to remove the request. - * @param ReqType Type of the request to remove. - */ -static void ataAsyncIORemoveCurrentRequest(PAHCIATACONTROLLER pCtl, AHCIATAAIO ReqType) -{ - int rc; - - rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT); - AssertRC(rc); - if (pCtl->AsyncIOReqHead != pCtl->AsyncIOReqTail && pCtl->aAsyncIORequests[pCtl->AsyncIOReqTail].ReqType == ReqType) - { - pCtl->AsyncIOReqTail++; - pCtl->AsyncIOReqTail %= RT_ELEMENTS(pCtl->aAsyncIORequests); - } - rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex); - AssertRC(rc); -} - - -/** - * Dump the request queue for a particular controller. First dump the queue - * contents, then the already processed entries, as long as they haven't been - * overwritten. - * - * @param pCtl Controller for which to dump the queue. - */ -static void ataAsyncIODumpRequests(PAHCIATACONTROLLER pCtl) -{ - int rc; - uint8_t curr; - - rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT); - AssertRC(rc); - LogRel(("AHCI ATA: Ctl: request queue dump (topmost is current):\n")); - curr = pCtl->AsyncIOReqTail; - do - { - if (curr == pCtl->AsyncIOReqHead) - LogRel(("AHCI ATA: Ctl: processed requests (topmost is oldest):\n")); - switch (pCtl->aAsyncIORequests[curr].ReqType) - { - case AHCIATA_AIO_NEW: - LogRel(("new transfer request, iIf=%d iBeginTransfer=%d iSourceSink=%d cbTotalTransfer=%d uTxDir=%d\n", pCtl->aAsyncIORequests[curr].u.t.iIf, pCtl->aAsyncIORequests[curr].u.t.iBeginTransfer, pCtl->aAsyncIORequests[curr].u.t.iSourceSink, pCtl->aAsyncIORequests[curr].u.t.cbTotalTransfer, pCtl->aAsyncIORequests[curr].u.t.uTxDir)); - break; - case AHCIATA_AIO_DMA: - LogRel(("dma transfer finished\n")); - break; - case AHCIATA_AIO_PIO: - LogRel(("pio transfer finished\n")); - break; - case AHCIATA_AIO_RESET_ASSERTED: - LogRel(("reset asserted request\n")); - break; - case AHCIATA_AIO_RESET_CLEARED: - LogRel(("reset cleared request\n")); - break; - case AHCIATA_AIO_ABORT: - LogRel(("abort request, iIf=%d fResetDrive=%d\n", pCtl->aAsyncIORequests[curr].u.a.iIf, pCtl->aAsyncIORequests[curr].u.a.fResetDrive)); - break; - default: - LogRel(("unknown request %d\n", pCtl->aAsyncIORequests[curr].ReqType)); - } - curr = (curr + 1) % RT_ELEMENTS(pCtl->aAsyncIORequests); - } while (curr != pCtl->AsyncIOReqTail); - rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex); - AssertRC(rc); -} - - -/** - * Checks whether the request queue for a particular controller is empty - * or whether a particular controller is idle. - * - * @param pCtl Controller for which to check the queue. - * @param fStrict If set then the controller is checked to be idle. - */ -static bool ataAsyncIOIsIdle(PAHCIATACONTROLLER pCtl, bool fStrict) -{ - int rc; - bool fIdle; - - if (pCtl->AsyncIORequestMutex == NIL_RTSEMMUTEX) - return true; - - rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT); - AssertRC(rc); - fIdle = pCtl->fRedoIdle; - if (!fIdle) - fIdle = (pCtl->AsyncIOReqHead == pCtl->AsyncIOReqTail); - if (fStrict) - fIdle &= (pCtl->uAsyncIOState == AHCIATA_AIO_NEW); - rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex); - AssertRC(rc); - return fIdle; -} - - -/** - * Send a transfer request to the async I/O thread. - * - * @param s Pointer to the ATA device state data. - * @param cbTotalTransfer Data transfer size. - * @param uTxDir Data transfer direction. - * @param iBeginTransfer Index of BeginTransfer callback. - * @param iSourceSink Index of SourceSink callback. - * @param fChainedTransfer Whether this is a transfer that is part of the previous command/transfer. - */ -static void ataStartTransfer(AHCIATADevState *s, uint32_t cbTotalTransfer, uint8_t uTxDir, ATAFNBT iBeginTransfer, ATAFNSS iSourceSink, bool fChainedTransfer) -{ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - AHCIATARequest Req; - - Assert(PDMCritSectIsOwner(&pCtl->lock)); - - /* Do not issue new requests while the RESET line is asserted. */ - if (pCtl->fReset) - { - Log2(("%s: Ctl: suppressed new request as RESET is active\n", __FUNCTION__)); - return; - } - - /* If the controller is already doing something else right now, ignore - * the command that is being submitted. Some broken guests issue commands - * twice (e.g. the Linux kernel that comes with Acronis True Image 8). */ - if (!fChainedTransfer && !ataAsyncIOIsIdle(pCtl, true)) - { - Log(("%s: Ctl: ignored command %#04x, controller state %d\n", __FUNCTION__, s->uATARegCommand, pCtl->uAsyncIOState)); - LogRel(("IDE: guest issued command %#04x while controller busy\n", s->uATARegCommand)); - return; - } - - Req.ReqType = AHCIATA_AIO_NEW; - if (fChainedTransfer) - Req.u.t.iIf = pCtl->iAIOIf; - else - Req.u.t.iIf = pCtl->iSelectedIf; - Req.u.t.cbTotalTransfer = cbTotalTransfer; - Req.u.t.uTxDir = uTxDir; - Req.u.t.iBeginTransfer = iBeginTransfer; - Req.u.t.iSourceSink = iSourceSink; - ataSetStatusValue(s, ATA_STAT_BUSY); - pCtl->fChainedTransfer = fChainedTransfer; - - /* - * Kick the worker thread into action. - */ - Log2(("%s: Ctl: message to async I/O thread, new request\n", __FUNCTION__)); - ataAsyncIOPutRequest(pCtl, &Req); -} - - -/** - * Send an abort command request to the async I/O thread. - * - * @param s Pointer to the ATA device state data. - * @param fResetDrive Whether to reset the drive or just abort a command. - */ -static void ataAbortCurrentCommand(AHCIATADevState *s, bool fResetDrive) -{ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - AHCIATARequest Req; - - Assert(PDMCritSectIsOwner(&pCtl->lock)); - - /* Do not issue new requests while the RESET line is asserted. */ - if (pCtl->fReset) - { - Log2(("%s: Ctl: suppressed aborting command as RESET is active\n", __FUNCTION__)); - return; - } - - Req.ReqType = AHCIATA_AIO_ABORT; - Req.u.a.iIf = pCtl->iSelectedIf; - Req.u.a.fResetDrive = fResetDrive; - ataSetStatus(s, ATA_STAT_BUSY); - Log2(("%s: Ctl: message to async I/O thread, abort command on LUN#%d\n", __FUNCTION__, s->iLUN)); - ataAsyncIOPutRequest(pCtl, &Req); -} - - -static void ataSetIRQ(AHCIATADevState *s) -{ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s); - - if (!(s->uATARegDevCtl & ATA_DEVCTL_DISABLE_IRQ)) - { - Log2(("%s: LUN#%d asserting IRQ\n", __FUNCTION__, s->iLUN)); - /* The BMDMA unit unconditionally sets BM_STATUS_INT if the interrupt - * line is asserted. It monitors the line for a rising edge. */ - if (!s->fIrqPending) - pCtl->BmDma.u8Status |= BM_STATUS_INT; - /* Only actually set the IRQ line if updating the currently selected drive. */ - if (s == &pCtl->aIfs[pCtl->iSelectedIf]) - PDMDevHlpISASetIrq(pDevIns, pCtl->irq, 1); - } - s->fIrqPending = true; -} - -#endif /* IN_RING3 */ - -static void ataUnsetIRQ(AHCIATADevState *s) -{ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s); - - if (!(s->uATARegDevCtl & ATA_DEVCTL_DISABLE_IRQ)) - { - Log2(("%s: LUN#%d deasserting IRQ\n", __FUNCTION__, s->iLUN)); - /* Only actually unset the IRQ line if updating the currently selected drive. */ - if (s == &pCtl->aIfs[pCtl->iSelectedIf]) - PDMDevHlpISASetIrq(pDevIns, pCtl->irq, 0); - } - s->fIrqPending = false; -} - -#ifdef IN_RING3 - -static void ataPIOTransferStart(AHCIATADevState *s, uint32_t start, uint32_t size) -{ - Log2(("%s: LUN#%d start %d size %d\n", __FUNCTION__, s->iLUN, start, size)); - s->iIOBufferPIODataStart = start; - s->iIOBufferPIODataEnd = start + size; - ataSetStatus(s, ATA_STAT_DRQ); -} - - -static void ataPIOTransferStop(AHCIATADevState *s) -{ - Log2(("%s: LUN#%d\n", __FUNCTION__, s->iLUN)); - if (s->fATAPITransfer) - { - s->uATARegNSector = (s->uATARegNSector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; - Log2(("%s: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector)); - ataSetIRQ(s); - s->fATAPITransfer = false; - } - s->cbTotalTransfer = 0; - s->cbElementaryTransfer = 0; - s->iIOBufferPIODataStart = 0; - s->iIOBufferPIODataEnd = 0; - s->iBeginTransfer = ATAFN_BT_NULL; - s->iSourceSink = ATAFN_SS_NULL; -} - - -static void ataPIOTransferLimitATAPI(AHCIATADevState *s) -{ - uint32_t cbLimit, cbTransfer; - - cbLimit = s->uATARegLCyl | (s->uATARegHCyl << 8); - /* Use maximum transfer size if the guest requested 0. Avoids a hang. */ - if (cbLimit == 0) - cbLimit = 0xfffe; - Log2(("%s: byte count limit=%d\n", __FUNCTION__, cbLimit)); - if (cbLimit == 0xffff) - cbLimit--; - cbTransfer = RT_MIN(s->cbTotalTransfer, s->iIOBufferEnd - s->iIOBufferCur); - if (cbTransfer > cbLimit) - { - /* Byte count limit for clipping must be even in this case */ - if (cbLimit & 1) - cbLimit--; - cbTransfer = cbLimit; - } - s->uATARegLCyl = cbTransfer; - s->uATARegHCyl = cbTransfer >> 8; - s->cbElementaryTransfer = cbTransfer; -} - - -static uint32_t ataGetNSectors(AHCIATADevState *s) -{ - /* 0 means either 256 (LBA28) or 65536 (LBA48) sectors. */ - if (s->fLBA48) - { - if (!s->uATARegNSector && !s->uATARegNSectorHOB) - return 65536; - else - return s->uATARegNSectorHOB << 8 | s->uATARegNSector; - } - else - { - if (!s->uATARegNSector) - return 256; - else - return s->uATARegNSector; - } -} - - -static void ataPadString(uint8_t *pbDst, const char *pbSrc, uint32_t cbSize) -{ - for (uint32_t i = 0; i < cbSize; i++) - { - if (*pbSrc) - pbDst[i ^ 1] = *pbSrc++; - else - pbDst[i ^ 1] = ' '; - } -} - - -static void ataSCSIPadStr(uint8_t *pbDst, const char *pbSrc, uint32_t cbSize) -{ - for (uint32_t i = 0; i < cbSize; i++) - { - if (*pbSrc) - pbDst[i] = *pbSrc++; - else - pbDst[i] = ' '; - } -} - - -DECLINLINE(void) ataH2BE_U16(uint8_t *pbBuf, uint16_t val) -{ - pbBuf[0] = val >> 8; - pbBuf[1] = val; -} - - -DECLINLINE(void) ataH2BE_U24(uint8_t *pbBuf, uint32_t val) -{ - pbBuf[0] = val >> 16; - pbBuf[1] = val >> 8; - pbBuf[2] = val; -} - - -DECLINLINE(void) ataH2BE_U32(uint8_t *pbBuf, uint32_t val) -{ - pbBuf[0] = val >> 24; - pbBuf[1] = val >> 16; - pbBuf[2] = val >> 8; - pbBuf[3] = val; -} - - -DECLINLINE(uint16_t) ataBE2H_U16(const uint8_t *pbBuf) -{ - return (pbBuf[0] << 8) | pbBuf[1]; -} - - -DECLINLINE(uint32_t) ataBE2H_U24(const uint8_t *pbBuf) -{ - return (pbBuf[0] << 16) | (pbBuf[1] << 8) | pbBuf[2]; -} - - -DECLINLINE(uint32_t) ataBE2H_U32(const uint8_t *pbBuf) -{ - return (pbBuf[0] << 24) | (pbBuf[1] << 16) | (pbBuf[2] << 8) | pbBuf[3]; -} - - -DECLINLINE(void) ataLBA2MSF(uint8_t *pbBuf, uint32_t iATAPILBA) -{ - iATAPILBA += 150; - pbBuf[0] = (iATAPILBA / 75) / 60; - pbBuf[1] = (iATAPILBA / 75) % 60; - pbBuf[2] = iATAPILBA % 75; -} - - -DECLINLINE(uint32_t) ataMSF2LBA(const uint8_t *pbBuf) -{ - return (pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2]; -} - -static uint32_t ataChecksum(void *ptr, size_t count) -{ - uint8_t u8Sum = 0xa5; - uint8_t *p = (uint8_t*)ptr; - size_t i; - - for (i = 0; i < count; i++) - { - u8Sum += *p++; - } - - return (uint8_t)-(int32_t)u8Sum; -} - -static void ataCmdOK(AHCIATADevState *s, uint8_t status) -{ - s->uATARegError = 0; /* Not needed by ATA spec, but cannot hurt. */ - ataSetStatusValue(s, ATA_STAT_READY | status); -} - - -static void ataCmdError(AHCIATADevState *s, uint8_t uErrorCode) -{ - Log(("%s: code=%#x\n", __FUNCTION__, uErrorCode)); - s->uATARegError = uErrorCode; - ataSetStatusValue(s, ATA_STAT_READY | ATA_STAT_ERR); - s->cbTotalTransfer = 0; - s->cbElementaryTransfer = 0; - s->iIOBufferCur = 0; - s->iIOBufferEnd = 0; - s->uTxDir = PDMBLOCKTXDIR_NONE; - s->iBeginTransfer = ATAFN_BT_NULL; - s->iSourceSink = ATAFN_SS_NULL; -} - - -static bool ataIdentifySS(AHCIATADevState *s) -{ - uint16_t *p; - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - Assert(s->cbElementaryTransfer == 512); - - p = (uint16_t *)s->CTXALLSUFF(pbIOBuffer); - memset(p, 0, 512); - p[0] = RT_H2LE_U16(0x0040); - p[1] = RT_H2LE_U16(RT_MIN(s->PCHSGeometry.cCylinders, 16383)); - p[3] = RT_H2LE_U16(s->PCHSGeometry.cHeads); - /* Block size; obsolete, but required for the BIOS. */ - p[5] = RT_H2LE_U16(512); - p[6] = RT_H2LE_U16(s->PCHSGeometry.cSectors); - ataPadString((uint8_t *)(p + 10), s->pszSerialNumber, ATA_SERIAL_NUMBER_LENGTH); /* serial number */ - p[20] = RT_H2LE_U16(3); /* XXX: retired, cache type */ - p[21] = RT_H2LE_U16(512); /* XXX: retired, cache size in sectors */ - p[22] = RT_H2LE_U16(0); /* ECC bytes per sector */ - ataPadString((uint8_t *)(p + 23), s->pszFirmwareRevision, ATA_FIRMWARE_REVISION_LENGTH); /* firmware version */ - ataPadString((uint8_t *)(p + 27), s->pszModelNumber, ATA_MODEL_NUMBER_LENGTH); /* model */ -#if ATA_MAX_MULT_SECTORS > 1 - p[47] = RT_H2LE_U16(0x8000 | ATA_MAX_MULT_SECTORS); -#endif - p[48] = RT_H2LE_U16(1); /* dword I/O, used by the BIOS */ - p[49] = RT_H2LE_U16(1 << 11 | 1 << 9 | 1 << 8); /* DMA and LBA supported */ - p[50] = RT_H2LE_U16(1 << 14); /* No drive specific standby timer minimum */ - p[51] = RT_H2LE_U16(240); /* PIO transfer cycle */ - p[52] = RT_H2LE_U16(240); /* DMA transfer cycle */ - p[53] = RT_H2LE_U16(1 | 1 << 1 | 1 << 2); /* words 54-58,64-70,88 valid */ - p[54] = RT_H2LE_U16(RT_MIN(s->PCHSGeometry.cCylinders, 16383)); - p[55] = RT_H2LE_U16(s->PCHSGeometry.cHeads); - p[56] = RT_H2LE_U16(s->PCHSGeometry.cSectors); - p[57] = RT_H2LE_U16( RT_MIN(s->PCHSGeometry.cCylinders, 16383) - * s->PCHSGeometry.cHeads - * s->PCHSGeometry.cSectors); - p[58] = RT_H2LE_U16( RT_MIN(s->PCHSGeometry.cCylinders, 16383) - * s->PCHSGeometry.cHeads - * s->PCHSGeometry.cSectors >> 16); - if (s->cMultSectors) - p[59] = RT_H2LE_U16(0x100 | s->cMultSectors); - if (s->cTotalSectors <= (1 << 28) - 1) - { - p[60] = RT_H2LE_U16(s->cTotalSectors); - p[61] = RT_H2LE_U16(s->cTotalSectors >> 16); - } - else - { - /* Report maximum number of sectors possible with LBA28 */ - p[60] = RT_H2LE_U16(((1 << 28) - 1) & 0xffff); - p[61] = RT_H2LE_U16(((1 << 28) - 1) >> 16); - } - p[63] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_MDMA, ATA_MDMA_MODE_MAX, s->uATATransferMode)); /* MDMA modes supported / mode enabled */ - p[64] = RT_H2LE_U16(ATA_PIO_MODE_MAX > 2 ? (1 << (ATA_PIO_MODE_MAX - 2)) - 1 : 0); /* PIO modes beyond PIO2 supported */ - p[65] = RT_H2LE_U16(120); /* minimum DMA multiword tx cycle time */ - p[66] = RT_H2LE_U16(120); /* recommended DMA multiword tx cycle time */ - p[67] = RT_H2LE_U16(120); /* minimum PIO cycle time without flow control */ - p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */ - if (s->pDrvBlock->pfnDiscard) - { - p[80] = RT_H2LE_U16(0x1f0); /* support everything up to ATA/ATAPI-8 ACS */ - p[81] = RT_H2LE_U16(0x28); /* conforms to ATA/ATAPI-8 ACS */ - } - else - { - p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */ - p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */ - } - p[82] = RT_H2LE_U16(1 << 3 | 1 << 5 | 1 << 6); /* supports power management, write cache and look-ahead */ - if (s->cTotalSectors <= (1 << 28) - 1) - p[83] = RT_H2LE_U16(1 << 14 | 1 << 12); /* supports FLUSH CACHE */ - else - p[83] = RT_H2LE_U16(1 << 14 | 1 << 10 | 1 << 12 | 1 << 13); /* supports LBA48, FLUSH CACHE and FLUSH CACHE EXT */ - p[84] = RT_H2LE_U16(1 << 14); - p[85] = RT_H2LE_U16(1 << 3 | 1 << 5 | 1 << 6); /* enabled power management, write cache and look-ahead */ - if (s->cTotalSectors <= (1 << 28) - 1) - p[86] = RT_H2LE_U16(1 << 12); /* enabled FLUSH CACHE */ - else - p[86] = RT_H2LE_U16(1 << 10 | 1 << 12 | 1 << 13); /* enabled LBA48, FLUSH CACHE and FLUSH CACHE EXT */ - p[87] = RT_H2LE_U16(1 << 14); - p[88] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_UDMA, ATA_UDMA_MODE_MAX, s->uATATransferMode)); /* UDMA modes supported / mode enabled */ - p[93] = RT_H2LE_U16((1 | 1 << 1) << ((s->iLUN & 1) == 0 ? 0 : 8) | 1 << 13 | 1 << 14); - if (s->cTotalSectors > (1 << 28) - 1) - { - p[100] = RT_H2LE_U16(s->cTotalSectors); - p[101] = RT_H2LE_U16(s->cTotalSectors >> 16); - p[102] = RT_H2LE_U16(s->cTotalSectors >> 32); - p[103] = RT_H2LE_U16(s->cTotalSectors >> 48); - } - if (s->pDrvBlock->pfnDiscard) /** @todo: Set bit 14 in word 69 too? (Deterministic read after TRIM). */ - p[169] = RT_H2LE_U16(1); /* DATA SET MANAGEMENT command supported. */ - if (s->fNonRotational) - p[217] = RT_H2LE_U16(1); /* Non-rotational medium */ - uint32_t uCsum = ataChecksum(p, 510); - p[255] = RT_H2LE_U16(0xa5 | (uCsum << 8)); /* Integrity word */ - - s->iSourceSink = ATAFN_SS_NULL; - ataCmdOK(s, ATA_STAT_SEEK); - return false; -} - - -static bool ataFlushSS(AHCIATADevState *s) -{ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - int rc; - - Assert(s->uTxDir == PDMBLOCKTXDIR_NONE); - Assert(!s->cbElementaryTransfer); - - PDMCritSectLeave(&pCtl->lock); - - STAM_PROFILE_START(&s->StatFlushes, f); - rc = s->pDrvBlock->pfnFlush(s->pDrvBlock); - AssertRC(rc); - STAM_PROFILE_STOP(&s->StatFlushes, f); - - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - ataCmdOK(s, 0); - return false; -} - - -static bool atapiIdentifySS(AHCIATADevState *s) -{ - uint16_t *p; - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - Assert(s->cbElementaryTransfer == 512); - - p = (uint16_t *)s->CTXALLSUFF(pbIOBuffer); - memset(p, 0, 512); - /* Removable CDROM, 50us response, 12 byte packets */ - p[0] = RT_H2LE_U16(2 << 14 | 5 << 8 | 1 << 7 | 2 << 5 | 0 << 0); - ataPadString((uint8_t *)(p + 10), s->pszSerialNumber, ATA_SERIAL_NUMBER_LENGTH); /* serial number */ - p[20] = RT_H2LE_U16(3); /* XXX: retired, cache type */ - p[21] = RT_H2LE_U16(512); /* XXX: retired, cache size in sectors */ - ataPadString((uint8_t *)(p + 23), s->pszFirmwareRevision, ATA_FIRMWARE_REVISION_LENGTH); /* firmware version */ - ataPadString((uint8_t *)(p + 27), s->pszModelNumber, ATA_MODEL_NUMBER_LENGTH); /* model */ - p[49] = RT_H2LE_U16(1 << 11 | 1 << 9 | 1 << 8); /* DMA and LBA supported */ - p[50] = RT_H2LE_U16(1 << 14); /* No drive specific standby timer minimum */ - p[51] = RT_H2LE_U16(240); /* PIO transfer cycle */ - p[52] = RT_H2LE_U16(240); /* DMA transfer cycle */ - p[53] = RT_H2LE_U16(1 << 1 | 1 << 2); /* words 64-70,88 are valid */ - p[63] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_MDMA, ATA_MDMA_MODE_MAX, s->uATATransferMode)); /* MDMA modes supported / mode enabled */ - p[64] = RT_H2LE_U16(ATA_PIO_MODE_MAX > 2 ? (1 << (ATA_PIO_MODE_MAX - 2)) - 1 : 0); /* PIO modes beyond PIO2 supported */ - p[65] = RT_H2LE_U16(120); /* minimum DMA multiword tx cycle time */ - p[66] = RT_H2LE_U16(120); /* recommended DMA multiword tx cycle time */ - p[67] = RT_H2LE_U16(120); /* minimum PIO cycle time without flow control */ - p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */ - p[73] = RT_H2LE_U16(0x003e); /* ATAPI CDROM major */ - p[74] = RT_H2LE_U16(9); /* ATAPI CDROM minor */ - p[75] = RT_H2LE_U16(1); /* queue depth 1 */ - p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */ - p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */ - p[82] = RT_H2LE_U16(1 << 4 | 1 << 9); /* supports packet command set and DEVICE RESET */ - p[83] = RT_H2LE_U16(1 << 14); - p[84] = RT_H2LE_U16(1 << 14); - p[85] = RT_H2LE_U16(1 << 4 | 1 << 9); /* enabled packet command set and DEVICE RESET */ - p[86] = RT_H2LE_U16(0); - p[87] = RT_H2LE_U16(1 << 14); - p[88] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_UDMA, ATA_UDMA_MODE_MAX, s->uATATransferMode)); /* UDMA modes supported / mode enabled */ - p[93] = RT_H2LE_U16((1 | 1 << 1) << ((s->iLUN & 1) == 0 ? 0 : 8) | 1 << 13 | 1 << 14); - uint32_t uCsum = ataChecksum(p, 510); - p[255] = RT_H2LE_U16(0xa5 | (uCsum << 8)); /* Integrity word */ - s->iSourceSink = ATAFN_SS_NULL; - ataCmdOK(s, ATA_STAT_SEEK); - return false; -} - - -static void ataSetSignature(AHCIATADevState *s) -{ - s->uATARegSelect &= 0xf0; /* clear head */ - /* put signature */ - s->uATARegNSector = 1; - s->uATARegSector = 1; - if (s->fATAPI) - { - s->uATARegLCyl = 0x14; - s->uATARegHCyl = 0xeb; - } - else if (s->pDrvBlock) - { - s->uATARegLCyl = 0; - s->uATARegHCyl = 0; - } - else - { - s->uATARegLCyl = 0xff; - s->uATARegHCyl = 0xff; - } -} - - -static uint64_t ataGetSector(AHCIATADevState *s) -{ - uint64_t iLBA; - if (s->uATARegSelect & 0x40) - { - /* any LBA variant */ - if (s->fLBA48) - { - /* LBA48 */ - iLBA = ((uint64_t)s->uATARegHCylHOB << 40) | - ((uint64_t)s->uATARegLCylHOB << 32) | - ((uint64_t)s->uATARegSectorHOB << 24) | - ((uint64_t)s->uATARegHCyl << 16) | - ((uint64_t)s->uATARegLCyl << 8) | - s->uATARegSector; - } - else - { - /* LBA */ - iLBA = ((s->uATARegSelect & 0x0f) << 24) | (s->uATARegHCyl << 16) | - (s->uATARegLCyl << 8) | s->uATARegSector; - } - } - else - { - /* CHS */ - iLBA = ((s->uATARegHCyl << 8) | s->uATARegLCyl) * s->PCHSGeometry.cHeads * s->PCHSGeometry.cSectors + - (s->uATARegSelect & 0x0f) * s->PCHSGeometry.cSectors + - (s->uATARegSector - 1); - } - return iLBA; -} - -static void ataSetSector(AHCIATADevState *s, uint64_t iLBA) -{ - uint32_t cyl, r; - if (s->uATARegSelect & 0x40) - { - /* any LBA variant */ - if (s->fLBA48) - { - /* LBA48 */ - s->uATARegHCylHOB = iLBA >> 40; - s->uATARegLCylHOB = iLBA >> 32; - s->uATARegSectorHOB = iLBA >> 24; - s->uATARegHCyl = iLBA >> 16; - s->uATARegLCyl = iLBA >> 8; - s->uATARegSector = iLBA; - } - else - { - /* LBA */ - s->uATARegSelect = (s->uATARegSelect & 0xf0) | (iLBA >> 24); - s->uATARegHCyl = (iLBA >> 16); - s->uATARegLCyl = (iLBA >> 8); - s->uATARegSector = (iLBA); - } - } - else - { - /* CHS */ - cyl = iLBA / (s->PCHSGeometry.cHeads * s->PCHSGeometry.cSectors); - r = iLBA % (s->PCHSGeometry.cHeads * s->PCHSGeometry.cSectors); - s->uATARegHCyl = cyl >> 8; - s->uATARegLCyl = cyl; - s->uATARegSelect = (s->uATARegSelect & 0xf0) | ((r / s->PCHSGeometry.cSectors) & 0x0f); - s->uATARegSector = (r % s->PCHSGeometry.cSectors) + 1; - } -} - - -static void ataWarningDiskFull(PPDMDEVINS pDevIns) -{ - int rc; - LogRel(("AHCI ATA: Host disk full\n")); - rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevATA_DISKFULL", - N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space")); - AssertRC(rc); -} - -static void ataWarningFileTooBig(PPDMDEVINS pDevIns) -{ - int rc; - LogRel(("AHCI ATA: File too big\n")); - rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevATA_FILETOOBIG", - N_("Host system reported that the file size limit of the host file system has been exceeded. VM execution is suspended. You need to move your virtual hard disk to a filesystem which allows bigger files")); - AssertRC(rc); -} - -static void ataWarningISCSI(PPDMDEVINS pDevIns) -{ - int rc; - LogRel(("AHCI ATA: iSCSI target unavailable\n")); - rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevATA_ISCSIDOWN", - N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again")); - AssertRC(rc); -} - -static bool ataIsRedoSetWarning(AHCIATADevState *s, int rc) -{ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - Assert(!PDMCritSectIsOwner(&pCtl->lock)); - if (rc == VERR_DISK_FULL) - { - pCtl->fRedoIdle = true; - ataWarningDiskFull(ATADEVSTATE_2_DEVINS(s)); - return true; - } - if (rc == VERR_FILE_TOO_BIG) - { - pCtl->fRedoIdle = true; - ataWarningFileTooBig(ATADEVSTATE_2_DEVINS(s)); - return true; - } - if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED) - { - pCtl->fRedoIdle = true; - /* iSCSI connection abort (first error) or failure to reestablish - * connection (second error). Pause VM. On resume we'll retry. */ - ataWarningISCSI(ATADEVSTATE_2_DEVINS(s)); - return true; - } - return false; -} - - -static int ataReadSectors(AHCIATADevState *s, uint64_t u64Sector, void *pvBuf, - uint32_t cSectors, bool *pfRedo) -{ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - int rc; - - PDMCritSectLeave(&pCtl->lock); - - STAM_PROFILE_ADV_START(&s->StatReads, r); - s->pLed->Asserted.s.fReading = s->pLed->Actual.s.fReading = 1; - rc = s->pDrvBlock->pfnRead(s->pDrvBlock, u64Sector * 512, pvBuf, cSectors * 512); - s->pLed->Actual.s.fReading = 0; - STAM_PROFILE_ADV_STOP(&s->StatReads, r); - - STAM_REL_COUNTER_ADD(s->pStatBytesRead, cSectors * 512); - - if (RT_SUCCESS(rc)) - *pfRedo = false; - else - *pfRedo = ataIsRedoSetWarning(s, rc); - - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - return rc; -} - - -static int ataWriteSectors(AHCIATADevState *s, uint64_t u64Sector, - const void *pvBuf, uint32_t cSectors, bool *pfRedo) -{ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - int rc; - - PDMCritSectLeave(&pCtl->lock); - - STAM_PROFILE_ADV_START(&s->StatWrites, w); - s->pLed->Asserted.s.fWriting = s->pLed->Actual.s.fWriting = 1; - rc = s->pDrvBlock->pfnWrite(s->pDrvBlock, u64Sector * 512, pvBuf, cSectors * 512); - s->pLed->Actual.s.fWriting = 0; - STAM_PROFILE_ADV_STOP(&s->StatWrites, w); - - STAM_REL_COUNTER_ADD(s->pStatBytesWritten, cSectors * 512); - - if (RT_SUCCESS(rc)) - *pfRedo = false; - else - *pfRedo = ataIsRedoSetWarning(s, rc); - - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - return rc; -} - - -static void ataReadWriteSectorsBT(AHCIATADevState *s) -{ - uint32_t cSectors; - - cSectors = s->cbTotalTransfer / 512; - if (cSectors > s->cSectorsPerIRQ) - s->cbElementaryTransfer = s->cSectorsPerIRQ * 512; - else - s->cbElementaryTransfer = cSectors * 512; - if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE) - ataCmdOK(s, 0); -} - - -static bool ataReadSectorsSS(AHCIATADevState *s) -{ - int rc; - uint32_t cSectors; - uint64_t iLBA; - bool fRedo; - - cSectors = s->cbElementaryTransfer / 512; - Assert(cSectors); - iLBA = ataGetSector(s); - Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, iLBA)); - rc = ataReadSectors(s, iLBA, s->CTXALLSUFF(pbIOBuffer), cSectors, &fRedo); - if (RT_SUCCESS(rc)) - { - ataSetSector(s, iLBA + cSectors); - if (s->cbElementaryTransfer == s->cbTotalTransfer) - s->iSourceSink = ATAFN_SS_NULL; - ataCmdOK(s, ATA_STAT_SEEK); - } - else - { - if (fRedo) - return true; - if (s->cErrors++ < MAX_LOG_REL_ERRORS) - LogRel(("AHCI ATA: LUN#%d: disk read error (rc=%Rrc iSector=%#RX64 cSectors=%#RX32)\n", - s->iLUN, rc, iLBA, cSectors)); - ataCmdError(s, ID_ERR); - } - return false; -} - - -static bool ataWriteSectorsSS(AHCIATADevState *s) -{ - int rc; - uint32_t cSectors; - uint64_t iLBA; - bool fRedo; - - cSectors = s->cbElementaryTransfer / 512; - Assert(cSectors); - iLBA = ataGetSector(s); - Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, iLBA)); - rc = ataWriteSectors(s, iLBA, s->CTXALLSUFF(pbIOBuffer), cSectors, &fRedo); - if (RT_SUCCESS(rc)) - { - ataSetSector(s, iLBA + cSectors); - if (!s->cbTotalTransfer) - s->iSourceSink = ATAFN_SS_NULL; - ataCmdOK(s, ATA_STAT_SEEK); - } - else - { - if (fRedo) - return true; - if (s->cErrors++ < MAX_LOG_REL_ERRORS) - LogRel(("AHCI ATA: LUN#%d: disk write error (rc=%Rrc iSector=%#RX64 cSectors=%#RX32)\n", - s->iLUN, rc, iLBA, cSectors)); - ataCmdError(s, ID_ERR); - } - return false; -} - - -static void atapiCmdOK(AHCIATADevState *s) -{ - s->uATARegError = 0; - ataSetStatusValue(s, ATA_STAT_READY); - s->uATARegNSector = (s->uATARegNSector & ~7) - | ((s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE) ? ATAPI_INT_REASON_IO : 0) - | (!s->cbTotalTransfer ? ATAPI_INT_REASON_CD : 0); - Log2(("%s: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector)); - memset(s->abATAPISense, '\0', sizeof(s->abATAPISense)); - s->abATAPISense[0] = 0x70 | (1 << 7); - s->abATAPISense[7] = 10; -} - - -static void atapiCmdError(AHCIATADevState *s, const uint8_t *pabATAPISense, size_t cbATAPISense) -{ - Log(("%s: sense=%#x (%s) asc=%#x ascq=%#x (%s)\n", __FUNCTION__, pabATAPISense[2] & 0x0f, SCSISenseText(pabATAPISense[2] & 0x0f), - pabATAPISense[12], pabATAPISense[13], SCSISenseExtText(pabATAPISense[12], pabATAPISense[13]))); - s->uATARegError = pabATAPISense[2] << 4; - ataSetStatusValue(s, ATA_STAT_READY | ATA_STAT_ERR); - s->uATARegNSector = (s->uATARegNSector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; - Log2(("%s: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector)); - memset(s->abATAPISense, '\0', sizeof(s->abATAPISense)); - memcpy(s->abATAPISense, pabATAPISense, RT_MIN(cbATAPISense, sizeof(s->abATAPISense))); - s->cbTotalTransfer = 0; - s->cbElementaryTransfer = 0; - s->iIOBufferCur = 0; - s->iIOBufferEnd = 0; - s->uTxDir = PDMBLOCKTXDIR_NONE; - s->iBeginTransfer = ATAFN_BT_NULL; - s->iSourceSink = ATAFN_SS_NULL; -} - - -/** @todo deprecated function - doesn't provide enough info. Replace by direct - * calls to atapiCmdError() with full data. */ -static void atapiCmdErrorSimple(AHCIATADevState *s, uint8_t uATAPISenseKey, uint8_t uATAPIASC) -{ - uint8_t abATAPISense[ATAPI_SENSE_SIZE]; - memset(abATAPISense, '\0', sizeof(abATAPISense)); - abATAPISense[0] = 0x70 | (1 << 7); - abATAPISense[2] = uATAPISenseKey & 0x0f; - abATAPISense[7] = 10; - abATAPISense[12] = uATAPIASC; - atapiCmdError(s, abATAPISense, sizeof(abATAPISense)); -} - - -static void atapiCmdBT(AHCIATADevState *s) -{ - s->fATAPITransfer = true; - s->cbElementaryTransfer = s->cbTotalTransfer; - if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE) - atapiCmdOK(s); -} - - -static void atapiPassthroughCmdBT(AHCIATADevState *s) -{ - /* @todo implement an algorithm for correctly determining the read and - * write sector size without sending additional commands to the drive. - * This should be doable by saving processing the configuration requests - * and replies. */ -#if 0 - if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE) - { - uint8_t cmd = s->aATAPICmd[0]; - if (cmd == SCSI_WRITE_10 || cmd == SCSI_WRITE_12 || cmd == SCSI_WRITE_AND_VERIFY_10) - { - uint8_t aModeSenseCmd[10]; - uint8_t aModeSenseResult[16]; - uint8_t uDummySense; - uint32_t cbTransfer; - int rc; - - cbTransfer = sizeof(aModeSenseResult); - aModeSenseCmd[0] = SCSI_MODE_SENSE_10; - aModeSenseCmd[1] = 0x08; /* disable block descriptor = 1 */ - aModeSenseCmd[2] = (SCSI_PAGECONTROL_CURRENT << 6) | SCSI_MODEPAGE_WRITE_PARAMETER; - aModeSenseCmd[3] = 0; /* subpage code */ - aModeSenseCmd[4] = 0; /* reserved */ - aModeSenseCmd[5] = 0; /* reserved */ - aModeSenseCmd[6] = 0; /* reserved */ - aModeSenseCmd[7] = cbTransfer >> 8; - aModeSenseCmd[8] = cbTransfer & 0xff; - aModeSenseCmd[9] = 0; /* control */ - rc = s->pDrvBlock->pfnSendCmd(s->pDrvBlock, aModeSenseCmd, PDMBLOCKTXDIR_FROM_DEVICE, aModeSenseResult, &cbTransfer, &uDummySense, 500); - if (RT_FAILURE(rc)) - { - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_NONE); - return; - } - /* Select sector size based on the current data block type. */ - switch (aModeSenseResult[12] & 0x0f) - { - case 0: - s->cbATAPISector = 2352; - break; - case 1: - s->cbATAPISector = 2368; - break; - case 2: - case 3: - s->cbATAPISector = 2448; - break; - case 8: - case 10: - s->cbATAPISector = 2048; - break; - case 9: - s->cbATAPISector = 2336; - break; - case 11: - s->cbATAPISector = 2056; - break; - case 12: - s->cbATAPISector = 2324; - break; - case 13: - s->cbATAPISector = 2332; - break; - default: - s->cbATAPISector = 0; - } - Log2(("%s: sector size %d\n", __FUNCTION__, s->cbATAPISector)); - s->cbTotalTransfer *= s->cbATAPISector; - if (s->cbTotalTransfer == 0) - s->uTxDir = PDMBLOCKTXDIR_NONE; - } - } -#endif - atapiCmdBT(s); -} - - -static bool atapiReadSS(AHCIATADevState *s) -{ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - int rc = VINF_SUCCESS; - uint32_t cbTransfer, cSectors; - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - cbTransfer = RT_MIN(s->cbTotalTransfer, s->cbIOBuffer); - cSectors = cbTransfer / s->cbATAPISector; - Assert(cSectors * s->cbATAPISector <= cbTransfer); - Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, s->iATAPILBA)); - - PDMCritSectLeave(&pCtl->lock); - - STAM_PROFILE_ADV_START(&s->StatReads, r); - s->pLed->Asserted.s.fReading = s->pLed->Actual.s.fReading = 1; - switch (s->cbATAPISector) - { - case 2048: - rc = s->pDrvBlock->pfnRead(s->pDrvBlock, (uint64_t)s->iATAPILBA * s->cbATAPISector, s->CTXALLSUFF(pbIOBuffer), s->cbATAPISector * cSectors); - break; - case 2352: - { - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer); - - for (uint32_t i = s->iATAPILBA; i < s->iATAPILBA + cSectors; i++) - { - /* sync bytes */ - *pbBuf++ = 0x00; - memset(pbBuf, 0xff, 11); - pbBuf += 11; - /* MSF */ - ataLBA2MSF(pbBuf, i); - pbBuf += 3; - *pbBuf++ = 0x01; /* mode 1 data */ - /* data */ - rc = s->pDrvBlock->pfnRead(s->pDrvBlock, (uint64_t)i * 2048, pbBuf, 2048); - if (RT_FAILURE(rc)) - break; - pbBuf += 2048; - /* ECC */ - memset(pbBuf, 0, 288); - pbBuf += 288; - } - } - break; - default: - break; - } - STAM_PROFILE_ADV_STOP(&s->StatReads, r); - - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - - if (RT_SUCCESS(rc)) - { - s->pLed->Actual.s.fReading = 0; - STAM_REL_COUNTER_ADD(s->pStatBytesRead, s->cbATAPISector * cSectors); - - /* The initial buffer end value has been set up based on the total - * transfer size. But the I/O buffer size limits what can actually be - * done in one transfer, so set the actual value of the buffer end. */ - s->cbElementaryTransfer = cbTransfer; - if (cbTransfer >= s->cbTotalTransfer) - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - s->iATAPILBA += cSectors; - } - else - { - if (s->cErrors++ < MAX_LOG_REL_ERRORS) - LogRel(("AHCI ATA: LUN#%d: CD-ROM read error, %d sectors at LBA %d\n", s->iLUN, cSectors, s->iATAPILBA)); - atapiCmdErrorSimple(s, SCSI_SENSE_MEDIUM_ERROR, SCSI_ASC_READ_ERROR); - } - return false; -} - - -static bool atapiPassthroughSS(AHCIATADevState *s) -{ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - int rc = VINF_SUCCESS; - uint8_t abATAPISense[ATAPI_SENSE_SIZE]; - uint32_t cbTransfer; - PSTAMPROFILEADV pProf = NULL; - - cbTransfer = s->cbElementaryTransfer; - - if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE) - Log3(("ATAPI PT data write (%d): %.*Rhxs\n", cbTransfer, cbTransfer, s->CTXALLSUFF(pbIOBuffer))); - - /* Simple heuristics: if there is at least one sector of data - * to transfer, it's worth updating the LEDs. */ - if (cbTransfer >= 2048) - { - if (s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE) - { - s->pLed->Asserted.s.fReading = s->pLed->Actual.s.fReading = 1; - pProf = &s->StatReads; - } - else - { - s->pLed->Asserted.s.fWriting = s->pLed->Actual.s.fWriting = 1; - pProf = &s->StatWrites; - } - } - - PDMCritSectLeave(&pCtl->lock); - - memset(abATAPISense, '\0', sizeof(abATAPISense)); - if (pProf) { STAM_PROFILE_ADV_START(pProf, b); } - if (cbTransfer > 100 * _1K) - { - /* Linux accepts commands with up to 100KB of data, but expects - * us to handle commands with up to 128KB of data. The usual - * imbalance of powers. */ - uint8_t aATAPICmd[ATAPI_PACKET_SIZE]; - uint32_t iATAPILBA, cSectors, cReqSectors, cbCurrTX; - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer); - - switch (s->aATAPICmd[0]) - { - case SCSI_READ_10: - case SCSI_WRITE_10: - case SCSI_WRITE_AND_VERIFY_10: - iATAPILBA = ataBE2H_U32(s->aATAPICmd + 2); - cSectors = ataBE2H_U16(s->aATAPICmd + 7); - break; - case SCSI_READ_12: - case SCSI_WRITE_12: - iATAPILBA = ataBE2H_U32(s->aATAPICmd + 2); - cSectors = ataBE2H_U32(s->aATAPICmd + 6); - break; - case SCSI_READ_CD: - iATAPILBA = ataBE2H_U32(s->aATAPICmd + 2); - cSectors = ataBE2H_U24(s->aATAPICmd + 6) / s->cbATAPISector; - break; - case SCSI_READ_CD_MSF: - iATAPILBA = ataMSF2LBA(s->aATAPICmd + 3); - cSectors = ataMSF2LBA(s->aATAPICmd + 6) - iATAPILBA; - break; - default: - AssertMsgFailed(("Don't know how to split command %#04x\n", s->aATAPICmd[0])); - if (s->cErrors++ < MAX_LOG_REL_ERRORS) - LogRel(("AHCI ATA: LUN#%d: CD-ROM passthrough split error\n", s->iLUN)); - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE); - { - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - } - return false; - } - memcpy(aATAPICmd, s->aATAPICmd, ATAPI_PACKET_SIZE); - cReqSectors = 0; - for (uint32_t i = cSectors; i > 0; i -= cReqSectors) - { - if (i * s->cbATAPISector > 100 * _1K) - cReqSectors = (100 * _1K) / s->cbATAPISector; - else - cReqSectors = i; - cbCurrTX = s->cbATAPISector * cReqSectors; - switch (s->aATAPICmd[0]) - { - case SCSI_READ_10: - case SCSI_WRITE_10: - case SCSI_WRITE_AND_VERIFY_10: - ataH2BE_U32(aATAPICmd + 2, iATAPILBA); - ataH2BE_U16(aATAPICmd + 7, cReqSectors); - break; - case SCSI_READ_12: - case SCSI_WRITE_12: - ataH2BE_U32(aATAPICmd + 2, iATAPILBA); - ataH2BE_U32(aATAPICmd + 6, cReqSectors); - break; - case SCSI_READ_CD: - ataH2BE_U32(s->aATAPICmd + 2, iATAPILBA); - ataH2BE_U24(s->aATAPICmd + 6, cbCurrTX); - break; - case SCSI_READ_CD_MSF: - ataLBA2MSF(aATAPICmd + 3, iATAPILBA); - ataLBA2MSF(aATAPICmd + 6, iATAPILBA + cReqSectors); - break; - } - rc = s->pDrvBlock->pfnSendCmd(s->pDrvBlock, aATAPICmd, (PDMBLOCKTXDIR)s->uTxDir, pbBuf, &cbCurrTX, abATAPISense, sizeof(abATAPISense), 30000 /**< @todo timeout */); - if (rc != VINF_SUCCESS) - break; - iATAPILBA += cReqSectors; - pbBuf += s->cbATAPISector * cReqSectors; - } - } - else - rc = s->pDrvBlock->pfnSendCmd(s->pDrvBlock, s->aATAPICmd, (PDMBLOCKTXDIR)s->uTxDir, s->CTXALLSUFF(pbIOBuffer), &cbTransfer, abATAPISense, sizeof(abATAPISense), 30000 /**< @todo timeout */); - if (pProf) { STAM_PROFILE_ADV_STOP(pProf, b); } - - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - - /* Update the LEDs and the read/write statistics. */ - if (cbTransfer >= 2048) - { - if (s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE) - { - s->pLed->Actual.s.fReading = 0; - STAM_REL_COUNTER_ADD(s->pStatBytesRead, cbTransfer); - } - else - { - s->pLed->Actual.s.fWriting = 0; - STAM_REL_COUNTER_ADD(s->pStatBytesWritten, cbTransfer); - } - } - - if (RT_SUCCESS(rc)) - { - if (s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE) - { - Assert(cbTransfer <= s->cbTotalTransfer); - /* Reply with the same amount of data as the real drive. */ - s->cbTotalTransfer = cbTransfer; - /* The initial buffer end value has been set up based on the total - * transfer size. But the I/O buffer size limits what can actually be - * done in one transfer, so set the actual value of the buffer end. */ - s->cbElementaryTransfer = cbTransfer; - if (s->aATAPICmd[0] == SCSI_INQUIRY) - { - /* Make sure that the real drive cannot be identified. - * Motivation: changing the VM configuration should be as - * invisible as possible to the guest. */ - Log3(("ATAPI PT inquiry data before (%d): %.*Rhxs\n", cbTransfer, cbTransfer, s->CTXALLSUFF(pbIOBuffer))); - ataSCSIPadStr(s->CTXALLSUFF(pbIOBuffer) + 8, "VBOX", 8); - ataSCSIPadStr(s->CTXALLSUFF(pbIOBuffer) + 16, "CD-ROM", 16); - ataSCSIPadStr(s->CTXALLSUFF(pbIOBuffer) + 32, "1.0", 4); - } - if (cbTransfer) - Log3(("ATAPI PT data read (%d): %.*Rhxs\n", cbTransfer, cbTransfer, s->CTXALLSUFF(pbIOBuffer))); - } - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - } - else - { - if (s->cErrors++ < MAX_LOG_REL_ERRORS) - { - uint8_t u8Cmd = s->aATAPICmd[0]; - do - { - /* don't log superfluous errors */ - if ( rc == VERR_DEV_IO_ERROR - && ( u8Cmd == SCSI_TEST_UNIT_READY - || u8Cmd == SCSI_READ_CAPACITY - || u8Cmd == SCSI_READ_TOC_PMA_ATIP)) - break; - LogRel(("AHCI ATA: LUN#%d: CD-ROM passthrough command (%#04x) error %d %Rrc\n", s->iLUN, u8Cmd, abATAPISense[0] & 0x0f, rc)); - } while (0); - } - atapiCmdError(s, abATAPISense, sizeof(abATAPISense)); - } - return false; -} - - -static bool atapiReadSectors(AHCIATADevState *s, uint32_t iATAPILBA, uint32_t cSectors, uint32_t cbSector) -{ - Assert(cSectors > 0); - s->iATAPILBA = iATAPILBA; - s->cbATAPISector = cbSector; - ataStartTransfer(s, cSectors * cbSector, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ, true); - return false; -} - - -static bool atapiReadCapacitySS(AHCIATADevState *s) -{ - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer); - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - Assert(s->cbElementaryTransfer <= 8); - ataH2BE_U32(pbBuf, s->cTotalSectors - 1); - ataH2BE_U32(pbBuf + 4, 2048); - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - return false; -} - - -static bool atapiReadDiscInformationSS(AHCIATADevState *s) -{ - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer); - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - Assert(s->cbElementaryTransfer <= 34); - memset(pbBuf, '\0', 34); - ataH2BE_U16(pbBuf, 32); - pbBuf[2] = (0 << 4) | (3 << 2) | (2 << 0); /* not erasable, complete session, complete disc */ - pbBuf[3] = 1; /* number of first track */ - pbBuf[4] = 1; /* number of sessions (LSB) */ - pbBuf[5] = 1; /* first track number in last session (LSB) */ - pbBuf[6] = 1; /* last track number in last session (LSB) */ - pbBuf[7] = (0 << 7) | (0 << 6) | (1 << 5) | (0 << 2) | (0 << 0); /* disc id not valid, disc bar code not valid, unrestricted use, not dirty, not RW medium */ - pbBuf[8] = 0; /* disc type = CD-ROM */ - pbBuf[9] = 0; /* number of sessions (MSB) */ - pbBuf[10] = 0; /* number of sessions (MSB) */ - pbBuf[11] = 0; /* number of sessions (MSB) */ - ataH2BE_U32(pbBuf + 16, 0x00ffffff); /* last session lead-in start time is not available */ - ataH2BE_U32(pbBuf + 20, 0x00ffffff); /* last possible start time for lead-out is not available */ - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - return false; -} - - -static bool atapiReadTrackInformationSS(AHCIATADevState *s) -{ - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer); - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - Assert(s->cbElementaryTransfer <= 36); - /* Accept address/number type of 1 only, and only track 1 exists. */ - if ((s->aATAPICmd[1] & 0x03) != 1 || ataBE2H_U32(&s->aATAPICmd[2]) != 1) - { - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); - return false; - } - memset(pbBuf, '\0', 36); - ataH2BE_U16(pbBuf, 34); - pbBuf[2] = 1; /* track number (LSB) */ - pbBuf[3] = 1; /* session number (LSB) */ - pbBuf[5] = (0 << 5) | (0 << 4) | (4 << 0); /* not damaged, primary copy, data track */ - pbBuf[6] = (0 << 7) | (0 << 6) | (0 << 5) | (0 << 6) | (1 << 0); /* not reserved track, not blank, not packet writing, not fixed packet, data mode 1 */ - pbBuf[7] = (0 << 1) | (0 << 0); /* last recorded address not valid, next recordable address not valid */ - ataH2BE_U32(pbBuf + 8, 0); /* track start address is 0 */ - ataH2BE_U32(pbBuf + 24, s->cTotalSectors); /* track size */ - pbBuf[32] = 0; /* track number (MSB) */ - pbBuf[33] = 0; /* session number (MSB) */ - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - return false; -} - -static size_t atapiGetConfigurationFillFeatureListProfiles(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf) -{ - if (cbBuf < 3*4) - return 0; - - ataH2BE_U16(pbBuf, 0x0); /* feature 0: list of profiles supported */ - pbBuf[2] = (0 << 2) | (1 << 1) | (1 || 0); /* version 0, persistent, current */ - pbBuf[3] = 8; /* additional bytes for profiles */ - /* The MMC-3 spec says that DVD-ROM read capability should be reported - * before CD-ROM read capability. */ - ataH2BE_U16(pbBuf + 4, 0x10); /* profile: read-only DVD */ - pbBuf[6] = (0 << 0); /* NOT current profile */ - ataH2BE_U16(pbBuf + 8, 0x08); /* profile: read only CD */ - pbBuf[10] = (1 << 0); /* current profile */ - - return 3*4; /* Header + 2 profiles entries */ -} - -static size_t atapiGetConfigurationFillFeatureCore(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf) -{ - if (cbBuf < 12) - return 0; - - ataH2BE_U16(pbBuf, 0x1); /* feature 0001h: Core Feature */ - pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */ - pbBuf[3] = 8; /* Additional length */ - ataH2BE_U16(pbBuf + 4, 0x00000002); /* Physical interface ATAPI. */ - pbBuf[8] = RT_BIT(0); /* DBE */ - /* Rest is reserved. */ - - return 12; -} - -static size_t atapiGetConfigurationFillFeatureMorphing(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf) -{ - if (cbBuf < 8) - return 0; - - ataH2BE_U16(pbBuf, 0x2); /* feature 0002h: Morphing Feature */ - pbBuf[2] = (0x1 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */ - pbBuf[3] = 4; /* Additional length */ - pbBuf[4] = RT_BIT(1) | 0x0; /* OCEvent | !ASYNC */ - /* Rest is reserved. */ - - return 8; -} - -static size_t atapiGetConfigurationFillFeatureRemovableMedium(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf) -{ - if (cbBuf < 8) - return 0; - - ataH2BE_U16(pbBuf, 0x3); /* feature 0003h: Removable Medium Feature */ - pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */ - pbBuf[3] = 4; /* Additional length */ - /* Tray type loading | Load | Eject | !Pvnt Jmpr | !DBML | Lock */ - pbBuf[4] = (0x2 << 5) | RT_BIT(4) | RT_BIT(3) | (0x0 << 2) | (0x0 << 1) | RT_BIT(0); - /* Rest is reserved. */ - - return 8; -} - -static size_t atapiGetConfigurationFillFeatureRandomReadable(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf) -{ - if (cbBuf < 12) - return 0; - - ataH2BE_U16(pbBuf, 0x10); /* feature 0010h: Random Readable Feature */ - pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */ - pbBuf[3] = 8; /* Additional length */ - ataH2BE_U32(pbBuf + 4, 2048); /* Logical block size. */ - ataH2BE_U16(pbBuf + 8, 0x10); /* Blocking (0x10 for DVD, CD is not defined). */ - pbBuf[10] = 0; /* PP not present */ - /* Rest is reserved. */ - - return 12; -} - -static size_t atapiGetConfigurationFillFeatureCDRead(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf) -{ - if (cbBuf < 8) - return 0; - - ataH2BE_U16(pbBuf, 0x1e); /* feature 001Eh: CD Read Feature */ - pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */ - pbBuf[3] = 0; /* Additional length */ - pbBuf[4] = (0x0 << 7) | (0x0 << 1) | 0x0; /* !DAP | !C2-Flags | !CD-Text. */ - /* Rest is reserved. */ - - return 8; -} - -static size_t atapiGetConfigurationFillFeaturePowerManagement(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf) -{ - if (cbBuf < 4) - return 0; - - ataH2BE_U16(pbBuf, 0x100); /* feature 0100h: Power Management Feature */ - pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */ - pbBuf[3] = 0; /* Additional length */ - - return 4; -} - -static size_t atapiGetConfigurationFillFeatureTimeout(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf) -{ - if (cbBuf < 8) - return 0; - - ataH2BE_U16(pbBuf, 0x105); /* feature 0105h: Timeout Feature */ - pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */ - pbBuf[3] = 4; /* Additional length */ - pbBuf[4] = 0x0; /* !Group3 */ - - return 8; -} - -static bool atapiGetConfigurationSS(AHCIATADevState *s) -{ - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer); - size_t cbBuf = s->cbIOBuffer; - size_t cbCopied = 0; - uint16_t u16Sfn = ataBE2H_U16(&s->aATAPICmd[2]); - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - Assert(s->cbElementaryTransfer <= 80); - /* Accept valid request types only, and only starting feature 0. */ - if ((s->aATAPICmd[1] & 0x03) == 3 || u16Sfn != 0) - { - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); - return false; - } - memset(pbBuf, '\0', cbBuf); - /** @todo implement switching between CD-ROM and DVD-ROM profile (the only - * way to differentiate them right now is based on the image size). */ - if (s->cTotalSectors) - ataH2BE_U16(pbBuf + 6, 0x08); /* current profile: read-only CD */ - else - ataH2BE_U16(pbBuf + 6, 0x00); /* current profile: none -> no media */ - cbBuf -= 8; - pbBuf += 8; - - cbCopied = atapiGetConfigurationFillFeatureListProfiles(s, pbBuf, cbBuf); - cbBuf -= cbCopied; - pbBuf += cbCopied; - - cbCopied = atapiGetConfigurationFillFeatureCore(s, pbBuf, cbBuf); - cbBuf -= cbCopied; - pbBuf += cbCopied; - - cbCopied = atapiGetConfigurationFillFeatureMorphing(s, pbBuf, cbBuf); - cbBuf -= cbCopied; - pbBuf += cbCopied; - - cbCopied = atapiGetConfigurationFillFeatureRemovableMedium(s, pbBuf, cbBuf); - cbBuf -= cbCopied; - pbBuf += cbCopied; - - cbCopied = atapiGetConfigurationFillFeatureRandomReadable(s, pbBuf, cbBuf); - cbBuf -= cbCopied; - pbBuf += cbCopied; - - cbCopied = atapiGetConfigurationFillFeatureCDRead(s, pbBuf, cbBuf); - cbBuf -= cbCopied; - pbBuf += cbCopied; - - cbCopied = atapiGetConfigurationFillFeaturePowerManagement(s, pbBuf, cbBuf); - cbBuf -= cbCopied; - pbBuf += cbCopied; - - cbCopied = atapiGetConfigurationFillFeatureTimeout(s, pbBuf, cbBuf); - cbBuf -= cbCopied; - pbBuf += cbCopied; - - /* Set data length now. */ - ataH2BE_U32(s->CTX_SUFF(pbIOBuffer), s->cbIOBuffer - cbBuf); - - /* Other profiles we might want to add in the future: 0x40 (BD-ROM) and 0x50 (HDDVD-ROM) */ - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - return false; -} - - -static bool atapiGetEventStatusNotificationSS(AHCIATADevState *s) -{ - uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer); - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - Assert(s->cbElementaryTransfer <= 8); - - if (!(s->aATAPICmd[1] & 1)) - { - /* no asynchronous operation supported */ - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); - return false; - } - - uint32_t OldStatus, NewStatus; - do - { - OldStatus = ASMAtomicReadU32(&s->MediaEventStatus); - NewStatus = ATA_EVENT_STATUS_UNCHANGED; - switch (OldStatus) - { - case ATA_EVENT_STATUS_MEDIA_NEW: - /* mount */ - ataH2BE_U16(pbBuf + 0, 6); - pbBuf[2] = 0x04; /* media */ - pbBuf[3] = 0x5e; /* supported = busy|media|external|power|operational */ - pbBuf[4] = 0x02; /* new medium */ - pbBuf[5] = 0x02; /* medium present / door closed */ - pbBuf[6] = 0x00; - pbBuf[7] = 0x00; - break; - - case ATA_EVENT_STATUS_MEDIA_CHANGED: - case ATA_EVENT_STATUS_MEDIA_REMOVED: - /* umount */ - ataH2BE_U16(pbBuf + 0, 6); - pbBuf[2] = 0x04; /* media */ - pbBuf[3] = 0x5e; /* supported = busy|media|external|power|operational */ - pbBuf[4] = 0x03; /* media removal */ - pbBuf[5] = 0x00; /* medium absent / door closed */ - pbBuf[6] = 0x00; - pbBuf[7] = 0x00; - if (OldStatus == ATA_EVENT_STATUS_MEDIA_CHANGED) - NewStatus = ATA_EVENT_STATUS_MEDIA_NEW; - break; - - case ATA_EVENT_STATUS_MEDIA_EJECT_REQUESTED: /* currently unused */ - ataH2BE_U16(pbBuf + 0, 6); - pbBuf[2] = 0x04; /* media */ - pbBuf[3] = 0x5e; /* supported = busy|media|external|power|operational */ - pbBuf[4] = 0x01; /* eject requested (eject button pressed) */ - pbBuf[5] = 0x02; /* medium present / door closed */ - pbBuf[6] = 0x00; - pbBuf[7] = 0x00; - break; - - case ATA_EVENT_STATUS_UNCHANGED: - default: - ataH2BE_U16(pbBuf + 0, 6); - pbBuf[2] = 0x01; /* operational change request / notification */ - pbBuf[3] = 0x5e; /* supported = busy|media|external|power|operational */ - pbBuf[4] = 0x00; - pbBuf[5] = 0x00; - pbBuf[6] = 0x00; - pbBuf[7] = 0x00; - break; - } - } while (!ASMAtomicCmpXchgU32(&s->MediaEventStatus, NewStatus, OldStatus)); - - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - return false; -} - - -static bool atapiInquirySS(AHCIATADevState *s) -{ - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer); - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - Assert(s->cbElementaryTransfer <= 36); - pbBuf[0] = 0x05; /* CD-ROM */ - pbBuf[1] = 0x80; /* removable */ -#if 1/*ndef VBOX*/ /** @todo implement MESN + AENC. (async notification on removal and stuff.) */ - pbBuf[2] = 0x00; /* ISO */ - pbBuf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */ -#else - pbBuf[2] = 0x00; /* ISO */ - pbBuf[3] = 0x91; /* format 1, MESN=1, AENC=9 ??? */ -#endif - pbBuf[4] = 31; /* additional length */ - pbBuf[5] = 0; /* reserved */ - pbBuf[6] = 0; /* reserved */ - pbBuf[7] = 0; /* reserved */ - ataSCSIPadStr(pbBuf + 8, s->pszInquiryVendorId, 8); - ataSCSIPadStr(pbBuf + 16, s->pszInquiryProductId, 16); - ataSCSIPadStr(pbBuf + 32, s->pszInquiryRevision, 4); - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - return false; -} - - -static bool atapiModeSenseErrorRecoverySS(AHCIATADevState *s) -{ - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer); - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - Assert(s->cbElementaryTransfer <= 16); - ataH2BE_U16(&pbBuf[0], 16 + 6); - pbBuf[2] = 0x70; - pbBuf[3] = 0; - pbBuf[4] = 0; - pbBuf[5] = 0; - pbBuf[6] = 0; - pbBuf[7] = 0; - - pbBuf[8] = 0x01; - pbBuf[9] = 0x06; - pbBuf[10] = 0x00; - pbBuf[11] = 0x05; - pbBuf[12] = 0x00; - pbBuf[13] = 0x00; - pbBuf[14] = 0x00; - pbBuf[15] = 0x00; - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - return false; -} - - -static bool atapiModeSenseCDStatusSS(AHCIATADevState *s) -{ - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer); - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - Assert(s->cbElementaryTransfer <= 40); - ataH2BE_U16(&pbBuf[0], 38); - pbBuf[2] = 0x70; - pbBuf[3] = 0; - pbBuf[4] = 0; - pbBuf[5] = 0; - pbBuf[6] = 0; - pbBuf[7] = 0; - - pbBuf[8] = 0x2a; - pbBuf[9] = 30; /* page length */ - pbBuf[10] = 0x08; /* DVD-ROM read support */ - pbBuf[11] = 0x00; /* no write support */ - /* The following claims we support audio play. This is obviously false, - * but the Linux generic CDROM support makes many features depend on this - * capability. If it's not set, this causes many things to be disabled. */ - pbBuf[12] = 0x71; /* multisession support, mode 2 form 1/2 support, audio play */ - pbBuf[13] = 0x00; /* no subchannel reads supported */ - pbBuf[14] = (1 << 0) | (1 << 3) | (1 << 5); /* lock supported, eject supported, tray type loading mechanism */ - if (s->pDrvMount->pfnIsLocked(s->pDrvMount)) - pbBuf[14] |= 1 << 1; /* report lock state */ - pbBuf[15] = 0; /* no subchannel reads supported, no separate audio volume control, no changer etc. */ - ataH2BE_U16(&pbBuf[16], 5632); /* (obsolete) claim 32x speed support */ - ataH2BE_U16(&pbBuf[18], 2); /* number of audio volume levels */ - ataH2BE_U16(&pbBuf[20], s->cbIOBuffer / _1K); /* buffer size supported in Kbyte */ - ataH2BE_U16(&pbBuf[22], 5632); /* (obsolete) current read speed 32x */ - pbBuf[24] = 0; /* reserved */ - pbBuf[25] = 0; /* reserved for digital audio (see idx 15) */ - ataH2BE_U16(&pbBuf[26], 0); /* (obsolete) maximum write speed */ - ataH2BE_U16(&pbBuf[28], 0); /* (obsolete) current write speed */ - ataH2BE_U16(&pbBuf[30], 0); /* copy management revision supported 0=no CSS */ - pbBuf[32] = 0; /* reserved */ - pbBuf[33] = 0; /* reserved */ - pbBuf[34] = 0; /* reserved */ - pbBuf[35] = 1; /* rotation control CAV */ - ataH2BE_U16(&pbBuf[36], 0); /* current write speed */ - ataH2BE_U16(&pbBuf[38], 0); /* number of write speed performance descriptors */ - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - return false; -} - - -static bool atapiRequestSenseSS(AHCIATADevState *s) -{ - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer); - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - memset(pbBuf, '\0', s->cbElementaryTransfer); - memcpy(pbBuf, s->abATAPISense, RT_MIN(s->cbElementaryTransfer, sizeof(s->abATAPISense))); - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - return false; -} - - -static bool atapiMechanismStatusSS(AHCIATADevState *s) -{ - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer); - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - Assert(s->cbElementaryTransfer <= 8); - ataH2BE_U16(pbBuf, 0); - /* no current LBA */ - pbBuf[2] = 0; - pbBuf[3] = 0; - pbBuf[4] = 0; - pbBuf[5] = 1; - ataH2BE_U16(pbBuf + 6, 0); - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - return false; -} - - -static bool atapiReadTOCNormalSS(AHCIATADevState *s) -{ - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer), *q, iStartTrack; - bool fMSF; - uint32_t cbSize; - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - fMSF = (s->aATAPICmd[1] >> 1) & 1; - iStartTrack = s->aATAPICmd[6]; - if (iStartTrack > 1 && iStartTrack != 0xaa) - { - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); - return false; - } - q = pbBuf + 2; - *q++ = 1; /* first session */ - *q++ = 1; /* last session */ - if (iStartTrack <= 1) - { - *q++ = 0; /* reserved */ - *q++ = 0x14; /* ADR, control */ - *q++ = 1; /* track number */ - *q++ = 0; /* reserved */ - if (fMSF) - { - *q++ = 0; /* reserved */ - ataLBA2MSF(q, 0); - q += 3; - } - else - { - /* sector 0 */ - ataH2BE_U32(q, 0); - q += 4; - } - } - /* lead out track */ - *q++ = 0; /* reserved */ - *q++ = 0x14; /* ADR, control */ - *q++ = 0xaa; /* track number */ - *q++ = 0; /* reserved */ - if (fMSF) - { - *q++ = 0; /* reserved */ - ataLBA2MSF(q, s->cTotalSectors); - q += 3; - } - else - { - ataH2BE_U32(q, s->cTotalSectors); - q += 4; - } - cbSize = q - pbBuf; - ataH2BE_U16(pbBuf, cbSize - 2); - if (cbSize < s->cbTotalTransfer) - s->cbTotalTransfer = cbSize; - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - return false; -} - - -static bool atapiReadTOCMultiSS(AHCIATADevState *s) -{ - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer); - bool fMSF; - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - Assert(s->cbElementaryTransfer <= 12); - fMSF = (s->aATAPICmd[1] >> 1) & 1; - /* multi session: only a single session defined */ -/** @todo double-check this stuff against what a real drive says for a CD-ROM (not a CD-R) with only a single data session. Maybe solve the problem with "cdrdao read-toc" not being able to figure out whether numbers are in BCD or hex. */ - memset(pbBuf, 0, 12); - pbBuf[1] = 0x0a; - pbBuf[2] = 0x01; - pbBuf[3] = 0x01; - pbBuf[5] = 0x14; /* ADR, control */ - pbBuf[6] = 1; /* first track in last complete session */ - if (fMSF) - { - pbBuf[8] = 0; /* reserved */ - ataLBA2MSF(&pbBuf[9], 0); - } - else - { - /* sector 0 */ - ataH2BE_U32(pbBuf + 8, 0); - } - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - return false; -} - - -static bool atapiReadTOCRawSS(AHCIATADevState *s) -{ - uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer), *q, iStartTrack; - bool fMSF; - uint32_t cbSize; - - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - fMSF = (s->aATAPICmd[1] >> 1) & 1; - iStartTrack = s->aATAPICmd[6]; - - q = pbBuf + 2; - *q++ = 1; /* first session */ - *q++ = 1; /* last session */ - - *q++ = 1; /* session number */ - *q++ = 0x14; /* data track */ - *q++ = 0; /* track number */ - *q++ = 0xa0; /* first track in program area */ - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - *q++ = 0; - *q++ = 1; /* first track */ - *q++ = 0x00; /* disk type CD-DA or CD data */ - *q++ = 0; - - *q++ = 1; /* session number */ - *q++ = 0x14; /* data track */ - *q++ = 0; /* track number */ - *q++ = 0xa1; /* last track in program area */ - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - *q++ = 0; - *q++ = 1; /* last track */ - *q++ = 0; - *q++ = 0; - - *q++ = 1; /* session number */ - *q++ = 0x14; /* data track */ - *q++ = 0; /* track number */ - *q++ = 0xa2; /* lead-out */ - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - if (fMSF) - { - *q++ = 0; /* reserved */ - ataLBA2MSF(q, s->cTotalSectors); - q += 3; - } - else - { - ataH2BE_U32(q, s->cTotalSectors); - q += 4; - } - - *q++ = 1; /* session number */ - *q++ = 0x14; /* ADR, control */ - *q++ = 0; /* track number */ - *q++ = 1; /* point */ - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - if (fMSF) - { - *q++ = 0; /* reserved */ - ataLBA2MSF(q, 0); - q += 3; - } - else - { - /* sector 0 */ - ataH2BE_U32(q, 0); - q += 4; - } - - cbSize = q - pbBuf; - ataH2BE_U16(pbBuf, cbSize - 2); - if (cbSize < s->cbTotalTransfer) - s->cbTotalTransfer = cbSize; - s->iSourceSink = ATAFN_SS_NULL; - atapiCmdOK(s); - return false; -} - - -static void atapiParseCmdVirtualATAPI(AHCIATADevState *s) -{ - const uint8_t *pbPacket; - uint8_t *pbBuf; - uint32_t cbMax; - - pbPacket = s->aATAPICmd; - pbBuf = s->CTXALLSUFF(pbIOBuffer); - switch (pbPacket[0]) - { - case SCSI_TEST_UNIT_READY: - if (s->cNotifiedMediaChange > 0) - { - if (s->cNotifiedMediaChange-- > 2) - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - else - atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - } - else if (s->pDrvMount->pfnIsMounted(s->pDrvMount)) - atapiCmdOK(s); - else - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; - case SCSI_GET_EVENT_STATUS_NOTIFICATION: - cbMax = ataBE2H_U16(pbPacket + 7); - ataStartTransfer(s, RT_MIN(cbMax, 8), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_GET_EVENT_STATUS_NOTIFICATION, true); - break; - case SCSI_MODE_SENSE_10: - { - uint8_t uPageControl, uPageCode; - cbMax = ataBE2H_U16(pbPacket + 7); - uPageControl = pbPacket[2] >> 6; - uPageCode = pbPacket[2] & 0x3f; - switch (uPageControl) - { - case SCSI_PAGECONTROL_CURRENT: - switch (uPageCode) - { - case SCSI_MODEPAGE_ERROR_RECOVERY: - ataStartTransfer(s, RT_MIN(cbMax, 16), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY, true); - break; - case SCSI_MODEPAGE_CD_STATUS: - ataStartTransfer(s, RT_MIN(cbMax, 40), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS, true); - break; - default: - goto error_cmd; - } - break; - case SCSI_PAGECONTROL_CHANGEABLE: - goto error_cmd; - case SCSI_PAGECONTROL_DEFAULT: - goto error_cmd; - default: - case SCSI_PAGECONTROL_SAVED: - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED); - break; - } - } - break; - case SCSI_REQUEST_SENSE: - cbMax = pbPacket[4]; - ataStartTransfer(s, RT_MIN(cbMax, 18), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_REQUEST_SENSE, true); - break; - case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL: - if (s->pDrvMount->pfnIsMounted(s->pDrvMount)) - { - if (pbPacket[4] & 1) - s->pDrvMount->pfnLock(s->pDrvMount); - else - s->pDrvMount->pfnUnlock(s->pDrvMount); - atapiCmdOK(s); - } - else - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; - case SCSI_READ_10: - case SCSI_READ_12: - { - uint32_t cSectors, iATAPILBA; - - if (s->cNotifiedMediaChange > 0) - { - s->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - break; - } - else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) - { - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; - } - if (pbPacket[0] == SCSI_READ_10) - cSectors = ataBE2H_U16(pbPacket + 7); - else - cSectors = ataBE2H_U32(pbPacket + 6); - iATAPILBA = ataBE2H_U32(pbPacket + 2); - if (cSectors == 0) - { - atapiCmdOK(s); - break; - } - if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors) - { - /* Rate limited logging, one log line per second. For - * guests that insist on reading from places outside the - * valid area this often generates too many release log - * entries otherwise. */ - static uint64_t uLastLogTS = 0; - if (RTTimeMilliTS() >= uLastLogTS + 1000) - { - LogRel(("AHCI ATA: LUN#%d: CD-ROM block number %Ld invalid (READ)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors)); - uLastLogTS = RTTimeMilliTS(); - } - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); - break; - } - atapiReadSectors(s, iATAPILBA, cSectors, 2048); - } - break; - case SCSI_READ_CD: - { - uint32_t cSectors, iATAPILBA; - - if (s->cNotifiedMediaChange > 0) - { - s->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - break; - } - else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) - { - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; - } - cSectors = (pbPacket[6] << 16) | (pbPacket[7] << 8) | pbPacket[8]; - iATAPILBA = ataBE2H_U32(pbPacket + 2); - if (cSectors == 0) - { - atapiCmdOK(s); - break; - } - if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors) - { - /* Rate limited logging, one log line per second. For - * guests that insist on reading from places outside the - * valid area this often generates too many release log - * entries otherwise. */ - static uint64_t uLastLogTS = 0; - if (RTTimeMilliTS() >= uLastLogTS + 1000) - { - LogRel(("AHCI ATA: LUN#%d: CD-ROM block number %Ld invalid (READ CD)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors)); - uLastLogTS = RTTimeMilliTS(); - } - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); - break; - } - switch (pbPacket[9] & 0xf8) - { - case 0x00: - /* nothing */ - atapiCmdOK(s); - break; - case 0x10: - /* normal read */ - atapiReadSectors(s, iATAPILBA, cSectors, 2048); - break; - case 0xf8: - /* read all data */ - atapiReadSectors(s, iATAPILBA, cSectors, 2352); - break; - default: - LogRel(("AHCI ATA: LUN#%d: CD-ROM sector format not supported\n", s->iLUN)); - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); - break; - } - } - break; - case SCSI_SEEK_10: - { - uint32_t iATAPILBA; - if (s->cNotifiedMediaChange > 0) - { - s->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - break; - } - else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) - { - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; - } - iATAPILBA = ataBE2H_U32(pbPacket + 2); - if (iATAPILBA > s->cTotalSectors) - { - /* Rate limited logging, one log line per second. For - * guests that insist on seeking to places outside the - * valid area this often generates too many release log - * entries otherwise. */ - static uint64_t uLastLogTS = 0; - if (RTTimeMilliTS() >= uLastLogTS + 1000) - { - LogRel(("AHCI ATA: LUN#%d: CD-ROM block number %Ld invalid (SEEK)\n", s->iLUN, (uint64_t)iATAPILBA)); - uLastLogTS = RTTimeMilliTS(); - } - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); - break; - } - atapiCmdOK(s); - ataSetStatus(s, ATA_STAT_SEEK); /* Linux expects this. */ - } - break; - case SCSI_START_STOP_UNIT: - { - int rc = VINF_SUCCESS; - switch (pbPacket[4] & 3) - { - case 0: /* 00 - Stop motor */ - case 1: /* 01 - Start motor */ - break; - case 2: /* 10 - Eject media */ - { - /* This must be done from EMT. */ - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s); - - PDMCritSectLeave(&pCtl->lock); - rc = VMR3ReqPriorityCallWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY, - (PFNRT)s->pDrvMount->pfnUnmount, 3, s->pDrvMount, - false /*=fForce*/, true /*=fEeject*/); - Assert(RT_SUCCESS(rc) || (rc == VERR_PDM_MEDIA_LOCKED) || (rc = VERR_PDM_MEDIA_NOT_MOUNTED)); - if (RT_SUCCESS(rc) && pCtl->pMediaNotify) - { - rc = VMR3ReqCallNoWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY, - (PFNRT)pCtl->pMediaNotify->pfnEjected, 2, - pCtl->pMediaNotify, s->iLUN); - AssertRC(rc); - } - { - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - } - break; - } - case 3: /* 11 - Load media */ - /** @todo rc = s->pDrvMount->pfnLoadMedia(s->pDrvMount) */ - break; - } - if (RT_SUCCESS(rc)) - atapiCmdOK(s); - else - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED); - } - break; - case SCSI_MECHANISM_STATUS: - { - cbMax = ataBE2H_U16(pbPacket + 8); - ataStartTransfer(s, RT_MIN(cbMax, 8), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MECHANISM_STATUS, true); - } - break; - case SCSI_READ_TOC_PMA_ATIP: - { - uint8_t format; - - if (s->cNotifiedMediaChange > 0) - { - s->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - break; - } - else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) - { - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; - } - cbMax = ataBE2H_U16(pbPacket + 7); - /* SCSI MMC-3 spec says format is at offset 2 (lower 4 bits), - * but Linux kernel uses offset 9 (topmost 2 bits). Hope that - * the other field is clear... */ - format = (pbPacket[2] & 0xf) | (pbPacket[9] >> 6); - switch (format) - { - case 0: - ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_NORMAL, true); - break; - case 1: - ataStartTransfer(s, RT_MIN(cbMax, 12), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_MULTI, true); - break; - case 2: - ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_RAW, true); - break; - default: - error_cmd: - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); - break; - } - } - break; - case SCSI_READ_CAPACITY: - if (s->cNotifiedMediaChange > 0) - { - s->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - break; - } - else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) - { - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; - } - ataStartTransfer(s, 8, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_CAPACITY, true); - break; - case SCSI_READ_DISC_INFORMATION: - if (s->cNotifiedMediaChange > 0) - { - s->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - break; - } - else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) - { - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; - } - cbMax = ataBE2H_U16(pbPacket + 7); - ataStartTransfer(s, RT_MIN(cbMax, 34), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_DISC_INFORMATION, true); - break; - case SCSI_READ_TRACK_INFORMATION: - if (s->cNotifiedMediaChange > 0) - { - s->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - break; - } - else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) - { - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; - } - cbMax = ataBE2H_U16(pbPacket + 7); - ataStartTransfer(s, RT_MIN(cbMax, 36), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TRACK_INFORMATION, true); - break; - case SCSI_GET_CONFIGURATION: - /* No media change stuff here, it can confuse Linux guests. */ - cbMax = ataBE2H_U16(pbPacket + 7); - ataStartTransfer(s, RT_MIN(cbMax, 80), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_GET_CONFIGURATION, true); - break; - case SCSI_INQUIRY: - cbMax = ataBE2H_U16(pbPacket + 3); - ataStartTransfer(s, RT_MIN(cbMax, 36), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_INQUIRY, true); - break; - default: - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE); - break; - } -} - - -/* - * Parse ATAPI commands, passing them directly to the CD/DVD drive. - */ -static void atapiParseCmdPassthrough(AHCIATADevState *s) -{ - const uint8_t *pbPacket; - uint8_t *pbBuf; - uint32_t cSectors, iATAPILBA; - uint32_t cbTransfer = 0; - PDMBLOCKTXDIR uTxDir = PDMBLOCKTXDIR_NONE; - - pbPacket = s->aATAPICmd; - pbBuf = s->CTXALLSUFF(pbIOBuffer); - switch (pbPacket[0]) - { - case SCSI_BLANK: - goto sendcmd; - case SCSI_CLOSE_TRACK_SESSION: - goto sendcmd; - case SCSI_ERASE_10: - iATAPILBA = ataBE2H_U32(pbPacket + 2); - cbTransfer = ataBE2H_U16(pbPacket + 7); - Log2(("ATAPI PT: lba %d\n", iATAPILBA)); - uTxDir = PDMBLOCKTXDIR_TO_DEVICE; - goto sendcmd; - case SCSI_FORMAT_UNIT: - cbTransfer = s->uATARegLCyl | (s->uATARegHCyl << 8); /* use ATAPI transfer length */ - uTxDir = PDMBLOCKTXDIR_TO_DEVICE; - goto sendcmd; - case SCSI_GET_CONFIGURATION: - cbTransfer = ataBE2H_U16(pbPacket + 7); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_GET_EVENT_STATUS_NOTIFICATION: - cbTransfer = ataBE2H_U16(pbPacket + 7); - if (ASMAtomicReadU32(&s->MediaEventStatus) != ATA_EVENT_STATUS_UNCHANGED) - { - ataStartTransfer(s, RT_MIN(cbTransfer, 8), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_GET_EVENT_STATUS_NOTIFICATION, true); - break; - } - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_GET_PERFORMANCE: - cbTransfer = s->uATARegLCyl | (s->uATARegHCyl << 8); /* use ATAPI transfer length */ - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_INQUIRY: - cbTransfer = ataBE2H_U16(pbPacket + 3); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_LOAD_UNLOAD_MEDIUM: - goto sendcmd; - case SCSI_MECHANISM_STATUS: - cbTransfer = ataBE2H_U16(pbPacket + 8); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_MODE_SELECT_10: - cbTransfer = ataBE2H_U16(pbPacket + 7); - uTxDir = PDMBLOCKTXDIR_TO_DEVICE; - goto sendcmd; - case SCSI_MODE_SENSE_10: - cbTransfer = ataBE2H_U16(pbPacket + 7); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_PAUSE_RESUME: - goto sendcmd; - case SCSI_PLAY_AUDIO_10: - goto sendcmd; - case SCSI_PLAY_AUDIO_12: - goto sendcmd; - case SCSI_PLAY_AUDIO_MSF: - goto sendcmd; - case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL: - /** @todo do not forget to unlock when a VM is shut down */ - goto sendcmd; - case SCSI_READ_10: - iATAPILBA = ataBE2H_U32(pbPacket + 2); - cSectors = ataBE2H_U16(pbPacket + 7); - Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors)); - s->cbATAPISector = 2048; /**< @todo this size is not always correct */ - cbTransfer = cSectors * s->cbATAPISector; - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_READ_12: - iATAPILBA = ataBE2H_U32(pbPacket + 2); - cSectors = ataBE2H_U32(pbPacket + 6); - Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors)); - s->cbATAPISector = 2048; /**< @todo this size is not always correct */ - cbTransfer = cSectors * s->cbATAPISector; - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_READ_BUFFER: - cbTransfer = ataBE2H_U24(pbPacket + 6); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_READ_BUFFER_CAPACITY: - cbTransfer = ataBE2H_U16(pbPacket + 7); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_READ_CAPACITY: - cbTransfer = 8; - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_READ_CD: - s->cbATAPISector = 2048; /**< @todo this size is not always correct */ - cbTransfer = ataBE2H_U24(pbPacket + 6) / s->cbATAPISector * s->cbATAPISector; - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_READ_CD_MSF: - cSectors = ataMSF2LBA(pbPacket + 6) - ataMSF2LBA(pbPacket + 3); - if (cSectors > 32) - cSectors = 32; /* Limit transfer size to 64~74K. Safety first. In any case this can only harm software doing CDDA extraction. */ - s->cbATAPISector = 2048; /**< @todo this size is not always correct */ - cbTransfer = cSectors * s->cbATAPISector; - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_READ_DISC_INFORMATION: - cbTransfer = ataBE2H_U16(pbPacket + 7); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_READ_DVD_STRUCTURE: - cbTransfer = ataBE2H_U16(pbPacket + 8); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_READ_FORMAT_CAPACITIES: - cbTransfer = ataBE2H_U16(pbPacket + 7); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_READ_SUBCHANNEL: - cbTransfer = ataBE2H_U16(pbPacket + 7); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_READ_TOC_PMA_ATIP: - cbTransfer = ataBE2H_U16(pbPacket + 7); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_READ_TRACK_INFORMATION: - cbTransfer = ataBE2H_U16(pbPacket + 7); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_REPAIR_TRACK: - goto sendcmd; - case SCSI_REPORT_KEY: - cbTransfer = ataBE2H_U16(pbPacket + 8); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_REQUEST_SENSE: - cbTransfer = pbPacket[4]; - if ((s->abATAPISense[2] & 0x0f) != SCSI_SENSE_NONE) - { - ataStartTransfer(s, RT_MIN(cbTransfer, 18), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_REQUEST_SENSE, true); - break; - } - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_RESERVE_TRACK: - goto sendcmd; - case SCSI_SCAN: - goto sendcmd; - case SCSI_SEEK_10: - goto sendcmd; - case SCSI_SEND_CUE_SHEET: - cbTransfer = ataBE2H_U24(pbPacket + 6); - uTxDir = PDMBLOCKTXDIR_TO_DEVICE; - goto sendcmd; - case SCSI_SEND_DVD_STRUCTURE: - cbTransfer = ataBE2H_U16(pbPacket + 8); - uTxDir = PDMBLOCKTXDIR_TO_DEVICE; - goto sendcmd; - case SCSI_SEND_EVENT: - cbTransfer = ataBE2H_U16(pbPacket + 8); - uTxDir = PDMBLOCKTXDIR_TO_DEVICE; - goto sendcmd; - case SCSI_SEND_KEY: - cbTransfer = ataBE2H_U16(pbPacket + 8); - uTxDir = PDMBLOCKTXDIR_TO_DEVICE; - goto sendcmd; - case SCSI_SEND_OPC_INFORMATION: - cbTransfer = ataBE2H_U16(pbPacket + 7); - uTxDir = PDMBLOCKTXDIR_TO_DEVICE; - goto sendcmd; - case SCSI_SET_CD_SPEED: - goto sendcmd; - case SCSI_SET_READ_AHEAD: - goto sendcmd; - case SCSI_SET_STREAMING: - cbTransfer = ataBE2H_U16(pbPacket + 9); - uTxDir = PDMBLOCKTXDIR_TO_DEVICE; - goto sendcmd; - case SCSI_START_STOP_UNIT: - goto sendcmd; - case SCSI_STOP_PLAY_SCAN: - goto sendcmd; - case SCSI_SYNCHRONIZE_CACHE: - goto sendcmd; - case SCSI_TEST_UNIT_READY: - goto sendcmd; - case SCSI_VERIFY_10: - goto sendcmd; - case SCSI_WRITE_10: - iATAPILBA = ataBE2H_U32(pbPacket + 2); - cSectors = ataBE2H_U16(pbPacket + 7); - Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors)); -#if 0 - /* The sector size is determined by the async I/O thread. */ - s->cbATAPISector = 0; - /* Preliminary, will be corrected once the sector size is known. */ - cbTransfer = cSectors; -#else - s->cbATAPISector = 2048; /**< @todo this size is not always correct */ - cbTransfer = cSectors * s->cbATAPISector; -#endif - uTxDir = PDMBLOCKTXDIR_TO_DEVICE; - goto sendcmd; - case SCSI_WRITE_12: - iATAPILBA = ataBE2H_U32(pbPacket + 2); - cSectors = ataBE2H_U32(pbPacket + 6); - Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors)); -#if 0 - /* The sector size is determined by the async I/O thread. */ - s->cbATAPISector = 0; - /* Preliminary, will be corrected once the sector size is known. */ - cbTransfer = cSectors; -#else - s->cbATAPISector = 2048; /**< @todo this size is not always correct */ - cbTransfer = cSectors * s->cbATAPISector; -#endif - uTxDir = PDMBLOCKTXDIR_TO_DEVICE; - goto sendcmd; - case SCSI_WRITE_AND_VERIFY_10: - iATAPILBA = ataBE2H_U32(pbPacket + 2); - cSectors = ataBE2H_U16(pbPacket + 7); - Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors)); - /* The sector size is determined by the async I/O thread. */ - s->cbATAPISector = 0; - /* Preliminary, will be corrected once the sector size is known. */ - cbTransfer = cSectors; - uTxDir = PDMBLOCKTXDIR_TO_DEVICE; - goto sendcmd; - case SCSI_WRITE_BUFFER: - switch (pbPacket[1] & 0x1f) - { - case 0x04: /* download microcode */ - case 0x05: /* download microcode and save */ - case 0x06: /* download microcode with offsets */ - case 0x07: /* download microcode with offsets and save */ - case 0x0e: /* download microcode with offsets and defer activation */ - case 0x0f: /* activate deferred microcode */ - LogRel(("AHCI ATA: LUN#%d: CD-ROM passthrough command attempted to update firmware, blocked\n", s->iLUN)); - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); - break; - default: - cbTransfer = ataBE2H_U16(pbPacket + 6); - uTxDir = PDMBLOCKTXDIR_TO_DEVICE; - goto sendcmd; - } - break; - case SCSI_REPORT_LUNS: /* Not part of MMC-3, but used by Windows. */ - cbTransfer = ataBE2H_U32(pbPacket + 6); - uTxDir = PDMBLOCKTXDIR_FROM_DEVICE; - goto sendcmd; - case SCSI_REZERO_UNIT: - /* Obsolete command used by cdrecord. What else would one expect? - * This command is not sent to the drive, it is handled internally, - * as the Linux kernel doesn't like it (message "scsi: unknown - * opcode 0x01" in syslog) and replies with a sense code of 0, - * which sends cdrecord to an endless loop. */ - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE); - break; - default: - LogRel(("AHCI ATA: LUN#%d: passthrough unimplemented for command %#x\n", s->iLUN, pbPacket[0])); - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE); - break; - sendcmd: - /* Send a command to the drive, passing data in/out as required. */ - Log2(("ATAPI PT: max size %d\n", cbTransfer)); - Assert(cbTransfer <= s->cbIOBuffer); - if (cbTransfer == 0) - uTxDir = PDMBLOCKTXDIR_NONE; - ataStartTransfer(s, cbTransfer, uTxDir, ATAFN_BT_ATAPI_PASSTHROUGH_CMD, ATAFN_SS_ATAPI_PASSTHROUGH, true); - } -} - - -static void atapiParseCmd(AHCIATADevState *s) -{ - const uint8_t *pbPacket; - - pbPacket = s->aATAPICmd; -#ifdef DEBUG - Log(("%s: LUN#%d DMA=%d CMD=%#04x \"%s\"\n", __FUNCTION__, s->iLUN, s->fDMA, pbPacket[0], SCSICmdText(pbPacket[0]))); -#else /* !DEBUG */ - Log(("%s: LUN#%d DMA=%d CMD=%#04x\n", __FUNCTION__, s->iLUN, s->fDMA, pbPacket[0])); -#endif /* !DEBUG */ - Log2(("%s: limit=%#x packet: %.*Rhxs\n", __FUNCTION__, s->uATARegLCyl | (s->uATARegHCyl << 8), ATAPI_PACKET_SIZE, pbPacket)); - - if (s->fATAPIPassthrough) - atapiParseCmdPassthrough(s); - else - atapiParseCmdVirtualATAPI(s); -} - - -static bool ataPacketSS(AHCIATADevState *s) -{ - s->fDMA = !!(s->uATARegFeature & 1); - memcpy(s->aATAPICmd, s->CTXALLSUFF(pbIOBuffer), ATAPI_PACKET_SIZE); - s->uTxDir = PDMBLOCKTXDIR_NONE; - s->cbTotalTransfer = 0; - s->cbElementaryTransfer = 0; - atapiParseCmd(s); - return false; -} - -#if 0 - -/** - * SCSI_GET_EVENT_STATUS_NOTIFICATION should return "medium removed" event - * from now on, regardless if there was a medium inserted or not. - */ -static void ataMediumRemoved(AHCIATADevState *s) -{ - ASMAtomicWriteU32(&s->MediaEventStatus, ATA_EVENT_STATUS_MEDIA_REMOVED); -} - - -/** - * SCSI_GET_EVENT_STATUS_NOTIFICATION should return "medium inserted". If - * there was already a medium inserted, don't forget to send the "medium - * removed" event first. - */ -static void ataMediumInserted(AHCIATADevState *s) -{ - uint32_t OldStatus, NewStatus; - do - { - OldStatus = ASMAtomicReadU32(&s->MediaEventStatus); - switch (OldStatus) - { - case ATA_EVENT_STATUS_MEDIA_CHANGED: - case ATA_EVENT_STATUS_MEDIA_REMOVED: - /* no change, we will send "medium removed" + "medium inserted" */ - NewStatus = ATA_EVENT_STATUS_MEDIA_CHANGED; - break; - default: - NewStatus = ATA_EVENT_STATUS_MEDIA_NEW; - break; - } - } while (!ASMAtomicCmpXchgU32(&s->MediaEventStatus, NewStatus, OldStatus)); -} - - -/** - * Called when a media is mounted. - * - * @param pInterface Pointer to the interface structure containing the called function pointer. - */ -static DECLCALLBACK(void) ataMountNotify(PPDMIMOUNTNOTIFY pInterface) -{ - AHCIATADevState *pIf = PDMIMOUNTNOTIFY_2_ATASTATE(pInterface); - Log(("%s: changing LUN#%d\n", __FUNCTION__, pIf->iLUN)); - - /* Ignore the call if we're called while being attached. */ - if (!pIf->pDrvBlock) - return; - - LogRel(("AHCI ATA: LUN#%d: CD/DVD, total number of sectors %Ld, passthrough unchanged\n", pIf->iLUN, pIf->cTotalSectors)); - - if (pIf->fATAPI) - pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 2048; - else - pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 512; - - /* Report media changed in TEST UNIT and other (probably incorrect) places. */ - if (pIf->cNotifiedMediaChange < 2) - pIf->cNotifiedMediaChange = 2; - ataMediumInserted(pIf); -} - -/** - * Called when a media is unmounted - * @param pInterface Pointer to the interface structure containing the called function pointer. - */ -static DECLCALLBACK(void) ataUnmountNotify(PPDMIMOUNTNOTIFY pInterface) -{ - AHCIATADevState *pIf = PDMIMOUNTNOTIFY_2_ATASTATE(pInterface); - Log(("%s:\n", __FUNCTION__)); - pIf->cTotalSectors = 0; - - /* - * Whatever I do, XP will not use the GET MEDIA STATUS nor the EVENT stuff. - * However, it will respond to TEST UNIT with a 0x6 0x28 (media changed) sense code. - * So, we'll give it 4 TEST UNIT command to catch up, two which the media is not - * present and 2 in which it is changed. - */ - pIf->cNotifiedMediaChange = 4; - ataMediumRemoved(pIf); -} -#endif - -static void ataPacketBT(AHCIATADevState *s) -{ - s->cbElementaryTransfer = s->cbTotalTransfer; - s->uATARegNSector = (s->uATARegNSector & ~7) | ATAPI_INT_REASON_CD; - Log2(("%s: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector)); - ataSetStatusValue(s, ATA_STAT_READY); -} - - -static void ataResetDevice(AHCIATADevState *s) -{ - s->cMultSectors = ATA_MAX_MULT_SECTORS; - s->cNotifiedMediaChange = 0; - ASMAtomicWriteU32(&s->MediaEventStatus, ATA_EVENT_STATUS_UNCHANGED); - ataUnsetIRQ(s); - - s->uATARegSelect = 0x20; - ataSetStatusValue(s, ATA_STAT_READY); - ataSetSignature(s); - s->cbTotalTransfer = 0; - s->cbElementaryTransfer = 0; - s->iIOBufferPIODataStart = 0; - s->iIOBufferPIODataEnd = 0; - s->iBeginTransfer = ATAFN_BT_NULL; - s->iSourceSink = ATAFN_SS_NULL; - s->fATAPITransfer = false; - s->uATATransferMode = ATA_MODE_UDMA | 2; /* AHCI supports only up to UDMA2 */ - - s->uATARegFeature = 0; -} - - -static bool ataExecuteDeviceDiagnosticSS(AHCIATADevState *s) -{ - ataSetSignature(s); - if (s->fATAPI) - ataSetStatusValue(s, 0); /* NOTE: READY is _not_ set */ - else - ataSetStatusValue(s, ATA_STAT_READY); - s->uATARegError = 0x01; - return false; -} - - -static int ataTrimSectors(AHCIATADevState *s, uint64_t u64Sector, uint32_t cSectors, - bool *pfRedo) -{ - RTRANGE TrimRange; - PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - int rc; - - PDMCritSectLeave(&pCtl->lock); - - TrimRange.offStart = u64Sector * 512; - TrimRange.cbRange = cSectors * 512; - - s->pLed->Asserted.s.fWriting = s->pLed->Actual.s.fWriting = 1; - rc = s->pDrvBlock->pfnDiscard(s->pDrvBlock, &TrimRange, 1); - s->pLed->Actual.s.fWriting = 0; - - if (RT_SUCCESS(rc)) - *pfRedo = false; - else - *pfRedo = ataIsRedoSetWarning(s, rc); - - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - return rc; -} - - -static bool ataTrimSS(AHCIATADevState *s) -{ - int rc = VERR_GENERAL_FAILURE; - uint32_t cRangesMax; - uint64_t *pu64Range = (uint64_t *)s->CTX_SUFF(pbIOBuffer); - bool fRedo = false; - - cRangesMax = s->cbElementaryTransfer / sizeof(uint64_t); - Assert(cRangesMax); - - while (cRangesMax-- > 0) - { - if (ATA_RANGE_LENGTH_GET(*pu64Range) == 0) - break; - - rc = ataTrimSectors(s, *pu64Range & ATA_RANGE_LBA_MASK, - ATA_RANGE_LENGTH_GET(*pu64Range), &fRedo); - if (RT_FAILURE(rc)) - break; - - pu64Range++; - } - - if (RT_SUCCESS(rc)) - { - s->iSourceSink = ATAFN_SS_NULL; - ataCmdOK(s, ATA_STAT_SEEK); - } - else - { - if (fRedo) - return fRedo; - if (s->cErrors++ < MAX_LOG_REL_ERRORS) - LogRel(("PIIX3 ATA: LUN#%d: disk trim error (rc=%Rrc iSector=%#RX64 cSectors=%#RX32)\n", - s->iLUN, rc, *pu64Range & ATA_RANGE_LBA_MASK, ATA_RANGE_LENGTH_GET(*pu64Range))); - - /* - * Check if we got interrupted. We don't need to set status variables - * because the request was aborted. - */ - if (rc != VERR_INTERRUPTED) - ataCmdError(s, ID_ERR); - } - - return false; -} - - -static void ataParseCmd(AHCIATADevState *s, uint8_t cmd) -{ -#ifdef DEBUG - Log(("%s: LUN#%d CMD=%#04x \"%s\"\n", __FUNCTION__, s->iLUN, cmd, ATACmdText(cmd))); -#else /* !DEBUG */ - Log(("%s: LUN#%d CMD=%#04x\n", __FUNCTION__, s->iLUN, cmd)); -#endif /* !DEBUG */ - s->fLBA48 = false; - s->fDMA = false; - if (cmd == ATA_IDLE_IMMEDIATE) - { - /* Detect Linux timeout recovery, first tries IDLE IMMEDIATE (which - * would overwrite the failing command unfortunately), then RESET. */ - int32_t uCmdWait = -1; - uint64_t uNow = RTTimeNanoTS(); - if (s->u64CmdTS) - uCmdWait = (uNow - s->u64CmdTS) / 1000; - LogRel(("AHCI ATA: LUN#%d: IDLE IMMEDIATE, CmdIf=%#04x (%d usec ago)\n", - s->iLUN, s->uATARegCommand, uCmdWait)); - } - s->uATARegCommand = cmd; - switch (cmd) - { - case ATA_IDENTIFY_DEVICE: - if (s->pDrvBlock && !s->fATAPI) - ataStartTransfer(s, 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_NULL, ATAFN_SS_IDENTIFY, false); - else - { - if (s->fATAPI) - ataSetSignature(s); - ataCmdError(s, ABRT_ERR); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - } - break; - case ATA_INITIALIZE_DEVICE_PARAMETERS: - case ATA_RECALIBRATE: - ataCmdOK(s, ATA_STAT_SEEK); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - case ATA_SET_MULTIPLE_MODE: - if ( s->uATARegNSector != 0 - && ( s->uATARegNSector > ATA_MAX_MULT_SECTORS - || (s->uATARegNSector & (s->uATARegNSector - 1)) != 0)) - { - ataCmdError(s, ABRT_ERR); - } - else - { - Log2(("%s: set multi sector count to %d\n", __FUNCTION__, s->uATARegNSector)); - s->cMultSectors = s->uATARegNSector; - ataCmdOK(s, 0); - } - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - case ATA_READ_VERIFY_SECTORS_EXT: - s->fLBA48 = true; - case ATA_READ_VERIFY_SECTORS: - case ATA_READ_VERIFY_SECTORS_WITHOUT_RETRIES: - /* do sector number check ? */ - ataCmdOK(s, 0); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - case ATA_READ_SECTORS_EXT: - s->fLBA48 = true; - case ATA_READ_SECTORS: - case ATA_READ_SECTORS_WITHOUT_RETRIES: - if (!s->pDrvBlock || s->fATAPI) - goto abort_cmd; - s->cSectorsPerIRQ = 1; - ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false); - break; - case ATA_WRITE_SECTORS_EXT: - s->fLBA48 = true; - case ATA_WRITE_SECTORS: - case ATA_WRITE_SECTORS_WITHOUT_RETRIES: - if (!s->pDrvBlock || s->fATAPI) - goto abort_cmd; - s->cSectorsPerIRQ = 1; - ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false); - break; - case ATA_READ_MULTIPLE_EXT: - s->fLBA48 = true; - case ATA_READ_MULTIPLE: - if (!s->pDrvBlock || !s->cMultSectors || s->fATAPI) - goto abort_cmd; - s->cSectorsPerIRQ = s->cMultSectors; - ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false); - break; - case ATA_WRITE_MULTIPLE_EXT: - s->fLBA48 = true; - case ATA_WRITE_MULTIPLE: - if (!s->pDrvBlock || !s->cMultSectors || s->fATAPI) - goto abort_cmd; - s->cSectorsPerIRQ = s->cMultSectors; - ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false); - break; - case ATA_READ_DMA_EXT: - s->fLBA48 = true; - case ATA_READ_DMA: - case ATA_READ_DMA_WITHOUT_RETRIES: - if (!s->pDrvBlock || s->fATAPI) - goto abort_cmd; - s->cSectorsPerIRQ = ATA_MAX_MULT_SECTORS; - s->fDMA = true; - ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false); - break; - case ATA_WRITE_DMA_EXT: - s->fLBA48 = true; - case ATA_WRITE_DMA: - case ATA_WRITE_DMA_WITHOUT_RETRIES: - if (!s->pDrvBlock || s->fATAPI) - goto abort_cmd; - s->cSectorsPerIRQ = ATA_MAX_MULT_SECTORS; - s->fDMA = true; - ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false); - break; - case ATA_READ_NATIVE_MAX_ADDRESS_EXT: - s->fLBA48 = true; - ataSetSector(s, s->cTotalSectors - 1); - ataCmdOK(s, 0); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - case ATA_READ_NATIVE_MAX_ADDRESS: - ataSetSector(s, RT_MIN(s->cTotalSectors, 1 << 28) - 1); - ataCmdOK(s, 0); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - case ATA_CHECK_POWER_MODE: - s->uATARegNSector = 0xff; /* drive active or idle */ - ataCmdOK(s, 0); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - case ATA_SET_FEATURES: - Log2(("%s: feature=%#x\n", __FUNCTION__, s->uATARegFeature)); - if (!s->pDrvBlock) - goto abort_cmd; - switch (s->uATARegFeature) - { - case 0x02: /* write cache enable */ - Log2(("%s: write cache enable\n", __FUNCTION__)); - ataCmdOK(s, ATA_STAT_SEEK); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - case 0xaa: /* read look-ahead enable */ - Log2(("%s: read look-ahead enable\n", __FUNCTION__)); - ataCmdOK(s, ATA_STAT_SEEK); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - case 0x55: /* read look-ahead disable */ - Log2(("%s: read look-ahead disable\n", __FUNCTION__)); - ataCmdOK(s, ATA_STAT_SEEK); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - case 0xcc: /* reverting to power-on defaults enable */ - Log2(("%s: revert to power-on defaults enable\n", __FUNCTION__)); - ataCmdOK(s, ATA_STAT_SEEK); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - case 0x66: /* reverting to power-on defaults disable */ - Log2(("%s: revert to power-on defaults disable\n", __FUNCTION__)); - ataCmdOK(s, ATA_STAT_SEEK); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - case 0x82: /* write cache disable */ - Log2(("%s: write cache disable\n", __FUNCTION__)); - /* As per the ATA/ATAPI-6 specs, a write cache disable - * command MUST flush the write buffers to disc. */ - ataStartTransfer(s, 0, PDMBLOCKTXDIR_NONE, ATAFN_BT_NULL, ATAFN_SS_FLUSH, false); - break; - case 0x03: { /* set transfer mode */ - Log2(("%s: transfer mode %#04x\n", __FUNCTION__, s->uATARegNSector)); - switch (s->uATARegNSector & 0xf8) - { - case 0x00: /* PIO default */ - case 0x08: /* PIO mode */ - break; - case ATA_MODE_MDMA: /* MDMA mode */ - s->uATATransferMode = (s->uATARegNSector & 0xf8) | RT_MIN(s->uATARegNSector & 0x07, ATA_MDMA_MODE_MAX); - break; - case ATA_MODE_UDMA: /* UDMA mode */ - s->uATATransferMode = (s->uATARegNSector & 0xf8) | RT_MIN(s->uATARegNSector & 0x07, ATA_UDMA_MODE_MAX); - break; - default: - goto abort_cmd; - } - ataCmdOK(s, ATA_STAT_SEEK); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - } - default: - goto abort_cmd; - } - /* - * OS/2 workarond: - * The OS/2 IDE driver from MCP2 appears to rely on the feature register being - * reset here. According to the specification, this is a driver bug as the register - * contents are undefined after the call. This means we can just as well reset it. - */ - s->uATARegFeature = 0; - break; - case ATA_FLUSH_CACHE_EXT: - case ATA_FLUSH_CACHE: - if (!s->pDrvBlock || s->fATAPI) - goto abort_cmd; - ataStartTransfer(s, 0, PDMBLOCKTXDIR_NONE, ATAFN_BT_NULL, ATAFN_SS_FLUSH, false); - break; - case ATA_STANDBY_IMMEDIATE: - ataCmdOK(s, 0); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - case ATA_IDLE_IMMEDIATE: - LogRel(("AHCI ATA: LUN#%d: aborting current command\n", s->iLUN)); - ataAbortCurrentCommand(s, false); - break; - /* ATAPI commands */ - case ATA_IDENTIFY_PACKET_DEVICE: - if (s->fATAPI) - ataStartTransfer(s, 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_NULL, ATAFN_SS_ATAPI_IDENTIFY, false); - else - { - ataCmdError(s, ABRT_ERR); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - } - break; - case ATA_EXECUTE_DEVICE_DIAGNOSTIC: - ataStartTransfer(s, 0, PDMBLOCKTXDIR_NONE, ATAFN_BT_NULL, ATAFN_SS_EXECUTE_DEVICE_DIAGNOSTIC, false); - break; - case ATA_DEVICE_RESET: - if (!s->fATAPI) - goto abort_cmd; - LogRel(("AHCI ATA: LUN#%d: performing device RESET\n", s->iLUN)); - ataAbortCurrentCommand(s, true); - break; - case ATA_PACKET: - if (!s->fATAPI) - goto abort_cmd; - /* overlapping commands not supported */ - if (s->uATARegFeature & 0x02) - goto abort_cmd; - ataStartTransfer(s, ATAPI_PACKET_SIZE, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_PACKET, ATAFN_SS_PACKET, false); - break; - case ATA_DATA_SET_MANAGEMENT: - if (!s->pDrvBlock || !s->pDrvBlock->pfnDiscard) - goto abort_cmd; - if ( !(s->uATARegFeature & UINT8_C(0x01)) - || (s->uATARegFeature & ~UINT8_C(0x01))) - goto abort_cmd; - s->fDMA = true; - ataStartTransfer(s, (s->uATARegNSectorHOB << 8 | s->uATARegNSector) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_NULL, ATAFN_SS_TRIM, false); - break; - default: - abort_cmd: - ataCmdError(s, ABRT_ERR); - ataSetIRQ(s); /* Shortcut, do not use AIO thread. */ - break; - } -} - - -/** - * Waits for a particular async I/O thread to complete whatever it - * is doing at the moment. - * - * @returns true on success. - * @returns false when the thread is still processing. - * @param pThis Pointer to the controller data. - * @param cMillies How long to wait (total). - */ -static bool ataWaitForAsyncIOIsIdle(PAHCIATACONTROLLER pCtl, RTMSINTERVAL cMillies) -{ - uint64_t u64Start; - bool fRc; - - /* Hope for the simple way out... */ - if (ataAsyncIOIsIdle(pCtl, false /*fStrict*/)) - return true; - - /* - * Have to wait. Do the setup while owning the mutex to avoid races. - */ - RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT); - - RTThreadUserReset(pCtl->AsyncIOThread); - ASMAtomicWriteBool(&pCtl->fSignalIdle, true); - - RTSemMutexRelease(pCtl->AsyncIORequestMutex); - - u64Start = RTTimeMilliTS(); - for (;;) - { - fRc = ataAsyncIOIsIdle(pCtl, false /*fStrict*/); - if (fRc) - break; - - if (RTTimeMilliTS() - u64Start >= cMillies) - break; - - int rc = RTThreadUserWait(pCtl->AsyncIOThread, 100 /*ms*/); - AssertMsg( ( RT_SUCCESS(rc) - && ataAsyncIOIsIdle(pCtl, false /*fStrict*/)) - || rc == VERR_TIMEOUT, - ("rc=%Rrc irq=%u\n", rc, pCtl->irq)); - } - - ASMAtomicWriteBool(&pCtl->fSignalIdle, false); - return fRc; -} - -#endif /* IN_RING3 */ - -static int ataIOPortWriteU8(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val) -{ - Log2(("%s: write addr=%#x val=%#04x\n", __FUNCTION__, addr, val)); - addr &= 7; - switch (addr) - { - case 0: - break; - case 1: /* feature register */ - /* NOTE: data is written to the two drives */ - pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB; - pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB; - pCtl->aIfs[0].uATARegFeatureHOB = pCtl->aIfs[0].uATARegFeature; - pCtl->aIfs[1].uATARegFeatureHOB = pCtl->aIfs[1].uATARegFeature; - pCtl->aIfs[0].uATARegFeature = val; - pCtl->aIfs[1].uATARegFeature = val; - break; - case 2: /* sector count */ - pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB; - pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB; - pCtl->aIfs[0].uATARegNSectorHOB = pCtl->aIfs[0].uATARegNSector; - pCtl->aIfs[1].uATARegNSectorHOB = pCtl->aIfs[1].uATARegNSector; - pCtl->aIfs[0].uATARegNSector = val; - pCtl->aIfs[1].uATARegNSector = val; - break; - case 3: /* sector number */ - pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB; - pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB; - pCtl->aIfs[0].uATARegSectorHOB = pCtl->aIfs[0].uATARegSector; - pCtl->aIfs[1].uATARegSectorHOB = pCtl->aIfs[1].uATARegSector; - pCtl->aIfs[0].uATARegSector = val; - pCtl->aIfs[1].uATARegSector = val; - break; - case 4: /* cylinder low */ - pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB; - pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB; - pCtl->aIfs[0].uATARegLCylHOB = pCtl->aIfs[0].uATARegLCyl; - pCtl->aIfs[1].uATARegLCylHOB = pCtl->aIfs[1].uATARegLCyl; - pCtl->aIfs[0].uATARegLCyl = val; - pCtl->aIfs[1].uATARegLCyl = val; - break; - case 5: /* cylinder high */ - pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB; - pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB; - pCtl->aIfs[0].uATARegHCylHOB = pCtl->aIfs[0].uATARegHCyl; - pCtl->aIfs[1].uATARegHCylHOB = pCtl->aIfs[1].uATARegHCyl; - pCtl->aIfs[0].uATARegHCyl = val; - pCtl->aIfs[1].uATARegHCyl = val; - break; - case 6: /* drive/head */ - pCtl->aIfs[0].uATARegSelect = (val & ~0x10) | 0xa0; - pCtl->aIfs[1].uATARegSelect = (val | 0x10) | 0xa0; - if (((val >> 4) & 1) != pCtl->iSelectedIf) - { - PPDMDEVINS pDevIns = CONTROLLER_2_DEVINS(pCtl); - - /* select another drive */ - pCtl->iSelectedIf = (val >> 4) & 1; - /* The IRQ line is multiplexed between the two drives, so - * update the state when switching to another drive. Only need - * to update interrupt line if it is enabled and there is a - * state change. */ - if ( !(pCtl->aIfs[pCtl->iSelectedIf].uATARegDevCtl & ATA_DEVCTL_DISABLE_IRQ) - && ( pCtl->aIfs[pCtl->iSelectedIf].fIrqPending - != pCtl->aIfs[pCtl->iSelectedIf ^ 1].fIrqPending)) - { - if (pCtl->aIfs[pCtl->iSelectedIf].fIrqPending) - { - Log2(("%s: LUN#%d asserting IRQ (drive select change)\n", __FUNCTION__, pCtl->aIfs[pCtl->iSelectedIf].iLUN)); - /* The BMDMA unit unconditionally sets BM_STATUS_INT if - * the interrupt line is asserted. It monitors the line - * for a rising edge. */ - pCtl->BmDma.u8Status |= BM_STATUS_INT; - if (pCtl->irq == 16) - PDMDevHlpPCISetIrq(pDevIns, 0, 1); - else - PDMDevHlpISASetIrq(pDevIns, pCtl->irq, 1); - } - else - { - Log2(("%s: LUN#%d deasserting IRQ (drive select change)\n", __FUNCTION__, pCtl->aIfs[pCtl->iSelectedIf].iLUN)); - if (pCtl->irq == 16) - PDMDevHlpPCISetIrq(pDevIns, 0, 0); - else - PDMDevHlpISASetIrq(pDevIns, pCtl->irq, 0); - } - } - } - break; - default: - case 7: /* command */ - /* ignore commands to non existant slave */ - if (pCtl->iSelectedIf && !pCtl->aIfs[pCtl->iSelectedIf].pDrvBlock) - break; -#ifndef IN_RING3 - /* Don't do anything complicated in GC */ - return VINF_IOM_R3_IOPORT_WRITE; -#else /* IN_RING3 */ - ataParseCmd(&pCtl->aIfs[pCtl->iSelectedIf], val); -#endif /* !IN_RING3 */ - } - return VINF_SUCCESS; -} - - -static int ataIOPortReadU8(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t *pu32) -{ - AHCIATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf]; - uint32_t val; - bool fHOB; - - fHOB = !!(s->uATARegDevCtl & (1 << 7)); - switch (addr & 7) - { - case 0: /* data register */ - val = 0xff; - break; - case 1: /* error register */ - /* The ATA specification is very terse when it comes to specifying - * the precise effects of reading back the error/feature register. - * The error register (read-only) shares the register number with - * the feature register (write-only), so it seems that it's not - * necessary to support the usual HOB readback here. */ - if (!s->pDrvBlock) - val = 0; - else - val = s->uATARegError; - break; - case 2: /* sector count */ - if (!s->pDrvBlock) - val = 0; - else if (fHOB) - val = s->uATARegNSectorHOB; - else - val = s->uATARegNSector; - break; - case 3: /* sector number */ - if (!s->pDrvBlock) - val = 0; - else if (fHOB) - val = s->uATARegSectorHOB; - else - val = s->uATARegSector; - break; - case 4: /* cylinder low */ - if (!s->pDrvBlock) - val = 0; - else if (fHOB) - val = s->uATARegLCylHOB; - else - val = s->uATARegLCyl; - break; - case 5: /* cylinder high */ - if (!s->pDrvBlock) - val = 0; - else if (fHOB) - val = s->uATARegHCylHOB; - else - val = s->uATARegHCyl; - break; - case 6: /* drive/head */ - /* This register must always work as long as there is at least - * one drive attached to the controller. It is common between - * both drives anyway (completely identical content). */ - if (!pCtl->aIfs[0].pDrvBlock && !pCtl->aIfs[1].pDrvBlock) - val = 0; - else - val = s->uATARegSelect; - break; - default: - case 7: /* primary status */ - { - /* Counter for number of busy status seen in GC in a row. */ - static unsigned cBusy = 0; - - if (!s->pDrvBlock) - val = 0; - else - val = s->uATARegStatus; - - /* Give the async I/O thread an opportunity to make progress, - * don't let it starve by guests polling frequently. EMT has a - * lower priority than the async I/O thread, but sometimes the - * host OS doesn't care. With some guests we are only allowed to - * be busy for about 5 milliseconds in some situations. Note that - * this is no guarantee for any other VBox thread getting - * scheduled, so this just lowers the CPU load a bit when drives - * are busy. It cannot help with timing problems. */ - if (val & ATA_STAT_BUSY) - { -#ifdef IN_RING3 - cBusy = 0; - PDMCritSectLeave(&pCtl->lock); - - RTThreadYield(); - - { - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - } - - val = s->uATARegStatus; -#else /* !IN_RING3 */ - /* Cannot yield CPU in guest context. And switching to host - * context for each and every busy status is too costly, - * especially on SMP systems where we don't gain much by - * yielding the CPU to someone else. */ - if (++cBusy >= 20) - { - cBusy = 0; - return VINF_IOM_R3_IOPORT_READ; - } -#endif /* !IN_RING3 */ - } - else - cBusy = 0; - ataUnsetIRQ(s); - break; - } - } - Log2(("%s: addr=%#x val=%#04x\n", __FUNCTION__, addr, val)); - *pu32 = val; - return VINF_SUCCESS; -} - - -static uint32_t ataStatusRead(PAHCIATACONTROLLER pCtl, uint32_t addr) -{ - AHCIATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf]; - uint32_t val; - - if ((!pCtl->aIfs[0].pDrvBlock && !pCtl->aIfs[1].pDrvBlock) || - (pCtl->iSelectedIf == 1 && !s->pDrvBlock)) - val = 0; - else - val = s->uATARegStatus; - Log2(("%s: addr=%#x val=%#04x\n", __FUNCTION__, addr, val)); - return val; -} - -static int ataControlWrite(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val) -{ -#ifndef IN_RING3 - if ((val ^ pCtl->aIfs[0].uATARegDevCtl) & ATA_DEVCTL_RESET) - return VINF_IOM_R3_IOPORT_WRITE; /* The RESET stuff is too complicated for GC. */ -#endif /* !IN_RING3 */ - - Log2(("%s: addr=%#x val=%#04x\n", __FUNCTION__, addr, val)); - /* RESET is common for both drives attached to a controller. */ - if (!(pCtl->aIfs[0].uATARegDevCtl & ATA_DEVCTL_RESET) && - (val & ATA_DEVCTL_RESET)) - { -#ifdef IN_RING3 - /* Software RESET low to high */ - int32_t uCmdWait0 = -1, uCmdWait1 = -1; - uint64_t uNow = RTTimeNanoTS(); - if (pCtl->aIfs[0].u64CmdTS) - uCmdWait0 = (uNow - pCtl->aIfs[0].u64CmdTS) / 1000; - if (pCtl->aIfs[1].u64CmdTS) - uCmdWait1 = (uNow - pCtl->aIfs[1].u64CmdTS) / 1000; - LogRel(("AHCI ATA: Ctl: RESET, DevSel=%d AIOIf=%d CmdIf0=%#04x (%d usec ago) CmdIf1=%#04x (%d usec ago)\n", - pCtl->iSelectedIf, pCtl->iAIOIf, - pCtl->aIfs[0].uATARegCommand, uCmdWait0, - pCtl->aIfs[1].uATARegCommand, uCmdWait1)); - pCtl->fReset = true; - /* Everything must be done after the reset flag is set, otherwise - * there are unavoidable races with the currently executing request - * (which might just finish in the mean time). */ - pCtl->fChainedTransfer = false; - for (uint32_t i = 0; i < RT_ELEMENTS(pCtl->aIfs); i++) - { - ataResetDevice(&pCtl->aIfs[i]); - /* The following cannot be done using ataSetStatusValue() since the - * reset flag is already set, which suppresses all status changes. */ - pCtl->aIfs[i].uATARegStatus = ATA_STAT_BUSY | ATA_STAT_SEEK; - Log2(("%s: LUN#%d status %#04x\n", __FUNCTION__, pCtl->aIfs[i].iLUN, pCtl->aIfs[i].uATARegStatus)); - pCtl->aIfs[i].uATARegError = 0x01; - } - ataAsyncIOClearRequests(pCtl); - Log2(("%s: Ctl: message to async I/O thread, resetA\n", __FUNCTION__)); - if (val & ATA_DEVCTL_HOB) - { - val &= ~ATA_DEVCTL_HOB; - Log2(("%s: ignored setting HOB\n", __FUNCTION__)); - } - ataAsyncIOPutRequest(pCtl, &ataResetARequest); -#else /* !IN_RING3 */ - AssertMsgFailed(("RESET handling is too complicated for GC\n")); -#endif /* IN_RING3 */ - } - else if ((pCtl->aIfs[0].uATARegDevCtl & ATA_DEVCTL_RESET) && - !(val & ATA_DEVCTL_RESET)) - { -#ifdef IN_RING3 - /* Software RESET high to low */ - Log(("%s: deasserting RESET\n", __FUNCTION__)); - Log2(("%s: Ctl: message to async I/O thread, resetC\n", __FUNCTION__)); - if (val & ATA_DEVCTL_HOB) - { - val &= ~ATA_DEVCTL_HOB; - Log2(("%s: ignored setting HOB\n", __FUNCTION__)); - } - ataAsyncIOPutRequest(pCtl, &ataResetCRequest); -#else /* !IN_RING3 */ - AssertMsgFailed(("RESET handling is too complicated for GC\n")); -#endif /* IN_RING3 */ - } - - /* Change of interrupt disable flag. Update interrupt line if interrupt - * is pending on the current interface. */ - if ((val ^ pCtl->aIfs[0].uATARegDevCtl) & ATA_DEVCTL_DISABLE_IRQ - && pCtl->aIfs[pCtl->iSelectedIf].fIrqPending) - { - if (!(val & ATA_DEVCTL_DISABLE_IRQ)) - { - Log2(("%s: LUN#%d asserting IRQ (interrupt disable change)\n", __FUNCTION__, pCtl->aIfs[pCtl->iSelectedIf].iLUN)); - /* The BMDMA unit unconditionally sets BM_STATUS_INT if the - * interrupt line is asserted. It monitors the line for a rising - * edge. */ - pCtl->BmDma.u8Status |= BM_STATUS_INT; - if (pCtl->irq == 16) - PDMDevHlpPCISetIrq(CONTROLLER_2_DEVINS(pCtl), 0, 1); - else - PDMDevHlpISASetIrq(CONTROLLER_2_DEVINS(pCtl), pCtl->irq, 1); - } - else - { - Log2(("%s: LUN#%d deasserting IRQ (interrupt disable change)\n", __FUNCTION__, pCtl->aIfs[pCtl->iSelectedIf].iLUN)); - if (pCtl->irq == 16) - PDMDevHlpPCISetIrq(CONTROLLER_2_DEVINS(pCtl), 0, 0); - else - PDMDevHlpISASetIrq(CONTROLLER_2_DEVINS(pCtl), pCtl->irq, 0); - } - } - - if (val & ATA_DEVCTL_HOB) - Log2(("%s: set HOB\n", __FUNCTION__)); - - pCtl->aIfs[0].uATARegDevCtl = val; - pCtl->aIfs[1].uATARegDevCtl = val; - - return VINF_SUCCESS; -} - -#ifdef IN_RING3 - -static void ataPIOTransfer(PAHCIATACONTROLLER pCtl) -{ - AHCIATADevState *s; - - s = &pCtl->aIfs[pCtl->iAIOIf]; - Log3(("%s: if=%p\n", __FUNCTION__, s)); - - if (s->cbTotalTransfer && s->iIOBufferCur > s->iIOBufferEnd) - { - LogRel(("AHCI ATA: LUN#%d: %s data in the middle of a PIO transfer - VERY SLOW\n", s->iLUN, s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE ? "loading" : "storing")); - /* Any guest OS that triggers this case has a pathetic ATA driver. - * In a real system it would block the CPU via IORDY, here we do it - * very similarly by not continuing with the current instruction - * until the transfer to/from the storage medium is completed. */ - if (s->iSourceSink != ATAFN_SS_NULL) - { - bool fRedo; - uint8_t status = s->uATARegStatus; - ataSetStatusValue(s, ATA_STAT_BUSY); - Log2(("%s: calling source/sink function\n", __FUNCTION__)); - fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s); - pCtl->fRedo = fRedo; - if (RT_UNLIKELY(fRedo)) - return; - ataSetStatusValue(s, status); - s->iIOBufferCur = 0; - s->iIOBufferEnd = s->cbElementaryTransfer; - } - } - if (s->cbTotalTransfer) - { - if (s->fATAPITransfer) - ataPIOTransferLimitATAPI(s); - - if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE && s->cbElementaryTransfer > s->cbTotalTransfer) - s->cbElementaryTransfer = s->cbTotalTransfer; - - Log2(("%s: %s tx_size=%d elem_tx_size=%d index=%d end=%d\n", - __FUNCTION__, s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE ? "T2I" : "I2T", - s->cbTotalTransfer, s->cbElementaryTransfer, - s->iIOBufferCur, s->iIOBufferEnd)); - ataPIOTransferStart(s, s->iIOBufferCur, s->cbElementaryTransfer); - s->cbTotalTransfer -= s->cbElementaryTransfer; - s->iIOBufferCur += s->cbElementaryTransfer; - - if (s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE && s->cbElementaryTransfer > s->cbTotalTransfer) - s->cbElementaryTransfer = s->cbTotalTransfer; - } - else - ataPIOTransferStop(s); -} - - -DECLINLINE(void) ataPIOTransferFinish(PAHCIATACONTROLLER pCtl, AHCIATADevState *s) -{ - /* Do not interfere with RESET processing if the PIO transfer finishes - * while the RESET line is asserted. */ - if (pCtl->fReset) - { - Log2(("%s: Ctl: suppressed continuing PIO transfer as RESET is active\n", __FUNCTION__)); - return; - } - - if ( s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE - || ( s->iSourceSink != ATAFN_SS_NULL - && s->iIOBufferCur >= s->iIOBufferEnd)) - { - /* Need to continue the transfer in the async I/O thread. This is - * the case for write operations or generally for not yet finished - * transfers (some data might need to be read). */ - ataUnsetStatus(s, ATA_STAT_READY | ATA_STAT_DRQ); - ataSetStatus(s, ATA_STAT_BUSY); - - Log2(("%s: Ctl: message to async I/O thread, continuing PIO transfer\n", __FUNCTION__)); - ataAsyncIOPutRequest(pCtl, &ataPIORequest); - } - else - { - /* Either everything finished (though some data might still be pending) - * or some data is pending before the next read is due. */ - - /* Continue a previously started transfer. */ - ataUnsetStatus(s, ATA_STAT_DRQ); - ataSetStatus(s, ATA_STAT_READY); - - if (s->cbTotalTransfer) - { - /* There is more to transfer, happens usually for large ATAPI - * reads - the protocol limits the chunk size to 65534 bytes. */ - ataPIOTransfer(pCtl); - ataSetIRQ(s); - } - else - { - Log2(("%s: Ctl: skipping message to async I/O thread, ending PIO transfer\n", __FUNCTION__)); - /* Finish PIO transfer. */ - ataPIOTransfer(pCtl); - Assert(!pCtl->fRedo); - } - } -} - -#endif /* IN_RING3 */ - -static int ataDataWrite(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t cbSize, const uint8_t *pbBuf) -{ - AHCIATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf]; - uint8_t *p; - - if (s->iIOBufferPIODataStart < s->iIOBufferPIODataEnd) - { - Assert(s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE); - p = s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart; -#ifndef IN_RING3 - /* All but the last transfer unit is simple enough for GC, but - * sending a request to the async IO thread is too complicated. */ - if (s->iIOBufferPIODataStart + cbSize < s->iIOBufferPIODataEnd) - { - memcpy(p, pbBuf, cbSize); - s->iIOBufferPIODataStart += cbSize; - } - else - return VINF_IOM_R3_IOPORT_WRITE; -#else /* IN_RING3 */ - memcpy(p, pbBuf, cbSize); - s->iIOBufferPIODataStart += cbSize; - if (s->iIOBufferPIODataStart >= s->iIOBufferPIODataEnd) - ataPIOTransferFinish(pCtl, s); -#endif /* !IN_RING3 */ - } - else - Log2(("%s: DUMMY data\n", __FUNCTION__)); - Log3(("%s: addr=%#x val=%.*Rhxs\n", __FUNCTION__, addr, cbSize, pbBuf)); - return VINF_SUCCESS; -} - -static int ataDataRead(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t cbSize, uint8_t *pbBuf) -{ - AHCIATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf]; - uint8_t *p; - - if (s->iIOBufferPIODataStart < s->iIOBufferPIODataEnd) - { - Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); - p = s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart; -#ifndef IN_RING3 - /* All but the last transfer unit is simple enough for GC, but - * sending a request to the async IO thread is too complicated. */ - if (s->iIOBufferPIODataStart + cbSize < s->iIOBufferPIODataEnd) - { - memcpy(pbBuf, p, cbSize); - s->iIOBufferPIODataStart += cbSize; - } - else - return VINF_IOM_R3_IOPORT_READ; -#else /* IN_RING3 */ - memcpy(pbBuf, p, cbSize); - s->iIOBufferPIODataStart += cbSize; - if (s->iIOBufferPIODataStart >= s->iIOBufferPIODataEnd) - ataPIOTransferFinish(pCtl, s); -#endif /* !IN_RING3 */ - } - else - { - Log2(("%s: DUMMY data\n", __FUNCTION__)); - memset(pbBuf, '\xff', cbSize); - } - Log3(("%s: addr=%#x val=%.*Rhxs\n", __FUNCTION__, addr, cbSize, pbBuf)); - return VINF_SUCCESS; -} - -#ifdef IN_RING3 - -static void ataDMATransferStop(AHCIATADevState *s) -{ - s->cbTotalTransfer = 0; - s->cbElementaryTransfer = 0; - s->iBeginTransfer = ATAFN_BT_NULL; - s->iSourceSink = ATAFN_SS_NULL; -} - - -/** - * Perform the entire DMA transfer in one go (unless a source/sink operation - * has to be redone or a RESET comes in between). Unlike the PIO counterpart - * this function cannot handle empty transfers. - * - * @param pCtl Controller for which to perform the transfer. - */ -static void ataDMATransfer(PAHCIATACONTROLLER pCtl) -{ - PPDMDEVINS pDevIns = CONTROLLER_2_DEVINS(pCtl); - AHCIATADevState *s = &pCtl->aIfs[pCtl->iAIOIf]; - bool fRedo; - RTGCPHYS32 pDesc; - uint32_t cbTotalTransfer, cbElementaryTransfer; - uint32_t iIOBufferCur, iIOBufferEnd; - uint32_t dmalen; - PDMBLOCKTXDIR uTxDir; - bool fLastDesc = false; - - Assert(sizeof(BMDMADesc) == 8); - - fRedo = pCtl->fRedo; - if (RT_LIKELY(!fRedo)) - Assert(s->cbTotalTransfer); - uTxDir = (PDMBLOCKTXDIR)s->uTxDir; - cbTotalTransfer = s->cbTotalTransfer; - cbElementaryTransfer = s->cbElementaryTransfer; - iIOBufferCur = s->iIOBufferCur; - iIOBufferEnd = s->iIOBufferEnd; - - /* The DMA loop is designed to hold the lock only when absolutely - * necessary. This avoids long freezes should the guest access the - * ATA registers etc. for some reason. */ - PDMCritSectLeave(&pCtl->lock); - - Log2(("%s: %s tx_size=%d elem_tx_size=%d index=%d end=%d\n", - __FUNCTION__, uTxDir == PDMBLOCKTXDIR_FROM_DEVICE ? "T2I" : "I2T", - cbTotalTransfer, cbElementaryTransfer, - iIOBufferCur, iIOBufferEnd)); - for (pDesc = pCtl->pFirstDMADesc; pDesc <= pCtl->pLastDMADesc; pDesc += sizeof(BMDMADesc)) - { - BMDMADesc DMADesc; - RTGCPHYS32 pBuffer; - uint32_t cbBuffer; - - if (RT_UNLIKELY(fRedo)) - { - pBuffer = pCtl->pRedoDMABuffer; - cbBuffer = pCtl->cbRedoDMABuffer; - fLastDesc = pCtl->fRedoDMALastDesc; - } - else - { - PDMDevHlpPhysRead(pDevIns, pDesc, &DMADesc, sizeof(BMDMADesc)); - pBuffer = RT_LE2H_U32(DMADesc.pBuffer); - cbBuffer = RT_LE2H_U32(DMADesc.cbBuffer); - fLastDesc = !!(cbBuffer & 0x80000000); - cbBuffer &= 0xfffe; - if (cbBuffer == 0) - cbBuffer = 0x10000; - if (cbBuffer > cbTotalTransfer) - cbBuffer = cbTotalTransfer; - } - - while (RT_UNLIKELY(fRedo) || (cbBuffer && cbTotalTransfer)) - { - if (RT_LIKELY(!fRedo)) - { - dmalen = RT_MIN(cbBuffer, iIOBufferEnd - iIOBufferCur); - Log2(("%s: DMA desc %#010x: addr=%#010x size=%#010x\n", __FUNCTION__, - pDesc, pBuffer, cbBuffer)); - if (uTxDir == PDMBLOCKTXDIR_FROM_DEVICE) - PDMDevHlpPhysWrite(pDevIns, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen); - else - PDMDevHlpPhysRead(pDevIns, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen); - iIOBufferCur += dmalen; - cbTotalTransfer -= dmalen; - cbBuffer -= dmalen; - pBuffer += dmalen; - } - if ( iIOBufferCur == iIOBufferEnd - && (uTxDir == PDMBLOCKTXDIR_TO_DEVICE || cbTotalTransfer)) - { - if (uTxDir == PDMBLOCKTXDIR_FROM_DEVICE && cbElementaryTransfer > cbTotalTransfer) - cbElementaryTransfer = cbTotalTransfer; - - { - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - } - - /* The RESET handler could have cleared the DMA transfer - * state (since we didn't hold the lock until just now - * the guest can continue in parallel). If so, the state - * is already set up so the loop is exited immediately. */ - if (s->iSourceSink != ATAFN_SS_NULL) - { - s->iIOBufferCur = iIOBufferCur; - s->iIOBufferEnd = iIOBufferEnd; - s->cbElementaryTransfer = cbElementaryTransfer; - s->cbTotalTransfer = cbTotalTransfer; - Log2(("%s: calling source/sink function\n", __FUNCTION__)); - fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s); - if (RT_UNLIKELY(fRedo)) - { - pCtl->pFirstDMADesc = pDesc; - pCtl->pRedoDMABuffer = pBuffer; - pCtl->cbRedoDMABuffer = cbBuffer; - pCtl->fRedoDMALastDesc = fLastDesc; - } - else - { - cbTotalTransfer = s->cbTotalTransfer; - cbElementaryTransfer = s->cbElementaryTransfer; - - if (uTxDir == PDMBLOCKTXDIR_TO_DEVICE && cbElementaryTransfer > cbTotalTransfer) - cbElementaryTransfer = cbTotalTransfer; - iIOBufferCur = 0; - iIOBufferEnd = cbElementaryTransfer; - } - pCtl->fRedo = fRedo; - } - else - { - /* This forces the loop to exit immediately. */ - pDesc = pCtl->pLastDMADesc + 1; - } - - PDMCritSectLeave(&pCtl->lock); - if (RT_UNLIKELY(fRedo)) - break; - } - } - - if (RT_UNLIKELY(fRedo)) - break; - - /* end of transfer */ - if (!cbTotalTransfer || fLastDesc) - break; - - { - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - } - - if (!(pCtl->BmDma.u8Cmd & BM_CMD_START) || pCtl->fReset) - { - LogRel(("AHCI ATA: Ctl: ABORT DMA%s\n", pCtl->fReset ? " due to RESET" : "")); - if (!pCtl->fReset) - ataDMATransferStop(s); - /* This forces the loop to exit immediately. */ - pDesc = pCtl->pLastDMADesc + 1; - } - - PDMCritSectLeave(&pCtl->lock); - } - - { - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - } - - if (RT_UNLIKELY(fRedo)) - return; - - if (fLastDesc) - pCtl->BmDma.u8Status &= ~BM_STATUS_DMAING; - s->cbTotalTransfer = cbTotalTransfer; - s->cbElementaryTransfer = cbElementaryTransfer; - s->iIOBufferCur = iIOBufferCur; - s->iIOBufferEnd = iIOBufferEnd; -} - - -/** - * Signal ataWaitForAsyncIOIsIdle that we're idle (if we actually are). - * - * @param pCtl The controller. - */ -static void ataAsyncSignalIdle(PAHCIATACONTROLLER pCtl) -{ - /* - * Take the mutex here and recheck the idle indicator as there might be - * interesting races, like in the ataReset code. - */ - int rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT); AssertRC(rc); - - if ( pCtl->fSignalIdle - && ataAsyncIOIsIdle(pCtl, false /*fStrict*/)) - { - PDMDevHlpAsyncNotificationCompleted(pCtl->pDevInsR3); - RTThreadUserSignal(pCtl->AsyncIOThread); - } - - rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex); AssertRC(rc); -} - - -/** Async I/O thread for an interface. Once upon a time this was readable - * code with several loops and a different semaphore for each purpose. But - * then came the "how can one save the state in the middle of a PIO transfer" - * question. The solution was to use an ASM, which is what's there now. */ -static DECLCALLBACK(int) ataAsyncIOLoop(RTTHREAD ThreadSelf, void *pvUser) -{ - const AHCIATARequest *pReq; - uint64_t u64TS = 0; /* shut up gcc */ - uint64_t uWait; - int rc = VINF_SUCCESS; - PAHCIATACONTROLLER pCtl = (PAHCIATACONTROLLER)pvUser; - AHCIATADevState *s; - - pReq = NULL; - pCtl->fChainedTransfer = false; - while (!pCtl->fShutdown) - { - /* Keep this thread from doing anything as long as EMT is suspended. */ - while (pCtl->fRedoIdle) - { - if (pCtl->fSignalIdle) - ataAsyncSignalIdle(pCtl); - rc = RTSemEventWait(pCtl->SuspendIOSem, RT_INDEFINITE_WAIT); - if (RT_FAILURE(rc) || pCtl->fShutdown) - break; - - pCtl->fRedoIdle = false; - } - - /* Wait for work. */ - if (pReq == NULL) - { - if (pCtl->fSignalIdle) - ataAsyncSignalIdle(pCtl); - rc = RTSemEventWait(pCtl->AsyncIOSem, RT_INDEFINITE_WAIT); - if (RT_FAILURE(rc) || pCtl->fShutdown) - break; - - pReq = ataAsyncIOGetCurrentRequest(pCtl); - } - - if (pReq == NULL) - continue; - - AHCIATAAIO ReqType = pReq->ReqType; - - Log2(("%s: Ctl: state=%d, req=%d\n", __FUNCTION__, pCtl->uAsyncIOState, ReqType)); - if (pCtl->uAsyncIOState != ReqType) - { - /* The new state is not the state that was expected by the normal - * state changes. This is either a RESET/ABORT or there's something - * really strange going on. */ - if ( (pCtl->uAsyncIOState == AHCIATA_AIO_PIO || pCtl->uAsyncIOState == AHCIATA_AIO_DMA) - && (ReqType == AHCIATA_AIO_PIO || ReqType == AHCIATA_AIO_DMA)) - { - /* Incorrect sequence of PIO/DMA states. Dump request queue. */ - ataAsyncIODumpRequests(pCtl); - } - AssertReleaseMsg(ReqType == AHCIATA_AIO_RESET_ASSERTED || ReqType == AHCIATA_AIO_RESET_CLEARED || ReqType == AHCIATA_AIO_ABORT || pCtl->uAsyncIOState == ReqType, ("I/O state inconsistent: state=%d request=%d\n", pCtl->uAsyncIOState, ReqType)); - } - - /* Do our work. */ - { - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - } - - if (pCtl->uAsyncIOState == AHCIATA_AIO_NEW && !pCtl->fChainedTransfer) - { - u64TS = RTTimeNanoTS(); -#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS) - STAM_PROFILE_ADV_START(&pCtl->StatAsyncTime, a); -#endif /* DEBUG || VBOX_WITH_STATISTICS */ - } - - switch (ReqType) - { - case AHCIATA_AIO_NEW: - - pCtl->iAIOIf = pReq->u.t.iIf; - s = &pCtl->aIfs[pCtl->iAIOIf]; - s->cbTotalTransfer = pReq->u.t.cbTotalTransfer; - s->uTxDir = pReq->u.t.uTxDir; - s->iBeginTransfer = pReq->u.t.iBeginTransfer; - s->iSourceSink = pReq->u.t.iSourceSink; - s->iIOBufferEnd = 0; - s->u64CmdTS = u64TS; - - if (s->fATAPI) - { - if (pCtl->fChainedTransfer) - { - /* Only count the actual transfers, not the PIO - * transfer of the ATAPI command bytes. */ - if (s->fDMA) - STAM_REL_COUNTER_INC(&s->StatATAPIDMA); - else - STAM_REL_COUNTER_INC(&s->StatATAPIPIO); - } - } - else - { - if (s->fDMA) - STAM_REL_COUNTER_INC(&s->StatATADMA); - else - STAM_REL_COUNTER_INC(&s->StatATAPIO); - } - - pCtl->fChainedTransfer = false; - - if (s->iBeginTransfer != ATAFN_BT_NULL) - { - Log2(("%s: Ctl: calling begin transfer function\n", __FUNCTION__)); - g_apfnBeginTransFuncs[s->iBeginTransfer](s); - s->iBeginTransfer = ATAFN_BT_NULL; - if (s->uTxDir != PDMBLOCKTXDIR_FROM_DEVICE) - s->iIOBufferEnd = s->cbElementaryTransfer; - } - else - { - s->cbElementaryTransfer = s->cbTotalTransfer; - s->iIOBufferEnd = s->cbTotalTransfer; - } - s->iIOBufferCur = 0; - - if (s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE) - { - if (s->iSourceSink != ATAFN_SS_NULL) - { - bool fRedo; - Log2(("%s: Ctl: calling source/sink function\n", __FUNCTION__)); - fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s); - pCtl->fRedo = fRedo; - if (RT_UNLIKELY(fRedo)) - { - /* Operation failed at the initial transfer, restart - * everything from scratch by resending the current - * request. Occurs very rarely, not worth optimizing. */ - LogRel(("%s: Ctl: redo entire operation\n", __FUNCTION__)); - ataAsyncIOPutRequest(pCtl, pReq); - break; - } - } - else - ataCmdOK(s, 0); - s->iIOBufferEnd = s->cbElementaryTransfer; - - } - - /* Do not go into the transfer phase if RESET is asserted. - * The CritSect is released while waiting for the host OS - * to finish the I/O, thus RESET is possible here. Most - * important: do not change uAsyncIOState. */ - if (pCtl->fReset) - break; - - if (s->fDMA) - { - if (s->cbTotalTransfer) - { - ataSetStatus(s, ATA_STAT_DRQ); - - pCtl->uAsyncIOState = AHCIATA_AIO_DMA; - /* If BMDMA is already started, do the transfer now. */ - if (pCtl->BmDma.u8Cmd & BM_CMD_START) - { - Log2(("%s: Ctl: message to async I/O thread, continuing DMA transfer immediately\n", __FUNCTION__)); - ataAsyncIOPutRequest(pCtl, &ataDMARequest); - } - } - else - { - Assert(s->uTxDir == PDMBLOCKTXDIR_NONE); /* Any transfer which has an initial transfer size of 0 must be marked as such. */ - /* Finish DMA transfer. */ - ataDMATransferStop(s); - ataSetIRQ(s); - pCtl->uAsyncIOState = AHCIATA_AIO_NEW; - } - } - else - { - if (s->cbTotalTransfer) - { - ataPIOTransfer(pCtl); - Assert(!pCtl->fRedo); - if (s->fATAPITransfer || s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE) - ataSetIRQ(s); - - if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE || s->iSourceSink != ATAFN_SS_NULL) - { - /* Write operations and not yet finished transfers - * must be completed in the async I/O thread. */ - pCtl->uAsyncIOState = AHCIATA_AIO_PIO; - } - else - { - /* Finished read operation can be handled inline - * in the end of PIO transfer handling code. Linux - * depends on this, as it waits only briefly for - * devices to become ready after incoming data - * transfer. Cannot find anything in the ATA spec - * that backs this assumption, but as all kernels - * are affected (though most of the time it does - * not cause any harm) this must work. */ - pCtl->uAsyncIOState = AHCIATA_AIO_NEW; - } - } - else - { - Assert(s->uTxDir == PDMBLOCKTXDIR_NONE); /* Any transfer which has an initial transfer size of 0 must be marked as such. */ - /* Finish PIO transfer. */ - ataPIOTransfer(pCtl); - Assert(!pCtl->fRedo); - if (!s->fATAPITransfer) - ataSetIRQ(s); - pCtl->uAsyncIOState = AHCIATA_AIO_NEW; - } - } - break; - - case AHCIATA_AIO_DMA: - { - BMDMAState *bm = &pCtl->BmDma; - s = &pCtl->aIfs[pCtl->iAIOIf]; /* Do not remove or there's an instant crash after loading the saved state */ - ATAFNSS iOriginalSourceSink = (ATAFNSS)s->iSourceSink; /* Used by the hack below, but gets reset by then. */ - - if (s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE) - AssertRelease(bm->u8Cmd & BM_CMD_WRITE); - else - AssertRelease(!(bm->u8Cmd & BM_CMD_WRITE)); - - if (RT_LIKELY(!pCtl->fRedo)) - { - /* The specs say that the descriptor table must not cross a - * 4K boundary. */ - pCtl->pFirstDMADesc = bm->pvAddr; - pCtl->pLastDMADesc = RT_ALIGN_32(bm->pvAddr + 1, _4K) - sizeof(BMDMADesc); - } - ataDMATransfer(pCtl); - - if (RT_UNLIKELY(pCtl->fRedo)) - { - LogRel(("AHCI ATA: Ctl: redo DMA operation\n")); - ataAsyncIOPutRequest(pCtl, &ataDMARequest); - break; - } - - /* The infamous delay IRQ hack. */ - if ( iOriginalSourceSink == ATAFN_SS_WRITE_SECTORS - && s->cbTotalTransfer == 0 - && pCtl->DelayIRQMillies) - { - /* Delay IRQ for writing. Required to get the Win2K - * installation work reliably (otherwise it crashes, - * usually during component install). So far no better - * solution has been found. */ - Log(("%s: delay IRQ hack\n", __FUNCTION__)); - PDMCritSectLeave(&pCtl->lock); - RTThreadSleep(pCtl->DelayIRQMillies); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - } - - ataUnsetStatus(s, ATA_STAT_DRQ); - Assert(!pCtl->fChainedTransfer); - Assert(s->iSourceSink == ATAFN_SS_NULL); - if (s->fATAPITransfer) - { - s->uATARegNSector = (s->uATARegNSector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; - Log2(("%s: Ctl: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector)); - s->fATAPITransfer = false; - } - ataSetIRQ(s); - pCtl->uAsyncIOState = AHCIATA_AIO_NEW; - break; - } - - case AHCIATA_AIO_PIO: - s = &pCtl->aIfs[pCtl->iAIOIf]; /* Do not remove or there's an instant crash after loading the saved state */ - - if (s->iSourceSink != ATAFN_SS_NULL) - { - bool fRedo; - Log2(("%s: Ctl: calling source/sink function\n", __FUNCTION__)); - fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s); - pCtl->fRedo = fRedo; - if (RT_UNLIKELY(fRedo)) - { - LogRel(("AHCI ATA: Ctl: redo PIO operation\n")); - ataAsyncIOPutRequest(pCtl, &ataPIORequest); - break; - } - s->iIOBufferCur = 0; - s->iIOBufferEnd = s->cbElementaryTransfer; - } - else - { - /* Continue a previously started transfer. */ - ataUnsetStatus(s, ATA_STAT_BUSY); - ataSetStatus(s, ATA_STAT_READY); - } - - /* It is possible that the drives on this controller get RESET - * during the above call to the source/sink function. If that's - * the case, don't restart the transfer and don't finish it the - * usual way. RESET handling took care of all that already. - * Most important: do not change uAsyncIOState. */ - if (pCtl->fReset) - break; - - if (s->cbTotalTransfer) - { - ataPIOTransfer(pCtl); - ataSetIRQ(s); - - if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE || s->iSourceSink != ATAFN_SS_NULL) - { - /* Write operations and not yet finished transfers - * must be completed in the async I/O thread. */ - pCtl->uAsyncIOState = AHCIATA_AIO_PIO; - } - else - { - /* Finished read operation can be handled inline - * in the end of PIO transfer handling code. Linux - * depends on this, as it waits only briefly for - * devices to become ready after incoming data - * transfer. Cannot find anything in the ATA spec - * that backs this assumption, but as all kernels - * are affected (though most of the time it does - * not cause any harm) this must work. */ - pCtl->uAsyncIOState = AHCIATA_AIO_NEW; - } - } - else - { - /* Finish PIO transfer. */ - ataPIOTransfer(pCtl); - if ( !pCtl->fChainedTransfer - && !s->fATAPITransfer - && s->uTxDir != PDMBLOCKTXDIR_FROM_DEVICE) - { - ataSetIRQ(s); - } - pCtl->uAsyncIOState = AHCIATA_AIO_NEW; - } - break; - - case AHCIATA_AIO_RESET_ASSERTED: - pCtl->uAsyncIOState = AHCIATA_AIO_RESET_CLEARED; - ataPIOTransferStop(&pCtl->aIfs[0]); - ataPIOTransferStop(&pCtl->aIfs[1]); - /* Do not change the DMA registers, they are not affected by the - * ATA controller reset logic. It should be sufficient to issue a - * new command, which is now possible as the state is cleared. */ - break; - - case AHCIATA_AIO_RESET_CLEARED: - pCtl->uAsyncIOState = AHCIATA_AIO_NEW; - pCtl->fReset = false; - LogRel(("AHCI ATA: Ctl: finished processing RESET\n")); - for (uint32_t i = 0; i < RT_ELEMENTS(pCtl->aIfs); i++) - { - if (pCtl->aIfs[i].fATAPI) - ataSetStatusValue(&pCtl->aIfs[i], 0); /* NOTE: READY is _not_ set */ - else - ataSetStatusValue(&pCtl->aIfs[i], ATA_STAT_READY | ATA_STAT_SEEK); - ataSetSignature(&pCtl->aIfs[i]); - } - break; - - case AHCIATA_AIO_ABORT: - /* Abort the current command no matter what. There cannot be - * any command activity on the other drive otherwise using - * one thread per controller wouldn't work at all. */ - s = &pCtl->aIfs[pReq->u.a.iIf]; - - pCtl->uAsyncIOState = AHCIATA_AIO_NEW; - /* Do not change the DMA registers, they are not affected by the - * ATA controller reset logic. It should be sufficient to issue a - * new command, which is now possible as the state is cleared. */ - if (pReq->u.a.fResetDrive) - { - ataResetDevice(s); - ataExecuteDeviceDiagnosticSS(s); - } - else - { - ataPIOTransferStop(s); - ataUnsetStatus(s, ATA_STAT_BUSY | ATA_STAT_DRQ | ATA_STAT_SEEK | ATA_STAT_ERR); - ataSetStatus(s, ATA_STAT_READY); - ataSetIRQ(s); - } - break; - - default: - AssertMsgFailed(("Undefined async I/O state %d\n", pCtl->uAsyncIOState)); - } - - ataAsyncIORemoveCurrentRequest(pCtl, ReqType); - pReq = ataAsyncIOGetCurrentRequest(pCtl); - - if (pCtl->uAsyncIOState == AHCIATA_AIO_NEW && !pCtl->fChainedTransfer) - { -#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS) - STAM_PROFILE_ADV_STOP(&pCtl->StatAsyncTime, a); -#endif /* DEBUG || VBOX_WITH_STATISTICS */ - - u64TS = RTTimeNanoTS() - u64TS; - uWait = u64TS / 1000; - Log(("%s: Ctl: LUN#%d finished I/O transaction in %d microseconds\n", __FUNCTION__, pCtl->aIfs[pCtl->iAIOIf].iLUN, (uint32_t)(uWait))); - /* Mark command as finished. */ - pCtl->aIfs[pCtl->iAIOIf].u64CmdTS = 0; - - /* - * Release logging of command execution times depends on the - * command type. ATAPI commands often take longer (due to CD/DVD - * spin up time etc.) so the threshold is different. - */ - if (pCtl->aIfs[pCtl->iAIOIf].uATARegCommand != ATA_PACKET) - { - if (uWait > 8 * 1000 * 1000) - { - /* - * Command took longer than 8 seconds. This is close - * enough or over the guest's command timeout, so place - * an entry in the release log to allow tracking such - * timing errors (which are often caused by the host). - */ - LogRel(("AHCI ATA: execution time for ATA command %#04x was %d seconds\n", pCtl->aIfs[pCtl->iAIOIf].uATARegCommand, uWait / (1000 * 1000))); - } - } - else - { - if (uWait > 20 * 1000 * 1000) - { - /* - * Command took longer than 20 seconds. This is close - * enough or over the guest's command timeout, so place - * an entry in the release log to allow tracking such - * timing errors (which are often caused by the host). - */ - LogRel(("AHCI ATA: execution time for ATAPI command %#04x was %d seconds\n", pCtl->aIfs[pCtl->iAIOIf].aATAPICmd[0], uWait / (1000 * 1000))); - } - } - -#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS) - if (uWait < pCtl->StatAsyncMinWait || !pCtl->StatAsyncMinWait) - pCtl->StatAsyncMinWait = uWait; - if (uWait > pCtl->StatAsyncMaxWait) - pCtl->StatAsyncMaxWait = uWait; - - STAM_COUNTER_ADD(&pCtl->StatAsyncTimeUS, uWait); - STAM_COUNTER_INC(&pCtl->StatAsyncOps); -#endif /* DEBUG || VBOX_WITH_STATISTICS */ - } - - PDMCritSectLeave(&pCtl->lock); - } - - /* Signal the ultimate idleness. */ - if (pCtl->fSignalIdle) - PDMDevHlpAsyncNotificationCompleted(pCtl->pDevInsR3); - RTThreadUserSignal(ThreadSelf); - - /* Do not destroy request mutex yet, still needed for proper shutdown. */ - pCtl->fShutdown = false; - - Log2(("%s: Ctl: return %Rrc\n", __FUNCTION__, rc)); - return rc; -} - -#endif /* IN_RING3 */ - -static uint32_t ataBMDMACmdReadB(PAHCIATACONTROLLER pCtl, uint32_t addr) -{ - uint32_t val = pCtl->BmDma.u8Cmd; - Log2(("%s: addr=%#06x val=%#04x\n", __FUNCTION__, addr, val)); - return val; -} - - -static void ataBMDMACmdWriteB(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val) -{ - Log2(("%s: addr=%#06x val=%#04x\n", __FUNCTION__, addr, val)); - if (!(val & BM_CMD_START)) - { - pCtl->BmDma.u8Status &= ~BM_STATUS_DMAING; - pCtl->BmDma.u8Cmd = val & (BM_CMD_START | BM_CMD_WRITE); - } - else - { -#ifdef IN_RING3 - /* Check whether the guest OS wants to change DMA direction in - * mid-flight. Not allowed, according to the AHCI specs. */ - Assert(!(pCtl->BmDma.u8Status & BM_STATUS_DMAING) || !((val ^ pCtl->BmDma.u8Cmd) & 0x04)); - pCtl->BmDma.u8Status |= BM_STATUS_DMAING; - pCtl->BmDma.u8Cmd = val & (BM_CMD_START | BM_CMD_WRITE); - - /* Do not continue DMA transfers while the RESET line is asserted. */ - if (pCtl->fReset) - { - Log2(("%s: Ctl: suppressed continuing DMA transfer as RESET is active\n", __FUNCTION__)); - return; - } - - /* Do not start DMA transfers if there's a PIO transfer going on. */ - if (!pCtl->aIfs[pCtl->iSelectedIf].fDMA) - return; - - if (pCtl->aIfs[pCtl->iAIOIf].uATARegStatus & ATA_STAT_DRQ) - { - Log2(("%s: Ctl: message to async I/O thread, continuing DMA transfer\n", __FUNCTION__)); - ataAsyncIOPutRequest(pCtl, &ataDMARequest); - } -#else /* !IN_RING3 */ - AssertMsgFailed(("DMA START handling is too complicated for GC\n")); -#endif /* IN_RING3 */ - } -} - -static uint32_t ataBMDMAStatusReadB(PAHCIATACONTROLLER pCtl, uint32_t addr) -{ - uint32_t val = pCtl->BmDma.u8Status; - Log2(("%s: addr=%#06x val=%#04x\n", __FUNCTION__, addr, val)); - return val; -} - -static void ataBMDMAStatusWriteB(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val) -{ - Log2(("%s: addr=%#06x val=%#04x\n", __FUNCTION__, addr, val)); - pCtl->BmDma.u8Status = (val & (BM_STATUS_D0DMA | BM_STATUS_D1DMA)) - | (pCtl->BmDma.u8Status & BM_STATUS_DMAING) - | (pCtl->BmDma.u8Status & ~val & (BM_STATUS_ERROR | BM_STATUS_INT)); -} - -static uint32_t ataBMDMAAddrReadL(PAHCIATACONTROLLER pCtl, uint32_t addr) -{ - uint32_t val = (uint32_t)pCtl->BmDma.pvAddr; - Log2(("%s: addr=%#06x val=%#010x\n", __FUNCTION__, addr, val)); - return val; -} - -static void ataBMDMAAddrWriteL(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val) -{ - Log2(("%s: addr=%#06x val=%#010x\n", __FUNCTION__, addr, val)); - pCtl->BmDma.pvAddr = val & ~3; -} - -static void ataBMDMAAddrWriteLowWord(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val) -{ - Log2(("%s: addr=%#06x val=%#010x\n", __FUNCTION__, addr, val)); - pCtl->BmDma.pvAddr = (pCtl->BmDma.pvAddr & 0xFFFF0000) | RT_LOWORD(val & ~3); - -} - -static void ataBMDMAAddrWriteHighWord(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val) -{ - Log2(("%s: addr=%#06x val=%#010x\n", __FUNCTION__, addr, val)); - pCtl->BmDma.pvAddr = (RT_LOWORD(val) << 16) | RT_LOWORD(pCtl->BmDma.pvAddr); -} - -#define VAL(port, size) ( ((port) & 7) | ((size) << 3) ) - -/** - * Port I/O Handler for bus master DMA IN operations. - * @see FNIOMIOPORTIN for details. - */ -int ataControllerBMDMAIOPortRead(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t *pu32, unsigned cb) -{ - int rc; - - rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_READ); - if (rc != VINF_SUCCESS) - return rc; - switch (VAL(Port, cb)) - { - case VAL(0, 1): *pu32 = ataBMDMACmdReadB(pCtl, Port); break; - case VAL(0, 2): *pu32 = ataBMDMACmdReadB(pCtl, Port); break; - case VAL(2, 1): *pu32 = ataBMDMAStatusReadB(pCtl, Port); break; - case VAL(2, 2): *pu32 = ataBMDMAStatusReadB(pCtl, Port); break; - case VAL(4, 4): *pu32 = ataBMDMAAddrReadL(pCtl, Port); break; - default: - AssertMsgFailed(("%s: Unsupported read from port %x size=%d\n", __FUNCTION__, Port, cb)); - PDMCritSectLeave(&pCtl->lock); - return VERR_IOM_IOPORT_UNUSED; - } - PDMCritSectLeave(&pCtl->lock); - return rc; -} - -/** - * Port I/O Handler for bus master DMA OUT operations. - * @see FNIOMIOPORTOUT for details. - */ -int ataControllerBMDMAIOPortWrite(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t u32, unsigned cb) -{ - int rc; - - rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_WRITE); - if (rc != VINF_SUCCESS) - return rc; - switch (VAL(Port, cb)) - { - case VAL(0, 1): -#ifndef IN_RING3 - if (u32 & BM_CMD_START) - { - rc = VINF_IOM_R3_IOPORT_WRITE; - break; - } -#endif /* !IN_RING3 */ - ataBMDMACmdWriteB(pCtl, Port, u32); - break; - case VAL(2, 1): ataBMDMAStatusWriteB(pCtl, Port, u32); break; - case VAL(4, 4): ataBMDMAAddrWriteL(pCtl, Port, u32); break; - case VAL(4, 2): ataBMDMAAddrWriteLowWord(pCtl, Port, u32); break; - case VAL(6, 2): ataBMDMAAddrWriteHighWord(pCtl, Port, u32); break; - default: AssertMsgFailed(("%s: Unsupported write to port %x size=%d val=%x\n", __FUNCTION__, Port, cb, u32)); break; - } - PDMCritSectLeave(&pCtl->lock); - return rc; -} - -#undef VAL - - -#ifdef IN_RING3 -#if 0 - -/** - * Callback function for mapping an PCI I/O region. - * - * @return VBox status code. - * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance. - * @param iRegion The region number. - * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an - * I/O port, else it's a physical address. - * This address is *NOT* relative to pci_mem_base like earlier! - * @param enmType One of the PCI_ADDRESS_SPACE_* values. - */ -static DECLCALLBACK(int) ataBMDMAIORangeMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType) -{ - PCIATAState *pThis = PCIDEV_2_PCIATASTATE(pPciDev); - int rc = VINF_SUCCESS; - Assert(enmType == PCI_ADDRESS_SPACE_IO); - Assert(iRegion == 4); - AssertMsg(RT_ALIGN(GCPhysAddress, 8) == GCPhysAddress, ("Expected 8 byte alignment. GCPhysAddress=%#x\n", GCPhysAddress)); - - /* Register the port range. */ - for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++) - { - int rc2 = PDMDevHlpIOPortRegister(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress + i * 8, 8, - (RTHCPTR)i, ataBMDMAIOPortWrite, ataBMDMAIOPortRead, NULL, NULL, "ATA Bus Master DMA"); - AssertRC(rc2); - if (rc2 < rc) - rc = rc2; - - if (pThis->fGCEnabled) - { - rc2 = PDMDevHlpIOPortRegisterGC(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress + i * 8, 8, - (RTGCPTR)i, "ataBMDMAIOPortWrite", "ataBMDMAIOPortRead", NULL, NULL, "ATA Bus Master DMA"); - AssertRC(rc2); - if (rc2 < rc) - rc = rc2; - } - if (pThis->fR0Enabled) - { - rc2 = PDMDevHlpIOPortRegisterR0(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress + i * 8, 8, - (RTR0PTR)i, "ataBMDMAIOPortWrite", "ataBMDMAIOPortRead", NULL, NULL, "ATA Bus Master DMA"); - AssertRC(rc2); - if (rc2 < rc) - rc = rc2; - } - } - return rc; -} -#endif - -/** - * Reset the controller to an initial state. - * - * @returns VBox status. - * @param pDevIns The device instance data. - */ -void ataControllerReset(PAHCIATACONTROLLER pCtl) -{ - pCtl->iSelectedIf = 0; - pCtl->iAIOIf = 0; - pCtl->BmDma.u8Cmd = 0; - /* Report that both drives present on the bus are in DMA mode. This - * pretends that there is a BIOS that has set it up. Normal reset - * default is 0x00. */ - pCtl->BmDma.u8Status = (pCtl->aIfs[0].pDrvBase != NULL ? BM_STATUS_D0DMA : 0) - | (pCtl->aIfs[1].pDrvBase != NULL ? BM_STATUS_D1DMA : 0); - pCtl->BmDma.pvAddr = 0; - - pCtl->fReset = true; - pCtl->fRedo = false; - pCtl->fRedoIdle = false; - ataAsyncIOClearRequests(pCtl); - Log2(("%s: Ctl: message to async I/O thread, reset controller\n", __FUNCTION__)); - ataAsyncIOPutRequest(pCtl, &ataResetARequest); - ataAsyncIOPutRequest(pCtl, &ataResetCRequest); - if (!ataWaitForAsyncIOIsIdle(pCtl, 30000)) - AssertReleaseMsgFailed(("Async I/O thread busy after reset\n")); - - for (uint32_t i = 0; i < RT_ELEMENTS(pCtl->aIfs); i++) - ataResetDevice(&pCtl->aIfs[i]); -} - -#if 0 -/* -=-=-=-=-=- AHCIATADevState::IBase -=-=-=-=-=- */ - -/** - * @interface_method_impl{PDMIBASE,pfnQueryInterface} - */ -static DECLCALLBACK(void *) ataQueryInterface(PPDMIBASE pInterface, const char *pszIID) -{ - ATADevState *pIf = PDMIBASE_2_ATASTATE(pInterface); - PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pIf->IBase); - PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pIf->IPort); - PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pIf->IMountNotify); - return NULL; -} -#endif -#endif /* IN_RING3 */ - - -/* -=-=-=-=-=- Wrappers -=-=-=-=-=- */ - -/** - * Port I/O Handler for primary port range OUT operations. - * @see FNIOMIOPORTOUT for details. - */ -int ataControllerIOPortWrite1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t u32, unsigned cb) -{ - int rc = VINF_SUCCESS; - - rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_WRITE); - if (rc != VINF_SUCCESS) - return rc; - if (cb == 1) - rc = ataIOPortWriteU8(pCtl, Port, u32); - else if (Port == pCtl->IOPortBase1) - { - Assert(cb == 2 || cb == 4); - rc = ataDataWrite(pCtl, Port, cb, (const uint8_t *)&u32); - } - else - AssertMsgFailed(("ataIOPortWrite1: unsupported write to port %x val=%x size=%d\n", Port, u32, cb)); - PDMCritSectLeave(&pCtl->lock); - return rc; -} - - -/** - * Port I/O Handler for primary port range IN operations. - * @see FNIOMIOPORTIN for details. - */ -int ataControllerIOPortRead1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t *pu32, unsigned cb) -{ - int rc = VINF_SUCCESS; - - rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_READ); - if (rc != VINF_SUCCESS) - return rc; - if (cb == 1) - { - rc = ataIOPortReadU8(pCtl, Port, pu32); - } - else if (Port == pCtl->IOPortBase1) - { - Assert(cb == 2 || cb == 4); - rc = ataDataRead(pCtl, Port, cb, (uint8_t *)pu32); - if (cb == 2) - *pu32 &= 0xffff; - } - else - { - AssertMsgFailed(("ataIOPortRead1: unsupported read from port %x size=%d\n", Port, cb)); - rc = VERR_IOM_IOPORT_UNUSED; - } - PDMCritSectLeave(&pCtl->lock); - return rc; -} - -#ifndef IN_RING0 /** @todo do this in ring-0 as well. */ -/** - * Port I/O Handler for primary port range IN string operations. - * @see FNIOMIOPORTINSTRING for details. - */ -int ataControllerIOPortReadStr1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb) -{ - int rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_READ); - if (rc != VINF_SUCCESS) - return rc; - if (Port == pCtl->IOPortBase1) - { - uint32_t cTransAvailable, cTransfer = *pcTransfer, cbTransfer; - RTGCPTR GCDst = *pGCPtrDst; - AHCIATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf]; - Assert(cb == 2 || cb == 4); - - cTransAvailable = (s->iIOBufferPIODataEnd - s->iIOBufferPIODataStart) / cb; -#ifndef IN_RING3 - /* Deal with the unlikely case where no data (or not enough for the read length operation) is available; go back to ring 3. */ - if (!cTransAvailable) - { - PDMCritSectLeave(&pCtl->lock); - return VINF_IOM_R3_IOPORT_READ; - } - /* The last transfer unit cannot be handled in GC, as it involves thread communication. */ - cTransAvailable--; -#endif /* !IN_RING3 */ - /* Do not handle the dummy transfer stuff here, leave it to the single-word transfers. - * They are not performance-critical and generally shouldn't occur at all. */ - if (cTransAvailable > cTransfer) - cTransAvailable = cTransfer; - cbTransfer = cTransAvailable * cb; - - PPDMDEVINS pDevIns = pCtl->CTX_SUFF(pDevIns); - rc = PGMPhysSimpleDirtyWriteGCPtr(PDMDevHlpGetVMCPU(pDevIns), GCDst, s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart, cbTransfer); -#ifndef IN_RING3 - /* Paranoia. */ - if (RT_FAILURE(rc)) - { - PDMCritSectLeave(&pCtl->lock); - AssertFailed(); - return VINF_IOM_R3_IOPORT_READ; - } -#else - Assert(rc == VINF_SUCCESS); -#endif - - if (cbTransfer) - Log3(("%s: addr=%#x val=%.*Rhxs\n", __FUNCTION__, Port, cbTransfer, s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart)); - s->iIOBufferPIODataStart += cbTransfer; - *pGCPtrDst = (RTGCPTR)((RTGCUINTPTR)GCDst + cbTransfer); - *pcTransfer = cTransfer - cTransAvailable; -#ifdef IN_RING3 - if (s->iIOBufferPIODataStart >= s->iIOBufferPIODataEnd) - ataPIOTransferFinish(pCtl, s); -#endif /* IN_RING3 */ - } - PDMCritSectLeave(&pCtl->lock); - return rc; -} - - -/** - * Port I/O Handler for primary port range OUT string operations. - * @see FNIOMIOPORTOUTSTRING for details. - */ -int ataControllerIOPortWriteStr1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb) -{ - int rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_WRITE); - if (rc != VINF_SUCCESS) - return rc; - if (Port == pCtl->IOPortBase1) - { - uint32_t cTransAvailable, cTransfer = *pcTransfer, cbTransfer; - RTGCPTR GCSrc = *pGCPtrSrc; - AHCIATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf]; - Assert(cb == 2 || cb == 4); - - cTransAvailable = (s->iIOBufferPIODataEnd - s->iIOBufferPIODataStart) / cb; -#ifndef IN_RING3 - /* Deal with the unlikely case where no data (or not enough for the read length operation) is available; go back to ring 3. */ - if (!cTransAvailable) - { - PDMCritSectLeave(&pCtl->lock); - return VINF_IOM_R3_IOPORT_WRITE; - } - /* The last transfer unit cannot be handled in GC, as it involves thread communication. */ - cTransAvailable--; -#endif /* !IN_RING3 */ - /* Do not handle the dummy transfer stuff here, leave it to the single-word transfers. - * They are not performance-critical and generally shouldn't occur at all. */ - if (cTransAvailable > cTransfer) - cTransAvailable = cTransfer; - cbTransfer = cTransAvailable * cb; - - PPDMDEVINS pDevIns = pCtl->CTX_SUFF(pDevIns); - rc = PGMPhysSimpleReadGCPtr(PDMDevHlpGetVMCPU(pDevIns), s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart, GCSrc, cbTransfer); -#ifndef IN_RING3 - /* Paranoia. */ - if (RT_FAILURE(rc)) - { - PDMCritSectLeave(&pCtl->lock); - AssertFailed(); - return VINF_IOM_R3_IOPORT_WRITE; - } -#else - Assert(rc == VINF_SUCCESS); -#endif - - if (cbTransfer) - Log3(("%s: addr=%#x val=%.*Rhxs\n", __FUNCTION__, Port, cbTransfer, s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart)); - s->iIOBufferPIODataStart += cbTransfer; - *pGCPtrSrc = (RTGCPTR)((RTGCUINTPTR)GCSrc + cbTransfer); - *pcTransfer = cTransfer - cTransAvailable; -#ifdef IN_RING3 - if (s->iIOBufferPIODataStart >= s->iIOBufferPIODataEnd) - ataPIOTransferFinish(pCtl, s); -#endif /* IN_RING3 */ - } - PDMCritSectLeave(&pCtl->lock); - return rc; -} -#endif /* !IN_RING0 */ - -/** - * Port I/O Handler for secondary port range OUT operations. - * @see FNIOMIOPORTOUT for details. - */ -int ataControllerIOPortWrite2(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t u32, unsigned cb) -{ - int rc; - - if (cb != 1) - return VINF_SUCCESS; - rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_WRITE); - if (rc != VINF_SUCCESS) - return rc; - rc = ataControlWrite(pCtl, Port, u32); - PDMCritSectLeave(&pCtl->lock); - return rc; -} - - -/** - * Port I/O Handler for secondary port range IN operations. - * @see FNIOMIOPORTIN for details. - */ -int ataControllerIOPortRead2(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t *pu32, unsigned cb) -{ - int rc; - - if (cb != 1) - return VERR_IOM_IOPORT_UNUSED; - - rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_READ); - if (rc != VINF_SUCCESS) - return rc; - *pu32 = ataStatusRead(pCtl, Port); - PDMCritSectLeave(&pCtl->lock); - return VINF_SUCCESS; -} - -#ifdef IN_RING3 - -/** - * Waits for all async I/O threads to complete whatever they - * are doing at the moment. - * - * @returns true on success. - * @returns false when one or more threads is still processing. - * @param pThis Pointer to the instance data. - * @param cMillies How long to wait (total). - */ -static bool ataWaitForAllAsyncIOIsIdle(PAHCIATACONTROLLER pCtl, RTMSINTERVAL cMillies) -{ - uint64_t u64Start; - PPDMDEVINS pDevIns = pCtl->CTXALLSUFF(pDevIns); - bool fAllIdle = false; - - /* - * Wait for any pending async operation to finish - */ - u64Start = RTTimeMilliTS(); - for (;;) - { - /* Check all async I/O threads. */ - fAllIdle = true; - - fAllIdle &= ataAsyncIOIsIdle(pCtl, false); - if (!fAllIdle) - break; - - if ( fAllIdle - || RTTimeMilliTS() - u64Start >= cMillies) - break; - - /* Sleep for a bit. */ - RTThreadSleep(100); - } - - if (!fAllIdle) - LogRel(("AHCI ATA: Ctl is still executing, DevSel=%d AIOIf=%d CmdIf0=%#04x CmdIf1=%#04x\n", - pCtl->iSelectedIf, pCtl->iAIOIf, - pCtl->aIfs[0].uATARegCommand, pCtl->aIfs[1].uATARegCommand)); - - return fAllIdle; -} - - -DECLINLINE(void) ataRelocBuffer(PPDMDEVINS pDevIns, AHCIATADevState *s) -{ - if (s->pbIOBufferR3) - s->pbIOBufferRC = MMHyperR3ToRC(PDMDevHlpGetVM(pDevIns), s->pbIOBufferR3); -} - - -/** - * @copydoc FNPDMDEVRELOCATE - */ -void ataControllerRelocate(PAHCIATACONTROLLER pCtl, RTGCINTPTR offDelta) -{ - PPDMDEVINS pDevIns = pCtl->CTXALLSUFF(pDevIns); - - pCtl->pDevInsRC += offDelta; - pCtl->aIfs[0].pDevInsRC += offDelta; - pCtl->aIfs[0].pControllerRC += offDelta; - ataRelocBuffer(pDevIns, &pCtl->aIfs[0]); - pCtl->aIfs[1].pDevInsRC += offDelta; - pCtl->aIfs[1].pControllerRC += offDelta; - ataRelocBuffer(pDevIns, &pCtl->aIfs[1]); -} - - -/** - * Destroy a controller instance. - * - * Most VM resources are freed by the VM. This callback is provided so that any non-VM - * resources can be freed correctly. - * - * @param pCtl The controller instance. - */ -int ataControllerDestroy(PAHCIATACONTROLLER pCtl) -{ - int rc; - - Log(("%s:\n", __FUNCTION__)); - - /* - * Terminate the async helper thread and wait for it to finish up. - */ - if (pCtl->AsyncIOThread != NIL_RTTHREAD) - { - ASMAtomicWriteU32(&pCtl->fShutdown, true); - rc = RTSemEventSignal(pCtl->AsyncIOSem); - AssertRC(rc); - rc = RTSemEventSignal(pCtl->SuspendIOSem); - AssertRC(rc); - - rc = RTThreadWait(pCtl->AsyncIOThread, 30000 /* 30 s*/, NULL); - if (RT_SUCCESS(rc)) - pCtl->AsyncIOThread = NIL_RTTHREAD; - else - LogRel(("PIIX3 ATA Dtor: Ctl/irq=%u is still executing, DevSel=%d AIOIf=%d CmdIf0=%#04x CmdIf1=%#04x rc=%Rrc\n", - pCtl->irq, pCtl->iSelectedIf, pCtl->iAIOIf, - pCtl->aIfs[0].uATARegCommand, pCtl->aIfs[1].uATARegCommand, rc)); - } - - /* - * Now the request mutexes are no longer needed. Free resources. - */ - if (pCtl->AsyncIORequestMutex != NIL_RTSEMMUTEX) - { - RTSemMutexDestroy(pCtl->AsyncIORequestMutex); - pCtl->AsyncIORequestMutex = NIL_RTSEMMUTEX; - } - if (pCtl->AsyncIOSem != NIL_RTSEMEVENT) - { - RTSemEventDestroy(pCtl->AsyncIOSem); - pCtl->AsyncIOSem = NIL_RTSEMEVENT; - } - if (pCtl->SuspendIOSem != NIL_RTSEMEVENT) - { - RTSemEventDestroy(pCtl->SuspendIOSem); - pCtl->SuspendIOSem = NIL_RTSEMEVENT; - } - - /* try one final time */ - if (pCtl->AsyncIOThread != NIL_RTTHREAD) - { - rc = RTThreadWait(pCtl->AsyncIOThread, 1 /*ms*/, NULL); - if (RT_SUCCESS(rc)) - { - pCtl->AsyncIOThread = NIL_RTTHREAD; - LogRel(("AHCI ATA Dtor: Ctl/irq=%u actually completed.\n", pCtl->irq)); - } - } - - return VINF_SUCCESS; -} - -/** - * Detach notification. - * - * The DVD drive has been unplugged. - * - * @param pDevIns The device instance. - * @param fMaster True if the master is detached - * false for the slave - */ -void ataControllerDetach(PAHCIATACONTROLLER pCtl, bool fMaster) -{ - AHCIATADevState *pIf; - - /* - * Locate the controller and stuff. - */ - pIf = &pCtl->aIfs[fMaster ? 0 : 1]; - - /* - * Zero some important members. - */ - pIf->pDrvBase = NULL; - pIf->pDrvBlock = NULL; - pIf->pDrvBlockBios = NULL; - pIf->pDrvMount = NULL; -} - -/** - * Configure a LUN. - * - * @returns VBox status code. - * @param pDevIns The device instance. - * @param pIf The ATA unit state. - */ -static int ataConfigLun(PPDMDEVINS pDevIns, AHCIATADevState *pIf) -{ - int rc; - PDMBLOCKTYPE enmType; - - /* - * Query Block, Bios and Mount interfaces. - */ - pIf->pDrvBlock = PDMIBASE_QUERY_INTERFACE(pIf->pDrvBase, PDMIBLOCK); - if (!pIf->pDrvBlock) - { - AssertMsgFailed(("Configuration error: LUN#%d hasn't a block interface!\n", pIf->iLUN)); - return VERR_PDM_MISSING_INTERFACE; - } - - /** @todo implement the BIOS invisible code path. */ - pIf->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(pIf->pDrvBase, PDMIBLOCKBIOS); - if (!pIf->pDrvBlockBios) - { - AssertMsgFailed(("Configuration error: LUN#%d hasn't a block BIOS interface!\n", pIf->iLUN)); - return VERR_PDM_MISSING_INTERFACE; - } - pIf->pDrvMount = PDMIBASE_QUERY_INTERFACE(pIf->pDrvBase, PDMIMOUNT); - - /* - * Validate type. - */ - enmType = pIf->pDrvBlock->pfnGetType(pIf->pDrvBlock); - if ( enmType != PDMBLOCKTYPE_CDROM - && enmType != PDMBLOCKTYPE_DVD - && enmType != PDMBLOCKTYPE_HARD_DISK) - { - AssertMsgFailed(("Configuration error: LUN#%d isn't a disk or cd/dvd-rom. enmType=%d\n", pIf->iLUN, enmType)); - return VERR_PDM_UNSUPPORTED_BLOCK_TYPE; - } - if ( ( enmType == PDMBLOCKTYPE_DVD - || enmType == PDMBLOCKTYPE_CDROM) - && !pIf->pDrvMount) - { - AssertMsgFailed(("Internal error: cdrom without a mountable interface, WTF???!\n")); - return VERR_INTERNAL_ERROR; - } - pIf->fATAPI = enmType == PDMBLOCKTYPE_DVD || enmType == PDMBLOCKTYPE_CDROM; - pIf->fATAPIPassthrough = pIf->fATAPI ? (pIf->pDrvBlock->pfnSendCmd != NULL) : false; - - /* - * Allocate I/O buffer. - */ - PVM pVM = PDMDevHlpGetVM(pDevIns); - if (pIf->cbIOBuffer) - { - /* Buffer is (probably) already allocated. Validate the fields, - * because memory corruption can also overwrite pIf->cbIOBuffer. */ - if (pIf->fATAPI) - AssertRelease(pIf->cbIOBuffer == _128K); - else - AssertRelease(pIf->cbIOBuffer == ATA_MAX_MULT_SECTORS * 512); - Assert(pIf->pbIOBufferR3); - Assert(pIf->pbIOBufferR0 == MMHyperR3ToR0(pVM, pIf->pbIOBufferR3)); - Assert(pIf->pbIOBufferRC == MMHyperR3ToRC(pVM, pIf->pbIOBufferR3)); - } - else - { - if (pIf->fATAPI) - pIf->cbIOBuffer = _128K; - else - pIf->cbIOBuffer = ATA_MAX_MULT_SECTORS * 512; - Assert(!pIf->pbIOBufferR3); - rc = MMR3HyperAllocOnceNoRel(pVM, pIf->cbIOBuffer, 0, MM_TAG_PDM_DEVICE_USER, (void **)&pIf->pbIOBufferR3); - if (RT_FAILURE(rc)) - return VERR_NO_MEMORY; - pIf->pbIOBufferR0 = MMHyperR3ToR0(pVM, pIf->pbIOBufferR3); - pIf->pbIOBufferRC = MMHyperR3ToRC(pVM, pIf->pbIOBufferR3); - } - - /* - * Init geometry (only for non-CD/DVD media). - */ - if (pIf->fATAPI) - { - pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 2048; - pIf->PCHSGeometry.cCylinders = 0; /* dummy */ - pIf->PCHSGeometry.cHeads = 0; /* dummy */ - pIf->PCHSGeometry.cSectors = 0; /* dummy */ - LogRel(("AHCI ATA: LUN#%d: CD/DVD, total number of sectors %Ld, passthrough %s\n", pIf->iLUN, pIf->cTotalSectors, (pIf->fATAPIPassthrough ? "enabled" : "disabled"))); - } - else - { - pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 512; - rc = pIf->pDrvBlockBios->pfnGetPCHSGeometry(pIf->pDrvBlockBios, - &pIf->PCHSGeometry); - if (rc == VERR_PDM_MEDIA_NOT_MOUNTED) - { - pIf->PCHSGeometry.cCylinders = 0; - pIf->PCHSGeometry.cHeads = 16; /*??*/ - pIf->PCHSGeometry.cSectors = 63; /*??*/ - } - else if (rc == VERR_PDM_GEOMETRY_NOT_SET) - { - pIf->PCHSGeometry.cCylinders = 0; /* autodetect marker */ - rc = VINF_SUCCESS; - } - AssertRC(rc); - - if ( pIf->PCHSGeometry.cCylinders == 0 - || pIf->PCHSGeometry.cHeads == 0 - || pIf->PCHSGeometry.cSectors == 0 - ) - { - uint64_t cCylinders = pIf->cTotalSectors / (16 * 63); - pIf->PCHSGeometry.cCylinders = RT_MAX(RT_MIN(cCylinders, 16383), 1); - pIf->PCHSGeometry.cHeads = 16; - pIf->PCHSGeometry.cSectors = 63; - /* Set the disk geometry information. */ - rc = pIf->pDrvBlockBios->pfnSetPCHSGeometry(pIf->pDrvBlockBios, - &pIf->PCHSGeometry); - } - LogRel(("AHCI ATA: LUN#%d: disk, PCHS=%u/%u/%u, total number of sectors %Ld\n", pIf->iLUN, pIf->PCHSGeometry.cCylinders, pIf->PCHSGeometry.cHeads, pIf->PCHSGeometry.cSectors, pIf->cTotalSectors)); - } - return VINF_SUCCESS; -} - -/** - * Attach command. - * - * This is called when we change block driver for the DVD drive. - * - * @returns VBox status code. - * @param pDevIns The device instance. - * @param iLUN The logical unit which is being detached. - */ -int ataControllerAttach(PAHCIATACONTROLLER pCtl, PPDMIBASE pDrvBase, bool fMaster) -{ - AHCIATADevState *pIf; - int rc = VINF_SUCCESS; - - /* - * Locate the controller and stuff. - */ - pIf = &pCtl->aIfs[fMaster ? 0 : 1]; - - /* the usual paranoia */ - AssertRelease(!pIf->pDrvBase); - AssertRelease(!pIf->pDrvBlock); - Assert(ATADEVSTATE_2_CONTROLLER(pIf) == pCtl); - - /* - * Try attach the block device and get the interfaces, - * required as well as optional. - */ - - pIf->pDrvBase = pDrvBase; - if (pDrvBase) - { - rc = ataConfigLun(pCtl->pDevInsR3, pIf); - AssertRC(rc); - - if (RT_FAILURE(rc)) - { - pIf->pDrvBase = NULL; - pIf->pDrvBlock = NULL; - } - } - - return rc; -} - - -/** - * Resume notification. - * - * @returns VBox status. - * @param pDevIns The device instance data. - */ -void ataControllerResume(PAHCIATACONTROLLER pCtl) -{ - int rc; - - Log(("%s:\n", __FUNCTION__)); - if (pCtl->fRedo && pCtl->fRedoIdle) - { - rc = RTSemEventSignal(pCtl->SuspendIOSem); - AssertRC(rc); - } - - return; -} - - -/** - * Tests if the controller is idle, leaving the PDM notifications on if busy. - * - * @returns true if idle, false if idle. - * @param pCtl the controller instance. - */ -bool ataControllerIsIdle(PAHCIATACONTROLLER pCtl) -{ - ASMAtomicWriteBool(&pCtl->fSignalIdle, true); - if (ataAsyncIOIsIdle(pCtl, false /*fStrict*/)) - { - ASMAtomicWriteBool(&pCtl->fSignalIdle, false); - return true; - } - return false; -} - -/** - * Saves a state of the ATA device. - * - * @returns VBox status code. - * @param pCtl Controller instance. - * @param pSSM The handle to save the state to. - */ -int ataControllerSaveExec(PAHCIATACONTROLLER pCtl, PSSMHANDLE pSSM) -{ - SSMR3PutU32(pSSM, ATA_CTL_SAVED_STATE_VERSION); - SSMR3PutU8(pSSM, pCtl->iSelectedIf); - SSMR3PutU8(pSSM, pCtl->iAIOIf); - SSMR3PutU8(pSSM, pCtl->uAsyncIOState); - SSMR3PutBool(pSSM, pCtl->fChainedTransfer); - SSMR3PutBool(pSSM, pCtl->fReset); - SSMR3PutBool(pSSM, pCtl->fRedo); - SSMR3PutBool(pSSM, pCtl->fRedoIdle); - SSMR3PutBool(pSSM, pCtl->fRedoDMALastDesc); - SSMR3PutMem(pSSM, &pCtl->BmDma, sizeof(pCtl->BmDma)); - SSMR3PutGCPhys32(pSSM, pCtl->pFirstDMADesc); - SSMR3PutGCPhys32(pSSM, pCtl->pLastDMADesc); - SSMR3PutGCPhys32(pSSM, pCtl->pRedoDMABuffer); - SSMR3PutU32(pSSM, pCtl->cbRedoDMABuffer); - - for (uint32_t j = 0; j < RT_ELEMENTS(pCtl->aIfs); j++) - { - SSMR3PutBool(pSSM, pCtl->aIfs[j].fLBA48); - SSMR3PutBool(pSSM, pCtl->aIfs[j].fATAPI); - SSMR3PutBool(pSSM, pCtl->aIfs[j].fIrqPending); - SSMR3PutU8(pSSM, pCtl->aIfs[j].cMultSectors); - SSMR3PutU32(pSSM, pCtl->aIfs[j].PCHSGeometry.cCylinders); - SSMR3PutU32(pSSM, pCtl->aIfs[j].PCHSGeometry.cHeads); - SSMR3PutU32(pSSM, pCtl->aIfs[j].PCHSGeometry.cSectors); - SSMR3PutU32(pSSM, pCtl->aIfs[j].cSectorsPerIRQ); - SSMR3PutU64(pSSM, pCtl->aIfs[j].cTotalSectors); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegFeature); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegFeatureHOB); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegError); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegNSector); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegNSectorHOB); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegSector); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegSectorHOB); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegLCyl); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegLCylHOB); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegHCyl); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegHCylHOB); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegSelect); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegStatus); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegCommand); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegDevCtl); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uATATransferMode); - SSMR3PutU8(pSSM, pCtl->aIfs[j].uTxDir); - SSMR3PutU8(pSSM, pCtl->aIfs[j].iBeginTransfer); - SSMR3PutU8(pSSM, pCtl->aIfs[j].iSourceSink); - SSMR3PutBool(pSSM, pCtl->aIfs[j].fDMA); - SSMR3PutBool(pSSM, pCtl->aIfs[j].fATAPITransfer); - SSMR3PutU32(pSSM, pCtl->aIfs[j].cbTotalTransfer); - SSMR3PutU32(pSSM, pCtl->aIfs[j].cbElementaryTransfer); - SSMR3PutU32(pSSM, pCtl->aIfs[j].iIOBufferCur); - SSMR3PutU32(pSSM, pCtl->aIfs[j].iIOBufferEnd); - SSMR3PutU32(pSSM, pCtl->aIfs[j].iIOBufferPIODataStart); - SSMR3PutU32(pSSM, pCtl->aIfs[j].iIOBufferPIODataEnd); - SSMR3PutU32(pSSM, pCtl->aIfs[j].iATAPILBA); - SSMR3PutU32(pSSM, pCtl->aIfs[j].cbATAPISector); - SSMR3PutMem(pSSM, &pCtl->aIfs[j].aATAPICmd, sizeof(pCtl->aIfs[j].aATAPICmd)); - SSMR3PutMem(pSSM, &pCtl->aIfs[j].abATAPISense, sizeof(pCtl->aIfs[j].abATAPISense)); - SSMR3PutU8(pSSM, pCtl->aIfs[j].cNotifiedMediaChange); - SSMR3PutU32(pSSM, pCtl->aIfs[j].MediaEventStatus); - - PDMLED Led; - memset(&Led, 0, sizeof(PDMLED)); - SSMR3PutMem(pSSM, &Led, sizeof(PDMLED)); - SSMR3PutU32(pSSM, pCtl->aIfs[j].cbIOBuffer); - if (pCtl->aIfs[j].cbIOBuffer) - SSMR3PutMem(pSSM, pCtl->aIfs[j].CTX_SUFF(pbIOBuffer), pCtl->aIfs[j].cbIOBuffer); - else - Assert(pCtl->aIfs[j].CTX_SUFF(pbIOBuffer) == NULL); - } - - return SSMR3PutU32(pSSM, ~0); /* sanity/terminator */ -} - - -/** - * Loads a saved ATA device state. - * - * @returns VBox status code. - * @param pDevIns The device instance. - * @param pSSM The handle to the saved state. - */ -int ataControllerLoadExec(PAHCIATACONTROLLER pCtl, PSSMHANDLE pSSM) -{ - int rc; - uint32_t u32Version; - uint32_t u32; - - /* Test for correct version. */ - rc = SSMR3GetU32(pSSM, &u32Version); - AssertRCReturn(rc, rc); - - if ( u32Version != ATA_CTL_SAVED_STATE_VERSION - && u32Version != ATA_CTL_SAVED_STATE_VERSION_WITHOUT_FULL_SENSE - && u32Version != ATA_CTL_SAVED_STATE_VERSION_WITHOUT_EVENT_STATUS) - { - AssertMsgFailed(("u32Version=%d\n", u32Version)); - return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION; - } - - /* integrity check */ - if (!ataAsyncIOIsIdle(pCtl, false)) - { - AssertMsgFailed(("Async I/O for controller is active\n")); - return VERR_SSM_DATA_UNIT_FORMAT_CHANGED; - } - - SSMR3GetU8(pSSM, &pCtl->iSelectedIf); - SSMR3GetU8(pSSM, &pCtl->iAIOIf); - SSMR3GetU8(pSSM, &pCtl->uAsyncIOState); - SSMR3GetBool(pSSM, &pCtl->fChainedTransfer); - SSMR3GetBool(pSSM, (bool *)&pCtl->fReset); - SSMR3GetBool(pSSM, (bool *)&pCtl->fRedo); - SSMR3GetBool(pSSM, (bool *)&pCtl->fRedoIdle); - SSMR3GetBool(pSSM, (bool *)&pCtl->fRedoDMALastDesc); - SSMR3GetMem(pSSM, &pCtl->BmDma, sizeof(pCtl->BmDma)); - SSMR3GetGCPhys32(pSSM, &pCtl->pFirstDMADesc); - SSMR3GetGCPhys32(pSSM, &pCtl->pLastDMADesc); - SSMR3GetGCPhys32(pSSM, &pCtl->pRedoDMABuffer); - SSMR3GetU32(pSSM, &pCtl->cbRedoDMABuffer); - - for (uint32_t j = 0; j < RT_ELEMENTS(pCtl->aIfs); j++) - { - SSMR3GetBool(pSSM, &pCtl->aIfs[j].fLBA48); - SSMR3GetBool(pSSM, &pCtl->aIfs[j].fATAPI); - SSMR3GetBool(pSSM, &pCtl->aIfs[j].fIrqPending); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].cMultSectors); - SSMR3GetU32(pSSM, &pCtl->aIfs[j].PCHSGeometry.cCylinders); - SSMR3GetU32(pSSM, &pCtl->aIfs[j].PCHSGeometry.cHeads); - SSMR3GetU32(pSSM, &pCtl->aIfs[j].PCHSGeometry.cSectors); - SSMR3GetU32(pSSM, &pCtl->aIfs[j].cSectorsPerIRQ); - SSMR3GetU64(pSSM, &pCtl->aIfs[j].cTotalSectors); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegFeature); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegFeatureHOB); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegError); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegNSector); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegNSectorHOB); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegSector); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegSectorHOB); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegLCyl); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegLCylHOB); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegHCyl); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegHCylHOB); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegSelect); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegStatus); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegCommand); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegDevCtl); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATATransferMode); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].uTxDir); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].iBeginTransfer); - SSMR3GetU8(pSSM, &pCtl->aIfs[j].iSourceSink); - SSMR3GetBool(pSSM, &pCtl->aIfs[j].fDMA); - SSMR3GetBool(pSSM, &pCtl->aIfs[j].fATAPITransfer); - SSMR3GetU32(pSSM, &pCtl->aIfs[j].cbTotalTransfer); - SSMR3GetU32(pSSM, &pCtl->aIfs[j].cbElementaryTransfer); - SSMR3GetU32(pSSM, &pCtl->aIfs[j].iIOBufferCur); - SSMR3GetU32(pSSM, &pCtl->aIfs[j].iIOBufferEnd); - SSMR3GetU32(pSSM, &pCtl->aIfs[j].iIOBufferPIODataStart); - SSMR3GetU32(pSSM, &pCtl->aIfs[j].iIOBufferPIODataEnd); - SSMR3GetU32(pSSM, &pCtl->aIfs[j].iATAPILBA); - SSMR3GetU32(pSSM, &pCtl->aIfs[j].cbATAPISector); - SSMR3GetMem(pSSM, &pCtl->aIfs[j].aATAPICmd, sizeof(pCtl->aIfs[j].aATAPICmd)); - if (u32Version > ATA_CTL_SAVED_STATE_VERSION_WITHOUT_FULL_SENSE) - { - SSMR3GetMem(pSSM, &pCtl->aIfs[j].abATAPISense, sizeof(pCtl->aIfs[j].abATAPISense)); - } - else - { - uint8_t uATAPISenseKey, uATAPIASC; - memset(pCtl->aIfs[j].abATAPISense, '\0', sizeof(pCtl->aIfs[j].abATAPISense)); - pCtl->aIfs[j].abATAPISense[0] = 0x70 | (1 << 7); - pCtl->aIfs[j].abATAPISense[7] = 10; - SSMR3GetU8(pSSM, &uATAPISenseKey); - SSMR3GetU8(pSSM, &uATAPIASC); - pCtl->aIfs[j].abATAPISense[2] = uATAPISenseKey & 0x0f; - pCtl->aIfs[j].abATAPISense[12] = uATAPIASC; - } - /** @todo triple-check this hack after passthrough is working */ - SSMR3GetU8(pSSM, &pCtl->aIfs[j].cNotifiedMediaChange); - if (u32Version > ATA_CTL_SAVED_STATE_VERSION_WITHOUT_EVENT_STATUS) - SSMR3GetU32(pSSM, (uint32_t*)&pCtl->aIfs[j].MediaEventStatus); - else - pCtl->aIfs[j].MediaEventStatus = ATA_EVENT_STATUS_UNCHANGED; - - PDMLED Led; - SSMR3GetMem(pSSM, &Led, sizeof(PDMLED)); - SSMR3GetU32(pSSM, &pCtl->aIfs[j].cbIOBuffer); - if (pCtl->aIfs[j].cbIOBuffer) - { - if (pCtl->aIfs[j].CTX_SUFF(pbIOBuffer)) - SSMR3GetMem(pSSM, pCtl->aIfs[j].CTX_SUFF(pbIOBuffer), pCtl->aIfs[j].cbIOBuffer); - else - { - LogRel(("AHCI ATA: No buffer for %d\n", j)); - if (SSMR3HandleGetAfter(pSSM) != SSMAFTER_DEBUG_IT) - return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("No buffer for %d"), j); - - /* skip the buffer if we're loading for the debugger / animator. */ - uint8_t u8Ignored; - size_t cbLeft = pCtl->aIfs[j].cbIOBuffer; - while (cbLeft-- > 0) - SSMR3GetU8(pSSM, &u8Ignored); - } - } - else - Assert(pCtl->aIfs[j].CTX_SUFF(pbIOBuffer) == NULL); - } - - rc = SSMR3GetU32(pSSM, &u32); - if (RT_FAILURE(rc)) - return rc; - if (u32 != ~0U) - { - AssertMsgFailed(("u32=%#x expected ~0\n", u32)); - rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED; - return rc; - } - - return VINF_SUCCESS; -} - -int ataControllerInit(PPDMDEVINS pDevIns, PAHCIATACONTROLLER pCtl, - PPDMIMEDIANOTIFY pMediaNotify, - unsigned iLUNMaster, PPDMIBASE pDrvBaseMaster, PPDMLED pLedMaster, - PSTAMCOUNTER pStatBytesReadMaster, PSTAMCOUNTER pStatBytesWrittenMaster, - const char *pszSerialNumberMaster, const char *pszFirmwareRevisionMaster, - const char *pszModelNumberMaster, const char *pszInquiryVendorIdMaster, - const char *pszInquiryProductIdMaster, const char *pszInquiryRevisionMaster, - bool fNonRotationalMaster, - unsigned iLUNSlave, PPDMIBASE pDrvBaseSlave, PPDMLED pLedSlave, - PSTAMCOUNTER pStatBytesReadSlave, PSTAMCOUNTER pStatBytesWrittenSlave, - const char *pszSerialNumberSlave, const char *pszFirmwareRevisionSlave, - const char *pszModelNumberSlave, const char *pszInquiryVendorIdSlave, - const char *pszInquiryProductIdSlave, const char *pszInquiryRevisionSlave, - bool fNonRotationalSlave, - uint32_t *pcbSSMState, const char *szName) -{ - int rc; - - AssertMsg(pcbSSMState, ("pcbSSMState is invalid\n")); - - pCtl->pDevInsR3 = pDevIns; - pCtl->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns); - pCtl->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); - pCtl->pMediaNotify = pMediaNotify; - pCtl->AsyncIOSem = NIL_RTSEMEVENT; - pCtl->SuspendIOSem = NIL_RTSEMEVENT; - pCtl->AsyncIORequestMutex = NIL_RTSEMMUTEX; - pCtl->AsyncIOThread = NIL_RTTHREAD; - - for (uint32_t j = 0; j < RT_ELEMENTS(pCtl->aIfs); j++) - { - pCtl->aIfs[j].iLUN = j == 0 ? iLUNMaster : iLUNSlave; - pCtl->aIfs[j].pDevInsR3 = pDevIns; - pCtl->aIfs[j].pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns); - pCtl->aIfs[j].pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); - pCtl->aIfs[j].pControllerR3 = pCtl; - pCtl->aIfs[j].pControllerR0 = MMHyperR3ToR0(PDMDevHlpGetVM(pDevIns), pCtl); - pCtl->aIfs[j].pControllerRC = MMHyperR3ToRC(PDMDevHlpGetVM(pDevIns), pCtl); - pCtl->aIfs[j].pLed = j == 0 ? pLedMaster : pLedSlave; - pCtl->aIfs[j].pStatBytesRead = j == 0 ? pStatBytesReadMaster : pStatBytesReadSlave; - pCtl->aIfs[j].pStatBytesWritten = j == 0 ? pStatBytesWrittenMaster : pStatBytesWrittenSlave; - pCtl->aIfs[j].pszSerialNumber = j == 0 ? pszSerialNumberMaster : pszSerialNumberSlave; - pCtl->aIfs[j].pszFirmwareRevision = j == 0 ? pszFirmwareRevisionMaster : pszFirmwareRevisionSlave; - pCtl->aIfs[j].pszModelNumber = j == 0 ? pszModelNumberMaster : pszModelNumberSlave; - pCtl->aIfs[j].pszInquiryVendorId = j == 0 ? pszInquiryVendorIdMaster : pszInquiryVendorIdSlave; - pCtl->aIfs[j].pszInquiryProductId = j == 0 ? pszInquiryProductIdMaster : pszInquiryProductIdSlave; - pCtl->aIfs[j].pszInquiryRevision = j == 0 ? pszInquiryRevisionMaster : pszInquiryRevisionSlave; - pCtl->aIfs[j].fNonRotational = j == 0 ? fNonRotationalMaster : fNonRotationalSlave; - } - - /* Initialize per-controller critical section */ - rc = PDMDevHlpCritSectInit(pDevIns, &pCtl->lock, RT_SRC_POS, "%s", szName); - if (RT_FAILURE(rc)) - return PDMDEV_SET_ERROR(pDevIns, rc, N_("AHCI ATA: cannot initialize critical section")); - - /* - * Attach the units. - */ - uint32_t cbTotalBuffer = 0; - - /* - * Start the worker thread. - */ - pCtl->uAsyncIOState = AHCIATA_AIO_NEW; - rc = RTSemEventCreate(&pCtl->AsyncIOSem); - AssertRCReturn(rc, rc); - rc = RTSemEventCreate(&pCtl->SuspendIOSem); - AssertRCReturn(rc, rc); - rc = RTSemMutexCreate(&pCtl->AsyncIORequestMutex); - AssertRCReturn(rc, rc); - ataAsyncIOClearRequests(pCtl); - rc = RTThreadCreateF(&pCtl->AsyncIOThread, ataAsyncIOLoop, (void *)pCtl, 128*1024, - RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "AHCI-ATA-%u", pCtl->irq); - AssertRCReturn(rc, rc); - Assert(pCtl->AsyncIOThread != NIL_RTTHREAD && pCtl->AsyncIOSem != NIL_RTSEMEVENT && pCtl->SuspendIOSem != NIL_RTSEMEVENT && pCtl->AsyncIORequestMutex != NIL_RTSEMMUTEX); - Log(("%s: controller AIO thread id %#x; sem %p susp_sem %p mutex %p\n", __FUNCTION__, pCtl->AsyncIOThread, pCtl->AsyncIOSem, pCtl->SuspendIOSem, pCtl->AsyncIORequestMutex)); - - for (uint32_t j = 0; j < RT_ELEMENTS(pCtl->aIfs); j++) - { - /* - * Try attach the block device and get the interfaces, - * required as well as optional. - */ - AHCIATADevState *pIf = &pCtl->aIfs[j]; - - pIf->pDrvBase = (j == 0) ? pDrvBaseMaster : pDrvBaseSlave; - -#if 0 - rc = PDMDevHlpDriverAttach(pDevIns, pIf->iLUN, &pIf->IBase, &pIf->pDrvBase, s_apszDescs[j]); - if (RT_SUCCESS(rc)) -#endif - if (pIf->pDrvBase) - rc = ataConfigLun(pDevIns, pIf); - else - { - pIf->pDrvBase = NULL; - pIf->pDrvBlock = NULL; - pIf->cbIOBuffer = 0; - pIf->pbIOBufferR3 = NULL; - pIf->pbIOBufferR0 = NIL_RTR0PTR; - pIf->pbIOBufferRC = NIL_RTRCPTR; - LogRel(("AHCI ATA: LUN#%d: no unit\n", pIf->iLUN)); - } - cbTotalBuffer += pIf->cbIOBuffer; - } - - *pcbSSMState = cbTotalBuffer; - - /* - * Initialize the device state. - */ - ataControllerReset(pCtl); - - return VINF_SUCCESS; -} -#endif /* IN_RING3 */ -#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */ diff --git a/src/VBox/Devices/Storage/ATAController.h b/src/VBox/Devices/Storage/ATAController.h deleted file mode 100644 index 12addeb5..00000000 --- a/src/VBox/Devices/Storage/ATAController.h +++ /dev/null @@ -1,563 +0,0 @@ -/* $Id: ATAController.h $ */ -/** @file - * DevATA, DevAHCI - Shared ATA/ATAPI controller types. - */ - -/* - * Copyright (C) 2006-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. - */ - -#ifndef ___Storage_ATAController_h -#define ___Storage_ATAController_h - -/******************************************************************************* -* Header Files * -*******************************************************************************/ -#include <VBox/vmm/pdmdev.h> -#ifdef IN_RING3 -# include <iprt/semaphore.h> -# include <iprt/thread.h> -#endif /* IN_RING3 */ -#include <iprt/critsect.h> -#include <VBox/vmm/stam.h> - -#include "PIIX3ATABmDma.h" -#include "ide.h" - - -/******************************************************************************* -* Defined Constants And Macros * -*******************************************************************************/ -/** - * Maximum number of sectors to transfer in a READ/WRITE MULTIPLE request. - * Set to 1 to disable multi-sector read support. According to the ATA - * specification this must be a power of 2 and it must fit in an 8 bit - * value. Thus the only valid values are 1, 2, 4, 8, 16, 32, 64 and 128. - */ -#define ATA_MAX_MULT_SECTORS 128 - -/** - * Fastest PIO mode supported by the drive. - */ -#define ATA_PIO_MODE_MAX 4 -/** - * Fastest MDMA mode supported by the drive. - */ -#define ATA_MDMA_MODE_MAX 2 -/** - * Fastest UDMA mode supported by the drive. - */ -#define ATA_UDMA_MODE_MAX 6 - -/** ATAPI sense info size. */ -#define ATAPI_SENSE_SIZE 64 - -/** The maximum number of release log entries per device. */ -#define MAX_LOG_REL_ERRORS 1024 - -/* MediaEventStatus */ -#define ATA_EVENT_STATUS_UNCHANGED 0 /**< medium event status not changed */ -#define ATA_EVENT_STATUS_MEDIA_NEW 1 /**< new medium inserted */ -#define ATA_EVENT_STATUS_MEDIA_REMOVED 2 /**< medium removed */ -#define ATA_EVENT_STATUS_MEDIA_CHANGED 3 /**< medium was removed + new medium was inserted */ -#define ATA_EVENT_STATUS_MEDIA_EJECT_REQUESTED 4 /**< medium eject requested (eject button pressed) */ - - -/******************************************************************************* -* Structures and Typedefs * -*******************************************************************************/ -typedef struct AHCIATADevState { - /** Flag indicating whether the current command uses LBA48 mode. */ - bool fLBA48; - /** Flag indicating whether this drive implements the ATAPI command set. */ - bool fATAPI; - /** Set if this interface has asserted the IRQ. */ - bool fIrqPending; - /** Currently configured number of sectors in a multi-sector transfer. */ - uint8_t cMultSectors; - /** PCHS disk geometry. */ - PDMMEDIAGEOMETRY PCHSGeometry; - /** Total number of sectors on this disk. */ - uint64_t cTotalSectors; - /** Number of sectors to transfer per IRQ. */ - uint32_t cSectorsPerIRQ; - - /** ATA/ATAPI register 1: feature (write-only). */ - uint8_t uATARegFeature; - /** ATA/ATAPI register 1: feature, high order byte. */ - uint8_t uATARegFeatureHOB; - /** ATA/ATAPI register 1: error (read-only). */ - uint8_t uATARegError; - /** ATA/ATAPI register 2: sector count (read/write). */ - uint8_t uATARegNSector; - /** ATA/ATAPI register 2: sector count, high order byte. */ - uint8_t uATARegNSectorHOB; - /** ATA/ATAPI register 3: sector (read/write). */ - uint8_t uATARegSector; - /** ATA/ATAPI register 3: sector, high order byte. */ - uint8_t uATARegSectorHOB; - /** ATA/ATAPI register 4: cylinder low (read/write). */ - uint8_t uATARegLCyl; - /** ATA/ATAPI register 4: cylinder low, high order byte. */ - uint8_t uATARegLCylHOB; - /** ATA/ATAPI register 5: cylinder high (read/write). */ - uint8_t uATARegHCyl; - /** ATA/ATAPI register 5: cylinder high, high order byte. */ - uint8_t uATARegHCylHOB; - /** ATA/ATAPI register 6: select drive/head (read/write). */ - uint8_t uATARegSelect; - /** ATA/ATAPI register 7: status (read-only). */ - uint8_t uATARegStatus; - /** ATA/ATAPI register 7: command (write-only). */ - uint8_t uATARegCommand; - /** ATA/ATAPI drive control register (write-only). */ - uint8_t uATARegDevCtl; - - /** Currently active transfer mode (MDMA/UDMA) and speed. */ - uint8_t uATATransferMode; - /** Current transfer direction. */ - uint8_t uTxDir; - /** Index of callback for begin transfer. */ - uint8_t iBeginTransfer; - /** Index of callback for source/sink of data. */ - uint8_t iSourceSink; - /** Flag indicating whether the current command transfers data in DMA mode. */ - bool fDMA; - /** Set to indicate that ATAPI transfer semantics must be used. */ - bool fATAPITransfer; - - /** Total ATA/ATAPI transfer size, shared PIO/DMA. */ - uint32_t cbTotalTransfer; - /** Elementary ATA/ATAPI transfer size, shared PIO/DMA. */ - uint32_t cbElementaryTransfer; - /** Current read/write buffer position, shared PIO/DMA. */ - uint32_t iIOBufferCur; - /** First element beyond end of valid buffer content, shared PIO/DMA. */ - uint32_t iIOBufferEnd; - - /** ATA/ATAPI current PIO read/write transfer position. Not shared with DMA for safety reasons. */ - uint32_t iIOBufferPIODataStart; - /** ATA/ATAPI current PIO read/write transfer end. Not shared with DMA for safety reasons. */ - uint32_t iIOBufferPIODataEnd; - - /** ATAPI current LBA position. */ - uint32_t iATAPILBA; - /** ATAPI current sector size. */ - uint32_t cbATAPISector; - /** ATAPI current command. */ - uint8_t aATAPICmd[ATAPI_PACKET_SIZE]; - /** ATAPI sense data. */ - uint8_t abATAPISense[ATAPI_SENSE_SIZE]; - /** HACK: Countdown till we report a newly unmounted drive as mounted. */ - uint8_t cNotifiedMediaChange; - /** The same for GET_EVENT_STATUS for mechanism */ - volatile uint32_t MediaEventStatus; - - /** The status LED state for this drive. */ - R3PTRTYPE(PPDMLED) pLed; -#if HC_ARCH_BITS == 64 - uint32_t uAlignment3; -#endif - - /** Size of I/O buffer. */ - uint32_t cbIOBuffer; - /** Pointer to the I/O buffer. */ - R3PTRTYPE(uint8_t *) pbIOBufferR3; - /** Pointer to the I/O buffer. */ - R0PTRTYPE(uint8_t *) pbIOBufferR0; - /** Pointer to the I/O buffer. */ - RCPTRTYPE(uint8_t *) pbIOBufferRC; - - RTRCPTR Aligmnent1; /**< Align the statistics at an 8-byte boundary. */ - - /* - * No data that is part of the saved state after this point!!!!! - */ - - /* Release statistics: number of ATA DMA commands. */ - STAMCOUNTER StatATADMA; - /* Release statistics: number of ATA PIO commands. */ - STAMCOUNTER StatATAPIO; - /* Release statistics: number of ATAPI PIO commands. */ - STAMCOUNTER StatATAPIDMA; - /* Release statistics: number of ATAPI PIO commands. */ - STAMCOUNTER StatATAPIPIO; -#ifdef VBOX_INSTRUMENT_DMA_WRITES - /* Release statistics: number of DMA sector writes and the time spent. */ - STAMPROFILEADV StatInstrVDWrites; -#endif - - /** Statistics: number of read operations and the time spent reading. */ - STAMPROFILEADV StatReads; - /** Statistics: number of bytes read. */ - R3PTRTYPE(PSTAMCOUNTER) pStatBytesRead; -#if HC_ARCH_BITS == 64 - uint64_t uAlignment4; -#endif - /** Statistics: number of write operations and the time spent writing. */ - STAMPROFILEADV StatWrites; - /** Statistics: number of bytes written. */ - R3PTRTYPE(PSTAMCOUNTER) pStatBytesWritten; -#if HC_ARCH_BITS == 64 - uint64_t uAlignment5; -#endif - /** Statistics: number of flush operations and the time spend flushing. */ - STAMPROFILE StatFlushes; - - /** The serial number to use for IDENTIFY DEVICE commands. */ - R3PTRTYPE(const char *) pszSerialNumber; - /** The firmware revision to use for IDENTIFY DEVICE commands. */ - R3PTRTYPE(const char *) pszFirmwareRevision; - /** The model number to use for IDENTIFY DEVICE commands. */ - R3PTRTYPE(const char *) pszModelNumber; - /** The vendor identification string for SCSI INQUIRY commands. */ - R3PTRTYPE(const char *) pszInquiryVendorId; - /** The product identification string for SCSI INQUIRY commands. */ - R3PTRTYPE(const char *) pszInquiryProductId; - /** The revision string for SCSI INQUIRY commands. */ - R3PTRTYPE(const char *) pszInquiryRevision; - /** Mark the drive as having a non-rotational medium (i.e. as a SSD). */ - bool fNonRotational; - /** Enable passing through commands directly to the ATAPI drive. */ - bool fATAPIPassthrough; - /** Number of errors we've reported to the release log. - * This is to prevent flooding caused by something going horribly wrong. - * this value against MAX_LOG_REL_ERRORS in places likely to cause floods - * like the ones we currently seeing on the linux smoke tests (2006-11-10). */ - uint32_t cErrors; - /** Timestamp of last started command. 0 if no command pending. */ - uint64_t u64CmdTS; - - /** Pointer to the attached driver's base interface. */ - R3PTRTYPE(PPDMIBASE) pDrvBase; - /** Pointer to the attached driver's block interface. */ - R3PTRTYPE(PPDMIBLOCK) pDrvBlock; - /** Pointer to the attached driver's block bios interface. */ - R3PTRTYPE(PPDMIBLOCKBIOS) pDrvBlockBios; - /** Pointer to the attached driver's mount interface. - * This is NULL if the driver isn't a removable unit. */ - R3PTRTYPE(PPDMIMOUNT) pDrvMount; - /** The base interface. */ - PDMIBASE IBase; - /** The block port interface. */ - PDMIBLOCKPORT IPort; - /** The mount notify interface. */ - PDMIMOUNTNOTIFY IMountNotify; - /** The LUN #. */ - RTUINT iLUN; -#if HC_ARCH_BITS == 64 - RTUINT Alignment2; /**< Align pDevInsR3 correctly. */ -#endif - /** Pointer to device instance. */ - PPDMDEVINSR3 pDevInsR3; - /** Pointer to controller instance. */ - R3PTRTYPE(struct AHCIATACONTROLLER *) pControllerR3; - /** Pointer to device instance. */ - PPDMDEVINSR0 pDevInsR0; - /** Pointer to controller instance. */ - R0PTRTYPE(struct AHCIATACONTROLLER *) pControllerR0; - /** Pointer to device instance. */ - PPDMDEVINSRC pDevInsRC; - /** Pointer to controller instance. */ - RCPTRTYPE(struct AHCIATACONTROLLER *) pControllerRC; -} AHCIATADevState; - - -typedef struct AHCIATATransferRequest -{ - uint8_t iIf; - uint8_t iBeginTransfer; - uint8_t iSourceSink; - uint32_t cbTotalTransfer; - uint8_t uTxDir; -} AHCIATATransferRequest; - - -typedef struct AHCIATAAbortRequest -{ - uint8_t iIf; - bool fResetDrive; -} AHCIATAAbortRequest; - - -typedef enum -{ - /** Begin a new transfer. */ - AHCIATA_AIO_NEW = 0, - /** Continue a DMA transfer. */ - AHCIATA_AIO_DMA, - /** Continue a PIO transfer. */ - AHCIATA_AIO_PIO, - /** Reset the drives on current controller, stop all transfer activity. */ - AHCIATA_AIO_RESET_ASSERTED, - /** Reset the drives on current controller, resume operation. */ - AHCIATA_AIO_RESET_CLEARED, - /** Abort the current transfer of a particular drive. */ - AHCIATA_AIO_ABORT -} AHCIATAAIO; - - -typedef struct AHCIATARequest -{ - AHCIATAAIO ReqType; - union - { - AHCIATATransferRequest t; - AHCIATAAbortRequest a; - } u; -} AHCIATARequest; - - -typedef struct AHCIATACONTROLLER -{ - /** The base of the first I/O Port range. */ - RTIOPORT IOPortBase1; - /** The base of the second I/O Port range. (0 if none) */ - RTIOPORT IOPortBase2; - /** The assigned IRQ. */ - RTUINT irq; - /** Access critical section */ - PDMCRITSECT lock; - - /** Selected drive. */ - uint8_t iSelectedIf; - /** The interface on which to handle async I/O. */ - uint8_t iAIOIf; - /** The state of the async I/O thread. */ - uint8_t uAsyncIOState; - /** Flag indicating whether the next transfer is part of the current command. */ - bool fChainedTransfer; - /** Set when the reset processing is currently active on this controller. */ - bool fReset; - /** Flag whether the current transfer needs to be redone. */ - bool fRedo; - /** Flag whether the redo suspend has been finished. */ - bool fRedoIdle; - /** Flag whether the DMA operation to be redone is the final transfer. */ - bool fRedoDMALastDesc; - /** The BusMaster DMA state. */ - BMDMAState BmDma; - /** Pointer to first DMA descriptor. */ - RTGCPHYS32 pFirstDMADesc; - /** Pointer to last DMA descriptor. */ - RTGCPHYS32 pLastDMADesc; - /** Pointer to current DMA buffer (for redo operations). */ - RTGCPHYS32 pRedoDMABuffer; - /** Size of current DMA buffer (for redo operations). */ - uint32_t cbRedoDMABuffer; - - /** The ATA/ATAPI interfaces of this controller. */ - AHCIATADevState aIfs[2]; - - /** Pointer to device instance. */ - PPDMDEVINSR3 pDevInsR3; - /** Pointer to device instance. */ - PPDMDEVINSR0 pDevInsR0; - /** Pointer to device instance. */ - PPDMDEVINSRC pDevInsRC; - - /** Set when the destroying the device instance and the thread must exit. */ - uint32_t volatile fShutdown; - /** The async I/O thread handle. NIL_RTTHREAD if no thread. */ - RTTHREAD AsyncIOThread; - /** The event semaphore the thread is waiting on for requests. */ - RTSEMEVENT AsyncIOSem; - /** The request queue for the AIO thread. One element is always unused. */ - AHCIATARequest aAsyncIORequests[4]; - /** The position at which to insert a new request for the AIO thread. */ - uint8_t AsyncIOReqHead; - /** The position at which to get a new request for the AIO thread. */ - uint8_t AsyncIOReqTail; - /** Whether to call RTThreadUserSignal and PDMDevHlpAsyncNotificationCompleted - * when idle. Before setting this, call RTThreadUserReset. */ - bool volatile fSignalIdle; - uint8_t Alignment3[1]; /**< Explicit padding of the 1 byte gap. */ - /** Magic delay before triggering interrupts in DMA mode. */ - uint32_t DelayIRQMillies; - /** The mutex protecting the request queue. */ - RTSEMMUTEX AsyncIORequestMutex; - /** The event semaphore the thread is waiting on during suspended I/O. */ - RTSEMEVENT SuspendIOSem; - /** Pointer to Media Notify interface. */ - R3PTRTYPE(PPDMIMEDIANOTIFY) pMediaNotify; -#if HC_ARCH_BITS == 32 - uint32_t Alignment0; -#endif - - /* Statistics */ - STAMCOUNTER StatAsyncOps; - uint64_t StatAsyncMinWait; - uint64_t StatAsyncMaxWait; - STAMCOUNTER StatAsyncTimeUS; - STAMPROFILEADV StatAsyncTime; - STAMPROFILE StatLockWait; -} AHCIATACONTROLLER, *PAHCIATACONTROLLER; - -#ifndef VBOX_DEVICE_STRUCT_TESTCASE - -#define ATADEVSTATE_2_CONTROLLER(pIf) ( (pIf)->CTX_SUFF(pController) ) -#define ATADEVSTATE_2_DEVINS(pIf) ( (pIf)->CTX_SUFF(pDevIns) ) -#define CONTROLLER_2_DEVINS(pController) ( (pController)->CTX_SUFF(pDevIns) ) -#define PDMIBASE_2_ATASTATE(pInterface) ( (AHCIATADevState *)((uintptr_t)(pInterface) - RT_OFFSETOF(AHCIATADevState, IBase)) ) - - -/******************************************************************************* - * Internal Functions * - ******************************************************************************/ -RT_C_DECLS_BEGIN -int ataControllerIOPortWrite1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t u32, unsigned cb); -int ataControllerIOPortRead1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t *u32, unsigned cb); -int ataControllerIOPortWriteStr1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb); -int ataControllerIOPortReadStr1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb); -int ataControllerIOPortWrite2(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t u32, unsigned cb); -int ataControllerIOPortRead2(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t *u32, unsigned cb); -int ataControllerBMDMAIOPortRead(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t *pu32, unsigned cb); -int ataControllerBMDMAIOPortWrite(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t u32, unsigned cb); -RT_C_DECLS_END - -#ifdef IN_RING3 -/** - * Initialize a controller state. - * - * @returns VBox status code. - * @param pDevIns Pointer to the device instance which creates a controller. - * @param pCtl Pointer to the unitialized ATA controller structure. - * @param pMediaNotify Pointer to PDM interface for media eject. - * @param iLUNMaster Port number of the master device. - * @param pDrvBaseMaster Pointer to the base driver interface which acts as the master. - * @param pLedMaster Pointer to LED state for master device. - * @param pStatBytesReadMaster Pointer to statistics structure for reads. - * @param pStatBytesWrittenMaster Pointer to statistics structure for writes. - * @param pszSerialNumberMaster VPD serial number for master. - * @param pszFirmwareRevisionMaster VPD firmware revision for master - * @param pszModelNumberMaster VPD model number for master - * @param pszInquiryVendorIdMaster VPD vendor ID for master - * @param pszInquiryProductIdMaster VPD product ID for master - * @param pszInquiryRevisionMaster VPD revision for master - * @param fNonRotationalMaster Flag for non-rotational media. - * @param iLUNSlave Port number of the slave device. - * @param pDrvBaseSlave Pointer to the base driver interface which acts as the slave. - * @param pLedSlave Pointer to LED state for slave device. - * @param pStatBytesReadSlave Pointer to statistics structure for reads. - * @param pStatBytesWrittenSlave Pointer to statistics structure for writes. - * @param pszSerialNumberSlave VPD serial number for slave. - * @param pszFirmwareRevisionSlave VPD firmware revision for slave - * @param pszModelNumberSlave VPD model number for slave - * @param pszInquiryVendorIdSlave VPD vendor ID for slave - * @param pszInquiryProductIdSlave VPD product ID for slave - * @param pszInquiryRevisionSlave VPD revision for slave - * @param fNonRotationalSlave Flag for non-rotational media. - * @param pcbSSMState Where to store the size of the device state for loading/saving. - * @param szName Name of the controller (Used to initialize the critical section). - */ -int ataControllerInit(PPDMDEVINS pDevIns, PAHCIATACONTROLLER pCtl, - PPDMIMEDIANOTIFY pMediaNotify, - unsigned iLUNMaster, PPDMIBASE pDrvBaseMaster, PPDMLED pLedMaster, - PSTAMCOUNTER pStatBytesReadMaster, PSTAMCOUNTER pStatBytesWrittenMaster, - const char *pszSerialNumberMaster, const char *pszFirmwareRevisionMaster, - const char *pszModelNumberMaster, const char *pszInquiryVendorIdMaster, - const char *pszInquiryProductIdMaster, const char *pszInquiryRevisionMaster, - bool fNonRotationalMaster, - unsigned iLUNSlave, PPDMIBASE pDrvBaseSlave, PPDMLED pLedSlave, - PSTAMCOUNTER pStatBytesReadSlave, PSTAMCOUNTER pStatBytesWrittenSlave, - const char *pszSerialNumberSlave, const char *pszFirmwareRevisionSlave, - const char *pszModelNumberSlave, const char *pszInquiryVendorIdSlave, - const char *pszInquiryProductIdSlave, const char *pszInquiryRevisionSlave, - bool fNonRotationalSlave, - uint32_t *pcbSSMState, const char *szName); - -/** - * Free all allocated resources for one controller instance. - * - * @returns VBox status code. - * @param pCtl The controller instance. - */ -int ataControllerDestroy(PAHCIATACONTROLLER pCtl); - -/** - * Tests if the controller is idle, leaving the PDM notifications on if busy. - * - * @returns true if idle, false if idle. - * @param pCtl the controller instance. - */ -bool ataControllerIsIdle(PAHCIATACONTROLLER pCtl); - -/** - * Reset a controller instance to an initial state. - * - * @returns VBox status code. - * @param pCtl Pointer to the controller. - */ -void ataControllerReset(PAHCIATACONTROLLER pCtl); - -/** - * Resume operation of an controller. - * - * @returns nothing - * @param pCtl The controller instance. - */ - -void ataControllerResume(PAHCIATACONTROLLER pCtl); - -/** - * Relocate necessary pointers. - * - * @returns nothing. - * @param pCtl The controller instance. - * @param offDelta The relocation delta relative to the old location. - */ -void ataControllerRelocate(PAHCIATACONTROLLER pCtl, RTGCINTPTR offDelta); - -/** - * Execute state save operation. - * - * @returns VBox status code. - * @param pCtl The controller instance. - * @param pSSM SSM operation handle. - */ -int ataControllerSaveExec(PAHCIATACONTROLLER pCtl, PSSMHANDLE pSSM); - -/** - * Excute state load operation. - * - * @returns VBox status code. - * @param pCtl The controller instance. - * @param pSSM SSM operation handle. - */ -int ataControllerLoadExec(PAHCIATACONTROLLER pCtl, PSSMHANDLE pSSM); - -/** - * Attach command. - * - * This is called when we change block driver for the DVD drive. - * - * @returns VBox status code. - * @param pDevIns The device instance. - * @param iLUN The logical unit which is being detached. - */ -int ataControllerAttach(PAHCIATACONTROLLER pCtl, PPDMIBASE pDrvBase, bool fMaster); - -/** - * Detach notification. - * - * The DVD drive has been unplugged. - * - * @param pDevIns The device instance. - * @param fMaster True if the master is detached - * false for the slave - */ -void ataControllerDetach(PAHCIATACONTROLLER pCtl, bool fMaster); - -#endif /* IN_RING3 */ - -#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */ -#endif /* !___Storage_ATAController_h */ - diff --git a/src/VBox/Devices/Storage/ATAPIPassthrough.cpp b/src/VBox/Devices/Storage/ATAPIPassthrough.cpp new file mode 100644 index 00000000..22cc27fc --- /dev/null +++ b/src/VBox/Devices/Storage/ATAPIPassthrough.cpp @@ -0,0 +1,632 @@ +/* $Id: ATAPIPassthrough.cpp $ */ +/** @file + * VBox storage devices: ATAPI emulation (common code for DevATA and DevAHCI). + */ + +/* + * Copyright (C) 2012 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. + */ +#define LOG_GROUP LOG_GROUP_DEV_IDE +#include <iprt/log.h> +#include <iprt/assert.h> +#include <iprt/mem.h> + +#include <VBox/log.h> +#include <VBox/err.h> +#include <VBox/cdefs.h> +#include <VBox/scsi.h> + +#include "ATAPIPassthrough.h" + +/** The track was not detected yet. */ +#define TRACK_FLAGS_UNDETECTED RT_BIT_32(0) +/** The track is the lead in track of the medium. */ +#define TRACK_FLAGS_LEAD_IN RT_BIT_32(1) +/** The track is the lead out track of the medium. */ +#define TRACK_FLAGS_LEAD_OUT RT_BIT_32(2) + +/** Don't clear already detected tracks on the medium. */ +#define ATAPI_TRACK_LIST_REALLOCATE_FLAGS_DONT_CLEAR RT_BIT_32(0) + +/** + * Track main data form. + */ +typedef enum TRACKDATAFORM +{ + /** Invalid data form. */ + TRACKDATAFORM_INVALID = 0, + /** 2352 bytes of data. */ + TRACKDATAFORM_CDDA, + /** CDDA data is pause. */ + TRACKDATAFORM_CDDA_PAUSE, + /** Mode 1 with 2048 bytes sector size. */ + TRACKDATAFORM_MODE1_2048, + /** Mode 1 with 2352 bytes sector size. */ + TRACKDATAFORM_MODE1_2352, + /** Mode 1 with 0 bytes sector size (generated by the drive). */ + TRACKDATAFORM_MODE1_0, + /** XA Mode with 2336 bytes sector size. */ + TRACKDATAFORM_XA_2336, + /** XA Mode with 2352 bytes sector size. */ + TRACKDATAFORM_XA_2352, + /** XA Mode with 0 bytes sector size (generated by the drive). */ + TRACKDATAFORM_XA_0, + /** Mode 2 with 2336 bytes sector size. */ + TRACKDATAFORM_MODE2_2336, + /** Mode 2 with 2352 bytes sector size. */ + TRACKDATAFORM_MODE2_2352, + /** Mode 2 with 0 bytes sector size (generated by the drive). */ + TRACKDATAFORM_MODE2_0 +} TRACKDATAFORM; + +/** + * Subchannel data form. + */ +typedef enum SUBCHNDATAFORM +{ + /** Invalid subchannel data form. */ + SUBCHNDATAFORM_INVALID = 0, + /** 0 bytes for the subchannel (generated by the drive). */ + SUBCHNDATAFORM_0, + /** 96 bytes of data for the subchannel. */ + SUBCHNDATAFORM_96 +} SUBCHNDATAFORM; + +/** + * Track entry. + */ +typedef struct TRACK +{ + /** Start LBA of the track. */ + int64_t iLbaStart; + /** Number of sectors in the track. */ + uint32_t cSectors; + /** Data form of main data. */ + TRACKDATAFORM enmMainDataForm; + /** Data form of sub channel. */ + SUBCHNDATAFORM enmSubChnDataForm; + /** Flags for the track. */ + uint32_t fFlags; +} TRACK, *PTRACK; + +/** + * Media track list. + */ +typedef struct TRACKLIST +{ + /** Number of detected tracks of the current medium. */ + unsigned cTracksCurrent; + /** Maximum number of tracks the list can contain. */ + unsigned cTracksMax; + /** Variable list of tracks. */ + PTRACK paTracks; +} TRACKLIST, *PTRACKLIST; + +DECLINLINE(uint16_t) atapiBE2H_U16(const uint8_t *pbBuf) +{ + return (pbBuf[0] << 8) | pbBuf[1]; +} + + +DECLINLINE(uint32_t) atapiBE2H_U24(const uint8_t *pbBuf) +{ + return (pbBuf[0] << 16) | (pbBuf[1] << 8) | pbBuf[2]; +} + + +DECLINLINE(uint32_t) atapiBE2H_U32(const uint8_t *pbBuf) +{ + return (pbBuf[0] << 24) | (pbBuf[1] << 16) | (pbBuf[2] << 8) | pbBuf[3]; +} + +DECLINLINE(int64_t) atapiMSF2LBA(const uint8_t *pbBuf) +{ + return ((int64_t)(pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2]) - 150; /* 2 second pregap */ +} + +/** + * Reallocate the given track list to be able to hold the given number of tracks. + * + * @returns VBox status code. + * @param pTrackList The track list to reallocate. + * @param cTracks Number of tracks the list must be able to hold. + * @param fFlags Flags for the reallocation. + */ +static int atapiTrackListReallocate(PTRACKLIST pTrackList, unsigned cTracks, uint32_t fFlags) +{ + int rc = VINF_SUCCESS; + + if (!(fFlags & ATAPI_TRACK_LIST_REALLOCATE_FLAGS_DONT_CLEAR)) + ATAPIPassthroughTrackListClear(pTrackList); + + if (pTrackList->cTracksMax < cTracks) + { + PTRACK paTracksNew = (PTRACK)RTMemRealloc(pTrackList->paTracks, cTracks * sizeof(TRACK)); + if (paTracksNew) + { + pTrackList->paTracks = paTracksNew; + + /* Mark new tracks as undetected. */ + for (unsigned i = pTrackList->cTracksMax; i < cTracks; i++) + pTrackList->paTracks[i].fFlags |= TRACK_FLAGS_UNDETECTED; + + pTrackList->cTracksMax = cTracks; + } + else + rc = VERR_NO_MEMORY; + } + + pTrackList->cTracksCurrent = cTracks; + + return rc; +} + +/** + * Initilizes the given track from the given CUE sheet entry. + * + * @returns nothing. + * @param pTrack The track to initialize. + * @param pbCueSheetEntry CUE sheet entry to use. + */ +static void atapiTrackListEntryCreateFromCueSheetEntry(PTRACK pTrack, const uint8_t *pbCueSheetEntry) +{ + TRACKDATAFORM enmTrackDataForm = TRACKDATAFORM_INVALID; + SUBCHNDATAFORM enmSubChnDataForm = SUBCHNDATAFORM_INVALID; + + /* Determine size of main data based on the data form field. */ + switch (pbCueSheetEntry[3] & 0x3f) + { + case 0x00: /* CD-DA with data. */ + enmTrackDataForm = TRACKDATAFORM_CDDA; + break; + case 0x01: /* CD-DA without data (used for pauses between tracks). */ + enmTrackDataForm = TRACKDATAFORM_CDDA_PAUSE; + break; + case 0x10: /* CD-ROM mode 1 */ + case 0x12: + enmTrackDataForm = TRACKDATAFORM_MODE1_2048; + break; + case 0x11: + case 0x13: + enmTrackDataForm = TRACKDATAFORM_MODE1_2352; + break; + case 0x14: + enmTrackDataForm = TRACKDATAFORM_MODE1_0; + break; + case 0x20: /* CD-ROM XA, CD-I */ + case 0x22: + enmTrackDataForm = TRACKDATAFORM_XA_2336; + break; + case 0x21: + case 0x23: + enmTrackDataForm = TRACKDATAFORM_XA_2352; + break; + case 0x24: + enmTrackDataForm = TRACKDATAFORM_XA_0; + break; + case 0x31: /* CD-ROM Mode 2 */ + case 0x33: + enmTrackDataForm = TRACKDATAFORM_MODE2_2352; + break; + case 0x30: + case 0x32: + enmTrackDataForm = TRACKDATAFORM_MODE2_2336; + break; + case 0x34: + enmTrackDataForm = TRACKDATAFORM_MODE2_0; + break; + default: /* Reserved, invalid mode. Log and leave default sector size. */ + LogRel(("ATA: Invalid data form mode %d for current CUE sheet\n", + pbCueSheetEntry[3] & 0x3f)); + } + + /* Determine size of sub channel data based on data form field. */ + switch ((pbCueSheetEntry[3] & 0xc0) >> 6) + { + case 0x00: /* Sub channel all zeroes, autogenerated by the drive. */ + enmSubChnDataForm = SUBCHNDATAFORM_0; + break; + case 0x01: + case 0x03: + enmSubChnDataForm = SUBCHNDATAFORM_96; + break; + default: + LogRel(("ATA: Invalid sub-channel data form mode %u for current CUE sheet\n", + pbCueSheetEntry[3] & 0xc0)); + } + + pTrack->enmMainDataForm = enmTrackDataForm; + pTrack->enmSubChnDataForm = enmSubChnDataForm; + pTrack->iLbaStart = atapiMSF2LBA(&pbCueSheetEntry[5]); + if (pbCueSheetEntry[1] != 0xaa) + { + /* Calculate number of sectors from the next entry. */ + int64_t iLbaNext = atapiMSF2LBA(&pbCueSheetEntry[5+8]); + pTrack->cSectors = iLbaNext - pTrack->iLbaStart; + } + else + { + pTrack->fFlags |= TRACK_FLAGS_LEAD_OUT; + pTrack->cSectors = 0; + } + pTrack->fFlags &= ~TRACK_FLAGS_UNDETECTED; +} + +/** + * Update the track list from a SEND CUE SHEET request. + * + * @returns VBox status code. + * @param pTrackList Track list to update. + * @param pbCDB CDB of the SEND CUE SHEET request. + * @param pvBuf The CUE sheet. + */ +static int atapiTrackListUpdateFromSendCueSheet(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf) +{ + int rc = VINF_SUCCESS; + unsigned cbCueSheet = atapiBE2H_U24(pbCDB + 6); + unsigned cTracks = cbCueSheet / 8; + + AssertReturn(cbCueSheet % 8 == 0 && cTracks, VERR_INVALID_PARAMETER); + + rc = atapiTrackListReallocate(pTrackList, cTracks, 0); + if (RT_SUCCESS(rc)) + { + const uint8_t *pbCueSheet = (uint8_t *)pvBuf; + PTRACK pTrack = pTrackList->paTracks; + + for (unsigned i = 0; i < cTracks; i++) + { + atapiTrackListEntryCreateFromCueSheetEntry(pTrack, pbCueSheet); + if (i == 0) + pTrack->fFlags |= TRACK_FLAGS_LEAD_IN; + pTrack++; + pbCueSheet += 8; + } + } + + return rc; +} + +static int atapiTrackListUpdateFromSendDvdStructure(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf) +{ + return VERR_NOT_IMPLEMENTED; +} + +/** + * Update track list from formatted TOC data. + * + * @returns VBox status code. + * @param pTrackList The track list to update. + * @param fMSF Flag whether block addresses are in MSF or LBA format. + * @param pbBuf Buffer holding the formatted TOC. + * @param cbBuffer Size of the buffer. + */ +static int atapiTrackListUpdateFromFormattedToc(PTRACKLIST pTrackList, uint8_t iTrack, + bool fMSF, const uint8_t *pbBuf, uint32_t cbBuffer) +{ + int rc = VINF_SUCCESS; + unsigned cbToc = atapiBE2H_U16(pbBuf); + uint8_t iTrackFirst = pbBuf[2]; + unsigned cTracks; + + cbToc -= 2; + pbBuf += 4; + AssertReturn(cbToc % 8 == 0, VERR_INVALID_PARAMETER); + + cTracks = cbToc / 8 + iTrackFirst; + + rc = atapiTrackListReallocate(pTrackList, cTracks, ATAPI_TRACK_LIST_REALLOCATE_FLAGS_DONT_CLEAR); + if (RT_SUCCESS(rc)) + { + PTRACK pTrack = &pTrackList->paTracks[iTrackFirst]; + + for (unsigned i = iTrackFirst; i < cTracks; i++) + { + if (pbBuf[1] & 0x4) + pTrack->enmMainDataForm = TRACKDATAFORM_MODE1_2048; + else + pTrack->enmMainDataForm = TRACKDATAFORM_CDDA; + + pTrack->enmSubChnDataForm = SUBCHNDATAFORM_0; + if (fMSF) + pTrack->iLbaStart = atapiMSF2LBA(&pbBuf[4]); + else + pTrack->iLbaStart = atapiBE2H_U32(&pbBuf[4]); + + if (pbBuf[2] != 0xaa) + { + /* Calculate number of sectors from the next entry. */ + int64_t iLbaNext; + + if (fMSF) + iLbaNext = atapiMSF2LBA(&pbBuf[4+8]); + else + iLbaNext = atapiBE2H_U32(&pbBuf[4+8]); + + pTrack->cSectors = iLbaNext - pTrack->iLbaStart; + } + else + pTrack->cSectors = 0; + + pTrack->fFlags &= ~TRACK_FLAGS_UNDETECTED; + pbBuf += 8; + pTrack++; + } + } + + return rc; +} + +static int atapiTrackListUpdateFromReadTocPmaAtip(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf) +{ + int rc = VINF_SUCCESS; + uint16_t cbBuffer = atapiBE2H_U16(&pbCDB[7]); + bool fMSF = (pbCDB[1] & 0x2) != 0; + uint8_t uFmt = pbCDB[2] & 0xf; + uint8_t iTrack = pbCDB[6]; + + switch (uFmt) + { + case 0x00: + rc = atapiTrackListUpdateFromFormattedToc(pTrackList, iTrack, fMSF, (uint8_t *)pvBuf, cbBuffer); + break; + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + default: + rc = VERR_INVALID_PARAMETER; + } + + return rc; +} + +static int atapiTrackListUpdateFromReadTrackInformation(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf) +{ + return VERR_NOT_IMPLEMENTED; +} + +static int atapiTrackListUpdateFromReadDvdStructure(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf) +{ + return VERR_NOT_IMPLEMENTED; +} + +static int atapiTrackListUpdateFromReadDiscInformation(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf) +{ + return VERR_NOT_IMPLEMENTED; +} + +/** + * Converts the given track data form to a string. + * + * @returns Track data form as a string. + * @param enmTrackDataForm The track main data form. + */ +static const char *atapiTrackListMainDataFormToString(TRACKDATAFORM enmTrackDataForm) +{ + switch (enmTrackDataForm) + { + case TRACKDATAFORM_CDDA: + return "CD-DA"; + case TRACKDATAFORM_CDDA_PAUSE: + return "CD-DA Pause"; + case TRACKDATAFORM_MODE1_2048: + return "Mode 1 (2048 bytes)"; + case TRACKDATAFORM_MODE1_2352: + return "Mode 1 (2352 bytes)"; + case TRACKDATAFORM_MODE1_0: + return "Mode 1 (0 bytes)"; + case TRACKDATAFORM_XA_2336: + return "XA (2336 bytes)"; + case TRACKDATAFORM_XA_2352: + return "XA (2352 bytes)"; + case TRACKDATAFORM_XA_0: + return "XA (0 bytes)"; + case TRACKDATAFORM_MODE2_2336: + return "Mode 2 (2336 bytes)"; + case TRACKDATAFORM_MODE2_2352: + return "Mode 2 (2352 bytes)"; + case TRACKDATAFORM_MODE2_0: + return "Mode 2 (0 bytes)"; + case TRACKDATAFORM_INVALID: + default: + return "Invalid"; + } +} + +/** + * Converts the given subchannel data form to a string. + * + * @returns Subchannel data form as a string. + * @param enmSubChnDataForm The subchannel main data form. + */ +static const char *atapiTrackListSubChnDataFormToString(SUBCHNDATAFORM enmSubChnDataForm) +{ + switch (enmSubChnDataForm) + { + case SUBCHNDATAFORM_0: + return "0"; + case SUBCHNDATAFORM_96: + return "96"; + case SUBCHNDATAFORM_INVALID: + default: + return "Invalid"; + } +} + +/** + * Dump the complete track list to the release log. + * + * @returns nothing. + * @param pTrackList The track list to dump. + */ +static void atapiTrackListDump(PTRACKLIST pTrackList) +{ + LogRel(("Track List: cTracks=%u\n", pTrackList->cTracksCurrent)); + for (unsigned i = 0; i < pTrackList->cTracksCurrent; i++) + { + PTRACK pTrack = &pTrackList->paTracks[i]; + + LogRel((" Track %u: LBAStart=%lld cSectors=%u enmMainDataForm=%s enmSubChnDataForm=%s fFlags=[%s%s%s]\n", + i, pTrack->iLbaStart, pTrack->cSectors, atapiTrackListMainDataFormToString(pTrack->enmMainDataForm), + atapiTrackListSubChnDataFormToString(pTrack->enmSubChnDataForm), + pTrack->fFlags & TRACK_FLAGS_UNDETECTED ? "UNDETECTED " : "", + pTrack->fFlags & TRACK_FLAGS_LEAD_IN ? "Lead-In " : "", + pTrack->fFlags & TRACK_FLAGS_LEAD_OUT ? "Lead-Out" : "")); + } +} + +DECLHIDDEN(int) ATAPIPassthroughTrackListCreateEmpty(PTRACKLIST *ppTrackList) +{ + int rc = VERR_NO_MEMORY; + PTRACKLIST pTrackList = (PTRACKLIST)RTMemAllocZ(sizeof(TRACKLIST)); + + if (pTrackList) + { + rc = VINF_SUCCESS; + *ppTrackList = pTrackList; + } + + return rc; +} + +DECLHIDDEN(void) ATAPIPassthroughTrackListDestroy(PTRACKLIST pTrackList) +{ + if (pTrackList->paTracks) + RTMemFree(pTrackList->paTracks); + RTMemFree(pTrackList); +} + +DECLHIDDEN(void) ATAPIPassthroughTrackListClear(PTRACKLIST pTrackList) +{ + pTrackList->cTracksCurrent = 0; + + /* Mark all tracks as undetected. */ + for (unsigned i = 0; i < pTrackList->cTracksMax; i++) + pTrackList->paTracks[i].fFlags |= TRACK_FLAGS_UNDETECTED; +} + +DECLHIDDEN(int) ATAPIPassthroughTrackListUpdate(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf) +{ + int rc = VINF_SUCCESS; + + switch (pbCDB[0]) + { + case SCSI_SEND_CUE_SHEET: + rc = atapiTrackListUpdateFromSendCueSheet(pTrackList, pbCDB, pvBuf); + break; + case SCSI_SEND_DVD_STRUCTURE: + rc = atapiTrackListUpdateFromSendDvdStructure(pTrackList, pbCDB, pvBuf); + break; + case SCSI_READ_TOC_PMA_ATIP: + rc = atapiTrackListUpdateFromReadTocPmaAtip(pTrackList, pbCDB, pvBuf); + break; + case SCSI_READ_TRACK_INFORMATION: + rc = atapiTrackListUpdateFromReadTrackInformation(pTrackList, pbCDB, pvBuf); + break; + case SCSI_READ_DVD_STRUCTURE: + rc = atapiTrackListUpdateFromReadDvdStructure(pTrackList, pbCDB, pvBuf); + break; + case SCSI_READ_DISC_INFORMATION: + rc = atapiTrackListUpdateFromReadDiscInformation(pTrackList, pbCDB, pvBuf); + break; + default: + LogRel(("ATAPI: Invalid opcode %#x while determining media layout\n", pbCDB[0])); + rc = VERR_INVALID_PARAMETER; + } + +#ifdef LOG_ENABLED + atapiTrackListDump(pTrackList); +#endif + + return rc; +} + +DECLHIDDEN(uint32_t) ATAPIPassthroughTrackListGetSectorSizeFromLba(PTRACKLIST pTrackList, uint32_t iAtapiLba) +{ + PTRACK pTrack = NULL; + uint32_t cbAtapiSector = 2048; + + if (pTrackList->cTracksCurrent) + { + if ( iAtapiLba > UINT32_C(0xffff4fa1) + && (int32_t)iAtapiLba < -150) + { + /* Lead-In area, this is always the first entry in the cue sheet. */ + pTrack = pTrackList->paTracks; + Assert(pTrack->fFlags & TRACK_FLAGS_LEAD_IN); + LogFlowFunc(("Selected Lead-In area\n")); + } + else + { + int64_t iAtapiLba64 = (int32_t)iAtapiLba; + pTrack = &pTrackList->paTracks[1]; + + /* Go through the track list and find the correct entry. */ + for (unsigned i = 1; i < pTrackList->cTracksCurrent - 1; i++) + { + if (pTrack->fFlags & TRACK_FLAGS_UNDETECTED) + continue; + + if ( pTrack->iLbaStart <= iAtapiLba64 + && iAtapiLba64 < pTrack->iLbaStart + pTrack->cSectors) + break; + + pTrack++; + } + } + + if (pTrack) + { + switch (pTrack->enmMainDataForm) + { + case TRACKDATAFORM_CDDA: + case TRACKDATAFORM_MODE1_2352: + case TRACKDATAFORM_XA_2352: + case TRACKDATAFORM_MODE2_2352: + cbAtapiSector = 2352; + break; + case TRACKDATAFORM_MODE1_2048: + cbAtapiSector = 2048; + break; + case TRACKDATAFORM_CDDA_PAUSE: + case TRACKDATAFORM_MODE1_0: + case TRACKDATAFORM_XA_0: + case TRACKDATAFORM_MODE2_0: + cbAtapiSector = 0; + break; + case TRACKDATAFORM_XA_2336: + case TRACKDATAFORM_MODE2_2336: + cbAtapiSector = 2336; + break; + case TRACKDATAFORM_INVALID: + default: + AssertMsgFailed(("Invalid track data form %d\n", pTrack->enmMainDataForm)); + } + + switch (pTrack->enmSubChnDataForm) + { + case SUBCHNDATAFORM_0: + break; + case SUBCHNDATAFORM_96: + cbAtapiSector += 96; + break; + case SUBCHNDATAFORM_INVALID: + default: + AssertMsgFailed(("Invalid subchannel data form %d\n", pTrack->enmSubChnDataForm)); + } + } + } + + return cbAtapiSector; +} + diff --git a/src/VBox/Devices/Storage/ATAPIPassthrough.h b/src/VBox/Devices/Storage/ATAPIPassthrough.h new file mode 100644 index 00000000..0ccaf62e --- /dev/null +++ b/src/VBox/Devices/Storage/ATAPIPassthrough.h @@ -0,0 +1,74 @@ +/* $Id: ATAPIPassthrough.h $ */ +/** @file + * VBox storage devices: ATAPI passthrough helpers (common code for DevATA and DevAHCI). + */ + +/* + * Copyright (C) 2012 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. + */ +#ifndef __ATAPIPassthrough_h +#define __ATAPIPassthrough_h + +#include <VBox/cdefs.h> + +RT_C_DECLS_BEGIN + +/** + * Opaque media track list. + */ +typedef struct TRACKLIST *PTRACKLIST; + +/** + * Creates an empty track list handle. + * + * @returns VBox status code. + * @param ppTrackList Where to store the track list handle on success. + */ +DECLHIDDEN(int) ATAPIPassthroughTrackListCreateEmpty(PTRACKLIST *ppTrackList); + +/** + * Destroys the allocated task list handle. + * + * @returns nothing. + * @param pTrackList The track list handle to destroy. + */ +DECLHIDDEN(void) ATAPIPassthroughTrackListDestroy(PTRACKLIST pTrackList); + +/** + * Clears all tracks from the given task list. + * + * @returns nothing. + * @param pTrackList The track list to clear. + */ +DECLHIDDEN(void) ATAPIPassthroughTrackListClear(PTRACKLIST pTrackList); + +/** + * Updates the track list from the given CDB and data buffer. + * + * @returns VBox status code. + * @param pTrackList The track list to update. + * @param pCDB The CDB buffer. + * @param pvBuf The data buffer. + */ +DECLHIDDEN(int) ATAPIPassthroughTrackListUpdate(PTRACKLIST pTrackList, const uint8_t *pCDB, const void *pvBuf); + +/** + * Return the sector size from the track matching the LBA in the given track list. + * + * @returns Sector size. + * @param pTrackList The track list to use. + * @param iAtapiLba The start LBA to get the sector size for. + */ +DECLHIDDEN(uint32_t) ATAPIPassthroughTrackListGetSectorSizeFromLba(PTRACKLIST pTrackList, uint32_t iAtapiLba); + +RT_C_DECLS_END + +#endif /* __ATAPIPassthrough_h */ diff --git a/src/VBox/Devices/Storage/Debug.cpp b/src/VBox/Devices/Storage/Debug.cpp index 2b6678d5..1fb479e9 100644 --- a/src/VBox/Devices/Storage/Debug.cpp +++ b/src/VBox/Devices/Storage/Debug.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Devices/Storage/DevAHCI.cpp b/src/VBox/Devices/Storage/DevAHCI.cpp index 0a300e3d..50b0ca8b 100644 --- a/src/VBox/Devices/Storage/DevAHCI.cpp +++ b/src/VBox/Devices/Storage/DevAHCI.cpp @@ -1,11 +1,12 @@ /* $Id: DevAHCI.cpp $ */ /** @file - * VBox storage devices: AHCI controller device (disk and cdrom). - * Implements the AHCI standard 1.1 + * DevAHCI - AHCI controller device (disk and cdrom). + * + * Implements the AHCI standard 1.1 */ /* - * Copyright (C) 2006-2012 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; @@ -35,12 +36,12 @@ /******************************************************************************* * Header Files * *******************************************************************************/ -//#define DEBUG #define LOG_GROUP LOG_GROUP_DEV_AHCI #include <VBox/vmm/pdmdev.h> #include <VBox/vmm/pdmqueue.h> #include <VBox/vmm/pdmthread.h> #include <VBox/vmm/pdmcritsect.h> +#include <VBox/sup.h> #include <VBox/scsi.h> #include <iprt/assert.h> #include <iprt/asm.h> @@ -55,8 +56,20 @@ #endif #include "PIIX3ATABmDma.h" #include "ide.h" +#include "ATAPIPassthrough.h" #include "VBoxDD.h" +#if defined(VBOX_WITH_DTRACE) \ + && defined(IN_RING3) \ + && !defined(VBOX_DEVICE_STRUCT_TESTCASE) +# include "dtrace/VBoxDD.h" +#else +# define VBOXDD_AHCI_REQ_SUBMIT(a,b,c,d) do { } while (0) +# define VBOXDD_AHCI_REQ_SUBMIT_TIMESTAMP(a,b) do { } while (0) +# define VBOXDD_AHCI_REQ_COMPLETED(a,b,c,d,e) do { } while (0) +# define VBOXDD_AHCI_REQ_COMPLETED_TIMESTAMP(a,b) do { } while (0) +#endif + /** Maximum number of ports available. * Spec defines 32 but we have one allocated for command completion coalescing * and another for a reserved future feature. @@ -124,8 +137,6 @@ /* Media track type */ #define ATA_MEDIA_TYPE_UNKNOWN 0 /**< unknown CD type */ -#define ATA_MEDIA_TYPE_DATA 1 /**< Data CD */ -#define ATA_MEDIA_TYPE_CDDA 2 /**< CD-DA (audio) CD type */ /** ATAPI sense info size. */ #define ATAPI_SENSE_SIZE 64 @@ -278,6 +289,8 @@ typedef struct AHCIREQ { /** Task state. */ volatile AHCITXSTATE enmTxState; + /** Start timestamp of the request. */ + uint64_t tsStart; /** Tag of the task. */ uint32_t uTag; /** The command header for this task. */ @@ -432,17 +445,15 @@ typedef struct AHCIPort bool fAsyncInterface; /** Flag if we are in a device reset. */ bool fResetDevice; - /** Flag whether the I/O thread idles. */ - volatile bool fAsyncIOThreadIdle; /** Flag whether the port is in redo task mode. */ volatile bool fRedo; - -#if HC_ARCH_BITS == 64 - bool fAlignment2; -#endif + /** Flag whether the worker thread is sleeping. */ + volatile bool fWrkThreadSleeping; /** Number of total sectors. */ uint64_t cTotalSectors; + /** Size of one sector. */ + uint32_t cbSector; /** Currently configured number of sectors in a multi-sector transfer. */ uint32_t cMultSectors; /** Currently active transfer mode (MDMA/UDMA) and speed. */ @@ -451,6 +462,8 @@ typedef struct AHCIPort uint8_t abATAPISense[ATAPI_SENSE_SIZE]; /** HACK: Countdown till we report a newly unmounted drive as mounted. */ uint8_t cNotifiedMediaChange; + /** Exponent of logical sectors in a physical sector, number of logical sectors is 2^exp. */ + uint8_t cLogSectorsPerPhysicalExp; /** The same for GET_EVENT_STATUS for mechanism */ volatile uint32_t MediaEventStatus; /** Media type if known. */ @@ -464,6 +477,8 @@ typedef struct AHCIPort volatile uint32_t u32QueuedTasksFinished; /** Bitmap for new queued tasks (Guest -> R3). */ volatile uint32_t u32TasksNew; + /** Bitmap of tasks which must be redone because of a non fatal error. */ + volatile uint32_t u32TasksRedo; /** Current command slot processed. * Accessed by the guest by reading the CMD register. @@ -504,8 +519,6 @@ typedef struct AHCIPort /** Async IO Thread. */ R3PTRTYPE(PPDMTHREAD) pAsyncIOThread; - /** Request semaphore. */ - RTSEMEVENT AsyncIORequestSem; /** * Array of cached tasks. The tag number is the index value. * Only used with the async interface. @@ -513,10 +526,11 @@ typedef struct AHCIPort R3PTRTYPE(PAHCIREQ) aCachedTasks[AHCI_NR_COMMAND_SLOTS]; /** First task throwing an error. */ R3PTRTYPE(volatile PAHCIREQ) pTaskErr; + /** The current tracklist of the loaded medium if passthrough is used. */ + R3PTRTYPE(PTRACKLIST) pTrackList; -#if HC_ARCH_BITS == 32 - uint32_t u32Alignment4; -#endif + /** The event semaphore the processing thread waits on. */ + SUPSEMEVENT hEvtProcess; /** Release statistics: number of DMA commands. */ STAMCOUNTER StatDMA; @@ -529,12 +543,8 @@ typedef struct AHCIPort #ifdef VBOX_WITH_STATISTICS /** Statistics: Time to complete one request. */ STAMPROFILE StatProfileProcessTime; - /** Statistics: Time to map requests into R3. */ - STAMPROFILE StatProfileMapIntoR3; /** Statistics: Amount of time to read/write data. */ STAMPROFILE StatProfileReadWrite; - /** Statistics: Amount of time to destroy a list. */ - STAMPROFILE StatProfileDestroyScatterGatherList; #endif /* VBOX_WITH_STATISTICS */ /** The serial numnber to use for IDENTIFY DEVICE commands. */ @@ -664,6 +674,8 @@ typedef struct AHCI /** Bitmask of ports which asserted an interrupt. */ volatile uint32_t u32PortsInterrupted; + /** Number of I/O threads currently active - used for async controller reset handling. */ + volatile uint32_t cThreadsActive; /** Device is in a reset state. */ bool fReset; /** Supports 64bit addressing */ @@ -688,6 +700,12 @@ typedef struct AHCI /** Flag whether we have written the first 4bytes in an 8byte MMIO write successfully. */ volatile bool f8ByteMMIO4BytesWrittenSuccessfully; +#if HC_ARCH_BITS == 64 + uint32_t Alignment7; +#endif + + /** The support driver session handle. */ + R3R0PTRTYPE(PSUPDRVSESSION) pSupDrvSession; } AHCI; /** Pointer to the state of an AHCI device. */ typedef AHCI *PAHCI; @@ -1049,8 +1067,6 @@ static int PortCmdIssue_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32 if ( (pAhciPort->regCMD & AHCI_PORT_CMD_CR) && u32Value > 0) { - uint32_t u32Tasks; - /* * Clear all tasks which are already marked as busy. The guest * shouldn't write already busy tasks actually. @@ -1058,16 +1074,21 @@ static int PortCmdIssue_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32 u32Value &= ~pAhciPort->regCI; ASMAtomicOrU32(&pAhciPort->u32TasksNew, u32Value); - u32Tasks = ASMAtomicReadU32(&pAhciPort->u32TasksNew); /* Send a notification to R3 if u32TasksNew was before our write. */ - if (!(u32Tasks ^ u32Value)) + if (ASMAtomicReadBool(&pAhciPort->fWrkThreadSleeping)) { +#ifdef IN_RC PDEVPORTNOTIFIERQUEUEITEM pItem = (PDEVPORTNOTIFIERQUEUEITEM)PDMQueueAlloc(ahci->CTX_SUFF(pNotifierQueue)); AssertMsg(VALID_PTR(pItem), ("Allocating item for queue failed\n")); pItem->iPort = pAhciPort->iLUN; PDMQueueInsert(ahci->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem); +#else + LogFlowFunc(("Signal event semaphore\n")); + int rc = SUPSemEventSignal(ahci->pSupDrvSession, pAhciPort->hEvtProcess); + AssertRC(rc); +#endif } } @@ -1160,7 +1181,7 @@ static int PortSControl_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32 Assert(fAllTasksCanceled); if (!ASMAtomicXchgBool(&pAhciPort->fPortReset, true)) - LogRel(("AHCI#%d: Port %d reset\n", ahci->CTX_SUFF(pDevIns)->iInstance, + LogRel(("AHCI#%u: Port %d reset\n", ahci->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN)); pAhciPort->regSSTS = 0; @@ -1269,7 +1290,7 @@ static int PortTaskFileData_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, ui */ static int PortCmd_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value) { - ahciLog(("%s: read regCMD=%#010x\n", __FUNCTION__, pAhciPort->regCMD)); + ahciLog(("%s: read regCMD=%#010x\n", __FUNCTION__, pAhciPort->regCMD | AHCI_PORT_CMD_CCS_SHIFT(pAhciPort->u32CurrentCommandSlot))); ahciLog(("%s: ICC=%d ASP=%d ALPE=%d DLAE=%d ATAPI=%d CPD=%d ISP=%d HPCP=%d PMA=%d CPS=%d CR=%d FR=%d ISS=%d CCS=%d FRE=%d CLO=%d POD=%d SUD=%d ST=%d\n", __FUNCTION__, (pAhciPort->regCMD & AHCI_PORT_CMD_ICC) >> 28, (pAhciPort->regCMD & AHCI_PORT_CMD_ASP) >> 27, (pAhciPort->regCMD & AHCI_PORT_CMD_ALPE) >> 26, (pAhciPort->regCMD & AHCI_PORT_CMD_DLAE) >> 25, @@ -1304,6 +1325,9 @@ static int PortCmd_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u3 (u32Value & AHCI_PORT_CMD_POD) >> 2, (u32Value & AHCI_PORT_CMD_SUD) >> 1, (u32Value & AHCI_PORT_CMD_ST))); + /* The PxCMD.CCS bits are R/O and maintained separately. */ + u32Value &= ~AHCI_PORT_CMD_CCS; + if (pAhciPort->fPoweredOn && pAhciPort->fSpunUp) { if (u32Value & AHCI_PORT_CMD_CLO) @@ -1316,17 +1340,43 @@ static int PortCmd_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u3 if (u32Value & AHCI_PORT_CMD_ST) { - ahciLog(("%s: Engine starts\n", __FUNCTION__)); - - /* Set engine state to running if there is a device attached. */ - if (pAhciPort->pDrvBase) + /* + * Set engine state to running if there is a device attached and + * IS.PCS is clear. + */ + if ( pAhciPort->pDrvBase + && !(pAhciPort->regIS & AHCI_PORT_IS_PCS)) + { + ahciLog(("%s: Engine starts\n", __FUNCTION__)); u32Value |= AHCI_PORT_CMD_CR; + + /* If there is something in CI, kick the I/O thread. */ + if ( pAhciPort->regCI > 0 + && ASMAtomicReadBool(&pAhciPort->fWrkThreadSleeping)) + { + ASMAtomicOrU32(&pAhciPort->u32TasksNew, pAhciPort->regCI); +#ifdef IN_RC + PDEVPORTNOTIFIERQUEUEITEM pItem = (PDEVPORTNOTIFIERQUEUEITEM)PDMQueueAlloc(ahci->CTX_SUFF(pNotifierQueue)); + AssertMsg(VALID_PTR(pItem), ("Allocating item for queue failed\n")); + + pItem->iPort = pAhciPort->iLUN; + PDMQueueInsert(ahci->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem); +#else + LogFlowFunc(("Signal event semaphore\n")); + int rc = SUPSemEventSignal(ahci->pSupDrvSession, pAhciPort->hEvtProcess); + AssertRC(rc); +#endif + } + } + else + u32Value &= ~AHCI_PORT_CMD_CR; } else { ahciLog(("%s: Engine stops\n", __FUNCTION__)); /* Clear command issue register. */ pAhciPort->regCI = 0; + pAhciPort->regSACT = 0; /* Clear current command slot. */ pAhciPort->u32CurrentCommandSlot = 0; u32Value &= ~AHCI_PORT_CMD_CR; @@ -1718,9 +1768,22 @@ static int HbaControl_w(PAHCI ahci, uint32_t iReg, uint32_t u32Value) #ifndef IN_RING3 return VINF_IOM_R3_MMIO_WRITE; #else - ahci->regHbaCtrl = (u32Value & AHCI_HBA_CTRL_RW_MASK) | AHCI_HBA_CTRL_AE; - if (ahci->regHbaCtrl & AHCI_HBA_CTRL_HR) + /* + * Increase the active thread counter because we might set the host controller + * reset bit. + */ + ASMAtomicIncU32(&ahci->cThreadsActive); + ASMAtomicWriteU32(&ahci->regHbaCtrl, (u32Value & AHCI_HBA_CTRL_RW_MASK) | AHCI_HBA_CTRL_AE); + + /* + * Do the HBA reset if requested and there is no other active thread at the moment, + * the work is deferred to the last active thread otherwise. + */ + uint32_t cThreadsActive = ASMAtomicDecU32(&ahci->cThreadsActive); + if ( (u32Value & AHCI_HBA_CTRL_HR) + && !cThreadsActive) ahciHBAReset(ahci); + return VINF_SUCCESS; #endif } @@ -1935,6 +1998,7 @@ static void ahciPortSwReset(PAHCIPort pAhciPort) pAhciPort->uATATransferMode = ATA_MODE_UDMA | 6; pAhciPort->u32TasksNew = 0; + pAhciPort->u32TasksRedo = 0; pAhciPort->u32TasksFinished = 0; pAhciPort->u32QueuedTasksFinished = 0; pAhciPort->u32CurrentCommandSlot = 0; @@ -2008,7 +2072,7 @@ static void ahciHBAReset(PAHCI pThis) unsigned i; int rc = VINF_SUCCESS; - LogRel(("AHCI#%d: Reset the HBA\n", pThis->CTX_SUFF(pDevIns)->iInstance)); + LogRel(("AHCI#%u: Reset the HBA\n", pThis->CTX_SUFF(pDevIns)->iInstance)); /* Stop the CCC timer. */ if (pThis->regHbaCccCtl & AHCI_HBA_CCC_CTL_EN) @@ -2037,7 +2101,6 @@ static void ahciHBAReset(PAHCI pThis) AHCI_HBA_CAP_NCS_SET(pThis->cCmdSlotsAvail) | /* Number of command slots we support */ AHCI_HBA_CAP_NP_SET(pThis->cPortsImpl); /* Number of supported ports */ pThis->regHbaCtrl = AHCI_HBA_CTRL_AE; - pThis->regHbaIs = 0; pThis->regHbaPi = ahciGetPortsImplemented(pThis->cPortsImpl); pThis->regHbaVs = AHCI_HBA_VS_MJR | AHCI_HBA_VS_MNR; pThis->regHbaCccCtl = 0; @@ -2046,6 +2109,11 @@ static void ahciHBAReset(PAHCI pThis) pThis->uCccPortNr = 0; pThis->uCccNr = 0; + /* Clear pending interrupts. */ + pThis->regHbaIs = 0; + pThis->u32PortsInterrupted = 0; + ahciHbaClearInterrupt(pThis); + pThis->f64BitAddr = false; pThis->u32PortsInterrupted = 0; pThis->f8ByteMMIO4BytesWrittenSuccessfully = false; @@ -2150,23 +2218,26 @@ static int ahciRegisterRead(PAHCI pAhci, uint32_t uReg, void *pv, unsigned cb) * @returns VBox status code. * * @param pAhci The AHCI instance. - * @param uReg The register to write. - * @param pv Where to fetch the result. - * @param cb Number of bytes to write. + * @param offReg The offset of the register to write to. + * @param u32Value The value to write. */ -static int ahciRegisterWrite(PAHCI pAhci, uint32_t uReg, void const *pv, unsigned cb) +static int ahciRegisterWrite(PAHCI pAhci, uint32_t offReg, uint32_t u32Value) { - int rc = VINF_SUCCESS; + int rc; uint32_t iReg; - if (uReg < AHCI_HBA_GLOBAL_SIZE) + /* + * If the access offset is smaller than 100h the guest accesses the global registers. + * Otherwise it accesses the registers of a port. + */ + if (offReg < AHCI_HBA_GLOBAL_SIZE) { Log3(("Write global HBA register\n")); - iReg = uReg >> 2; + iReg = offReg >> 2; if (iReg < RT_ELEMENTS(g_aOpRegs)) { const AHCIOPREG *pReg = &g_aOpRegs[iReg]; - rc = pReg->pfnWrite(pAhci, iReg, *(uint32_t *)pv); + rc = pReg->pfnWrite(pAhci, iReg, u32Value); } else { @@ -2179,15 +2250,15 @@ static int ahciRegisterWrite(PAHCI pAhci, uint32_t uReg, void const *pv, unsigne uint32_t iPort; Log3(("Write Port register\n")); /* Calculate accessed port. */ - uReg -= AHCI_HBA_GLOBAL_SIZE; - iPort = uReg / AHCI_PORT_REGISTER_SIZE; - iReg = (uReg % AHCI_PORT_REGISTER_SIZE) >> 2; + offReg -= AHCI_HBA_GLOBAL_SIZE; + iPort = offReg / AHCI_PORT_REGISTER_SIZE; + iReg = (offReg % AHCI_PORT_REGISTER_SIZE) >> 2; Log3(("%s: Trying to write to port %u and register %u\n", __FUNCTION__, iPort, iReg)); if (RT_LIKELY( iPort < pAhci->cPortsImpl && iReg < RT_ELEMENTS(g_aPortOpRegs))) { const AHCIPORTOPREG *pPortReg = &g_aPortOpRegs[iReg]; - rc = pPortReg->pfnWrite(pAhci, &pAhci->ahciPort[iPort], iReg, *(uint32_t *)pv); + rc = pPortReg->pfnWrite(pAhci, &pAhci->ahciPort[iPort], iReg, u32Value); } else { @@ -2213,23 +2284,10 @@ static int ahciRegisterWrite(PAHCI pAhci, uint32_t uReg, void const *pv, unsigne PDMBOTHCBDECL(int) ahciMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb) { PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI); - int rc = VINF_SUCCESS; - - /* Break up 64 bits reads into two dword reads. */ - if (cb == 8) - { - rc = ahciMMIORead(pDevIns, pvUser, GCPhysAddr, pv, 4); - if (RT_FAILURE(rc)) - return rc; - - return ahciMMIORead(pDevIns, pvUser, GCPhysAddr + 4, (uint8_t *)pv + 4, 4); - } - - Log2(("#%d ahciMMIORead: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp rc=%Rrc\n", - pDevIns->iInstance, pv, cb, pv, cb, GCPhysAddr, rc)); + Log2(("#%d ahciMMIORead: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp\n", + pDevIns->iInstance, pv, cb, pv, cb, GCPhysAddr)); - uint32_t uOffset = (GCPhysAddr - pAhci->MMIOBase); - rc = ahciRegisterRead(pAhci, uOffset, pv, cb); + int rc = ahciRegisterRead(pAhci, GCPhysAddr - pAhci->MMIOBase, pv, cb); Log2(("#%d ahciMMIORead: return pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp rc=%Rrc\n", pDevIns->iInstance, pv, cb, pv, cb, GCPhysAddr, rc)); @@ -2251,9 +2309,12 @@ PDMBOTHCBDECL(int) ahciMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhy PDMBOTHCBDECL(int) ahciMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb) { PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI); - int rc = VINF_SUCCESS; + Assert(cb == 4 || cb == 8); + Assert(!(GCPhysAddr & (cb - 1))); /* Break up 64 bits writes into two dword writes. */ + /** @todo Eliminate this code once the IOM/EM starts taking care of these + * situations. */ if (cb == 8) { /* @@ -2263,6 +2324,7 @@ PDMBOTHCBDECL(int) ahciMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPh * Writing the first 4 bytes again could cause indeterminate behavior * which can cause errors in the guest. */ + int rc = VINF_SUCCESS; if (!pAhci->f8ByteMMIO4BytesWrittenSuccessfully) { rc = ahciMMIOWrite(pDevIns, pvUser, GCPhysAddr, pv, 4); @@ -2283,29 +2345,9 @@ PDMBOTHCBDECL(int) ahciMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPh return rc; } - Log2(("#%d ahciMMIOWrite: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp\n", - pDevIns->iInstance, pv, cb, pv, cb, GCPhysAddr)); - - /* Validate access. */ - if (cb != sizeof(uint32_t)) - { - Log2(("%s: Bad write size!!! GCPhysAddr=%RGp cb=%d\n", __FUNCTION__, GCPhysAddr, cb)); - return VINF_SUCCESS; - } - if (GCPhysAddr & 0x3) - { - Log2(("%s: Unaligned write!!! GCPhysAddr=%RGp cb=%d\n", __FUNCTION__, GCPhysAddr, cb)); - return VINF_SUCCESS; - } - - /* - * If the access offset is smaller than 100h the guest accesses the global registers. - * Otherwise it accesses the registers of a port. - */ - uint32_t uOffset = (GCPhysAddr - pAhci->MMIOBase); - rc = ahciRegisterWrite(pAhci, uOffset, pv, cb); - - return rc; + /* Do the access. */ + Log2(("#%d ahciMMIOWrite: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp\n", pDevIns->iInstance, pv, cb, pv, cb, GCPhysAddr)); + return ahciRegisterWrite(pAhci, GCPhysAddr - pAhci->MMIOBase, *(uint32_t const *)pv); } PDMBOTHCBDECL(int) ahciLegacyFakeWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb) @@ -2349,8 +2391,9 @@ PDMBOTHCBDECL(int) ahciIdxDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT P } else { + /** @todo range check? */ Assert(iReg == 1); - rc = ahciRegisterWrite(pAhci, pAhci->regIdx, &u32, cb); + rc = ahciRegisterWrite(pAhci, pAhci->regIdx, u32); if (rc == VINF_IOM_R3_MMIO_WRITE) rc = VINF_IOM_R3_IOPORT_WRITE; } @@ -2392,9 +2435,12 @@ PDMBOTHCBDECL(int) ahciIdxDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Po else { Assert(iReg == 1); + /** @todo range check? */ rc = ahciRegisterRead(pAhci, pAhci->regIdx, pu32, cb); if (rc == VINF_IOM_R3_MMIO_READ) rc = VINF_IOM_R3_IOPORT_READ; + else if (rc == VINF_IOM_MMIO_UNUSED_00) + rc = VERR_IOM_IOPORT_UNUSED; } } else @@ -2419,8 +2465,10 @@ static DECLCALLBACK(int) ahciR3MMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iReg Assert(cb >= 4352); /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */ + /** @todo change this to IOMMMIO_FLAGS_WRITE_ONLY_DWORD once EM/IOM starts + * handling 2nd DWORD failures on split accesses correctly. */ rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/, - IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, + IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_ONLY_DWORD_QWORD, ahciMMIOWrite, ahciMMIORead, "AHCI"); if (RT_FAILURE(rc)) return rc; @@ -2586,7 +2634,7 @@ static DECLCALLBACK(int) ahciR3PortQueryDeviceLocation(PPDMIBLOCKPORT pInterface return VINF_SUCCESS; } -#ifdef DEBUG +#ifdef LOG_ENABLED /** * Dump info about the FIS @@ -2701,7 +2749,8 @@ static void ahciDumpCmdHdrInfo(PAHCIPort pAhciPort, CmdHdr *pCmdHdr) ahciLog(("%s: Command FIS length %u DW\n", __FUNCTION__, (pCmdHdr->u32DescInf & AHCI_CMDHDR_CFL_MASK))); ahciLog(("%s: *** End command header info dump. ***\n", __FUNCTION__)); } -#endif /* DEBUG */ + +#endif /* LOG_ENABLED */ /** * Post the first D2H FIS from the device into guest memory. @@ -2801,8 +2850,8 @@ static int ahciPostFisIntoMemory(PAHCIPort pAhciPort, unsigned uFisType, uint8_t } /* Post the FIS into memory. */ - ahciLog(("%s: PDMDevHlpPhysWrite GCPhysAddrRecFis=%RGp cbFis=%u\n", __FUNCTION__, GCPhysAddrRecFis, cbFis)); - PDMDevHlpPhysWrite(pAhciPort->CTX_SUFF(pDevIns), GCPhysAddrRecFis, pCmdFis, cbFis); + ahciLog(("%s: PDMDevHlpPCIPhysWrite GCPhysAddrRecFis=%RGp cbFis=%u\n", __FUNCTION__, GCPhysAddrRecFis, cbFis)); + PDMDevHlpPCIPhysWrite(pAhciPort->CTX_SUFF(pDevIns), GCPhysAddrRecFis, pCmdFis, cbFis); } return rc; @@ -2990,7 +3039,9 @@ static int ahciIdentifySS(PAHCIPort pAhciPort, void *pvBuf) p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */ if ( pAhciPort->pDrvBlock->pfnDiscard || ( pAhciPort->fAsyncInterface - && pAhciPort->pDrvBlockAsync->pfnStartDiscard)) + && pAhciPort->pDrvBlockAsync->pfnStartDiscard) + || pAhciPort->cbSector != 512 + || pAhciPort->fNonRotational) { p[80] = RT_H2LE_U16(0x1f0); /* support everything up to ATA/ATAPI-8 ACS */ p[81] = RT_H2LE_U16(0x28); /* conforms to ATA/ATAPI-8 ACS */ @@ -3012,6 +3063,20 @@ static int ahciIdentifySS(PAHCIPort pAhciPort, void *pvBuf) p[101] = RT_H2LE_U16(pAhciPort->cTotalSectors >> 16); p[102] = RT_H2LE_U16(pAhciPort->cTotalSectors >> 32); p[103] = RT_H2LE_U16(pAhciPort->cTotalSectors >> 48); + + /* valid information, more than one logical sector per physical sector, 2^cLogSectorsPerPhysicalExp logical sectors per physical sector */ + if (pAhciPort->cLogSectorsPerPhysicalExp) + p[106] = RT_H2LE_U16(RT_BIT(14) | RT_BIT(13) | pAhciPort->cLogSectorsPerPhysicalExp); + + if (pAhciPort->cbSector != 512) + { + uint32_t cSectorSizeInWords = pAhciPort->cbSector / sizeof(uint16_t); + /* Enable reporting of logical sector size. */ + p[106] |= RT_H2LE_U16(RT_BIT(12) | RT_BIT(14)); + p[117] = RT_H2LE_U16(cSectorSizeInWords); + p[118] = RT_H2LE_U16(cSectorSizeInWords >> 16); + } + if (pAhciPort->fNonRotational) p[217] = RT_H2LE_U16(1); /* Non-rotational medium */ @@ -3047,6 +3112,7 @@ static int atapiReadTOCRawSS(PAHCIREQ, PAHCIPort, size_t, size_t *); static int atapiReadTrackInformationSS(PAHCIREQ, PAHCIPort, size_t, size_t *); static int atapiRequestSenseSS(PAHCIREQ, PAHCIPort, size_t, size_t *); static int atapiPassthroughSS(PAHCIREQ, PAHCIPort, size_t, size_t *); +static int atapiReadDVDStructureSS(PAHCIREQ, PAHCIPort, size_t, size_t *); /** * Source/sink function indexes for g_apfnAtapiFuncs. @@ -3069,6 +3135,7 @@ typedef enum ATAPIFN ATAFN_SS_ATAPI_READ_TRACK_INFORMATION, ATAFN_SS_ATAPI_REQUEST_SENSE, ATAFN_SS_ATAPI_PASSTHROUGH, + ATAFN_SS_ATAPI_READ_DVD_STRUCTURE, ATAFN_SS_MAX } ATAPIFN; @@ -3093,7 +3160,8 @@ static const PAtapiFunc g_apfnAtapiFuncs[ATAFN_SS_MAX] = atapiReadTOCRawSS, atapiReadTrackInformationSS, atapiRequestSenseSS, - atapiPassthroughSS + atapiPassthroughSS, + atapiReadDVDStructureSS }; static int atapiIdentifySS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData) @@ -3804,7 +3872,7 @@ static int atapiReadTOCRawSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbDa /** * Sets the given media track type. */ -static uint32_t ataMediumTypeSet(PAHCIPort pAhciPort, uint32_t MediaTrackType) +static uint32_t ahciMediumTypeSet(PAHCIPort pAhciPort, uint32_t MediaTrackType) { return ASMAtomicXchgU32(&pAhciPort->MediaTrackType, MediaTrackType); } @@ -3967,10 +4035,35 @@ static int atapiPassthroughSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbD if (RT_SUCCESS(rc)) { - Assert(cbTransfer <= pAhciReq->cbTransfer); + /* Do post processing for certain commands. */ + switch (pAhciReq->aATAPICmd[0]) + { + case SCSI_SEND_CUE_SHEET: + case SCSI_READ_TOC_PMA_ATIP: + { + if (!pAhciPort->pTrackList) + rc = ATAPIPassthroughTrackListCreateEmpty(&pAhciPort->pTrackList); + + if (RT_SUCCESS(rc)) + rc = ATAPIPassthroughTrackListUpdate(pAhciPort->pTrackList, pAhciReq->aATAPICmd, pvBuf); + + if ( RT_FAILURE(rc) + && pAhciPort->cErrors++ < MAX_LOG_REL_ERRORS) + LogRel(("AHCI: Error (%Rrc) while updating the tracklist during %s, burning the disc might fail\n", + rc, pAhciReq->aATAPICmd[0] == SCSI_SEND_CUE_SHEET ? "SEND CUE SHEET" : "READ TOC/PMA/ATIP")); + break; + } + case SCSI_SYNCHRONIZE_CACHE: + { + ATAPIPassthroughTrackListClear(pAhciPort->pTrackList); + break; + } + } if (pAhciReq->enmTxDir == AHCITXDIR_READ) { + Assert(cbTransfer <= pAhciReq->cbTransfer); + if (pAhciReq->aATAPICmd[0] == SCSI_INQUIRY) { /* Make sure that the real drive cannot be identified. @@ -3983,37 +4076,6 @@ static int atapiPassthroughSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbD if (cbTransfer >= 32 + 4) ataSCSIPadStr((uint8_t *)pvBuf + 32, "1.0", 4); } - else if ( pAhciReq->aATAPICmd[0] == SCSI_READ_TOC_PMA_ATIP - && (pAhciReq->aATAPICmd[2] & 0xf) != 0x05 - && pAhciReq->aATAPICmd[6] != 0xaa) - { - /* Set the media type if we can detect it. */ - uint8_t *pbBuf = (uint8_t *)pvBuf; - - /** @todo: Implemented only for formatted TOC now. */ - if ( (pAhciReq->aATAPICmd[1] & 0xf) == 0 - && cbTransfer >= 6) - { - uint32_t NewMediaType; - uint32_t OldMediaType; - - if (pbBuf[5] & 0x4) - NewMediaType = ATA_MEDIA_TYPE_DATA; - else - NewMediaType = ATA_MEDIA_TYPE_CDDA; - - OldMediaType = ataMediumTypeSet(pAhciPort, NewMediaType); - - if (OldMediaType != NewMediaType) - LogRel(("AHCI: LUN#%d: CD-ROM passthrough, detected %s CD\n", - pAhciPort->iLUN, - NewMediaType == ATA_MEDIA_TYPE_DATA - ? "data" - : "audio")); - } - else /* Play safe and set to unknown. */ - ataMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN); - } if (cbTransfer) { @@ -4058,10 +4120,170 @@ static int atapiPassthroughSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbD return VINF_SUCCESS; } +/** @todo: Revise ASAP. */ +/* Keep in sync with DevATA.cpp! */ +static int atapiReadDVDStructureSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData) +{ + uint8_t aBuf[25]; /* Counted a maximum of 20 bytes but better be on the safe side. */ + uint8_t *buf = aBuf; + int media = pAhciReq->aATAPICmd[1]; + int format = pAhciReq->aATAPICmd[7]; + + uint16_t max_len = ataBE2H_U16(&pAhciReq->aATAPICmd[8]); + + memset(buf, 0, max_len); + + switch (format) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x30: + case 0x31: + case 0xff: + if (media == 0) + { + int uASC = SCSI_ASC_NONE; + + switch (format) + { + case 0x0: /* Physical format information */ + { + int layer = pAhciReq->aATAPICmd[6]; + uint64_t total_sectors; + + if (layer != 0) + { + uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET; + break; + } + + total_sectors = pAhciPort->cTotalSectors; + total_sectors >>= 2; + if (total_sectors == 0) + { + uASC = -SCSI_ASC_MEDIUM_NOT_PRESENT; + break; + } + + buf[4] = 1; /* DVD-ROM, part version 1 */ + buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */ + buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */ + buf[7] = 0; /* default densities */ + + /* FIXME: 0x30000 per spec? */ + ataH2BE_U32(buf + 8, 0); /* start sector */ + ataH2BE_U32(buf + 12, total_sectors - 1); /* end sector */ + ataH2BE_U32(buf + 16, total_sectors - 1); /* l0 end sector */ + + /* Size of buffer, not including 2 byte size field */ + ataH2BE_U32(&buf[0], 2048 + 2); + + /* 2k data + 4 byte header */ + uASC = (2048 + 4); + break; + } + case 0x01: /* DVD copyright information */ + buf[4] = 0; /* no copyright data */ + buf[5] = 0; /* no region restrictions */ + + /* Size of buffer, not including 2 byte size field */ + ataH2BE_U16(buf, 4 + 2); + + /* 4 byte header + 4 byte data */ + uASC = (4 + 4); + + case 0x03: /* BCA information - invalid field for no BCA info */ + uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET; + break; + + case 0x04: /* DVD disc manufacturing information */ + /* Size of buffer, not including 2 byte size field */ + ataH2BE_U16(buf, 2048 + 2); + + /* 2k data + 4 byte header */ + uASC = (2048 + 4); + break; + case 0xff: + /* + * This lists all the command capabilities above. Add new ones + * in order and update the length and buffer return values. + */ + + buf[4] = 0x00; /* Physical format */ + buf[5] = 0x40; /* Not writable, is readable */ + ataH2BE_U16((buf + 6), 2048 + 4); + + buf[8] = 0x01; /* Copyright info */ + buf[9] = 0x40; /* Not writable, is readable */ + ataH2BE_U16((buf + 10), 4 + 4); + + buf[12] = 0x03; /* BCA info */ + buf[13] = 0x40; /* Not writable, is readable */ + ataH2BE_U16((buf + 14), 188 + 4); + + buf[16] = 0x04; /* Manufacturing info */ + buf[17] = 0x40; /* Not writable, is readable */ + ataH2BE_U16((buf + 18), 2048 + 4); + + /* Size of buffer, not including 2 byte size field */ + ataH2BE_U16(buf, 16 + 2); + + /* data written + 4 byte header */ + uASC = (16 + 4); + break; + default: /* TODO: formats beyond DVD-ROM requires */ + uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET; + } + + if (uASC < 0) + { + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, -uASC); + return false; + } + break; + } + /* TODO: BD support, fall through for now */ + + /* Generic disk structures */ + case 0x80: /* TODO: AACS volume identifier */ + case 0x81: /* TODO: AACS media serial number */ + case 0x82: /* TODO: AACS media identifier */ + case 0x83: /* TODO: AACS media key block */ + case 0x90: /* TODO: List of recognized format layers */ + case 0xc0: /* TODO: Write protection status */ + default: + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, + SCSI_ASC_INV_FIELD_IN_CMD_PACKET); + return false; + } + + /* Copy the buffer into the scatter gather list. */ + *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0], + RT_MIN(cbData, max_len)); + + atapiCmdOK(pAhciPort, pAhciReq); + return false; +} + static int atapiDoTransfer(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, size_t cbMax, ATAPIFN iSourceSink) { size_t cbTransfered = 0; - int rc, rcSourceSink; + int rcSourceSink; rcSourceSink = g_apfnAtapiFuncs[iSourceSink](pAhciReq, pAhciPort, cbMax, &cbTransfered); @@ -4072,8 +4294,7 @@ static int atapiDoTransfer(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, size_t cbMax, LogFlow(("cbTransfered=%d\n", cbTransfered)); /* Write updated command header into memory of the guest. */ - PDMDevHlpPhysWrite(pAhciPort->CTX_SUFF(pDevIns), pAhciReq->GCPhysCmdHdrAddr, - &pAhciReq->cmdHdr, sizeof(CmdHdr)); + PDMDevHlpPCIPhysWrite(pAhciPort->CTX_SUFF(pDevIns), pAhciReq->GCPhysCmdHdrAddr, &pAhciReq->cmdHdr, sizeof(CmdHdr)); return rcSourceSink; } @@ -4085,8 +4306,9 @@ static int atapiReadSectors2352PostProcess(PAHCIREQ pAhciReq, void **ppvProc, si uint32_t iATAPILBA = pAhciReq->uOffset / 2048; uint8_t *pbBufDst; uint8_t *pbBufSrc = (uint8_t *)pAhciReq->u.Io.DataSeg.pvSeg; + size_t cbAlloc = pAhciReq->cbTransfer + cSectors * (1 + 11 + 3 + 1 + 288); /* Per sector data like ECC. */ - pbBuf = (uint8_t *)RTMemAlloc(pAhciReq->cbTransfer); + pbBuf = (uint8_t *)RTMemAlloc(cbAlloc); if (RT_UNLIKELY(!pbBuf)) return VERR_NO_MEMORY; @@ -4112,7 +4334,7 @@ static int atapiReadSectors2352PostProcess(PAHCIREQ pAhciReq, void **ppvProc, si } *ppvProc = pbBuf; - *pcbProc = pAhciReq->cbTransfer; + *pcbProc = cbAlloc; return VINF_SUCCESS; } @@ -4144,7 +4366,7 @@ static int atapiReadSectors(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint32_t iAT static AHCITXDIR atapiParseCmdVirtualATAPI(PAHCIPort pAhciPort, PAHCIREQ pAhciReq) { - AHCITXDIR rc = AHCITXDIR_NONE; + AHCITXDIR enmTxDir = AHCITXDIR_NONE; const uint8_t *pbPacket; uint32_t cbMax; @@ -4172,37 +4394,37 @@ static AHCITXDIR atapiParseCmdVirtualATAPI(PAHCIPort pAhciPort, PAHCIREQ pAhciRe atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_GET_EVENT_STATUS_NOTIFICATION); break; case SCSI_MODE_SENSE_10: + { + uint8_t uPageControl, uPageCode; + cbMax = ataBE2H_U16(pbPacket + 7); + uPageControl = pbPacket[2] >> 6; + uPageCode = pbPacket[2] & 0x3f; + switch (uPageControl) { - uint8_t uPageControl, uPageCode; - cbMax = ataBE2H_U16(pbPacket + 7); - uPageControl = pbPacket[2] >> 6; - uPageCode = pbPacket[2] & 0x3f; - switch (uPageControl) - { - case SCSI_PAGECONTROL_CURRENT: - switch (uPageCode) - { - case SCSI_MODEPAGE_ERROR_RECOVERY: - atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY); - break; - case SCSI_MODEPAGE_CD_STATUS: - atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS); - break; - default: - goto error_cmd; - } - break; - case SCSI_PAGECONTROL_CHANGEABLE: - goto error_cmd; - case SCSI_PAGECONTROL_DEFAULT: - goto error_cmd; - default: - case SCSI_PAGECONTROL_SAVED: - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED); - break; - } + case SCSI_PAGECONTROL_CURRENT: + switch (uPageCode) + { + case SCSI_MODEPAGE_ERROR_RECOVERY: + atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY); + break; + case SCSI_MODEPAGE_CD_STATUS: + atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS); + break; + default: + goto error_cmd; + } + break; + case SCSI_PAGECONTROL_CHANGEABLE: + goto error_cmd; + case SCSI_PAGECONTROL_DEFAULT: + goto error_cmd; + default: + case SCSI_PAGECONTROL_SAVED: + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED); + break; } break; + } case SCSI_REQUEST_SENSE: cbMax = pbPacket[4]; atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_REQUEST_SENSE); @@ -4221,224 +4443,224 @@ static AHCITXDIR atapiParseCmdVirtualATAPI(PAHCIPort pAhciPort, PAHCIREQ pAhciRe break; case SCSI_READ_10: case SCSI_READ_12: - { - uint32_t cSectors, iATAPILBA; + { + uint32_t cSectors, iATAPILBA; - if (pAhciPort->cNotifiedMediaChange > 0) - { - pAhciPort->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - break; - } - else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount)) - { - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; - } - if (pbPacket[0] == SCSI_READ_10) - cSectors = ataBE2H_U16(pbPacket + 7); - else - cSectors = ataBE2H_U32(pbPacket + 6); - iATAPILBA = ataBE2H_U32(pbPacket + 2); - if (cSectors == 0) - { - atapiCmdOK(pAhciPort, pAhciReq); - break; - } - if ((uint64_t)iATAPILBA + cSectors > pAhciPort->cTotalSectors) + if (pAhciPort->cNotifiedMediaChange > 0) + { + pAhciPort->cNotifiedMediaChange-- ; + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ + break; + } + if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount)) + { + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); + break; + } + if (pbPacket[0] == SCSI_READ_10) + cSectors = ataBE2H_U16(pbPacket + 7); + else + cSectors = ataBE2H_U32(pbPacket + 6); + iATAPILBA = ataBE2H_U32(pbPacket + 2); + if (cSectors == 0) + { + atapiCmdOK(pAhciPort, pAhciReq); + break; + } + if ((uint64_t)iATAPILBA + cSectors > pAhciPort->cTotalSectors) + { + /* Rate limited logging, one log line per second. For + * guests that insist on reading from places outside the + * valid area this often generates too many release log + * entries otherwise. */ + static uint64_t s_uLastLogTS = 0; + if (RTTimeMilliTS() >= s_uLastLogTS + 1000) { - /* Rate limited logging, one log line per second. For - * guests that insist on reading from places outside the - * valid area this often generates too many release log - * entries otherwise. */ - static uint64_t uLastLogTS = 0; - if (RTTimeMilliTS() >= uLastLogTS + 1000) - { - LogRel(("AHCI ATAPI: LUN#%d: CD-ROM block number %Ld invalid (READ)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA + cSectors)); - uLastLogTS = RTTimeMilliTS(); - } - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); - break; + LogRel(("AHCI ATAPI: LUN#%d: CD-ROM block number %Ld invalid (READ)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA + cSectors)); + s_uLastLogTS = RTTimeMilliTS(); } - atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2048); - rc = AHCITXDIR_READ; + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); + break; } + atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2048); + enmTxDir = AHCITXDIR_READ; break; + } case SCSI_READ_CD: - { - uint32_t cSectors, iATAPILBA; + { + uint32_t cSectors, iATAPILBA; - if (pAhciPort->cNotifiedMediaChange > 0) - { - pAhciPort->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - break; - } - else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount)) + if (pAhciPort->cNotifiedMediaChange > 0) + { + pAhciPort->cNotifiedMediaChange-- ; + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ + break; + } + else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount)) + { + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); + break; + } + cSectors = (pbPacket[6] << 16) | (pbPacket[7] << 8) | pbPacket[8]; + iATAPILBA = ataBE2H_U32(pbPacket + 2); + if (cSectors == 0) + { + atapiCmdOK(pAhciPort, pAhciReq); + break; + } + if ((uint64_t)iATAPILBA + cSectors > pAhciPort->cTotalSectors) + { + /* Rate limited logging, one log line per second. For + * guests that insist on reading from places outside the + * valid area this often generates too many release log + * entries otherwise. */ + static uint64_t s_uLastLogTS = 0; + if (RTTimeMilliTS() >= s_uLastLogTS + 1000) { - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; + LogRel(("AHCI ATA: LUN#%d: CD-ROM block number %Ld invalid (READ CD)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA + cSectors)); + s_uLastLogTS = RTTimeMilliTS(); } - cSectors = (pbPacket[6] << 16) | (pbPacket[7] << 8) | pbPacket[8]; - iATAPILBA = ataBE2H_U32(pbPacket + 2); - if (cSectors == 0) - { + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); + break; + } + switch (pbPacket[9] & 0xf8) + { + case 0x00: + /* nothing */ atapiCmdOK(pAhciPort, pAhciReq); break; - } - if ((uint64_t)iATAPILBA + cSectors > pAhciPort->cTotalSectors) - { - /* Rate limited logging, one log line per second. For - * guests that insist on reading from places outside the - * valid area this often generates too many release log - * entries otherwise. */ - static uint64_t uLastLogTS = 0; - if (RTTimeMilliTS() >= uLastLogTS + 1000) - { - LogRel(("AHCI ATA: LUN#%d: CD-ROM block number %Ld invalid (READ CD)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA + cSectors)); - uLastLogTS = RTTimeMilliTS(); - } - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); + case 0x10: + /* normal read */ + atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2048); + enmTxDir = AHCITXDIR_READ; + break; + case 0xf8: + /* read all data */ + atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2352); + enmTxDir = AHCITXDIR_READ; + break; + default: + LogRel(("AHCI ATAPI: LUN#%d: CD-ROM sector format not supported\n", pAhciPort->iLUN)); + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); break; - } - switch (pbPacket[9] & 0xf8) - { - case 0x00: - /* nothing */ - atapiCmdOK(pAhciPort, pAhciReq); - break; - case 0x10: - /* normal read */ - atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2048); - rc = AHCITXDIR_READ; - break; - case 0xf8: - /* read all data */ - atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2352); - rc = AHCITXDIR_READ; - break; - default: - LogRel(("AHCI ATAPI: LUN#%d: CD-ROM sector format not supported\n", pAhciPort->iLUN)); - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); - break; - } } break; + } case SCSI_SEEK_10: + { + uint32_t iATAPILBA; + if (pAhciPort->cNotifiedMediaChange > 0) { - uint32_t iATAPILBA; - if (pAhciPort->cNotifiedMediaChange > 0) - { - pAhciPort->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - break; - } - else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount)) - { - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; - } - iATAPILBA = ataBE2H_U32(pbPacket + 2); - if (iATAPILBA > pAhciPort->cTotalSectors) + pAhciPort->cNotifiedMediaChange-- ; + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ + break; + } + else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount)) + { + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); + break; + } + iATAPILBA = ataBE2H_U32(pbPacket + 2); + if (iATAPILBA > pAhciPort->cTotalSectors) + { + /* Rate limited logging, one log line per second. For + * guests that insist on seeking to places outside the + * valid area this often generates too many release log + * entries otherwise. */ + static uint64_t s_uLastLogTS = 0; + if (RTTimeMilliTS() >= s_uLastLogTS + 1000) { - /* Rate limited logging, one log line per second. For - * guests that insist on seeking to places outside the - * valid area this often generates too many release log - * entries otherwise. */ - static uint64_t uLastLogTS = 0; - if (RTTimeMilliTS() >= uLastLogTS + 1000) - { - LogRel(("AHCI ATAPI: LUN#%d: CD-ROM block number %Ld invalid (SEEK)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA)); - uLastLogTS = RTTimeMilliTS(); - } - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); - break; + LogRel(("AHCI ATAPI: LUN#%d: CD-ROM block number %Ld invalid (SEEK)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA)); + s_uLastLogTS = RTTimeMilliTS(); } - atapiCmdOK(pAhciPort, pAhciReq); - pAhciReq->uATARegStatus |= ATA_STAT_SEEK; /* Linux expects this. */ + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); + break; } + atapiCmdOK(pAhciPort, pAhciReq); + pAhciReq->uATARegStatus |= ATA_STAT_SEEK; /* Linux expects this. */ break; + } case SCSI_START_STOP_UNIT: + { + int rc = VINF_SUCCESS; + switch (pbPacket[4] & 3) { - int rc2 = VINF_SUCCESS; - switch (pbPacket[4] & 3) + case 0: /* 00 - Stop motor */ + case 1: /* 01 - Start motor */ + break; + case 2: /* 10 - Eject media */ { - case 0: /* 00 - Stop motor */ - case 1: /* 01 - Start motor */ - break; - case 2: /* 10 - Eject media */ + /* This must be done from EMT. */ + PAHCI pAhci = pAhciPort->CTX_SUFF(pAhci); + PPDMDEVINS pDevIns = pAhci->CTX_SUFF(pDevIns); + + rc = VMR3ReqPriorityCallWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY, + (PFNRT)pAhciPort->pDrvMount->pfnUnmount, 3, + pAhciPort->pDrvMount, false/*=fForce*/, true/*=fEject*/); + Assert(RT_SUCCESS(rc) || rc == VERR_PDM_MEDIA_LOCKED || rc == VERR_PDM_MEDIA_NOT_MOUNTED); + if (RT_SUCCESS(rc) && pAhci->pMediaNotify) { - /* This must be done from EMT. */ - PAHCI pAhci = pAhciPort->CTX_SUFF(pAhci); - PPDMDEVINS pDevIns = pAhci->CTX_SUFF(pDevIns); - - rc2 = VMR3ReqPriorityCallWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY, - (PFNRT)pAhciPort->pDrvMount->pfnUnmount, 3, - pAhciPort->pDrvMount, false/*=fForce*/, true/*=fEject*/); - Assert(RT_SUCCESS(rc2) || (rc2 == VERR_PDM_MEDIA_LOCKED) || (rc2 = VERR_PDM_MEDIA_NOT_MOUNTED)); - if (RT_SUCCESS(rc) && pAhci->pMediaNotify) - { - rc2 = VMR3ReqCallNoWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY, - (PFNRT)pAhci->pMediaNotify->pfnEjected, 2, - pAhci->pMediaNotify, pAhciPort->iLUN); - AssertRC(rc); - } - break; + rc = VMR3ReqCallNoWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY, + (PFNRT)pAhci->pMediaNotify->pfnEjected, 2, + pAhci->pMediaNotify, pAhciPort->iLUN); + AssertRC(rc); } - case 3: /* 11 - Load media */ - /** @todo rc = s->pDrvMount->pfnLoadMedia(s->pDrvMount) */ - break; + break; } - if (RT_SUCCESS(rc2)) - atapiCmdOK(pAhciPort, pAhciReq); - else - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED); + case 3: /* 11 - Load media */ + /** @todo rc = s->pDrvMount->pfnLoadMedia(s->pDrvMount) */ + break; } + if (RT_SUCCESS(rc)) + atapiCmdOK(pAhciPort, pAhciReq); + else + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED); break; + } case SCSI_MECHANISM_STATUS: - { - cbMax = ataBE2H_U16(pbPacket + 8); - atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MECHANISM_STATUS); - } + { + cbMax = ataBE2H_U16(pbPacket + 8); + atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MECHANISM_STATUS); break; + } case SCSI_READ_TOC_PMA_ATIP: - { - uint8_t format; + { + uint8_t format; - if (pAhciPort->cNotifiedMediaChange > 0) - { - pAhciPort->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ + if (pAhciPort->cNotifiedMediaChange > 0) + { + pAhciPort->cNotifiedMediaChange-- ; + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ + break; + } + else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount)) + { + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); + break; + } + cbMax = ataBE2H_U16(pbPacket + 7); + /* SCSI MMC-3 spec says format is at offset 2 (lower 4 bits), + * but Linux kernel uses offset 9 (topmost 2 bits). Hope that + * the other field is clear... */ + format = (pbPacket[2] & 0xf) | (pbPacket[9] >> 6); + switch (format) + { + case 0: + atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_NORMAL); break; - } - else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount)) - { - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); + case 1: + atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_MULTI); + break; + case 2: + atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_RAW); + break; + default: + error_cmd: + atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); break; - } - cbMax = ataBE2H_U16(pbPacket + 7); - /* SCSI MMC-3 spec says format is at offset 2 (lower 4 bits), - * but Linux kernel uses offset 9 (topmost 2 bits). Hope that - * the other field is clear... */ - format = (pbPacket[2] & 0xf) | (pbPacket[9] >> 6); - switch (format) - { - case 0: - atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_NORMAL); - break; - case 1: - atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_MULTI); - break; - case 2: - atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_RAW); - break; - default: - error_cmd: - atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); - break; - } } break; + } case SCSI_READ_CAPACITY: if (pAhciPort->cNotifiedMediaChange > 0) { @@ -4492,12 +4714,16 @@ static AHCITXDIR atapiParseCmdVirtualATAPI(PAHCIPort pAhciPort, PAHCIREQ pAhciRe cbMax = pbPacket[4]; atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_INQUIRY); break; + case SCSI_READ_DVD_STRUCTURE: + cbMax = ataBE2H_U16(pbPacket + 8); + atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_DVD_STRUCTURE); + break; default: atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE); break; } - return rc; + return enmTxDir; } /* @@ -4603,16 +4829,26 @@ static AHCITXDIR atapiParseCmdPassthrough(PAHCIPort pAhciPort, PAHCIREQ pAhciReq enmTxDir = AHCITXDIR_READ; goto sendcmd; case SCSI_READ_CD: + case SCSI_READ_CD_MSF: { /* Get sector size based on the expected sector type field. */ switch ((pbPacket[1] >> 2) & 0x7) { case 0x0: /* All types. */ - if (ASMAtomicReadU32(&pAhciPort->MediaTrackType) == ATA_MEDIA_TYPE_CDDA) - pAhciReq->cbATAPISector = 2352; + { + uint32_t iLbaStart; + + if (pbPacket[0] == SCSI_READ_CD) + iLbaStart = ataBE2H_U32(&pbPacket[2]); + else + iLbaStart = ataMSF2LBA(&pbPacket[3]); + + if (pAhciPort->pTrackList) + pAhciReq->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pAhciPort->pTrackList, iLbaStart); else pAhciReq->cbATAPISector = 2048; /* Might be incorrect if we couldn't determine the type. */ break; + } case 0x1: /* CD-DA */ pAhciReq->cbATAPISector = 2352; break; @@ -4633,18 +4869,18 @@ static AHCITXDIR atapiParseCmdPassthrough(PAHCIPort pAhciPort, PAHCIREQ pAhciReq pAhciReq->cbATAPISector = 0; /** @todo we should probably fail the command here already. */ } - cbTransfer = ataBE2H_U24(pbPacket + 6) * pAhciReq->cbATAPISector; + if (pbPacket[0] == SCSI_READ_CD) + cbTransfer = ataBE2H_U24(pbPacket + 6) * pAhciReq->cbATAPISector; + else /* SCSI_READ_MSF */ + { + cSectors = ataMSF2LBA(pbPacket + 6) - ataMSF2LBA(pbPacket + 3); + if (cSectors > 32) + cSectors = 32; /* Limit transfer size to 64~74K. Safety first. In any case this can only harm software doing CDDA extraction. */ + cbTransfer = cSectors * pAhciReq->cbATAPISector; + } enmTxDir = AHCITXDIR_READ; goto sendcmd; } - case SCSI_READ_CD_MSF: - cSectors = ataMSF2LBA(pbPacket + 6) - ataMSF2LBA(pbPacket + 3); - if (cSectors > 32) - cSectors = 32; /* Limit transfer size to 64~74K. Safety first. In any case this can only harm software doing CDDA extraction. */ - pAhciReq->cbATAPISector = 2048; /**< @todo this size is not always correct */ - cbTransfer = cSectors * pAhciReq->cbATAPISector; - enmTxDir = AHCITXDIR_READ; - goto sendcmd; case SCSI_READ_DISC_INFORMATION: cbTransfer = ataBE2H_U16(pbPacket + 7); enmTxDir = AHCITXDIR_READ; @@ -4731,10 +4967,14 @@ static AHCITXDIR atapiParseCmdPassthrough(PAHCIPort pAhciPort, PAHCIREQ pAhciReq case SCSI_VERIFY_10: goto sendcmd; case SCSI_WRITE_10: + case SCSI_WRITE_AND_VERIFY_10: iATAPILBA = ataBE2H_U32(pbPacket + 2); cSectors = ataBE2H_U16(pbPacket + 7); Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors)); - pAhciReq->cbATAPISector = 2048; /**< @todo this size is not always correct */ + if (pAhciPort->pTrackList) + pAhciReq->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pAhciPort->pTrackList, iATAPILBA); + else + pAhciReq->cbATAPISector = 2048; cbTransfer = cSectors * pAhciReq->cbATAPISector; enmTxDir = AHCITXDIR_WRITE; goto sendcmd; @@ -4742,20 +4982,13 @@ static AHCITXDIR atapiParseCmdPassthrough(PAHCIPort pAhciPort, PAHCIREQ pAhciReq iATAPILBA = ataBE2H_U32(pbPacket + 2); cSectors = ataBE2H_U32(pbPacket + 6); Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors)); - pAhciReq->cbATAPISector = 2048; /**< @todo this size is not always correct */ + if (pAhciPort->pTrackList) + pAhciReq->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pAhciPort->pTrackList, iATAPILBA); + else + pAhciReq->cbATAPISector = 2048; cbTransfer = cSectors * pAhciReq->cbATAPISector; enmTxDir = AHCITXDIR_WRITE; goto sendcmd; - case SCSI_WRITE_AND_VERIFY_10: - iATAPILBA = ataBE2H_U32(pbPacket + 2); - cSectors = ataBE2H_U16(pbPacket + 7); - Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors)); - /* The sector size is determined by the async I/O thread. */ - pAhciReq->cbATAPISector = 0; - /* Preliminary, will be corrected once the sector size is known. */ - cbTransfer = cSectors; - enmTxDir = AHCITXDIR_WRITE; - goto sendcmd; case SCSI_WRITE_BUFFER: switch (pbPacket[1] & 0x1f) { @@ -4809,11 +5042,7 @@ static AHCITXDIR atapiParseCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq) const uint8_t *pbPacket; pbPacket = pAhciReq->aATAPICmd; -#ifdef DEBUG Log(("%s: LUN#%d CMD=%#04x \"%s\"\n", __FUNCTION__, pAhciPort->iLUN, pbPacket[0], SCSICmdText(pbPacket[0]))); -#else /* !DEBUG */ - Log(("%s: LUN#%d CMD=%#04x\n", __FUNCTION__, pAhciPort->iLUN, pbPacket[0])); -#endif /* !DEBUG */ Log2(("%s: limit=%#x packet: %.*Rhxs\n", __FUNCTION__, pAhciReq->cmdFis[AHCI_CMDFIS_CYLL] | (pAhciReq->cmdFis[AHCI_CMDFIS_CYLH] << 8), ATAPI_PACKET_SIZE, pbPacket)); if (pAhciPort->fATAPIPassthrough) @@ -4875,7 +5104,7 @@ static void ahciDeviceReset(PAHCIPort pAhciPort, PAHCIREQ pAhciReq) /** * Create a PIO setup FIS and post it into the memory area of the guest. * - *Â @returns nothing. + * @returns nothing. * @param pAhciPort The port of the SATA controller. * @param pAhciReq The state of the task. * @param pCmdFis Pointer to the command FIS from the guest. @@ -5219,9 +5448,9 @@ static size_t ahciCopyToPrdtl(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq, do { - uint32_t cPrdtlEntriesRead = (cPrdtlEntries < RT_ELEMENTS(aPrdtlEntries)) - ? cPrdtlEntries - : RT_ELEMENTS(aPrdtlEntries); + uint32_t cPrdtlEntriesRead = cPrdtlEntries < RT_ELEMENTS(aPrdtlEntries) + ? cPrdtlEntries + : RT_ELEMENTS(aPrdtlEntries); PDMDevHlpPhysRead(pDevIns, GCPhysPrdtl, &aPrdtlEntries[0], cPrdtlEntriesRead * sizeof(SGLEntry)); @@ -5233,7 +5462,7 @@ static size_t ahciCopyToPrdtl(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq, cbThisCopy = RT_MIN(cbThisCopy, cbBuf); /* Copy into SG entry. */ - PDMDevHlpPhysWrite(pDevIns, GCPhysAddrDataBase, pbBuf, cbThisCopy); + PDMDevHlpPCIPhysWrite(pDevIns, GCPhysAddrDataBase, pbBuf, cbThisCopy); pbBuf += cbThisCopy; cbBuf -= cbThisCopy; @@ -5307,6 +5536,7 @@ static size_t ahciCopyFromPrdtl(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq, * Allocate I/O memory and copies the guest buffer for writes. * * @returns VBox status code. + * @param pDevIns The device instance. * @param pAhciReq The request state. * @param cbTransfer Amount of bytes to allocate. */ @@ -5330,6 +5560,15 @@ static int ahciIoBufAllocate(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq, size_t cbTra return VINF_SUCCESS; } +/** + * Frees the I/O memory of the given request and updates the guest buffer if necessary. + * + * @returns nothing. + * @param pDevIns The device instance. + * @param pAhciReq The request state. + * @param fCopyToGuest Flag whether to update the guest buffer if necessary. + * Nothing is copied if false even if the request was a read. + */ static void ahciIoBufFree(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq, bool fCopyToGuest) { @@ -5348,7 +5587,7 @@ static void ahciIoBufFree(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq, if (RT_SUCCESS(rc)) { - ahciCopyToPrdtl(pDevIns, pAhciReq, pv, cb); + pAhciReq->cbTransfer = ahciCopyToPrdtl(pDevIns, pAhciReq, pv, cb); RTMemFree(pv); } } @@ -5393,7 +5632,7 @@ static bool ahciCancelActiveTasks(PAHCIPort pAhciPort) * a new task structure for this tag. */ ASMAtomicWriteNullPtr(&pAhciPort->aCachedTasks[i]); - LogRel(("AHCI#%dP%d: Cancelled task %u\n", pAhciPort->CTX_SUFF(pDevIns)->iInstance, + LogRel(("AHCI#%uP%u: Cancelled task %u\n", pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, pAhciReq->uTag)); } else @@ -5567,8 +5806,8 @@ static int ahciTrimRangesCreate(PAHCIPort pAhciPort, PAHCIREQ pAhciReq) aRanges[idxRangeSrc] = RT_H2LE_U64(aRanges[idxRangeSrc]); if (AHCI_RANGE_LENGTH_GET(aRanges[idxRangeSrc]) != 0) { - pAhciReq->u.Trim.paRanges[idxRange].offStart = (aRanges[idxRangeSrc] & AHCI_RANGE_LBA_MASK) * 512; - pAhciReq->u.Trim.paRanges[idxRange].cbRange = AHCI_RANGE_LENGTH_GET(aRanges[idxRangeSrc]) * 512; + pAhciReq->u.Trim.paRanges[idxRange].offStart = (aRanges[idxRangeSrc] & AHCI_RANGE_LBA_MASK) * pAhciPort->cbSector; + pAhciReq->u.Trim.paRanges[idxRange].cbRange = AHCI_RANGE_LENGTH_GET(aRanges[idxRangeSrc]) * pAhciPort->cbSector; idxRange++; } else @@ -5603,7 +5842,7 @@ static void ahciTrimRangesDestroy(PAHCIREQ pAhciReq) * Complete a data transfer task by freeing all occupied resources * and notifying the guest. * - * @returns Flag whether the given request was canceled inbetween. + * @returns Flag whether the given request was canceled inbetween; * * @param pAhciPort Pointer to the port where to request completed. * @param pAhciReq Pointer to the task which finished. @@ -5615,6 +5854,45 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR bool fXchg = false; bool fRedo = false; bool fCanceled = false; + uint64_t tsNow = RTTimeMilliTS(); + AHCITXSTATE enmTxState = AHCITXSTATE_INVALID; + + LogFlowFunc(("pAhciPort=%p pAhciReq=%p rcReq=%d fFreeReq=%RTbool\n", + pAhciPort, pAhciReq, rcReq, fFreeReq)); + + ASMAtomicReadSize(&pAhciReq->enmTxState, &enmTxState); + VBOXDD_AHCI_REQ_COMPLETED(pAhciReq, rcReq, enmTxState, pAhciReq->uOffset, pAhciReq->cbTransfer); + VBOXDD_AHCI_REQ_COMPLETED_TIMESTAMP(pAhciReq, tsNow); + + /* + * Leave a release log entry if the request was active for more than 25 seconds + * (30 seconds is the timeout of the guest). + */ + if (tsNow - pAhciReq->tsStart >= 25 * 1000) + { + const char *pcszReq = NULL; + + switch (pAhciReq->enmTxDir) + { + case AHCITXDIR_READ: + pcszReq = "Read"; + break; + case AHCITXDIR_WRITE: + pcszReq = "Write"; + break; + case AHCITXDIR_FLUSH: + pcszReq = "Flush"; + break; + case AHCITXDIR_TRIM: + pcszReq = "Trim"; + break; + default: + pcszReq = "<Invalid>"; + } + + LogRel(("AHCI#%uP%u: %s request was active for %llu seconds\n", + pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, pcszReq, (tsNow - pAhciReq->tsStart) / 1000)); + } ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg); @@ -5644,14 +5922,14 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR if (pAhciPort->cErrors++ < MAX_LOG_REL_ERRORS) { if (pAhciReq->enmTxDir == AHCITXDIR_FLUSH) - LogRel(("AHCI#%u: Flush returned rc=%Rrc\n", - pAhciPort->iLUN, rcReq)); + LogRel(("AHCI#%uP%u: Flush returned rc=%Rrc\n", + pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, rcReq)); else if (pAhciReq->enmTxDir == AHCITXDIR_TRIM) - LogRel(("AHCI#%u: Trim returned rc=%Rrc\n", - pAhciPort->iLUN, rcReq)); + LogRel(("AHCI#%uP%u: Trim returned rc=%Rrc\n", + pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, rcReq)); else - LogRel(("AHCI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n", - pAhciPort->iLUN, + LogRel(("AHCI#%uP%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n", + pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, pAhciReq->enmTxDir == AHCITXDIR_READ ? "Read" : "Write", @@ -5668,7 +5946,7 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR ASMAtomicCmpXchgPtr(&pAhciPort->pTaskErr, pAhciReq, NULL); } else - ASMAtomicOrU32(&pAhciPort->u32TasksNew, RT_BIT_32(pAhciReq->uTag)); + ASMAtomicOrU32(&pAhciPort->u32TasksRedo, RT_BIT_32(pAhciReq->uTag)); } else { @@ -5682,8 +5960,7 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR } /* Write updated command header into memory of the guest. */ - PDMDevHlpPhysWrite(pAhciPort->CTX_SUFF(pDevIns), pAhciReq->GCPhysCmdHdrAddr, - &pAhciReq->cmdHdr, sizeof(CmdHdr)); + PDMDevHlpPCIPhysWrite(pAhciPort->CTX_SUFF(pDevIns), pAhciReq->GCPhysCmdHdrAddr, &pAhciReq->cmdHdr, sizeof(CmdHdr)); if (pAhciReq->fFlags & AHCI_REQ_OVERFLOW) { @@ -5749,14 +6026,14 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR if (pAhciPort->cErrors++ < MAX_LOG_REL_ERRORS) { if (pAhciReq->enmTxDir == AHCITXDIR_FLUSH) - LogRel(("AHCI#%u: Canceled flush returned rc=%Rrc\n", - pAhciPort->iLUN, rcReq)); + LogRel(("AHCI#%uP%u: Canceled flush returned rc=%Rrc\n", + pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, rcReq)); else if (pAhciReq->enmTxDir == AHCITXDIR_TRIM) - LogRel(("AHCI#%u: Canceled trim returned rc=%Rrc\n", - pAhciPort->iLUN, rcReq)); + LogRel(("AHCI#%uP%u: Canceled trim returned rc=%Rrc\n", + pAhciPort->CTX_SUFF(pDevIns)->iInstance,pAhciPort->iLUN, rcReq)); else - LogRel(("AHCI#%u: Canceled %s at offset %llu (%u bytes left) returned rc=%Rrc\n", - pAhciPort->iLUN, + LogRel(("AHCI#%uP%u: Canceled %s at offset %llu (%u bytes left) returned rc=%Rrc\n", + pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, pAhciReq->enmTxDir == AHCITXDIR_READ ? "read" : "write", @@ -5769,6 +6046,9 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR RTMemFree(pAhciReq); } + if (pAhciPort->cTasksActive == 0 && pAhciPort->pAhciR3->fSignalIdle) + PDMDevHlpAsyncNotificationCompleted(pAhciPort->pDevInsR3); + return fCanceled; } @@ -5780,7 +6060,7 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR * @param pvUser User data. * @param rcReq IPRT Status code of the completed request. */ -static DECLCALLBACK(int) ahciTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser, int rcReq) +static DECLCALLBACK(int) ahciR3TransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser, int rcReq) { PAHCIPort pAhciPort = PDMIBLOCKASYNCPORT_2_PAHCIPORT(pInterface); PAHCIREQ pAhciReq = (PAHCIREQ)pvUser; @@ -5788,11 +6068,9 @@ static DECLCALLBACK(int) ahciTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterfa ahciLog(("%s: pInterface=%p pvUser=%p uTag=%u\n", __FUNCTION__, pInterface, pvUser, pAhciReq->uTag)); - int rc = ahciTransferComplete(pAhciPort, pAhciReq, rcReq, true); + ahciTransferComplete(pAhciPort, pAhciReq, rcReq, true); - if (pAhciPort->cTasksActive == 0 && pAhciPort->pAhciR3->fSignalIdle) - PDMDevHlpAsyncNotificationCompleted(pAhciPort->pDevInsR3); - return rc; + return VINF_SUCCESS; } /** @@ -5803,7 +6081,7 @@ static DECLCALLBACK(int) ahciTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterfa */ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t *pCmdFis) { - AHCITXDIR rc = AHCITXDIR_NONE; + AHCITXDIR enmTxDir = AHCITXDIR_NONE; bool fLBA48 = false; CmdHdr *pCmdHdr = &pAhciReq->cmdHdr; @@ -5817,7 +6095,6 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t { if (pAhciPort->pDrvBlock && !pAhciPort->fATAPI) { - int rc2; uint16_t u16Temp[256]; size_t cbCopied; @@ -5854,10 +6131,11 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_SEEK; break; case 0x82: /* write cache disable */ - rc = AHCITXDIR_FLUSH; + enmTxDir = AHCITXDIR_FLUSH; break; case 0x03: - { /* set transfer mode */ + { + /* set transfer mode */ Log2(("%s: transfer mode %#04x\n", __FUNCTION__, pCmdFis[AHCI_CMDFIS_SECTC])); switch (pCmdFis[AHCI_CMDFIS_SECTC] & 0xf8) { @@ -5895,7 +6173,7 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t } case ATA_FLUSH_CACHE_EXT: case ATA_FLUSH_CACHE: - rc = AHCITXDIR_FLUSH; + enmTxDir = AHCITXDIR_FLUSH; break; case ATA_PACKET: if (!pAhciPort->fATAPI) @@ -5904,7 +6182,7 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR; } else - rc = atapiParseCmd(pAhciPort, pAhciReq); + enmTxDir = atapiParseCmd(pAhciPort, pAhciReq); break; case ATA_IDENTIFY_PACKET_DEVICE: if (!pAhciPort->fATAPI) @@ -5957,34 +6235,34 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t fLBA48 = true; case ATA_READ_DMA: { - pAhciReq->cbTransfer = ahciGetNSectors(pCmdFis, fLBA48) * 512; - pAhciReq->uOffset = ahciGetSector(pAhciPort, pCmdFis, fLBA48) * 512; - rc = AHCITXDIR_READ; + pAhciReq->cbTransfer = ahciGetNSectors(pCmdFis, fLBA48) * pAhciPort->cbSector; + pAhciReq->uOffset = ahciGetSector(pAhciPort, pCmdFis, fLBA48) * pAhciPort->cbSector; + enmTxDir = AHCITXDIR_READ; break; } case ATA_WRITE_DMA_EXT: fLBA48 = true; case ATA_WRITE_DMA: { - pAhciReq->cbTransfer = ahciGetNSectors(pCmdFis, fLBA48) * 512; - pAhciReq->uOffset = ahciGetSector(pAhciPort, pCmdFis, fLBA48) * 512; - rc = AHCITXDIR_WRITE; + pAhciReq->cbTransfer = ahciGetNSectors(pCmdFis, fLBA48) * pAhciPort->cbSector; + pAhciReq->uOffset = ahciGetSector(pAhciPort, pCmdFis, fLBA48) * pAhciPort->cbSector; + enmTxDir = AHCITXDIR_WRITE; break; } case ATA_READ_FPDMA_QUEUED: { - pAhciReq->cbTransfer = ahciGetNSectorsQueued(pCmdFis) * 512; - pAhciReq->uOffset = ahciGetSectorQueued(pCmdFis) * 512; + pAhciReq->cbTransfer = ahciGetNSectorsQueued(pCmdFis) * pAhciPort->cbSector; + pAhciReq->uOffset = ahciGetSectorQueued(pCmdFis) * pAhciPort->cbSector; pAhciReq->fFlags |= AHCI_REQ_IS_QUEUED; - rc = AHCITXDIR_READ; + enmTxDir = AHCITXDIR_READ; break; } case ATA_WRITE_FPDMA_QUEUED: { - pAhciReq->cbTransfer = ahciGetNSectorsQueued(pCmdFis) * 512; - pAhciReq->uOffset = ahciGetSectorQueued(pCmdFis) * 512; + pAhciReq->cbTransfer = ahciGetNSectorsQueued(pCmdFis) * pAhciPort->cbSector; + pAhciReq->uOffset = ahciGetSectorQueued(pCmdFis) * pAhciPort->cbSector; pAhciReq->fFlags |= AHCI_REQ_IS_QUEUED; - rc = AHCITXDIR_WRITE; + enmTxDir = AHCITXDIR_WRITE; break; } case ATA_READ_LOG_EXT: @@ -6065,7 +6343,7 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_SEEK; } else - rc = AHCITXDIR_TRIM; + enmTxDir = AHCITXDIR_TRIM; break; } /* else: fall through and report error to the guest. */ @@ -6084,20 +6362,22 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR; } - return rc; + return enmTxDir; } /** * Retrieve a command FIS from guest memory. * - * @returns nothing + * @returns whether the H2D FIS was successfully read from the guest memory. * @param pAhciReq The state of the actual task. */ -static void ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq) +static bool ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq) { RTGCPHYS GCPhysAddrCmdTbl; - AssertMsg(pAhciPort->GCPhysAddrClb && pAhciPort->GCPhysAddrFb, ("%s: GCPhysAddrClb and/or GCPhysAddrFb are 0\n", __FUNCTION__)); + AssertMsgReturn(pAhciPort->GCPhysAddrClb && pAhciPort->GCPhysAddrFb, + ("%s: GCPhysAddrClb and/or GCPhysAddrFb are 0\n", __FUNCTION__), + false); /* * First we are reading the command header pointed to by regCLB. @@ -6109,20 +6389,25 @@ static void ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq) pAhciReq->GCPhysCmdHdrAddr, sizeof(CmdHdr))); PDMDevHlpPhysRead(pAhciPort->CTX_SUFF(pDevIns), pAhciReq->GCPhysCmdHdrAddr, &pAhciReq->cmdHdr, sizeof(CmdHdr)); -#ifdef DEBUG +#ifdef LOG_ENABLED /* Print some infos about the command header. */ ahciDumpCmdHdrInfo(pAhciPort, &pAhciReq->cmdHdr); #endif GCPhysAddrCmdTbl = AHCI_RTGCPHYS_FROM_U32(pAhciReq->cmdHdr.u32CmdTblAddrUp, pAhciReq->cmdHdr.u32CmdTblAddr); - AssertMsg((pAhciReq->cmdHdr.u32DescInf & AHCI_CMDHDR_CFL_MASK) * sizeof(uint32_t) == AHCI_CMDFIS_TYPE_H2D_SIZE, - ("This is not a command FIS!!\n")); + AssertMsgReturn((pAhciReq->cmdHdr.u32DescInf & AHCI_CMDHDR_CFL_MASK) * sizeof(uint32_t) == AHCI_CMDFIS_TYPE_H2D_SIZE, + ("This is not a command FIS!!\n"), + false); /* Read the command Fis. */ LogFlow(("%s: PDMDevHlpPhysRead GCPhysAddrCmdTbl=%RGp cbCmdFis=%u\n", __FUNCTION__, GCPhysAddrCmdTbl, AHCI_CMDFIS_TYPE_H2D_SIZE)); PDMDevHlpPhysRead(pAhciPort->CTX_SUFF(pDevIns), GCPhysAddrCmdTbl, &pAhciReq->cmdFis[0], AHCI_CMDFIS_TYPE_H2D_SIZE); + AssertMsgReturn(pAhciReq->cmdFis[AHCI_CMDFIS_TYPE] == AHCI_CMDFIS_TYPE_H2D, + ("This is not a command FIS\n"), + false); + /* Set transfer direction. */ pAhciReq->enmTxDir = (pAhciReq->cmdHdr.u32DescInf & AHCI_CMDHDR_W) ? AHCITXDIR_WRITE : AHCITXDIR_READ; @@ -6147,7 +6432,7 @@ static void ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq) pAhciReq->GCPhysPrdtl = AHCI_RTGCPHYS_FROM_U32(pAhciReq->cmdHdr.u32CmdTblAddrUp, pAhciReq->cmdHdr.u32CmdTblAddr) + AHCI_CMDHDR_PRDT_OFFSET; pAhciReq->cPrdtlEntries = AHCI_CMDHDR_PRDTL_ENTRIES(pAhciReq->cmdHdr.u32DescInf); -#ifdef DEBUG +#ifdef LOG_ENABLED /* Print some infos about the FIS. */ ahciDumpFisInfo(pAhciPort, &pAhciReq->cmdFis[0]); @@ -6168,6 +6453,8 @@ static void ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq) GCPhysPrdtl += sizeof(SGLEntry); } #endif + + return true; } /** @@ -6182,25 +6469,73 @@ static void ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq) static DECLCALLBACK(bool) ahciNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem) { PDEVPORTNOTIFIERQUEUEITEM pNotifierItem = (PDEVPORTNOTIFIERQUEUEITEM)pItem; - PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI); - PAHCIPort pAhciPort = &pAhci->ahciPort[pNotifierItem->iPort]; + PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI); + PAHCIPort pAhciPort = &pThis->ahciPort[pNotifierItem->iPort]; int rc = VINF_SUCCESS; - if (!pAhciPort->fAsyncInterface) - { - ahciLog(("%s: Got notification from GC\n", __FUNCTION__)); - /* Notify the async IO thread. */ - rc = RTSemEventSignal(pAhciPort->AsyncIORequestSem); - AssertRC(rc); - } - else + ahciLog(("%s: Got notification from GC\n", __FUNCTION__)); + /* Notify the async IO thread. */ + rc = SUPSemEventSignal(pThis->pSupDrvSession, pAhciPort->hEvtProcess); + AssertRC(rc); + + return true; +} + +/* The async IO thread for one port. */ +static DECLCALLBACK(int) ahciAsyncIOLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread) +{ + PAHCIPort pAhciPort = (PAHCIPort)pThread->pvUser; + PAHCI pAhci = pAhciPort->CTX_SUFF(pAhci); + int rc = VINF_SUCCESS; + uint64_t u64StartTime = 0; + uint64_t u64StopTime = 0; + uint32_t uIORequestsProcessed = 0; + uint32_t uIOsPerSec = 0; + uint32_t fTasksToProcess = 0; + + ahciLog(("%s: Port %d entering async IO loop.\n", __FUNCTION__, pAhciPort->iLUN)); + + if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) + return VINF_SUCCESS; + + while (pThread->enmState == PDMTHREADSTATE_RUNNING) { unsigned idx = 0; - uint32_t u32Tasks = ASMAtomicXchgU32(&pAhciPort->u32TasksNew, 0); + uint32_t u32Tasks = 0; + uint32_t u32RegHbaCtrl = 0; + + ASMAtomicWriteBool(&pAhciPort->fWrkThreadSleeping, true); + u32Tasks = ASMAtomicXchgU32(&pAhciPort->u32TasksNew, 0); + if (!u32Tasks) + { + Assert(ASMAtomicReadBool(&pAhciPort->fWrkThreadSleeping)); + rc = SUPSemEventWaitNoResume(pAhci->pSupDrvSession, pAhciPort->hEvtProcess, RT_INDEFINITE_WAIT); + AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc); + if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING)) + break; + LogFlowFunc(("Woken up with rc=%Rrc\n", rc)); + u32Tasks = ASMAtomicXchgU32(&pAhciPort->u32TasksNew, 0); + } + + ASMAtomicWriteBool(&pAhciPort->fWrkThreadSleeping, false); + ASMAtomicIncU32(&pAhci->cThreadsActive); + + /* + * Check whether the global host controller bit is set and go to sleep immediately again + * if it is set. + */ + u32RegHbaCtrl = ASMAtomicReadU32(&pAhci->regHbaCtrl); + if ( u32RegHbaCtrl & AHCI_HBA_CTRL_HR + && !ASMAtomicDecU32(&pAhci->cThreadsActive)) + { + ahciHBAReset(pAhci); + continue; + } idx = ASMBitFirstSetU32(u32Tasks); while (idx) { + bool fReqCanceled = false; AHCITXDIR enmTxDir; PAHCIREQ pAhciReq; @@ -6226,6 +6561,7 @@ static DECLCALLBACK(bool) ahciNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEI ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_ACTIVE, AHCITXSTATE_FREE, fXchg); AssertMsg(fXchg, ("Task is already active\n")); + pAhciReq->tsStart = RTTimeMilliTS(); pAhciReq->uATARegStatus = 0; pAhciReq->uATARegError = 0; pAhciReq->fFlags = 0; @@ -6234,7 +6570,23 @@ static DECLCALLBACK(bool) ahciNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEI pAhciReq->uTag = idx; ASMAtomicWriteU32(&pAhciPort->u32CurrentCommandSlot, pAhciReq->uTag); - ahciPortTaskGetCommandFis(pAhciPort, pAhciReq); + bool fFisRead = ahciPortTaskGetCommandFis(pAhciPort, pAhciReq); + if (RT_UNLIKELY(!fFisRead)) + { + /* + * Couldn't find anything in either the AHCI or SATA spec which + * indicates what should be done if the FIS is not read successfully. + * The closest thing is in the state machine, stating that the device + * should go into idle state again (SATA spec 1.0 chapter 8.7.1). + * Do the same here and ignore any corrupt FIS types, after all + * the guest messed up everything and this behavior is undefined. + */ + ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg); + Assert(fXchg); + u32Tasks &= ~RT_BIT_32(idx); /* Clear task bit. */ + idx = ASMBitFirstSetU32(u32Tasks); + continue; + } /* Mark the task as processed by the HBA if this is a queued task so that it doesn't occur in the CI register anymore. */ if (pAhciPort->regSACT & (1 << idx)) @@ -6251,21 +6603,15 @@ static DECLCALLBACK(bool) ahciNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEI ahciLog(("%s: Setting device into reset state\n", __FUNCTION__)); pAhciPort->fResetDevice = true; ahciSendD2HFis(pAhciPort, pAhciReq, pAhciReq->cmdFis, true); - - ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg); - AssertMsg(fXchg, ("Task is not active\n")); - return true; } else if (pAhciPort->fResetDevice) /* The bit is not set and we are in a reset state. */ - { ahciFinishStorageDeviceReset(pAhciPort, pAhciReq); - - ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg); - AssertMsg(fXchg, ("Task is not active\n")); - return true; - } else /* We are not in a reset state update the control registers. */ AssertMsgFailed(("%s: Update the control register\n", __FUNCTION__)); + + ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg); + AssertMsg(fXchg, ("Task is not active\n")); + break; } else { @@ -6290,282 +6636,106 @@ static DECLCALLBACK(bool) ahciNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEI if (!(pAhciReq->fFlags & AHCI_REQ_OVERFLOW)) { - if (enmTxDir == AHCITXDIR_FLUSH) - { - rc = pAhciPort->pDrvBlockAsync->pfnStartFlush(pAhciPort->pDrvBlockAsync, - pAhciReq); - } - else if (enmTxDir == AHCITXDIR_TRIM) + if (pAhciPort->fAsyncInterface) { - rc = ahciTrimRangesCreate(pAhciPort, pAhciReq); - if (RT_SUCCESS(rc)) + VBOXDD_AHCI_REQ_SUBMIT(pAhciReq, enmTxDir, pAhciReq->uOffset, pAhciReq->cbTransfer); + VBOXDD_AHCI_REQ_SUBMIT_TIMESTAMP(pAhciReq, pAhciReq->tsStart); + if (enmTxDir == AHCITXDIR_FLUSH) + { + rc = pAhciPort->pDrvBlockAsync->pfnStartFlush(pAhciPort->pDrvBlockAsync, + pAhciReq); + } + else if (enmTxDir == AHCITXDIR_TRIM) + { + rc = ahciTrimRangesCreate(pAhciPort, pAhciReq); + if (RT_SUCCESS(rc)) + { + pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1; + rc = pAhciPort->pDrvBlockAsync->pfnStartDiscard(pAhciPort->pDrvBlockAsync, pAhciReq->u.Trim.paRanges, + pAhciReq->u.Trim.cRanges, pAhciReq); + } + } + else if (enmTxDir == AHCITXDIR_READ) + { + pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1; + rc = pAhciPort->pDrvBlockAsync->pfnStartRead(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset, + &pAhciReq->u.Io.DataSeg, 1, + pAhciReq->cbTransfer, + pAhciReq); + } + else { pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1; - rc = pAhciPort->pDrvBlockAsync->pfnStartDiscard(pAhciPort->pDrvBlockAsync, pAhciReq->u.Trim.paRanges, - pAhciReq->u.Trim.cRanges, pAhciReq); + rc = pAhciPort->pDrvBlockAsync->pfnStartWrite(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset, + &pAhciReq->u.Io.DataSeg, 1, + pAhciReq->cbTransfer, + pAhciReq); } - } - else if (enmTxDir == AHCITXDIR_READ) - { - pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1; - rc = pAhciPort->pDrvBlockAsync->pfnStartRead(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset, - &pAhciReq->u.Io.DataSeg, 1, - pAhciReq->cbTransfer, - pAhciReq); + if (rc == VINF_VD_ASYNC_IO_FINISHED) + fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true); + else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS) + fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc, true); } else { - pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1; - rc = pAhciPort->pDrvBlockAsync->pfnStartWrite(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset, - &pAhciReq->u.Io.DataSeg, 1, - pAhciReq->cbTransfer, - pAhciReq); + if (enmTxDir == AHCITXDIR_FLUSH) + rc = pAhciPort->pDrvBlock->pfnFlush(pAhciPort->pDrvBlock); + else if (enmTxDir == AHCITXDIR_TRIM) + { + rc = ahciTrimRangesCreate(pAhciPort, pAhciReq); + if (RT_SUCCESS(rc)) + { + pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1; + rc = pAhciPort->pDrvBlock->pfnDiscard(pAhciPort->pDrvBlock, pAhciReq->u.Trim.paRanges, + pAhciReq->u.Trim.cRanges); + pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 0; + } + } + else if (enmTxDir == AHCITXDIR_READ) + { + pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1; + rc = pAhciPort->pDrvBlock->pfnRead(pAhciPort->pDrvBlock, pAhciReq->uOffset, + pAhciReq->u.Io.DataSeg.pvSeg, + pAhciReq->cbTransfer); + pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 0; + } + else + { + pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1; + rc = pAhciPort->pDrvBlock->pfnWrite(pAhciPort->pDrvBlock, pAhciReq->uOffset, + pAhciReq->u.Io.DataSeg.pvSeg, + pAhciReq->cbTransfer); + pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 0; + } + fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc, true); } - if (rc == VINF_VD_ASYNC_IO_FINISHED) - rc = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true); - else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS) - rc = ahciTransferComplete(pAhciPort, pAhciReq, rc, true); } } else - rc = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true); + fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true); } /* Command */ - u32Tasks &= ~RT_BIT_32(idx); /* Clear task bit. */ - idx = ASMBitFirstSetU32(u32Tasks); - } /* while tasks available */ - } /* fUseAsyncInterface */ - - return true; -} - -/* The async IO thread for one port. */ -static DECLCALLBACK(int) ahciAsyncIOLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread) -{ - PAHCIPort pAhciPort = (PAHCIPort)pThread->pvUser; - PAHCI pAhci = pAhciPort->CTX_SUFF(pAhci); - PAHCIREQ pAhciReq; - int rc = VINF_SUCCESS; - uint64_t u64StartTime = 0; - uint64_t u64StopTime = 0; - uint32_t uIORequestsProcessed = 0; - uint32_t uIOsPerSec = 0; - uint32_t fTasksToProcess = 0; - unsigned idx = 0; - - ahciLog(("%s: Port %d entering async IO loop.\n", __FUNCTION__, pAhciPort->iLUN)); - - if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) - return VINF_SUCCESS; - - /* We use only one task structure. */ - pAhciReq = (PAHCIREQ)RTMemAllocZ(sizeof(AHCIREQ)); - if (!pAhciReq) - { - AssertMsgFailed(("Failed to allocate task state memory\n")); - return VERR_NO_MEMORY; - } - - pAhciReq->enmTxState = AHCITXSTATE_FREE; - - while (pThread->enmState == PDMTHREADSTATE_RUNNING) - { - /* New run to get number of I/O requests per second?. */ - if (!u64StartTime) - u64StartTime = RTTimeMilliTS(); - - ASMAtomicXchgBool(&pAhciPort->fAsyncIOThreadIdle, true); - if (pAhci->fSignalIdle) - PDMDevHlpAsyncNotificationCompleted(pAhciPort->pDevInsR3); - - rc = RTSemEventWait(pAhciPort->AsyncIORequestSem, 1000); - if (rc == VERR_TIMEOUT) - { - /* No I/O requests in-between. Reset statistics and wait again. */ - pAhciPort->StatIORequestsPerSecond.c = 0; - rc = RTSemEventWait(pAhciPort->AsyncIORequestSem, RT_INDEFINITE_WAIT); - } - - if (RT_FAILURE(rc) || (pThread->enmState != PDMTHREADSTATE_RUNNING)) - break; - - /* Go to sleep again if we are in redo mode. */ - if (RT_UNLIKELY(pAhciPort->fRedo)) - continue; - - AssertMsg(pAhciPort->pDrvBase, ("I/O thread without attached device?!\n")); - - ASMAtomicXchgBool(&pAhciPort->fAsyncIOThreadIdle, false); - fTasksToProcess = ASMAtomicXchgU32(&pAhciPort->u32TasksNew, 0); - - idx = ASMBitFirstSetU32(fTasksToProcess); - - /* Process commands. */ - while ( idx - && RT_LIKELY(!pAhciPort->fPortReset)) - { - bool fReqCanceled = false; - AHCITXDIR enmTxDir; - - idx--; - STAM_PROFILE_START(&pAhciPort->StatProfileProcessTime, a); - - pAhciReq->uATARegStatus = 0; - pAhciReq->uATARegError = 0; - pAhciReq->fFlags = 0; - pAhciReq->uTag = idx; - AssertMsg(pAhciReq->uTag < AHCI_NR_COMMAND_SLOTS, ("%s: Invalid Tag number %u!!\n", __FUNCTION__, pAhciReq->uTag)); - - bool fXchg; - ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_ACTIVE, AHCITXSTATE_FREE, fXchg); - AssertMsg(fXchg, ("Task is already active\n")); - - /* Set current command slot */ - ASMAtomicWriteU32(&pAhciPort->u32CurrentCommandSlot, pAhciReq->uTag); - pAhciPort->aCachedTasks[0] = pAhciReq; /* Make cancelling the request possible. */ - - /* Mark the task as processed by the HBA if this is a queued task so that it doesn't occur in the CI register anymore. */ - if (pAhciPort->regSACT & (1 << idx)) - { - pAhciReq->fFlags |= AHCI_REQ_CLEAR_SACT; - ASMAtomicOrU32(&pAhciPort->u32TasksFinished, (1 << pAhciReq->uTag)); - } - - ahciPortTaskGetCommandFis(pAhciPort, pAhciReq); - - ahciLog(("%s: Got command at slot %d\n", __FUNCTION__, pAhciReq->uTag)); - - if (!(pAhciReq->cmdFis[AHCI_CMDFIS_BITS] & AHCI_CMDFIS_C)) - { - /* If the reset bit is set put the device into reset state. */ - if (pAhciReq->cmdFis[AHCI_CMDFIS_CTL] & AHCI_CMDFIS_CTL_SRST) - { - ahciLog(("%s: Setting device into reset state\n", __FUNCTION__)); - pAhciPort->fResetDevice = true; - ahciSendD2HFis(pAhciPort, pAhciReq, &pAhciReq->cmdFis[0], true); - } - else if (pAhciPort->fResetDevice) /* The bit is not set and we are in a reset state. */ - { - ahciFinishStorageDeviceReset(pAhciPort, pAhciReq); - } - /* TODO: We are not in a reset state update the control registers. */ - - ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg); - AssertMsg(fXchg, ("Task is already free\n")); - } - else - { - AssertReleaseMsg(ASMAtomicReadU32(&pAhciPort->cTasksActive) < AHCI_NR_COMMAND_SLOTS, - ("There are more than 32 requests active")); - ASMAtomicIncU32(&pAhciPort->cTasksActive); - enmTxDir = ahciProcessCmd(pAhciPort, pAhciReq, &pAhciReq->cmdFis[0]); - pAhciReq->enmTxDir = enmTxDir; - - if (enmTxDir == AHCITXDIR_FLUSH) - rc = pAhciPort->pDrvBlock->pfnFlush(pAhciPort->pDrvBlock); - else if (enmTxDir == AHCITXDIR_TRIM) - { - rc = ahciTrimRangesCreate(pAhciPort, pAhciReq); - if (RT_SUCCESS(rc)) - { - pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1; - rc = pAhciPort->pDrvBlock->pfnDiscard(pAhciPort->pDrvBlock, - pAhciReq->u.Trim.paRanges, - pAhciReq->u.Trim.cRanges); - pAhciPort->Led.Actual.s.fWriting = 0; - } - } - else if (enmTxDir != AHCITXDIR_NONE) - { - uint64_t uOffset = 0; - size_t cbTransfer = 0; - - rc = ahciIoBufAllocate(pAhciPort->pDevInsR3, pAhciReq, pAhciReq->cbTransfer); - if (RT_FAILURE(rc)) - AssertMsgFailed(("%s: Failed to get number of list elments %Rrc\n", __FUNCTION__, rc)); - - if (!(pAhciReq->fFlags & AHCI_REQ_OVERFLOW)) - { - STAM_REL_COUNTER_INC(&pAhciPort->StatDMA); - - /* Initialize all values. */ - uOffset = pAhciReq->uOffset; - cbTransfer = pAhciReq->cbTransfer; - - STAM_PROFILE_START(&pAhciPort->StatProfileReadWrite, b); - - AssertMsg(!(uOffset % 512), ("Offset is not sector aligned %llu\n", uOffset)); - AssertMsg(!(cbTransfer % 512), ("Number of bytes to process is not sector aligned %lu\n", cbTransfer)); - - if (enmTxDir == AHCITXDIR_READ) - { - pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1; - rc = pAhciPort->pDrvBlock->pfnRead(pAhciPort->pDrvBlock, uOffset, - pAhciReq->u.Io.DataSeg.pvSeg, - cbTransfer); - pAhciPort->Led.Actual.s.fReading = 0; - STAM_REL_COUNTER_ADD(&pAhciPort->StatBytesRead, cbTransfer); - } - else - { - pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1; - rc = pAhciPort->pDrvBlock->pfnWrite(pAhciPort->pDrvBlock, uOffset, - pAhciReq->u.Io.DataSeg.pvSeg, - cbTransfer); - pAhciPort->Led.Actual.s.fWriting = 0; - STAM_REL_COUNTER_ADD(&pAhciPort->StatBytesWritten, cbTransfer); - } - - STAM_PROFILE_STOP(&pAhciPort->StatProfileReadWrite, b); - } - } - - fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc, false /* fFreeReq */); - uIORequestsProcessed++; - STAM_PROFILE_STOP(&pAhciPort->StatProfileProcessTime, a); - } - - if (!pAhciPort->fRedo) - { -#ifdef DEBUG - /* Be paranoid. */ - memset(&pAhciReq->cmdHdr, 0, sizeof(CmdHdr)); - memset(&pAhciReq->cmdFis, 0, AHCI_CMDFIS_TYPE_H2D_SIZE); - pAhciReq->GCPhysCmdHdrAddr = 0; - pAhciReq->uOffset = 0; - pAhciReq->cbTransfer = 0; -#endif - } - /* * Don't process other requests if the last one was canceled, * the others are not valid anymore. */ if (fReqCanceled) break; - fTasksToProcess &= ~(1 << idx); - idx = ASMBitFirstSetU32(fTasksToProcess); - } /* while tasks to process */ - - u64StopTime = RTTimeMilliTS(); - /* Check if one second has passed. */ - if (u64StopTime - u64StartTime >= 1000) - { - /* Calculate number of I/O requests per second. */ - uIOsPerSec = uIORequestsProcessed / ((u64StopTime - u64StartTime) / 1000); - ahciLog(("%s: Processed %u requests in %llu ms -> %u requests/s\n", __FUNCTION__, uIORequestsProcessed, u64StopTime - u64StartTime, uIOsPerSec)); - u64StartTime = 0; - uIORequestsProcessed = 0; - /* For the release statistics. There is no macro to set the counter to a specific value. */ - pAhciPort->StatIORequestsPerSecond.c = uIOsPerSec; - } - } /* While running */ - if (pAhci->fSignalIdle) - PDMDevHlpAsyncNotificationCompleted(pAhciPort->pDevInsR3); + u32Tasks &= ~RT_BIT_32(idx); /* Clear task bit. */ + idx = ASMBitFirstSetU32(u32Tasks); + } /* while tasks available */ - RTMemFree(pAhciReq); - memset(pAhciPort->aCachedTasks, 0, sizeof(pAhciPort->aCachedTasks)); + /* + * Check whether a host controller reset is pending and execute the reset + * if this is the last active thread. + */ + u32RegHbaCtrl = ASMAtomicReadU32(&pAhci->regHbaCtrl); + uint32_t cThreadsActive = ASMAtomicDecU32(&pAhci->cThreadsActive); + if ( (u32RegHbaCtrl & AHCI_HBA_CTRL_HR) + && !cThreadsActive) + ahciHBAReset(pAhci); + } /* While running */ ahciLog(("%s: Port %d async IO thread exiting\n", __FUNCTION__, pAhciPort->iLUN)); return VINF_SUCCESS; @@ -6575,13 +6745,14 @@ static DECLCALLBACK(int) ahciAsyncIOLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread) * Unblock the async I/O thread so it can respond to a state change. * * @returns VBox status code. - * @param pDevIns The pcnet device instance. + * @param pDevIns The device instance. * @param pThread The send thread. */ static DECLCALLBACK(int) ahciAsyncIOLoopWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread) { + PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI); PAHCIPort pAhciPort = (PAHCIPort)pThread->pvUser; - return RTSemEventSignal(pAhciPort->AsyncIORequestSem); + return SUPSemEventSignal(pThis->pSupDrvSession, pAhciPort->hEvtProcess); } /* -=-=-=-=- DBGF -=-=-=-=- */ @@ -6653,7 +6824,6 @@ static DECLCALLBACK(void) ahciR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, con pHlp->pfnPrintf(pHlp, "PortATAPI=%RTbool\n", pThisPort->fATAPI); pHlp->pfnPrintf(pHlp, "PortTasksFinished=%#x\n", pThisPort->u32TasksFinished); pHlp->pfnPrintf(pHlp, "PortQueuedTasksFinished=%#x\n", pThisPort->u32QueuedTasksFinished); - pHlp->pfnPrintf(pHlp, "PortAsyncIoThreadIdle=%RTbool\n", pThisPort->fAsyncIOThreadIdle); pHlp->pfnPrintf(pHlp, "\n"); } } @@ -6673,17 +6843,16 @@ static bool ahciR3AllAsyncIOIsFinished(PPDMDEVINS pDevIns) { PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI); + if (pThis->cThreadsActive) + return false; + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->ahciPort); i++) { PAHCIPort pThisPort = &pThis->ahciPort[i]; if (pThisPort->pDrvBase) { - bool fFinished; - if (pThisPort->fAsyncInterface) - fFinished = (pThisPort->cTasksActive == 0); - else - fFinished = ((pThisPort->cTasksActive == 0) && (pThisPort->fAsyncIOThreadIdle)); - if (!fFinished) + if ( (pThisPort->cTasksActive != 0) + || (pThisPort->u32TasksNew != 0)) return false; } } @@ -7099,59 +7268,6 @@ static DECLCALLBACK(void) ahciR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta } /** - * Destroy a driver instance. - * - * Most VM resources are freed by the VM. This callback is provided so that any non-VM - * resources can be freed correctly. - * - * @param pDevIns The device instance data. - */ -static DECLCALLBACK(int) ahciR3Destruct(PPDMDEVINS pDevIns) -{ - PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI); - int rc = VINF_SUCCESS; - unsigned iActPort = 0; - PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); - - /* - * At this point the async I/O thread is suspended and will not enter - * this module again. So, no coordination is needed here and PDM - * will take care of terminating and cleaning up the thread. - */ - if (PDMCritSectIsInitialized(&pAhci->lock)) - { - TMR3TimerDestroy(pAhci->CTX_SUFF(pHbaCccTimer)); - - Log(("%s: Destruct every port\n", __FUNCTION__)); - for (iActPort = 0; iActPort < pAhci->cPortsImpl; iActPort++) - { - PAHCIPort pAhciPort = &pAhci->ahciPort[iActPort]; - - if (pAhciPort->pAsyncIOThread) - { - /* Destroy the event semaphore. */ - rc = RTSemEventDestroy(pAhciPort->AsyncIORequestSem); - if (RT_FAILURE(rc)) - { - Log(("%s: Destroying event semaphore for port %d failed rc=%Rrc\n", __FUNCTION__, iActPort, rc)); - } - } - - /* Free all cached tasks. */ - for (uint32_t i = 0; i < AHCI_NR_COMMAND_SLOTS; i++) - { - if (pAhciPort->aCachedTasks[i]) - RTMemFree(pAhciPort->aCachedTasks[i]); - } - } - - PDMR3CritSectDelete(&pAhci->lock); - } - - return rc; -} - -/** * SCSI_GET_EVENT_STATUS_NOTIFICATION should return "medium removed" event * from now on, regardless if there was a medium inserted or not. */ @@ -7160,7 +7276,6 @@ static void ahciMediumRemoved(PAHCIPort pAhciPort) ASMAtomicWriteU32(&pAhciPort->MediaEventStatus, ATA_EVENT_STATUS_MEDIA_REMOVED); } - /** * SCSI_GET_EVENT_STATUS_NOTIFICATION should return "medium inserted". If * there was already a medium inserted, don't forget to send the "medium @@ -7191,7 +7306,7 @@ static void ahciMediumInserted(PAHCIPort pAhciPort) * * @param pInterface Pointer to the interface structure containing the called function pointer. */ -static DECLCALLBACK(void) ahciMountNotify(PPDMIMOUNTNOTIFY pInterface) +static DECLCALLBACK(void) ahciR3MountNotify(PPDMIMOUNTNOTIFY pInterface) { PAHCIPort pAhciPort = PDMIMOUNTNOTIFY_2_PAHCIPORT(pInterface); Log(("%s: changing LUN#%d\n", __FUNCTION__, pAhciPort->iLUN)); @@ -7210,7 +7325,7 @@ static DECLCALLBACK(void) ahciMountNotify(PPDMIMOUNTNOTIFY pInterface) if (pAhciPort->cNotifiedMediaChange < 2) pAhciPort->cNotifiedMediaChange = 2; ahciMediumInserted(pAhciPort); - ataMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN); + ahciMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN); } else AssertMsgFailed(("Hard disks don't have a mount interface!\n")); @@ -7220,7 +7335,7 @@ static DECLCALLBACK(void) ahciMountNotify(PPDMIMOUNTNOTIFY pInterface) * Called when a media is unmounted * @param pInterface Pointer to the interface structure containing the called function pointer. */ -static DECLCALLBACK(void) ahciUnmountNotify(PPDMIMOUNTNOTIFY pInterface) +static DECLCALLBACK(void) ahciR3UnmountNotify(PPDMIMOUNTNOTIFY pInterface) { PAHCIPort pAhciPort = PDMIMOUNTNOTIFY_2_PAHCIPORT(pInterface); Log(("%s:\n", __FUNCTION__)); @@ -7237,7 +7352,7 @@ static DECLCALLBACK(void) ahciUnmountNotify(PPDMIMOUNTNOTIFY pInterface) */ pAhciPort->cNotifiedMediaChange = 4; ahciMediumRemoved(pAhciPort); - ataMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN); + ahciMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN); } else AssertMsgFailed(("Hard disks don't have a mount interface!\n")); @@ -7310,7 +7425,8 @@ static int ahciR3ConfigureLUN(PPDMDEVINS pDevIns, PAHCIPort pAhciPort) } else { - pAhciPort->cTotalSectors = pAhciPort->pDrvBlock->pfnGetSize(pAhciPort->pDrvBlock) / 512; + pAhciPort->cbSector = pAhciPort->pDrvBlock->pfnGetSectorSize(pAhciPort->pDrvBlock); + pAhciPort->cTotalSectors = pAhciPort->pDrvBlock->pfnGetSize(pAhciPort->pDrvBlock) / pAhciPort->cbSector; rc = pAhciPort->pDrvBlockBios->pfnGetPCHSGeometry(pAhciPort->pDrvBlockBios, &pAhciPort->PCHSGeometry); if (rc == VERR_PDM_MEDIA_NOT_MOUNTED) @@ -7407,11 +7523,14 @@ static DECLCALLBACK(void) ahciR3Resume(PPDMDEVINS pDevIns) { PAHCIPort pAhciPort = &pAhci->ahciPort[i]; - if (pAhciPort->u32TasksNew) + if (pAhciPort->u32TasksRedo) { PDEVPORTNOTIFIERQUEUEITEM pItem = (PDEVPORTNOTIFIERQUEUEITEM)PDMQueueAlloc(pAhci->CTX_SUFF(pNotifierQueue)); AssertMsg(pItem, ("Allocating item for queue failed\n")); + pAhciPort->u32TasksNew |= pAhciPort->u32TasksRedo; + pAhciPort->u32TasksRedo = 0; + Assert(pAhciPort->fRedo); pAhciPort->fRedo = false; @@ -7494,6 +7613,14 @@ static int ahciR3VpdInit(PPDMDEVINS pDevIns, PAHCIPort pAhciPort, const char *ps return PDMDEV_SET_ERROR(pDevIns, rc, N_("AHCI configuration error: failed to read \"NonRotationalMedium\" as boolean")); + rc = CFGMR3QueryU8Def(pCfgNode, "LogicalSectorsPerPhysical", &pAhciPort->cLogSectorsPerPhysicalExp, 0); + if (RT_FAILURE(rc)) + return PDMDEV_SET_ERROR(pDevIns, rc, + N_("AHCI configuration error: failed to read \"LogicalSectorsPerPhysical\" as integer")); + if (pAhciPort->cLogSectorsPerPhysicalExp >= 16) + return PDMDEV_SET_ERROR(pDevIns, rc, + N_("AHCI configuration error: \"LogicalSectorsPerPhysical\" must be between 0 and 15")); + /* There are three other identification strings for CD drives used for INQUIRY */ if (pAhciPort->fATAPI) { @@ -7555,7 +7682,7 @@ static DECLCALLBACK(void) ahciR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32 AssertMsg(iLUN < pAhci->cPortsImpl, ("iLUN=%u", iLUN)); - if (!pAhciPort->fAsyncInterface) + if (pAhciPort->pAsyncIOThread) { int rcThread; /* Destroy the thread. */ @@ -7564,10 +7691,6 @@ static DECLCALLBACK(void) ahciR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32 AssertMsgFailed(("%s Failed to destroy async IO thread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread)); pAhciPort->pAsyncIOThread = NULL; - - rc = RTSemEventDestroy(pAhciPort->AsyncIORequestSem); - if (RT_FAILURE(rc)) - AssertMsgFailed(("%s: Failed to destroy the event semaphore rc=%Rrc.\n", __FUNCTION__, rc)); } if (pAhciPort->fATAPI) @@ -7579,13 +7702,14 @@ static DECLCALLBACK(void) ahciR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32 * Inform the guest about the removed device. */ pAhciPort->regSSTS = 0; + pAhciPort->regSIG = 0; /* * Clear CR bit too to prevent submission of new commands when CI is written * (AHCI Spec 1.2: 7.4 Interaction of the Command List and Port Change Status). */ ASMAtomicAndU32(&pAhciPort->regCMD, ~(AHCI_PORT_CMD_CPS | AHCI_PORT_CMD_CR)); - ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_CPDS | AHCI_PORT_IS_PRCS); - ASMAtomicOrU32(&pAhciPort->regSERR, AHCI_PORT_SERR_N); + ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_CPDS | AHCI_PORT_IS_PRCS | AHCI_PORT_IS_PCS); + ASMAtomicOrU32(&pAhciPort->regSERR, AHCI_PORT_SERR_X | AHCI_PORT_SERR_N); if ( (pAhciPort->regIE & AHCI_PORT_IE_CPDE) || (pAhciPort->regIE & AHCI_PORT_IE_PCE) || (pAhciPort->regIE & AHCI_PORT_IE_PRCE)) @@ -7614,14 +7738,14 @@ static DECLCALLBACK(void) ahciR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32 */ static DECLCALLBACK(int) ahciR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) { - PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI); - PAHCIPort pAhciPort = &pAhci->ahciPort[iLUN]; + PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI); + PAHCIPort pAhciPort = &pThis->ahciPort[iLUN]; int rc; Log(("%s:\n", __FUNCTION__)); /* the usual paranoia */ - AssertMsg(iLUN < pAhci->cPortsImpl, ("iLUN=%u", iLUN)); + AssertMsg(iLUN < pThis->cPortsImpl, ("iLUN=%u", iLUN)); AssertRelease(!pAhciPort->pDrvBase); AssertRelease(!pAhciPort->pDrvBlock); AssertRelease(!pAhciPort->pDrvBlockAsync); @@ -7633,7 +7757,15 @@ static DECLCALLBACK(int) ahciR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32 */ rc = PDMDevHlpDriverAttach(pDevIns, pAhciPort->iLUN, &pAhciPort->IBase, &pAhciPort->pDrvBase, NULL); if (RT_SUCCESS(rc)) + { rc = ahciR3ConfigureLUN(pDevIns, pAhciPort); + + /* + * In case there is a medium inserted. + */ + ahciMediumInserted(pAhciPort); + ahciMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN); + } else AssertMsgFailed(("Failed to attach LUN#%d. rc=%Rrc\n", pAhciPort->iLUN, rc)); @@ -7651,26 +7783,18 @@ static DECLCALLBACK(int) ahciR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32 && !pAhciPort->fATAPI) pAhciPort->fAsyncInterface = true; else - { pAhciPort->fAsyncInterface = false; - /* Create event semaphore. */ - rc = RTSemEventCreate(&pAhciPort->AsyncIORequestSem); - if (RT_FAILURE(rc)) - { - Log(("%s: Failed to create event semaphore for %s.\n", __FUNCTION__, szName)); - return rc; - } + rc = SUPSemEventCreate(pThis->pSupDrvSession, &pAhciPort->hEvtProcess); + if (RT_FAILURE(rc)) + return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, + N_("AHCI: Failed to create SUP event semaphore")); - /* Create the async IO thread. */ - rc = PDMDevHlpThreadCreate(pDevIns, &pAhciPort->pAsyncIOThread, pAhciPort, ahciAsyncIOLoop, ahciAsyncIOLoopWakeUp, 0, - RTTHREADTYPE_IO, szName); - if (RT_FAILURE(rc)) - { - AssertMsgFailed(("%s: Async IO Thread creation for %s failed rc=%d\n", __FUNCTION__, szName, rc)); - return rc; - } - } + /* Create the async IO thread. */ + rc = PDMDevHlpThreadCreate(pDevIns, &pAhciPort->pAsyncIOThread, pAhciPort, ahciAsyncIOLoop, ahciAsyncIOLoopWakeUp, 0, + RTTHREADTYPE_IO, szName); + if (RT_FAILURE(rc)) + return rc; /* * Init vendor product data. @@ -7774,6 +7898,56 @@ static DECLCALLBACK(void) ahciR3PowerOff(PPDMDEVINS pDevIns) } /** + * Destroy a driver instance. + * + * Most VM resources are freed by the VM. This callback is provided so that any non-VM + * resources can be freed correctly. + * + * @param pDevIns The device instance data. + */ +static DECLCALLBACK(int) ahciR3Destruct(PPDMDEVINS pDevIns) +{ + PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI); + int rc = VINF_SUCCESS; + PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); + + /* + * At this point the async I/O thread is suspended and will not enter + * this module again. So, no coordination is needed here and PDM + * will take care of terminating and cleaning up the thread. + */ + if (PDMCritSectIsInitialized(&pThis->lock)) + { + TMR3TimerDestroy(pThis->CTX_SUFF(pHbaCccTimer)); + pThis->CTX_SUFF(pHbaCccTimer) = NULL; + + Log(("%s: Destruct every port\n", __FUNCTION__)); + for (unsigned iActPort = 0; iActPort < pThis->cPortsImpl; iActPort++) + { + PAHCIPort pAhciPort = &pThis->ahciPort[iActPort]; + + if (pAhciPort->hEvtProcess != NIL_SUPSEMEVENT) + { + SUPSemEventClose(pThis->pSupDrvSession, pAhciPort->hEvtProcess); + pAhciPort->hEvtProcess = NIL_SUPSEMEVENT; + } + + /* Free all cached tasks. */ + for (uint32_t i = 0; i < AHCI_NR_COMMAND_SLOTS; i++) + if (pAhciPort->aCachedTasks[i]) + { + RTMemFree(pAhciPort->aCachedTasks[i]); + pAhciPort->aCachedTasks[i] = NULL; + } + } + + PDMR3CritSectDelete(&pThis->lock); + } + + return rc; +} + +/** * @interface_method_impl{PDMDEVREG,pfnConstruct} */ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg) @@ -7855,11 +8029,16 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG N_("AHCI configuration error: CmdSlotsAvail=%u should be at least 1"), pThis->cCmdSlotsAvail); + /* + * Initialize the instance data (everything touched by the destructor need + * to be initialized here!). + */ pThis->fR0Enabled = fR0Enabled; pThis->fGCEnabled = fGCEnabled; pThis->pDevInsR3 = pDevIns; pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns); pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); + pThis->pSupDrvSession = PDMDevHlpGetSupDrvSession(pDevIns); PCIDevSetVendorId (&pThis->dev, 0x8086); /* Intel */ PCIDevSetDeviceId (&pThis->dev, 0x2829); /* ICH-8M */ @@ -7894,6 +8073,39 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG PCIDevSetWord(&pThis->dev, 0xaa, 0x0010); /* Revision */ PCIDevSetDWord(&pThis->dev, 0xac, 0x00000028); /* SATA Capability Register 1 */ + pThis->cThreadsActive = 0; + + /* Initialize port members. */ + for (i = 0; i < AHCI_MAX_NR_PORTS_IMPL; i++) + { + PAHCIPort pAhciPort = &pThis->ahciPort[i]; + pAhciPort->pDevInsR3 = pDevIns; + pAhciPort->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns); + pAhciPort->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); + pAhciPort->iLUN = i; + pAhciPort->pAhciR3 = pThis; + pAhciPort->pAhciR0 = PDMINS_2_DATA_R0PTR(pDevIns); + pAhciPort->pAhciRC = PDMINS_2_DATA_RCPTR(pDevIns); + pAhciPort->Led.u32Magic = PDMLED_MAGIC; + pAhciPort->pDrvBase = NULL; + pAhciPort->pAsyncIOThread = NULL; + pAhciPort->hEvtProcess = NIL_SUPSEMEVENT; + } + + /* + * Init locks, using explicit locking where necessary. + */ + rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns)); + if (RT_FAILURE(rc)) + return rc; + + rc = PDMDevHlpCritSectInit(pDevIns, &pThis->lock, RT_SRC_POS, "AHCI#%u", iInstance); + if (RT_FAILURE(rc)) + { + Log(("%s: Failed to create critical section.\n", __FUNCTION__)); + return rc; + } + /* * Register the PCI device, it's I/O regions. */ @@ -7902,14 +8114,13 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG return rc; #ifdef VBOX_WITH_MSI_DEVICES - PDMMSIREG aMsiReg; - - RT_ZERO(aMsiReg); - aMsiReg.cMsiVectors = 1; - aMsiReg.iMsiCapOffset = 0x80; - aMsiReg.iMsiNextOffset = 0x70; - rc = PDMDevHlpPCIRegisterMsi(pDevIns, &aMsiReg); - if (RT_FAILURE (rc)) + PDMMSIREG MsiReg; + RT_ZERO(MsiReg); + MsiReg.cMsiVectors = 1; + MsiReg.iMsiCapOffset = 0x80; + MsiReg.iMsiNextOffset = 0x70; + rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg); + if (RT_FAILURE(rc)) { LogRel(("Chipset cannot do MSI: %Rrc\n", rc)); PCIDevSetCapabilityList(&pThis->dev, 0x70); @@ -7956,13 +8167,6 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG return PDMDEV_SET_ERROR(pDevIns, rc, N_("AHCI cannot register PCI memory region for registers")); - rc = PDMDevHlpCritSectInit(pDevIns, &pThis->lock, RT_SRC_POS, "AHCI#%u", iInstance); - if (RT_FAILURE(rc)) - { - Log(("%s: Failed to create critical section.\n", __FUNCTION__)); - return rc; - } - /* Create the timer for command completion coalescing feature. */ rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ahciCccTimer, pThis, TMTIMER_FLAGS_NO_CRIT_SECT, "AHCI CCC Timer", &pThis->pHbaCccTimerR3); @@ -7983,7 +8187,7 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG * * We need 2 items for every port because of SMP races. */ - rc = PDMDevHlpQueueCreate(pDevIns, sizeof(DEVPORTNOTIFIERQUEUEITEM), AHCI_MAX_NR_PORTS_IMPL*2, 0, + rc = PDMDevHlpQueueCreate(pDevIns, sizeof(DEVPORTNOTIFIERQUEUEITEM), AHCI_MAX_NR_PORTS_IMPL * 2, 0, ahciNotifyQueueConsumer, true, "AHCI-Xmit", &pThis->pNotifierQueueR3); if (RT_FAILURE(rc)) return rc; @@ -7993,21 +8197,8 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG /* Initialize static members on every port. */ for (i = 0; i < AHCI_MAX_NR_PORTS_IMPL; i++) { - /* - * Init members of the port. - */ - PAHCIPort pAhciPort = &pThis->ahciPort[i]; - pAhciPort->pDevInsR3 = pDevIns; - pAhciPort->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns); - pAhciPort->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); - pAhciPort->iLUN = i; - pAhciPort->pAhciR3 = pThis; - pAhciPort->pAhciR0 = PDMINS_2_DATA_R0PTR(pDevIns); - pAhciPort->pAhciRC = PDMINS_2_DATA_RCPTR(pDevIns); - pAhciPort->Led.u32Magic = PDMLED_MAGIC; - pAhciPort->pDrvBase = NULL; - - /* Register statistics counter. */ + PAHCIPort pAhciPort = &pThis->ahciPort[i]; + PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatDMA, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "Number of DMA transfers.", "/Devices/SATA%d/Port%d/DMA", iInstance, i); PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, @@ -8019,12 +8210,8 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG #ifdef VBOX_WITH_STATISTICS PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatProfileProcessTime, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL, "Amount of time to process one request.", "/Devices/SATA%d/Port%d/ProfileProcessTime", iInstance, i); - PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatProfileMapIntoR3, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL, - "Amount of time to map the guest buffers into R3.", "/Devices/SATA%d/Port%d/ProfileMapIntoR3", iInstance, i); PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatProfileReadWrite, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL, "Amount of time for the read/write operation to complete.", "/Devices/SATA%d/Port%d/ProfileReadWrite", iInstance, i); - PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatProfileDestroyScatterGatherList, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL, - "Amount of time to destroy the scatter gather list and free associated resources.", "/Devices/SATA%d/Port%d/ProfileDestroyScatterGatherList", iInstance, i); #endif ahciPortHwReset(pAhciPort); @@ -8040,12 +8227,11 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG /* * Init interfaces. */ - pAhciPort->IBase.pfnQueryInterface = ahciR3PortQueryInterface; - pAhciPort->IPortAsync.pfnTransferCompleteNotify = ahciTransferCompleteNotify; - pAhciPort->IPort.pfnQueryDeviceLocation = ahciR3PortQueryDeviceLocation; - pAhciPort->IMountNotify.pfnMountNotify = ahciMountNotify; - pAhciPort->IMountNotify.pfnUnmountNotify = ahciUnmountNotify; - pAhciPort->fAsyncIOThreadIdle = true; + pAhciPort->IBase.pfnQueryInterface = ahciR3PortQueryInterface; + pAhciPort->IPortAsync.pfnTransferCompleteNotify = ahciR3TransferCompleteNotify; + pAhciPort->IPort.pfnQueryDeviceLocation = ahciR3PortQueryDeviceLocation; + pAhciPort->IMountNotify.pfnMountNotify = ahciR3MountNotify; + pAhciPort->IMountNotify.pfnUnmountNotify = ahciR3UnmountNotify; /* * Attach the block driver @@ -8085,15 +8271,18 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG { LogRel(("AHCI: LUN#%d: using normal I/O\n", pAhciPort->iLUN)); pAhciPort->fAsyncInterface = false; + } - rc = RTSemEventCreate(&pAhciPort->AsyncIORequestSem); - AssertMsgRC(rc, ("Failed to create event semaphore for %s rc=%Rrc.\n", szName, rc)); - + rc = SUPSemEventCreate(pThis->pSupDrvSession, &pAhciPort->hEvtProcess); + if (RT_FAILURE(rc)) + return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, + N_("AHCI: Failed to create SUP event semaphore")); - rc = PDMDevHlpThreadCreate(pDevIns, &pAhciPort->pAsyncIOThread, pAhciPort, ahciAsyncIOLoop, ahciAsyncIOLoopWakeUp, 0, - RTTHREADTYPE_IO, szName); - AssertMsgRC(rc, ("%s: Async IO Thread creation for %s failed rc=%Rrc\n", szName, rc)); - } + rc = PDMDevHlpThreadCreate(pDevIns, &pAhciPort->pAsyncIOThread, pAhciPort, ahciAsyncIOLoop, + ahciAsyncIOLoopWakeUp, 0, RTTHREADTYPE_IO, szName); + if (RT_FAILURE(rc)) + return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, + N_("AHCI: Failed to create worker thread %s"), szName); } else if (rc == VERR_PDM_NO_ATTACHED_DRIVER) { @@ -8120,7 +8309,7 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc)); return PDMDEV_SET_ERROR(pDevIns, rc, N_("AHCI cannot attach to status driver")); } - rc = PDMDevHlpSSMRegisterEx(pDevIns, AHCI_SAVED_STATE_VERSION, sizeof(*pThis)+cbTotalBufferSize, NULL, + rc = PDMDevHlpSSMRegisterEx(pDevIns, AHCI_SAVED_STATE_VERSION, sizeof(*pThis) + cbTotalBufferSize, NULL, NULL, ahciR3LiveExec, NULL, ahciR3SavePrep, ahciR3SaveExec, NULL, ahciR3LoadPrep, ahciR3LoadExec, NULL); @@ -8168,7 +8357,7 @@ const PDMDEVREG g_DeviceAHCI = ahciR3Destruct, /* pfnRelocate */ ahciR3Relocate, - /* pfnIOCtl */ + /* pfnMemSetup */ NULL, /* pfnPowerOn */ NULL, diff --git a/src/VBox/Devices/Storage/DevATA.cpp b/src/VBox/Devices/Storage/DevATA.cpp index bf369dd6..b87bdc27 100644 --- a/src/VBox/Devices/Storage/DevATA.cpp +++ b/src/VBox/Devices/Storage/DevATA.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 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; @@ -59,6 +59,7 @@ #include "PIIX3ATABmDma.h" #include "ide.h" +#include "ATAPIPassthrough.h" #include "VBoxDD.h" /******************************************************************************* @@ -100,8 +101,6 @@ /* Media track type */ #define ATA_MEDIA_TYPE_UNKNOWN 0 /**< unknown CD type */ -#define ATA_MEDIA_TYPE_DATA 1 /**< Data CD */ -#define ATA_MEDIA_TYPE_CDDA 2 /**< CD-DA (audio) CD type */ #define ATA_MEDIA_NO_DISC 0x70 /**< Door closed, no medium */ /******************************************************************************* @@ -128,6 +127,8 @@ typedef struct ATADevState PDMMEDIAGEOMETRY PCHSGeometry; /** Total number of sectors on this disk. */ uint64_t cTotalSectors; + /** Sector size of the medium. */ + uint32_t cbSector; /** Number of sectors to transfer per IRQ. */ uint32_t cSectorsPerIRQ; @@ -179,6 +180,8 @@ typedef struct ATADevState uint32_t cbTotalTransfer; /** Elementary ATA/ATAPI transfer size, shared PIO/DMA. */ uint32_t cbElementaryTransfer; + /** Maximum ATAPI elementary transfer size, PIO only. */ + uint32_t cbPIOTransferLimit; /** Current read/write buffer position, shared PIO/DMA. */ uint32_t iIOBufferCur; /** First element beyond end of valid buffer content, shared PIO/DMA. */ @@ -304,12 +307,8 @@ typedef struct ATADevState char szInquiryProductId[ATAPI_INQUIRY_PRODUCT_ID_LENGTH+1]; /** The revision string for SCSI INQUIRY commands. */ char szInquiryRevision[ATAPI_INQUIRY_REVISION_LENGTH+1]; - /** Size of the current CUE sheet in bytes. */ - uint32_t cbCueSheet; - /** Align pbCueSheet correctly. */ - uint32_t u32Alignment3; - /** The current CUE Sheet if passthrough is used. */ - R3PTRTYPE(uint8_t *) pbCueSheet; + /** The current tracklist of the loaded medium if passthrough is used. */ + R3PTRTYPE(PTRACKLIST) pTrackList; uint8_t abAlignment4[HC_ARCH_BITS == 64 ? 7 : 3]; } ATADevState; @@ -987,6 +986,7 @@ static void ataPIOTransferStart(ATADevState *s, uint32_t start, uint32_t size) s->iIOBufferPIODataStart = start; s->iIOBufferPIODataEnd = start + size; ataSetStatus(s, ATA_STAT_DRQ | ATA_STAT_SEEK); + ataUnsetStatus(s, ATA_STAT_BUSY); } @@ -1013,7 +1013,7 @@ static void ataPIOTransferLimitATAPI(ATADevState *s) { uint32_t cbLimit, cbTransfer; - cbLimit = s->uATARegLCyl | (s->uATARegHCyl << 8); + cbLimit = s->cbPIOTransferLimit; /* Use maximum transfer size if the guest requested 0. Avoids a hang. */ if (cbLimit == 0) cbLimit = 0xfffe; @@ -1162,117 +1162,6 @@ DECLINLINE(int) atapiCmpMSF(const uint8_t *pbMSF1, const uint8_t *pbMSF2) return iRes; } -/** - * Return the correct sector size from the given LBA. - * - * @returns Sector size. - * @param s ATA Device state. - * @param iATAPILBA The LBA. - */ -static size_t atapiGetSectorSizeFromLba(ATADevState *s, uint32_t iATAPILBA) -{ - size_t cbATAPISector = 2048; - - /* - * Check if there is a valid CUE Sheet we can use, - * otherwise we just return the standard sector size. - */ - if (s->pbCueSheet) - { - uint8_t *pbCueSheetEntry = NULL; - uint8_t iMSF[3]; - - /* - * Convert the LBA to the MSF format so we can look it up in the cue sheet. - * Note that it is possible to have negative LBA values for the Lead-In area. - * See MMC6 spec chapter 6.46.3.3 for more details. - */ - LogFlowFunc(("iATAPILBA=%#x (signed: %d unsigned: %u)\n", - iATAPILBA, (int32_t)iATAPILBA, iATAPILBA)); - - if ( iATAPILBA > UINT32_C(0xffff4fa1) - && (int32_t)iATAPILBA < -150) - { - /* Lead-In area, this is always the first entry in the cue sheet. */ - pbCueSheetEntry = s->pbCueSheet; - LogFlowFunc(("Selected Lead-In area\n")); - } - else - { - iATAPILBA += 150; - iMSF[0] = (iATAPILBA / 75) / 60; - iMSF[1] = (iATAPILBA / 75) % 60; - iMSF[2] = iATAPILBA % 75; - - pbCueSheetEntry = s->pbCueSheet + 8; /* Start after the Lead-in track. */ - - /* Go through the CUE sheet and find the correct entry. */ - for (unsigned i = 0; i < (s->cbCueSheet / 8) - 2; i++) - { - if ( atapiCmpMSF(&pbCueSheetEntry[5], iMSF) != 1 - && atapiCmpMSF(&pbCueSheetEntry[8+5], iMSF) == 1) - break; - pbCueSheetEntry += 8; - } - LogFlowFunc(("Selected CUE sheet entry iMin=%u iSec=%u iFrame=%u\n", - pbCueSheetEntry[5], pbCueSheetEntry[6], pbCueSheetEntry[7])); - } - - if (pbCueSheetEntry) - { - /* Determine size of main data based on the data form field. */ - switch (pbCueSheetEntry[3] & 0x3f) - { - case 0x00: /* CD-DA with data. */ - case 0x11: /* CD-ROM mode 1 */ - case 0x13: - case 0x21: /* CD-ROM XA, CD-I */ - case 0x23: - case 0x31: /* CD-ROM Mode 2 */ - case 0x33: - cbATAPISector = 2352; - break; - case 0x01: /* CD-DA without data (used for pauses between tracks). */ - case 0x14: - case 0x24: - case 0x34: - cbATAPISector = 0; - break; - case 0x10: /* CD-ROM mode 1 */ - case 0x12: - cbATAPISector = 2048; - break; - case 0x20: /* CD-ROM XA, CD-I */ - case 0x22: - case 0x30: /* CD-ROM Mode 2 */ - case 0x32: - cbATAPISector = 2336; - break; - default: /* Reserved, invalid mode. Log and leave default sector size. */ - LogRel(("ATA: Invalid data form mode %u for current CUE sheet\n", - pbCueSheetEntry[3] & 0x3f)); - } - - /* Determine size of sub channel data based on data form field. */ - switch ((pbCueSheetEntry[3] & 0xc0) >> 6) - { - case 0x00: /* Sub channel all zeroes, autogenerated by the drive. */ - break; - case 0x01: - case 0x03: - cbATAPISector += 96; - break; - default: - LogRel(("ATA: Invalid sub-channel data form mode %u for current CUE sheet\n", - pbCueSheetEntry[3] & 0xc0)); - } - } - } - - LogFlowFunc(("cbATAPISector=%zu\n", cbATAPISector)); - return cbATAPISector; -} - static void ataCmdOK(ATADevState *s, uint8_t status) { s->uATARegError = 0; /* Not needed by ATA spec, but cannot hurt. */ @@ -1321,7 +1210,7 @@ static bool ataIdentifySS(ATADevState *s) p[1] = RT_H2LE_U16(RT_MIN(s->PCHSGeometry.cCylinders, 16383)); p[3] = RT_H2LE_U16(s->PCHSGeometry.cHeads); /* Block size; obsolete, but required for the BIOS. */ - p[5] = RT_H2LE_U16(512); + p[5] = RT_H2LE_U16(s->cbSector); p[6] = RT_H2LE_U16(s->PCHSGeometry.cSectors); ataPadString((uint8_t *)(p + 10), s->szSerialNumber, ATA_SERIAL_NUMBER_LENGTH); /* serial number */ p[20] = RT_H2LE_U16(3); /* XXX: retired, cache type */ @@ -1366,7 +1255,9 @@ static bool ataIdentifySS(ATADevState *s) p[66] = RT_H2LE_U16(120); /* recommended DMA multiword tx cycle time */ p[67] = RT_H2LE_U16(120); /* minimum PIO cycle time without flow control */ p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */ - if (s->pDrvBlock->pfnDiscard) + if ( s->pDrvBlock->pfnDiscard + || s->cbSector != 512 + || s->fNonRotational) { p[80] = RT_H2LE_U16(0x1f0); /* support everything up to ATA/ATAPI-8 ACS */ p[81] = RT_H2LE_U16(0x28); /* conforms to ATA/ATAPI-8 ACS */ @@ -1397,6 +1288,16 @@ static bool ataIdentifySS(ATADevState *s) p[102] = RT_H2LE_U16(s->cTotalSectors >> 32); p[103] = RT_H2LE_U16(s->cTotalSectors >> 48); } + + if (s->cbSector != 512) + { + uint32_t cSectorSizeInWords = s->cbSector / sizeof(uint16_t); + /* Enable reporting of logical sector size. */ + p[106] |= RT_H2LE_U16(RT_BIT(12) | RT_BIT(14)); + p[117] = RT_H2LE_U16(cSectorSizeInWords); + p[118] = RT_H2LE_U16(cSectorSizeInWords >> 16); + } + if (s->pDrvBlock->pfnDiscard) /** @todo: Set bit 14 in word 69 too? (Deterministic read after TRIM). */ p[169] = RT_H2LE_U16(1); /* DATA SET MANAGEMENT command supported. */ if (s->fNonRotational) @@ -1440,8 +1341,8 @@ static bool atapiIdentifySS(ATADevState *s) p = (uint16_t *)s->CTX_SUFF(pbIOBuffer); memset(p, 0, 512); - /* Removable CDROM, 50us response, 12 byte packets */ - p[0] = RT_H2LE_U16(2 << 14 | 5 << 8 | 1 << 7 | 2 << 5 | 0 << 0); + /* Removable CDROM, 3ms response, 12 byte packets */ + p[0] = RT_H2LE_U16(2 << 14 | 5 << 8 | 1 << 7 | 0 << 5 | 0 << 0); ataPadString((uint8_t *)(p + 10), s->szSerialNumber, ATA_SERIAL_NUMBER_LENGTH); /* serial number */ p[20] = RT_H2LE_U16(3); /* XXX: retired, cache type */ p[21] = RT_H2LE_U16(512); /* XXX: retired, cache size in sectors */ @@ -1653,11 +1554,13 @@ static int ataReadSectors(ATADevState *s, uint64_t u64Sector, void *pvBuf, STAM_PROFILE_ADV_START(&s->StatReads, r); s->Led.Asserted.s.fReading = s->Led.Actual.s.fReading = 1; - rc = s->pDrvBlock->pfnRead(s->pDrvBlock, u64Sector * 512, pvBuf, cSectors * 512); + rc = s->pDrvBlock->pfnRead(s->pDrvBlock, u64Sector * s->cbSector, pvBuf, cSectors * s->cbSector); s->Led.Actual.s.fReading = 0; STAM_PROFILE_ADV_STOP(&s->StatReads, r); + Log4(("ataReadSectors: rc=%Rrc cSectors=%#x u64Sector=%llu\n%.*Rhxd\n", + rc, cSectors, u64Sector, cSectors * s->cbSector, pvBuf)); - STAM_REL_COUNTER_ADD(&s->StatBytesRead, cSectors * 512); + STAM_REL_COUNTER_ADD(&s->StatBytesRead, cSectors * s->cbSector); if (RT_SUCCESS(rc)) *pfRedo = false; @@ -1685,15 +1588,17 @@ static int ataWriteSectors(ATADevState *s, uint64_t u64Sector, if (s->fDMA) STAM_PROFILE_ADV_START(&s->StatInstrVDWrites, vw); #endif - rc = s->pDrvBlock->pfnWrite(s->pDrvBlock, u64Sector * 512, pvBuf, cSectors * 512); + rc = s->pDrvBlock->pfnWrite(s->pDrvBlock, u64Sector * s->cbSector, pvBuf, cSectors * s->cbSector); #ifdef VBOX_INSTRUMENT_DMA_WRITES if (s->fDMA) STAM_PROFILE_ADV_STOP(&s->StatInstrVDWrites, vw); #endif s->Led.Actual.s.fWriting = 0; STAM_PROFILE_ADV_STOP(&s->StatWrites, w); + Log4(("ataWriteSectors: rc=%Rrc cSectors=%#x u64Sector=%llu\n%.*Rhxd\n", + rc, cSectors, u64Sector, cSectors * s->cbSector, pvBuf)); - STAM_REL_COUNTER_ADD(&s->StatBytesWritten, cSectors * 512); + STAM_REL_COUNTER_ADD(&s->StatBytesWritten, cSectors * s->cbSector); if (RT_SUCCESS(rc)) *pfRedo = false; @@ -1711,11 +1616,11 @@ static void ataReadWriteSectorsBT(ATADevState *s) { uint32_t cSectors; - cSectors = s->cbTotalTransfer / 512; + cSectors = s->cbTotalTransfer / s->cbSector; if (cSectors > s->cSectorsPerIRQ) - s->cbElementaryTransfer = s->cSectorsPerIRQ * 512; + s->cbElementaryTransfer = s->cSectorsPerIRQ * s->cbSector; else - s->cbElementaryTransfer = cSectors * 512; + s->cbElementaryTransfer = cSectors * s->cbSector; if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE) ataCmdOK(s, 0); } @@ -1728,7 +1633,7 @@ static bool ataReadSectorsSS(ATADevState *s) uint64_t iLBA; bool fRedo; - cSectors = s->cbElementaryTransfer / 512; + cSectors = s->cbElementaryTransfer / s->cbSector; Assert(cSectors); iLBA = ataGetSector(s); Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, iLBA)); @@ -1766,7 +1671,7 @@ static bool ataWriteSectorsSS(ATADevState *s) uint64_t iLBA; bool fRedo; - cSectors = s->cbElementaryTransfer / 512; + cSectors = s->cbElementaryTransfer / s->cbSector; Assert(cSectors); iLBA = ataGetSector(s); Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, iLBA)); @@ -1850,6 +1755,7 @@ static void atapiCmdBT(ATADevState *s) { s->fATAPITransfer = true; s->cbElementaryTransfer = s->cbTotalTransfer; + s->cbPIOTransferLimit = s->uATARegLCyl | (s->uATARegHCyl << 8); if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE) atapiCmdOK(s); } @@ -1955,36 +1861,36 @@ static bool atapiReadSS(ATADevState *s) rc = s->pDrvBlock->pfnRead(s->pDrvBlock, (uint64_t)s->iATAPILBA * s->cbATAPISector, s->CTX_SUFF(pbIOBuffer), s->cbATAPISector * cSectors); break; case 2352: - { - uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer); + { + uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer); - for (uint32_t i = s->iATAPILBA; i < s->iATAPILBA + cSectors; i++) - { - /* Sync bytes, see 4.2.3.8 CD Main Channel Block Formats */ - *pbBuf++ = 0x00; - memset(pbBuf, 0xff, 10); - pbBuf += 10; - *pbBuf++ = 0x00; - /* MSF */ - ataLBA2MSF(pbBuf, i); - pbBuf += 3; - *pbBuf++ = 0x01; /* mode 1 data */ - /* data */ - rc = s->pDrvBlock->pfnRead(s->pDrvBlock, (uint64_t)i * 2048, pbBuf, 2048); - if (RT_FAILURE(rc)) - break; - pbBuf += 2048; - /** - * @todo: maybe compute ECC and parity, layout is: - * 2072 4 EDC - * 2076 172 P parity symbols - * 2248 104 Q parity symbols - */ - memset(pbBuf, 0, 280); - pbBuf += 280; - } + for (uint32_t i = s->iATAPILBA; i < s->iATAPILBA + cSectors; i++) + { + /* Sync bytes, see 4.2.3.8 CD Main Channel Block Formats */ + *pbBuf++ = 0x00; + memset(pbBuf, 0xff, 10); + pbBuf += 10; + *pbBuf++ = 0x00; + /* MSF */ + ataLBA2MSF(pbBuf, i); + pbBuf += 3; + *pbBuf++ = 0x01; /* mode 1 data */ + /* data */ + rc = s->pDrvBlock->pfnRead(s->pDrvBlock, (uint64_t)i * 2048, pbBuf, 2048); + if (RT_FAILURE(rc)) + break; + pbBuf += 2048; + /** + * @todo: maybe compute ECC and parity, layout is: + * 2072 4 EDC + * 2076 172 P parity symbols + * 2248 104 Q parity symbols + */ + memset(pbBuf, 0, 280); + pbBuf += 280; } break; + } default: break; } @@ -2039,7 +1945,7 @@ static bool atapiPassthroughSS(ATADevState *s) uint32_t cbTransfer; PSTAMPROFILEADV pProf = NULL; - cbTransfer = RT_MIN(s->cbElementaryTransfer, s->cbIOBuffer); + cbTransfer = RT_MIN(s->cbTotalTransfer, s->cbIOBuffer); if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE) Log3(("ATAPI PT data write (%d): %.*Rhxs\n", cbTransfer, cbTransfer, s->CTX_SUFF(pbIOBuffer))); @@ -2241,33 +2147,23 @@ static bool atapiPassthroughSS(ATADevState *s) switch (s->aATAPICmd[0]) { case SCSI_SEND_CUE_SHEET: + case SCSI_READ_TOC_PMA_ATIP: { - /* Save the CUE sheet to determine sector sizes during writes */ - if (s->pbCueSheet) - { - s->cbCueSheet = 0; - RTMemFree(s->pbCueSheet); - } + if (!s->pTrackList) + rc = ATAPIPassthroughTrackListCreateEmpty(&s->pTrackList); - s->pbCueSheet = (uint8_t *)RTMemAllocZ(s->cbElementaryTransfer); - if (s->pbCueSheet) - { - s->cbCueSheet = s->cbElementaryTransfer; - memcpy(s->pbCueSheet, s->CTX_SUFF(pbIOBuffer), s->cbElementaryTransfer); - } - else if (s->cErrors++ < MAX_LOG_REL_ERRORS) - LogRel(("ATA: Out of memory while saving the CUE sheet, burning disc might fail\n")); + if (RT_SUCCESS(rc)) + rc = ATAPIPassthroughTrackListUpdate(s->pTrackList, s->aATAPICmd, s->CTX_SUFF(pbIOBuffer)); + + if ( RT_FAILURE(rc) + && s->cErrors++ < MAX_LOG_REL_ERRORS) + LogRel(("ATA: Error (%Rrc) while updating the tracklist during %s, burning the disc might fail\n", + rc, s->aATAPICmd[0] == SCSI_SEND_CUE_SHEET ? "SEND CUE SHEET" : "READ TOC/PMA/ATIP")); break; } case SCSI_SYNCHRONIZE_CACHE: { - /* Free the current CUE sheet after session at once recording. */ - if (s->pbCueSheet) - { - s->cbCueSheet = 0; - RTMemFree(s->pbCueSheet); - s->pbCueSheet = NULL; - } + ATAPIPassthroughTrackListClear(s->pTrackList); break; } } @@ -2277,10 +2173,12 @@ static bool atapiPassthroughSS(ATADevState *s) Assert(cbTransfer <= s->cbTotalTransfer); /* * Reply with the same amount of data as the real drive - * but only if the command wasn't splitted. + * but only if the command wasn't split. */ +#if 0 //@todo: This destroys commands where cbTotalTransfer > cbIOBuffer if (s->cbElementaryTransfer < s->cbIOBuffer) s->cbTotalTransfer = cbTransfer; +#endif if ( s->aATAPICmd[0] == SCSI_INQUIRY && s->fOverwriteInquiry) @@ -2293,37 +2191,7 @@ static bool atapiPassthroughSS(ATADevState *s) ataSCSIPadStr(s->CTX_SUFF(pbIOBuffer) + 16, "CD-ROM", 16); ataSCSIPadStr(s->CTX_SUFF(pbIOBuffer) + 32, "1.0", 4); } - else if ( s->aATAPICmd[0] == SCSI_READ_TOC_PMA_ATIP - && (s->aATAPICmd[2] & 0xf) != 0x05 - && s->aATAPICmd[6] != 0xaa) - { - /* Set the media type if we can detect it. */ - uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer); - - /** @todo: Implemented only for formatted TOC now. */ - if ( (s->aATAPICmd[2] & 0xf) == 0 - && cbTransfer >= 6) - { - uint32_t NewMediaType; - uint32_t OldMediaType; - if (pbBuf[5] & 0x4) - NewMediaType = ATA_MEDIA_TYPE_DATA; - else - NewMediaType = ATA_MEDIA_TYPE_CDDA; - - OldMediaType = ataMediumTypeSet(s, NewMediaType); - - if (OldMediaType != NewMediaType) - LogRel(("PIIX3 ATA: LUN#%d: CD-ROM passthrough, detected %s CD\n", - s->iLUN, - NewMediaType == ATA_MEDIA_TYPE_DATA - ? "data" - : "audio")); - } - else /* Play safe and set to unknown. */ - ataMediumTypeSet(s, ATA_MEDIA_TYPE_UNKNOWN); - } if (cbTransfer) Log3(("ATAPI PT data read (%d): %.*Rhxs\n", cbTransfer, cbTransfer, s->CTX_SUFF(pbIOBuffer))); } @@ -2402,41 +2270,41 @@ static bool atapiReadDVDStructureSS(ATADevState *s) switch (format) { case 0x0: /* Physical format information */ + { + int layer = s->aATAPICmd[6]; + uint64_t total_sectors; + + if (layer != 0) { - int layer = s->aATAPICmd[6]; - uint64_t total_sectors; - - if (layer != 0) - { - uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET; - break; - } - - total_sectors = s->cTotalSectors; - total_sectors >>= 2; - if (total_sectors == 0) - { - uASC = -SCSI_ASC_MEDIUM_NOT_PRESENT; - break; - } - - buf[4] = 1; /* DVD-ROM, part version 1 */ - buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */ - buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */ - buf[7] = 0; /* default densities */ - - /* FIXME: 0x30000 per spec? */ - ataH2BE_U32(buf + 8, 0); /* start sector */ - ataH2BE_U32(buf + 12, total_sectors - 1); /* end sector */ - ataH2BE_U32(buf + 16, total_sectors - 1); /* l0 end sector */ - - /* Size of buffer, not including 2 byte size field */ - ataH2BE_U32(&buf[0], 2048 + 2); - - /* 2k data + 4 byte header */ - uASC = (2048 + 4); + uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET; + break; } + + total_sectors = s->cTotalSectors; + total_sectors >>= 2; + if (total_sectors == 0) + { + uASC = -SCSI_ASC_MEDIUM_NOT_PRESENT; + break; + } + + buf[4] = 1; /* DVD-ROM, part version 1 */ + buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */ + buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */ + buf[7] = 0; /* default densities */ + + /* FIXME: 0x30000 per spec? */ + ataH2BE_U32(buf + 8, 0); /* start sector */ + ataH2BE_U32(buf + 12, total_sectors - 1); /* end sector */ + ataH2BE_U32(buf + 16, total_sectors - 1); /* l0 end sector */ + + /* Size of buffer, not including 2 byte size field */ + ataH2BE_U32(&buf[0], 2048 + 2); + + /* 2k data + 4 byte header */ + uASC = (2048 + 4); break; + } case 0x01: /* DVD copyright information */ buf[4] = 0; /* no copyright data */ buf[5] = 0; /* no region restrictions */ @@ -2597,7 +2465,7 @@ static bool atapiReadTrackInformationSS(ATADevState *s) return false; } -static size_t atapiGetConfigurationFillFeatureListProfiles(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) +static uint32_t atapiGetConfigurationFillFeatureListProfiles(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) { if (cbBuf < 3*4) return 0; @@ -2615,7 +2483,7 @@ static size_t atapiGetConfigurationFillFeatureListProfiles(ATADevState *s, uint8 return 3*4; /* Header + 2 profiles entries */ } -static size_t atapiGetConfigurationFillFeatureCore(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) +static uint32_t atapiGetConfigurationFillFeatureCore(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) { if (cbBuf < 12) return 0; @@ -2630,7 +2498,7 @@ static size_t atapiGetConfigurationFillFeatureCore(ATADevState *s, uint8_t *pbBu return 12; } -static size_t atapiGetConfigurationFillFeatureMorphing(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) +static uint32_t atapiGetConfigurationFillFeatureMorphing(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) { if (cbBuf < 8) return 0; @@ -2644,7 +2512,7 @@ static size_t atapiGetConfigurationFillFeatureMorphing(ATADevState *s, uint8_t * return 8; } -static size_t atapiGetConfigurationFillFeatureRemovableMedium(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) +static uint32_t atapiGetConfigurationFillFeatureRemovableMedium(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) { if (cbBuf < 8) return 0; @@ -2659,7 +2527,7 @@ static size_t atapiGetConfigurationFillFeatureRemovableMedium(ATADevState *s, ui return 8; } -static size_t atapiGetConfigurationFillFeatureRandomReadable(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) +static uint32_t atapiGetConfigurationFillFeatureRandomReadable(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) { if (cbBuf < 12) return 0; @@ -2675,7 +2543,7 @@ static size_t atapiGetConfigurationFillFeatureRandomReadable(ATADevState *s, uin return 12; } -static size_t atapiGetConfigurationFillFeatureCDRead(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) +static uint32_t atapiGetConfigurationFillFeatureCDRead(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) { if (cbBuf < 8) return 0; @@ -2689,7 +2557,7 @@ static size_t atapiGetConfigurationFillFeatureCDRead(ATADevState *s, uint8_t *pb return 8; } -static size_t atapiGetConfigurationFillFeaturePowerManagement(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) +static uint32_t atapiGetConfigurationFillFeaturePowerManagement(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) { if (cbBuf < 4) return 0; @@ -2701,7 +2569,7 @@ static size_t atapiGetConfigurationFillFeaturePowerManagement(ATADevState *s, ui return 4; } -static size_t atapiGetConfigurationFillFeatureTimeout(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) +static uint32_t atapiGetConfigurationFillFeatureTimeout(ATADevState *s, uint8_t *pbBuf, size_t cbBuf) { if (cbBuf < 8) return 0; @@ -2717,8 +2585,8 @@ static size_t atapiGetConfigurationFillFeatureTimeout(ATADevState *s, uint8_t *p static bool atapiGetConfigurationSS(ATADevState *s) { uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer); - size_t cbBuf = s->cbIOBuffer; - size_t cbCopied = 0; + uint32_t cbBuf = s->cbIOBuffer; + uint32_t cbCopied = 0; uint16_t u16Sfn = ataBE2H_U16(&s->aATAPICmd[2]); Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE); @@ -3201,37 +3069,37 @@ static void atapiParseCmdVirtualATAPI(ATADevState *s) ataStartTransfer(s, RT_MIN(cbMax, 8), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_GET_EVENT_STATUS_NOTIFICATION, true); break; case SCSI_MODE_SENSE_10: + { + uint8_t uPageControl, uPageCode; + cbMax = ataBE2H_U16(pbPacket + 7); + uPageControl = pbPacket[2] >> 6; + uPageCode = pbPacket[2] & 0x3f; + switch (uPageControl) { - uint8_t uPageControl, uPageCode; - cbMax = ataBE2H_U16(pbPacket + 7); - uPageControl = pbPacket[2] >> 6; - uPageCode = pbPacket[2] & 0x3f; - switch (uPageControl) - { - case SCSI_PAGECONTROL_CURRENT: - switch (uPageCode) - { - case SCSI_MODEPAGE_ERROR_RECOVERY: - ataStartTransfer(s, RT_MIN(cbMax, 16), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY, true); - break; - case SCSI_MODEPAGE_CD_STATUS: - ataStartTransfer(s, RT_MIN(cbMax, 40), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS, true); - break; - default: - goto error_cmd; - } - break; - case SCSI_PAGECONTROL_CHANGEABLE: - goto error_cmd; - case SCSI_PAGECONTROL_DEFAULT: - goto error_cmd; - default: - case SCSI_PAGECONTROL_SAVED: - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED); - break; - } + case SCSI_PAGECONTROL_CURRENT: + switch (uPageCode) + { + case SCSI_MODEPAGE_ERROR_RECOVERY: + ataStartTransfer(s, RT_MIN(cbMax, 16), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY, true); + break; + case SCSI_MODEPAGE_CD_STATUS: + ataStartTransfer(s, RT_MIN(cbMax, 40), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS, true); + break; + default: + goto error_cmd; + } + break; + case SCSI_PAGECONTROL_CHANGEABLE: + goto error_cmd; + case SCSI_PAGECONTROL_DEFAULT: + goto error_cmd; + default: + case SCSI_PAGECONTROL_SAVED: + atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED); + break; } break; + } case SCSI_REQUEST_SENSE: cbMax = pbPacket[4]; ataStartTransfer(s, RT_MIN(cbMax, 18), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_REQUEST_SENSE, true); @@ -3250,228 +3118,228 @@ static void atapiParseCmdVirtualATAPI(ATADevState *s) break; case SCSI_READ_10: case SCSI_READ_12: - { - uint32_t cSectors, iATAPILBA; + { + uint32_t cSectors, iATAPILBA; - if (s->cNotifiedMediaChange > 0) - { - s->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - break; - } - else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) - { - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; - } - if (pbPacket[0] == SCSI_READ_10) - cSectors = ataBE2H_U16(pbPacket + 7); - else - cSectors = ataBE2H_U32(pbPacket + 6); - iATAPILBA = ataBE2H_U32(pbPacket + 2); - if (cSectors == 0) - { - atapiCmdOK(s); - break; - } - if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors) + if (s->cNotifiedMediaChange > 0) + { + s->cNotifiedMediaChange-- ; + atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ + break; + } + else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) + { + atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); + break; + } + if (pbPacket[0] == SCSI_READ_10) + cSectors = ataBE2H_U16(pbPacket + 7); + else + cSectors = ataBE2H_U32(pbPacket + 6); + iATAPILBA = ataBE2H_U32(pbPacket + 2); + if (cSectors == 0) + { + atapiCmdOK(s); + break; + } + if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors) + { + /* Rate limited logging, one log line per second. For + * guests that insist on reading from places outside the + * valid area this often generates too many release log + * entries otherwise. */ + static uint64_t uLastLogTS = 0; + if (RTTimeMilliTS() >= uLastLogTS + 1000) { - /* Rate limited logging, one log line per second. For - * guests that insist on reading from places outside the - * valid area this often generates too many release log - * entries otherwise. */ - static uint64_t uLastLogTS = 0; - if (RTTimeMilliTS() >= uLastLogTS + 1000) - { - LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (READ)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors)); - uLastLogTS = RTTimeMilliTS(); - } - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); - break; + LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (READ)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors)); + uLastLogTS = RTTimeMilliTS(); } - atapiReadSectors(s, iATAPILBA, cSectors, 2048); + atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); + break; } + atapiReadSectors(s, iATAPILBA, cSectors, 2048); break; + } case SCSI_READ_CD: - { - uint32_t cSectors, iATAPILBA; + { + uint32_t cSectors, iATAPILBA; - if (s->cNotifiedMediaChange > 0) - { - s->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - break; - } - else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) + if (s->cNotifiedMediaChange > 0) + { + s->cNotifiedMediaChange-- ; + atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ + break; + } + else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) + { + atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); + break; + } + cSectors = (pbPacket[6] << 16) | (pbPacket[7] << 8) | pbPacket[8]; + iATAPILBA = ataBE2H_U32(pbPacket + 2); + if (cSectors == 0) + { + atapiCmdOK(s); + break; + } + if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors) + { + /* Rate limited logging, one log line per second. For + * guests that insist on reading from places outside the + * valid area this often generates too many release log + * entries otherwise. */ + static uint64_t uLastLogTS = 0; + if (RTTimeMilliTS() >= uLastLogTS + 1000) { - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; + LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (READ CD)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors)); + uLastLogTS = RTTimeMilliTS(); } - cSectors = (pbPacket[6] << 16) | (pbPacket[7] << 8) | pbPacket[8]; - iATAPILBA = ataBE2H_U32(pbPacket + 2); - if (cSectors == 0) - { + atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); + break; + } + switch (pbPacket[9] & 0xf8) + { + case 0x00: + /* nothing */ atapiCmdOK(s); break; - } - if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors) - { - /* Rate limited logging, one log line per second. For - * guests that insist on reading from places outside the - * valid area this often generates too many release log - * entries otherwise. */ - static uint64_t uLastLogTS = 0; - if (RTTimeMilliTS() >= uLastLogTS + 1000) - { - LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (READ CD)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors)); - uLastLogTS = RTTimeMilliTS(); - } - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); + case 0x10: + /* normal read */ + atapiReadSectors(s, iATAPILBA, cSectors, 2048); + break; + case 0xf8: + /* read all data */ + atapiReadSectors(s, iATAPILBA, cSectors, 2352); + break; + default: + LogRel(("PIIX3 ATA: LUN#%d: CD-ROM sector format not supported (%#x)\n", s->iLUN, pbPacket[9] & 0xf8)); + atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); break; - } - switch (pbPacket[9] & 0xf8) - { - case 0x00: - /* nothing */ - atapiCmdOK(s); - break; - case 0x10: - /* normal read */ - atapiReadSectors(s, iATAPILBA, cSectors, 2048); - break; - case 0xf8: - /* read all data */ - atapiReadSectors(s, iATAPILBA, cSectors, 2352); - break; - default: - LogRel(("PIIX3 ATA: LUN#%d: CD-ROM sector format not supported (%#x)\n", s->iLUN, pbPacket[9] & 0xf8)); - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); - break; - } } break; + } case SCSI_SEEK_10: + { + uint32_t iATAPILBA; + if (s->cNotifiedMediaChange > 0) { - uint32_t iATAPILBA; - if (s->cNotifiedMediaChange > 0) - { - s->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ - break; - } - else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) - { - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); - break; - } - iATAPILBA = ataBE2H_U32(pbPacket + 2); - if (iATAPILBA > s->cTotalSectors) + s->cNotifiedMediaChange-- ; + atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ + break; + } + else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) + { + atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); + break; + } + iATAPILBA = ataBE2H_U32(pbPacket + 2); + if (iATAPILBA > s->cTotalSectors) + { + /* Rate limited logging, one log line per second. For + * guests that insist on seeking to places outside the + * valid area this often generates too many release log + * entries otherwise. */ + static uint64_t uLastLogTS = 0; + if (RTTimeMilliTS() >= uLastLogTS + 1000) { - /* Rate limited logging, one log line per second. For - * guests that insist on seeking to places outside the - * valid area this often generates too many release log - * entries otherwise. */ - static uint64_t uLastLogTS = 0; - if (RTTimeMilliTS() >= uLastLogTS + 1000) - { - LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (SEEK)\n", s->iLUN, (uint64_t)iATAPILBA)); - uLastLogTS = RTTimeMilliTS(); - } - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); - break; + LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (SEEK)\n", s->iLUN, (uint64_t)iATAPILBA)); + uLastLogTS = RTTimeMilliTS(); } - atapiCmdOK(s); - ataSetStatus(s, ATA_STAT_SEEK); /* Linux expects this. */ + atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR); + break; } + atapiCmdOK(s); + ataSetStatus(s, ATA_STAT_SEEK); /* Linux expects this. */ break; + } case SCSI_START_STOP_UNIT: + { + int rc = VINF_SUCCESS; + switch (pbPacket[4] & 3) { - int rc = VINF_SUCCESS; - switch (pbPacket[4] & 3) + case 0: /* 00 - Stop motor */ + case 1: /* 01 - Start motor */ + break; + case 2: /* 10 - Eject media */ { - case 0: /* 00 - Stop motor */ - case 1: /* 01 - Start motor */ - break; - case 2: /* 10 - Eject media */ + /* This must be done from EMT. */ + PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); + PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s); + PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *); + + PDMCritSectLeave(&pCtl->lock); + rc = VMR3ReqPriorityCallWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY, + (PFNRT)s->pDrvMount->pfnUnmount, 3, + s->pDrvMount, false /*=fForce*/, true /*=fEject*/); + Assert(RT_SUCCESS(rc) || rc == VERR_PDM_MEDIA_LOCKED || rc == VERR_PDM_MEDIA_NOT_MOUNTED); + if (RT_SUCCESS(rc) && pThis->pMediaNotify) { - /* This must be done from EMT. */ - PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s); - PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s); - PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *); - - PDMCritSectLeave(&pCtl->lock); - rc = VMR3ReqPriorityCallWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY, - (PFNRT)s->pDrvMount->pfnUnmount, 3, - s->pDrvMount, false /*=fForce*/, true /*=fEject*/); - Assert(RT_SUCCESS(rc) || (rc == VERR_PDM_MEDIA_LOCKED) || (rc = VERR_PDM_MEDIA_NOT_MOUNTED)); - if (RT_SUCCESS(rc) && pThis->pMediaNotify) - { - rc = VMR3ReqCallNoWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY, - (PFNRT)pThis->pMediaNotify->pfnEjected, 2, - pThis->pMediaNotify, s->iLUN); - AssertRC(rc); - } - { - STAM_PROFILE_START(&pCtl->StatLockWait, a); - PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); - STAM_PROFILE_STOP(&pCtl->StatLockWait, a); - } - break; + rc = VMR3ReqCallNoWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY, + (PFNRT)pThis->pMediaNotify->pfnEjected, 2, + pThis->pMediaNotify, s->iLUN); + AssertRC(rc); } - case 3: /* 11 - Load media */ - /** @todo rc = s->pDrvMount->pfnLoadMedia(s->pDrvMount) */ - break; + { + STAM_PROFILE_START(&pCtl->StatLockWait, a); + PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS); + STAM_PROFILE_STOP(&pCtl->StatLockWait, a); + } + break; } - if (RT_SUCCESS(rc)) - atapiCmdOK(s); - else - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED); + case 3: /* 11 - Load media */ + /** @todo rc = s->pDrvMount->pfnLoadMedia(s->pDrvMount) */ + break; } + if (RT_SUCCESS(rc)) + atapiCmdOK(s); + else + atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED); break; + } case SCSI_MECHANISM_STATUS: - { - cbMax = ataBE2H_U16(pbPacket + 8); - ataStartTransfer(s, RT_MIN(cbMax, 8), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MECHANISM_STATUS, true); - } + { + cbMax = ataBE2H_U16(pbPacket + 8); + ataStartTransfer(s, RT_MIN(cbMax, 8), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MECHANISM_STATUS, true); break; + } case SCSI_READ_TOC_PMA_ATIP: - { - uint8_t format; + { + uint8_t format; - if (s->cNotifiedMediaChange > 0) - { - s->cNotifiedMediaChange-- ; - atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ + if (s->cNotifiedMediaChange > 0) + { + s->cNotifiedMediaChange-- ; + atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */ + break; + } + else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) + { + atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); + break; + } + cbMax = ataBE2H_U16(pbPacket + 7); + /* SCSI MMC-3 spec says format is at offset 2 (lower 4 bits), + * but Linux kernel uses offset 9 (topmost 2 bits). Hope that + * the other field is clear... */ + format = (pbPacket[2] & 0xf) | (pbPacket[9] >> 6); + switch (format) + { + case 0: + ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_NORMAL, true); break; - } - else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount)) - { - atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); + case 1: + ataStartTransfer(s, RT_MIN(cbMax, 12), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_MULTI, true); + break; + case 2: + ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_RAW, true); + break; + default: + error_cmd: + atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); break; - } - cbMax = ataBE2H_U16(pbPacket + 7); - /* SCSI MMC-3 spec says format is at offset 2 (lower 4 bits), - * but Linux kernel uses offset 9 (topmost 2 bits). Hope that - * the other field is clear... */ - format = (pbPacket[2] & 0xf) | (pbPacket[9] >> 6); - switch (format) - { - case 0: - ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_NORMAL, true); - break; - case 1: - ataStartTransfer(s, RT_MIN(cbMax, 12), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_MULTI, true); - break; - case 2: - ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_RAW, true); - break; - default: - error_cmd: - atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); - break; - } } break; + } case SCSI_READ_CAPACITY: if (s->cNotifiedMediaChange > 0) { @@ -3648,11 +3516,20 @@ static void atapiParseCmdPassthrough(ATADevState *s) switch ((pbPacket[1] >> 2) & 0x7) { case 0x0: /* All types. */ - if (ASMAtomicReadU32(&s->MediaTrackType) == ATA_MEDIA_TYPE_CDDA) - s->cbATAPISector = 2352; + { + uint32_t iLbaStart; + + if (pbPacket[0] == SCSI_READ_CD) + iLbaStart = ataBE2H_U32(&pbPacket[2]); + else + iLbaStart = ataMSF2LBA(&pbPacket[3]); + + if (s->pTrackList) + s->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(s->pTrackList, iLbaStart); else s->cbATAPISector = 2048; /* Might be incorrect if we couldn't determine the type. */ break; + } case 0x1: /* CD-DA */ s->cbATAPISector = 2352; break; @@ -3772,7 +3649,10 @@ static void atapiParseCmdPassthrough(ATADevState *s) case SCSI_WRITE_AND_VERIFY_10: iATAPILBA = ataBE2H_U32(pbPacket + 2); cSectors = ataBE2H_U16(pbPacket + 7); - s->cbATAPISector = atapiGetSectorSizeFromLba(s, iATAPILBA); + if (s->pTrackList) + s->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(s->pTrackList, iATAPILBA); + else + s->cbATAPISector = 2048; Log2(("ATAPI PT: lba %d sectors %d sector size %d\n", iATAPILBA, cSectors, s->cbATAPISector)); cbTransfer = cSectors * s->cbATAPISector; uTxDir = PDMBLOCKTXDIR_TO_DEVICE; @@ -3780,7 +3660,10 @@ static void atapiParseCmdPassthrough(ATADevState *s) case SCSI_WRITE_12: iATAPILBA = ataBE2H_U32(pbPacket + 2); cSectors = ataBE2H_U32(pbPacket + 6); - s->cbATAPISector = atapiGetSectorSizeFromLba(s, iATAPILBA); + if (s->pTrackList) + s->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(s->pTrackList, iATAPILBA); + else + s->cbATAPISector = 2048; Log2(("ATAPI PT: lba %d sectors %d sector size %d\n", iATAPILBA, cSectors, s->cbATAPISector)); cbTransfer = cSectors * s->cbATAPISector; uTxDir = PDMBLOCKTXDIR_TO_DEVICE; @@ -3822,7 +3705,7 @@ static void atapiParseCmdPassthrough(ATADevState *s) sendcmd: /* * Send a command to the drive, passing data in/out as required. - * Commands which exceed the I/O buffer size are splitted below + * Commands which exceed the I/O buffer size are split below * or aborted if splitting is not implemented. */ Log2(("ATAPI PT: max size %d\n", cbTransfer)); @@ -3916,7 +3799,7 @@ static DECLCALLBACK(void) ataMountNotify(PPDMIMOUNTNOTIFY pInterface) if (pIf->fATAPI) pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 2048; else - pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 512; + pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / pIf->cbSector; LogRel(("PIIX3 ATA: LUN#%d: CD/DVD, total number of sectors %Ld, passthrough unchanged\n", pIf->iLUN, pIf->cTotalSectors)); @@ -4003,8 +3886,8 @@ static int ataTrimSectors(ATADevState *s, uint64_t u64Sector, uint32_t cSectors, PDMCritSectLeave(&pCtl->lock); - TrimRange.offStart = u64Sector * 512; - TrimRange.cbRange = cSectors * 512; + TrimRange.offStart = u64Sector * s->cbSector; + TrimRange.cbRange = cSectors * s->cbSector; s->Led.Asserted.s.fWriting = s->Led.Actual.s.fWriting = 1; rc = s->pDrvBlock->pfnDiscard(s->pDrvBlock, &TrimRange, 1); @@ -4143,7 +4026,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd) if (!s->pDrvBlock || s->fATAPI) goto abort_cmd; s->cSectorsPerIRQ = 1; - ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false); + ataStartTransfer(s, ataGetNSectors(s) * s->cbSector, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false); break; case ATA_WRITE_SECTORS_EXT: s->fLBA48 = true; @@ -4152,7 +4035,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd) if (!s->pDrvBlock || s->fATAPI) goto abort_cmd; s->cSectorsPerIRQ = 1; - ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false); + ataStartTransfer(s, ataGetNSectors(s) * s->cbSector, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false); break; case ATA_READ_MULTIPLE_EXT: s->fLBA48 = true; @@ -4160,7 +4043,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd) if (!s->pDrvBlock || !s->cMultSectors || s->fATAPI) goto abort_cmd; s->cSectorsPerIRQ = s->cMultSectors; - ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false); + ataStartTransfer(s, ataGetNSectors(s) * s->cbSector, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false); break; case ATA_WRITE_MULTIPLE_EXT: s->fLBA48 = true; @@ -4168,7 +4051,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd) if (!s->pDrvBlock || !s->cMultSectors || s->fATAPI) goto abort_cmd; s->cSectorsPerIRQ = s->cMultSectors; - ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false); + ataStartTransfer(s, ataGetNSectors(s) * s->cbSector, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false); break; case ATA_READ_DMA_EXT: s->fLBA48 = true; @@ -4178,7 +4061,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd) goto abort_cmd; s->cSectorsPerIRQ = ATA_MAX_MULT_SECTORS; s->fDMA = true; - ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false); + ataStartTransfer(s, ataGetNSectors(s) * s->cbSector, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false); break; case ATA_WRITE_DMA_EXT: s->fLBA48 = true; @@ -4188,7 +4071,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd) goto abort_cmd; s->cSectorsPerIRQ = ATA_MAX_MULT_SECTORS; s->fDMA = true; - ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false); + ataStartTransfer(s, ataGetNSectors(s) * s->cbSector, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false); break; case ATA_READ_NATIVE_MAX_ADDRESS_EXT: s->fLBA48 = true; @@ -4330,7 +4213,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd) || (s->uATARegFeature & ~UINT8_C(0x01))) goto abort_cmd; s->fDMA = true; - ataStartTransfer(s, (s->uATARegNSectorHOB << 8 | s->uATARegNSector) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_NULL, ATAFN_SS_TRIM, false); + ataStartTransfer(s, (s->uATARegNSectorHOB << 8 | s->uATARegNSector) * s->cbSector, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_NULL, ATAFN_SS_TRIM, false); break; default: abort_cmd: @@ -4641,9 +4524,12 @@ static uint32_t ataStatusRead(PATACONTROLLER pCtl, uint32_t addr) ATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf]; uint32_t val; - if ((!pCtl->aIfs[0].pDrvBlock && !pCtl->aIfs[1].pDrvBlock) || - (pCtl->iSelectedIf == 1 && !s->pDrvBlock)) - val = 0; + //@todo: The handler should not be even registered if there + // is no device on an IDE channel. + if (!pCtl->aIfs[0].pDrvBlock && !pCtl->aIfs[1].pDrvBlock) + val = 0xff; + else if (pCtl->iSelectedIf == 1 && !s->pDrvBlock) + val = 0; /* Device 1 selected, Device 0 responding for it. */ else val = s->uATARegStatus; Log2(("%s: addr=%#x val=%#04x\n", __FUNCTION__, addr, val)); @@ -5018,9 +4904,9 @@ static void ataDMATransfer(PATACONTROLLER pCtl) PCIATAState *pATAState = PDMINS_2_DATA(pDevIns, PCIATAState *); AssertPtr(pATAState); if (uTxDir == PDMBLOCKTXDIR_FROM_DEVICE) - PDMDevHlpPCIDevPhysWrite(&pATAState->dev, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen); + PDMDevHlpPCIPhysWrite(pDevIns, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen); else - PDMDevHlpPCIDevPhysRead(&pATAState->dev, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen); + PDMDevHlpPCIPhysRead(pDevIns, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen); iIOBufferCur += dmalen; cbTotalTransfer -= dmalen; @@ -6221,120 +6107,6 @@ DECLINLINE(void) ataRelocBuffer(PPDMDEVINS pDevIns, ATADevState *s) /** - * @copydoc FNPDMDEVRELOCATE - */ -static DECLCALLBACK(void) ataR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta) -{ - PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *); - - for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++) - { - pThis->aCts[i].pDevInsRC += offDelta; - pThis->aCts[i].aIfs[0].pDevInsRC += offDelta; - pThis->aCts[i].aIfs[0].pControllerRC += offDelta; - ataRelocBuffer(pDevIns, &pThis->aCts[i].aIfs[0]); - pThis->aCts[i].aIfs[1].pDevInsRC += offDelta; - pThis->aCts[i].aIfs[1].pControllerRC += offDelta; - ataRelocBuffer(pDevIns, &pThis->aCts[i].aIfs[1]); - } -} - - -/** - * Destroy a driver instance. - * - * Most VM resources are freed by the VM. This callback is provided so that any non-VM - * resources can be freed correctly. - * - * @param pDevIns The device instance data. - */ -static DECLCALLBACK(int) ataR3Destruct(PPDMDEVINS pDevIns) -{ - PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *); - int rc; - - Log(("ataR3Destruct\n")); - PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); - - /* - * Tell the async I/O threads to terminate. - */ - for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++) - { - if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD) - { - ASMAtomicWriteU32(&pThis->aCts[i].fShutdown, true); - rc = RTSemEventSignal(pThis->aCts[i].AsyncIOSem); - AssertRC(rc); - rc = RTSemEventSignal(pThis->aCts[i].SuspendIOSem); - AssertRC(rc); - } - } - - /* - * Wait for the threads to terminate before destroying their resources. - */ - for (unsigned i = 0; i < RT_ELEMENTS(pThis->aCts); i++) - { - if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD) - { - rc = RTThreadWait(pThis->aCts[i].AsyncIOThread, 30000 /* 30 s*/, NULL); - if (RT_SUCCESS(rc)) - pThis->aCts[i].AsyncIOThread = NIL_RTTHREAD; - else - LogRel(("PIIX3 ATA Dtor: Ctl#%u is still executing, DevSel=%d AIOIf=%d CmdIf0=%#04x CmdIf1=%#04x rc=%Rrc\n", - i, pThis->aCts[i].iSelectedIf, pThis->aCts[i].iAIOIf, - pThis->aCts[i].aIfs[0].uATARegCommand, pThis->aCts[i].aIfs[1].uATARegCommand, rc)); - } - } - - /* - * Free resources. - */ - for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++) - { - if (pThis->aCts[i].AsyncIORequestMutex != NIL_RTSEMMUTEX) - { - RTSemMutexDestroy(pThis->aCts[i].AsyncIORequestMutex); - pThis->aCts[i].AsyncIORequestMutex = NIL_RTSEMMUTEX; - } - if (pThis->aCts[i].AsyncIOSem != NIL_RTSEMEVENT) - { - RTSemEventDestroy(pThis->aCts[i].AsyncIOSem); - pThis->aCts[i].AsyncIOSem = NIL_RTSEMEVENT; - } - if (pThis->aCts[i].SuspendIOSem != NIL_RTSEMEVENT) - { - RTSemEventDestroy(pThis->aCts[i].SuspendIOSem); - pThis->aCts[i].SuspendIOSem = NIL_RTSEMEVENT; - } - - /* try one final time */ - if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD) - { - rc = RTThreadWait(pThis->aCts[i].AsyncIOThread, 1 /*ms*/, NULL); - if (RT_SUCCESS(rc)) - { - pThis->aCts[i].AsyncIOThread = NIL_RTTHREAD; - LogRel(("PIIX3 ATA Dtor: Ctl#%u actually completed.\n", i)); - } - } - - for (uint32_t iIf = 0; iIf < RT_ELEMENTS(pThis->aCts[i].aIfs); iIf++) - { - if (pThis->aCts[i].aIfs[iIf].pbCueSheet) - { - RTMemFree(pThis->aCts[i].aIfs[iIf].pbCueSheet); - pThis->aCts[i].aIfs[iIf].pbCueSheet = NULL; - } - } - } - - return VINF_SUCCESS; -} - - -/** * Detach notification. * * The DVD drive has been unplugged. @@ -6434,6 +6206,11 @@ static int ataConfigLun(PPDMDEVINS pDevIns, ATADevState *pIf) /* * Allocate I/O buffer. */ + if (pIf->fATAPI) + pIf->cbSector = 2048; + else + pIf->cbSector = pIf->pDrvBlock->pfnGetSectorSize(pIf->pDrvBlock); + PVM pVM = PDMDevHlpGetVM(pDevIns); if (pIf->cbIOBuffer) { @@ -6442,7 +6219,7 @@ static int ataConfigLun(PPDMDEVINS pDevIns, ATADevState *pIf) if (pIf->fATAPI) AssertRelease(pIf->cbIOBuffer == _128K); else - AssertRelease(pIf->cbIOBuffer == ATA_MAX_MULT_SECTORS * 512); + AssertRelease(pIf->cbIOBuffer == ATA_MAX_MULT_SECTORS * pIf->cbSector); Assert(pIf->pbIOBufferR3); Assert(pIf->pbIOBufferR0 == MMHyperR3ToR0(pVM, pIf->pbIOBufferR3)); Assert(pIf->pbIOBufferRC == MMHyperR3ToRC(pVM, pIf->pbIOBufferR3)); @@ -6452,7 +6229,7 @@ static int ataConfigLun(PPDMDEVINS pDevIns, ATADevState *pIf) if (pIf->fATAPI) pIf->cbIOBuffer = _128K; else - pIf->cbIOBuffer = ATA_MAX_MULT_SECTORS * 512; + pIf->cbIOBuffer = ATA_MAX_MULT_SECTORS * pIf->cbSector; Assert(!pIf->pbIOBufferR3); rc = MMR3HyperAllocOnceNoRel(pVM, pIf->cbIOBuffer, 0, MM_TAG_PDM_DEVICE_USER, (void **)&pIf->pbIOBufferR3); if (RT_FAILURE(rc)) @@ -6466,7 +6243,7 @@ static int ataConfigLun(PPDMDEVINS pDevIns, ATADevState *pIf) */ if (pIf->fATAPI) { - pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 2048; + pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / pIf->cbSector; pIf->PCHSGeometry.cCylinders = 0; /* dummy */ pIf->PCHSGeometry.cHeads = 0; /* dummy */ pIf->PCHSGeometry.cSectors = 0; /* dummy */ @@ -6474,7 +6251,7 @@ static int ataConfigLun(PPDMDEVINS pDevIns, ATADevState *pIf) } else { - pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 512; + pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / pIf->cbSector; rc = pIf->pDrvBlockBios->pfnGetPCHSGeometry(pIf->pDrvBlockBios, &pIf->PCHSGeometry); if (rc == VERR_PDM_MEDIA_NOT_MOUNTED) @@ -6639,183 +6416,6 @@ static bool ataR3AllAsyncIOIsIdle(PPDMDEVINS pDevIns) return true; } - -/** - * Callback employed by ataSuspend and ataR3PowerOff. - * - * @returns true if we've quiesced, false if we're still working. - * @param pDevIns The device instance. - */ -static DECLCALLBACK(bool) ataR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns) -{ - return ataR3AllAsyncIOIsIdle(pDevIns); -} - - -/** - * Common worker for ataSuspend and ataR3PowerOff. - */ -static void ataR3SuspendOrPowerOff(PPDMDEVINS pDevIns) -{ - if (!ataR3AllAsyncIOIsIdle(pDevIns)) - PDMDevHlpSetAsyncNotification(pDevIns, ataR3IsAsyncSuspendOrPowerOffDone); -} - - -/** - * Power Off notification. - * - * @returns VBox status. - * @param pDevIns The device instance data. - */ -static DECLCALLBACK(void) ataR3PowerOff(PPDMDEVINS pDevIns) -{ - Log(("%s:\n", __FUNCTION__)); - ataR3SuspendOrPowerOff(pDevIns); -} - - -/** - * Suspend notification. - * - * @returns VBox status. - * @param pDevIns The device instance data. - */ -static DECLCALLBACK(void) ataR3Suspend(PPDMDEVINS pDevIns) -{ - Log(("%s:\n", __FUNCTION__)); - ataR3SuspendOrPowerOff(pDevIns); -} - - -/** - * Callback employed by ataR3Reset. - * - * @returns true if we've quiesced, false if we're still working. - * @param pDevIns The device instance. - */ -static DECLCALLBACK(bool) ataR3IsAsyncResetDone(PPDMDEVINS pDevIns) -{ - PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *); - - if (!ataR3AllAsyncIOIsIdle(pDevIns)) - return false; - - for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++) - { - PDMCritSectEnter(&pThis->aCts[i].lock, VERR_INTERNAL_ERROR); - for (uint32_t j = 0; j < RT_ELEMENTS(pThis->aCts[i].aIfs); j++) - ataResetDevice(&pThis->aCts[i].aIfs[j]); - PDMCritSectLeave(&pThis->aCts[i].lock); - } - return true; -} - - -/** - * Common reset worker for ataR3Reset and ataR3Construct. - * - * @returns VBox status. - * @param pDevIns The device instance data. - * @param fConstruct Indicates who is calling. - */ -static int ataR3ResetCommon(PPDMDEVINS pDevIns, bool fConstruct) -{ - PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *); - - for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++) - { - PDMCritSectEnter(&pThis->aCts[i].lock, VERR_INTERNAL_ERROR); - - pThis->aCts[i].iSelectedIf = 0; - pThis->aCts[i].iAIOIf = 0; - pThis->aCts[i].BmDma.u8Cmd = 0; - /* Report that both drives present on the bus are in DMA mode. This - * pretends that there is a BIOS that has set it up. Normal reset - * default is 0x00. */ - pThis->aCts[i].BmDma.u8Status = (pThis->aCts[i].aIfs[0].pDrvBase != NULL ? BM_STATUS_D0DMA : 0) - | (pThis->aCts[i].aIfs[1].pDrvBase != NULL ? BM_STATUS_D1DMA : 0); - pThis->aCts[i].BmDma.pvAddr = 0; - - pThis->aCts[i].fReset = true; - pThis->aCts[i].fRedo = false; - pThis->aCts[i].fRedoIdle = false; - ataAsyncIOClearRequests(&pThis->aCts[i]); - Log2(("%s: Ctl#%d: message to async I/O thread, reset controller\n", __FUNCTION__, i)); - ataAsyncIOPutRequest(&pThis->aCts[i], &g_ataResetARequest); - ataAsyncIOPutRequest(&pThis->aCts[i], &g_ataResetCRequest); - - PDMCritSectLeave(&pThis->aCts[i].lock); - } - - int rcRet = VINF_SUCCESS; - if (!fConstruct) - { - /* - * Setup asynchronous notification completion if the requests haven't - * completed yet. - */ - if (!ataR3IsAsyncResetDone(pDevIns)) - PDMDevHlpSetAsyncNotification(pDevIns, ataR3IsAsyncResetDone); - } - else - { - /* - * Wait for the requests for complete. - * - * Would be real nice if we could do it all from EMT(0) and not - * involve the worker threads, then we could dispense with all the - * waiting and semaphore ping-pong here... - */ - for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++) - { - if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD) - { - int rc = RTSemMutexRequest(pThis->aCts[i].AsyncIORequestMutex, RT_INDEFINITE_WAIT); - AssertRC(rc); - - ASMAtomicWriteBool(&pThis->aCts[i].fSignalIdle, true); - rc = RTThreadUserReset(pThis->aCts[i].AsyncIOThread); - AssertRC(rc); - - rc = RTSemMutexRelease(pThis->aCts[i].AsyncIORequestMutex); - AssertRC(rc); - - if (!ataAsyncIOIsIdle(&pThis->aCts[i], false /*fStrict*/)) - { - rc = RTThreadUserWait(pThis->aCts[i].AsyncIOThread, 30*1000 /*ms*/); - if (RT_FAILURE(rc)) - rc = RTThreadUserWait(pThis->aCts[i].AsyncIOThread, 1000 /*ms*/); - if (RT_FAILURE(rc)) - { - AssertRC(rc); - rcRet = rc; - } - } - } - ASMAtomicWriteBool(&pThis->aCts[i].fSignalIdle, false); - } - if (RT_SUCCESS(rcRet)) - { - rcRet = ataR3IsAsyncResetDone(pDevIns) ? VINF_SUCCESS : VERR_INTERNAL_ERROR; - AssertRC(rcRet); - } - } - return rcRet; -} - - -/** - * Reset notification. - * - * @param pDevIns The device instance data. - */ -static DECLCALLBACK(void) ataR3Reset(PPDMDEVINS pDevIns) -{ - ataR3ResetCommon(pDevIns, false /*fConstruct*/); -} - - /** * Prepare state save and load operation. * @@ -6858,7 +6458,6 @@ static DECLCALLBACK(int) ataLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32 return VINF_SSM_DONT_CALL_AGAIN; } - /** * @copydoc FNSSMDEVSAVEEXEC */ @@ -7091,6 +6690,12 @@ static DECLCALLBACK(int) ataLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32 SSMR3GetBool(pSSM, &pThis->aCts[i].aIfs[j].fATAPITransfer); SSMR3GetU32(pSSM, &pThis->aCts[i].aIfs[j].cbTotalTransfer); SSMR3GetU32(pSSM, &pThis->aCts[i].aIfs[j].cbElementaryTransfer); + /* NB: cbPIOTransferLimit could be saved/restored but it's sufficient + * to re-calculate it here, with a tiny risk that it could be + * unnecessarily low for the current transfer only. Could be changed + * when changing the saved state in the future. + */ + pThis->aCts[i].aIfs[j].cbPIOTransferLimit = (pThis->aCts[i].aIfs[j].uATARegHCyl << 8) | pThis->aCts[i].aIfs[j].uATARegLCyl; SSMR3GetU32(pSSM, &pThis->aCts[i].aIfs[j].iIOBufferCur); SSMR3GetU32(pSSM, &pThis->aCts[i].aIfs[j].iIOBufferEnd); SSMR3GetU32(pSSM, &pThis->aCts[i].aIfs[j].iIOBufferPIODataStart); @@ -7158,6 +6763,293 @@ static DECLCALLBACK(int) ataLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32 return VINF_SUCCESS; } + +/** + * Callback employed by ataSuspend and ataR3PowerOff. + * + * @returns true if we've quiesced, false if we're still working. + * @param pDevIns The device instance. + */ +static DECLCALLBACK(bool) ataR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns) +{ + return ataR3AllAsyncIOIsIdle(pDevIns); +} + + +/** + * Common worker for ataSuspend and ataR3PowerOff. + */ +static void ataR3SuspendOrPowerOff(PPDMDEVINS pDevIns) +{ + if (!ataR3AllAsyncIOIsIdle(pDevIns)) + PDMDevHlpSetAsyncNotification(pDevIns, ataR3IsAsyncSuspendOrPowerOffDone); +} + + +/** + * Power Off notification. + * + * @returns VBox status. + * @param pDevIns The device instance data. + */ +static DECLCALLBACK(void) ataR3PowerOff(PPDMDEVINS pDevIns) +{ + Log(("%s:\n", __FUNCTION__)); + ataR3SuspendOrPowerOff(pDevIns); +} + + +/** + * Suspend notification. + * + * @returns VBox status. + * @param pDevIns The device instance data. + */ +static DECLCALLBACK(void) ataR3Suspend(PPDMDEVINS pDevIns) +{ + Log(("%s:\n", __FUNCTION__)); + ataR3SuspendOrPowerOff(pDevIns); +} + + +/** + * Callback employed by ataR3Reset. + * + * @returns true if we've quiesced, false if we're still working. + * @param pDevIns The device instance. + */ +static DECLCALLBACK(bool) ataR3IsAsyncResetDone(PPDMDEVINS pDevIns) +{ + PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *); + + if (!ataR3AllAsyncIOIsIdle(pDevIns)) + return false; + + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++) + { + PDMCritSectEnter(&pThis->aCts[i].lock, VERR_INTERNAL_ERROR); + for (uint32_t j = 0; j < RT_ELEMENTS(pThis->aCts[i].aIfs); j++) + ataResetDevice(&pThis->aCts[i].aIfs[j]); + PDMCritSectLeave(&pThis->aCts[i].lock); + } + return true; +} + + +/** + * Common reset worker for ataR3Reset and ataR3Construct. + * + * @returns VBox status. + * @param pDevIns The device instance data. + * @param fConstruct Indicates who is calling. + */ +static int ataR3ResetCommon(PPDMDEVINS pDevIns, bool fConstruct) +{ + PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *); + + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++) + { + PDMCritSectEnter(&pThis->aCts[i].lock, VERR_INTERNAL_ERROR); + + pThis->aCts[i].iSelectedIf = 0; + pThis->aCts[i].iAIOIf = 0; + pThis->aCts[i].BmDma.u8Cmd = 0; + /* Report that both drives present on the bus are in DMA mode. This + * pretends that there is a BIOS that has set it up. Normal reset + * default is 0x00. */ + pThis->aCts[i].BmDma.u8Status = (pThis->aCts[i].aIfs[0].pDrvBase != NULL ? BM_STATUS_D0DMA : 0) + | (pThis->aCts[i].aIfs[1].pDrvBase != NULL ? BM_STATUS_D1DMA : 0); + pThis->aCts[i].BmDma.pvAddr = 0; + + pThis->aCts[i].fReset = true; + pThis->aCts[i].fRedo = false; + pThis->aCts[i].fRedoIdle = false; + ataAsyncIOClearRequests(&pThis->aCts[i]); + Log2(("%s: Ctl#%d: message to async I/O thread, reset controller\n", __FUNCTION__, i)); + ataAsyncIOPutRequest(&pThis->aCts[i], &g_ataResetARequest); + ataAsyncIOPutRequest(&pThis->aCts[i], &g_ataResetCRequest); + + PDMCritSectLeave(&pThis->aCts[i].lock); + } + + int rcRet = VINF_SUCCESS; + if (!fConstruct) + { + /* + * Setup asynchronous notification completion if the requests haven't + * completed yet. + */ + if (!ataR3IsAsyncResetDone(pDevIns)) + PDMDevHlpSetAsyncNotification(pDevIns, ataR3IsAsyncResetDone); + } + else + { + /* + * Wait for the requests for complete. + * + * Would be real nice if we could do it all from EMT(0) and not + * involve the worker threads, then we could dispense with all the + * waiting and semaphore ping-pong here... + */ + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++) + { + if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD) + { + int rc = RTSemMutexRequest(pThis->aCts[i].AsyncIORequestMutex, RT_INDEFINITE_WAIT); + AssertRC(rc); + + ASMAtomicWriteBool(&pThis->aCts[i].fSignalIdle, true); + rc = RTThreadUserReset(pThis->aCts[i].AsyncIOThread); + AssertRC(rc); + + rc = RTSemMutexRelease(pThis->aCts[i].AsyncIORequestMutex); + AssertRC(rc); + + if (!ataAsyncIOIsIdle(&pThis->aCts[i], false /*fStrict*/)) + { + rc = RTThreadUserWait(pThis->aCts[i].AsyncIOThread, 30*1000 /*ms*/); + if (RT_FAILURE(rc)) + rc = RTThreadUserWait(pThis->aCts[i].AsyncIOThread, 1000 /*ms*/); + if (RT_FAILURE(rc)) + { + AssertRC(rc); + rcRet = rc; + } + } + } + ASMAtomicWriteBool(&pThis->aCts[i].fSignalIdle, false); + } + if (RT_SUCCESS(rcRet)) + { + rcRet = ataR3IsAsyncResetDone(pDevIns) ? VINF_SUCCESS : VERR_INTERNAL_ERROR; + AssertRC(rcRet); + } + } + return rcRet; +} + +/** + * Reset notification. + * + * @param pDevIns The device instance data. + */ +static DECLCALLBACK(void) ataR3Reset(PPDMDEVINS pDevIns) +{ + ataR3ResetCommon(pDevIns, false /*fConstruct*/); +} + +/** + * @copydoc FNPDMDEVRELOCATE + */ +static DECLCALLBACK(void) ataR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta) +{ + PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *); + + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++) + { + pThis->aCts[i].pDevInsRC += offDelta; + pThis->aCts[i].aIfs[0].pDevInsRC += offDelta; + pThis->aCts[i].aIfs[0].pControllerRC += offDelta; + ataRelocBuffer(pDevIns, &pThis->aCts[i].aIfs[0]); + pThis->aCts[i].aIfs[1].pDevInsRC += offDelta; + pThis->aCts[i].aIfs[1].pControllerRC += offDelta; + ataRelocBuffer(pDevIns, &pThis->aCts[i].aIfs[1]); + } +} + +/** + * Destroy a driver instance. + * + * Most VM resources are freed by the VM. This callback is provided so that any non-VM + * resources can be freed correctly. + * + * @param pDevIns The device instance data. + */ +static DECLCALLBACK(int) ataR3Destruct(PPDMDEVINS pDevIns) +{ + PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *); + int rc; + + Log(("ataR3Destruct\n")); + PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); + + /* + * Tell the async I/O threads to terminate. + */ + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++) + { + if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD) + { + ASMAtomicWriteU32(&pThis->aCts[i].fShutdown, true); + rc = RTSemEventSignal(pThis->aCts[i].AsyncIOSem); + AssertRC(rc); + rc = RTSemEventSignal(pThis->aCts[i].SuspendIOSem); + AssertRC(rc); + } + } + + /* + * Wait for the threads to terminate before destroying their resources. + */ + for (unsigned i = 0; i < RT_ELEMENTS(pThis->aCts); i++) + { + if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD) + { + rc = RTThreadWait(pThis->aCts[i].AsyncIOThread, 30000 /* 30 s*/, NULL); + if (RT_SUCCESS(rc)) + pThis->aCts[i].AsyncIOThread = NIL_RTTHREAD; + else + LogRel(("PIIX3 ATA Dtor: Ctl#%u is still executing, DevSel=%d AIOIf=%d CmdIf0=%#04x CmdIf1=%#04x rc=%Rrc\n", + i, pThis->aCts[i].iSelectedIf, pThis->aCts[i].iAIOIf, + pThis->aCts[i].aIfs[0].uATARegCommand, pThis->aCts[i].aIfs[1].uATARegCommand, rc)); + } + } + + /* + * Free resources. + */ + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++) + { + if (pThis->aCts[i].AsyncIORequestMutex != NIL_RTSEMMUTEX) + { + RTSemMutexDestroy(pThis->aCts[i].AsyncIORequestMutex); + pThis->aCts[i].AsyncIORequestMutex = NIL_RTSEMMUTEX; + } + if (pThis->aCts[i].AsyncIOSem != NIL_RTSEMEVENT) + { + RTSemEventDestroy(pThis->aCts[i].AsyncIOSem); + pThis->aCts[i].AsyncIOSem = NIL_RTSEMEVENT; + } + if (pThis->aCts[i].SuspendIOSem != NIL_RTSEMEVENT) + { + RTSemEventDestroy(pThis->aCts[i].SuspendIOSem); + pThis->aCts[i].SuspendIOSem = NIL_RTSEMEVENT; + } + + /* try one final time */ + if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD) + { + rc = RTThreadWait(pThis->aCts[i].AsyncIOThread, 1 /*ms*/, NULL); + if (RT_SUCCESS(rc)) + { + pThis->aCts[i].AsyncIOThread = NIL_RTTHREAD; + LogRel(("PIIX3 ATA Dtor: Ctl#%u actually completed.\n", i)); + } + } + + for (uint32_t iIf = 0; iIf < RT_ELEMENTS(pThis->aCts[i].aIfs); iIf++) + { + if (pThis->aCts[i].aIfs[iIf].pTrackList) + { + ATAPIPassthroughTrackListDestroy(pThis->aCts[i].aIfs[iIf].pTrackList); + pThis->aCts[i].aIfs[iIf].pTrackList = NULL; + } + } + } + + return VINF_SUCCESS; +} + /** * Convert config value to DEVPCBIOSBOOT. * @@ -7190,7 +7082,6 @@ static int ataControllerFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfg, CHIPSET *pen return rc; } - /** * @interface_method_impl{PDMDEVREG,pfnConstruct} */ @@ -7346,6 +7237,12 @@ static DECLCALLBACK(int) ataR3Construct(PPDMDEVINS pDevIns, int iInstance, PCF pThis->aCts[1].IOPortBase2 = 0x376; /* + * Set the default critical section to NOP as we lock on controller level. + */ + rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns)); + AssertRCReturn(rc, rc); + + /* * Register the PCI device. * N.B. There's a hack in the PIIX3 PCI bridge device to assign this * device the slot next to itself. @@ -7714,7 +7611,7 @@ const PDMDEVREG g_DevicePIIX3IDE = ataR3Destruct, /* pfnRelocate */ ataR3Relocate, - /* pfnIOCtl */ + /* pfnMemSetup */ NULL, /* pfnPowerOn */ NULL, diff --git a/src/VBox/Devices/Storage/DevBusLogic.cpp b/src/VBox/Devices/Storage/DevBusLogic.cpp index b4932a55..b0b52a12 100644 --- a/src/VBox/Devices/Storage/DevBusLogic.cpp +++ b/src/VBox/Devices/Storage/DevBusLogic.cpp @@ -1,10 +1,12 @@ /* $Id: DevBusLogic.cpp $ */ /** @file - * VBox storage devices: BusLogic SCSI host adapter BT-958. + * VBox storage devices - BusLogic SCSI host adapter BT-958. + * + * Based on the Multi-Master Ultra SCSI Systems Technical Reference Manual. */ /* - * Copyright (C) 2006-2012 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; @@ -15,14 +17,10 @@ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ -/* Implemented looking at the driver source in the linux kernel (drivers/scsi/BusLogic.[ch]). - * See also: http://www.drdobbs.com/184410111 - */ /******************************************************************************* * Header Files * *******************************************************************************/ -//#define DEBUG #define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC #include <VBox/vmm/pdmdev.h> #include <VBox/vmm/pdmifs.h> @@ -42,32 +40,50 @@ #include "VBoxSCSI.h" #include "VBoxDD.h" -/* Maximum number of attached devices the adapter can handle. */ + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** Maximum number of attached devices the adapter can handle. */ #define BUSLOGIC_MAX_DEVICES 16 -/* Maximum number of scatter gather elements this device can handle. */ +/** Maximum number of scatter gather elements this device can handle. */ #define BUSLOGIC_MAX_SCATTER_GATHER_LIST_SIZE 128 -/* Size of the command buffer. */ -#define BUSLOGIC_COMMAND_SIZE_MAX 5 +/** Size of the command buffer. */ +#define BUSLOGIC_COMMAND_SIZE_MAX 53 -/* Size of the reply buffer. */ -#define BUSLOGIC_REPLY_SIZE_MAX 64 +/** Size of the reply buffer. */ +#define BUSLOGIC_REPLY_SIZE_MAX 64 -/* - * Custom fixed I/O ports for BIOS controller access. Note that these should - * not be in the ISA range (below 400h) to avoid conflicts with ISA device - * probing. Addresses in the 300h-340h range should be especially avoided. +/** Custom fixed I/O ports for BIOS controller access. + * Note that these should not be in the ISA range (below 400h) to avoid + * conflicts with ISA device probing. Addresses in the 300h-340h range should be + * especially avoided. */ - -#define BUSLOGIC_BIOS_IO_PORT 0x330 +#define BUSLOGIC_BIOS_IO_PORT 0x430 /** State saved version. */ -#define BUSLOGIC_SAVED_STATE_MINOR_VERSION 2 +#define BUSLOGIC_SAVED_STATE_MINOR_VERSION 4 /** Saved state version before the suspend on error feature was implemented. */ #define BUSLOGIC_SAVED_STATE_MINOR_PRE_ERROR_HANDLING 1 +/** Saved state version before 24-bit mailbox support was implemented. */ +#define BUSLOGIC_SAVED_STATE_MINOR_PRE_24BIT_MBOX 2 +/** Saved state version before command buffer size was raised. */ +#define BUSLOGIC_SAVED_STATE_MINOR_PRE_CMDBUF_RESIZE 3 + +/** Command buffer size in old saved states. */ +#define BUSLOGIC_COMMAND_SIZE_OLD 5 + +/** The duration of software-initiated reset (in nano seconds). + * Not documented, set to 50 ms. */ +#define BUSLOGIC_RESET_DURATION_NS UINT64_C(50000000) + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ /** * State of a device attached to the buslogic host adapter. * @@ -115,12 +131,12 @@ typedef struct BUSLOGICDEVICE } BUSLOGICDEVICE, *PBUSLOGICDEVICE; -/* +/** * Commands the BusLogic adapter supports. */ enum BUSLOGICCOMMAND { - BUSLOGICCOMMAND_TEST_COMMAND_COMPLETE_INTERRUPT = 0x00, + BUSLOGICCOMMAND_TEST_CMDC_INTERRUPT = 0x00, BUSLOGICCOMMAND_INITIALIZE_MAILBOX = 0x01, BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND = 0x02, BUSLOGICCOMMAND_EXECUTE_BIOS_COMMAND = 0x03, @@ -180,35 +196,35 @@ typedef struct AutoSCSIRam uint8_t cbInformation; uint8_t aHostAdaptertype[6]; uint8_t uReserved1; - bool fFloppyEnabled: 1; - bool fFloppySecondary: 1; - bool fLevelSensitiveInterrupt: 1; - unsigned char uReserved2: 2; - unsigned char uSystemRAMAreForBIOS: 3; - unsigned char uDMAChannel: 7; - bool fDMAAutoConfiguration: 1; - unsigned char uIrqChannel: 7; - bool fIrqAutoConfiguration: 1; + bool fFloppyEnabled : 1; + bool fFloppySecondary : 1; + bool fLevelSensitiveInterrupt : 1; + unsigned char uReserved2 : 2; + unsigned char uSystemRAMAreForBIOS : 3; + unsigned char uDMAChannel : 7; + bool fDMAAutoConfiguration : 1; + unsigned char uIrqChannel : 7; + bool fIrqAutoConfiguration : 1; uint8_t uDMATransferRate; uint8_t uSCSIId; - bool fLowByteTerminated: 1; - bool fParityCheckingEnabled: 1; - bool fHighByteTerminated: 1; - bool fNoisyCablingEnvironment: 1; - bool fFastSynchronousNeogtiation: 1; - bool fBusResetEnabled: 1; - bool fReserved3: 1; - bool fActiveNegotiationEnabled: 1; + bool fLowByteTerminated : 1; + bool fParityCheckingEnabled : 1; + bool fHighByteTerminated : 1; + bool fNoisyCablingEnvironment : 1; + bool fFastSynchronousNeogtiation : 1; + bool fBusResetEnabled : 1; + bool fReserved3 : 1; + bool fActiveNegotiationEnabled : 1; uint8_t uBusOnDelay; uint8_t uBusOffDelay; - bool fHostAdapterBIOSEnabled: 1; - bool fBIOSRedirectionOfInt19: 1; - bool fExtendedTranslation: 1; - bool fMapRemovableAsFixed: 1; - bool fReserved4: 1; - bool fBIOSSupportsMoreThan2Drives: 1; - bool fBIOSInterruptMode: 1; - bool fFlopticalSupport: 1; + bool fHostAdapterBIOSEnabled : 1; + bool fBIOSRedirectionOfInt19 : 1; + bool fExtendedTranslation : 1; + bool fMapRemovableAsFixed : 1; + bool fReserved4 : 1; + bool fBIOSSupportsMoreThan2Drives : 1; + bool fBIOSInterruptMode : 1; + bool fFlopticalSupport : 1; uint16_t u16DeviceEnabledMask; uint16_t u16WidePermittedMask; uint16_t u16FastPermittedMask; @@ -216,29 +232,29 @@ typedef struct AutoSCSIRam uint16_t u16DisconnectPermittedMask; uint16_t u16SendStartUnitCommandMask; uint16_t u16IgnoreInBIOSScanMask; - unsigned char uPCIInterruptPin: 2; - unsigned char uHostAdapterIoPortAddress: 2; - bool fStrictRoundRobinMode: 1; - bool fVesaBusSpeedGreaterThan33MHz: 1; - bool fVesaBurstWrite: 1; - bool fVesaBurstRead: 1; + unsigned char uPCIInterruptPin : 2; + unsigned char uHostAdapterIoPortAddress : 2; + bool fStrictRoundRobinMode : 1; + bool fVesaBusSpeedGreaterThan33MHz : 1; + bool fVesaBurstWrite : 1; + bool fVesaBurstRead : 1; uint16_t u16UltraPermittedMask; uint32_t uReserved5; uint8_t uReserved6; uint8_t uAutoSCSIMaximumLUN; - bool fReserved7: 1; - bool fSCAMDominant: 1; - bool fSCAMenabled: 1; - bool fSCAMLevel2: 1; - unsigned char uReserved8: 4; - bool fInt13Extension: 1; - bool fReserved9: 1; - bool fCDROMBoot: 1; - unsigned char uReserved10: 5; - unsigned char uBootTargetId: 4; - unsigned char uBootChannel: 4; - bool fForceBusDeviceScanningOrder: 1; - unsigned char uReserved11: 7; + bool fReserved7 : 1; + bool fSCAMDominant : 1; + bool fSCAMenabled : 1; + bool fSCAMLevel2 : 1; + unsigned char uReserved8 : 4; + bool fInt13Extension : 1; + bool fReserved9 : 1; + bool fCDROMBoot : 1; + unsigned char uReserved10 : 5; + unsigned char uBootTargetId : 4; + unsigned char uBootChannel : 4; + bool fForceBusDeviceScanningOrder : 1; + unsigned char uReserved11 : 7; uint16_t u16NonTaggedToAlternateLunPermittedMask; uint16_t u16RenegotiateSyncAfterCheckConditionMask; uint8_t aReserved12[10]; @@ -248,15 +264,14 @@ typedef struct AutoSCSIRam AssertCompileSize(AutoSCSIRam, 64); #pragma pack() -#pragma pack(1) /** * The local Ram. */ typedef union HostAdapterLocalRam { - /* Byte view. */ + /** Byte view. */ uint8_t u8View[256]; - /* Structured view. */ + /** Structured view. */ struct { /** Offset 0 - 63 is for BIOS. */ @@ -266,7 +281,33 @@ typedef union HostAdapterLocalRam } structured; } HostAdapterLocalRam, *PHostAdapterLocalRam; AssertCompileSize(HostAdapterLocalRam, 256); -#pragma pack() + + +/** Ugly 24-bit big-endian addressing. */ +typedef struct +{ + uint8_t hi; + uint8_t mid; + uint8_t lo; +} Addr24, Len24; +AssertCompileSize(Addr24, 3); + +#define ADDR_TO_U32(x) (((x).hi << 16) | ((x).mid << 8) | (x).lo) +#define LEN_TO_U32 ADDR_TO_U32 +#define U32_TO_ADDR(a, x) do {(a).hi = (x) >> 16; (a).mid = (x) >> 8; (a).lo = (x);} while(0) +#define U32_TO_LEN U32_TO_ADDR + +/** @name Compatible ISA base I/O port addresses. Disabled if zero. + * @{ */ +#define NUM_ISA_BASES 8 +#define MAX_ISA_BASE (NUM_ISA_BASES - 1) +#define ISA_BASE_DISABLED 6 + +static uint16_t const g_aISABases[NUM_ISA_BASES] = +{ + 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0, 0 +}; +/** @} */ /** Pointer to a task state structure. */ typedef struct BUSLOGICTASKSTATE *PBUSLOGICTASKSTATE; @@ -303,6 +344,8 @@ typedef struct BUSLOGIC volatile uint8_t regInterrupt; /** Geometry register - Readonly. */ volatile uint8_t regGeometry; + /** Pending (delayed) interrupt. */ + uint8_t uPendingIntr; /** Local RAM for the fetch hostadapter local RAM request. * I don't know how big the buffer really is but the maximum @@ -334,8 +377,17 @@ typedef struct BUSLOGIC /** Flag whether IRQs are enabled. */ bool fIRQEnabled; /** Flag whether the ISA I/O port range is disabled - * to prevent the BIOs to access the device. */ - bool fISAEnabled; + * to prevent the BIOS to access the device. */ + bool fISAEnabled; /**< @todo unused, to be removed */ + /** Flag whether 24-bit mailboxes are in use (default is 32-bit). */ + bool fMbxIs24Bit; + /** ISA I/O port base (encoded in FW-compatible format). */ + uint8_t uISABaseCode; + + /** ISA I/O port base (disabled if zero). */ + RTIOPORT IOISABase; + /** Default ISA I/O port base in FW-compatible format. */ + uint8_t uDefaultISABaseCode; /** Number of mailboxes the guest set up. */ uint32_t cMailbox; @@ -344,6 +396,8 @@ typedef struct BUSLOGIC uint32_t Alignment0; #endif + /** Time when HBA reset was last initiated. */ /**< @todo does this need to be saved? */ + uint64_t u64ResetTime; /** Physical base address of the outgoing mailboxes. */ RTGCPHYS GCPhysAddrMailboxOutgoingBase; /** Current outgoing mailbox position. */ @@ -419,14 +473,14 @@ typedef struct BUSLOGIC } BUSLOGIC, *PBUSLOGIC; /** Register offsets in the I/O port space. */ -#define BUSLOGIC_REGISTER_CONTROL 0 /* Writeonly */ +#define BUSLOGIC_REGISTER_CONTROL 0 /**< Writeonly */ /** Fields for the control register. */ # define BUSLOGIC_REGISTER_CONTROL_SCSI_BUSRESET RT_BIT(4) # define BUSLOGIC_REGISTER_CONTROL_INTERRUPT_RESET RT_BIT(5) # define BUSLOGIC_REGISTER_CONTROL_SOFT_RESET RT_BIT(6) # define BUSLOGIC_REGISTER_CONTROL_HARD_RESET RT_BIT(7) -#define BUSLOGIC_REGISTER_STATUS 0 /* Readonly */ +#define BUSLOGIC_REGISTER_STATUS 0 /**< Readonly */ /** Fields for the status register. */ # define BUSLOGIC_REGISTER_STATUS_COMMAND_INVALID RT_BIT(0) # define BUSLOGIC_REGISTER_STATUS_DATA_IN_REGISTER_READY RT_BIT(2) @@ -436,12 +490,12 @@ typedef struct BUSLOGIC # define BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_FAILURE RT_BIT(6) # define BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE RT_BIT(7) -#define BUSLOGIC_REGISTER_COMMAND 1 /* Writeonly */ -#define BUSLOGIC_REGISTER_DATAIN 1 /* Readonly */ -#define BUSLOGIC_REGISTER_INTERRUPT 2 /* Readonly */ +#define BUSLOGIC_REGISTER_COMMAND 1 /**< Writeonly */ +#define BUSLOGIC_REGISTER_DATAIN 1 /**< Readonly */ +#define BUSLOGIC_REGISTER_INTERRUPT 2 /**< Readonly */ /** Fields for the interrupt register. */ # define BUSLOGIC_REGISTER_INTERRUPT_INCOMING_MAILBOX_LOADED RT_BIT(0) -# define BUSLOGIC_REGISTER_INTERRUPT_OUTCOMING_MAILBOX_AVAILABLE RT_BIT(1) +# define BUSLOGIC_REGISTER_INTERRUPT_OUTGOING_MAILBOX_AVAILABLE RT_BIT(1) # define BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE RT_BIT(2) # define BUSLOGIC_REGISTER_INTERRUPT_EXTERNAL_BUS_RESET RT_BIT(3) # define BUSLOGIC_REGISTER_INTERRUPT_INTERRUPT_VALID RT_BIT(7) @@ -449,69 +503,62 @@ typedef struct BUSLOGIC #define BUSLOGIC_REGISTER_GEOMETRY 3 /* Readonly */ # define BUSLOGIC_REGISTER_GEOMETRY_EXTENTED_TRANSLATION_ENABLED RT_BIT(7) -/* Structure for the INQUIRE_PCI_HOST_ADAPTER_INFORMATION reply. */ -#pragma pack(1) +/** Structure for the INQUIRE_PCI_HOST_ADAPTER_INFORMATION reply. */ typedef struct ReplyInquirePCIHostAdapterInformation { uint8_t IsaIOPort; uint8_t IRQ; - unsigned char LowByteTerminated:1; - unsigned char HighByteTerminated:1; - unsigned char uReserved:2; /* Reserved. */ - unsigned char JP1:1; /* Whatever that means. */ - unsigned char JP2:1; /* Whatever that means. */ - unsigned char JP3:1; /* Whatever that means. */ + unsigned char LowByteTerminated : 1; + unsigned char HighByteTerminated : 1; + unsigned char uReserved : 2; /* Reserved. */ + unsigned char JP1 : 1; /* Whatever that means. */ + unsigned char JP2 : 1; /* Whatever that means. */ + unsigned char JP3 : 1; /* Whatever that means. */ /** Whether the provided info is valid. */ unsigned char InformationIsValid: 1; uint8_t uReserved2; /* Reserved. */ } ReplyInquirePCIHostAdapterInformation, *PReplyInquirePCIHostAdapterInformation; AssertCompileSize(ReplyInquirePCIHostAdapterInformation, 4); -#pragma pack() -/* Structure for the INQUIRE_CONFIGURATION reply. */ -#pragma pack(1) +/** Structure for the INQUIRE_CONFIGURATION reply. */ typedef struct ReplyInquireConfiguration { - unsigned char uReserved1: 5; - bool fDmaChannel5: 1; - bool fDmaChannel6: 1; - bool fDmaChannel7: 1; - bool fIrqChannel9: 1; - bool fIrqChannel10: 1; - bool fIrqChannel11: 1; - bool fIrqChannel12: 1; - unsigned char uReserved2: 1; - bool fIrqChannel14: 1; - bool fIrqChannel15: 1; - unsigned char uReserved3: 1; - unsigned char uHostAdapterId: 4; - unsigned char uReserved4: 4; + unsigned char uReserved1 : 5; + bool fDmaChannel5 : 1; + bool fDmaChannel6 : 1; + bool fDmaChannel7 : 1; + bool fIrqChannel9 : 1; + bool fIrqChannel10 : 1; + bool fIrqChannel11 : 1; + bool fIrqChannel12 : 1; + unsigned char uReserved2 : 1; + bool fIrqChannel14 : 1; + bool fIrqChannel15 : 1; + unsigned char uReserved3 : 1; + unsigned char uHostAdapterId : 4; + unsigned char uReserved4 : 4; } ReplyInquireConfiguration, *PReplyInquireConfiguration; AssertCompileSize(ReplyInquireConfiguration, 3); -#pragma pack() -/* Structure for the INQUIRE_SETUP_INFORMATION reply. */ -#pragma pack(1) +/** Structure for the INQUIRE_SETUP_INFORMATION reply. */ typedef struct ReplyInquireSetupInformationSynchronousValue { - unsigned char uOffset: 4; - unsigned char uTransferPeriod: 3; - bool fSynchronous: 1; + unsigned char uOffset : 4; + unsigned char uTransferPeriod : 3; + bool fSynchronous : 1; }ReplyInquireSetupInformationSynchronousValue, *PReplyInquireSetupInformationSynchronousValue; AssertCompileSize(ReplyInquireSetupInformationSynchronousValue, 1); -#pragma pack() -#pragma pack(1) typedef struct ReplyInquireSetupInformation { - bool fSynchronousInitiationEnabled: 1; - bool fParityCheckingEnabled: 1; - unsigned char uReserved1: 6; + bool fSynchronousInitiationEnabled : 1; + bool fParityCheckingEnabled : 1; + unsigned char uReserved1 : 6; uint8_t uBusTransferRate; uint8_t uPreemptTimeOnBus; uint8_t uTimeOffBus; uint8_t cMailbox; - uint8_t MailboxAddress[3]; + Addr24 MailboxAddress; ReplyInquireSetupInformationSynchronousValue SynchronousValuesId0To7[8]; uint8_t uDisconnectPermittedId0To7; uint8_t uSignature; @@ -526,9 +573,8 @@ typedef struct ReplyInquireSetupInformation uint8_t uWideTransfersActiveId8To15; } ReplyInquireSetupInformation, *PReplyInquireSetupInformation; AssertCompileSize(ReplyInquireSetupInformation, 34); -#pragma pack() -/* Structure for the INQUIRE_EXTENDED_SETUP_INFORMATION. */ +/** Structure for the INQUIRE_EXTENDED_SETUP_INFORMATION. */ #pragma pack(1) typedef struct ReplyInquireExtendedSetupInformation { @@ -537,23 +583,23 @@ typedef struct ReplyInquireExtendedSetupInformation uint16_t u16ScatterGatherLimit; uint8_t cMailbox; uint32_t uMailboxAddressBase; - unsigned char uReserved1: 2; - bool fFastEISA: 1; - unsigned char uReserved2: 3; - bool fLevelSensitiveInterrupt: 1; - unsigned char uReserved3: 1; + unsigned char uReserved1 : 2; + bool fFastEISA : 1; + unsigned char uReserved2 : 3; + bool fLevelSensitiveInterrupt : 1; + unsigned char uReserved3 : 1; unsigned char aFirmwareRevision[3]; - bool fHostWideSCSI: 1; - bool fHostDifferentialSCSI: 1; - bool fHostSupportsSCAM: 1; - bool fHostUltraSCSI: 1; - bool fHostSmartTermination: 1; - unsigned char uReserved4: 3; + bool fHostWideSCSI : 1; + bool fHostDifferentialSCSI : 1; + bool fHostSupportsSCAM : 1; + bool fHostUltraSCSI : 1; + bool fHostSmartTermination : 1; + unsigned char uReserved4 : 3; } ReplyInquireExtendedSetupInformation, *PReplyInquireExtendedSetupInformation; AssertCompileSize(ReplyInquireExtendedSetupInformation, 14); #pragma pack() -/* Structure for the INITIALIZE EXTENDED MAILBOX request. */ +/** Structure for the INITIALIZE EXTENDED MAILBOX request. */ #pragma pack(1) typedef struct RequestInitializeExtendedMailbox { @@ -565,7 +611,17 @@ typedef struct RequestInitializeExtendedMailbox AssertCompileSize(RequestInitializeExtendedMailbox, 5); #pragma pack() -/* +/** Structure for the INITIALIZE MAILBOX request. */ +typedef struct +{ + /** Number of mailboxes to set up. */ + uint8_t cMailbox; + /** Physical address of the first mailbox. */ + Addr24 aMailboxBaseAddr; +} RequestInitMbx, *PRequestInitMbx; +AssertCompileSize(RequestInitMbx, 4); + +/** * Structure of a mailbox in guest memory. * The incoming and outgoing mailbox have the same size * but the incoming one has some more fields defined which @@ -575,8 +631,7 @@ AssertCompileSize(RequestInitializeExtendedMailbox, 5); * for incoming ones the completion status code for the task. * We use one structure for both types. */ -#pragma pack(1) -typedef struct Mailbox +typedef struct Mailbox32 { /** Physical address of the CCB structure in the guest memory. */ uint32_t u32PhysAddrCCB; @@ -604,11 +659,20 @@ typedef struct Mailbox uint8_t uCompletionCode; } in; } u; -} Mailbox, *PMailbox; -AssertCompileSize(Mailbox, 8); -#pragma pack() +} Mailbox32, *PMailbox32; +AssertCompileSize(Mailbox32, 8); -/* +/** Old style 24-bit mailbox entry. */ +typedef struct Mailbox24 +{ + /** Mailbox command (incoming) or state (outgoing). */ + uint8_t uCmdState; + /** Physical address of the CCB structure in the guest memory. */ + Addr24 aPhysAddrCCB; +} Mailbox24, *PMailbox24; +AssertCompileSize(Mailbox24, 4); + +/** * Action codes for outgoing mailboxes. */ enum BUSLOGIC_MAILBOX_OUTGOING_ACTION @@ -618,7 +682,7 @@ enum BUSLOGIC_MAILBOX_OUTGOING_ACTION BUSLOGIC_MAILBOX_OUTGOING_ACTION_ABORT_COMMAND = 0x02 }; -/* +/** * Completion codes for incoming mailboxes. */ enum BUSLOGIC_MAILBOX_INCOMING_COMPLETION @@ -631,7 +695,7 @@ enum BUSLOGIC_MAILBOX_INCOMING_COMPLETION BUSLOGIC_MAILBOX_INCOMING_COMPLETION_INVALID_CCB = 0x05 }; -/* +/** * Host adapter status for incoming mailboxes. */ enum BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS @@ -663,7 +727,7 @@ enum BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_SCSI_PARITY_ERROR_DETECTED = 0x34 }; -/* +/** * Device status codes for incoming mailboxes. */ enum BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS @@ -673,7 +737,7 @@ enum BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_DEVICE_BUSY = 0x08 }; -/* +/** * Opcode types for CCB. */ enum BUSLOGIC_CCB_OPCODE @@ -686,7 +750,7 @@ enum BUSLOGIC_CCB_OPCODE BUSLOGIC_CCB_OPCODE_BUS_DEVICE_RESET = 0x81 }; -/* +/** * Data transfer direction. */ enum BUSLOGIC_CCB_DIRECTION @@ -697,22 +761,21 @@ enum BUSLOGIC_CCB_DIRECTION BUSLOGIC_CCB_DIRECTION_NO_DATA = 0x03 }; -/* +/** * The command control block for a SCSI request. */ -#pragma pack(1) -typedef struct CommandControlBlock +typedef struct CCB32 { /** Opcode. */ uint8_t uOpcode; /** Reserved */ - unsigned char uReserved1: 3; + unsigned char uReserved1 : 3; /** Data direction for the request. */ - unsigned char uDataDirection: 2; + unsigned char uDataDirection : 2; /** Whether the request is tag queued. */ - bool fTagQueued: 1; + bool fTagQueued : 1; /** Queue tag mode. */ - unsigned char uQueueTag: 2; + unsigned char uQueueTag : 2; /** Length of the SCSI CDB. */ uint8_t cbCDB; /** Sense data length. */ @@ -729,34 +792,153 @@ typedef struct CommandControlBlock uint8_t uHostAdapterStatus; /** Device adapter status. */ uint8_t uDeviceStatus; - /** The device the request is send to. */ + /** The device the request is sent to. */ uint8_t uTargetId; /**The LUN in the device. */ - unsigned char uLogicalUnit: 5; + unsigned char uLogicalUnit : 5; /** Legacy tag. */ - bool fLegacyTagEnable: 1; + bool fLegacyTagEnable : 1; /** Legacy queue tag. */ - unsigned char uLegacyQueueTag: 2; - /** The SCSI CDB. */ - uint8_t aCDB[12]; /* A CDB can be 12 bytes long. */ + unsigned char uLegacyQueueTag : 2; + /** The SCSI CDB. (A CDB can be 12 bytes long.) */ + uint8_t abCDB[12]; /** Reserved. */ uint8_t uReserved3[6]; /** Sense data pointer. */ uint32_t u32PhysAddrSenseData; -} CommandControlBlock, *PCommandControlBlock; -AssertCompileSize(CommandControlBlock, 40); -#pragma pack() +} CCB32, *PCCB32; +AssertCompileSize(CCB32, 40); -#pragma pack(1) -typedef struct ScatterGatherEntry + +/** + * The 24-bit command control block. + */ +typedef struct CCB24 +{ + /** Opcode. */ + uint8_t uOpcode; + /** The LUN in the device. */ + unsigned char uLogicalUnit : 3; + /** Data direction for the request. */ + unsigned char uDataDirection : 2; + /** The target device ID. */ + unsigned char uTargetId : 3; + /** Length of the SCSI CDB. */ + uint8_t cbCDB; + /** Sense data length. */ + uint8_t cbSenseData; + /** Data length. */ + Len24 acbData; + /** Data pointer. + * This points to the data region or a scatter gather list based on the opc + */ + Addr24 aPhysAddrData; + /** Pointer to next CCB for linked commands. */ + Addr24 aPhysAddrLink; + /** Command linking identifier. */ + uint8_t uLinkId; + /** Host adapter status. */ + uint8_t uHostAdapterStatus; + /** Device adapter status. */ + uint8_t uDeviceStatus; + /** Two unused bytes. */ + uint8_t aReserved[2]; + /** The SCSI CDB. (A CDB can be 12 bytes long.) */ + uint8_t abCDB[12]; +} CCB24, *PCCB24; +AssertCompileSize(CCB24, 30); + +/** + * The common 24-bit/32-bit command control block. The 32-bit CCB is laid out + * such that many fields are in the same location as in the older 24-bit CCB. + */ +typedef struct CCBC +{ + /** Opcode. */ + uint8_t uOpcode; + /** The LUN in the device. */ + unsigned char uPad1 : 3; + /** Data direction for the request. */ + unsigned char uDataDirection : 2; + /** The target device ID. */ + unsigned char uPad2 : 3; + /** Length of the SCSI CDB. */ + uint8_t cbCDB; + /** Sense data length. */ + uint8_t cbSenseData; + uint8_t aPad1[10]; + /** Host adapter status. */ + uint8_t uHostAdapterStatus; + /** Device adapter status. */ + uint8_t uDeviceStatus; + uint8_t aPad2[2]; + /** The SCSI CDB (up to 12 bytes). */ + uint8_t abCDB[12]; +} CCBC, *PCCBC; +AssertCompileSize(CCB24, 30); + +/* Make sure that the 24-bit/32-bit/common CCB offsets match. */ +AssertCompileMemberOffset(CCBC, cbCDB, 2); +AssertCompileMemberOffset(CCB24, cbCDB, 2); +AssertCompileMemberOffset(CCB32, cbCDB, 2); +AssertCompileMemberOffset(CCBC, uHostAdapterStatus, 14); +AssertCompileMemberOffset(CCB24, uHostAdapterStatus, 14); +AssertCompileMemberOffset(CCB32, uHostAdapterStatus, 14); +AssertCompileMemberOffset(CCBC, abCDB, 18); +AssertCompileMemberOffset(CCB24, abCDB, 18); +AssertCompileMemberOffset(CCB32, abCDB, 18); + +/** A union of all CCB types (24-bit/32-bit/common). */ +typedef union CCBU +{ + CCB32 n; /**< New 32-bit CCB. */ + CCB24 o; /**< Old 24-bit CCB. */ + CCBC c; /**< Common CCB subset. */ +} CCBU, *PCCBU; + +/** 32-bit scatter-gather list entry. */ +typedef struct SGE32 { uint32_t cbSegment; uint32_t u32PhysAddrSegmentBase; -} ScatterGatherEntry, *PScatterGatherEntry; -AssertCompileSize(ScatterGatherEntry, 8); -#pragma pack() +} SGE32, *PSGE32; +AssertCompileSize(SGE32, 8); -/* +/** 24-bit scatter-gather list entry. */ +typedef struct SGE24 +{ + Len24 acbSegment; + Addr24 aPhysAddrSegmentBase; +} SGE24, *PSGE24; +AssertCompileSize(SGE24, 6); + +/** + * The structure for the "Execute SCSI Command" command. + */ +typedef struct ESCMD +{ + /** Data length. */ + uint32_t cbData; + /** Data pointer. */ + uint32_t u32PhysAddrData; + /** The device the request is sent to. */ + uint8_t uTargetId; + /** The LUN in the device. */ + uint8_t uLogicalUnit; + /** Reserved */ + unsigned char uReserved1 : 3; + /** Data direction for the request. */ + unsigned char uDataDirection : 2; + /** Reserved */ + unsigned char uReserved2 : 3; + /** Length of the SCSI CDB. */ + uint8_t cbCDB; + /** The SCSI CDB. (A CDB can be 12 bytes long.) */ + uint8_t abCDB[12]; +} ESCMD, *PESCMD; +AssertCompileSize(ESCMD, 24); + +/** * Task state for a CCB request. */ typedef struct BUSLOGICTASKSTATE @@ -766,9 +948,9 @@ typedef struct BUSLOGICTASKSTATE /** Device this task is assigned to. */ R3PTRTYPE(PBUSLOGICDEVICE) pTargetDeviceR3; /** The command control block from the guest. */ - CommandControlBlock CommandControlBlockGuest; + CCBU CommandControlBlockGuest; /** Mailbox read from guest memory. */ - Mailbox MailboxGuest; + Mailbox32 MailboxGuest; /** The SCSI request we pass to the underlying SCSI engine. */ PDMSCSIREQUEST PDMScsiRequest; /** Data buffer segment */ @@ -777,6 +959,10 @@ typedef struct BUSLOGICTASKSTATE uint8_t *pbSenseBuffer; /** Flag whether this is a request from the BIOS. */ bool fBIOS; + /** 24-bit request flag (default is 32-bit). */ + bool fIs24Bit; + /** S/G entry size (depends on the above flag). */ + uint8_t cbSGEntry; } BUSLOGICTASKSTATE; #ifndef VBOX_DEVICE_STRUCT_TESTCASE @@ -787,18 +973,11 @@ typedef struct BUSLOGICTASKSTATE #define PDMIBASE_2_PBUSLOGIC(pInterface) ( (PBUSLOGIC)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGIC, IBase)) ) #define PDMILEDPORTS_2_PBUSLOGIC(pInterface) ( (PBUSLOGIC)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGIC, ILeds)) ) -/** - * Deasserts the interrupt line of the BusLogic adapter. - * - * @returns nothing - * @param pBuslogic Pointer to the BusLogic device instance. - */ -static void buslogicClearInterrupt(PBUSLOGIC pBusLogic) -{ - LogFlowFunc(("pBusLogic=%#p\n", pBusLogic)); - pBusLogic->regInterrupt = 0; - PDMDevHlpPCISetIrq(pBusLogic->CTX_SUFF(pDevIns), 0, 0); -} +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static int buslogicR3RegisterISARange(PBUSLOGIC pBusLogic, uint8_t uBaseCode); + /** * Assert IRQ line of the BusLogic adapter. @@ -806,31 +985,61 @@ static void buslogicClearInterrupt(PBUSLOGIC pBusLogic) * @returns nothing. * @param pBusLogic Pointer to the BusLogic device instance. * @param fSuppressIrq Flag to suppress IRQ generation regardless of fIRQEnabled + * @param uFlag Type of interrupt being generated. */ -static void buslogicSetInterrupt(PBUSLOGIC pBusLogic, bool fSuppressIrq) +static void buslogicSetInterrupt(PBUSLOGIC pBusLogic, bool fSuppressIrq, uint8_t uIrqType) { LogFlowFunc(("pBusLogic=%#p\n", pBusLogic)); + + /* The CMDC interrupt has priority over IMBL and MBOR. */ + if (uIrqType & (BUSLOGIC_REGISTER_INTERRUPT_INCOMING_MAILBOX_LOADED | BUSLOGIC_REGISTER_INTERRUPT_OUTGOING_MAILBOX_AVAILABLE)) + { + if (!(pBusLogic->regInterrupt & BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE)) + pBusLogic->regInterrupt |= uIrqType; /* Report now. */ + else + pBusLogic->uPendingIntr |= uIrqType; /* Report later. */ + } + else if (uIrqType & BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE) + { + Assert(!pBusLogic->regInterrupt); + pBusLogic->regInterrupt |= uIrqType; + } + else + AssertMsgFailed(("Invalid interrupt state!\n")); + pBusLogic->regInterrupt |= BUSLOGIC_REGISTER_INTERRUPT_INTERRUPT_VALID; if (pBusLogic->fIRQEnabled && !fSuppressIrq) PDMDevHlpPCISetIrq(pBusLogic->CTX_SUFF(pDevIns), 0, 1); } -#if defined(IN_RING3) - /** - * Advances the mailbox pointer to the next slot. + * Deasserts the interrupt line of the BusLogic adapter. + * + * @returns nothing + * @param pBuslogic Pointer to the BusLogic device instance. */ -DECLINLINE(void) buslogicOutgoingMailboxAdvance(PBUSLOGIC pBusLogic) +static void buslogicClearInterrupt(PBUSLOGIC pBusLogic) { - pBusLogic->uMailboxOutgoingPositionCurrent = (pBusLogic->uMailboxOutgoingPositionCurrent + 1) % pBusLogic->cMailbox; + LogFlowFunc(("pBusLogic=%#p, clearing %#02x (pending %#02x)\n", + pBusLogic, pBusLogic->regInterrupt, pBusLogic->uPendingIntr)); + pBusLogic->regInterrupt = 0; + PDMDevHlpPCISetIrq(pBusLogic->CTX_SUFF(pDevIns), 0, 0); + /* If there's another pending interrupt, report it now. */ + if (pBusLogic->uPendingIntr) + { + buslogicSetInterrupt(pBusLogic, false, pBusLogic->uPendingIntr); + pBusLogic->uPendingIntr = 0; + } } +#if defined(IN_RING3) + /** - * Returns the physical address of the next outgoing mailbox to process. + * Advances the mailbox pointer to the next slot. */ -DECLINLINE(RTGCPHYS) buslogicOutgoingMailboxGetGCPhys(PBUSLOGIC pBusLogic) +DECLINLINE(void) buslogicR3OutgoingMailboxAdvance(PBUSLOGIC pBusLogic) { - return pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox)); + pBusLogic->uMailboxOutgoingPositionCurrent = (pBusLogic->uMailboxOutgoingPositionCurrent + 1) % pBusLogic->cMailbox; } /** @@ -839,7 +1048,7 @@ DECLINLINE(RTGCPHYS) buslogicOutgoingMailboxGetGCPhys(PBUSLOGIC pBusLogic) * @returns nothing. * @param pBusLogic. */ -static void buslogicInitializeLocalRam(PBUSLOGIC pBusLogic) +static void buslogicR3InitializeLocalRam(PBUSLOGIC pBusLogic) { /* * These values are mostly from what I think is right @@ -859,7 +1068,7 @@ static void buslogicInitializeLocalRam(PBUSLOGIC pBusLogic) pBusLogic->LocalRam.structured.autoSCSIData.u16DisconnectPermittedMask = ~0; pBusLogic->LocalRam.structured.autoSCSIData.fStrictRoundRobinMode = pBusLogic->fStrictRoundRobinMode; pBusLogic->LocalRam.structured.autoSCSIData.u16UltraPermittedMask = ~0; - /* @todo calculate checksum? */ + /** @todo calculate checksum? */ } /** @@ -867,29 +1076,40 @@ static void buslogicInitializeLocalRam(PBUSLOGIC pBusLogic) * * @returns VBox status code. * @param pBusLogic Pointer to the BusLogic device instance. + * @param fResetIO Flag determining whether ISA I/O should be reset. */ -static int buslogicHwReset(PBUSLOGIC pBusLogic) +static int buslogicR3HwReset(PBUSLOGIC pBusLogic, bool fResetIO) { LogFlowFunc(("pBusLogic=%#p\n", pBusLogic)); - /* Reset registers to default value. */ + /* Reset registers to default values. */ pBusLogic->regStatus = BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY | BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED; - pBusLogic->regInterrupt = 0; pBusLogic->regGeometry = BUSLOGIC_REGISTER_GEOMETRY_EXTENTED_TRANSLATION_ENABLED; pBusLogic->uOperationCode = 0xff; /* No command executing. */ pBusLogic->iParameter = 0; pBusLogic->cbCommandParametersLeft = 0; pBusLogic->fIRQEnabled = true; - pBusLogic->fISAEnabled = true; + pBusLogic->fStrictRoundRobinMode = false; + pBusLogic->fExtendedLunCCBFormat = false; pBusLogic->uMailboxOutgoingPositionCurrent = 0; pBusLogic->uMailboxIncomingPositionCurrent = 0; - buslogicInitializeLocalRam(pBusLogic); + /* Clear any active/pending interrupts. */ + pBusLogic->uPendingIntr = 0; + buslogicClearInterrupt(pBusLogic); + + /* Guest-initiated HBA reset does not affect ISA port I/O. */ + if (fResetIO) + { + buslogicR3RegisterISARange(pBusLogic, pBusLogic->uDefaultISABaseCode); + } + buslogicR3InitializeLocalRam(pBusLogic); vboxscsiInitialize(&pBusLogic->VBoxSCSI); return VINF_SUCCESS; } -#endif + +#endif /* IN_RING3 */ /** * Resets the command state machine for the next command and notifies the guest. @@ -911,9 +1131,7 @@ static void buslogicCommandComplete(PBUSLOGIC pBusLogic, bool fSuppressIrq) { /* Notify that the command is complete. */ pBusLogic->regStatus &= ~BUSLOGIC_REGISTER_STATUS_DATA_IN_REGISTER_READY; - pBusLogic->regInterrupt |= BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE; - - buslogicSetInterrupt(pBusLogic, fSuppressIrq); + buslogicSetInterrupt(pBusLogic, fSuppressIrq, BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE); } pBusLogic->uOperationCode = 0xff; @@ -921,20 +1139,29 @@ static void buslogicCommandComplete(PBUSLOGIC pBusLogic, bool fSuppressIrq) } #if defined(IN_RING3) + /** * Initiates a hard reset which was issued from the guest. * * @returns nothing * @param pBusLogic Pointer to the BusLogic device instance. + * @param fHardReset Flag initiating a hard (vs. soft) reset. */ -static void buslogicIntiateHardReset(PBUSLOGIC pBusLogic) +static void buslogicR3InitiateReset(PBUSLOGIC pBusLogic, bool fHardReset) { - LogFlowFunc(("pBusLogic=%#p\n", pBusLogic)); + LogFlowFunc(("pBusLogic=%#p fHardReset=%d\n", pBusLogic, fHardReset)); - buslogicHwReset(pBusLogic); + buslogicR3HwReset(pBusLogic, false); - /* We set the diagnostic active in the status register. */ - pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE; + if (fHardReset) + { + /* Set the diagnostic active bit in the status register and clear the ready state. */ + pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE; + pBusLogic->regStatus &= ~BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY; + + /* Remember when the guest initiated a reset (after we're done resetting). */ + pBusLogic->u64ResetTime = PDMDevHlpTMTimeVirtGetNano(pBusLogic->CTX_SUFF(pDevIns)); + } } /** @@ -947,9 +1174,9 @@ static void buslogicIntiateHardReset(PBUSLOGIC pBusLogic) * @param uDeviceStatus The target device status to set. * @param uMailboxCompletionCode Completion status code to set in the mailbox. */ -static void buslogicSendIncomingMailbox(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState, - uint8_t uHostAdapterStatus, uint8_t uDeviceStatus, - uint8_t uMailboxCompletionCode) +static void buslogicR3SendIncomingMailbox(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState, + uint8_t uHostAdapterStatus, uint8_t uDeviceStatus, + uint8_t uMailboxCompletionCode) { pTaskState->MailboxGuest.u.in.uHostAdapterStatus = uHostAdapterStatus; pTaskState->MailboxGuest.u.in.uTargetDeviceStatus = uDeviceStatus; @@ -957,42 +1184,65 @@ static void buslogicSendIncomingMailbox(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE int rc = PDMCritSectEnter(&pBusLogic->CritSectIntr, VINF_SUCCESS); AssertRC(rc); - RTGCPHYS GCPhysAddrMailboxIncoming = pBusLogic->GCPhysAddrMailboxIncomingBase + (pBusLogic->uMailboxIncomingPositionCurrent * sizeof(Mailbox)); - RTGCPHYS GCPhysAddrCCB = (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB; - LogFlowFunc(("Completing CCB %RGp\n", GCPhysAddrCCB)); + RTGCPHYS GCPhysAddrMailboxIncoming = pBusLogic->GCPhysAddrMailboxIncomingBase + + ( pBusLogic->uMailboxIncomingPositionCurrent + * (pTaskState->fIs24Bit ? sizeof(Mailbox24) : sizeof(Mailbox32)) ); - /* Update CCB. */ - pTaskState->CommandControlBlockGuest.uHostAdapterStatus = uHostAdapterStatus; - pTaskState->CommandControlBlockGuest.uDeviceStatus = uDeviceStatus; - /* @todo: this is wrong - writing too much! */ - PDMDevHlpPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB, &pTaskState->CommandControlBlockGuest, sizeof(CommandControlBlock)); + if (uMailboxCompletionCode != BUSLOGIC_MAILBOX_INCOMING_COMPLETION_ABORTED_NOT_FOUND) + { + RTGCPHYS GCPhysAddrCCB = pTaskState->MailboxGuest.u32PhysAddrCCB; + LogFlowFunc(("Completing CCB %RGp hstat=%u, dstat=%u, outgoing mailbox at %RGp\n", GCPhysAddrCCB, + uHostAdapterStatus, uDeviceStatus, GCPhysAddrMailboxIncoming)); + + /* Update CCB. */ + pTaskState->CommandControlBlockGuest.c.uHostAdapterStatus = uHostAdapterStatus; + pTaskState->CommandControlBlockGuest.c.uDeviceStatus = uDeviceStatus; + /* Rewrite CCB up to the CDB; perhaps more than necessary. */ + PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB, + &pTaskState->CommandControlBlockGuest, RT_OFFSETOF(CCBC, abCDB)); + } -#ifdef RT_STRICT - Mailbox Tmp; - PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming, &Tmp, sizeof(Mailbox)); - Assert(Tmp.u.in.uCompletionCode == BUSLOGIC_MAILBOX_INCOMING_COMPLETION_FREE); -#endif +# ifdef RT_STRICT + uint8_t uCode; + unsigned uCodeOffs = pTaskState->fIs24Bit ? RT_OFFSETOF(Mailbox24, uCmdState) : RT_OFFSETOF(Mailbox32, u.out.uActionCode); + PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming + uCodeOffs, &uCode, sizeof(uCode)); + Assert(uCode == BUSLOGIC_MAILBOX_INCOMING_COMPLETION_FREE); +# endif /* Update mailbox. */ - PDMDevHlpPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming, &pTaskState->MailboxGuest, sizeof(Mailbox)); + if (pTaskState->fIs24Bit) + { + Mailbox24 Mbx24; + + Mbx24.uCmdState = pTaskState->MailboxGuest.u.in.uCompletionCode; + U32_TO_ADDR(Mbx24.aPhysAddrCCB, pTaskState->MailboxGuest.u32PhysAddrCCB); + Log(("24-bit mailbox: completion code=%u, CCB at %RGp\n", Mbx24.uCmdState, (RTGCPHYS)ADDR_TO_U32(Mbx24.aPhysAddrCCB))); + PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming, &Mbx24, sizeof(Mailbox24)); + } + else + { + Log(("32-bit mailbox: completion code=%u, CCB at %RGp\n", pTaskState->MailboxGuest.u.in.uCompletionCode, (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB)); + PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming, + &pTaskState->MailboxGuest, sizeof(Mailbox32)); + } /* Advance to next mailbox position. */ pBusLogic->uMailboxIncomingPositionCurrent++; if (pBusLogic->uMailboxIncomingPositionCurrent >= pBusLogic->cMailbox) pBusLogic->uMailboxIncomingPositionCurrent = 0; -#ifdef LOG_ENABLED +# ifdef LOG_ENABLED ASMAtomicIncU32(&pBusLogic->cInMailboxesReady); -#endif +# endif - pBusLogic->regInterrupt |= BUSLOGIC_REGISTER_INTERRUPT_INCOMING_MAILBOX_LOADED; - buslogicSetInterrupt(pBusLogic, false); + buslogicSetInterrupt(pBusLogic, false, BUSLOGIC_REGISTER_INTERRUPT_INCOMING_MAILBOX_LOADED); PDMCritSectLeave(&pBusLogic->CritSectIntr); } -#if defined(DEBUG) +# ifdef LOG_ENABLED + /** * Dumps the content of a mailbox for debugging purposes. * @@ -1001,7 +1251,7 @@ static void buslogicSendIncomingMailbox(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE * @param fOutgoing true if dumping the outgoing state. * false if dumping the incoming state. */ -static void buslogicDumpMailboxInfo(PMailbox pMailbox, bool fOutgoing) +static void buslogicR3DumpMailboxInfo(PMailbox32 pMailbox, bool fOutgoing) { Log(("%s: Dump for %s mailbox:\n", __FUNCTION__, fOutgoing ? "outgoing" : "incoming")); Log(("%s: u32PhysAddrCCB=%#x\n", __FUNCTION__, pMailbox->u32PhysAddrCCB)); @@ -1021,31 +1271,72 @@ static void buslogicDumpMailboxInfo(PMailbox pMailbox, bool fOutgoing) * Dumps the content of a command control block for debugging purposes. * * @returns nothing. - * @param pCCB Pointer to the command control block to dump. - */ -static void buslogicDumpCCBInfo(PCommandControlBlock pCCB) -{ - Log(("%s: Dump for Command Control Block:\n", __FUNCTION__)); - Log(("%s: uOpCode=%#x\n", __FUNCTION__, pCCB->uOpcode)); - Log(("%s: uDataDirection=%u\n", __FUNCTION__, pCCB->uDataDirection)); - Log(("%s: fTagQueued=%d\n", __FUNCTION__, pCCB->fTagQueued)); - Log(("%s: uQueueTag=%u\n", __FUNCTION__, pCCB->uQueueTag)); - Log(("%s: cbCDB=%u\n", __FUNCTION__, pCCB->cbCDB)); - Log(("%s: cbSenseData=%u\n", __FUNCTION__, pCCB->cbSenseData)); - Log(("%s: cbData=%u\n", __FUNCTION__, pCCB->cbData)); - Log(("%s: u32PhysAddrData=%#x\n", __FUNCTION__, pCCB->u32PhysAddrData)); - Log(("%s: uHostAdapterStatus=%u\n", __FUNCTION__, pCCB->uHostAdapterStatus)); - Log(("%s: uDeviceStatus=%u\n", __FUNCTION__, pCCB->uDeviceStatus)); - Log(("%s: uTargetId=%u\n", __FUNCTION__, pCCB->uTargetId)); - Log(("%s: uLogicalUnit=%u\n", __FUNCTION__, pCCB->uLogicalUnit)); - Log(("%s: fLegacyTagEnable=%u\n", __FUNCTION__, pCCB->fLegacyTagEnable)); - Log(("%s: uLegacyQueueTag=%u\n", __FUNCTION__, pCCB->uLegacyQueueTag)); - Log(("%s: uCDB[0]=%#x\n", __FUNCTION__, pCCB->aCDB[0])); - for (int i = 1; i < pCCB->cbCDB; i++) - Log(("%s: uCDB[%d]=%u\n", __FUNCTION__, i, pCCB->aCDB[i])); - Log(("%s: u32PhysAddrSenseData=%#x\n", __FUNCTION__, pCCB->u32PhysAddrSenseData)); + * @param pCCB Pointer to the command control block to dump. + * @param fIs24BitCCB Flag to determine CCB format. + */ +static void buslogicR3DumpCCBInfo(PCCBU pCCB, bool fIs24BitCCB) +{ + Log(("%s: Dump for %s Command Control Block:\n", __FUNCTION__, fIs24BitCCB ? "24-bit" : "32-bit")); + Log(("%s: uOpCode=%#x\n", __FUNCTION__, pCCB->c.uOpcode)); + Log(("%s: uDataDirection=%u\n", __FUNCTION__, pCCB->c.uDataDirection)); + Log(("%s: cbCDB=%u\n", __FUNCTION__, pCCB->c.cbCDB)); + Log(("%s: cbSenseData=%u\n", __FUNCTION__, pCCB->c.cbSenseData)); + Log(("%s: uHostAdapterStatus=%u\n", __FUNCTION__, pCCB->c.uHostAdapterStatus)); + Log(("%s: uDeviceStatus=%u\n", __FUNCTION__, pCCB->c.uDeviceStatus)); + if (fIs24BitCCB) + { + Log(("%s: cbData=%u\n", __FUNCTION__, LEN_TO_U32(pCCB->o.acbData))); + Log(("%s: PhysAddrData=%#x\n", __FUNCTION__, ADDR_TO_U32(pCCB->o.aPhysAddrData))); + Log(("%s: uTargetId=%u\n", __FUNCTION__, pCCB->o.uTargetId)); + Log(("%s: uLogicalUnit=%u\n", __FUNCTION__, pCCB->o.uLogicalUnit)); + } + else + { + Log(("%s: cbData=%u\n", __FUNCTION__, pCCB->n.cbData)); + Log(("%s: PhysAddrData=%#x\n", __FUNCTION__, pCCB->n.u32PhysAddrData)); + Log(("%s: uTargetId=%u\n", __FUNCTION__, pCCB->n.uTargetId)); + Log(("%s: uLogicalUnit=%u\n", __FUNCTION__, pCCB->n.uLogicalUnit)); + Log(("%s: fTagQueued=%d\n", __FUNCTION__, pCCB->n.fTagQueued)); + Log(("%s: uQueueTag=%u\n", __FUNCTION__, pCCB->n.uQueueTag)); + Log(("%s: fLegacyTagEnable=%u\n", __FUNCTION__, pCCB->n.fLegacyTagEnable)); + Log(("%s: uLegacyQueueTag=%u\n", __FUNCTION__, pCCB->n.uLegacyQueueTag)); + Log(("%s: PhysAddrSenseData=%#x\n", __FUNCTION__, pCCB->n.u32PhysAddrSenseData)); + } + Log(("%s: uCDB[0]=%#x\n", __FUNCTION__, pCCB->c.abCDB[0])); + for (int i = 1; i < pCCB->c.cbCDB; i++) + Log(("%s: uCDB[%d]=%u\n", __FUNCTION__, i, pCCB->c.abCDB[i])); +} + +# endif /* LOG_ENABLED */ + +/** + * Allocate data buffer. + * + * @param pTaskState Pointer to the task state. + * @param GCSGList Guest physical address of S/G list. + * @param cEntries Number of list entries to read. + * @param pSGEList Pointer to 32-bit S/G list storage. + */ +static void buslogicR3ReadSGEntries(PBUSLOGICTASKSTATE pTaskState, RTGCPHYS GCSGList, uint32_t cEntries, SGE32 *pSGEList) +{ + PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns); + SGE24 aSGE24[32]; + Assert(cEntries <= RT_ELEMENTS(aSGE24)); + + /* Read the S/G entries. Convert 24-bit entries to 32-bit format. */ + if (pTaskState->fIs24Bit) + { + Log2(("Converting %u 24-bit S/G entries to 32-bit\n", cEntries)); + PDMDevHlpPhysRead(pDevIns, GCSGList, &aSGE24, cEntries * sizeof(SGE24)); + for (uint32_t i = 0; i < cEntries; ++i) + { + pSGEList[i].cbSegment = LEN_TO_U32(aSGE24[i].acbSegment); + pSGEList[i].u32PhysAddrSegmentBase = ADDR_TO_U32(aSGE24[i].aPhysAddrSegmentBase); + } + } + else + PDMDevHlpPhysRead(pDevIns, GCSGList, pSGEList, cEntries * sizeof(SGE32)); } -#endif /** * Allocate data buffer. @@ -1053,29 +1344,42 @@ static void buslogicDumpCCBInfo(PCommandControlBlock pCCB) * @returns VBox status code. * @param pTaskState Pointer to the task state. */ -static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState) +static int buslogicR3DataBufferAlloc(PBUSLOGICTASKSTATE pTaskState) { PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns); + uint32_t cbDataCCB; + uint32_t u32PhysAddrCCB; + + /* Extract the data length and physical address from the CCB. */ + if (pTaskState->fIs24Bit) + { + u32PhysAddrCCB = ADDR_TO_U32(pTaskState->CommandControlBlockGuest.o.aPhysAddrData); + cbDataCCB = LEN_TO_U32(pTaskState->CommandControlBlockGuest.o.acbData); + } + else + { + u32PhysAddrCCB = pTaskState->CommandControlBlockGuest.n.u32PhysAddrData; + cbDataCCB = pTaskState->CommandControlBlockGuest.n.cbData; + } - if ( (pTaskState->CommandControlBlockGuest.uDataDirection != BUSLOGIC_CCB_DIRECTION_NO_DATA) - && (pTaskState->CommandControlBlockGuest.cbData > 0)) + if ( (pTaskState->CommandControlBlockGuest.c.uDataDirection != BUSLOGIC_CCB_DIRECTION_NO_DATA) + && cbDataCCB) { + /** @todo Check following assumption and what residual means. */ /* - * @todo: Check following assumption and what residual means. - * * The BusLogic adapter can handle two different data buffer formats. * The first one is that the data pointer entry in the CCB points to * the buffer directly. In second mode the data pointer points to a * scatter gather list which describes the buffer. */ - if ( (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER) - || (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER)) + if ( (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER) + || (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER)) { uint32_t cScatterGatherGCRead; uint32_t iScatterGatherEntry; - ScatterGatherEntry aScatterGatherReadGC[32]; /* Number of scatter gather list entries read from guest memory. */ - uint32_t cScatterGatherGCLeft = pTaskState->CommandControlBlockGuest.cbData / sizeof(ScatterGatherEntry); - RTGCPHYS GCPhysAddrScatterGatherCurrent = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData; + SGE32 aScatterGatherReadGC[32]; /* A buffer for scatter gather list entries read from guest memory. */ + uint32_t cScatterGatherGCLeft = cbDataCCB / pTaskState->cbSGEntry; + RTGCPHYS GCPhysAddrScatterGatherCurrent = u32PhysAddrCCB; size_t cbDataToTransfer = 0; /* Count number of bytes to transfer. */ @@ -1086,9 +1390,7 @@ static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState) : RT_ELEMENTS(aScatterGatherReadGC); cScatterGatherGCLeft -= cScatterGatherGCRead; - /* Read the SG entries. */ - PDMDevHlpPhysRead(pDevIns, GCPhysAddrScatterGatherCurrent, &aScatterGatherReadGC[0], - cScatterGatherGCRead * sizeof(ScatterGatherEntry)); + buslogicR3ReadSGEntries(pTaskState, GCPhysAddrScatterGatherCurrent, cScatterGatherGCRead, aScatterGatherReadGC); for (iScatterGatherEntry = 0; iScatterGatherEntry < cScatterGatherGCRead; iScatterGatherEntry++) { @@ -1105,7 +1407,7 @@ static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState) } /* Set address to the next entries to read. */ - GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * sizeof(ScatterGatherEntry); + GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * pTaskState->cbSGEntry; } while (cScatterGatherGCLeft > 0); Log(("%s: cbDataToTransfer=%d\n", __FUNCTION__, cbDataToTransfer)); @@ -1117,10 +1419,11 @@ static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState) return VERR_NO_MEMORY; /* Copy the data if needed */ - if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_OUT) + if ( (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_OUT) + || (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN)) { - cScatterGatherGCLeft = pTaskState->CommandControlBlockGuest.cbData / sizeof(ScatterGatherEntry); - GCPhysAddrScatterGatherCurrent = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData; + cScatterGatherGCLeft = cbDataCCB / pTaskState->cbSGEntry; + GCPhysAddrScatterGatherCurrent = u32PhysAddrCCB; uint8_t *pbData = (uint8_t *)pTaskState->DataSeg.pvSeg; do @@ -1130,9 +1433,7 @@ static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState) : RT_ELEMENTS(aScatterGatherReadGC); cScatterGatherGCLeft -= cScatterGatherGCRead; - /* Read the SG entries. */ - PDMDevHlpPhysRead(pDevIns, GCPhysAddrScatterGatherCurrent, &aScatterGatherReadGC[0], - cScatterGatherGCRead * sizeof(ScatterGatherEntry)); + buslogicR3ReadSGEntries(pTaskState, GCPhysAddrScatterGatherCurrent, cScatterGatherGCRead, aScatterGatherReadGC); for (iScatterGatherEntry = 0; iScatterGatherEntry < cScatterGatherGCRead; iScatterGatherEntry++) { @@ -1150,27 +1451,27 @@ static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState) } /* Set address to the next entries to read. */ - GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * sizeof(ScatterGatherEntry); + GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * pTaskState->cbSGEntry; } while (cScatterGatherGCLeft > 0); } } - else if ( pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB - || pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH) + else if ( pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB + || pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH) { /* The buffer is not scattered. */ - RTGCPHYS GCPhysAddrDataBase = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData; + RTGCPHYS GCPhysAddrDataBase = u32PhysAddrCCB; AssertMsg(GCPhysAddrDataBase != 0, ("Physical address is 0\n")); - pTaskState->DataSeg.cbSeg = pTaskState->CommandControlBlockGuest.cbData; + pTaskState->DataSeg.cbSeg = cbDataCCB; pTaskState->DataSeg.pvSeg = RTMemAlloc(pTaskState->DataSeg.cbSeg); if (!pTaskState->DataSeg.pvSeg) return VERR_NO_MEMORY; Log(("Non scattered buffer:\n")); - Log(("u32PhysAddrData=%#x\n", pTaskState->CommandControlBlockGuest.u32PhysAddrData)); - Log(("cbData=%u\n", pTaskState->CommandControlBlockGuest.cbData)); + Log(("u32PhysAddrData=%#x\n", u32PhysAddrCCB)); + Log(("cbData=%u\n", cbDataCCB)); Log(("GCPhysAddrDataBase=0x%RGp\n", GCPhysAddrDataBase)); /* Copy the data into the buffer. */ @@ -1187,34 +1488,57 @@ static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState) * @returns nothing. * @param pTaskState Pointer to the task state. */ -static void buslogicDataBufferFree(PBUSLOGICTASKSTATE pTaskState) +static void buslogicR3DataBufferFree(PBUSLOGICTASKSTATE pTaskState) { PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns); + uint32_t cbDataCCB; + uint32_t u32PhysAddrCCB; + + /* Extract the data length and physical address from the CCB. */ + if (pTaskState->fIs24Bit) + { + u32PhysAddrCCB = ADDR_TO_U32(pTaskState->CommandControlBlockGuest.o.aPhysAddrData); + cbDataCCB = LEN_TO_U32(pTaskState->CommandControlBlockGuest.o.acbData); + } + else + { + u32PhysAddrCCB = pTaskState->CommandControlBlockGuest.n.u32PhysAddrData; + cbDataCCB = pTaskState->CommandControlBlockGuest.n.cbData; + } + +#if 1 + /* Hack for NT 10/91: A CCB describes a 2K buffer, but TEST UNIT READY is executed. This command + * returns no data, hence the buffer must be left alone! + */ + if (pTaskState->CommandControlBlockGuest.c.abCDB[0] == 0) + cbDataCCB = 0; +#endif + + LogFlowFunc(("pTaskState=%#p cbDataCCB=%u direction=%u cbSeg=%u\n", pTaskState, cbDataCCB, + pTaskState->CommandControlBlockGuest.c.uDataDirection, pTaskState->DataSeg.cbSeg)); - if ( (pTaskState->CommandControlBlockGuest.cbData > 0) - && ( (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_IN) - || (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN))) + if ( (cbDataCCB > 0) + && ( (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_IN) + || (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN))) { - if ( (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER) - || (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER)) + if ( (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER) + || (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER)) { uint32_t cScatterGatherGCRead; uint32_t iScatterGatherEntry; - ScatterGatherEntry aScatterGatherReadGC[32]; /* Number of scatter gather list entries read from guest memory. */ - uint32_t cScatterGatherGCLeft = pTaskState->CommandControlBlockGuest.cbData / sizeof(ScatterGatherEntry); - RTGCPHYS GCPhysAddrScatterGatherCurrent = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData; + SGE32 aScatterGatherReadGC[32]; /* Number of scatter gather list entries read from guest memory. */ + uint32_t cScatterGatherGCLeft = cbDataCCB / pTaskState->cbSGEntry; + RTGCPHYS GCPhysAddrScatterGatherCurrent = u32PhysAddrCCB; uint8_t *pbData = (uint8_t *)pTaskState->DataSeg.pvSeg; do { - cScatterGatherGCRead = (cScatterGatherGCLeft < RT_ELEMENTS(aScatterGatherReadGC)) - ? cScatterGatherGCLeft - : RT_ELEMENTS(aScatterGatherReadGC); + cScatterGatherGCRead = (cScatterGatherGCLeft < RT_ELEMENTS(aScatterGatherReadGC)) + ? cScatterGatherGCLeft + : RT_ELEMENTS(aScatterGatherReadGC); cScatterGatherGCLeft -= cScatterGatherGCRead; - /* Read the SG entries. */ - PDMDevHlpPhysRead(pDevIns, GCPhysAddrScatterGatherCurrent, &aScatterGatherReadGC[0], - cScatterGatherGCRead * sizeof(ScatterGatherEntry)); + buslogicR3ReadSGEntries(pTaskState, GCPhysAddrScatterGatherCurrent, cScatterGatherGCRead, aScatterGatherReadGC); for (iScatterGatherEntry = 0; iScatterGatherEntry < cScatterGatherGCRead; iScatterGatherEntry++) { @@ -1228,31 +1552,45 @@ static void buslogicDataBufferFree(PBUSLOGICTASKSTATE pTaskState) Log(("%s: GCPhysAddrDataBase=%RGp cbDataToTransfer=%u\n", __FUNCTION__, GCPhysAddrDataBase, cbDataToTransfer)); - PDMDevHlpPhysWrite(pDevIns, GCPhysAddrDataBase, pbData, cbDataToTransfer); + PDMDevHlpPCIPhysWrite(pDevIns, GCPhysAddrDataBase, pbData, cbDataToTransfer); pbData += cbDataToTransfer; } /* Set address to the next entries to read. */ - GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * sizeof(ScatterGatherEntry); + GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * pTaskState->cbSGEntry; } while (cScatterGatherGCLeft > 0); } - else if ( pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB - || pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH) + else if ( pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB + || pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH) { /* The buffer is not scattered. */ - RTGCPHYS GCPhysAddrDataBase = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData; + RTGCPHYS GCPhysAddrDataBase = u32PhysAddrCCB; AssertMsg(GCPhysAddrDataBase != 0, ("Physical address is 0\n")); - Log(("Non scattered buffer:\n")); - Log(("u32PhysAddrData=%#x\n", pTaskState->CommandControlBlockGuest.u32PhysAddrData)); - Log(("cbData=%u\n", pTaskState->CommandControlBlockGuest.cbData)); + Log(("Non-scattered buffer:\n")); + Log(("u32PhysAddrData=%#x\n", u32PhysAddrCCB)); + Log(("cbData=%u\n", cbDataCCB)); Log(("GCPhysAddrDataBase=0x%RGp\n", GCPhysAddrDataBase)); /* Copy the data into the guest memory. */ - PDMDevHlpPhysWrite(pDevIns, GCPhysAddrDataBase, pTaskState->DataSeg.pvSeg, pTaskState->DataSeg.cbSeg); + PDMDevHlpPCIPhysWrite(pDevIns, GCPhysAddrDataBase, pTaskState->DataSeg.pvSeg, pTaskState->DataSeg.cbSeg); } + + } + /* Update residual data length. */ + if ( (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH) + || (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER)) + { + uint32_t cbResidual; + + /** @todo we need to get the actual transfer length from the VSCSI layer?! */ + cbResidual = 0; //LEN_TO_U32(pTaskState->CCBGuest.acbData) - ???; + if (pTaskState->fIs24Bit) + U32_TO_LEN(pTaskState->CommandControlBlockGuest.o.acbData, cbResidual); + else + pTaskState->CommandControlBlockGuest.n.cbData = cbResidual; } RTMemFree(pTaskState->DataSeg.pvSeg); @@ -1260,6 +1598,20 @@ static void buslogicDataBufferFree(PBUSLOGICTASKSTATE pTaskState) pTaskState->DataSeg.cbSeg = 0; } +/** Convert sense buffer length taking into account shortcut values. */ +static uint32_t buslogicR3ConvertSenseBufferLength(uint32_t cbSense) +{ + /* Convert special sense buffer length values. */ + if (cbSense == 0) + cbSense = 14; /* 0 means standard 14-byte buffer. */ + else if (cbSense == 1) + cbSense = 0; /* 1 means no sense data. */ + else if (cbSense < 8) + AssertMsgFailed(("Reserved cbSense value of %d used!\n", cbSense)); + + return cbSense; +} + /** * Free the sense buffer. * @@ -1267,15 +1619,33 @@ static void buslogicDataBufferFree(PBUSLOGICTASKSTATE pTaskState) * @param pTaskState Pointer to the task state. * @param fCopy If sense data should be copied to guest memory. */ -static void buslogicSenseBufferFree(PBUSLOGICTASKSTATE pTaskState, bool fCopy) +static void buslogicR3SenseBufferFree(PBUSLOGICTASKSTATE pTaskState, bool fCopy) { - PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns); - RTGCPHYS GCPhysAddrSenseBuffer = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrSenseData; - uint32_t cbSenseBuffer = pTaskState->CommandControlBlockGuest.cbSenseData; + uint32_t cbSenseBuffer; + + cbSenseBuffer = buslogicR3ConvertSenseBufferLength(pTaskState->CommandControlBlockGuest.c.cbSenseData); + + /* Copy the sense buffer into guest memory if requested. */ + if (fCopy && cbSenseBuffer) + { + PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns); + RTGCPHYS GCPhysAddrSenseBuffer; + + /* With 32-bit CCBs, the (optional) sense buffer physical address is provided separately. + * On the other hand, with 24-bit CCBs, the sense buffer is simply located at the end of + * the CCB, right after the variable-length CDB. + */ + if (pTaskState->fIs24Bit) + { + GCPhysAddrSenseBuffer = pTaskState->MailboxGuest.u32PhysAddrCCB; + GCPhysAddrSenseBuffer += pTaskState->CommandControlBlockGuest.c.cbCDB + RT_OFFSETOF(CCB24, abCDB); + } + else + GCPhysAddrSenseBuffer = pTaskState->CommandControlBlockGuest.n.u32PhysAddrSenseData; - /* Copy into guest memory. */ - if (fCopy) - PDMDevHlpPhysWrite(pDevIns, GCPhysAddrSenseBuffer, pTaskState->pbSenseBuffer, cbSenseBuffer); + Log3(("%s: sense buffer: %.*Rhxs\n", __FUNCTION__, cbSenseBuffer, pTaskState->pbSenseBuffer)); + PDMDevHlpPCIPhysWrite(pDevIns, GCPhysAddrSenseBuffer, pTaskState->pbSenseBuffer, cbSenseBuffer); + } RTMemFree(pTaskState->pbSenseBuffer); pTaskState->pbSenseBuffer = NULL; @@ -1288,17 +1658,24 @@ static void buslogicSenseBufferFree(PBUSLOGICTASKSTATE pTaskState, bool fCopy) * @param pTaskState Pointer to the task state. * @note Current assumption is that the sense buffer is not scattered and does not cross a page boundary. */ -static int buslogicSenseBufferAlloc(PBUSLOGICTASKSTATE pTaskState) +static int buslogicR3SenseBufferAlloc(PBUSLOGICTASKSTATE pTaskState) { PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns); - uint32_t cbSenseBuffer = pTaskState->CommandControlBlockGuest.cbSenseData; + uint32_t cbSenseBuffer; + + pTaskState->pbSenseBuffer = NULL; - pTaskState->pbSenseBuffer = (uint8_t *)RTMemAllocZ(cbSenseBuffer); - if (!pTaskState->pbSenseBuffer) - return VERR_NO_MEMORY; + cbSenseBuffer = buslogicR3ConvertSenseBufferLength(pTaskState->CommandControlBlockGuest.c.cbSenseData); + if (cbSenseBuffer) + { + pTaskState->pbSenseBuffer = (uint8_t *)RTMemAllocZ(cbSenseBuffer); + if (!pTaskState->pbSenseBuffer) + return VERR_NO_MEMORY; + } return VINF_SUCCESS; } + #endif /* IN_RING3 */ /** @@ -1317,6 +1694,10 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic) switch (pBusLogic->uOperationCode) { + case BUSLOGICCOMMAND_TEST_CMDC_INTERRUPT: + /* Valid command, no reply. */ + pBusLogic->cbReplyParametersLeft = 0; + break; case BUSLOGICCOMMAND_INQUIRE_PCI_HOST_ADAPTER_INFORMATION: { PReplyInquirePCIHostAdapterInformation pReply = (PReplyInquirePCIHostAdapterInformation)pBusLogic->aReplyBuffer; @@ -1324,20 +1705,26 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic) /* It seems VMware does not provide valid information here too, lets do the same :) */ pReply->InformationIsValid = 0; - pReply->IsaIOPort = 0xff; /* Make it invalid. */ + pReply->IsaIOPort = pBusLogic->uISABaseCode; + pReply->IRQ = PCIDevGetInterruptLine(&pBusLogic->dev); pBusLogic->cbReplyParametersLeft = sizeof(ReplyInquirePCIHostAdapterInformation); break; } case BUSLOGICCOMMAND_MODIFY_IO_ADDRESS: { + /* Modify the ISA-compatible I/O port base. Note that this technically + * violates the PCI spec, as this address is not reported through PCI. + * However, it is required for compatibility with old drivers. + */ +#ifdef IN_RING3 + Log(("ISA I/O for PCI (code %x)\n", pBusLogic->aCommandBuffer[0])); + buslogicR3RegisterISARange(pBusLogic, pBusLogic->aCommandBuffer[0]); pBusLogic->cbReplyParametersLeft = 0; - if (pBusLogic->aCommandBuffer[0] == 0x06) - { - Log(("Disabling ISA I/O ports.\n")); - pBusLogic->fISAEnabled = false; - } fSuppressIrq = true; break; +#else + AssertMsgFailed(("Must never get here!\n")); +#endif } case BUSLOGICCOMMAND_INQUIRE_BOARD_ID: { @@ -1366,6 +1753,50 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic) pBusLogic->cbReplyParametersLeft = 1; break; } + case BUSLOGICCOMMAND_SET_ADAPTER_OPTIONS: + /* The parameter list length is determined by the first byte of the command buffer. */ + if (pBusLogic->iParameter == 1) + { + /* First pass - set the number of following parameter bytes. */ + pBusLogic->cbCommandParametersLeft = pBusLogic->aCommandBuffer[0]; + Log(("Set HA options: %u bytes follow\n", pBusLogic->cbCommandParametersLeft)); + } + else + { + /* Second pass - process received data. */ + Log(("Set HA options: received %u bytes\n", pBusLogic->aCommandBuffer[0])); + /* We ignore the data - it only concerns the SCSI hardware protocol. */ + } + pBusLogic->cbReplyParametersLeft = 0; + break; + + case BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND: + /* The parameter list length is at least 12 bytes; the 12th byte determines + * the number of additional CDB bytes that will follow. + */ + if (pBusLogic->iParameter == 12) + { + /* First pass - set the number of following CDB bytes. */ + pBusLogic->cbCommandParametersLeft = pBusLogic->aCommandBuffer[11]; + Log(("Execute SCSI cmd: %u more bytes follow\n", pBusLogic->cbCommandParametersLeft)); + } + else + { + PESCMD pCmd; + + /* Second pass - process received data. */ + Log(("Execute SCSI cmd: received %u bytes\n", pBusLogic->aCommandBuffer[0])); + + pCmd = (PESCMD)pBusLogic->aCommandBuffer; + Log(("Addr %08X, cbData %08X, cbCDB=%u\n", pCmd->u32PhysAddrData, pCmd->cbData, pCmd->cbCDB)); + } + // This is currently a dummy - just fails every command. + pBusLogic->cbReplyParametersLeft = 4; + pBusLogic->aReplyBuffer[0] = pBusLogic->aReplyBuffer[1] = 0; + pBusLogic->aReplyBuffer[2] = 0x11; /* HBA status (timeout). */ + pBusLogic->aReplyBuffer[3] = 0; /* Device status. */ + break; + case BUSLOGICCOMMAND_INQUIRE_HOST_ADAPTER_MODEL_NUMBER: { /* The reply length is set by the guest and is found in the first byte of the command buffer. */ @@ -1383,27 +1814,49 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic) } case BUSLOGICCOMMAND_INQUIRE_CONFIGURATION: { + uint8_t uPciIrq = PCIDevGetInterruptLine(&pBusLogic->dev); + pBusLogic->cbReplyParametersLeft = sizeof(ReplyInquireConfiguration); PReplyInquireConfiguration pReply = (PReplyInquireConfiguration)pBusLogic->aReplyBuffer; memset(pReply, 0, sizeof(ReplyInquireConfiguration)); pReply->uHostAdapterId = 7; /* The controller has always 7 as ID. */ - /* - * The rest of this reply only applies for ISA adapters. - * This is a PCI adapter so they are not important and are skipped. + pReply->fDmaChannel6 = 1; /* DMA channel 6 is a good default. */ + /* The PCI IRQ is not necessarily representable in this structure. + * If that is the case, the guest likely won't function correctly, + * therefore we log a warning. */ + switch (uPciIrq) + { + case 9: pReply->fIrqChannel9 = 1; break; + case 10: pReply->fIrqChannel10 = 1; break; + case 11: pReply->fIrqChannel11 = 1; break; + case 12: pReply->fIrqChannel12 = 1; break; + case 14: pReply->fIrqChannel14 = 1; break; + case 15: pReply->fIrqChannel15 = 1; break; + default: + LogRel(("Warning: PCI IRQ %d cannot be represented as ISA!\n", uPciIrq)); + break; + } break; } case BUSLOGICCOMMAND_INQUIRE_EXTENDED_SETUP_INFORMATION: { + /* Some Adaptec AHA-154x drivers (e.g. OS/2) execute this command and expect + * it to fail. If it succeeds, the drivers refuse to load. However, some newer + * Adaptec 154x models supposedly support it too?? + */ + /* The reply length is set by the guest and is found in the first byte of the command buffer. */ pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0]; PReplyInquireExtendedSetupInformation pReply = (PReplyInquireExtendedSetupInformation)pBusLogic->aReplyBuffer; memset(pReply, 0, sizeof(ReplyInquireExtendedSetupInformation)); - //@todo: should this reflect the RAM contents (AutoSCSIRam)? + /** @todo should this reflect the RAM contents (AutoSCSIRam)? */ pReply->uBusType = 'E'; /* EISA style */ pReply->u16ScatterGatherLimit = 8192; + pReply->cMailbox = pBusLogic->cMailbox; + pReply->uMailboxAddressBase = (uint32_t)pBusLogic->GCPhysAddrMailboxOutgoingBase; pReply->fLevelSensitiveInterrupt = true; pReply->fHostWideSCSI = true; pReply->fHostUltraSCSI = true; @@ -1417,6 +1870,16 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic) pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0]; PReplyInquireSetupInformation pReply = (PReplyInquireSetupInformation)pBusLogic->aReplyBuffer; memset(pReply, 0, sizeof(ReplyInquireSetupInformation)); + pReply->fSynchronousInitiationEnabled = true; + pReply->fParityCheckingEnabled = true; + pReply->cMailbox = pBusLogic->cMailbox; + U32_TO_ADDR(pReply->MailboxAddress, pBusLogic->GCPhysAddrMailboxOutgoingBase); + pReply->uSignature = 'B'; + /* The 'D' signature prevents Adaptec's OS/2 drivers from getting too + * friendly with BusLogic hardware and upsetting the HBA state. + */ + pReply->uCharacterD = 'D'; /* BusLogic model. */ + pReply->uHostBusType = 'F'; /* PCI bus. */ break; } case BUSLOGICCOMMAND_FETCH_HOST_ADAPTER_LOCAL_RAM: @@ -1432,18 +1895,39 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic) pBusLogic->iReply = uOffset; break; } + case BUSLOGICCOMMAND_INITIALIZE_MAILBOX: + { + PRequestInitMbx pRequest = (PRequestInitMbx)pBusLogic->aCommandBuffer; + + pBusLogic->fMbxIs24Bit = true; + pBusLogic->cMailbox = pRequest->cMailbox; + pBusLogic->GCPhysAddrMailboxOutgoingBase = (RTGCPHYS)ADDR_TO_U32(pRequest->aMailboxBaseAddr); + /* The area for incoming mailboxes is right after the last entry of outgoing mailboxes. */ + pBusLogic->GCPhysAddrMailboxIncomingBase = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->cMailbox * sizeof(Mailbox24)); + + Log(("GCPhysAddrMailboxOutgoingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxOutgoingBase)); + Log(("GCPhysAddrMailboxIncomingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxIncomingBase)); + Log(("cMailboxes=%u (24-bit mode)\n", pBusLogic->cMailbox)); + LogRel(("Initialized 24-bit mailbox, %d entries at %08x\n", pRequest->cMailbox, ADDR_TO_U32(pRequest->aMailboxBaseAddr))); + + pBusLogic->regStatus &= ~BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED; + pBusLogic->cbReplyParametersLeft = 0; + break; + } case BUSLOGICCOMMAND_INITIALIZE_EXTENDED_MAILBOX: { PRequestInitializeExtendedMailbox pRequest = (PRequestInitializeExtendedMailbox)pBusLogic->aCommandBuffer; + pBusLogic->fMbxIs24Bit = false; pBusLogic->cMailbox = pRequest->cMailbox; pBusLogic->GCPhysAddrMailboxOutgoingBase = (RTGCPHYS)pRequest->uMailboxBaseAddress; /* The area for incoming mailboxes is right after the last entry of outgoing mailboxes. */ - pBusLogic->GCPhysAddrMailboxIncomingBase = (RTGCPHYS)pRequest->uMailboxBaseAddress + (pBusLogic->cMailbox * sizeof(Mailbox)); + pBusLogic->GCPhysAddrMailboxIncomingBase = (RTGCPHYS)pRequest->uMailboxBaseAddress + (pBusLogic->cMailbox * sizeof(Mailbox32)); Log(("GCPhysAddrMailboxOutgoingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxOutgoingBase)); Log(("GCPhysAddrMailboxIncomingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxIncomingBase)); - Log(("cMailboxes=%u\n", pBusLogic->cMailbox)); + Log(("cMailboxes=%u (32-bit mode)\n", pBusLogic->cMailbox)); + LogRel(("Initialized 32-bit mailbox, %d entries at %08x\n", pRequest->cMailbox, pRequest->uMailboxBaseAddress)); pBusLogic->regStatus &= ~BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED; pBusLogic->cbReplyParametersLeft = 0; @@ -1473,6 +1957,29 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic) pBusLogic->cbReplyParametersLeft = 0; break; } + case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_0_TO_7: + /* This is supposed to send TEST UNIT READY to each target/LUN. + * We cheat and skip that, since we already know what's attached + */ + memset(pBusLogic->aReplyBuffer, 0, 8); + for (int i = 0; i < 8; ++i) + { + if (pBusLogic->aDeviceStates[i].fPresent) + pBusLogic->aReplyBuffer[i] = 1; + } + pBusLogic->aReplyBuffer[7] = 0; /* HA hardcoded at ID 7. */ + pBusLogic->cbReplyParametersLeft = 8; + break; + case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_8_TO_15: + /* See note about cheating above. */ + memset(pBusLogic->aReplyBuffer, 0, 8); + for (int i = 0; i < 8; ++i) + { + if (pBusLogic->aDeviceStates[i + 8].fPresent) + pBusLogic->aReplyBuffer[i] = 1; + } + pBusLogic->cbReplyParametersLeft = 8; + break; case BUSLOGICCOMMAND_INQUIRE_TARGET_DEVICES: { /* Each bit which is set in the 16bit wide variable means a present device. */ @@ -1493,7 +2000,7 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic) pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0]; for (uint8_t i = 0; i < pBusLogic->cbReplyParametersLeft; i++) - pBusLogic->aReplyBuffer[i] = 0; /* @todo Figure if we need something other here. It's not needed for the linux driver */ + pBusLogic->aReplyBuffer[i] = 0; /** @todo Figure if we need something other here. It's not needed for the linux driver */ break; } @@ -1527,6 +2034,45 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic) Log(("Bus-off time: %d\n", pBusLogic->aCommandBuffer[0])); break; } + case BUSLOGICCOMMAND_SET_BUS_TRANSFER_RATE: + { + pBusLogic->cbReplyParametersLeft = 0; + pBusLogic->LocalRam.structured.autoSCSIData.uDMATransferRate = pBusLogic->aCommandBuffer[0]; + Log(("Bus transfer rate: %02X\n", pBusLogic->aCommandBuffer[0])); + break; + } + case BUSLOGICCOMMAND_WRITE_BUSMASTER_CHIP_FIFO: + { + RTGCPHYS GCPhysFifoBuf; + Addr24 addr; + + pBusLogic->cbReplyParametersLeft = 0; + addr.hi = pBusLogic->aCommandBuffer[0]; + addr.mid = pBusLogic->aCommandBuffer[1]; + addr.lo = pBusLogic->aCommandBuffer[2]; + GCPhysFifoBuf = (RTGCPHYS)ADDR_TO_U32(addr); + Log(("Write busmaster FIFO at: %04X\n", ADDR_TO_U32(addr))); + PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysFifoBuf, + &pBusLogic->LocalRam.u8View[64], 64); + break; + } + case BUSLOGICCOMMAND_READ_BUSMASTER_CHIP_FIFO: + { + RTGCPHYS GCPhysFifoBuf; + Addr24 addr; + + pBusLogic->cbReplyParametersLeft = 0; + addr.hi = pBusLogic->aCommandBuffer[0]; + addr.mid = pBusLogic->aCommandBuffer[1]; + addr.lo = pBusLogic->aCommandBuffer[2]; + GCPhysFifoBuf = (RTGCPHYS)ADDR_TO_U32(addr); + Log(("Read busmaster FIFO at: %04X\n", ADDR_TO_U32(addr))); + PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysFifoBuf, + &pBusLogic->LocalRam.u8View[64], 64); + break; + } + default: + AssertMsgFailed(("Invalid command %#x\n", pBusLogic->uOperationCode)); case BUSLOGICCOMMAND_EXT_BIOS_INFO: case BUSLOGICCOMMAND_UNLOCK_MAILBOX: /* Commands valid for Adaptec 154xC which we don't handle since @@ -1537,8 +2083,7 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic) pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_COMMAND_INVALID; break; case BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND: /* Should be handled already. */ - default: - AssertMsgFailed(("Invalid command %#x\n", pBusLogic->uOperationCode)); + AssertMsgFailed(("Invalid mailbox execute state!\n")); } Log(("uOperationCode=%#x, cbReplyParametersLeft=%d\n", pBusLogic->uOperationCode, pBusLogic->cbReplyParametersLeft)); @@ -1546,7 +2091,7 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic) /* Set the data in ready bit in the status register in case the command has a reply. */ if (pBusLogic->cbReplyParametersLeft) pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_DATA_IN_REGISTER_READY; - else + else if (!pBusLogic->cbCommandParametersLeft) buslogicCommandComplete(pBusLogic, fSuppressIrq); return rc; @@ -1569,14 +2114,27 @@ static int buslogicRegisterRead(PBUSLOGIC pBusLogic, unsigned iRegister, uint32_ case BUSLOGIC_REGISTER_STATUS: { *pu32 = pBusLogic->regStatus; - /* - * If the diagnostic active bit is set we are in a hard reset initiated from the guest. - * The guest reads the status register and waits that the host adapter ready bit is set. + + /* If the diagnostic active bit is set, we are in a guest-initiated + * hard reset. If the guest reads the status register and waits for + * the host adapter ready bit to be set, we terminate the reset right + * away. However, guests may also expect the reset condition to clear + * automatically after a period of time, in which case we can't show + * the DIAG bit at all. */ if (pBusLogic->regStatus & BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE) { + uint64_t u64AccessTime = PDMDevHlpTMTimeVirtGetNano(pBusLogic->CTX_SUFF(pDevIns)); + pBusLogic->regStatus &= ~BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE; pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY; + + if (u64AccessTime - pBusLogic->u64ResetTime > BUSLOGIC_RESET_DURATION_NS) + { + /* If reset already expired, let the guest see that right away. */ + *pu32 = pBusLogic->regStatus; + pBusLogic->u64ResetTime = 0; + } } break; } @@ -1587,18 +2145,24 @@ static int buslogicRegisterRead(PBUSLOGIC pBusLogic, unsigned iRegister, uint32_ else *pu32 = pBusLogic->aReplyBuffer[pBusLogic->iReply]; - pBusLogic->iReply++; - pBusLogic->cbReplyParametersLeft--; - - LogFlowFunc(("cbReplyParametersLeft=%u\n", pBusLogic->cbReplyParametersLeft)); - if (!pBusLogic->cbReplyParametersLeft) + /* Careful about underflow - guest can read data register even if + * no data is available. + */ + if (pBusLogic->cbReplyParametersLeft) { - /* - * Reply finished, set command complete bit, unset data in ready bit and - * interrupt the guest if enabled. - */ - buslogicCommandComplete(pBusLogic, false); + pBusLogic->iReply++; + pBusLogic->cbReplyParametersLeft--; + if (!pBusLogic->cbReplyParametersLeft) + { + /* + * Reply finished, set command complete bit, unset data-in ready bit and + * interrupt the guest if enabled. + */ + buslogicCommandComplete(pBusLogic, false); + } } + LogFlowFunc(("data=%02x, iReply=%d, cbReplyParametersLeft=%u\n", *pu32, + pBusLogic->iReply, pBusLogic->cbReplyParametersLeft)); break; } case BUSLOGIC_REGISTER_INTERRUPT: @@ -1637,13 +2201,26 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_ { case BUSLOGIC_REGISTER_CONTROL: { + if ((uVal & BUSLOGIC_REGISTER_CONTROL_HARD_RESET) || (uVal & BUSLOGIC_REGISTER_CONTROL_SOFT_RESET)) + { +#ifdef IN_RING3 + bool fHardReset = !!(uVal & BUSLOGIC_REGISTER_CONTROL_HARD_RESET); + + LogRel(("BusLogic: %s reset\n", fHardReset ? "hard" : "soft")); + buslogicR3InitiateReset(pBusLogic, fHardReset); +#else + rc = VINF_IOM_R3_IOPORT_WRITE; +#endif + break; + } + rc = PDMCritSectEnter(&pBusLogic->CritSectIntr, VINF_IOM_R3_IOPORT_WRITE); if (rc != VINF_SUCCESS) return rc; #ifdef LOG_ENABLED uint32_t cMailboxesReady = ASMAtomicXchgU32(&pBusLogic->cInMailboxesReady, 0); - Log(("%u incoming mailboxes are ready when this interrupt was cleared\n", cMailboxesReady)); + Log(("%u incoming mailboxes were ready when this interrupt was cleared\n", cMailboxesReady)); #endif if (uVal & BUSLOGIC_REGISTER_CONTROL_INTERRUPT_RESET) @@ -1651,15 +2228,6 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_ PDMCritSectLeave(&pBusLogic->CritSectIntr); - if ((uVal & BUSLOGIC_REGISTER_CONTROL_HARD_RESET) || (uVal & BUSLOGIC_REGISTER_CONTROL_SOFT_RESET)) - { -#ifdef IN_RING3 - buslogicIntiateHardReset(pBusLogic); -#else - rc = VINF_IOM_R3_IOPORT_WRITE; -#endif - } - break; } case BUSLOGIC_REGISTER_COMMAND: @@ -1667,13 +2235,16 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_ /* Fast path for mailbox execution command. */ if ((uVal == BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND) && (pBusLogic->uOperationCode == 0xff)) { - ASMAtomicIncU32(&pBusLogic->cMailboxesReady); - if (!ASMAtomicXchgBool(&pBusLogic->fNotificationSend, true)) - { - /* Send new notification to the queue. */ - PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pBusLogic->CTX_SUFF(pNotifierQueue)); - AssertMsg(pItem, ("Allocating item for queue failed\n")); - PDMQueueInsert(pBusLogic->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem); + /* If there are no mailboxes configured, don't even try to do anything. */ + if (pBusLogic->cMailbox) { + ASMAtomicIncU32(&pBusLogic->cMailboxesReady); + if (!ASMAtomicXchgBool(&pBusLogic->fNotificationSend, true)) + { + /* Send new notification to the queue. */ + PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pBusLogic->CTX_SUFF(pNotifierQueue)); + AssertMsg(pItem, ("Allocating item for queue failed\n")); + PDMQueueInsert(pBusLogic->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem); + } } return rc; @@ -1694,11 +2265,14 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_ /* Get the number of bytes for parameters from the command code. */ switch (pBusLogic->uOperationCode) { + case BUSLOGICCOMMAND_TEST_CMDC_INTERRUPT: case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_LETTER: case BUSLOGICCOMMAND_INQUIRE_BOARD_ID: case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_3RD_LETTER: case BUSLOGICCOMMAND_INQUIRE_PCI_HOST_ADAPTER_INFORMATION: case BUSLOGICCOMMAND_INQUIRE_CONFIGURATION: + case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_0_TO_7: + case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_8_TO_15: case BUSLOGICCOMMAND_INQUIRE_TARGET_DEVICES: pBusLogic->cbCommandParametersLeft = 0; break; @@ -1713,14 +2287,30 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_ case BUSLOGICCOMMAND_ECHO_COMMAND_DATA: case BUSLOGICCOMMAND_SET_PREEMPT_TIME_ON_BUS: case BUSLOGICCOMMAND_SET_TIME_OFF_BUS: + case BUSLOGICCOMMAND_SET_BUS_TRANSFER_RATE: pBusLogic->cbCommandParametersLeft = 1; break; case BUSLOGICCOMMAND_FETCH_HOST_ADAPTER_LOCAL_RAM: pBusLogic->cbCommandParametersLeft = 2; break; + case BUSLOGICCOMMAND_READ_BUSMASTER_CHIP_FIFO: + case BUSLOGICCOMMAND_WRITE_BUSMASTER_CHIP_FIFO: + pBusLogic->cbCommandParametersLeft = 3; + break; + case BUSLOGICCOMMAND_INITIALIZE_MAILBOX: + pBusLogic->cbCommandParametersLeft = sizeof(RequestInitMbx); + break; case BUSLOGICCOMMAND_INITIALIZE_EXTENDED_MAILBOX: pBusLogic->cbCommandParametersLeft = sizeof(RequestInitializeExtendedMailbox); break; + case BUSLOGICCOMMAND_SET_ADAPTER_OPTIONS: + /* There must be at least one byte following this command. */ + pBusLogic->cbCommandParametersLeft = 1; + break; + case BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND: + /* 12 bytes + variable-length CDB. */ + pBusLogic->cbCommandParametersLeft = 12; + break; case BUSLOGICCOMMAND_EXT_BIOS_INFO: case BUSLOGICCOMMAND_UNLOCK_MAILBOX: /* Invalid commands. */ @@ -1733,6 +2323,14 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_ } else { +#ifndef IN_RING3 + /* This command must be executed in R3 as it rehooks the ISA I/O port. */ + if (pBusLogic->uOperationCode == BUSLOGICCOMMAND_MODIFY_IO_ADDRESS) + { + rc = VINF_IOM_R3_IOPORT_WRITE; + break; + } +#endif /* * The real adapter would set the Command register busy bit in the status register. * The guest has to wait until it is unset. @@ -1783,8 +2381,7 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_ * @param pv Where to store the result. * @param cb Number of bytes read. */ -PDMBOTHCBDECL(int) buslogicMMIORead(PPDMDEVINS pDevIns, void *pvUser, - RTGCPHYS GCPhysAddr, void *pv, unsigned cb) +PDMBOTHCBDECL(int) buslogicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb) { /* the linux driver does not make use of the MMIO area. */ AssertMsgFailed(("MMIO Read\n")); @@ -1802,8 +2399,7 @@ PDMBOTHCBDECL(int) buslogicMMIORead(PPDMDEVINS pDevIns, void *pvUser, * @param pv Where to fetch the result. * @param cb Number of bytes to write. */ -PDMBOTHCBDECL(int) buslogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, - RTGCPHYS GCPhysAddr, void const *pv, unsigned cb) +PDMBOTHCBDECL(int) buslogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb) { /* the linux driver does not make use of the MMIO area. */ AssertMsgFailed(("MMIO Write\n")); @@ -1821,11 +2417,10 @@ PDMBOTHCBDECL(int) buslogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, * @param pu32 Where to store the result. * @param cb Number of bytes read. */ -PDMBOTHCBDECL(int) buslogicIOPortRead (PPDMDEVINS pDevIns, void *pvUser, - RTIOPORT Port, uint32_t *pu32, unsigned cb) +PDMBOTHCBDECL(int) buslogicIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb) { - PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);; - unsigned iRegister = Port - pBusLogic->IOPortBase; + PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC); + unsigned iRegister = Port % 4; Assert(cb == 1); @@ -1843,12 +2438,11 @@ PDMBOTHCBDECL(int) buslogicIOPortRead (PPDMDEVINS pDevIns, void *pvUser, * @param u32 The value to output. * @param cb The value size in bytes. */ -PDMBOTHCBDECL(int) buslogicIOPortWrite (PPDMDEVINS pDevIns, void *pvUser, - RTIOPORT Port, uint32_t u32, unsigned cb) +PDMBOTHCBDECL(int) buslogicIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb) { PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC); int rc = VINF_SUCCESS; - unsigned iRegister = Port - pBusLogic->IOPortBase; + unsigned iRegister = Port % 4; uint8_t uVal = (uint8_t)u32; Assert(cb == 1); @@ -1862,90 +2456,8 @@ PDMBOTHCBDECL(int) buslogicIOPortWrite (PPDMDEVINS pDevIns, void *pvUser, } #ifdef IN_RING3 -/** - * Port I/O Handler for IN operations - legacy port. - * - * @returns VBox status code. - * - * @param pDevIns The device instance. - * @param pvUser User argument. - * @param uPort Port number used for the IN operation. - * @param pu32 Where to store the result. - * @param cb Number of bytes read. - */ -static int buslogicIsaIOPortRead (PPDMDEVINS pDevIns, void *pvUser, - RTIOPORT Port, uint32_t *pu32, unsigned cb) -{ - int rc; - PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC); - - Assert(cb == 1); - - if (!pBusLogic->fISAEnabled) - return VINF_SUCCESS; - - rc = vboxscsiReadRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT), pu32); - - //Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n", - // __FUNCTION__, pu32, 1, pu32, (Port - BUSLOGIC_BIOS_IO_PORT), rc)); - - return rc; -} - -static void buslogicWarningDiskFull(PPDMDEVINS pDevIns) -{ - int rc; - LogRel(("BusLogic#%d: Host disk full\n", pDevIns->iInstance)); - rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_DISKFULL", - N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space")); - AssertRC(rc); -} - -static void buslogicWarningFileTooBig(PPDMDEVINS pDevIns) -{ - int rc; - LogRel(("BusLogic#%d: File too big\n", pDevIns->iInstance)); - rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_FILETOOBIG", - N_("Host system reported that the file size limit of the host file system has been exceeded. VM execution is suspended. You need to move your virtual hard disk to a filesystem which allows bigger files")); - AssertRC(rc); -} - -static void buslogicWarningISCSI(PPDMDEVINS pDevIns) -{ - int rc; - LogRel(("BusLogic#%d: iSCSI target unavailable\n", pDevIns->iInstance)); - rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_ISCSIDOWN", - N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again")); - AssertRC(rc); -} - -static void buslogicWarningUnknown(PPDMDEVINS pDevIns, int rc) -{ - int rc2; - LogRel(("BusLogic#%d: Unknown but recoverable error has occurred (rc=%Rrc)\n", pDevIns->iInstance, rc)); - rc2 = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_UNKNOWN", - N_("An unknown but recoverable I/O error has occurred (rc=%Rrc). VM execution is suspended. You can resume when the error is fixed"), rc); - AssertRC(rc2); -} - -static void buslogicRedoSetWarning(PBUSLOGIC pThis, int rc) -{ - if (rc == VERR_DISK_FULL) - buslogicWarningDiskFull(pThis->CTX_SUFF(pDevIns)); - else if (rc == VERR_FILE_TOO_BIG) - buslogicWarningFileTooBig(pThis->CTX_SUFF(pDevIns)); - else if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED) - { - /* iSCSI connection abort (first error) or failure to reestablish - * connection (second error). Pause VM. On resume we'll retry. */ - buslogicWarningISCSI(pThis->CTX_SUFF(pDevIns)); - } - else - buslogicWarningUnknown(pThis->CTX_SUFF(pDevIns), rc); -} - -static int buslogicPrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic) +static int buslogicR3PrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic) { int rc; PBUSLOGICTASKSTATE pTaskState; @@ -1975,9 +2487,9 @@ static int buslogicPrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic) ScsiInquiryData.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN; ScsiInquiryData.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED; - memcpy(pBusLogic->VBoxSCSI.pBuf, &ScsiInquiryData, 5); + memcpy(pBusLogic->VBoxSCSI.pbBuf, &ScsiInquiryData, 5); - rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest); + rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest, SCSI_STATUS_OK); AssertMsgRCReturn(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc), rc); RTMemCacheFree(pBusLogic->hTaskCache, pTaskState); @@ -1996,8 +2508,35 @@ static int buslogicPrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic) return rc; } + +/** + * Port I/O Handler for IN operations - BIOS port. + * + * @returns VBox status code. + * + * @param pDevIns The device instance. + * @param pvUser User argument. + * @param uPort Port number used for the IN operation. + * @param pu32 Where to store the result. + * @param cb Number of bytes read. + */ +static DECLCALLBACK(int) buslogicR3BiosIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb) +{ + int rc; + PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC); + + Assert(cb == 1); + + rc = vboxscsiReadRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT), pu32); + + //Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n", + // __FUNCTION__, pu32, 1, pu32, (Port - BUSLOGIC_BIOS_IO_PORT), rc)); + + return rc; +} + /** - * Port I/O Handler for OUT operations - legacy port. + * Port I/O Handler for OUT operations - BIOS port. * * @returns VBox status code. * @@ -2007,8 +2546,7 @@ static int buslogicPrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic) * @param u32 The value to output. * @param cb The value size in bytes. */ -static int buslogicIsaIOPortWrite (PPDMDEVINS pDevIns, void *pvUser, - RTIOPORT Port, uint32_t u32, unsigned cb) +static DECLCALLBACK(int) buslogicR3BiosIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb) { int rc; PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC); @@ -2018,13 +2556,10 @@ static int buslogicIsaIOPortWrite (PPDMDEVINS pDevIns, void *pvUser, Assert(cb == 1); - if (!pBusLogic->fISAEnabled) - return VINF_SUCCESS; - rc = vboxscsiWriteRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT), (uint8_t)u32); if (rc == VERR_MORE_DATA) { - rc = buslogicPrepareBIOSSCSIRequest(pBusLogic); + rc = buslogicR3PrepareBIOSSCSIRequest(pBusLogic); AssertRC(rc); } else if (RT_FAILURE(rc)) @@ -2037,7 +2572,8 @@ static int buslogicIsaIOPortWrite (PPDMDEVINS pDevIns, void *pvUser, * Port I/O Handler for primary port range OUT string operations. * @see FNIOMIOPORTOUTSTRING for details. */ -static DECLCALLBACK(int) buslogicIsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb) +static DECLCALLBACK(int) buslogicR3BiosIoPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, + PRTGCUINTREG pcTransfer, unsigned cb) { PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC); int rc; @@ -2049,7 +2585,7 @@ static DECLCALLBACK(int) buslogicIsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvU pGCPtrSrc, pcTransfer, cb); if (rc == VERR_MORE_DATA) { - rc = buslogicPrepareBIOSSCSIRequest(pBusLogic); + rc = buslogicR3PrepareBIOSSCSIRequest(pBusLogic); AssertRC(rc); } else if (RT_FAILURE(rc)) @@ -2062,7 +2598,8 @@ static DECLCALLBACK(int) buslogicIsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvU * Port I/O Handler for primary port range IN string operations. * @see FNIOMIOPORTINSTRING for details. */ -static DECLCALLBACK(int) buslogicIsaIOPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb) +static DECLCALLBACK(int) buslogicR3BiosIoPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, + PRTGCUINTREG pcTransfer, unsigned cb) { PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC); @@ -2073,9 +2610,120 @@ static DECLCALLBACK(int) buslogicIsaIOPortReadStr(PPDMDEVINS pDevIns, void *pvUs pGCPtrDst, pcTransfer, cb); } -static DECLCALLBACK(int) buslogicMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, - RTGCPHYS GCPhysAddress, uint32_t cb, - PCIADDRESSSPACE enmType) +/** + * Update the ISA I/O range. + * + * @returns nothing. + * @param pBusLogic Pointer to the BusLogic device instance. + * @param uBaseCode Encoded ISA I/O base; only low 3 bits are used. + */ +static int buslogicR3RegisterISARange(PBUSLOGIC pBusLogic, uint8_t uBaseCode) +{ + uint8_t uCode = uBaseCode & MAX_ISA_BASE; + uint16_t uNewBase = g_aISABases[uCode]; + int rc = VINF_SUCCESS; + + LogFlowFunc(("ISA I/O code %02X, new base %X\n", uBaseCode, uNewBase)); + + /* Check if the same port range is already registered. */ + if (uNewBase != pBusLogic->IOISABase) + { + /* Unregister the old range, if any. */ + if (pBusLogic->IOISABase) + rc = PDMDevHlpIOPortDeregister(pBusLogic->CTX_SUFF(pDevIns), pBusLogic->IOISABase, 4); + + if (RT_SUCCESS(rc)) + { + pBusLogic->IOISABase = 0; /* First mark as unregistered. */ + pBusLogic->uISABaseCode = ISA_BASE_DISABLED; + + if (uNewBase) + { + /* Register the new range if requested. */ + rc = PDMDevHlpIOPortRegister(pBusLogic->CTX_SUFF(pDevIns), uNewBase, 4, NULL, + buslogicIOPortWrite, buslogicIOPortRead, + NULL, NULL, + "BusLogic ISA"); + if (RT_SUCCESS(rc)) + { + pBusLogic->IOISABase = uNewBase; + pBusLogic->uISABaseCode = uCode; + } + } + } + if (RT_SUCCESS(rc)) + { + if (uNewBase) + { + Log(("ISA I/O base: %x\n", uNewBase)); + LogRel(("BusLogic: ISA I/O base: %x\n", uNewBase)); + } + else + { + Log(("Disabling ISA I/O ports.\n")); + LogRel(("BusLogic: ISA I/O disabled\n")); + } + } + + } + return rc; +} + +static void buslogicR3WarningDiskFull(PPDMDEVINS pDevIns) +{ + int rc; + LogRel(("BusLogic#%d: Host disk full\n", pDevIns->iInstance)); + rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_DISKFULL", + N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space")); + AssertRC(rc); +} + +static void buslogicR3WarningFileTooBig(PPDMDEVINS pDevIns) +{ + int rc; + LogRel(("BusLogic#%d: File too big\n", pDevIns->iInstance)); + rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_FILETOOBIG", + N_("Host system reported that the file size limit of the host file system has been exceeded. VM execution is suspended. You need to move your virtual hard disk to a filesystem which allows bigger files")); + AssertRC(rc); +} + +static void buslogicR3WarningISCSI(PPDMDEVINS pDevIns) +{ + int rc; + LogRel(("BusLogic#%d: iSCSI target unavailable\n", pDevIns->iInstance)); + rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_ISCSIDOWN", + N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again")); + AssertRC(rc); +} + +static void buslogicR3WarningUnknown(PPDMDEVINS pDevIns, int rc) +{ + int rc2; + LogRel(("BusLogic#%d: Unknown but recoverable error has occurred (rc=%Rrc)\n", pDevIns->iInstance, rc)); + rc2 = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_UNKNOWN", + N_("An unknown but recoverable I/O error has occurred (rc=%Rrc). VM execution is suspended. You can resume when the error is fixed"), rc); + AssertRC(rc2); +} + +static void buslogicR3RedoSetWarning(PBUSLOGIC pThis, int rc) +{ + if (rc == VERR_DISK_FULL) + buslogicR3WarningDiskFull(pThis->CTX_SUFF(pDevIns)); + else if (rc == VERR_FILE_TOO_BIG) + buslogicR3WarningFileTooBig(pThis->CTX_SUFF(pDevIns)); + else if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED) + { + /* iSCSI connection abort (first error) or failure to reestablish + * connection (second error). Pause VM. On resume we'll retry. */ + buslogicR3WarningISCSI(pThis->CTX_SUFF(pDevIns)); + } + else + buslogicR3WarningUnknown(pThis->CTX_SUFF(pDevIns), rc); +} + + +static DECLCALLBACK(int) buslogicR3MmioMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, + RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType) { PPDMDEVINS pDevIns = pPciDev->pDevIns; PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC); @@ -2090,7 +2738,7 @@ static DECLCALLBACK(int) buslogicMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iR /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */ rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/, IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, - buslogicMMIOWrite, buslogicMMIORead, "BusLogic"); + buslogicMMIOWrite, buslogicMMIORead, "BusLogic MMIO"); if (RT_FAILURE(rc)) return rc; @@ -2115,14 +2763,14 @@ static DECLCALLBACK(int) buslogicMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iR else if (enmType == PCI_ADDRESS_SPACE_IO) { rc = PDMDevHlpIOPortRegister(pDevIns, (RTIOPORT)GCPhysAddress, 32, - NULL, buslogicIOPortWrite, buslogicIOPortRead, NULL, NULL, "BusLogic"); + NULL, buslogicIOPortWrite, buslogicIOPortRead, NULL, NULL, "BusLogic PCI"); if (RT_FAILURE(rc)) return rc; if (pThis->fR0Enabled) { rc = PDMDevHlpIOPortRegisterR0(pDevIns, (RTIOPORT)GCPhysAddress, 32, - 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic"); + 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic PCI"); if (RT_FAILURE(rc)) return rc; } @@ -2130,7 +2778,7 @@ static DECLCALLBACK(int) buslogicMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iR if (pThis->fGCEnabled) { rc = PDMDevHlpIOPortRegisterRC(pDevIns, (RTIOPORT)GCPhysAddress, 32, - 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic"); + 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic PCI"); if (RT_FAILURE(rc)) return rc; } @@ -2143,8 +2791,8 @@ static DECLCALLBACK(int) buslogicMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iR return rc; } -static DECLCALLBACK(int) buslogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest, - int rcCompletion, bool fRedo, int rcReq) +static DECLCALLBACK(int) buslogicR3DeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest, + int rcCompletion, bool fRedo, int rcReq) { int rc; PBUSLOGICTASKSTATE pTaskState = (PBUSLOGICTASKSTATE)pSCSIRequest->pvUser; @@ -2159,10 +2807,10 @@ static DECLCALLBACK(int) buslogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInter { if (!pTaskState->fBIOS) { - buslogicDataBufferFree(pTaskState); + buslogicR3DataBufferFree(pTaskState); if (pTaskState->pbSenseBuffer) - buslogicSenseBufferFree(pTaskState, false /* fCopy */); + buslogicR3SenseBufferFree(pTaskState, false /* fCopy */); } /* Add to the list. */ @@ -2173,37 +2821,40 @@ static DECLCALLBACK(int) buslogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInter /* Suspend the VM if not done already. */ if (!ASMAtomicXchgBool(&pBusLogic->fRedo, true)) - buslogicRedoSetWarning(pBusLogic, rcReq); + buslogicR3RedoSetWarning(pBusLogic, rcReq); } else { if (pTaskState->fBIOS) { - rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, pSCSIRequest); + rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, pSCSIRequest, rcCompletion); AssertMsgRC(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc)); } else { - buslogicDataBufferFree(pTaskState); + buslogicR3DataBufferFree(pTaskState); if (pTaskState->pbSenseBuffer) - buslogicSenseBufferFree(pTaskState, (rcCompletion != SCSI_STATUS_OK)); + buslogicR3SenseBufferFree(pTaskState, (rcCompletion != SCSI_STATUS_OK)); if (rcCompletion == SCSI_STATUS_OK) - buslogicSendIncomingMailbox(pBusLogic, pTaskState, + buslogicR3SendIncomingMailbox(pBusLogic, pTaskState, BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_CMD_COMPLETED, BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD, BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITHOUT_ERROR); else if (rcCompletion == SCSI_STATUS_CHECK_CONDITION) - buslogicSendIncomingMailbox(pBusLogic, pTaskState, + buslogicR3SendIncomingMailbox(pBusLogic, pTaskState, BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_CMD_COMPLETED, BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_CHECK_CONDITION, BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITH_ERROR); else AssertMsgFailed(("invalid completion status %d\n", rcCompletion)); } +#ifdef LOG_ENABLED + buslogicR3DumpCCBInfo(&pTaskState->CommandControlBlockGuest, pTaskState->fIs24Bit); +#endif - /* Add task to the cache. */ + /* Remove task from the cache. */ RTMemCacheFree(pBusLogic->hTaskCache, pTaskState); } @@ -2213,8 +2864,8 @@ static DECLCALLBACK(int) buslogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInter return VINF_SUCCESS; } -static DECLCALLBACK(int) buslogicQueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController, - uint32_t *piInstance, uint32_t *piLUN) +static DECLCALLBACK(int) buslogicR3QueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController, + uint32_t *piInstance, uint32_t *piLUN) { PBUSLOGICDEVICE pBusLogicDevice = PDMISCSIPORT_2_PBUSLOGICDEVICE(pInterface); PPDMDEVINS pDevIns = pBusLogicDevice->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns); @@ -2230,41 +2881,42 @@ static DECLCALLBACK(int) buslogicQueryDeviceLocation(PPDMISCSIPORT pInterface, c return VINF_SUCCESS; } -static int buslogicDeviceSCSIRequestSetup(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState) +static int buslogicR3DeviceSCSIRequestSetup(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState) { int rc = VINF_SUCCESS; + uint8_t uTargetIdCCB; + PBUSLOGICDEVICE pTargetDevice; - /* Fetch CCB. */ + /* Fetch the CCB from guest memory. */ + /** @todo How much do we really have to read? */ RTGCPHYS GCPhysAddrCCB = (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB; PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB, - &pTaskState->CommandControlBlockGuest, sizeof(CommandControlBlock)); + &pTaskState->CommandControlBlockGuest, sizeof(CCB32)); - PBUSLOGICDEVICE pTargetDevice = &pBusLogic->aDeviceStates[pTaskState->CommandControlBlockGuest.uTargetId]; + uTargetIdCCB = pTaskState->fIs24Bit ? pTaskState->CommandControlBlockGuest.o.uTargetId : pTaskState->CommandControlBlockGuest.n.uTargetId; + pTargetDevice = &pBusLogic->aDeviceStates[uTargetIdCCB]; pTaskState->CTX_SUFF(pTargetDevice) = pTargetDevice; -#ifdef DEBUG - buslogicDumpCCBInfo(&pTaskState->CommandControlBlockGuest); +#ifdef LOG_ENABLED + buslogicR3DumpCCBInfo(&pTaskState->CommandControlBlockGuest, pTaskState->fIs24Bit); #endif /* Alloc required buffers. */ - rc = buslogicDataBufferAlloc(pTaskState); + rc = buslogicR3DataBufferAlloc(pTaskState); AssertMsgRC(rc, ("Alloc failed rc=%Rrc\n", rc)); - if (pTaskState->CommandControlBlockGuest.cbSenseData) - { - rc = buslogicSenseBufferAlloc(pTaskState); - AssertMsgRC(rc, ("Mapping sense buffer failed rc=%Rrc\n", rc)); - } + rc = buslogicR3SenseBufferAlloc(pTaskState); + AssertMsgRC(rc, ("Mapping sense buffer failed rc=%Rrc\n", rc)); /* Check if device is present on bus. If not return error immediately and don't process this further. */ - if (!pBusLogic->aDeviceStates[pTaskState->CommandControlBlockGuest.uTargetId].fPresent) + if (!pBusLogic->aDeviceStates[uTargetIdCCB].fPresent) { - buslogicDataBufferFree(pTaskState); + buslogicR3DataBufferFree(pTaskState); if (pTaskState->pbSenseBuffer) - buslogicSenseBufferFree(pTaskState, true); + buslogicR3SenseBufferFree(pTaskState, true); - buslogicSendIncomingMailbox(pBusLogic, pTaskState, + buslogicR3SendIncomingMailbox(pBusLogic, pTaskState, BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_SCSI_SELECTION_TIMEOUT, BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD, BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITH_ERROR); @@ -2274,21 +2926,22 @@ static int buslogicDeviceSCSIRequestSetup(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTAT else { /* Setup SCSI request. */ - pTaskState->PDMScsiRequest.uLogicalUnit = pTaskState->CommandControlBlockGuest.uLogicalUnit; + pTaskState->PDMScsiRequest.uLogicalUnit = pTaskState->fIs24Bit ? pTaskState->CommandControlBlockGuest.o.uLogicalUnit + : pTaskState->CommandControlBlockGuest.n.uLogicalUnit; - if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN) + if (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN) pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_UNKNOWN; - else if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_IN) + else if (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_IN) pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_FROM_DEVICE; - else if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_OUT) + else if (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_OUT) pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_TO_DEVICE; - else if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_NO_DATA) + else if (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_NO_DATA) pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_NONE; else - AssertMsgFailed(("Invalid data direction type %d\n", pTaskState->CommandControlBlockGuest.uDataDirection)); + AssertMsgFailed(("Invalid data direction type %d\n", pTaskState->CommandControlBlockGuest.c.uDataDirection)); - pTaskState->PDMScsiRequest.cbCDB = pTaskState->CommandControlBlockGuest.cbCDB; - pTaskState->PDMScsiRequest.pbCDB = pTaskState->CommandControlBlockGuest.aCDB; + pTaskState->PDMScsiRequest.cbCDB = pTaskState->CommandControlBlockGuest.c.cbCDB; + pTaskState->PDMScsiRequest.pbCDB = pTaskState->CommandControlBlockGuest.c.abCDB; if (pTaskState->DataSeg.cbSeg) { pTaskState->PDMScsiRequest.cbScatterGather = pTaskState->DataSeg.cbSeg; @@ -2301,7 +2954,7 @@ static int buslogicDeviceSCSIRequestSetup(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTAT pTaskState->PDMScsiRequest.cScatterGatherEntries = 0; pTaskState->PDMScsiRequest.paScatterGatherHead = NULL; } - pTaskState->PDMScsiRequest.cbSenseBuffer = pTaskState->CommandControlBlockGuest.cbSenseData; + pTaskState->PDMScsiRequest.cbSenseBuffer = buslogicR3ConvertSenseBufferLength(pTaskState->CommandControlBlockGuest.c.cbSenseData); pTaskState->PDMScsiRequest.pbSenseBuffer = pTaskState->pbSenseBuffer; pTaskState->PDMScsiRequest.pvUser = pTaskState; @@ -2313,13 +2966,67 @@ static int buslogicDeviceSCSIRequestSetup(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTAT return rc; } +static int buslogicR3DeviceSCSIRequestAbort(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState) +{ + int rc = VINF_SUCCESS; + uint8_t uTargetIdCCB; + PBUSLOGICDEVICE pTargetDevice; + RTGCPHYS GCPhysAddrCCB = (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB; + + PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB, + &pTaskState->CommandControlBlockGuest, sizeof(CCB32)); + + uTargetIdCCB = pTaskState->fIs24Bit ? pTaskState->CommandControlBlockGuest.o.uTargetId : pTaskState->CommandControlBlockGuest.n.uTargetId; + pTargetDevice = &pBusLogic->aDeviceStates[uTargetIdCCB]; + pTaskState->CTX_SUFF(pTargetDevice) = pTargetDevice; + + buslogicR3SendIncomingMailbox(pBusLogic, pTaskState, + BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_ABORT_QUEUE_GENERATED, + BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD, + BUSLOGIC_MAILBOX_INCOMING_COMPLETION_ABORTED_NOT_FOUND); + + RTMemCacheFree(pBusLogic->hTaskCache, pTaskState); + + return rc; +} + +/** + * Read a mailbox from guest memory. Convert 24-bit mailboxes to + * 32-bit format. + * + * @returns Mailbox guest physical address. + * @param pBusLogic Pointer to the BusLogic instance data. + * @param pTaskStat Pointer to the task state being set up. + */ +static RTGCPHYS buslogicR3ReadOutgoingMailbox(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState) +{ + RTGCPHYS GCMailbox; + + if (pBusLogic->fMbxIs24Bit) + { + Mailbox24 Mbx24; + + GCMailbox = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox24)); + PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCMailbox, &Mbx24, sizeof(Mailbox24)); + pTaskState->MailboxGuest.u32PhysAddrCCB = ADDR_TO_U32(Mbx24.aPhysAddrCCB); + pTaskState->MailboxGuest.u.out.uActionCode = Mbx24.uCmdState; + } + else + { + GCMailbox = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox32)); + PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCMailbox, &pTaskState->MailboxGuest, sizeof(Mailbox32)); + } + + return GCMailbox; +} + /** * Read mailbox from the guest and execute command. * * @returns VBox status code. * @param pBusLogic Pointer to the BusLogic instance data. */ -static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic) +static int buslogicR3ProcessMailboxNext(PBUSLOGIC pBusLogic) { PBUSLOGICTASKSTATE pTaskState = NULL; RTGCPHYS GCPhysAddrMailboxCurrent; @@ -2328,7 +3035,9 @@ static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic) rc = RTMemCacheAllocEx(pBusLogic->hTaskCache, (void **)&pTaskState); AssertMsgReturn(RT_SUCCESS(rc) && (pTaskState != NULL), ("Failed to get task state from cache\n"), rc); - pTaskState->fBIOS = false; + pTaskState->fBIOS = false; + pTaskState->fIs24Bit = pBusLogic->fMbxIs24Bit; + pTaskState->cbSGEntry = pBusLogic->fMbxIs24Bit ? sizeof(SGE24) : sizeof(SGE32); if (!pBusLogic->fStrictRoundRobinMode) { @@ -2338,23 +3047,17 @@ static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic) do { /* Fetch mailbox from guest memory. */ - GCPhysAddrMailboxCurrent = buslogicOutgoingMailboxGetGCPhys(pBusLogic); - - PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent, - &pTaskState->MailboxGuest, sizeof(Mailbox)); + GCPhysAddrMailboxCurrent = buslogicR3ReadOutgoingMailbox(pBusLogic,pTaskState); /* Check the next mailbox. */ - buslogicOutgoingMailboxAdvance(pBusLogic); + buslogicR3OutgoingMailboxAdvance(pBusLogic); } while ( pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE && uMailboxPosCur != pBusLogic->uMailboxOutgoingPositionCurrent); } else { /* Fetch mailbox from guest memory. */ - GCPhysAddrMailboxCurrent = buslogicOutgoingMailboxGetGCPhys(pBusLogic); - - PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent, - &pTaskState->MailboxGuest, sizeof(Mailbox)); + GCPhysAddrMailboxCurrent = buslogicR3ReadOutgoingMailbox(pBusLogic,pTaskState); } /* @@ -2370,20 +3073,22 @@ static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic) return VERR_NO_DATA; } - LogFlow(("Got loaded mailbox at slot %u, CCB phys %RGp\n", pBusLogic->uMailboxOutgoingPositionCurrent, pTaskState->MailboxGuest.u32PhysAddrCCB)); -#ifdef DEBUG - buslogicDumpMailboxInfo(&pTaskState->MailboxGuest, true); + LogFlow(("Got loaded mailbox at slot %u, CCB phys %RGp\n", pBusLogic->uMailboxOutgoingPositionCurrent, (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB)); +#ifdef LOG_ENABLED + buslogicR3DumpMailboxInfo(&pTaskState->MailboxGuest, true); #endif /* We got the mailbox, mark it as free in the guest. */ uint8_t uActionCode = BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE; - PDMDevHlpPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent + RT_OFFSETOF(Mailbox, u.out.uActionCode), &uActionCode, sizeof(uActionCode)); + unsigned uCodeOffs = pTaskState->fIs24Bit ? RT_OFFSETOF(Mailbox24, uCmdState) : RT_OFFSETOF(Mailbox32, u.out.uActionCode); + PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent + uCodeOffs, &uActionCode, sizeof(uActionCode)); if (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_START_COMMAND) - rc = buslogicDeviceSCSIRequestSetup(pBusLogic, pTaskState); + rc = buslogicR3DeviceSCSIRequestSetup(pBusLogic, pTaskState); else if (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_ABORT_COMMAND) { - AssertMsgFailed(("Not implemented yet\n")); + LogFlow(("Aborting mailbox\n")); + rc = buslogicR3DeviceSCSIRequestAbort(pBusLogic, pTaskState); } else AssertMsgFailed(("Invalid outgoing mailbox action code %u\n", pTaskState->MailboxGuest.u.out.uActionCode)); @@ -2392,7 +3097,7 @@ static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic) /* Advance to the next mailbox. */ if (pBusLogic->fStrictRoundRobinMode) - buslogicOutgoingMailboxAdvance(pBusLogic); + buslogicR3OutgoingMailboxAdvance(pBusLogic); return rc; } @@ -2406,20 +3111,20 @@ static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic) * @param pDevIns The device instance. * @param pItem The item to consume. Upon return this item will be freed. */ -static DECLCALLBACK(bool) buslogicNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem) +static DECLCALLBACK(bool) buslogicR3NotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem) { PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC); /* Reset notification send flag now. */ Assert(pBusLogic->fNotificationSend); ASMAtomicXchgBool(&pBusLogic->fNotificationSend, false); - ASMAtomicXchgU32(&pBusLogic->cMailboxesReady, 0); /* @todo: Actually not required anymore but to stay compatible with older saved states. */ + ASMAtomicXchgU32(&pBusLogic->cMailboxesReady, 0); /** @todo Actually not required anymore but to stay compatible with older saved states. */ /* Process mailboxes. */ int rc; do { - rc = buslogicProcessMailboxNext(pBusLogic); + rc = buslogicR3ProcessMailboxNext(pBusLogic); AssertMsg(RT_SUCCESS(rc) || rc == VERR_NO_DATA, ("Processing mailbox failed rc=%Rrc\n", rc)); } while (RT_SUCCESS(rc)); @@ -2433,7 +3138,7 @@ static DECLCALLBACK(bool) buslogicNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQU * @returns nothing. * @param pThis The BusLogic device instance. */ -static void buslogicKick(PBUSLOGIC pThis) +static void buslogicR3Kick(PBUSLOGIC pThis) { if (pThis->fRedo) { @@ -2442,7 +3147,7 @@ static void buslogicKick(PBUSLOGIC pThis) { /* The BIOS had a request active when we got suspended. Resume it. */ - int rc = buslogicPrepareBIOSSCSIRequest(pThis); + int rc = buslogicR3PrepareBIOSSCSIRequest(pThis); AssertRC(rc); } else @@ -2456,7 +3161,7 @@ static void buslogicKick(PBUSLOGIC pThis) { PBUSLOGICTASKSTATE pCur = pTaskState; - int rc = buslogicDeviceSCSIRequestSetup(pThis, pCur); + int rc = buslogicR3DeviceSCSIRequestSetup(pThis, pCur); AssertRC(rc); pTaskState = pTaskState->pRedoNext; @@ -2465,7 +3170,8 @@ static void buslogicKick(PBUSLOGIC pThis) } } -static DECLCALLBACK(int) buslogicLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass) +/** @callback_method_impl{FNSSMDEVLIVEEXEC} */ +static DECLCALLBACK(int) buslogicR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass) { PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC); @@ -2476,7 +3182,8 @@ static DECLCALLBACK(int) buslogicLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u return VINF_SSM_DONT_CALL_AGAIN; } -static DECLCALLBACK(int) buslogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) +/** @callback_method_impl{FNSSMDEVSAVEEXEC} */ +static DECLCALLBACK(int) buslogicR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) { PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC); @@ -2504,8 +3211,9 @@ static DECLCALLBACK(int) buslogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) SSMR3PutU8 (pSSM, pBusLogic->iReply); SSMR3PutU8 (pSSM, pBusLogic->cbReplyParametersLeft); SSMR3PutBool (pSSM, pBusLogic->fIRQEnabled); - SSMR3PutBool (pSSM, pBusLogic->fISAEnabled); + SSMR3PutU8 (pSSM, pBusLogic->uISABaseCode); SSMR3PutU32 (pSSM, pBusLogic->cMailbox); + SSMR3PutBool (pSSM, pBusLogic->fMbxIs24Bit); SSMR3PutGCPhys(pSSM, pBusLogic->GCPhysAddrMailboxOutgoingBase); SSMR3PutU32 (pSSM, pBusLogic->uMailboxOutgoingPositionCurrent); SSMR3PutU32 (pSSM, pBusLogic->cMailboxesReady); @@ -2519,14 +3227,14 @@ static DECLCALLBACK(int) buslogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.uTargetDevice); SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.uTxDir); SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.cbCDB); - SSMR3PutMem (pSSM, pBusLogic->VBoxSCSI.aCDB, sizeof(pBusLogic->VBoxSCSI.aCDB)); + SSMR3PutMem (pSSM, pBusLogic->VBoxSCSI.abCDB, sizeof(pBusLogic->VBoxSCSI.abCDB)); SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.iCDB); SSMR3PutU32 (pSSM, pBusLogic->VBoxSCSI.cbBuf); SSMR3PutU32 (pSSM, pBusLogic->VBoxSCSI.iBuf); SSMR3PutBool (pSSM, pBusLogic->VBoxSCSI.fBusy); SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.enmState); if (pBusLogic->VBoxSCSI.cbBuf) - SSMR3PutMem(pSSM, pBusLogic->VBoxSCSI.pBuf, pBusLogic->VBoxSCSI.cbBuf); + SSMR3PutMem(pSSM, pBusLogic->VBoxSCSI.pbBuf, pBusLogic->VBoxSCSI.cbBuf); /* * Save the physical addresses of the command control blocks of still pending tasks. @@ -2558,15 +3266,18 @@ static DECLCALLBACK(int) buslogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) return SSMR3PutU32(pSSM, ~0); } -static DECLCALLBACK(int) buslogicLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) +/** @callback_method_impl{FNSSMDEVLOADDONE} */ +static DECLCALLBACK(int) buslogicR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) { PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC); - buslogicKick(pThis); + buslogicR3RegisterISARange(pThis, pThis->uISABaseCode); + buslogicR3Kick(pThis); return VINF_SUCCESS; } -static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass) +/** @callback_method_impl{FNSSMDEVLOADEXEC} */ +static DECLCALLBACK(int) buslogicR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass) { PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC); int rc = VINF_SUCCESS; @@ -2601,7 +3312,10 @@ static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->regGeometry); SSMR3GetMem (pSSM, &pBusLogic->LocalRam, sizeof(pBusLogic->LocalRam)); SSMR3GetU8 (pSSM, &pBusLogic->uOperationCode); - SSMR3GetMem (pSSM, &pBusLogic->aCommandBuffer, sizeof(pBusLogic->aCommandBuffer)); + if (uVersion > BUSLOGIC_SAVED_STATE_MINOR_PRE_CMDBUF_RESIZE) + SSMR3GetMem (pSSM, &pBusLogic->aCommandBuffer, sizeof(pBusLogic->aCommandBuffer)); + else + SSMR3GetMem (pSSM, &pBusLogic->aCommandBuffer, BUSLOGIC_COMMAND_SIZE_OLD); SSMR3GetU8 (pSSM, &pBusLogic->iParameter); SSMR3GetU8 (pSSM, &pBusLogic->cbCommandParametersLeft); SSMR3GetBool (pSSM, &pBusLogic->fUseLocalRam); @@ -2609,8 +3323,10 @@ static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u SSMR3GetU8 (pSSM, &pBusLogic->iReply); SSMR3GetU8 (pSSM, &pBusLogic->cbReplyParametersLeft); SSMR3GetBool (pSSM, &pBusLogic->fIRQEnabled); - SSMR3GetBool (pSSM, &pBusLogic->fISAEnabled); + SSMR3GetU8 (pSSM, &pBusLogic->uISABaseCode); SSMR3GetU32 (pSSM, &pBusLogic->cMailbox); + if (uVersion > BUSLOGIC_SAVED_STATE_MINOR_PRE_24BIT_MBOX) + SSMR3GetBool (pSSM, &pBusLogic->fMbxIs24Bit); SSMR3GetGCPhys(pSSM, &pBusLogic->GCPhysAddrMailboxOutgoingBase); SSMR3GetU32 (pSSM, &pBusLogic->uMailboxOutgoingPositionCurrent); SSMR3GetU32 (pSSM, (uint32_t *)&pBusLogic->cMailboxesReady); @@ -2624,7 +3340,7 @@ static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.uTargetDevice); SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.uTxDir); SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.cbCDB); - SSMR3GetMem (pSSM, pBusLogic->VBoxSCSI.aCDB, sizeof(pBusLogic->VBoxSCSI.aCDB)); + SSMR3GetMem (pSSM, pBusLogic->VBoxSCSI.abCDB, sizeof(pBusLogic->VBoxSCSI.abCDB)); SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.iCDB); SSMR3GetU32 (pSSM, &pBusLogic->VBoxSCSI.cbBuf); SSMR3GetU32 (pSSM, &pBusLogic->VBoxSCSI.iBuf); @@ -2632,14 +3348,14 @@ static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->VBoxSCSI.enmState); if (pBusLogic->VBoxSCSI.cbBuf) { - pBusLogic->VBoxSCSI.pBuf = (uint8_t *)RTMemAllocZ(pBusLogic->VBoxSCSI.cbBuf); - if (!pBusLogic->VBoxSCSI.pBuf) + pBusLogic->VBoxSCSI.pbBuf = (uint8_t *)RTMemAllocZ(pBusLogic->VBoxSCSI.cbBuf); + if (!pBusLogic->VBoxSCSI.pbBuf) { LogRel(("BusLogic: Out of memory during restore.\n")); return PDMDEV_SET_ERROR(pDevIns, VERR_NO_MEMORY, N_("BusLogic: Out of memory during restore\n")); } - SSMR3GetMem(pSSM, pBusLogic->VBoxSCSI.pBuf, pBusLogic->VBoxSCSI.cbBuf); + SSMR3GetMem(pSSM, pBusLogic->VBoxSCSI.pbBuf, pBusLogic->VBoxSCSI.cbBuf); } if (pBusLogic->VBoxSCSI.fBusy) @@ -2689,7 +3405,7 @@ static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u } /** - * Gets the pointer to the status LED of a device - called from the SCSi driver. + * Gets the pointer to the status LED of a device - called from the SCSI driver. * * @returns VBox status code. * @param pInterface Pointer to the interface structure containing the called function pointer. @@ -2697,7 +3413,7 @@ static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u * doesn't know about other LUN's. * @param ppLed Where to store the LED pointer. */ -static DECLCALLBACK(int) buslogicDeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed) +static DECLCALLBACK(int) buslogicR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed) { PBUSLOGICDEVICE pDevice = PDMILEDPORTS_2_PBUSLOGICDEVICE(pInterface); if (iLUN == 0) @@ -2712,7 +3428,7 @@ static DECLCALLBACK(int) buslogicDeviceQueryStatusLed(PPDMILEDPORTS pInterface, /** * @interface_method_impl{PDMIBASE,pfnQueryInterface} */ -static DECLCALLBACK(void *) buslogicDeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID) +static DECLCALLBACK(void *) buslogicR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID) { PBUSLOGICDEVICE pDevice = PDMIBASE_2_PBUSLOGICDEVICE(pInterface); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevice->IBase); @@ -2729,7 +3445,7 @@ static DECLCALLBACK(void *) buslogicDeviceQueryInterface(PPDMIBASE pInterface, c * @param iLUN The unit which status LED we desire. * @param ppLed Where to store the LED pointer. */ -static DECLCALLBACK(int) buslogicStatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed) +static DECLCALLBACK(int) buslogicR3StatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed) { PBUSLOGIC pBusLogic = PDMILEDPORTS_2_PBUSLOGIC(pInterface); if (iLUN < BUSLOGIC_MAX_DEVICES) @@ -2744,7 +3460,7 @@ static DECLCALLBACK(int) buslogicStatusQueryStatusLed(PPDMILEDPORTS pInterface, /** * @interface_method_impl{PDMIBASE,pfnQueryInterface} */ -static DECLCALLBACK(void *) buslogicStatusQueryInterface(PPDMIBASE pInterface, const char *pszIID) +static DECLCALLBACK(void *) buslogicR3StatusQueryInterface(PPDMIBASE pInterface, const char *pszIID) { PBUSLOGIC pThis = PDMIBASE_2_PBUSLOGIC(pInterface); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase); @@ -2752,12 +3468,117 @@ static DECLCALLBACK(void *) buslogicStatusQueryInterface(PPDMIBASE pInterface, c return NULL; } +/** + * BusLogic debugger info callback. + * + * @param pDevIns The device instance. + * @param pHlp The output helpers. + * @param pszArgs The arguments. + */ +static DECLCALLBACK(void) buslogicR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs) +{ + PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC); + unsigned i; + bool fVerbose = false; + + /* Parse arguments. */ + if (pszArgs) + fVerbose = strstr(pszArgs, "verbose") != NULL; + + /* Show basic information. */ + pHlp->pfnPrintf(pHlp, + "%s#%d: PCI I/O=%RTiop ISA I/O=%RTiop MMIO=%RGp IRQ=%u GC=%RTbool R0=%RTbool\n", + pDevIns->pReg->szName, + pDevIns->iInstance, + pThis->IOPortBase, pThis->IOISABase, pThis->MMIOBase, + PCIDevGetInterruptLine(&pThis->dev), + !!pThis->fGCEnabled, !!pThis->fR0Enabled); + + /* Print mailbox state. */ + if (pThis->regStatus & BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED) + pHlp->pfnPrintf(pHlp, "Mailbox not initialized\n"); + else + pHlp->pfnPrintf(pHlp, "%u-bit mailbox with %u entries at %RGp\n", + pThis->fMbxIs24Bit ? 24 : 32, pThis->cMailbox, + pThis->GCPhysAddrMailboxOutgoingBase); + + /* Print register contents. */ + pHlp->pfnPrintf(pHlp, "Registers: STAT=%02x INTR=%02x GEOM=%02x\n", + pThis->regStatus, pThis->regInterrupt, pThis->regGeometry); + + /* Print the current command, if any. */ + if (pThis->uOperationCode != 0xff ) + pHlp->pfnPrintf(pHlp, "Current command: %02X\n", pThis->uOperationCode); + + if (fVerbose && (pThis->regStatus & BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED) == 0) + { + RTGCPHYS GCMailbox; + + /* Dump the mailbox contents. */ + if (pThis->fMbxIs24Bit) + { + Mailbox24 Mbx24; + + /* Outgoing mailbox, 24-bit format. */ + GCMailbox = pThis->GCPhysAddrMailboxOutgoingBase; + pHlp->pfnPrintf(pHlp, " Outgoing mailbox entries (24-bit) at %06X:\n", GCMailbox); + for (i = 0; i < pThis->cMailbox; ++i) + { + PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCMailbox, &Mbx24, sizeof(Mailbox24)); + pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %06X action code %02X", i, ADDR_TO_U32(Mbx24.aPhysAddrCCB), Mbx24.uCmdState); + pHlp->pfnPrintf(pHlp, "%s\n", pThis->uMailboxOutgoingPositionCurrent == i ? " *" : ""); + GCMailbox += sizeof(Mailbox24); + } + + /* Incoming mailbox, 24-bit format. */ + GCMailbox = pThis->GCPhysAddrMailboxOutgoingBase + (pThis->cMailbox * sizeof(Mailbox24)); + pHlp->pfnPrintf(pHlp, " Incoming mailbox entries (24-bit) at %06X:\n", GCMailbox); + for (i = 0; i < pThis->cMailbox; ++i) + { + PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCMailbox, &Mbx24, sizeof(Mailbox24)); + pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %06X completion code %02X", i, ADDR_TO_U32(Mbx24.aPhysAddrCCB), Mbx24.uCmdState); + pHlp->pfnPrintf(pHlp, "%s\n", pThis->uMailboxIncomingPositionCurrent == i ? " *" : ""); + GCMailbox += sizeof(Mailbox24); + } + + } + else + { + Mailbox32 Mbx32; + + /* Outgoing mailbox, 32-bit format. */ + GCMailbox = pThis->GCPhysAddrMailboxOutgoingBase; + pHlp->pfnPrintf(pHlp, " Outgoing mailbox entries (32-bit) at %08X:\n", (uint32_t)GCMailbox); + for (i = 0; i < pThis->cMailbox; ++i) + { + PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCMailbox, &Mbx32, sizeof(Mailbox32)); + pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %08X action code %02X", i, Mbx32.u32PhysAddrCCB, Mbx32.u.out.uActionCode); + pHlp->pfnPrintf(pHlp, "%s\n", pThis->uMailboxOutgoingPositionCurrent == i ? " *" : ""); + GCMailbox += sizeof(Mailbox32); + } + + /* Incoming mailbox, 32-bit format. */ + GCMailbox = pThis->GCPhysAddrMailboxOutgoingBase + (pThis->cMailbox * sizeof(Mailbox32)); + pHlp->pfnPrintf(pHlp, " Outgoing mailbox entries (32-bit) at %08X:\n", (uint32_t)GCMailbox); + for (i = 0; i < pThis->cMailbox; ++i) + { + PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCMailbox, &Mbx32, sizeof(Mailbox32)); + pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %08X completion code %02X BTSTAT %02X SDSTAT %02X", i, + Mbx32.u32PhysAddrCCB, Mbx32.u.in.uCompletionCode, Mbx32.u.in.uHostAdapterStatus, Mbx32.u.in.uTargetDeviceStatus); + pHlp->pfnPrintf(pHlp, "%s\n", pThis->uMailboxOutgoingPositionCurrent == i ? " *" : ""); + GCMailbox += sizeof(Mailbox32); + } + + } + } +} + /* -=-=-=-=- Helper -=-=-=-=- */ /** * Checks if all asynchronous I/O is finished. * - * Used by buslogicReset, buslogicSuspend and buslogicPowerOff. + * Used by buslogicR3Reset, buslogicR3Suspend and buslogicR3PowerOff. * * @returns true if quiesced, false if busy. * @param pDevIns The device instance. @@ -2849,9 +3670,9 @@ static void buslogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns, bool fPowerOff) * * @param pDevIns The device instance data. */ -static DECLCALLBACK(void) buslogicSuspend(PPDMDEVINS pDevIns) +static DECLCALLBACK(void) buslogicR3Suspend(PPDMDEVINS pDevIns) { - Log(("buslogicSuspend\n")); + Log(("buslogicR3Suspend\n")); buslogicR3SuspendOrPowerOff(pDevIns, false /* fPoweroff */); } @@ -2860,11 +3681,11 @@ static DECLCALLBACK(void) buslogicSuspend(PPDMDEVINS pDevIns) * * @param pDevIns The device instance data. */ -static DECLCALLBACK(void) buslogicResume(PPDMDEVINS pDevIns) +static DECLCALLBACK(void) buslogicR3Resume(PPDMDEVINS pDevIns) { - Log(("buslogicResume\n")); + Log(("buslogicR3Resume\n")); PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC); - buslogicKick(pThis); + buslogicR3Kick(pThis); } @@ -2878,7 +3699,7 @@ static DECLCALLBACK(void) buslogicResume(PPDMDEVINS pDevIns) * @param iLUN The logical unit which is being detached. * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines. */ -static DECLCALLBACK(void) buslogicDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) +static DECLCALLBACK(void) buslogicR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) { PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC); PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[iLUN]; @@ -2906,7 +3727,7 @@ static DECLCALLBACK(void) buslogicDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint * @param iLUN The logical unit which is being detached. * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines. */ -static DECLCALLBACK(int) buslogicAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) +static DECLCALLBACK(int) buslogicR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) { PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC); PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[iLUN]; @@ -2958,14 +3779,14 @@ static DECLCALLBACK(bool) buslogicR3IsAsyncResetDone(PPDMDEVINS pDevIns) return false; ASMAtomicWriteBool(&pThis->fSignalIdle, false); - buslogicHwReset(pThis); + buslogicR3HwReset(pThis, true); return true; } /** * @copydoc FNPDMDEVRESET */ -static DECLCALLBACK(void) buslogicReset(PPDMDEVINS pDevIns) +static DECLCALLBACK(void) buslogicR3Reset(PPDMDEVINS pDevIns) { PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC); @@ -2975,21 +3796,20 @@ static DECLCALLBACK(void) buslogicReset(PPDMDEVINS pDevIns) else { ASMAtomicWriteBool(&pThis->fSignalIdle, false); - buslogicHwReset(pThis); + buslogicR3HwReset(pThis, true); } } -static DECLCALLBACK(void) buslogicRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta) +static DECLCALLBACK(void) buslogicR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta) { - uint32_t i; - PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC); + PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC); - pBusLogic->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); - pBusLogic->pNotifierQueueRC = PDMQueueRCPtr(pBusLogic->pNotifierQueueR3); + pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); + pThis->pNotifierQueueRC = PDMQueueRCPtr(pThis->pNotifierQueueR3); - for (i = 0; i < BUSLOGIC_MAX_DEVICES; i++) + for (uint32_t i = 0; i < BUSLOGIC_MAX_DEVICES; i++) { - PBUSLOGICDEVICE pDevice = &pBusLogic->aDeviceStates[i]; + PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[i]; pDevice->pBusLogicRC = PDMINS_2_DATA_RCPTR(pDevIns); } @@ -3001,9 +3821,9 @@ static DECLCALLBACK(void) buslogicRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDel * * @param pDevIns Pointer to the device instance */ -static DECLCALLBACK(void) buslogicPowerOff(PPDMDEVINS pDevIns) +static DECLCALLBACK(void) buslogicR3PowerOff(PPDMDEVINS pDevIns) { - Log(("buslogicPowerOff\n")); + Log(("buslogicR3PowerOff\n")); buslogicR3SuspendOrPowerOff(pDevIns, true /* fPoweroff */); } @@ -3015,7 +3835,7 @@ static DECLCALLBACK(void) buslogicPowerOff(PPDMDEVINS pDevIns) * * @param pDevIns The device instance data. */ -static DECLCALLBACK(int) buslogicDestruct(PPDMDEVINS pDevIns) +static DECLCALLBACK(int) buslogicR3Destruct(PPDMDEVINS pDevIns) { PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC); PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); @@ -3054,20 +3874,46 @@ static DECLCALLBACK(int) buslogicDestruct(PPDMDEVINS pDevIns) /** * @interface_method_impl{PDMDEVREG,pfnConstruct} */ -static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg) +static DECLCALLBACK(int) buslogicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg) { PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC); int rc = VINF_SUCCESS; bool fBootable = true; + char achISACompat[16]; PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); /* + * Init instance data (do early because of constructor). + */ + pThis->hTaskCache = NIL_RTMEMCACHE; + pThis->pDevInsR3 = pDevIns; + pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns); + pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); + pThis->IBase.pfnQueryInterface = buslogicR3StatusQueryInterface; + pThis->ILeds.pfnQueryStatusLed = buslogicR3StatusQueryStatusLed; + + PCIDevSetVendorId (&pThis->dev, 0x104b); /* BusLogic */ + PCIDevSetDeviceId (&pThis->dev, 0x1040); /* BT-958 */ + PCIDevSetCommand (&pThis->dev, 0x0003); + PCIDevSetRevisionId (&pThis->dev, 0x01); + PCIDevSetClassProg (&pThis->dev, 0x00); /* SCSI */ + PCIDevSetClassSub (&pThis->dev, 0x00); /* SCSI */ + PCIDevSetClassBase (&pThis->dev, 0x01); /* Mass storage */ + PCIDevSetBaseAddress (&pThis->dev, 0, true /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000); + PCIDevSetBaseAddress (&pThis->dev, 1, false /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000); + PCIDevSetSubSystemVendorId(&pThis->dev, 0x104b); + PCIDevSetSubSystemId (&pThis->dev, 0x1040); + PCIDevSetInterruptLine (&pThis->dev, 0x00); + PCIDevSetInterruptPin (&pThis->dev, 0x01); + + /* * Validate and read configuration. */ if (!CFGMR3AreValuesValid(pCfg, "GCEnabled\0" "R0Enabled\0" - "Bootable\0")) + "Bootable\0" + "ISACompat\0")) return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, N_("BusLogic configuration error: unknown option specified")); @@ -3088,52 +3934,58 @@ static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC N_("BusLogic configuration error: failed to read Bootable as boolean")); Log(("%s: fBootable=%RTbool\n", __FUNCTION__, fBootable)); - pThis->pDevInsR3 = pDevIns; - pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns); - pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); - pThis->IBase.pfnQueryInterface = buslogicStatusQueryInterface; - pThis->ILeds.pfnQueryStatusLed = buslogicStatusQueryStatusLed; - - PCIDevSetVendorId (&pThis->dev, 0x104b); /* BusLogic */ - PCIDevSetDeviceId (&pThis->dev, 0x1040); /* BT-958 */ - PCIDevSetCommand (&pThis->dev, 0x0003); - PCIDevSetRevisionId (&pThis->dev, 0x01); - PCIDevSetClassProg (&pThis->dev, 0x00); /* SCSI */ - PCIDevSetClassSub (&pThis->dev, 0x00); /* SCSI */ - PCIDevSetClassBase (&pThis->dev, 0x01); /* Mass storage */ - PCIDevSetBaseAddress (&pThis->dev, 0, true /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000); - PCIDevSetBaseAddress (&pThis->dev, 1, false /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000); - PCIDevSetSubSystemVendorId(&pThis->dev, 0x104b); - PCIDevSetSubSystemId (&pThis->dev, 0x1040); - PCIDevSetInterruptLine (&pThis->dev, 0x00); - PCIDevSetInterruptPin (&pThis->dev, 0x01); + /* Only the first instance defaults to having the ISA compatibility ports enabled. */ + if (iInstance == 0) + rc = CFGMR3QueryStringDef(pCfg, "ISACompat", achISACompat, sizeof(achISACompat), "Alternate"); + else + rc = CFGMR3QueryStringDef(pCfg, "ISACompat", achISACompat, sizeof(achISACompat), "Disabled"); + if (RT_FAILURE(rc)) + return PDMDEV_SET_ERROR(pDevIns, rc, + N_("BusLogic configuration error: failed to read ISACompat as string")); + Log(("%s: ISACompat=%s\n", __FUNCTION__, achISACompat)); + + /* Grok the ISACompat setting. */ + if (!strcmp(achISACompat, "Disabled")) + pThis->uDefaultISABaseCode = ISA_BASE_DISABLED; + else if (!strcmp(achISACompat, "Primary")) + pThis->uDefaultISABaseCode = 0; /* I/O base at 330h. */ + else if (!strcmp(achISACompat, "Alternate")) + pThis->uDefaultISABaseCode = 1; /* I/O base at 334h. */ + else + return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, + N_("BusLogic configuration error: invalid ISACompat setting")); /* - * Register the PCI device, it's I/O regions. + * Register the PCI device and its I/O regions. */ - rc = PDMDevHlpPCIRegister (pDevIns, &pThis->dev); + rc = PDMDevHlpPCIRegister(pDevIns, &pThis->dev); if (RT_FAILURE(rc)) return rc; - rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 32, PCI_ADDRESS_SPACE_IO, buslogicMMIOMap); + rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 32, PCI_ADDRESS_SPACE_IO, buslogicR3MmioMap); if (RT_FAILURE(rc)) return rc; - rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, 32, PCI_ADDRESS_SPACE_MEM, buslogicMMIOMap); + rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, 32, PCI_ADDRESS_SPACE_MEM, buslogicR3MmioMap); if (RT_FAILURE(rc)) return rc; if (fBootable) { - /* Register I/O port space in ISA region for BIOS access. */ + /* Register I/O port space for BIOS access. */ rc = PDMDevHlpIOPortRegister(pDevIns, BUSLOGIC_BIOS_IO_PORT, 4, NULL, - buslogicIsaIOPortWrite, buslogicIsaIOPortRead, - buslogicIsaIOPortWriteStr, buslogicIsaIOPortReadStr, + buslogicR3BiosIoPortWrite, buslogicR3BiosIoPortRead, + buslogicR3BiosIoPortWriteStr, buslogicR3BiosIoPortReadStr, "BusLogic BIOS"); if (RT_FAILURE(rc)) - return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register legacy I/O handlers")); + return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register BIOS I/O handlers")); } + /* Set up the compatibility I/O range. */ + rc = buslogicR3RegisterISARange(pThis, pThis->uDefaultISABaseCode); + if (RT_FAILURE(rc)) + return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register ISA I/O handlers")); + /* Initialize task cache. */ rc = RTMemCacheCreate(&pThis->hTaskCache, sizeof(BUSLOGICTASKSTATE), 0, UINT32_MAX, NULL, NULL, NULL, 0); @@ -3143,7 +3995,7 @@ static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC /* Initialize task queue. */ rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 5, 0, - buslogicNotifyQueueConsumer, true, "BusLogicTask", &pThis->pNotifierQueueR3); + buslogicR3NotifyQueueConsumer, true, "BusLogicTask", &pThis->pNotifierQueueR3); if (RT_FAILURE(rc)) return rc; pThis->pNotifierQueueR0 = PDMQueueR0Ptr(pThis->pNotifierQueueR3); @@ -3151,8 +4003,7 @@ static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIntr, RT_SRC_POS, "BusLogic-Intr#%u", pDevIns->iInstance); if (RT_FAILURE(rc)) - return PDMDEV_SET_ERROR(pDevIns, rc, - N_("BusLogic: cannot create critical section")); + return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic: cannot create critical section")); /* Initialize per device state. */ for (unsigned i = 0; i < RT_ELEMENTS(pThis->aDeviceStates); i++) @@ -3160,7 +4011,7 @@ static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC char szName[24]; PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[i]; - RTStrPrintf(szName, sizeof(szName), "Device%d", i); + RTStrPrintf(szName, sizeof(szName), "Device%u", i); /* Initialize static parts of the device. */ pDevice->iLUN = i; @@ -3168,10 +4019,10 @@ static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC pDevice->pBusLogicR0 = PDMINS_2_DATA_R0PTR(pDevIns); pDevice->pBusLogicRC = PDMINS_2_DATA_RCPTR(pDevIns); pDevice->Led.u32Magic = PDMLED_MAGIC; - pDevice->IBase.pfnQueryInterface = buslogicDeviceQueryInterface; - pDevice->ISCSIPort.pfnSCSIRequestCompleted = buslogicDeviceSCSIRequestCompleted; - pDevice->ISCSIPort.pfnQueryDeviceLocation = buslogicQueryDeviceLocation; - pDevice->ILed.pfnQueryStatusLed = buslogicDeviceQueryStatusLed; + pDevice->IBase.pfnQueryInterface = buslogicR3DeviceQueryInterface; + pDevice->ISCSIPort.pfnSCSIRequestCompleted = buslogicR3DeviceSCSIRequestCompleted; + pDevice->ISCSIPort.pfnQueryDeviceLocation = buslogicR3QueryDeviceLocation; + pDevice->ILed.pfnQueryStatusLed = buslogicR3DeviceQueryStatusLed; /* Attach SCSI driver. */ rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, szName); @@ -3211,13 +4062,20 @@ static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC } rc = PDMDevHlpSSMRegisterEx(pDevIns, BUSLOGIC_SAVED_STATE_MINOR_VERSION, sizeof(*pThis), NULL, - NULL, buslogicLiveExec, NULL, - NULL, buslogicSaveExec, NULL, - NULL, buslogicLoadExec, buslogicLoadDone); + NULL, buslogicR3LiveExec, NULL, + NULL, buslogicR3SaveExec, NULL, + NULL, buslogicR3LoadExec, buslogicR3LoadDone); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register save state handlers")); - rc = buslogicHwReset(pThis); + /* + * Register the debugger info callback. + */ + char szTmp[128]; + RTStrPrintf(szTmp, sizeof(szTmp), "%s%d", pDevIns->pReg->szName, pDevIns->iInstance); + PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "BusLogic HBA info", buslogicR3Info); + + rc = buslogicR3HwReset(pThis, true); AssertMsgRC(rc, ("hardware reset of BusLogic host adapter failed rc=%Rrc\n", rc)); return rc; @@ -3249,31 +4107,31 @@ const PDMDEVREG g_DeviceBusLogic = /* cbInstance */ sizeof(BUSLOGIC), /* pfnConstruct */ - buslogicConstruct, + buslogicR3Construct, /* pfnDestruct */ - buslogicDestruct, + buslogicR3Destruct, /* pfnRelocate */ - buslogicRelocate, - /* pfnIOCtl */ + buslogicR3Relocate, + /* pfnMemSetup */ NULL, /* pfnPowerOn */ NULL, /* pfnReset */ - buslogicReset, + buslogicR3Reset, /* pfnSuspend */ - buslogicSuspend, + buslogicR3Suspend, /* pfnResume */ - buslogicResume, + buslogicR3Resume, /* pfnAttach */ - buslogicAttach, + buslogicR3Attach, /* pfnDetach */ - buslogicDetach, + buslogicR3Detach, /* pfnQueryInterface. */ NULL, /* pfnInitComplete */ NULL, /* pfnPowerOff */ - buslogicPowerOff, + buslogicR3PowerOff, /* pfnSoftReset */ NULL, /* u32VersionEnd */ @@ -3282,4 +4140,3 @@ const PDMDEVREG g_DeviceBusLogic = #endif /* IN_RING3 */ #endif /* !VBOX_DEVICE_STRUCT_TESTCASE */ - diff --git a/src/VBox/Devices/Storage/fdc.c b/src/VBox/Devices/Storage/DevFdc.cpp index 703639e3..b719d8c0 100644 --- a/src/VBox/Devices/Storage/fdc.c +++ b/src/VBox/Devices/Storage/DevFdc.cpp @@ -1,10 +1,10 @@ -/* $Id: fdc.c $ */ +/* $Id: DevFdc.cpp $ */ /** @file * VBox storage devices: Floppy disk controller */ /* - * Copyright (C) 2006-2010 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; @@ -59,12 +59,6 @@ #define MAX_FD 2 -#define PDMIBASE_2_FDRIVE(pInterface) \ - ((fdrive_t *)((uintptr_t)(pInterface) - RT_OFFSETOF(fdrive_t, IBase))) - -#define PDMIMOUNTNOTIFY_2_FDRIVE(p) \ - ((fdrive_t *)((uintptr_t)(p) - RT_OFFSETOF(fdrive_t, IMountNotify))) - /********************************************************/ /* debug Floppy devices */ @@ -127,11 +121,14 @@ typedef enum fdrive_type_t { FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */ FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */ FDRIVE_DRV_NONE = 0x03 /* No drive connected */ +#ifdef VBOX + , FDRIVE_DRV_FAKE_15_6 = 0x0e /* Fake 15.6 MB drive. */ + , FDRIVE_DRV_FAKE_63_5 = 0x0f /* Fake 63.5 MB drive. */ +#endif } fdrive_type_t; -typedef enum fdrive_flags_t { - FDISK_DBL_SIDES = 0x01 -} fdrive_flags_t; +typedef uint8_t fdrive_flags_t; +#define FDISK_DBL_SIDES UINT8_C(0x01) typedef enum fdrive_rate_t { FDRIVE_RATE_500K = 0x00, /* 500 Kbps */ @@ -179,6 +176,7 @@ typedef struct fdrive_t { uint8_t head; uint8_t track; uint8_t sect; + uint8_t ltrk; /* Logical track */ /* Media */ fdrive_flags_t flags; uint8_t last_sect; /* Nb sector per track */ @@ -190,10 +188,43 @@ typedef struct fdrive_t { #define NUM_SIDES(drv) (drv->flags & FDISK_DBL_SIDES ? 2 : 1) -static void fd_init(fdrive_t *drv) +static void fd_init(fdrive_t *drv, bool fInit) { /* Drive */ +#ifndef VBOX drv->drive = FDRIVE_DRV_NONE; +#else /* VBOX */ + if (fInit) { + /* Fixate the drive type at init time if possible. */ + if (drv->pDrvBlock) { + PDMBLOCKTYPE enmType = drv->pDrvBlock->pfnGetType(drv->pDrvBlock); + switch (enmType) { + case PDMBLOCKTYPE_FLOPPY_360: + case PDMBLOCKTYPE_FLOPPY_1_20: + drv->drive = FDRIVE_DRV_120; + break; + case PDMBLOCKTYPE_FLOPPY_720: + case PDMBLOCKTYPE_FLOPPY_1_44: + drv->drive = FDRIVE_DRV_144; + break; + default: + AssertFailed(); + case PDMBLOCKTYPE_FLOPPY_2_88: + drv->drive = FDRIVE_DRV_288; + break; + case PDMBLOCKTYPE_FLOPPY_FAKE_15_6: + drv->drive = FDRIVE_DRV_FAKE_15_6; + break; + case PDMBLOCKTYPE_FLOPPY_FAKE_63_5: + drv->drive = FDRIVE_DRV_FAKE_63_5; + break; + } + } else { + drv->drive = FDRIVE_DRV_NONE; + } + } /* else: The BIOS (and others) get the drive type via the CMOS, so + don't change it after the VM has been constructed. */ +#endif /* VBOX */ drv->perpendicular = 0; /* Disk */ drv->last_sect = 0; @@ -233,7 +264,7 @@ static int fd_seek(fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect, drv->max_track, drv->last_sect); return 2; } - if (sect > drv->last_sect) { + if (sect > drv->last_sect || sect < 1) { FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", head, track, sect, 1, (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, @@ -256,6 +287,7 @@ static int fd_seek(fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect, drv->track = track; drv->sect = sect; } + drv->ltrk = drv->track; return ret; } @@ -266,6 +298,7 @@ static void fd_recalibrate(fdrive_t *drv) FLOPPY_DPRINTF("recalibrate\n"); drv->head = 0; drv->track = 0; + drv->ltrk = 0; drv->sect = 1; } @@ -273,13 +306,16 @@ static void fd_recalibrate(fdrive_t *drv) typedef struct fd_format_t { fdrive_type_t drive; fdisk_type_t disk; - uint8_t last_sect; - uint8_t max_track; - uint8_t max_head; + uint8_t last_sect; /**< Number of sectors. */ + uint8_t max_track; /**< Number of tracks. */ + uint8_t max_head; /**< Max head number. */ fdrive_rate_t rate; const char *str; } fd_format_t; +/* Note: Low-density disks (160K/180K/320K/360K) use 250 Kbps data rate + * in 40-track drives, but 300 Kbps in high-capacity 80-track drives. + */ static fd_format_t fd_formats[] = { /* First entry is default format */ /* 1.44 MB 3"1/2 floppy disks */ @@ -313,18 +349,69 @@ static fd_format_t fd_formats[] = { /* 720 kB 5"1/4 floppy disks */ { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 80, 1, FDRIVE_RATE_250K, "720 kB 5\"1/4", }, { FDRIVE_DRV_120, FDRIVE_DISK_288, 11, 80, 1, FDRIVE_RATE_250K, "880 kB 5\"1/4", }, - /* 360 kB 5"1/4 floppy disks */ + /* 360 kB 5"1/4 floppy disks (newer 9-sector formats) */ { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 1, FDRIVE_RATE_300K, "360 kB 5\"1/4", }, { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, FDRIVE_RATE_300K, "180 kB 5\"1/4", }, { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, FDRIVE_RATE_300K, "410 kB 5\"1/4", }, { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, FDRIVE_RATE_300K, "420 kB 5\"1/4", }, - /* 320 kB 5"1/4 floppy disks */ - { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, FDRIVE_RATE_250K, "320 kB 5\"1/4", }, - { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, FDRIVE_RATE_250K, "160 kB 5\"1/4", }, - /* 360 kB must match 5"1/4 better than 3"1/2... */ - { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 0, FDRIVE_RATE_250K, "360 kB 3\"1/2", }, + /* 320 kB 5"1/4 floppy disks (old 8-sector formats) */ + { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, FDRIVE_RATE_300K, "320 kB 5\"1/4", }, + { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, FDRIVE_RATE_300K, "160 kB 5\"1/4", }, + /* 1.2 MB and low density 3"1/2 floppy 'aliases' */ + { FDRIVE_DRV_144, FDRIVE_DISK_144, 15, 80, 1, FDRIVE_RATE_500K, " 1.2 MB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 40, 1, FDRIVE_RATE_300K, "360 kB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 40, 0, FDRIVE_RATE_300K, "180 kB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_720, 8, 40, 1, FDRIVE_RATE_300K, "320 kB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_720, 8, 40, 0, FDRIVE_RATE_300K, "160 kB 3\"1/2", }, +#ifdef VBOX /* For larger than real life floppy images (see DrvBlock.cpp). */ + /* 15.6 MB fake floppy disk (just need something big). */ + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_USER, 63, 255, 1, FDRIVE_RATE_1M, "15.6 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_288, 36, 80, 1, FDRIVE_RATE_1M, "2.88 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_288, 39, 80, 1, FDRIVE_RATE_1M, "3.12 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_288, 40, 80, 1, FDRIVE_RATE_1M, "3.2 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_288, 44, 80, 1, FDRIVE_RATE_1M, "3.52 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_288, 48, 80, 1, FDRIVE_RATE_1M, "3.84 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 18, 80, 1, FDRIVE_RATE_500K, "1.44 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 20, 80, 1, FDRIVE_RATE_500K, "1.6 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 21, 80, 1, FDRIVE_RATE_500K, "1.68 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 21, 82, 1, FDRIVE_RATE_500K, "1.72 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 21, 83, 1, FDRIVE_RATE_500K, "1.74 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 22, 80, 1, FDRIVE_RATE_500K, "1.76 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 23, 80, 1, FDRIVE_RATE_500K, "1.84 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 24, 80, 1, FDRIVE_RATE_500K, "1.92 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 9, 80, 1, FDRIVE_RATE_250K, "720 kB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 10, 80, 1, FDRIVE_RATE_250K, "800 kB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 10, 82, 1, FDRIVE_RATE_250K, "820 kB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 10, 83, 1, FDRIVE_RATE_250K, "830 kB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 13, 80, 1, FDRIVE_RATE_250K, "1.04 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 14, 80, 1, FDRIVE_RATE_250K, "1.12 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 9, 80, 0, FDRIVE_RATE_250K, "360 kB 3\"1/2", }, + /* 63.5 MB fake floppy disk (just need something big). */ + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_USER, 255, 255, 1, FDRIVE_RATE_1M, "63.5 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_USER, 63, 255, 1, FDRIVE_RATE_1M, "15.6 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_288, 36, 80, 1, FDRIVE_RATE_1M, "2.88 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_288, 39, 80, 1, FDRIVE_RATE_1M, "3.12 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_288, 40, 80, 1, FDRIVE_RATE_1M, "3.2 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_288, 44, 80, 1, FDRIVE_RATE_1M, "3.52 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_288, 48, 80, 1, FDRIVE_RATE_1M, "3.84 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 18, 80, 1, FDRIVE_RATE_500K, "1.44 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 20, 80, 1, FDRIVE_RATE_500K, "1.6 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 21, 80, 1, FDRIVE_RATE_500K, "1.68 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 21, 82, 1, FDRIVE_RATE_500K, "1.72 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 21, 83, 1, FDRIVE_RATE_500K, "1.74 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 22, 80, 1, FDRIVE_RATE_500K, "1.76 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 23, 80, 1, FDRIVE_RATE_500K, "1.84 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 24, 80, 1, FDRIVE_RATE_500K, "1.92 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 9, 80, 1, FDRIVE_RATE_250K, "720 kB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 10, 80, 1, FDRIVE_RATE_250K, "800 kB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 10, 82, 1, FDRIVE_RATE_250K, "820 kB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 10, 83, 1, FDRIVE_RATE_250K, "830 kB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 13, 80, 1, FDRIVE_RATE_250K, "1.04 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 14, 80, 1, FDRIVE_RATE_250K, "1.12 MB 3\"1/2", }, + { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 9, 80, 0, FDRIVE_RATE_250K, "360 kB 3\"1/2", }, +#endif /* end */ - { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, 0, NULL, }, + { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, (uint8_t)-1, (uint8_t)-1, 0, (fdrive_rate_t)0, NULL, }, }; /* Revalidate a disk drive after a disk change */ @@ -341,7 +428,7 @@ static void fd_revalidate(fdrive_t *drv) ro = bdrv_is_read_only(drv->bs); bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect); #else /* VBOX */ - if (drv->pDrvBlock + if ( drv->pDrvBlock && drv->pDrvMount && drv->pDrvMount->pfnIsMounted (drv->pDrvMount)) { ro = drv->pDrvBlock->pfnIsReadOnly (drv->pDrvBlock); @@ -450,7 +537,8 @@ enum { FD_DIR_READ = 1, FD_DIR_SCANE = 2, FD_DIR_SCANL = 3, - FD_DIR_SCANH = 4 + FD_DIR_SCANH = 4, + FD_DIR_FORMAT = 5 }; enum { @@ -523,10 +611,12 @@ enum { enum { FD_SR1_MA = 0x01, /* Missing address mark */ FD_SR1_NW = 0x02, /* Not writable */ + FD_SR1_ND = 0x04, /* No data */ FD_SR1_EC = 0x80 /* End of cylinder */ }; enum { + FD_SR2_MD = 0x01, /* Missing data address mark */ FD_SR2_SNS = 0x04, /* Scan not satisfied */ FD_SR2_SEH = 0x08 /* Scan equal hit */ }; @@ -668,7 +758,7 @@ struct fdctrl_t { static uint32_t fdctrl_read (void *opaque, uint32_t reg) { - fdctrl_t *fdctrl = opaque; + fdctrl_t *fdctrl = (fdctrl_t *)opaque; uint32_t retval; switch (reg) { @@ -704,7 +794,7 @@ static uint32_t fdctrl_read (void *opaque, uint32_t reg) static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) { - fdctrl_t *fdctrl = opaque; + fdctrl_t *fdctrl = (fdctrl_t *)opaque; FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); @@ -729,33 +819,6 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) } } -#ifdef VBOX -/** - * Called when a medium is mounted. - * - * @param pInterface Pointer to the interface structure - * containing the called function pointer. - */ -static DECLCALLBACK(void) fdMountNotify(PPDMIMOUNTNOTIFY pInterface) -{ - fdrive_t *drv = PDMIMOUNTNOTIFY_2_FDRIVE (pInterface); - LogFlow(("fdMountNotify:\n")); - fd_revalidate(drv); -} - -/** - * Called when a medium is unmounted. - * @param pInterface Pointer to the interface structure - * containing the called function pointer. - */ -static DECLCALLBACK(void) fdUnmountNotify(PPDMIMOUNTNOTIFY pInterface) -{ - fdrive_t *drv = PDMIMOUNTNOTIFY_2_FDRIVE (pInterface); - LogFlow(("fdUnmountNotify:\n")); - fd_revalidate(drv); -} -#endif - /* Change IRQ state */ static void fdctrl_reset_irq(fdctrl_t *fdctrl) { @@ -773,6 +836,7 @@ static void fdctrl_reset_irq(fdctrl_t *fdctrl) static void fdctrl_raise_irq(fdctrl_t *fdctrl, uint8_t status0) { if (!(fdctrl->sra & FD_SRA_INTPEND)) { + FLOPPY_DPRINTF("Raising interrupt...\n"); #ifdef VBOX PDMDevHlpISASetIrq (fdctrl->pDevIns, fdctrl->irq_lvl, 1); #else @@ -1038,7 +1102,12 @@ static uint32_t fdctrl_read_dir(fdctrl_t *fdctrl) uint32_t retval = 0; #ifdef VBOX - if (fdctrl_media_changed(get_cur_drv(fdctrl))) + /* The change line signal is reported by the currently selected + * drive. If the corresponding motor on bit is not set, the drive + * is *not* selected! + */ + if (fdctrl_media_changed(get_cur_drv(fdctrl)) + && (fdctrl->dor & (0x10 << fdctrl->cur_drv))) #else if (fdctrl_media_changed(drv0(fdctrl)) || fdctrl_media_changed(drv1(fdctrl)) @@ -1099,12 +1168,12 @@ static int fdctrl_seek_to_next_sect(fdctrl_t *fdctrl, fdrive_t *cur_drv) cur_drv->head = 1; } else { cur_drv->head = 0; - cur_drv->track++; + cur_drv->ltrk++; if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) return 0; } } else { - cur_drv->track++; + cur_drv->ltrk++; return 0; } FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n", @@ -1129,10 +1198,14 @@ static void fdctrl_stop_transfer(fdctrl_t *fdctrl, uint8_t status0, fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl); fdctrl->fifo[1] = status1; fdctrl->fifo[2] = status2; - fdctrl->fifo[3] = cur_drv->track; + fdctrl->fifo[3] = cur_drv->ltrk; fdctrl->fifo[4] = cur_drv->head; fdctrl->fifo[5] = cur_drv->sect; fdctrl->fifo[6] = FD_SECTOR_SC; + FLOPPY_DPRINTF("ST0:%02x ST1:%02x ST2:%02x C:%02x H:%02x R:%02x N:%02x\n", + fdctrl->fifo[0], fdctrl->fifo[1], fdctrl->fifo[2], fdctrl->fifo[3], + fdctrl->fifo[4], fdctrl->fifo[5], fdctrl->fifo[6]); + fdctrl->data_dir = FD_DIR_READ; if (!(fdctrl->msr & FD_MSR_NONDMA)) { #ifdef VBOX @@ -1161,6 +1234,10 @@ static void fdctrl_start_transfer(fdctrl_t *fdctrl, int direction) FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n", GET_CUR_DRV(fdctrl), kh, kt, ks, fd_sector_calc(kh, kt, ks, cur_drv->last_sect, NUM_SIDES(cur_drv))); + FLOPPY_DPRINTF("CMD:%02x SEL:%02x C:%02x H:%02x R:%02x N:%02x EOT:%02x GPL:%02x DTL:%02x\n", + fdctrl->fifo[0], fdctrl->fifo[1], fdctrl->fifo[2], + fdctrl->fifo[3], fdctrl->fifo[4], fdctrl->fifo[5], + fdctrl->fifo[6], fdctrl->fifo[7], fdctrl->fifo[8]); switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { case 2: /* sect too big */ @@ -1196,7 +1273,7 @@ static void fdctrl_start_transfer(fdctrl_t *fdctrl, int direction) if ((fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) { FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n", fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate); - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, FD_SR2_MD); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; @@ -1242,7 +1319,7 @@ static void fdctrl_start_transfer(fdctrl_t *fdctrl, int direction) if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL || direction == FD_DIR_SCANH) && dma_mode == 0) || (direction == FD_DIR_WRITE && dma_mode == 2) || - (direction == FD_DIR_READ && dma_mode == 1)) { + (direction == FD_DIR_READ && (dma_mode == 1 || dma_mode == 0))) { /* No access is allowed until DMA transfer has completed */ fdctrl->msr &= ~FD_MSR_RQM; /* Now, we just have to wait for the DMA controller to @@ -1270,6 +1347,110 @@ static void fdctrl_start_transfer(fdctrl_t *fdctrl, int direction) return; } +/* Prepare a format data transfer (either DMA or FIFO) */ +static void fdctrl_start_format(fdctrl_t *fdctrl) +{ + fdrive_t *cur_drv; + uint8_t ns, dp, kh, kt, ks; + + SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); + cur_drv = get_cur_drv(fdctrl); + kt = cur_drv->track; + kh = (fdctrl->fifo[1] & 0x04) >> 2; + ns = fdctrl->fifo[3]; + dp = fdctrl->fifo[5]; + ks = 1; + FLOPPY_DPRINTF("Start format at %d %d %02x, %d sect, pat %02x (%d)\n", + GET_CUR_DRV(fdctrl), kh, kt, ns, dp, + fd_sector_calc(kh, kt, ks, cur_drv->last_sect, NUM_SIDES(cur_drv))); + switch (fd_seek(cur_drv, kh, kt, ks, false)) { + case 2: + /* sect too big */ + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; + case 3: + /* track too big */ + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; + case 4: + /* No seek enabled */ + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; + case 1: + break; + default: + break; + } + /* It's not clear what should happen if the data rate does not match. */ +#if 0 + /* Check the data rate. If the programmed data rate does not match + * the currently inserted medium, the operation has to fail. + */ + if ((fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) { + FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n", + fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate); + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, FD_SR2_MD); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; + } +#endif + /* Set the FIFO state */ + fdctrl->data_dir = FD_DIR_FORMAT; + fdctrl->data_pos = 0; + fdctrl->msr |= FD_MSR_CMDBUSY; + fdctrl->data_state &= ~(FD_STATE_MULTI | FD_STATE_SEEK); + fdctrl->data_len = ns * 4; + fdctrl->eot = ns; + if (fdctrl->dor & FD_DOR_DMAEN) { + int dma_mode; + /* DMA transfer are enabled. Check if DMA channel is well programmed */ +#ifndef VBOX + dma_mode = DMA_get_channel_mode(fdctrl->dma_chann); +#else + dma_mode = PDMDevHlpDMAGetChannelMode (fdctrl->pDevIns, fdctrl->dma_chann); +#endif + dma_mode = (dma_mode >> 2) & 3; + FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n", + dma_mode, fdctrl->data_dir, + (128 << fdctrl->fifo[2]) * + (cur_drv->last_sect + 1), fdctrl->data_len); + if (fdctrl->data_dir == FD_DIR_FORMAT && dma_mode == 2) { + /* No access is allowed until DMA transfer has completed */ + fdctrl->msr &= ~FD_MSR_RQM; + /* Now, we just have to wait for the DMA controller to + * recall us... + */ +#ifndef VBOX + DMA_hold_DREQ(fdctrl->dma_chann); + DMA_schedule(fdctrl->dma_chann); +#else + PDMDevHlpDMASetDREQ (fdctrl->pDevIns, fdctrl->dma_chann, 1); + PDMDevHlpDMASchedule (fdctrl->pDevIns); +#endif + return; + } else { + FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, fdctrl->data_dir); + } + } + FLOPPY_DPRINTF("start non-DMA format\n"); + fdctrl->msr |= FD_MSR_NONDMA; + /* IO based transfer: calculate len */ + fdctrl_raise_irq(fdctrl, 0x00); + + return; +} + /* Prepare a transfer of deleted data */ static void fdctrl_start_transfer_del(fdctrl_t *fdctrl, int direction) { @@ -1335,13 +1516,14 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, fdrive_t *cur_drv; #ifdef VBOX int rc; - uint32_t len, start_pos, rel_pos; + uint32_t len = 0; + uint32_t start_pos, rel_pos; #else int len, start_pos, rel_pos; #endif uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00; - fdctrl = opaque; + fdctrl = (fdctrl_t *)opaque; if (fdctrl->msr & FD_MSR_RQM) { FLOPPY_DPRINTF("Not in DMA transfer mode !\n"); return 0; @@ -1362,9 +1544,27 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); else fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); - len = 0; + Assert(len == 0); goto transfer_error; } + +#ifdef VBOX + if (cur_drv->ro) + { + if (fdctrl->data_dir == FD_DIR_WRITE || fdctrl->data_dir == FD_DIR_FORMAT) + { + /* Handle readonly medium early, no need to do DMA, touch the + * LED or attempt any writes. A real floppy doesn't attempt + * to write to readonly media either. */ + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW, + 0x00); + Assert(len == 0); + goto transfer_error; + } + } +#endif + + rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) { len = dma_len - fdctrl->data_pos; @@ -1375,8 +1575,9 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, fdctrl->data_len, GET_CUR_DRV(fdctrl), cur_drv->head, cur_drv->track, cur_drv->sect, fd_sector(cur_drv), fd_sector(cur_drv) * FD_SECTOR_LEN); - if (fdctrl->data_dir != FD_DIR_WRITE || - len < FD_SECTOR_LEN || rel_pos != 0) { + if (fdctrl->data_dir != FD_DIR_FORMAT && + (fdctrl->data_dir != FD_DIR_WRITE || + len < FD_SECTOR_LEN || rel_pos != 0)) { /* READ & SCAN commands and realign to a sector for WRITE */ #ifdef VBOX rc = blk_read(cur_drv, fd_sector(cur_drv), fdctrl->fifo, 1); @@ -1414,16 +1615,6 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, case FD_DIR_WRITE: /* WRITE commands */ #ifdef VBOX - if (cur_drv->ro) - { - /* Handle readonly medium early, no need to do DMA, touch the - * LED or attempt any writes. A real floppy doesn't attempt - * to write to readonly media either. */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW, - 0x00); - goto transfer_error; - } - { uint32_t written; int rc2 = PDMDevHlpDMAReadMemory(fdctrl->pDevIns, nchan, @@ -1447,6 +1638,38 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, goto transfer_error; } break; +#ifdef VBOX + case FD_DIR_FORMAT: + /* FORMAT command */ + { + uint8_t eot = fdctrl->fifo[3]; + uint8_t filler = fdctrl->fifo[5]; + uint32_t written; + int sct; + int rc2 = PDMDevHlpDMAReadMemory(fdctrl->pDevIns, nchan, + fdctrl->fifo + rel_pos, + fdctrl->data_pos, + len, &written); + AssertMsgRC (rc2, ("DMAReadMemory -> %Rrc\n", rc2)); + + /* Fill the entire track with desired data pattern. */ + FLOPPY_DPRINTF("formatting track: %d sectors, pattern %02x\n", + eot, filler); + memset(fdctrl->fifo, filler, FD_SECTOR_LEN); + for (sct = 0; sct < eot; ++sct) + { + rc = blk_write(cur_drv, fd_sector(cur_drv), fdctrl->fifo, 1); + if (RT_FAILURE(rc)) + { + FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv)); + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); + goto transfer_error; + } + fdctrl_seek_to_next_sect(fdctrl, cur_drv); + } + } + break; +#endif default: /* SCAN commands */ { @@ -1736,6 +1959,8 @@ static void fdctrl_handle_readid(fdctrl_t *fdctrl, int direction) { fdrive_t *cur_drv = get_cur_drv(fdctrl); + FLOPPY_DPRINTF("CMD:%02x SEL:%02x\n", fdctrl->fifo[0], fdctrl->fifo[1]); + /* XXX: should set main status register to busy */ cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; #ifdef VBOX @@ -1749,30 +1974,33 @@ static void fdctrl_handle_readid(fdctrl_t *fdctrl, int direction) static void fdctrl_handle_format_track(fdctrl_t *fdctrl, int direction) { fdrive_t *cur_drv; + uint8_t ns, dp; SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); cur_drv = get_cur_drv(fdctrl); - fdctrl->data_state |= FD_STATE_FORMAT; - if (fdctrl->fifo[0] & 0x80) - fdctrl->data_state |= FD_STATE_MULTI; - else - fdctrl->data_state &= ~FD_STATE_MULTI; - fdctrl->data_state &= ~FD_STATE_SEEK; - cur_drv->bps = - fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2]; -#if 0 - cur_drv->last_sect = - cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] : - fdctrl->fifo[3] / 2; -#else - cur_drv->last_sect = fdctrl->fifo[3]; -#endif - /* TODO: implement format using DMA expected by the Bochs BIOS - * and Linux fdformat (read 3 bytes per sector via DMA and fill - * the sector with the specified fill byte + fdctrl->data_state &= ~(FD_STATE_MULTI | FD_STATE_SEEK); + ns = fdctrl->fifo[3]; + dp = fdctrl->fifo[5]; + + FLOPPY_DPRINTF("Format track %d at %d, %d sectors, filler %02x\n", + cur_drv->track, GET_CUR_DRV(fdctrl), ns, dp); + FLOPPY_DPRINTF("CMD:%02x SEL:%02x N:%02x SC:%02x GPL:%02x D:%02x\n", + fdctrl->fifo[0], fdctrl->fifo[1], fdctrl->fifo[2], + fdctrl->fifo[3], fdctrl->fifo[4], fdctrl->fifo[5]); + + /* Since we cannot actually format anything, we have to make sure that + * whatever new format the guest is trying to establish matches the + * existing format of the medium. */ - fdctrl->data_state &= ~FD_STATE_FORMAT; - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); + if (cur_drv->last_sect != ns || fdctrl->fifo[2] != 2) + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_NW, 0); + else + { + cur_drv->bps = fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2]; + cur_drv->last_sect = ns; + + fdctrl_start_format(fdctrl); + } } static void fdctrl_handle_specify(fdctrl_t *fdctrl, int direction) @@ -1806,19 +2034,25 @@ static void fdctrl_handle_sense_drive_status(fdctrl_t *fdctrl, int direction) static void fdctrl_handle_recalibrate(fdctrl_t *fdctrl, int direction) { fdrive_t *cur_drv; + uint8_t st0; SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); cur_drv = get_cur_drv(fdctrl); fd_recalibrate(cur_drv); fdctrl_reset_fifo(fdctrl); + st0 = FD_SR0_SEEK | GET_CUR_DRV(fdctrl); + /* No drive means no TRK0 signal. */ + if (cur_drv->drive == FDRIVE_DRV_NONE) + st0 |= FD_SR0_ABNTERM | FD_SR0_EQPMT; /* Raise Interrupt */ - fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); + fdctrl_raise_irq(fdctrl, st0); } static void fdctrl_handle_sense_interrupt_status(fdctrl_t *fdctrl, int direction) { fdrive_t *cur_drv = get_cur_drv(fdctrl); + FLOPPY_DPRINTF("CMD:%02x\n", fdctrl->fifo[0]); if(fdctrl->reset_sensei > 0) { fdctrl->fifo[0] = FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei; @@ -1829,11 +2063,14 @@ static void fdctrl_handle_sense_interrupt_status(fdctrl_t *fdctrl, int direction ASAP */ fdctrl->fifo[0] = FD_SR0_SEEK | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl); + /* Hack to preserve SR0 on equipment check failures (no drive). */ + if (fdctrl->status0 & FD_SR0_EQPMT) + fdctrl->fifo[0] = fdctrl->status0; } fdctrl->fifo[1] = cur_drv->track; fdctrl_set_fifo(fdctrl, 2, 0); - fdctrl_reset_irq(fdctrl); + FLOPPY_DPRINTF("ST0:%02x PCN:%02x\n", fdctrl->fifo[0], fdctrl->fifo[1]); fdctrl->status0 = FD_SR0_RDYCHG; } @@ -1841,6 +2078,9 @@ static void fdctrl_handle_seek(fdctrl_t *fdctrl, int direction) { fdrive_t *cur_drv; + FLOPPY_DPRINTF("CMD:%02x SEL:%02x NCN:%02x\n", fdctrl->fifo[0], + fdctrl->fifo[1], fdctrl->fifo[2]); + SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); cur_drv = get_cur_drv(fdctrl); fdctrl_reset_fifo(fdctrl); @@ -1849,8 +2089,10 @@ static void fdctrl_handle_seek(fdctrl_t *fdctrl, int direction) * there's a medium inserted or if it's banging the head against the drive. */ cur_drv->track = fdctrl->fifo[2]; + cur_drv->ltrk = cur_drv->track; + cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; /* Raise Interrupt */ - fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); + fdctrl_raise_irq(fdctrl, FD_SR0_SEEK | GET_CUR_DRV(fdctrl)); #else if (fdctrl->fifo[2] > cur_drv->max_track) { fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK); @@ -2031,6 +2273,7 @@ static void fdctrl_write_data(fdctrl_t *fdctrl, uint32_t value) } if (fdctrl->data_pos == 0) { /* Command */ + fdctrl_reset_irq(fdctrl); /* If pending from previous seek/recalibrate. */ pos = command_to_handler[value & 0xff]; FLOPPY_DPRINTF("%s command\n", handlers[pos].name); fdctrl->data_len = handlers[pos].parameters + 1; @@ -2056,7 +2299,7 @@ static void fdctrl_write_data(fdctrl_t *fdctrl, uint32_t value) static void fdctrl_result_timer(void *opaque) { - fdctrl_t *fdctrl = opaque; + fdctrl_t *fdctrl = (fdctrl_t *)opaque; fdrive_t *cur_drv = get_cur_drv(fdctrl); /* Pretend we are spinning. @@ -2068,115 +2311,134 @@ static void fdctrl_result_timer(void *opaque) } /* READ_ID can't automatically succeed! */ #ifdef VBOX - if (/* !cur_drv->fMediaPresent || */ - ((fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate)) { + if (!cur_drv->max_track) { + FLOPPY_DPRINTF("read id when no disk in drive\n"); + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA | FD_SR1_ND, FD_SR2_MD); + } else if ((fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) { FLOPPY_DPRINTF("read id rate mismatch (fdc=%d, media=%d)\n", fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate); - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA | FD_SR1_ND, FD_SR2_MD); + } else if (cur_drv->track >= cur_drv->max_track) { + FLOPPY_DPRINTF("read id past last track (%d >= %d)\n", + cur_drv->track, cur_drv->max_track); + cur_drv->ltrk = 0; + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA | FD_SR1_ND, FD_SR2_MD); } else #endif fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); } + #ifdef VBOX -static DECLCALLBACK(void) fdc_timer (PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser) + +/* -=-=-=-=-=-=-=-=- Timer Callback -=-=-=-=-=-=-=-=- */ + +/** + * @callback_method_impl{FNTMTIMERDEV} + */ +static DECLCALLBACK(void) fdcTimerCallback(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser) { fdctrl_t *fdctrl = (fdctrl_t *)pvUser; - fdctrl_result_timer (fdctrl); + fdctrl_result_timer(fdctrl); } -static DECLCALLBACK(int) fdc_io_write (PPDMDEVINS pDevIns, - void *pvUser, - RTIOPORT Port, - uint32_t u32, - unsigned cb) + +/* -=-=-=-=-=-=-=-=- I/O Port Access Handlers -=-=-=-=-=-=-=-=- */ + +/** + * @callback_method_impl{FNIOMIOPORTOUT} + */ +static DECLCALLBACK(int) fdcIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb) { - if (cb == 1) { + if (cb == 1) fdctrl_write (pvUser, Port & 7, u32); - } - else { + else AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32)); - } return VINF_SUCCESS; } -static DECLCALLBACK(int) fdc_io_read (PPDMDEVINS pDevIns, - void *pvUser, - RTIOPORT Port, - uint32_t *pu32, - unsigned cb) + +/** + * @callback_method_impl{FNIOMIOPORTOUT} + */ +static DECLCALLBACK(int) fdcIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb) { - if (cb == 1) { + if (cb == 1) + { *pu32 = fdctrl_read (pvUser, Port & 7); return VINF_SUCCESS; } - else { - return VERR_IOM_IOPORT_UNUSED; - } + return VERR_IOM_IOPORT_UNUSED; } -static DECLCALLBACK(int) fdcSaveExec (PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle) + +/* -=-=-=-=-=-=-=-=- Saved state -=-=-=-=-=-=-=-=- */ + +/** + * @callback_method_impl{FNSSMDEVSAVEEXEC} + */ +static DECLCALLBACK(int) fdcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) { - fdctrl_t *s = PDMINS_2_DATA (pDevIns, fdctrl_t *); - QEMUFile *f = pSSMHandle; + fdctrl_t *pThis = PDMINS_2_DATA(pDevIns, fdctrl_t *); unsigned int i; /* Save the FDC I/O registers... */ - SSMR3PutU8(pSSMHandle, s->sra); - SSMR3PutU8(pSSMHandle, s->srb); - SSMR3PutU8(pSSMHandle, s->dor); - SSMR3PutU8(pSSMHandle, s->tdr); - SSMR3PutU8(pSSMHandle, s->dsr); - SSMR3PutU8(pSSMHandle, s->msr); + SSMR3PutU8(pSSM, pThis->sra); + SSMR3PutU8(pSSM, pThis->srb); + SSMR3PutU8(pSSM, pThis->dor); + SSMR3PutU8(pSSM, pThis->tdr); + SSMR3PutU8(pSSM, pThis->dsr); + SSMR3PutU8(pSSM, pThis->msr); /* ...the status registers... */ - SSMR3PutU8(pSSMHandle, s->status0); - SSMR3PutU8(pSSMHandle, s->status1); - SSMR3PutU8(pSSMHandle, s->status2); + SSMR3PutU8(pSSM, pThis->status0); + SSMR3PutU8(pSSM, pThis->status1); + SSMR3PutU8(pSSM, pThis->status2); /* ...the command FIFO... */ - SSMR3PutU32(pSSMHandle, sizeof(s->fifo)); - SSMR3PutMem(pSSMHandle, &s->fifo, sizeof(s->fifo)); - SSMR3PutU32(pSSMHandle, s->data_pos); - SSMR3PutU32(pSSMHandle, s->data_len); - SSMR3PutU8(pSSMHandle, s->data_state); - SSMR3PutU8(pSSMHandle, s->data_dir); + SSMR3PutU32(pSSM, sizeof(pThis->fifo)); + SSMR3PutMem(pSSM, &pThis->fifo, sizeof(pThis->fifo)); + SSMR3PutU32(pSSM, pThis->data_pos); + SSMR3PutU32(pSSM, pThis->data_len); + SSMR3PutU8(pSSM, pThis->data_state); + SSMR3PutU8(pSSM, pThis->data_dir); /* ...and miscellaneous internal FDC state. */ - SSMR3PutU8(pSSMHandle, s->reset_sensei); - SSMR3PutU8(pSSMHandle, s->eot); - SSMR3PutU8(pSSMHandle, s->timer0); - SSMR3PutU8(pSSMHandle, s->timer1); - SSMR3PutU8(pSSMHandle, s->precomp_trk); - SSMR3PutU8(pSSMHandle, s->config); - SSMR3PutU8(pSSMHandle, s->lock); - SSMR3PutU8(pSSMHandle, s->pwrd); - SSMR3PutU8(pSSMHandle, s->version); + SSMR3PutU8(pSSM, pThis->reset_sensei); + SSMR3PutU8(pSSM, pThis->eot); + SSMR3PutU8(pSSM, pThis->timer0); + SSMR3PutU8(pSSM, pThis->timer1); + SSMR3PutU8(pSSM, pThis->precomp_trk); + SSMR3PutU8(pSSM, pThis->config); + SSMR3PutU8(pSSM, pThis->lock); + SSMR3PutU8(pSSM, pThis->pwrd); + SSMR3PutU8(pSSM, pThis->version); /* Save the number of drives and per-drive state. Note that the media * states will be updated in fd_revalidate() and need not be saved. */ - SSMR3PutU8(pSSMHandle, s->num_floppies); - Assert(RT_ELEMENTS(s->drives) == s->num_floppies); - for (i = 0; i < s->num_floppies; ++i) { - fdrive_t *d = &s->drives[i]; + SSMR3PutU8(pSSM, pThis->num_floppies); + Assert(RT_ELEMENTS(pThis->drives) == pThis->num_floppies); + for (i = 0; i < pThis->num_floppies; ++i) + { + fdrive_t *d = &pThis->drives[i]; - SSMR3PutMem(pSSMHandle, &d->Led, sizeof(d->Led)); - SSMR3PutU32(pSSMHandle, d->drive); - SSMR3PutU8(pSSMHandle, d->dsk_chg); - SSMR3PutU8(pSSMHandle, d->perpendicular); - SSMR3PutU8(pSSMHandle, d->head); - SSMR3PutU8(pSSMHandle, d->track); - SSMR3PutU8(pSSMHandle, d->sect); + SSMR3PutMem(pSSM, &d->Led, sizeof(d->Led)); + SSMR3PutU32(pSSM, d->drive); + SSMR3PutU8(pSSM, d->dsk_chg); + SSMR3PutU8(pSSM, d->perpendicular); + SSMR3PutU8(pSSM, d->head); + SSMR3PutU8(pSSM, d->track); + SSMR3PutU8(pSSM, d->sect); } - return TMR3TimerSave (s->result_timer, pSSMHandle); + return TMR3TimerSave (pThis->result_timer, pSSM); } -static DECLCALLBACK(int) fdcLoadExec (PPDMDEVINS pDevIns, - PSSMHANDLE pSSMHandle, - uint32_t uVersion, - uint32_t uPass) + +/** + * @callback_method_impl{FNSSMDEVLOADEXEC} + */ +static DECLCALLBACK(int) fdcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass) { - fdctrl_t *s = PDMINS_2_DATA (pDevIns, fdctrl_t *); - QEMUFile *f = pSSMHandle; + fdctrl_t *pThis = PDMINS_2_DATA(pDevIns, fdctrl_t *); unsigned int i; uint32_t val32; uint8_t val8; @@ -2193,149 +2455,171 @@ static DECLCALLBACK(int) fdcLoadExec (PPDMDEVINS pDevIns, if (uVersion == FDC_SAVESTATE_OLD) { /* First verify a few assumptions. */ - AssertMsgReturn(sizeof(s->fifo) == FD_SECTOR_LEN, + AssertMsgReturn(sizeof(pThis->fifo) == FD_SECTOR_LEN, ("The size of FIFO in saved state doesn't match!\n"), VERR_SSM_DATA_UNIT_FORMAT_CHANGED); - AssertMsgReturn(RT_ELEMENTS(s->drives) == 2, + AssertMsgReturn(RT_ELEMENTS(pThis->drives) == 2, ("The number of drives in old saved state doesn't match!\n"), VERR_SSM_DATA_UNIT_FORMAT_CHANGED); /* Now load the old state. */ - SSMR3GetU8(pSSMHandle, &s->version); + SSMR3GetU8(pSSM, &pThis->version); /* Toss IRQ level, DMA channel, I/O base, and state. */ - SSMR3GetU8(pSSMHandle, &val8); - SSMR3GetU8(pSSMHandle, &val8); - SSMR3GetU32(pSSMHandle, &val32); - SSMR3GetU8(pSSMHandle, &val8); + SSMR3GetU8(pSSM, &val8); + SSMR3GetU8(pSSM, &val8); + SSMR3GetU32(pSSM, &val32); + SSMR3GetU8(pSSM, &val8); /* Translate dma_en. */ - SSMR3GetU8(pSSMHandle, &val8); + SSMR3GetU8(pSSM, &val8); if (val8) - s->dor |= FD_DOR_DMAEN; - SSMR3GetU8(pSSMHandle, &s->cur_drv); + pThis->dor |= FD_DOR_DMAEN; + SSMR3GetU8(pSSM, &pThis->cur_drv); /* Translate bootsel. */ - SSMR3GetU8(pSSMHandle, &val8); - s->tdr |= val8 << 2; - SSMR3GetMem(pSSMHandle, &s->fifo, FD_SECTOR_LEN); - SSMR3GetU32(pSSMHandle, &s->data_pos); - SSMR3GetU32(pSSMHandle, &s->data_len); - SSMR3GetU8(pSSMHandle, &s->data_state); - SSMR3GetU8(pSSMHandle, &s->data_dir); - SSMR3GetU8(pSSMHandle, &s->status0); - SSMR3GetU8(pSSMHandle, &s->eot); - SSMR3GetU8(pSSMHandle, &s->timer0); - SSMR3GetU8(pSSMHandle, &s->timer1); - SSMR3GetU8(pSSMHandle, &s->precomp_trk); - SSMR3GetU8(pSSMHandle, &s->config); - SSMR3GetU8(pSSMHandle, &s->lock); - SSMR3GetU8(pSSMHandle, &s->pwrd); - - for (i = 0; i < 2; ++i) { - fdrive_t *d = &s->drives[i]; - - SSMR3GetMem (pSSMHandle, &d->Led, sizeof (d->Led)); - SSMR3GetU32(pSSMHandle, &val32); - d->drive = val32; - SSMR3GetU32(pSSMHandle, &val32); /* Toss drflags */ - SSMR3GetU8(pSSMHandle, &d->perpendicular); - SSMR3GetU8(pSSMHandle, &d->head); - SSMR3GetU8(pSSMHandle, &d->track); - SSMR3GetU8(pSSMHandle, &d->sect); - SSMR3GetU8(pSSMHandle, &val8); /* Toss dir, rw */ - SSMR3GetU8(pSSMHandle, &val8); - SSMR3GetU32(pSSMHandle, &val32); - d->flags = val32; - SSMR3GetU8(pSSMHandle, &d->last_sect); - SSMR3GetU8(pSSMHandle, &d->max_track); - SSMR3GetU16(pSSMHandle, &d->bps); - SSMR3GetU8(pSSMHandle, &d->ro); + SSMR3GetU8(pSSM, &val8); + pThis->tdr |= val8 << 2; + SSMR3GetMem(pSSM, &pThis->fifo, FD_SECTOR_LEN); + SSMR3GetU32(pSSM, &pThis->data_pos); + SSMR3GetU32(pSSM, &pThis->data_len); + SSMR3GetU8(pSSM, &pThis->data_state); + SSMR3GetU8(pSSM, &pThis->data_dir); + SSMR3GetU8(pSSM, &pThis->status0); + SSMR3GetU8(pSSM, &pThis->eot); + SSMR3GetU8(pSSM, &pThis->timer0); + SSMR3GetU8(pSSM, &pThis->timer1); + SSMR3GetU8(pSSM, &pThis->precomp_trk); + SSMR3GetU8(pSSM, &pThis->config); + SSMR3GetU8(pSSM, &pThis->lock); + SSMR3GetU8(pSSM, &pThis->pwrd); + + for (i = 0; i < 2; ++i) + { + fdrive_t *d = &pThis->drives[i]; + + SSMR3GetMem (pSSM, &d->Led, sizeof (d->Led)); + SSMR3GetU32(pSSM, &val32); + d->drive = (fdrive_type_t)val32; + SSMR3GetU32(pSSM, &val32); /* Toss drflags */ + SSMR3GetU8(pSSM, &d->perpendicular); + SSMR3GetU8(pSSM, &d->head); + SSMR3GetU8(pSSM, &d->track); + SSMR3GetU8(pSSM, &d->sect); + SSMR3GetU8(pSSM, &val8); /* Toss dir, rw */ + SSMR3GetU8(pSSM, &val8); + SSMR3GetU32(pSSM, &val32); + d->flags = (fdrive_flags_t)val32; + SSMR3GetU8(pSSM, &d->last_sect); + SSMR3GetU8(pSSM, &d->max_track); + SSMR3GetU16(pSSM, &d->bps); + SSMR3GetU8(pSSM, &d->ro); } } else /* New state - straightforward. */ { Assert(uVersion == FDC_SAVESTATE_CURRENT); /* Load the FDC I/O registers... */ - SSMR3GetU8(pSSMHandle, &s->sra); - SSMR3GetU8(pSSMHandle, &s->srb); - SSMR3GetU8(pSSMHandle, &s->dor); - SSMR3GetU8(pSSMHandle, &s->tdr); - SSMR3GetU8(pSSMHandle, &s->dsr); - SSMR3GetU8(pSSMHandle, &s->msr); + SSMR3GetU8(pSSM, &pThis->sra); + SSMR3GetU8(pSSM, &pThis->srb); + SSMR3GetU8(pSSM, &pThis->dor); + SSMR3GetU8(pSSM, &pThis->tdr); + SSMR3GetU8(pSSM, &pThis->dsr); + SSMR3GetU8(pSSM, &pThis->msr); /* ...the status registers... */ - SSMR3GetU8(pSSMHandle, &s->status0); - SSMR3GetU8(pSSMHandle, &s->status1); - SSMR3GetU8(pSSMHandle, &s->status2); + SSMR3GetU8(pSSM, &pThis->status0); + SSMR3GetU8(pSSM, &pThis->status1); + SSMR3GetU8(pSSM, &pThis->status2); /* ...the command FIFO, if the size matches... */ - SSMR3GetU32(pSSMHandle, &val32); - AssertMsgReturn(sizeof(s->fifo) == val32, + SSMR3GetU32(pSSM, &val32); + AssertMsgReturn(sizeof(pThis->fifo) == val32, ("The size of FIFO in saved state doesn't match!\n"), VERR_SSM_DATA_UNIT_FORMAT_CHANGED); - SSMR3GetMem(pSSMHandle, &s->fifo, sizeof(s->fifo)); - SSMR3GetU32(pSSMHandle, &s->data_pos); - SSMR3GetU32(pSSMHandle, &s->data_len); - SSMR3GetU8(pSSMHandle, &s->data_state); - SSMR3GetU8(pSSMHandle, &s->data_dir); + SSMR3GetMem(pSSM, &pThis->fifo, sizeof(pThis->fifo)); + SSMR3GetU32(pSSM, &pThis->data_pos); + SSMR3GetU32(pSSM, &pThis->data_len); + SSMR3GetU8(pSSM, &pThis->data_state); + SSMR3GetU8(pSSM, &pThis->data_dir); /* ...and miscellaneous internal FDC state. */ - SSMR3GetU8(pSSMHandle, &s->reset_sensei); - SSMR3GetU8(pSSMHandle, &s->eot); - SSMR3GetU8(pSSMHandle, &s->timer0); - SSMR3GetU8(pSSMHandle, &s->timer1); - SSMR3GetU8(pSSMHandle, &s->precomp_trk); - SSMR3GetU8(pSSMHandle, &s->config); - SSMR3GetU8(pSSMHandle, &s->lock); - SSMR3GetU8(pSSMHandle, &s->pwrd); - SSMR3GetU8(pSSMHandle, &s->version); + SSMR3GetU8(pSSM, &pThis->reset_sensei); + SSMR3GetU8(pSSM, &pThis->eot); + SSMR3GetU8(pSSM, &pThis->timer0); + SSMR3GetU8(pSSM, &pThis->timer1); + SSMR3GetU8(pSSM, &pThis->precomp_trk); + SSMR3GetU8(pSSM, &pThis->config); + SSMR3GetU8(pSSM, &pThis->lock); + SSMR3GetU8(pSSM, &pThis->pwrd); + SSMR3GetU8(pSSM, &pThis->version); /* Validate the number of drives. */ - SSMR3GetU8(pSSMHandle, &s->num_floppies); - AssertMsgReturn(RT_ELEMENTS(s->drives) == s->num_floppies, + SSMR3GetU8(pSSM, &pThis->num_floppies); + AssertMsgReturn(RT_ELEMENTS(pThis->drives) == pThis->num_floppies, ("The number of drives in saved state doesn't match!\n"), VERR_SSM_DATA_UNIT_FORMAT_CHANGED); /* Load the per-drive state. */ - for (i = 0; i < s->num_floppies; ++i) { - fdrive_t *d = &s->drives[i]; - - SSMR3GetMem(pSSMHandle, &d->Led, sizeof(d->Led)); - SSMR3GetU32(pSSMHandle, &val32); - d->drive = val32; - SSMR3GetU8(pSSMHandle, &d->dsk_chg); - SSMR3GetU8(pSSMHandle, &d->perpendicular); - SSMR3GetU8(pSSMHandle, &d->head); - SSMR3GetU8(pSSMHandle, &d->track); - SSMR3GetU8(pSSMHandle, &d->sect); + for (i = 0; i < pThis->num_floppies; ++i) + { + fdrive_t *d = &pThis->drives[i]; + + SSMR3GetMem(pSSM, &d->Led, sizeof(d->Led)); + SSMR3GetU32(pSSM, &val32); + d->drive = (fdrive_type_t)val32; + SSMR3GetU8(pSSM, &d->dsk_chg); + SSMR3GetU8(pSSM, &d->perpendicular); + SSMR3GetU8(pSSM, &d->head); + SSMR3GetU8(pSSM, &d->track); + SSMR3GetU8(pSSM, &d->sect); } } - return TMR3TimerLoad (s->result_timer, pSSMHandle); + return TMR3TimerLoad (pThis->result_timer, pSSM); } + +/* -=-=-=-=-=-=-=-=- Drive level interfaces -=-=-=-=-=-=-=-=- */ + +/** + * @interface_method_impl{PDMIMOUNTNOTIFY,pfnMountNotify} + */ +static DECLCALLBACK(void) fdMountNotify(PPDMIMOUNTNOTIFY pInterface) +{ + fdrive_t *pDrv = RT_FROM_MEMBER(pInterface, fdrive_t, IMountNotify); + LogFlow(("fdMountNotify:\n")); + fd_revalidate(pDrv); +} + + +/** + * @interface_method_impl{PDMIMOUNTNOTIFY,pfnUnmountNotify} + */ +static DECLCALLBACK(void) fdUnmountNotify(PPDMIMOUNTNOTIFY pInterface) +{ + fdrive_t *pDrv = RT_FROM_MEMBER(pInterface, fdrive_t, IMountNotify); + LogFlow(("fdUnmountNotify:\n")); + fd_revalidate(pDrv); +} + + /** * @interface_method_impl{PDMIBASE,pfnQueryInterface} */ static DECLCALLBACK(void *) fdQueryInterface (PPDMIBASE pInterface, const char *pszIID) { - fdrive_t *pDrive = PDMIBASE_2_FDRIVE(pInterface); + fdrive_t *pDrv = RT_FROM_MEMBER(pInterface, fdrive_t, IBase); - PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrive->IBase); - PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pDrive->IPort); - PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pDrive->IMountNotify); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrv->IBase); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pDrv->IPort); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pDrv->IMountNotify); return NULL; } + +/* -=-=-=-=-=-=-=-=- Controller level interfaces -=-=-=-=-=-=-=-=- */ + /** - * Gets the pointer to the status LED of a unit. - * - * @returns VBox status code. - * @param pInterface Pointer to the interface structure containing the called function pointer. - * @param iLUN The unit which status LED we desire. - * @param ppLed Where to store the LED pointer. + * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed} */ -static DECLCALLBACK(int) fdcStatusQueryStatusLed (PPDMILEDPORTS pInterface, - unsigned iLUN, - PPDMLED *ppLed) -{ - fdctrl_t *fdctrl = (fdctrl_t *) - ((uintptr_t )pInterface - RT_OFFSETOF (fdctrl_t, ILeds)); - if (iLUN < RT_ELEMENTS(fdctrl->drives)) { - *ppLed = &fdctrl->drives[iLUN].Led; +static DECLCALLBACK(int) fdcStatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed) +{ + fdctrl_t *pThis = RT_FROM_MEMBER (pInterface, fdctrl_t, ILeds); + if (iLUN < RT_ELEMENTS(pThis->drives)) { + *ppLed = &pThis->drives[iLUN].Led; Assert ((*ppLed)->u32Magic == PDMLED_MAGIC); return VINF_SUCCESS; } @@ -2362,16 +2646,17 @@ static DECLCALLBACK(void *) fdcStatusQueryInterface(PPDMIBASE pInterface, const * @returns VBox status code. * @param drv The drive in question. * @param pDevIns The driver instance. + * @param fInit Set if we're at init time and can change the drive type. */ -static int fdConfig (fdrive_t *drv, PPDMDEVINS pDevIns) +static int fdConfig(fdrive_t *drv, PPDMDEVINS pDevIns, bool fInit) { - static const char *descs[] = {"Floppy Drive A:", "Floppy Drive B"}; + static const char * const s_apszDesc[] = {"Floppy Drive A:", "Floppy Drive B"}; int rc; /* * Reset the LED just to be on the safe side. */ - Assert (RT_ELEMENTS(descs) > drv->iLUN); + Assert (RT_ELEMENTS(s_apszDesc) > drv->iLUN); Assert (drv->Led.u32Magic == PDMLED_MAGIC); drv->Led.Actual.u32 = 0; drv->Led.Asserted.u32 = 0; @@ -2379,7 +2664,7 @@ static int fdConfig (fdrive_t *drv, PPDMDEVINS pDevIns) /* * Try attach the block device and get the interfaces. */ - rc = PDMDevHlpDriverAttach (pDevIns, drv->iLUN, &drv->IBase, &drv->pDrvBase, descs[drv->iLUN]); + rc = PDMDevHlpDriverAttach (pDevIns, drv->iLUN, &drv->IBase, &drv->pDrvBase, s_apszDesc[drv->iLUN]); if (RT_SUCCESS (rc)) { drv->pDrvBlock = PDMIBASE_QUERY_INTERFACE(drv->pDrvBase, PDMIBLOCK); if (drv->pDrvBlock) { @@ -2387,7 +2672,7 @@ static int fdConfig (fdrive_t *drv, PPDMDEVINS pDevIns) if (drv->pDrvBlockBios) { drv->pDrvMount = PDMIBASE_QUERY_INTERFACE(drv->pDrvBase, PDMIMOUNT); if (drv->pDrvMount) { - fd_init(drv); + fd_init(drv, fInit); } else { AssertMsgFailed (("Configuration error: LUN#%d without mountable interface!\n", drv->iLUN)); rc = VERR_PDM_MISSING_INTERFACE; @@ -2431,18 +2716,13 @@ static int fdConfig (fdrive_t *drv, PPDMDEVINS pDevIns) /** - * Attach command. + * @interface_method_impl{PDMDEVREG,pfnAttach} * * This is called when we change block driver for a floppy drive. - * - * @returns VBox status code. - * @param pDevIns The device instance. - * @param iLUN The logical unit which is being detached. */ -static DECLCALLBACK(int) fdcAttach (PPDMDEVINS pDevIns, - unsigned iLUN, uint32_t fFlags) +static DECLCALLBACK(int) fdcAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) { - fdctrl_t *fdctrl = PDMINS_2_DATA (pDevIns, fdctrl_t *); + fdctrl_t *fdctrl = PDMINS_2_DATA(pDevIns, fdctrl_t *); fdrive_t *drv; int rc; LogFlow (("ideDetach: iLUN=%u\n", iLUN)); @@ -2471,7 +2751,7 @@ static DECLCALLBACK(int) fdcAttach (PPDMDEVINS pDevIns, AssertRelease (!drv->pDrvBlockBios); AssertRelease (!drv->pDrvMount); - rc = fdConfig (drv, pDevIns); + rc = fdConfig (drv, pDevIns, false /*fInit*/); AssertMsg (rc != VERR_PDM_NO_ATTACHED_DRIVER, ("Configuration error: failed to configure drive %d, rc=%Rrc\n", rc)); if (RT_SUCCESS(rc)) { @@ -2484,69 +2764,62 @@ static DECLCALLBACK(int) fdcAttach (PPDMDEVINS pDevIns, /** - * Detach notification. + * @interface_method_impl{PDMDEVREG,pfnDetach} * * The floppy drive has been temporarily 'unplugged'. - * - * @param pDevIns The device instance. - * @param iLUN The logical unit which is being detached. */ -static DECLCALLBACK(void) fdcDetach (PPDMDEVINS pDevIns, - unsigned iLUN, uint32_t fFlags) +static DECLCALLBACK(void) fdcDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) { - fdctrl_t *fdctrl = PDMINS_2_DATA (pDevIns, fdctrl_t *); + fdctrl_t *pThis = PDMINS_2_DATA(pDevIns, fdctrl_t *); LogFlow (("ideDetach: iLUN=%u\n", iLUN)); - switch (iLUN) { - case 0: - case 1: { - fdrive_t *drv = &fdctrl->drives[iLUN]; - drv->pDrvBase = NULL; - drv->pDrvBlock = NULL; - drv->pDrvBlockBios = NULL; - drv->pDrvMount = NULL; - break; - } + switch (iLUN) + { + case 0: + case 1: + { + fdrive_t *drv = &pThis->drives[iLUN]; + drv->pDrvBase = NULL; + drv->pDrvBlock = NULL; + drv->pDrvBlockBios = NULL; + drv->pDrvMount = NULL; + break; + } - default: - AssertMsgFailed (("Cannot detach LUN#%d!\n", iLUN)); - break; + default: + AssertMsgFailed(("Cannot detach LUN#%d!\n", iLUN)); + break; } } /** - * Handle reset. + * @interface_method_impl{PDMDEVREG,pfnReset} * * I haven't check the specs on what's supposed to happen on reset, but we * should get any 'FATAL: floppy recal:f07 ctrl not ready' when resetting * at wrong time like we do if this was all void. - * - * @param pDevIns The device instance. */ -static DECLCALLBACK(void) fdcReset (PPDMDEVINS pDevIns) +static DECLCALLBACK(void) fdcReset(PPDMDEVINS pDevIns) { - fdctrl_t *fdctrl = PDMINS_2_DATA (pDevIns, fdctrl_t *); + fdctrl_t *pThis = PDMINS_2_DATA (pDevIns, fdctrl_t *); unsigned i; LogFlow (("fdcReset:\n")); - fdctrl_reset(fdctrl, 0); + fdctrl_reset(pThis, 0); - for (i = 0; i < RT_ELEMENTS(fdctrl->drives); i++) { - fd_revalidate(&fdctrl->drives[i]); - } + for (i = 0; i < RT_ELEMENTS(pThis->drives); i++) + fd_revalidate(&pThis->drives[i]); } /** * @interface_method_impl{PDMDEVREG,pfnConstruct} */ -static DECLCALLBACK(int) fdcConstruct (PPDMDEVINS pDevIns, - int iInstance, - PCFGMNODE pCfg) +static DECLCALLBACK(int) fdcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg) { + fdctrl_t *pThis = PDMINS_2_DATA(pDevIns, fdctrl_t *); int rc; - fdctrl_t *fdctrl = PDMINS_2_DATA(pDevIns, fdctrl_t*); unsigned i, j; int ii; bool mem_mapped; @@ -2566,154 +2839,139 @@ static DECLCALLBACK(int) fdcConstruct (PPDMDEVINS pDevIns, /* * Read the configuration. */ - rc = CFGMR3QueryU8 (pCfg, "IRQ", &irq_lvl); - if (rc == VERR_CFGM_VALUE_NOT_FOUND) - irq_lvl = 6; - else if (RT_FAILURE (rc)) { - AssertMsgFailed (("Configuration error: Failed to read U8 IRQ, rc=%Rrc\n", rc)); - return rc; - } + rc = CFGMR3QueryU8Def(pCfg, "IRQ", &irq_lvl, 6); + AssertMsgRCReturn(rc, ("Configuration error: Failed to read U8 IRQ, rc=%Rrc\n", rc), rc); - rc = CFGMR3QueryU8 (pCfg, "DMA", &dma_chann); - if (rc == VERR_CFGM_VALUE_NOT_FOUND) - dma_chann = 2; - else if (RT_FAILURE (rc)) { - AssertMsgFailed (("Configuration error: Failed to read U8 DMA, rc=%Rrc\n", rc)); - return rc; - } + rc = CFGMR3QueryU8Def(pCfg, "DMA", &dma_chann, 2); + AssertMsgRCReturn(rc, ("Configuration error: Failed to read U8 DMA, rc=%Rrc\n", rc), rc); - rc = CFGMR3QueryU16 (pCfg, "IOBase", &io_base); - if (rc == VERR_CFGM_VALUE_NOT_FOUND) - io_base = 0x3f0; - else if (RT_FAILURE (rc)) { - AssertMsgFailed (("Configuration error: Failed to read U16 IOBase, rc=%Rrc\n", rc)); - return rc; - } + rc = CFGMR3QueryU16Def(pCfg, "IOBase", &io_base, 0x3f0); + AssertMsgRCReturn(rc, ("Configuration error: Failed to read U16 IOBase, rc=%Rrc\n", rc), rc); - rc = CFGMR3QueryBool (pCfg, "MemMapped", &mem_mapped); - if (rc == VERR_CFGM_VALUE_NOT_FOUND) - mem_mapped = false; - else if (RT_FAILURE (rc)) { - AssertMsgFailed (("Configuration error: Failed to read bool value MemMapped rc=%Rrc\n", rc)); - return rc; - } + rc = CFGMR3QueryBoolDef(pCfg, "MemMapped", &mem_mapped, false); + AssertMsgRCReturn(rc, ("Configuration error: Failed to read bool value MemMapped rc=%Rrc\n", rc), rc); /* * Initialize data. */ LogFlow(("fdcConstruct: irq_lvl=%d dma_chann=%d io_base=%#x\n", irq_lvl, dma_chann, io_base)); - fdctrl->pDevIns = pDevIns; - fdctrl->version = 0x90; /* Intel 82078 controller */ - fdctrl->irq_lvl = irq_lvl; - fdctrl->dma_chann = dma_chann; - fdctrl->io_base = io_base; - fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */ - fdctrl->num_floppies = MAX_FD; + pThis->pDevIns = pDevIns; + pThis->version = 0x90; /* Intel 82078 controller */ + pThis->irq_lvl = irq_lvl; + pThis->dma_chann = dma_chann; + pThis->io_base = io_base; + pThis->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */ + pThis->num_floppies = MAX_FD; /* Fill 'command_to_handler' lookup table */ - for (ii = RT_ELEMENTS(handlers) - 1; ii >= 0; ii--) { - for (j = 0; j < sizeof(command_to_handler); j++) { - if ((j & handlers[ii].mask) == handlers[ii].value) { + for (ii = RT_ELEMENTS(handlers) - 1; ii >= 0; ii--) + for (j = 0; j < sizeof(command_to_handler); j++) + if ((j & handlers[ii].mask) == handlers[ii].value) command_to_handler[j] = ii; - } - } - } - fdctrl->IBaseStatus.pfnQueryInterface = fdcStatusQueryInterface; - fdctrl->ILeds.pfnQueryStatusLed = fdcStatusQueryStatusLed; + pThis->IBaseStatus.pfnQueryInterface = fdcStatusQueryInterface; + pThis->ILeds.pfnQueryStatusLed = fdcStatusQueryStatusLed; - for (i = 0; i < RT_ELEMENTS(fdctrl->drives); ++i) { - fdrive_t *drv = &fdctrl->drives[i]; + for (i = 0; i < RT_ELEMENTS(pThis->drives); ++i) + { + fdrive_t *pDrv = &pThis->drives[i]; - drv->drive = FDRIVE_DRV_NONE; - drv->iLUN = i; + pDrv->drive = FDRIVE_DRV_NONE; + pDrv->iLUN = i; - drv->IBase.pfnQueryInterface = fdQueryInterface; - drv->IMountNotify.pfnMountNotify = fdMountNotify; - drv->IMountNotify.pfnUnmountNotify = fdUnmountNotify; - drv->Led.u32Magic = PDMLED_MAGIC; + pDrv->IBase.pfnQueryInterface = fdQueryInterface; + pDrv->IMountNotify.pfnMountNotify = fdMountNotify; + pDrv->IMountNotify.pfnUnmountNotify = fdUnmountNotify; + pDrv->Led.u32Magic = PDMLED_MAGIC; } /* * Create the FDC timer. */ - rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, fdc_timer, fdctrl, - TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "FDC Timer", &fdctrl->result_timer); - if (RT_FAILURE (rc)) + rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, fdcTimerCallback, pThis, + TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "FDC Timer", &pThis->result_timer); + if (RT_FAILURE(rc)) return rc; /* * Register DMA channel. */ - if (fdctrl->dma_chann != 0xff) { - rc = PDMDevHlpDMARegister (pDevIns, dma_chann, &fdctrl_transfer_handler, fdctrl); - if (RT_FAILURE (rc)) + if (pThis->dma_chann != 0xff) + { + rc = PDMDevHlpDMARegister(pDevIns, dma_chann, &fdctrl_transfer_handler, pThis); + if (RT_FAILURE(rc)) return rc; } /* * IO / MMIO. */ - if (mem_mapped) { - AssertMsgFailed (("Memory mapped floppy not support by now\n")); + if (mem_mapped) + { + AssertMsgFailed(("Memory mapped floppy not support by now\n")); return VERR_NOT_SUPPORTED; #if 0 FLOPPY_ERROR("memory mapped floppy not supported by now !\n"); io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write); cpu_register_physical_memory(base, 0x08, io_mem); #endif - } else { - rc = PDMDevHlpIOPortRegister (pDevIns, io_base + 0x1, 5, fdctrl, - fdc_io_write, fdc_io_read, NULL, NULL, "FDC#1"); - if (RT_FAILURE (rc)) + } + else + { + rc = PDMDevHlpIOPortRegister(pDevIns, io_base + 0x1, 5, pThis, + fdcIoPortWrite, fdcIoPortRead, NULL, NULL, "FDC#1"); + if (RT_FAILURE(rc)) return rc; - rc = PDMDevHlpIOPortRegister (pDevIns, io_base + 0x7, 1, fdctrl, - fdc_io_write, fdc_io_read, NULL, NULL, "FDC#2"); - if (RT_FAILURE (rc)) + rc = PDMDevHlpIOPortRegister(pDevIns, io_base + 0x7, 1, pThis, + fdcIoPortWrite, fdcIoPortRead, NULL, NULL, "FDC#2"); + if (RT_FAILURE(rc)) return rc; } /* * Register the saved state data unit. */ - rc = PDMDevHlpSSMRegister (pDevIns, FDC_SAVESTATE_CURRENT, sizeof(*fdctrl), fdcSaveExec, fdcLoadExec); + rc = PDMDevHlpSSMRegister(pDevIns, FDC_SAVESTATE_CURRENT, sizeof(*pThis), fdcSaveExec, fdcLoadExec); if (RT_FAILURE(rc)) return rc; /* * Attach the status port (optional). */ - rc = PDMDevHlpDriverAttach (pDevIns, PDM_STATUS_LUN, &fdctrl->IBaseStatus, &pBase, "Status Port"); - if (RT_SUCCESS (rc)) { - fdctrl->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS); - } else if (rc != VERR_PDM_NO_ATTACHED_DRIVER) { - AssertMsgFailed (("Failed to attach to status driver. rc=%Rrc\n", - rc)); + rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBaseStatus, &pBase, "Status Port"); + if (RT_SUCCESS (rc)) + pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS); + else if (rc != VERR_PDM_NO_ATTACHED_DRIVER) + { + AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc)); return rc; } /* * Initialize drives. */ - for (i = 0; i < RT_ELEMENTS(fdctrl->drives); i++) { - fdrive_t *drv = &fdctrl->drives[i]; - rc = fdConfig (drv, pDevIns); - if ( RT_FAILURE (rc) - && rc != VERR_PDM_NO_ATTACHED_DRIVER) { - AssertMsgFailed (("Configuration error: failed to configure drive %d, rc=%Rrc\n", rc)); + for (i = 0; i < RT_ELEMENTS(pThis->drives); i++) + { + fdrive_t *pDrv = &pThis->drives[i]; + rc = fdConfig(pDrv, pDevIns, true /*fInit*/); + if ( RT_FAILURE(rc) + && rc != VERR_PDM_NO_ATTACHED_DRIVER) + { + AssertMsgFailed(("Configuration error: failed to configure drive %d, rc=%Rrc\n", rc)); return rc; } } - fdctrl_reset(fdctrl, 0); + fdctrl_reset(pThis, 0); - for (i = 0; i < RT_ELEMENTS(fdctrl->drives); i++) - fd_revalidate(&fdctrl->drives[i]); + for (i = 0; i < RT_ELEMENTS(pThis->drives); i++) + fd_revalidate(&pThis->drives[i]); return VINF_SUCCESS; } + /** * The device registration structure. */ @@ -2743,7 +3001,7 @@ const PDMDEVREG g_DeviceFloppyController = NULL, /* pfnRelocate */ NULL, - /* pfnIOCtl */ + /* pfnMemSetup */ NULL, /* pfnPowerOn */ NULL, diff --git a/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp b/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp index a8bce58e..6511ef7c 100644 --- a/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp +++ b/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp @@ -1,10 +1,10 @@ /* $Id: DevLsiLogicSCSI.cpp $ */ /** @file - * VBox storage devices: LsiLogic LSI53c1030 SCSI controller. + * DevLsiLogicSCSI - LsiLogic LSI53c1030 SCSI controller. */ /* - * Copyright (C) 2006-2012 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; @@ -15,15 +15,20 @@ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ -//#define DEBUG +/******************************************************************************* +* Header Files * +*******************************************************************************/ #define LOG_GROUP LOG_GROUP_DEV_LSILOGICSCSI #include <VBox/vmm/pdmdev.h> #include <VBox/vmm/pdmqueue.h> +#include <VBox/vmm/pdmthread.h> #include <VBox/vmm/pdmcritsect.h> #include <VBox/scsi.h> +#include <VBox/sup.h> #include <iprt/assert.h> #include <iprt/asm.h> #include <iprt/string.h> +#include <iprt/list.h> #ifdef IN_RING3 # include <iprt/memcache.h> # include <iprt/mem.h> @@ -37,17 +42,54 @@ #include "VBoxDD.h" + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ /** The current saved state version. */ -#define LSILOGIC_SAVED_STATE_VERSION 3 +#define LSILOGIC_SAVED_STATE_VERSION 5 +/** The saved state version used by VirtualBox before the diagnostic + * memory access was implemented. */ +#define LSILOGIC_SAVED_STATE_VERSION_PRE_DIAG_MEM 4 +/** The saved state version used by VirtualBox before the doorbell status flag + * was changed from bool to a 32bit enum. */ +#define LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL 3 /** The saved state version used by VirtualBox before SAS support was added. */ -#define LSILOGIC_SAVED_STATE_VERSION_PRE_SAS 2 +#define LSILOGIC_SAVED_STATE_VERSION_PRE_SAS 2 /** The saved state version used by VirtualBox 3.0 and earlier. It does not * include the device config part. */ -#define LSILOGIC_SAVED_STATE_VERSION_VBOX_30 1 +#define LSILOGIC_SAVED_STATE_VERSION_VBOX_30 1 /** Maximum number of entries in the release log. */ #define MAX_REL_LOG_ERRORS 1024 +#define LSILOGIC_RTGCPHYS_FROM_U32(Hi, Lo) ( (RTGCPHYS)RT_MAKE_U64(Lo, Hi) ) + +/** Upper number a buffer is freed if it was too big before. */ +#define LSILOGIC_MAX_ALLOC_TOO_MUCH 20 + +/** Maximum size of the memory regions (prevents teh guest from DOSing the host by + * allocating loadds of memory). */ +#define LSILOGIC_MEMORY_REGIONS_MAX (_1M) + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ + +/** + * I/O buffer copy worker. + * + * @returns nothing. + * @param pDevIns Device instance data. + * @param GCPhysIoBuf Guest physical address of the I/O buffer. + * @param pvBuf R3 buffer pointer. + * @param cbCopy How much to copy. + */ +typedef DECLCALLBACK(void) FNLSILOGICIOBUFCOPY(PPDMDEVINS pDevIns, RTGCPHYS GCPhysIoBuf, + void *pvBuf, size_t cbCopy); +/** Pointer to a I/O buffer copy worker. */ +typedef FNLSILOGICIOBUFCOPY *PFNLSILOGICIOBUFCOPY; + /** * Reply data. */ @@ -61,7 +103,26 @@ typedef struct LSILOGICSCSIREPLY uint32_t cbReply; /** Different views to the reply depending on the request type. */ MptReplyUnion Reply; -} LSILOGICSCSIREPLY, *PLSILOGICSCSIREPLY; +} LSILOGICSCSIREPLY; +/** Pointer to reply data. */ +typedef LSILOGICSCSIREPLY *PLSILOGICSCSIREPLY; + +/** + * Memory region of the IOC. + */ +typedef struct LSILOGICMEMREGN +{ + /** List node. */ + RTLISTNODE NodeList; + /** 32bit address the region starts to describe. */ + uint32_t u32AddrStart; + /** 32bit address the region ends (inclusive). */ + uint32_t u32AddrEnd; + /** Data for this region - variable. */ + uint32_t au32Data[1]; +} LSILOGICMEMREGN; +/** Pointer to a memory region. */ +typedef LSILOGICMEMREGN *PLSILOGICMEMREGN; /** * State of a device attached to the buslogic host adapter. @@ -76,7 +137,7 @@ typedef struct LSILOGICDEVICE R3PTRTYPE(struct LSILOGICSCSI *) pLsiLogicR3; /** LUN of the device. */ - RTUINT iLUN; + uint32_t iLUN; /** Number of outstanding tasks on the port. */ volatile uint32_t cOutstandingRequests; @@ -97,14 +158,15 @@ typedef struct LSILOGICDEVICE /** The status LED state for this device. */ PDMLED Led; -} LSILOGICDEVICE, *PLSILOGICDEVICE; +} LSILOGICDEVICE; +/** Pointer to a device state. */ +typedef LSILOGICDEVICE *PLSILOGICDEVICE; /** Pointer to a task state. */ -typedef struct LSILOGICTASKSTATE *PLSILOGICTASKSTATE; +typedef struct LSILOGICREQ *PLSILOGICREQ; /** - * Device instance data for the emulated - * SCSI controller. + * Device instance data for the emulated SCSI controller. */ typedef struct LSILOGICSCSI { @@ -127,19 +189,15 @@ typedef struct LSILOGICSCSI /** Who needs to init the driver to get into operational state. */ LSILOGICWHOINIT enmWhoInit; /** Flag whether we are in doorbell function. */ - bool fDoorbellInProgress; + LSILOGICDOORBELLSTATE enmDoorbellState; /** Flag whether diagnostic access is enabled. */ bool fDiagnosticEnabled; - /** Flag whether a notification was send to R3. */ bool fNotificationSend; - /** Flag whether the guest enabled event notification from the IOC. */ bool fEventNotificationEnabled; - -#if HC_ARCH_BITS == 64 - uint32_t Alignment0; -#endif + /** Flag whether the diagnostic address and RW registers are enabled. */ + bool fDiagRegsEnabled; /** Queue to send tasks to R3. - R3 ptr */ R3PTRTYPE(PPDMQUEUE) pNotificationQueueR3; @@ -148,40 +206,30 @@ typedef struct LSILOGICSCSI /** Queue to send tasks to R3. - RC ptr */ RCPTRTYPE(PPDMQUEUE) pNotificationQueueRC; -#if HC_ARCH_BITS == 64 - uint32_t Alignment1; -#endif - /** Number of device states allocated. */ - uint32_t cDeviceStates; - -#if HC_ARCH_BITS == 64 - uint32_t Alignment2; -#endif + uint32_t cDeviceStates; /** States for attached devices. */ R3PTRTYPE(PLSILOGICDEVICE) paDeviceStates; - - /** MMIO address the device is mapped to. */ - RTGCPHYS GCPhysMMIOBase; - /** I/O port address the device is mapped to. */ - RTIOPORT IOPortBase; +#if HC_ARCH_BITS == 32 + RTR3PTR R3PtrPadding0; +#endif /** Interrupt mask. */ volatile uint32_t uInterruptMask; /** Interrupt status register. */ volatile uint32_t uInterruptStatus; - /** Buffer for messages which are passed - * through the doorbell using the + /** Buffer for messages which are passed through the doorbell using the * handshake method. */ - uint32_t aMessage[sizeof(MptConfigurationRequest)]; + uint32_t aMessage[sizeof(MptConfigurationRequest)]; /** @todo r=bird: Looks like 4 tims the required size? Please explain in comment if this correct... */ /** Actual position in the buffer. */ uint32_t iMessage; /** Size of the message which is given in the doorbell message in dwords. */ uint32_t cMessage; - /** Reply buffer. */ + /** Reply buffer. + * @note 60 bytes */ MptReplyUnion ReplyBuffer; /** Next entry to read. */ uint32_t uNextReplyEntryRead; @@ -191,6 +239,11 @@ typedef struct LSILOGICSCSI /** The fault code of the I/O controller if we are in the fault state. */ uint16_t u16IOCFaultCode; + /** I/O port address the device is mapped to. */ + RTIOPORT IOPortBase; + /** MMIO address the device is mapped to. */ + RTGCPHYS GCPhysMMIOBase; + /** Upper 32 bits of the message frame address to locate requests in guest memory. */ uint32_t u32HostMFAHighAddr; /** Upper 32 bits of the sense buffer address. */ @@ -211,7 +264,6 @@ typedef struct LSILOGICSCSI /** Number entries allocated for the outstanding request queue. */ uint32_t cRequestQueueEntries; - uint32_t Alignment3; /** Critical section protecting the reply post queue. */ PDMCRITSECT ReplyPostQueueCritSect; @@ -238,6 +290,8 @@ typedef struct LSILOGICSCSI RCPTRTYPE(volatile uint32_t *) pReplyPostQueueBaseRC; /** Pointer to the start of the request queue - RC. */ RCPTRTYPE(volatile uint32_t *) pRequestQueueBaseRC; + /** End these RC pointers on a 64-bit boundrary. */ + RTRCPTR RCPtrPadding1; /** Next free entry in the reply queue the guest can write a address to. */ volatile uint32_t uReplyFreeQueueNextEntryFreeWrite; @@ -259,18 +313,12 @@ typedef struct LSILOGICSCSI /** Handle counter */ uint16_t u16NextHandle; - uint16_t u16Alignment4; - uint32_t u32Alignment5; - /** Number of ports this controller has. */ uint8_t cPorts; -#if HC_ARCH_BITS == 64 - uint32_t Alignment6; -#endif - /** BIOS emulation. */ VBOXSCSI VBoxSCSI; + /** Cache for allocated tasks. */ R3PTRTYPE(RTMEMCACHE) hTaskCache; /** Status LUN: The base interface. */ @@ -282,54 +330,53 @@ typedef struct LSILOGICSCSI /** Pointer to the configuration page area. */ R3PTRTYPE(PMptConfigurationPagesSupported) pConfigurationPages; -#if HC_ARCH_BITS == 64 - uint32_t Alignment7; -#endif - /** Indicates that PDMDevHlpAsyncNotificationCompleted should be called when * a port is entering the idle state. */ - bool volatile fSignalIdle; + bool volatile fSignalIdle; /** Flag whether we have tasks which need to be processed again- */ - bool volatile fRedo; + bool volatile fRedo; + /** Flag whether the worker thread is sleeping. */ + volatile bool fWrkThreadSleeping; + /** Alignment padding. */ + bool afPadding2[HC_ARCH_BITS == 32 ? 1 : 5]; /** List of tasks which can be redone. */ - R3PTRTYPE(volatile PLSILOGICTASKSTATE) pTasksRedoHead; + R3PTRTYPE(volatile PLSILOGICREQ) pTasksRedoHead; -} LSILOGISCSI, *PLSILOGICSCSI; + /** Current address to read from or write to in the diagnostic memory region. */ + uint32_t u32DiagMemAddr; + /** Current size of the memory regions. */ + uint32_t cbMemRegns; + +#if HC_ARCH_BITS ==32 + uint32_t u32Padding3; +#endif -/** - * Scatter gather list entry data. - */ -typedef struct LSILOGICTASKSTATESGENTRY -{ - /** Flag whether the buffer in the list is from the guest or an - * allocated temporary buffer because the segments in the guest - * are not sector aligned. - */ - bool fGuestMemory; - /** Flag whether the buffer contains data or is the destination for the transfer. */ - bool fBufferContainsData; - /** Pointer to the start of the buffer. */ - void *pvBuf; - /** Size of the buffer. */ - uint32_t cbBuf; - /** Flag dependent data. */ union { - /** Data to handle direct mappings of guest buffers. */ - PGMPAGEMAPLOCK PageLock; - /** The segment in the guest which is not sector aligned. */ - RTGCPHYS GCPhysAddrBufferUnaligned; - } u; -} LSILOGICTASKSTATESGENTRY, *PLSILOGICTASKSTATESGENTRY; + /** List of memory regions - PLSILOGICMEMREGN. */ + RTLISTANCHOR ListMemRegns; + uint8_t u8Padding[2 * sizeof(RTUINTPTR)]; + }; + + /** The support driver session handle. */ + R3R0PTRTYPE(PSUPDRVSESSION) pSupDrvSession; + /** Worker thread. */ + R3PTRTYPE(PPDMTHREAD) pThreadWrk; + /** The event semaphore the processing thread waits on. */ + SUPSEMEVENT hEvtProcess; + +} LSILOGISCSI; +/** Pointer to the device instance data of the LsiLogic emulation. */ +typedef LSILOGICSCSI *PLSILOGICSCSI; /** * Task state object which holds all necessary data while * processing the request from the guest. */ -typedef struct LSILOGICTASKSTATE +typedef struct LSILOGICREQ { /** Next in the redo list. */ - PLSILOGICTASKSTATE pRedoNext; + PLSILOGICREQ pRedoNext; /** Target device. */ PLSILOGICDEVICE pTargetDevice; /** The message request from the guest. */ @@ -341,50 +388,43 @@ typedef struct LSILOGICTASKSTATE /** Address of the message request frame in guests memory. * Used to read the S/G entries in the second step. */ RTGCPHYS GCPhysMessageFrameAddr; - /** Number of scatter gather list entries. */ - uint32_t cSGListEntries; - /** How many entries would fit into the sg list. */ - uint32_t cSGListSize; - /** How many times the list was too big. */ - uint32_t cSGListTooBig; - /** Pointer to the first entry of the scatter gather list. */ - PRTSGSEG pSGListHead; - /** How many entries would fit into the sg info list. */ - uint32_t cSGInfoSize; - /** Number of entries for the information entries. */ - uint32_t cSGInfoEntries; - /** How many times the list was too big. */ - uint32_t cSGInfoTooBig; - /** Pointer to the first mapping information entry. */ - PLSILOGICTASKSTATESGENTRY paSGEntries; - /** Size of the temporary buffer for unaligned guest segments. */ - uint32_t cbBufferUnaligned; - /** Pointer to the temporary buffer. */ - void *pvBufferUnaligned; + /** Physical start address of the S/G list. */ + RTGCPHYS GCPhysSgStart; + /** Chain offset */ + uint32_t cChainOffset; + /** Segment describing the I/O buffer. */ + RTSGSEG SegIoBuf; + /** Additional memory allocation for this task. */ + void *pvAlloc; + /** Siize of the allocation. */ + size_t cbAlloc; + /** Number of times we had too much memory allocated for the request. */ + unsigned cAllocTooMuch; /** Pointer to the sense buffer. */ uint8_t abSenseBuffer[18]; /** Flag whether the request was issued from the BIOS. */ bool fBIOS; -} LSILOGICTASKSTATE; +} LSILOGICREQ; + #ifndef VBOX_DEVICE_STRUCT_TESTCASE +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ RT_C_DECLS_BEGIN #ifdef IN_RING3 -static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic); -static void lsilogicConfigurationPagesFree(PLSILOGICSCSI pThis); -static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConfigurationRequest pConfigurationReq, - PMptConfigurationReply pReply); +static void lsilogicR3InitializeConfigurationPages(PLSILOGICSCSI pThis); +static void lsilogicR3ConfigurationPagesFree(PLSILOGICSCSI pThis); +static int lsilogicR3ProcessConfigurationRequest(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq, + PMptConfigurationReply pReply); #endif RT_C_DECLS_END -#define PDMIBASE_2_PLSILOGICDEVICE(pInterface) ( (PLSILOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICDEVICE, IBase)) ) -#define PDMISCSIPORT_2_PLSILOGICDEVICE(pInterface) ( (PLSILOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICDEVICE, ISCSIPort)) ) -#define PDMILEDPORTS_2_PLSILOGICDEVICE(pInterface) ( (PLSILOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICDEVICE, ILed)) ) -#define LSILOGIC_RTGCPHYS_FROM_U32(Hi, Lo) ( (RTGCPHYS)RT_MAKE_U64(Lo, Hi) ) -#define PDMIBASE_2_PLSILOGICSCSI(pInterface) ( (PLSILOGICSCSI)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICSCSI, IBase)) ) -#define PDMILEDPORTS_2_PLSILOGICSCSI(pInterface) ( (PLSILOGICSCSI)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICSCSI, ILeds)) ) +/******************************************************************************* +* Global Variables * +*******************************************************************************/ /** Key sequence the guest has to write to enable access * to diagnostic memory. */ static const uint8_t g_lsilogicDiagnosticAccess[] = {0x04, 0x0b, 0x02, 0x07, 0x0d}; @@ -393,7 +433,7 @@ static const uint8_t g_lsilogicDiagnosticAccess[] = {0x04, 0x0b, 0x02, 0x07, 0x0 * Updates the status of the interrupt pin of the device. * * @returns nothing. - * @param pThis Pointer to the device instance data. + * @param pThis Pointer to the LsiLogic device state. */ static void lsilogicUpdateInterrupt(PLSILOGICSCSI pThis) { @@ -423,13 +463,13 @@ static void lsilogicUpdateInterrupt(PLSILOGICSCSI pThis) * updates the interrupt status. * * @returns nothing. - * @param pLsiLogic Pointer to the device instance. - * @param uStatus The status bit to set. + * @param pThis Pointer to the LsiLogic device state. + * @param uStatus The status bit to set. */ -DECLINLINE(void) lsilogicSetInterrupt(PLSILOGICSCSI pLsiLogic, uint32_t uStatus) +DECLINLINE(void) lsilogicSetInterrupt(PLSILOGICSCSI pThis, uint32_t uStatus) { - ASMAtomicOrU32(&pLsiLogic->uInterruptStatus, uStatus); - lsilogicUpdateInterrupt(pLsiLogic); + ASMAtomicOrU32(&pThis->uInterruptStatus, uStatus); + lsilogicUpdateInterrupt(pThis); } /** @@ -437,50 +477,86 @@ DECLINLINE(void) lsilogicSetInterrupt(PLSILOGICSCSI pLsiLogic, uint32_t uStatus) * updates the interrupt status. * * @returns nothing. - * @param pLsiLogic Pointer to the device instance. - * @param uStatus The status bit to set. + * @param pThis Pointer to the LsiLogic device state. + * @param uStatus The status bit to set. */ -DECLINLINE(void) lsilogicClearInterrupt(PLSILOGICSCSI pLsiLogic, uint32_t uStatus) +DECLINLINE(void) lsilogicClearInterrupt(PLSILOGICSCSI pThis, uint32_t uStatus) { - ASMAtomicAndU32(&pLsiLogic->uInterruptStatus, ~uStatus); - lsilogicUpdateInterrupt(pLsiLogic); + ASMAtomicAndU32(&pThis->uInterruptStatus, ~uStatus); + lsilogicUpdateInterrupt(pThis); } /** * Sets the I/O controller into fault state and sets the fault code. * * @returns nothing - * @param pLsiLogic Pointer to the controller device instance. - * @param uIOCFaultCode Fault code to set. + * @param pThis Pointer to the LsiLogic device state. + * @param uIOCFaultCode Fault code to set. */ -DECLINLINE(void) lsilogicSetIOCFaultCode(PLSILOGICSCSI pLsiLogic, uint16_t uIOCFaultCode) +DECLINLINE(void) lsilogicSetIOCFaultCode(PLSILOGICSCSI pThis, uint16_t uIOCFaultCode) { - if (pLsiLogic->enmState != LSILOGICSTATE_FAULT) + if (pThis->enmState != LSILOGICSTATE_FAULT) { Log(("%s: Setting I/O controller into FAULT state: uIOCFaultCode=%u\n", __FUNCTION__, uIOCFaultCode)); - pLsiLogic->enmState = LSILOGICSTATE_FAULT; - pLsiLogic->u16IOCFaultCode = uIOCFaultCode; + pThis->enmState = LSILOGICSTATE_FAULT; + pThis->u16IOCFaultCode = uIOCFaultCode; } else - { Log(("%s: We are already in FAULT state\n")); - } +} + +/** + * Returns the number of frames in the reply free queue. + * + * @returns Number of frames in the reply free queue. + * @param pThis Pointer to the LsiLogic device state. + */ +DECLINLINE(uint32_t) lsilogicReplyFreeQueueGetFrameCount(PLSILOGICSCSI pThis) +{ + uint32_t cReplyFrames = 0; + + if (pThis->uReplyFreeQueueNextAddressRead <= pThis->uReplyFreeQueueNextEntryFreeWrite) + cReplyFrames = pThis->uReplyFreeQueueNextEntryFreeWrite - pThis->uReplyFreeQueueNextAddressRead; + else + cReplyFrames = pThis->cReplyQueueEntries - pThis->uReplyFreeQueueNextAddressRead + pThis->uReplyFreeQueueNextEntryFreeWrite; + + return cReplyFrames; +} + +/** + * Returns the number of free entries in the reply post queue. + * + * @returns Number of frames in the reply free queue. + * @param pThis Pointer to the LsiLogic device state. + */ +DECLINLINE(uint32_t) lsilogicReplyPostQueueGetFrameCount(PLSILOGICSCSI pThis) +{ + uint32_t cReplyFrames = 0; + + if (pThis->uReplyPostQueueNextAddressRead <= pThis->uReplyPostQueueNextEntryFreeWrite) + cReplyFrames = pThis->cReplyQueueEntries - pThis->uReplyPostQueueNextEntryFreeWrite + pThis->uReplyPostQueueNextAddressRead; + else + cReplyFrames = pThis->uReplyPostQueueNextEntryFreeWrite - pThis->uReplyPostQueueNextAddressRead; + + return cReplyFrames; } #ifdef IN_RING3 + /** * Performs a hard reset on the controller. * * @returns VBox status code. - * @param pThis Pointer to the device instance to initialize. + * @param pThis Pointer to the LsiLogic device state. */ -static int lsilogicHardReset(PLSILOGICSCSI pThis) +static int lsilogicR3HardReset(PLSILOGICSCSI pThis) { pThis->enmState = LSILOGICSTATE_RESET; + pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE; /* The interrupts are masked out. */ - pThis->uInterruptMask |= LSILOGIC_REG_HOST_INTR_MASK_DOORBELL | - LSILOGIC_REG_HOST_INTR_MASK_REPLY; + pThis->uInterruptMask |= LSILOGIC_REG_HOST_INTR_MASK_DOORBELL + | LSILOGIC_REG_HOST_INTR_MASK_REPLY; /* Reset interrupt states. */ pThis->uInterruptStatus = 0; lsilogicUpdateInterrupt(pThis); @@ -494,17 +570,19 @@ static int lsilogicHardReset(PLSILOGICSCSI pThis) pThis->uRequestQueueNextAddressRead = 0; /* Disable diagnostic access. */ - pThis->iDiagnosticAccess = 0; + pThis->iDiagnosticAccess = 0; + pThis->fDiagnosticEnabled = false; + pThis->fDiagRegsEnabled = false; /* Set default values. */ - pThis->cMaxDevices = pThis->cDeviceStates; - pThis->cMaxBuses = 1; - pThis->cbReplyFrame = 128; /* @todo Figure out where it is needed. */ - pThis->u16NextHandle = 1; - /** @todo: Put stuff to reset here. */ + pThis->cMaxDevices = pThis->cDeviceStates; + pThis->cMaxBuses = 1; + pThis->cbReplyFrame = 128; /* @todo Figure out where it is needed. */ + pThis->u16NextHandle = 1; + pThis->u32DiagMemAddr = 0; - lsilogicConfigurationPagesFree(pThis); - lsilogicInitializeConfigurationPages(pThis); + lsilogicR3ConfigurationPagesFree(pThis); + lsilogicR3InitializeConfigurationPages(pThis); /* Mark that we finished performing the reset. */ pThis->enmState = LSILOGICSTATE_READY; @@ -517,7 +595,7 @@ static int lsilogicHardReset(PLSILOGICSCSI pThis) * @returns nothing. * @param pThis The LsiLogic controller instance */ -static void lsilogicConfigurationPagesFree(PLSILOGICSCSI pThis) +static void lsilogicR3ConfigurationPagesFree(PLSILOGICSCSI pThis) { if (pThis->pConfigurationPages) @@ -553,69 +631,39 @@ static void lsilogicConfigurationPagesFree(PLSILOGICSCSI pThis) * Finishes a context reply. * * @returns nothing - * @param pLsiLogic Pointer to the device instance - * @param u32MessageContext The message context ID to post. + * @param pThis Pointer to the LsiLogic device state. + * @param u32MessageContext The message context ID to post. */ -static void lsilogicFinishContextReply(PLSILOGICSCSI pLsiLogic, uint32_t u32MessageContext) +static void lsilogicR3FinishContextReply(PLSILOGICSCSI pThis, uint32_t u32MessageContext) { int rc; - LogFlowFunc(("pLsiLogic=%#p u32MessageContext=%#x\n", pLsiLogic, u32MessageContext)); + LogFlowFunc(("pThis=%#p u32MessageContext=%#x\n", pThis, u32MessageContext)); - AssertMsg(!pLsiLogic->fDoorbellInProgress, ("We are in a doorbell function\n")); + AssertMsg(pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_NOT_IN_USE, ("We are in a doorbell function\n")); /* Write message context ID into reply post queue. */ - rc = PDMCritSectEnter(&pLsiLogic->ReplyPostQueueCritSect, VINF_SUCCESS); + rc = PDMCritSectEnter(&pThis->ReplyPostQueueCritSect, VINF_SUCCESS); AssertRC(rc); -#if 0 /* Check for a entry in the queue. */ - if (RT_UNLIKELY(pLsiLogic->uReplyPostQueueNextAddressRead != pLsiLogic->uReplyPostQueueNextEntryFreeWrite)) + if (!lsilogicReplyPostQueueGetFrameCount(pThis)) { /* Set error code. */ - lsilogicSetIOCFaultCode(pLsiLogic, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES); - PDMCritSectLeave(&pLsiLogic->ReplyPostQueueCritSect); + lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES); + PDMCritSectLeave(&pThis->ReplyPostQueueCritSect); return; } -#endif /* We have a context reply. */ - ASMAtomicWriteU32(&pLsiLogic->CTX_SUFF(pReplyPostQueueBase)[pLsiLogic->uReplyPostQueueNextEntryFreeWrite], u32MessageContext); - ASMAtomicIncU32(&pLsiLogic->uReplyPostQueueNextEntryFreeWrite); - pLsiLogic->uReplyPostQueueNextEntryFreeWrite %= pLsiLogic->cReplyQueueEntries; + ASMAtomicWriteU32(&pThis->CTX_SUFF(pReplyPostQueueBase)[pThis->uReplyPostQueueNextEntryFreeWrite], u32MessageContext); + ASMAtomicIncU32(&pThis->uReplyPostQueueNextEntryFreeWrite); + pThis->uReplyPostQueueNextEntryFreeWrite %= pThis->cReplyQueueEntries; /* Set interrupt. */ - lsilogicSetInterrupt(pLsiLogic, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR); - - PDMCritSectLeave(&pLsiLogic->ReplyPostQueueCritSect); -} + lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR); -static void lsilogicTaskStateClear(PLSILOGICTASKSTATE pTaskState) -{ - RTMemFree(pTaskState->pSGListHead); - RTMemFree(pTaskState->paSGEntries); - if (pTaskState->pvBufferUnaligned) - RTMemPageFree(pTaskState->pvBufferUnaligned, pTaskState->cbBufferUnaligned); - pTaskState->cSGListSize = 0; - pTaskState->cSGInfoSize = 0; - pTaskState->cSGInfoEntries = 0; - pTaskState->cSGListTooBig = 0; - pTaskState->pSGListHead = NULL; - pTaskState->paSGEntries = NULL; - pTaskState->pvBufferUnaligned = NULL; - pTaskState->cbBufferUnaligned = 0; -} - -static int lsilogicTaskStateCtor(RTMEMCACHE hMemCache, void *pvObj, void *pvUser) -{ - memset(pvObj, 0, sizeof(LSILOGICTASKSTATE)); - return VINF_SUCCESS; -} - -static void lsilogicTaskStateDtor(RTMEMCACHE hMemCache, void *pvObj, void *pvUser) -{ - PLSILOGICTASKSTATE pTaskState = (PLSILOGICTASKSTATE)pvObj; - lsilogicTaskStateClear(pTaskState); + PDMCritSectLeave(&pThis->ReplyPostQueueCritSect); } #endif /* IN_RING3 */ @@ -624,24 +672,24 @@ static void lsilogicTaskStateDtor(RTMEMCACHE hMemCache, void *pvObj, void *pvUse * Takes necessary steps to finish a reply frame. * * @returns nothing - * @param pLsiLogic Pointer to the device instance + * @param pThis Pointer to the LsiLogic device state. * @param pReply Pointer to the reply message. * @param fForceReplyFifo Flag whether the use of the reply post fifo is forced. */ -static void lsilogicFinishAddressReply(PLSILOGICSCSI pLsiLogic, PMptReplyUnion pReply, bool fForceReplyFifo) +static void lsilogicFinishAddressReply(PLSILOGICSCSI pThis, PMptReplyUnion pReply, bool fForceReplyFifo) { /* * If we are in a doorbell function we set the reply size now and * set the system doorbell status interrupt to notify the guest that * we are ready to send the reply. */ - if (pLsiLogic->fDoorbellInProgress && !fForceReplyFifo) + if (pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_NOT_IN_USE && !fForceReplyFifo) { /* Set size of the reply in 16bit words. The size in the reply is in 32bit dwords. */ - pLsiLogic->cReplySize = pReply->Header.u8MessageLength * 2; - Log(("%s: cReplySize=%u\n", __FUNCTION__, pLsiLogic->cReplySize)); - pLsiLogic->uNextReplyEntryRead = 0; - lsilogicSetInterrupt(pLsiLogic, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL); + pThis->cReplySize = pReply->Header.u8MessageLength * 2; + Log(("%s: cReplySize=%u\n", __FUNCTION__, pThis->cReplySize)); + pThis->uNextReplyEntryRead = 0; + lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL); } else { @@ -653,65 +701,61 @@ static void lsilogicFinishAddressReply(PLSILOGICSCSI pLsiLogic, PMptReplyUnion p #ifdef IN_RING3 int rc; /* Grab a free reply message from the queue. */ - rc = PDMCritSectEnter(&pLsiLogic->ReplyFreeQueueCritSect, VINF_SUCCESS); + rc = PDMCritSectEnter(&pThis->ReplyFreeQueueCritSect, VINF_SUCCESS); AssertRC(rc); -#if 0 /* Check for a free reply frame. */ - if (RT_UNLIKELY(pLsiLogic->uReplyFreeQueueNextAddressRead != pLsiLogic->uReplyFreeQueueNextEntryFreeWrite)) + if (!lsilogicReplyFreeQueueGetFrameCount(pThis)) { /* Set error code. */ - lsilogicSetIOCFaultCode(pLsiLogic, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES); - PDMCritSectLeave(&pLsiLogic->ReplyFreeQueueCritSect); + lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES); + PDMCritSectLeave(&pThis->ReplyFreeQueueCritSect); return; } -#endif - uint32_t u32ReplyFrameAddressLow = pLsiLogic->CTX_SUFF(pReplyFreeQueueBase)[pLsiLogic->uReplyFreeQueueNextAddressRead]; + uint32_t u32ReplyFrameAddressLow = pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead]; - pLsiLogic->uReplyFreeQueueNextAddressRead++; - pLsiLogic->uReplyFreeQueueNextAddressRead %= pLsiLogic->cReplyQueueEntries; + pThis->uReplyFreeQueueNextAddressRead++; + pThis->uReplyFreeQueueNextAddressRead %= pThis->cReplyQueueEntries; - PDMCritSectLeave(&pLsiLogic->ReplyFreeQueueCritSect); + PDMCritSectLeave(&pThis->ReplyFreeQueueCritSect); /* Build 64bit physical address. */ - RTGCPHYS GCPhysReplyMessage = LSILOGIC_RTGCPHYS_FROM_U32(pLsiLogic->u32HostMFAHighAddr, u32ReplyFrameAddressLow); - size_t cbReplyCopied = (pLsiLogic->cbReplyFrame < sizeof(MptReplyUnion)) ? pLsiLogic->cbReplyFrame : sizeof(MptReplyUnion); + RTGCPHYS GCPhysReplyMessage = LSILOGIC_RTGCPHYS_FROM_U32(pThis->u32HostMFAHighAddr, u32ReplyFrameAddressLow); + size_t cbReplyCopied = (pThis->cbReplyFrame < sizeof(MptReplyUnion)) ? pThis->cbReplyFrame : sizeof(MptReplyUnion); /* Write reply to guest memory. */ - PDMDevHlpPhysWrite(pLsiLogic->CTX_SUFF(pDevIns), GCPhysReplyMessage, pReply, cbReplyCopied); + PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhysReplyMessage, pReply, cbReplyCopied); /* Write low 32bits of reply frame into post reply queue. */ - rc = PDMCritSectEnter(&pLsiLogic->ReplyPostQueueCritSect, VINF_SUCCESS); + rc = PDMCritSectEnter(&pThis->ReplyPostQueueCritSect, VINF_SUCCESS); AssertRC(rc); -#if 0 /* Check for a entry in the queue. */ - if (RT_UNLIKELY(pLsiLogic->uReplyPostQueueNextAddressRead != pLsiLogic->uReplyPostQueueNextEntryFreeWrite)) + if (!lsilogicReplyPostQueueGetFrameCount(pThis)) { /* Set error code. */ - lsilogicSetIOCFaultCode(pLsiLogic, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES); - PDMCritSectLeave(&pLsiLogic->ReplyPostQueueCritSect); + lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES); + PDMCritSectLeave(&pThis->ReplyPostQueueCritSect); return; } -#endif /* We have a address reply. Set the 31th bit to indicate that. */ - ASMAtomicWriteU32(&pLsiLogic->CTX_SUFF(pReplyPostQueueBase)[pLsiLogic->uReplyPostQueueNextEntryFreeWrite], + ASMAtomicWriteU32(&pThis->CTX_SUFF(pReplyPostQueueBase)[pThis->uReplyPostQueueNextEntryFreeWrite], RT_BIT(31) | (u32ReplyFrameAddressLow >> 1)); - ASMAtomicIncU32(&pLsiLogic->uReplyPostQueueNextEntryFreeWrite); - pLsiLogic->uReplyPostQueueNextEntryFreeWrite %= pLsiLogic->cReplyQueueEntries; + ASMAtomicIncU32(&pThis->uReplyPostQueueNextEntryFreeWrite); + pThis->uReplyPostQueueNextEntryFreeWrite %= pThis->cReplyQueueEntries; if (fForceReplyFifo) { - pLsiLogic->fDoorbellInProgress = false; - lsilogicSetInterrupt(pLsiLogic, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL); + pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE; + lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL); } /* Set interrupt. */ - lsilogicSetInterrupt(pLsiLogic, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR); + lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR); - PDMCritSectLeave(&pLsiLogic->ReplyPostQueueCritSect); + PDMCritSectLeave(&pThis->ReplyPostQueueCritSect); #else AssertMsgFailed(("This is not allowed to happen.\n")); #endif @@ -719,25 +763,256 @@ static void lsilogicFinishAddressReply(PLSILOGICSCSI pLsiLogic, PMptReplyUnion p } #ifdef IN_RING3 + +/** + * Tries to find a memory region which covers the given address. + * + * @returns Pointer to memory region or NULL if not found. + * @param pThis Pointer to the LsiLogic device state. + * @param u32Addr The 32bit address to search for. + */ +static PLSILOGICMEMREGN lsilogicR3MemRegionFindByAddr(PLSILOGICSCSI pThis, uint32_t u32Addr) +{ + PLSILOGICMEMREGN pIt; + PLSILOGICMEMREGN pRegion = NULL; + + RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList) + { + if ( u32Addr >= pIt->u32AddrStart + && u32Addr <= pIt->u32AddrEnd) + { + pRegion = pIt; + break; + } + } + + return pRegion; +} + +/** + * Frees all allocated memory regions. + * + * @returns nothing. + * @param pThis Pointer to the LsiLogic device state. + */ +static void lsilogicR3MemRegionsFree(PLSILOGICSCSI pThis) +{ + PLSILOGICMEMREGN pIt; + PLSILOGICMEMREGN pItNext; + + RTListForEachSafe(&pThis->ListMemRegns, pIt, pItNext, LSILOGICMEMREGN, NodeList) + { + RTListNodeRemove(&pIt->NodeList); + RTMemFree(pIt); + } + pThis->cbMemRegns = 0; +} + +/** + * Inserts a given memory region into the list. + * + * @returns nothing. + * @param pThis Pointer to the LsiLogic device state. + * @param pRegion The region to insert. + */ +static void lsilogicR3MemRegionInsert(PLSILOGICSCSI pThis, PLSILOGICMEMREGN pRegion) +{ + PLSILOGICMEMREGN pIt; + bool fInserted = false; + + /* Insert at the right position. */ + RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList) + { + if (pRegion->u32AddrEnd < pIt->u32AddrStart) + { + RTListNodeInsertBefore(&pIt->NodeList, &pRegion->NodeList); + fInserted = true; + break; + } + } + if (!fInserted) + RTListAppend(&pThis->ListMemRegns, &pRegion->NodeList); +} + +/** + * Count number of memory regions. + * + * @returns Number of memory regions. + * @param pThis Pointer to the LsiLogic device state. + */ +static uint32_t lsilogicR3MemRegionsCount(PLSILOGICSCSI pThis) +{ + uint32_t cRegions = 0; + PLSILOGICMEMREGN pIt; + + RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList) + { + cRegions++; + } + + return cRegions; +} + +/** + * Handles a write to the diagnostic data register. + * + * @returns nothing. + * @param pThis Pointer to the LsiLogic device state. + * @param u32Data Data to write. + */ +static void lsilogicR3DiagRegDataWrite(PLSILOGICSCSI pThis, uint32_t u32Data) +{ + PLSILOGICMEMREGN pRegion = lsilogicR3MemRegionFindByAddr(pThis, pThis->u32DiagMemAddr); + + if (pRegion) + { + uint32_t offRegion = pThis->u32DiagMemAddr - pRegion->u32AddrStart; + + AssertMsg( offRegion % 4 == 0 + && pThis->u32DiagMemAddr <= pRegion->u32AddrEnd, + ("Region offset not on a word boundary or crosses memory region\n")); + + offRegion /= 4; + pRegion->au32Data[offRegion] = u32Data; + } + else + { + PLSILOGICMEMREGN pIt; + + pRegion = NULL; + + /* Create new region, first check whether we can extend another region. */ + RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList) + { + if (pThis->u32DiagMemAddr == pIt->u32AddrEnd + sizeof(uint32_t)) + { + pRegion = pIt; + break; + } + } + + if (pRegion) + { + /* Reallocate. */ + RTListNodeRemove(&pRegion->NodeList); + + uint32_t cRegionSizeOld = (pRegion->u32AddrEnd - pRegion->u32AddrStart) / 4 + 1; + uint32_t cRegionSizeNew = cRegionSizeOld + 512; + + if (pThis->cbMemRegns + 512 * sizeof(uint32_t) < LSILOGIC_MEMORY_REGIONS_MAX) + { + PLSILOGICMEMREGN pRegionNew = (PLSILOGICMEMREGN)RTMemRealloc(pRegion, RT_OFFSETOF(LSILOGICMEMREGN, au32Data[cRegionSizeNew])); + + if (pRegionNew) + { + pRegion = pRegionNew; + memset(&pRegion->au32Data[cRegionSizeOld], 0, 512 * sizeof(uint32_t)); + pRegion->au32Data[cRegionSizeOld] = u32Data; + pRegion->u32AddrEnd = pRegion->u32AddrStart + (cRegionSizeNew - 1) * sizeof(uint32_t); + pThis->cbMemRegns += 512 * sizeof(uint32_t); + } + /* else: Silently fail, there is nothing we can do here and the guest might work nevertheless. */ + + lsilogicR3MemRegionInsert(pThis, pRegion); + } + } + else + { + if (pThis->cbMemRegns + 512 * sizeof(uint32_t) < LSILOGIC_MEMORY_REGIONS_MAX) + { + /* Create completely new. */ + pRegion = (PLSILOGICMEMREGN)RTMemAllocZ(RT_OFFSETOF(LSILOGICMEMREGN, au32Data[512])); + if (pRegion) + { + pRegion->u32AddrStart = pThis->u32DiagMemAddr; + pRegion->u32AddrEnd = pRegion->u32AddrStart + (512 - 1) * sizeof(uint32_t); + pRegion->au32Data[0] = u32Data; + pThis->cbMemRegns += 512 * sizeof(uint32_t); + + lsilogicR3MemRegionInsert(pThis, pRegion); + } + /* else: Silently fail, there is nothing we can do here and the guest might work nevertheless. */ + } + } + + } + + /* Memory access is always 32bit big. */ + pThis->u32DiagMemAddr += sizeof(uint32_t); +} + +/** + * Handles a read from the diagnostic data register. + * + * @returns nothing. + * @param pThis Pointer to the LsiLogic device state. + * @param pu32Data Where to store the data. + */ +static void lsilogicR3DiagRegDataRead(PLSILOGICSCSI pThis, uint32_t *pu32Data) +{ + PLSILOGICMEMREGN pRegion = lsilogicR3MemRegionFindByAddr(pThis, pThis->u32DiagMemAddr); + + if (pRegion) + { + uint32_t offRegion = pThis->u32DiagMemAddr - pRegion->u32AddrStart; + + AssertMsg( offRegion % 4 == 0 + && pThis->u32DiagMemAddr <= pRegion->u32AddrEnd, + ("Region offset not on a word boundary or crosses memory region\n")); + + offRegion /= 4; + *pu32Data = pRegion->au32Data[offRegion]; + } + else /* No region, default value 0. */ + *pu32Data = 0; + + /* Memory access is always 32bit big. */ + pThis->u32DiagMemAddr += sizeof(uint32_t); +} + +/** + * Handles a write to the diagnostic memory address register. + * + * @returns nothing. + * @param pThis Pointer to the LsiLogic device state. + * @param u32Addr Address to write. + */ +static void lsilogicR3DiagRegAddressWrite(PLSILOGICSCSI pThis, uint32_t u32Addr) +{ + pThis->u32DiagMemAddr = u32Addr & ~UINT32_C(0x3); /* 32bit alignment. */ +} + +/** + * Handles a read from the diagnostic memory address register. + * + * @returns nothing. + * @param pThis Pointer to the LsiLogic device state. + * @param pu32Addr Where to store the current address. + */ +static void lsilogicR3DiagRegAddressRead(PLSILOGICSCSI pThis, uint32_t *pu32Addr) +{ + *pu32Addr = pThis->u32DiagMemAddr; +} + /** * Processes a given Request from the guest * * @returns VBox status code. - * @param pLsiLogic Pointer to the device instance. - * @param pMessageHdr Pointer to the message header of the request. - * @param pReply Pointer to the reply. + * @param pThis Pointer to the LsiLogic device state. + * @param pMessageHdr Pointer to the message header of the request. + * @param pReply Pointer to the reply. */ -static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr pMessageHdr, PMptReplyUnion pReply) +static int lsilogicR3ProcessMessageRequest(PLSILOGICSCSI pThis, PMptMessageHdr pMessageHdr, PMptReplyUnion pReply) { int rc = VINF_SUCCESS; bool fForceReplyPostFifo = false; -#ifdef DEBUG +# ifdef LOG_ENABLED if (pMessageHdr->u8Function < RT_ELEMENTS(g_apszMPTFunctionNames)) Log(("Message request function: %s\n", g_apszMPTFunctionNames[pMessageHdr->u8Function])); else Log(("Message request function: <unknown>\n")); -#endif +# endif memset(pReply, 0, sizeof(MptReplyUnion)); @@ -765,61 +1040,87 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr PMptIOCInitRequest pIOCInitReq = (PMptIOCInitRequest)pMessageHdr; /* Update configuration values. */ - pLsiLogic->enmWhoInit = (LSILOGICWHOINIT)pIOCInitReq->u8WhoInit; - pLsiLogic->cbReplyFrame = pIOCInitReq->u16ReplyFrameSize; - pLsiLogic->cMaxBuses = pIOCInitReq->u8MaxBuses; - pLsiLogic->cMaxDevices = pIOCInitReq->u8MaxDevices; - pLsiLogic->u32HostMFAHighAddr = pIOCInitReq->u32HostMfaHighAddr; - pLsiLogic->u32SenseBufferHighAddr = pIOCInitReq->u32SenseBufferHighAddr; - - if (pLsiLogic->enmState == LSILOGICSTATE_READY) + pThis->enmWhoInit = (LSILOGICWHOINIT)pIOCInitReq->u8WhoInit; + pThis->cbReplyFrame = pIOCInitReq->u16ReplyFrameSize; + pThis->cMaxBuses = pIOCInitReq->u8MaxBuses; + pThis->cMaxDevices = pIOCInitReq->u8MaxDevices; + pThis->u32HostMFAHighAddr = pIOCInitReq->u32HostMfaHighAddr; + pThis->u32SenseBufferHighAddr = pIOCInitReq->u32SenseBufferHighAddr; + + if (pThis->enmState == LSILOGICSTATE_READY) { - pLsiLogic->enmState = LSILOGICSTATE_OPERATIONAL; + pThis->enmState = LSILOGICSTATE_OPERATIONAL; } /* Return reply. */ pReply->IOCInit.u8MessageLength = 5; - pReply->IOCInit.u8WhoInit = pLsiLogic->enmWhoInit; - pReply->IOCInit.u8MaxDevices = pLsiLogic->cMaxDevices; - pReply->IOCInit.u8MaxBuses = pLsiLogic->cMaxBuses; + pReply->IOCInit.u8WhoInit = pThis->enmWhoInit; + pReply->IOCInit.u8MaxDevices = pThis->cMaxDevices; + pReply->IOCInit.u8MaxBuses = pThis->cMaxBuses; break; } case MPT_MESSAGE_HDR_FUNCTION_IOC_FACTS: { pReply->IOCFacts.u8MessageLength = 15; /* 15 32bit dwords. */ - if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) + if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) { pReply->IOCFacts.u16MessageVersion = 0x0102; /* Version from the specification. */ - pReply->IOCFacts.u8NumberOfPorts = pLsiLogic->cPorts; + pReply->IOCFacts.u8NumberOfPorts = pThis->cPorts; } - else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) + else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) { pReply->IOCFacts.u16MessageVersion = 0x0105; /* Version from the specification. */ - pReply->IOCFacts.u8NumberOfPorts = pLsiLogic->cPorts; + pReply->IOCFacts.u8NumberOfPorts = pThis->cPorts; } else - AssertMsgFailed(("Invalid controller type %d\n", pLsiLogic->enmCtrlType)); + AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType)); pReply->IOCFacts.u8IOCNumber = 0; /* PCI function number. */ pReply->IOCFacts.u16IOCExceptions = 0; pReply->IOCFacts.u8MaxChainDepth = LSILOGICSCSI_MAXIMUM_CHAIN_DEPTH; - pReply->IOCFacts.u8WhoInit = pLsiLogic->enmWhoInit; + pReply->IOCFacts.u8WhoInit = pThis->enmWhoInit; pReply->IOCFacts.u8BlockSize = 12; /* Block size in 32bit dwords. This is the largest request we can get (SCSI I/O). */ pReply->IOCFacts.u8Flags = 0; /* Bit 0 is set if the guest must upload the FW prior to using the controller. Obviously not needed here. */ - pReply->IOCFacts.u16ReplyQueueDepth = pLsiLogic->cReplyQueueEntries - 1; /* One entry is always free. */ + pReply->IOCFacts.u16ReplyQueueDepth = pThis->cReplyQueueEntries - 1; /* One entry is always free. */ pReply->IOCFacts.u16RequestFrameSize = 128; /* @todo Figure out where it is needed. */ - pReply->IOCFacts.u16ProductID = 0xcafe; /* Our own product ID :) */ - pReply->IOCFacts.u32CurrentHostMFAHighAddr = pLsiLogic->u32HostMFAHighAddr; - pReply->IOCFacts.u16GlobalCredits = pLsiLogic->cRequestQueueEntries - 1; /* One entry is always free. */ + pReply->IOCFacts.u32CurrentHostMFAHighAddr = pThis->u32HostMFAHighAddr; + pReply->IOCFacts.u16GlobalCredits = pThis->cRequestQueueEntries - 1; /* One entry is always free. */ pReply->IOCFacts.u8EventState = 0; /* Event notifications not enabled. */ - pReply->IOCFacts.u32CurrentSenseBufferHighAddr = pLsiLogic->u32SenseBufferHighAddr; - pReply->IOCFacts.u16CurReplyFrameSize = pLsiLogic->cbReplyFrame; - pReply->IOCFacts.u8MaxDevices = pLsiLogic->cMaxDevices; - pReply->IOCFacts.u8MaxBuses = pLsiLogic->cMaxBuses; - pReply->IOCFacts.u32FwImageSize = 0; /* No image needed. */ - pReply->IOCFacts.u32FWVersion = 0; + pReply->IOCFacts.u32CurrentSenseBufferHighAddr = pThis->u32SenseBufferHighAddr; + pReply->IOCFacts.u16CurReplyFrameSize = pThis->cbReplyFrame; + pReply->IOCFacts.u8MaxDevices = pThis->cMaxDevices; + pReply->IOCFacts.u8MaxBuses = pThis->cMaxBuses; + + /* Check for a valid firmware image in the IOC memory which was downlaoded by tzhe guest earlier. */ + PLSILOGICMEMREGN pRegion = lsilogicR3MemRegionFindByAddr(pThis, LSILOGIC_FWIMGHDR_LOAD_ADDRESS); + + if (pRegion) + { + uint32_t offImgHdr = (LSILOGIC_FWIMGHDR_LOAD_ADDRESS - pRegion->u32AddrStart) / 4; + PFwImageHdr pFwImgHdr = (PFwImageHdr)&pRegion->au32Data[offImgHdr]; + + /* Check for the signature. */ + /** @todo: Checksum validation. */ + if ( pFwImgHdr->u32Signature1 == LSILOGIC_FWIMGHDR_SIGNATURE1 + && pFwImgHdr->u32Signature2 == LSILOGIC_FWIMGHDR_SIGNATURE2 + && pFwImgHdr->u32Signature3 == LSILOGIC_FWIMGHDR_SIGNATURE3) + { + LogFlowFunc(("IOC Facts: Found valid firmware image header in memory, using version (%#x), size (%d) and product ID (%#x) from there\n", + pFwImgHdr->u32FwVersion, pFwImgHdr->u32ImageSize, pFwImgHdr->u16ProductId)); + + pReply->IOCFacts.u16ProductID = pFwImgHdr->u16ProductId; + pReply->IOCFacts.u32FwImageSize = pFwImgHdr->u32ImageSize; + pReply->IOCFacts.u32FWVersion = pFwImgHdr->u32FwVersion; + } + } + else + { + pReply->IOCFacts.u16ProductID = 0xcafe; /* Our own product ID :) */ + pReply->IOCFacts.u32FwImageSize = 0; /* No image needed. */ + pReply->IOCFacts.u32FWVersion = 0; + } break; } case MPT_MESSAGE_HDR_FUNCTION_PORT_FACTS: @@ -829,10 +1130,10 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr pReply->PortFacts.u8MessageLength = 10; pReply->PortFacts.u8PortNumber = pPortFactsReq->u8PortNumber; - if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) + if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) { /* This controller only supports one bus with bus number 0. */ - if (pPortFactsReq->u8PortNumber >= pLsiLogic->cPorts) + if (pPortFactsReq->u8PortNumber >= pThis->cPorts) { pReply->PortFacts.u8PortType = 0; /* Not existant. */ } @@ -847,25 +1148,25 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr pReply->PortFacts.u16MaxLANBuckets = 0; /* Only for the LAN controller. */ } } - else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) + else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) { - if (pPortFactsReq->u8PortNumber >= pLsiLogic->cPorts) + if (pPortFactsReq->u8PortNumber >= pThis->cPorts) { pReply->PortFacts.u8PortType = 0; /* Not existant. */ } else { pReply->PortFacts.u8PortType = 0x30; /* SAS Port. */ - pReply->PortFacts.u16MaxDevices = pLsiLogic->cPorts; + pReply->PortFacts.u16MaxDevices = pThis->cPorts; pReply->PortFacts.u16ProtocolFlags = RT_BIT(3) | RT_BIT(0); /* SCSI initiator and LUN supported. */ - pReply->PortFacts.u16PortSCSIID = pLsiLogic->cPorts; + pReply->PortFacts.u16PortSCSIID = pThis->cPorts; pReply->PortFacts.u16MaxPersistentIDs = 0; pReply->PortFacts.u16MaxPostedCmdBuffers = 0; /* Only applies for target mode which we dont support. */ pReply->PortFacts.u16MaxLANBuckets = 0; /* Only for the LAN controller. */ } } else - AssertMsgFailed(("Invalid controller type %d\n", pLsiLogic->enmCtrlType)); + AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType)); break; } case MPT_MESSAGE_HDR_FUNCTION_PORT_ENABLE: @@ -885,9 +1186,9 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr PMptEventNotificationRequest pEventNotificationReq = (PMptEventNotificationRequest)pMessageHdr; if (pEventNotificationReq->u8Switch) - pLsiLogic->fEventNotificationEnabled = true; + pThis->fEventNotificationEnabled = true; else - pLsiLogic->fEventNotificationEnabled = false; + pThis->fEventNotificationEnabled = false; pReply->EventNotification.u16EventDataLength = 1; /* 1 32bit D-Word. */ pReply->EventNotification.u8MessageLength = 8; @@ -895,7 +1196,7 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr pReply->EventNotification.u8AckRequired = 0; pReply->EventNotification.u32Event = MPT_EVENT_EVENT_CHANGE; pReply->EventNotification.u32EventContext = 0; - pReply->EventNotification.u32EventData = pLsiLogic->fEventNotificationEnabled ? 1 : 0; + pReply->EventNotification.u32EventData = pThis->fEventNotificationEnabled ? 1 : 0; break; } @@ -908,7 +1209,7 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr { PMptConfigurationRequest pConfigurationReq = (PMptConfigurationRequest)pMessageHdr; - rc = lsilogicProcessConfigurationRequest(pLsiLogic, pConfigurationReq, &pReply->Configuration); + rc = lsilogicR3ProcessConfigurationRequest(pThis, pConfigurationReq, &pReply->Configuration); AssertRC(rc); break; } @@ -926,6 +1227,7 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr //PMptFWDownloadRequest pFWDownloadReq = (PMptFWDownloadRequest)pMessageHdr; pReply->FWDownload.u8MessageLength = 5; + LogFlowFunc(("FW Download request issued\n")); break; } case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST: /* Should be handled already. */ @@ -937,27 +1239,24 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr pReply->Header.u8Function = pMessageHdr->u8Function; pReply->Header.u32MessageContext = pMessageHdr->u32MessageContext; - lsilogicFinishAddressReply(pLsiLogic, pReply, fForceReplyPostFifo); + lsilogicFinishAddressReply(pThis, pReply, fForceReplyPostFifo); return rc; } -#endif + +#endif /* IN_RING3 */ /** * Writes a value to a register at a given offset. * * @returns VBox status code. - * @param pThis Pointer to the LsiLogic SCSI controller instance data. - * @param uOffset Offset of the register to write. - * @param pv Pointer to the value to write - * @param cb Number of bytes to write. + * @param pThis Pointer to the LsiLogic device state. + * @param offReg Offset of the register to write. + * @param u32 The value being written. */ -static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void const *pv, unsigned cb) +static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t offReg, uint32_t u32) { - uint32_t u32 = *(uint32_t *)pv; - - LogFlowFunc(("pThis=%#p uOffset=%#x pv=%#p{%.*Rhxs} cb=%u\n", pThis, uOffset, pv, cb, pv, cb)); - - switch (uOffset) + LogFlowFunc(("pThis=%#p offReg=%#x u32=%#x\n", pThis, offReg, u32)); + switch (offReg) { case LSILOGIC_REG_REPLY_QUEUE: { @@ -986,10 +1285,16 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con /* Send notification to R3 if there is not one send already. */ if (!ASMAtomicXchgBool(&pThis->fNotificationSend, true)) { +#ifdef IN_RC PPDMQUEUEITEMCORE pNotificationItem = PDMQueueAlloc(pThis->CTX_SUFF(pNotificationQueue)); AssertPtr(pNotificationItem); PDMQueueInsert(pThis->CTX_SUFF(pNotificationQueue), pNotificationItem); +#else + LogFlowFunc(("Signal event semaphore\n")); + int rc = SUPSemEventSignal(pThis->pSupDrvSession, pThis->hEvtProcess); + AssertRC(rc); +#endif } break; } @@ -1002,14 +1307,20 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con * The guest needs to wait with posting new messages here until the bit is cleared. * Because the guest is not continuing execution while we are here we can skip this. */ - if (!pThis->fDoorbellInProgress) + if (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_NOT_IN_USE) { uint32_t uFunction = LSILOGIC_REG_DOORBELL_GET_FUNCTION(u32); switch (uFunction) { + case LSILOGIC_DOORBELL_FUNCTION_IO_UNIT_RESET: case LSILOGIC_DOORBELL_FUNCTION_IOC_MSG_UNIT_RESET: { + /* + * The I/O unit reset does much more on real hardware like + * reloading the firmware, nothing we need to do here, + * so this is like the IOC message unit reset. + */ pThis->enmState = LSILOGICSTATE_RESET; /* Reset interrupt status. */ @@ -1023,12 +1334,10 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con pThis->uReplyPostQueueNextAddressRead = 0; pThis->uRequestQueueNextEntryFreeWrite = 0; pThis->uRequestQueueNextAddressRead = 0; - pThis->enmState = LSILOGICSTATE_READY; - break; - } - case LSILOGIC_DOORBELL_FUNCTION_IO_UNIT_RESET: - { - AssertMsgFailed(("todo\n")); + + /* Only the IOC message unit reset transisionts to the ready state. */ + if (uFunction == LSILOGIC_DOORBELL_FUNCTION_IOC_MSG_UNIT_RESET) + pThis->enmState = LSILOGICSTATE_READY; break; } case LSILOGIC_DOORBELL_FUNCTION_HANDSHAKE: @@ -1037,21 +1346,23 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con pThis->iMessage = 0; AssertMsg(pThis->cMessage <= RT_ELEMENTS(pThis->aMessage), ("Message doesn't fit into the buffer, cMessage=%u", pThis->cMessage)); - pThis->fDoorbellInProgress = true; + pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_FN_HANDSHAKE; /* Update the interrupt status to notify the guest that a doorbell function was started. */ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL); break; } case LSILOGIC_DOORBELL_FUNCTION_REPLY_FRAME_REMOVAL: { - AssertMsgFailed(("todo\n")); + pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_LOW; + /* Update the interrupt status to notify the guest that a doorbell function was started. */ + lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL); break; } default: AssertMsgFailed(("Unknown function %u to perform\n", uFunction)); } } - else + else if (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_FN_HANDSHAKE) { /* * We are already performing a doorbell function. @@ -1070,7 +1381,7 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con #ifdef IN_RING3 if (pThis->iMessage == pThis->cMessage) { - int rc = lsilogicProcessMessageRequest(pThis, (PMptMessageHdr)pThis->aMessage, &pThis->ReplyBuffer); + int rc = lsilogicR3ProcessMessageRequest(pThis, (PMptMessageHdr)pThis->aMessage, &pThis->ReplyBuffer); AssertRC(rc); } #endif @@ -1093,17 +1404,26 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con * We do not use lsilogicSetInterrupt here because the interrupt status * is updated afterwards anyway. */ - if ( (pThis->fDoorbellInProgress) + if ( (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_FN_HANDSHAKE) && (pThis->cMessage == pThis->iMessage)) { if (pThis->uNextReplyEntryRead == pThis->cReplySize) { /* Reply finished. Reset doorbell in progress status. */ Log(("%s: Doorbell function finished\n", __FUNCTION__)); - pThis->fDoorbellInProgress = false; + pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE; } ASMAtomicOrU32(&pThis->uInterruptStatus, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL); } + else if ( pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_NOT_IN_USE + && pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_FN_HANDSHAKE) + { + /* Reply frame removal, check whether the reply free queue is empty. */ + if ( pThis->uReplyFreeQueueNextAddressRead == pThis->uReplyFreeQueueNextEntryFreeWrite + && pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW) + pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE; + ASMAtomicOrU32(&pThis->uInterruptStatus, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL); + } lsilogicUpdateInterrupt(pThis); break; @@ -1121,6 +1441,7 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con /* Any value will cause a reset and disabling access. */ pThis->fDiagnosticEnabled = false; pThis->iDiagnosticAccess = 0; + pThis->fDiagRegsEnabled = false; } else if ((u32 & 0xf) == g_lsilogicDiagnosticAccess[pThis->iDiagnosticAccess]) { @@ -1143,15 +1464,42 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con } case LSILOGIC_REG_HOST_DIAGNOSTIC: { + if (pThis->fDiagnosticEnabled) + { #ifndef IN_RING3 - return VINF_IOM_R3_IOPORT_WRITE; + return VINF_IOM_R3_MMIO_WRITE; #else - if (u32 & LSILOGIC_REG_HOST_DIAGNOSTIC_RESET_ADAPTER) + if (u32 & LSILOGIC_REG_HOST_DIAGNOSTIC_RESET_ADAPTER) + lsilogicR3HardReset(pThis); + else if (u32 & LSILOGIC_REG_HOST_DIAGNOSTIC_DIAG_RW_ENABLE) + pThis->fDiagRegsEnabled = true; +#endif + } + break; + } + case LSILOGIC_REG_DIAG_RW_DATA: + { + if (pThis->fDiagRegsEnabled) { - lsilogicHardReset(pThis); +#ifndef IN_RING3 + return VINF_IOM_R3_MMIO_WRITE; +#else + lsilogicR3DiagRegDataWrite(pThis, u32); +#endif } break; + } + case LSILOGIC_REG_DIAG_RW_ADDRESS: + { + if (pThis->fDiagRegsEnabled) + { +#ifndef IN_RING3 + return VINF_IOM_R3_MMIO_WRITE; +#else + lsilogicR3DiagRegAddressWrite(pThis, u32); #endif + } + break; } default: /* Ignore. */ { @@ -1165,28 +1513,21 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con * Reads the content of a register at a given offset. * * @returns VBox status code. - * @param pThis Pointer to the LsiLogic SCSI controller instance data. - * @param uOffset Offset of the register to read. - * @param pv Where to store the content of the register. - * @param cb Number of bytes to read. + * @param pThis Pointer to the LsiLogic device state. + * @param offReg Offset of the register to read. + * @param pu32 Where to store the content of the register. */ -static int lsilogicRegisterRead(PLSILOGICSCSI pThis, uint32_t uOffset, void *pv, unsigned cb) +static int lsilogicRegisterRead(PLSILOGICSCSI pThis, uint32_t offReg, uint32_t *pu32) { int rc = VINF_SUCCESS; uint32_t u32 = 0; + Assert(!(offReg & 3)); /* Align to a 4 byte offset. */ - switch (uOffset & ~3) + switch (offReg) { case LSILOGIC_REG_REPLY_QUEUE: { - /* - * Non 4-byte access may cause real strange behavior because the data is part of a physical guest address. - * But some drivers use 1-byte access to scan for SCSI controllers. - */ - if (RT_UNLIKELY(cb != 4)) - LogFlowFunc((": cb is not 4 (%u)\n", cb)); - rc = PDMCritSectEnter(&pThis->ReplyPostQueueCritSect, VINF_IOM_R3_MMIO_READ); if (rc != VINF_SUCCESS) break; @@ -1215,23 +1556,61 @@ static int lsilogicRegisterRead(PLSILOGICSCSI pThis, uint32_t uOffset, void *pv, case LSILOGIC_REG_DOORBELL: { u32 = LSILOGIC_REG_DOORBELL_SET_STATE(pThis->enmState); - u32 |= LSILOGIC_REG_DOORBELL_SET_USED(pThis->fDoorbellInProgress); + u32 |= LSILOGIC_REG_DOORBELL_SET_USED(pThis->enmDoorbellState); u32 |= LSILOGIC_REG_DOORBELL_SET_WHOINIT(pThis->enmWhoInit); /* * If there is a doorbell function in progress we pass the return value * instead of the status code. We transfer 16bit of the reply * during one read. */ - if (pThis->fDoorbellInProgress) + switch (pThis->enmDoorbellState) { - /* Return next 16bit value. */ - u32 |= pThis->ReplyBuffer.au16Reply[pThis->uNextReplyEntryRead++]; - } - else - { - /* We return the status code of the I/O controller. */ - u32 |= pThis->u16IOCFaultCode; + case LSILOGICDOORBELLSTATE_NOT_IN_USE: + /* We return the status code of the I/O controller. */ + u32 |= pThis->u16IOCFaultCode; + break; + case LSILOGICDOORBELLSTATE_FN_HANDSHAKE: + /* Return next 16bit value. */ + u32 |= pThis->ReplyBuffer.au16Reply[pThis->uNextReplyEntryRead++]; + lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL); + break; + case LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_LOW: + { + uint32_t cReplyFrames = lsilogicReplyFreeQueueGetFrameCount(pThis); + + u32 |= cReplyFrames & UINT32_C(0xffff); + pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_HIGH; + lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL); + break; + } + case LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_HIGH: + { + uint32_t cReplyFrames = lsilogicReplyFreeQueueGetFrameCount(pThis); + + u32 |= cReplyFrames >> 16; + pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW; + lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL); + break; + } + case LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW: + if (pThis->uReplyFreeQueueNextEntryFreeWrite != pThis->uReplyFreeQueueNextAddressRead) + { + u32 |= pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead] & UINT32_C(0xffff); + pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_HIGH; + lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL); + } + break; + case LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_HIGH: + u32 |= pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead] >> 16; + pThis->uReplyFreeQueueNextAddressRead++; + pThis->uReplyFreeQueueNextAddressRead %= pThis->cReplyQueueEntries; + pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW; + lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL); + break; + default: + AssertMsgFailed(("Invalid doorbell state %d\n", pThis->enmDoorbellState)); } + break; } case LSILOGIC_REG_HOST_INTR_STATUS: @@ -1247,99 +1626,130 @@ static int lsilogicRegisterRead(PLSILOGICSCSI pThis, uint32_t uOffset, void *pv, case LSILOGIC_REG_HOST_DIAGNOSTIC: { if (pThis->fDiagnosticEnabled) - u32 = LSILOGIC_REG_HOST_DIAGNOSTIC_DRWE; - else - u32 = 0; + u32 |= LSILOGIC_REG_HOST_DIAGNOSTIC_DRWE; + if (pThis->fDiagRegsEnabled) + u32 |= LSILOGIC_REG_HOST_DIAGNOSTIC_DIAG_RW_ENABLE; break; } - case LSILOGIC_REG_TEST_BASE_ADDRESS: /* The spec doesn't say anything about these registers, so we just ignore them */ case LSILOGIC_REG_DIAG_RW_DATA: - case LSILOGIC_REG_DIAG_RW_ADDRESS: - default: /* Ignore. */ { - break; - } - } - - /* Clip data according to the read size. */ - switch (cb) - { - case 4: - { - *(uint32_t *)pv = u32; - break; + if (pThis->fDiagRegsEnabled) + { +#ifndef IN_RING3 + return VINF_IOM_R3_MMIO_READ; +#else + lsilogicR3DiagRegDataRead(pThis, &u32); +#endif + } } - case 2: + case LSILOGIC_REG_DIAG_RW_ADDRESS: { - uint8_t uBitsOff = (uOffset - (uOffset & 3))*8; - - u32 &= (0xffff << uBitsOff); - *(uint16_t *)pv = (uint16_t)(u32 >> uBitsOff); - break; + if (pThis->fDiagRegsEnabled) + { +#ifndef IN_RING3 + return VINF_IOM_R3_MMIO_READ; +#else + lsilogicR3DiagRegAddressRead(pThis, &u32); +#endif + } } - case 1: + case LSILOGIC_REG_TEST_BASE_ADDRESS: /* The spec doesn't say anything about these registers, so we just ignore them */ + default: /* Ignore. */ { - uint8_t uBitsOff = (uOffset - (uOffset & 3))*8; - - u32 &= (0xff << uBitsOff); - *(uint8_t *)pv = (uint8_t)(u32 >> uBitsOff); + /** @todo LSILOGIC_REG_DIAG_* should return all F's when accessed by MMIO. We + * return 0. Likely to apply to undefined offsets as well. */ break; } - default: - AssertMsgFailed(("Invalid access size %u\n", cb)); } - LogFlowFunc(("pThis=%#p uOffset=%#x pv=%#p{%.*Rhxs} cb=%u\n", pThis, uOffset, pv, cb, pv, cb)); - + *pu32 = u32; + LogFlowFunc(("pThis=%#p offReg=%#x u32=%#x\n", pThis, offReg, u32)); return rc; } -PDMBOTHCBDECL(int) lsilogicIOPortWrite (PPDMDEVINS pDevIns, void *pvUser, - RTIOPORT Port, uint32_t u32, unsigned cb) +/** + * @callback_method_impl{FNIOMIOPORTOUT} + */ +PDMBOTHCBDECL(int) lsilogicIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb) { - PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); - uint32_t uOffset = Port - pThis->IOPortBase; - - Assert(cb <= 4); + PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); + uint32_t offReg = Port - pThis->IOPortBase; + int rc; - int rc = lsilogicRegisterWrite(pThis, uOffset, &u32, cb); - if (rc == VINF_IOM_R3_MMIO_WRITE) - rc = VINF_IOM_R3_IOPORT_WRITE; + if (!(offReg & 3)) + { + rc = lsilogicRegisterWrite(pThis, offReg, u32); + if (rc == VINF_IOM_R3_MMIO_WRITE) + rc = VINF_IOM_R3_IOPORT_WRITE; + } + else + { + Log(("lsilogicIOPortWrite: Ignoring misaligned write - offReg=%#x u32=%#x cb=%#x\n", offReg, u32, cb)); + rc = VINF_SUCCESS; + } return rc; } -PDMBOTHCBDECL(int) lsilogicIOPortRead (PPDMDEVINS pDevIns, void *pvUser, - RTIOPORT Port, uint32_t *pu32, unsigned cb) +/** + * @callback_method_impl{FNIOMIOPORTIN} + */ +PDMBOTHCBDECL(int) lsilogicIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb) { - PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); - uint32_t uOffset = Port - pThis->IOPortBase; + PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); + uint32_t offReg = Port - pThis->IOPortBase; - Assert(cb <= 4); - - int rc = lsilogicRegisterRead(pThis, uOffset, pu32, cb); + int rc = lsilogicRegisterRead(pThis, offReg & ~(uint32_t)3, pu32); if (rc == VINF_IOM_R3_MMIO_READ) rc = VINF_IOM_R3_IOPORT_READ; return rc; } -PDMBOTHCBDECL(int) lsilogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, - RTGCPHYS GCPhysAddr, void const *pv, unsigned cb) +/** + * @callback_method_impl{FNIOMMMIOWRITE} + */ +PDMBOTHCBDECL(int) lsilogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb) { - PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); - uint32_t uOffset = GCPhysAddr - pThis->GCPhysMMIOBase; + PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); + uint32_t offReg = GCPhysAddr - pThis->GCPhysMMIOBase; + uint32_t u32; + int rc; + + /* See comments in lsilogicR3Map regarding size and alignment. */ + if (cb == 4) + u32 = *(uint32_t const *)pv; + else + { + if (cb > 4) + u32 = *(uint32_t const *)pv; + else if (cb >= 2) + u32 = *(uint16_t const *)pv; + else + u32 = *(uint8_t const *)pv; + Log(("lsilogicMMIOWrite: Non-DWORD write access - offReg=%#x u32=%#x cb=%#x\n", offReg, u32, cb)); + } - return lsilogicRegisterWrite(pThis, uOffset, pv, cb); + if (!(offReg & 3)) + rc = lsilogicRegisterWrite(pThis, offReg, u32); + else + { + Log(("lsilogicIOPortWrite: Ignoring misaligned write - offReg=%#x u32=%#x cb=%#x\n", offReg, u32, cb)); + rc = VINF_SUCCESS; + } + return rc; } -PDMBOTHCBDECL(int) lsilogicMMIORead(PPDMDEVINS pDevIns, void *pvUser, - RTGCPHYS GCPhysAddr, void *pv, unsigned cb) +/** + * @callback_method_impl{FNIOMMMIOREAD} + */ +PDMBOTHCBDECL(int) lsilogicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb) { - PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); - uint32_t uOffset = GCPhysAddr - pThis->GCPhysMMIOBase; + PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); + uint32_t offReg = GCPhysAddr - pThis->GCPhysMMIOBase; + Assert(!(offReg & 3)); Assert(cb == 4); - return lsilogicRegisterRead(pThis, uOffset, pv, cb); + return lsilogicRegisterRead(pThis, offReg, (uint32_t *)pv); } PDMBOTHCBDECL(int) lsilogicDiagnosticWrite(PPDMDEVINS pDevIns, void *pvUser, @@ -1364,545 +1774,320 @@ PDMBOTHCBDECL(int) lsilogicDiagnosticRead(PPDMDEVINS pDevIns, void *pvUser, #ifdef IN_RING3 +# ifdef LOG_ENABLED /** - * Copies a contiguous buffer into the scatter gather list provided by the guest. + * Dump an SG entry. * - * @returns nothing - * @param pTaskState Pointer to the task state which contains the SGL. - * @param pvBuf Pointer to the buffer to copy. - * @param cbCopy Number of bytes to copy. + * @returns nothing. + * @param pSGEntry Pointer to the SG entry to dump */ -static void lsilogicScatterGatherListCopyFromBuffer(PLSILOGICTASKSTATE pTaskState, void *pvBuf, size_t cbCopy) +static void lsilogicDumpSGEntry(PMptSGEntryUnion pSGEntry) { - unsigned cSGEntry = 0; - PRTSGSEG pSGEntry = &pTaskState->pSGListHead[cSGEntry]; - uint8_t *pu8Buf = (uint8_t *)pvBuf; - - while (cSGEntry < pTaskState->cSGListEntries) + if (LogIsEnabled()) { - size_t cbToCopy = (cbCopy < pSGEntry->cbSeg) ? cbCopy : pSGEntry->cbSeg; - - memcpy(pSGEntry->pvSeg, pu8Buf, cbToCopy); - - cbCopy -= cbToCopy; - /* We finished. */ - if (!cbCopy) - break; - - /* Advance the buffer. */ - pu8Buf += cbToCopy; + switch (pSGEntry->Simple32.u2ElementType) + { + case MPTSGENTRYTYPE_SIMPLE: + { + Log(("%s: Dumping info for SIMPLE SG entry:\n", __FUNCTION__)); + Log(("%s: u24Length=%u\n", __FUNCTION__, pSGEntry->Simple32.u24Length)); + Log(("%s: fEndOfList=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfList)); + Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.f64BitAddress)); + Log(("%s: fBufferContainsData=%d\n", __FUNCTION__, pSGEntry->Simple32.fBufferContainsData)); + Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.fLocalAddress)); + Log(("%s: fEndOfBuffer=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfBuffer)); + Log(("%s: fLastElement=%d\n", __FUNCTION__, pSGEntry->Simple32.fLastElement)); + Log(("%s: u32DataBufferAddressLow=%u\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow)); + if (pSGEntry->Simple32.f64BitAddress) + { + Log(("%s: u32DataBufferAddressHigh=%u\n", __FUNCTION__, pSGEntry->Simple64.u32DataBufferAddressHigh)); + Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__, + ((uint64_t)pSGEntry->Simple64.u32DataBufferAddressHigh << 32) + | pSGEntry->Simple64.u32DataBufferAddressLow)); + } + else + Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow)); - /* Go to the next entry in the list. */ - pSGEntry++; - cSGEntry++; + break; + } + case MPTSGENTRYTYPE_CHAIN: + { + Log(("%s: Dumping info for CHAIN SG entry:\n", __FUNCTION__)); + Log(("%s: u16Length=%u\n", __FUNCTION__, pSGEntry->Chain.u16Length)); + Log(("%s: u8NExtChainOffset=%d\n", __FUNCTION__, pSGEntry->Chain.u8NextChainOffset)); + Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Chain.f64BitAddress)); + Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Chain.fLocalAddress)); + Log(("%s: u32SegmentAddressLow=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow)); + Log(("%s: u32SegmentAddressHigh=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressHigh)); + if (pSGEntry->Chain.f64BitAddress) + Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__, + ((uint64_t)pSGEntry->Chain.u32SegmentAddressHigh << 32) | pSGEntry->Chain.u32SegmentAddressLow)); + else + Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow)); + break; + } + } } } +# endif /* LOG_ENABLED */ /** - * Copy a temporary buffer into a part of the guest scatter gather list - * described by the given descriptor entry. + * Walks the guest S/G buffer calling the given copy worker for every buffer. * * @returns nothing. - * @param pDevIns Pointer to the device instance data. - * @param pSGInfo Pointer to the segment info structure which describes the guest segments - * to write to which are unaligned. + * @param pDevIns Device instance data. + * @param pLsiReq LSI request state. + * @param cbCopy How much bytes to copy. + * @param pfnIoBufCopy Copy worker to call. */ -static void lsilogicCopyFromBufferIntoSGList(PPDMDEVINS pDevIns, PLSILOGICTASKSTATESGENTRY pSGInfo) +static void lsilogicSgBufWalker(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq, size_t cbCopy, + PFNLSILOGICIOBUFCOPY pfnIoBufCopy) { - RTGCPHYS GCPhysBuffer = pSGInfo->u.GCPhysAddrBufferUnaligned; + bool fEndOfList = false; + RTGCPHYS GCPhysSgEntryNext = pLsiReq->GCPhysSgStart; + RTGCPHYS GCPhysSegmentStart = pLsiReq->GCPhysSgStart; + uint32_t cChainOffsetNext = pLsiReq->cChainOffset; + uint8_t *pbBuf = (uint8_t *)pLsiReq->SegIoBuf.pvSeg; + + /* Go through the list until we reach the end. */ + while ( !fEndOfList + && cbCopy) + { + bool fEndOfSegment = false; - AssertMsg(!pSGInfo->fGuestMemory, ("This is not possible\n")); + while ( !fEndOfSegment + && cbCopy) + { + MptSGEntryUnion SGEntry; - /* Copy into SG entry. */ - PDMDevHlpPhysWrite(pDevIns, GCPhysBuffer, pSGInfo->pvBuf, pSGInfo->cbBuf); + Log(("%s: Reading SG entry from %RGp\n", __FUNCTION__, GCPhysSgEntryNext)); -} + /* Read the entry. */ + PDMDevHlpPhysRead(pDevIns, GCPhysSgEntryNext, &SGEntry, sizeof(MptSGEntryUnion)); -/** - * Copy a part of the guest scatter gather list into a temporary buffer. - * - * @returns nothing. - * @param pDevIns Pointer to the device instance data. - * @param pSGInfo Pointer to the segment info structure which describes the guest segments - * to read from which are unaligned. - */ -static void lsilogicCopyFromSGListIntoBuffer(PPDMDEVINS pDevIns, PLSILOGICTASKSTATESGENTRY pSGInfo) -{ - RTGCPHYS GCPhysBuffer = pSGInfo->u.GCPhysAddrBufferUnaligned; +# ifdef LOG_ENABLED + lsilogicDumpSGEntry(&SGEntry); +# endif - AssertMsg(!pSGInfo->fGuestMemory, ("This is not possible\n")); + AssertMsg(SGEntry.Simple32.u2ElementType == MPTSGENTRYTYPE_SIMPLE, ("Invalid SG entry type\n")); - /* Copy into temporary buffer. */ - PDMDevHlpPhysRead(pDevIns, GCPhysBuffer, pSGInfo->pvBuf, pSGInfo->cbBuf); -} + /* Check if this is a zero element and abort. */ + if ( !SGEntry.Simple32.u24Length + && SGEntry.Simple32.fEndOfList + && SGEntry.Simple32.fEndOfBuffer) + return; -static int lsilogicScatterGatherListAllocate(PLSILOGICTASKSTATE pTaskState, uint32_t cSGList, uint32_t cSGInfo, uint32_t cbUnaligned) -{ - if (pTaskState->cSGListSize < cSGList) - { - /* The entries are not allocated yet or the number is too small. */ - if (pTaskState->cSGListSize) - RTMemFree(pTaskState->pSGListHead); - - /* Allocate R3 scatter gather list. */ - pTaskState->pSGListHead = (PRTSGSEG)RTMemAllocZ(cSGList * sizeof(RTSGSEG)); - if (!pTaskState->pSGListHead) - return VERR_NO_MEMORY; - - /* Reset usage statistics. */ - pTaskState->cSGListSize = cSGList; - pTaskState->cSGListEntries = cSGList; - pTaskState->cSGListTooBig = 0; - } - else if (pTaskState->cSGListSize > cSGList) - { - /* - * The list is too big. Increment counter. - * So that the destroying function can free - * the list if it is too big too many times - * in a row. - */ - pTaskState->cSGListEntries = cSGList; - pTaskState->cSGListTooBig++; - } - else - { - /* - * Needed entries matches current size. - * Reset counter. - */ - pTaskState->cSGListEntries = cSGList; - pTaskState->cSGListTooBig = 0; - } + uint32_t cbCopyThis = SGEntry.Simple32.u24Length; + RTGCPHYS GCPhysAddrDataBuffer = SGEntry.Simple32.u32DataBufferAddressLow; - if (pTaskState->cSGInfoSize < cSGInfo) - { - /* The entries are not allocated yet or the number is too small. */ - if (pTaskState->cSGInfoSize) - RTMemFree(pTaskState->paSGEntries); - - pTaskState->paSGEntries = (PLSILOGICTASKSTATESGENTRY)RTMemAllocZ(cSGInfo * sizeof(LSILOGICTASKSTATESGENTRY)); - if (!pTaskState->paSGEntries) - return VERR_NO_MEMORY; - - /* Reset usage statistics. */ - pTaskState->cSGInfoSize = cSGInfo; - pTaskState->cSGInfoEntries = cSGInfo; - pTaskState->cSGInfoTooBig = 0; - } - else if (pTaskState->cSGInfoSize > cSGInfo) - { - /* - * The list is too big. Increment counter. - * So that the destroying function can free - * the list if it is too big too many times - * in a row. - */ - pTaskState->cSGInfoEntries = cSGInfo; - pTaskState->cSGInfoTooBig++; - } - else - { - /* - * Needed entries matches current size. - * Reset counter. - */ - pTaskState->cSGInfoEntries = cSGInfo; - pTaskState->cSGInfoTooBig = 0; - } + if (SGEntry.Simple32.f64BitAddress) + { + GCPhysAddrDataBuffer |= ((uint64_t)SGEntry.Simple64.u32DataBufferAddressHigh) << 32; + GCPhysSgEntryNext += sizeof(MptSGEntrySimple64); + } + else + GCPhysSgEntryNext += sizeof(MptSGEntrySimple32); - if (pTaskState->cbBufferUnaligned < cbUnaligned) - { - if (pTaskState->pvBufferUnaligned) - RTMemPageFree(pTaskState->pvBufferUnaligned, pTaskState->cbBufferUnaligned); + pfnIoBufCopy(pDevIns, GCPhysAddrDataBuffer, pbBuf, cbCopyThis); + pbBuf += cbCopyThis; + cbCopy -= cbCopyThis; - Log(("%s: Allocating buffer for unaligned segments cbUnaligned=%u\n", __FUNCTION__, cbUnaligned)); + /* Check if we reached the end of the list. */ + if (SGEntry.Simple32.fEndOfList) + { + /* We finished. */ + fEndOfSegment = true; + fEndOfList = true; + } + else if (SGEntry.Simple32.fLastElement) + fEndOfSegment = true; + } /* while (!fEndOfSegment) */ - pTaskState->pvBufferUnaligned = RTMemPageAlloc(cbUnaligned); - if (!pTaskState->pvBufferUnaligned) - return VERR_NO_MEMORY; + /* Get next chain element. */ + if (cChainOffsetNext) + { + MptSGEntryChain SGEntryChain; - pTaskState->cbBufferUnaligned = cbUnaligned; - } + PDMDevHlpPhysRead(pDevIns, GCPhysSegmentStart + cChainOffsetNext, &SGEntryChain, sizeof(MptSGEntryChain)); - /* Make debugging easier. */ -#ifdef DEBUG - memset(pTaskState->pSGListHead, 0, pTaskState->cSGListSize * sizeof(RTSGSEG)); - memset(pTaskState->paSGEntries, 0, pTaskState->cSGInfoSize * sizeof(LSILOGICTASKSTATESGENTRY)); - if (pTaskState->pvBufferUnaligned) - memset(pTaskState->pvBufferUnaligned, 0, pTaskState->cbBufferUnaligned); -#endif - return VINF_SUCCESS; + AssertMsg(SGEntryChain.u2ElementType == MPTSGENTRYTYPE_CHAIN, ("Invalid SG entry type\n")); + + /* Set the next address now. */ + GCPhysSgEntryNext = SGEntryChain.u32SegmentAddressLow; + if (SGEntryChain.f64BitAddress) + GCPhysSgEntryNext |= ((uint64_t)SGEntryChain.u32SegmentAddressHigh) << 32; + + GCPhysSegmentStart = GCPhysSgEntryNext; + cChainOffsetNext = SGEntryChain.u8NextChainOffset * sizeof(uint32_t); + } + } /* while (!fEndOfList) */ +} + +static DECLCALLBACK(void) lsilogicCopyFromGuest(PPDMDEVINS pDevIns, RTGCPHYS GCPhysIoBuf, + void *pvBuf, size_t cbCopy) +{ + PDMDevHlpPhysRead(pDevIns, GCPhysIoBuf, pvBuf, cbCopy); +} + +static DECLCALLBACK(void) lsilogicCopyToGuest(PPDMDEVINS pDevIns, RTGCPHYS GCPhysIoBuf, + void *pvBuf, size_t cbCopy) +{ + PDMDevHlpPCIPhysWrite(pDevIns, GCPhysIoBuf, pvBuf, cbCopy); } /** - * Destroy a scatter gather list. + * Copy from a guest S/G buffer to the I/O buffer. * * @returns nothing. - * @param pLsiLogic Pointer to the LsiLogic SCSI controller. - * @param pTaskState Pointer to the task state. + * @param pDevIns Device instance data. + * @param pLsiReq Request data. + * @param cbCopy How much to copy over. */ -static void lsilogicScatterGatherListDestroy(PLSILOGICSCSI pLsiLogic, PLSILOGICTASKSTATE pTaskState) +DECLINLINE(void) lsilogicCopyFromSgBuf(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq, size_t cbCopy) { - PPDMDEVINS pDevIns = pLsiLogic->CTX_SUFF(pDevIns); - PLSILOGICTASKSTATESGENTRY pSGInfoCurr = pTaskState->paSGEntries; - - for (unsigned i = 0; i < pTaskState->cSGInfoEntries; i++) - { - if (pSGInfoCurr->fGuestMemory) - { - /* Release the lock. */ - PDMDevHlpPhysReleasePageMappingLock(pDevIns, &pSGInfoCurr->u.PageLock); - } - else if (!pSGInfoCurr->fBufferContainsData) - { - /* Copy the data into the guest segments now. */ - lsilogicCopyFromBufferIntoSGList(pLsiLogic->CTX_SUFF(pDevIns), pSGInfoCurr); - } - - pSGInfoCurr++; - } - - /* Free allocated memory if the list was too big too many times. */ - if (pTaskState->cSGListTooBig >= LSILOGIC_NR_OF_ALLOWED_BIGGER_LISTS) - lsilogicTaskStateClear(pTaskState); + lsilogicSgBufWalker(pDevIns, pLsiReq, cbCopy, lsilogicCopyFromGuest); } -#ifdef DEBUG /** - * Dump an SG entry. + * Copy from an I/O buffer to the guest S/G buffer. * * @returns nothing. - * @param pSGEntry Pointer to the SG entry to dump + * @param pDevIns Device instance data. + * @param pLsiReq Request data. + * @param cbCopy How much to copy over. */ -static void lsilogicDumpSGEntry(PMptSGEntryUnion pSGEntry) +DECLINLINE(void) lsilogicCopyToSgBuf(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq, size_t cbCopy) { - switch (pSGEntry->Simple32.u2ElementType) - { - case MPTSGENTRYTYPE_SIMPLE: - { - Log(("%s: Dumping info for SIMPLE SG entry:\n", __FUNCTION__)); - Log(("%s: u24Length=%u\n", __FUNCTION__, pSGEntry->Simple32.u24Length)); - Log(("%s: fEndOfList=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfList)); - Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.f64BitAddress)); - Log(("%s: fBufferContainsData=%d\n", __FUNCTION__, pSGEntry->Simple32.fBufferContainsData)); - Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.fLocalAddress)); - Log(("%s: fEndOfBuffer=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfBuffer)); - Log(("%s: fLastElement=%d\n", __FUNCTION__, pSGEntry->Simple32.fLastElement)); - Log(("%s: u32DataBufferAddressLow=%u\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow)); - if (pSGEntry->Simple32.f64BitAddress) - { - Log(("%s: u32DataBufferAddressHigh=%u\n", __FUNCTION__, pSGEntry->Simple64.u32DataBufferAddressHigh)); - Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__, - ((uint64_t)pSGEntry->Simple64.u32DataBufferAddressHigh << 32) | pSGEntry->Simple64.u32DataBufferAddressLow)); - } - else - Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow)); - - break; - } - case MPTSGENTRYTYPE_CHAIN: - { - Log(("%s: Dumping info for CHAIN SG entry:\n", __FUNCTION__)); - Log(("%s: u16Length=%u\n", __FUNCTION__, pSGEntry->Chain.u16Length)); - Log(("%s: u8NExtChainOffset=%d\n", __FUNCTION__, pSGEntry->Chain.u8NextChainOffset)); - Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Chain.f64BitAddress)); - Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Chain.fLocalAddress)); - Log(("%s: u32SegmentAddressLow=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow)); - Log(("%s: u32SegmentAddressHigh=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressHigh)); - if (pSGEntry->Chain.f64BitAddress) - Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__, - ((uint64_t)pSGEntry->Chain.u32SegmentAddressHigh << 32) | pSGEntry->Chain.u32SegmentAddressLow)); - else - Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow)); - break; - } - } + lsilogicSgBufWalker(pDevIns, pLsiReq, cbCopy, lsilogicCopyToGuest); } -#endif /** - * Create scatter gather list descriptors. + * Allocates memory for the given request using already allocated memory if possible. * - * @returns VBox status code. - * @param pLsiLogic Pointer to the LsiLogic SCSI controller. - * @param pTaskState Pointer to the task state. - * @param GCPhysSGLStart Guest physical address of the first SG entry. - * @param uChainOffset Offset in bytes from the beginning of the SGL segment to the chain element. - * @thread EMT + * @returns Pointer to the memory or NULL on failure + * @param pLsiReq The request to allocate memory for. + * @param cb The amount of memory to allocate. */ -static int lsilogicScatterGatherListCreate(PLSILOGICSCSI pLsiLogic, PLSILOGICTASKSTATE pTaskState, - RTGCPHYS GCPhysSGLStart, uint32_t uChainOffset) +static void *lsilogicReqMemAlloc(PLSILOGICREQ pLsiReq, size_t cb) { - int rc = VINF_SUCCESS; - PPDMDEVINS pDevIns = pLsiLogic->CTX_SUFF(pDevIns); - PVM pVM = PDMDevHlpGetVM(pDevIns); - bool fUnaligned; /* Flag whether the current buffer is unaligned. */ - uint32_t cbUnaligned; /* Size of the unaligned buffers. */ - uint32_t cSGEntriesR3 = 0; - uint32_t cSGInfo = 0; - uint32_t cbSegment = 0; - PLSILOGICTASKSTATESGENTRY pSGInfoCurr = NULL; - uint8_t *pu8BufferUnalignedPos = NULL; - uint8_t *pbBufferUnalignedSGInfoPos = NULL; - uint32_t cbUnalignedComplete = 0; - bool fDoMapping = false; - bool fEndOfList; - RTGCPHYS GCPhysSGEntryNext; - RTGCPHYS GCPhysSegmentStart; - uint32_t uChainOffsetNext; - - /* - * Two passes - one to count needed scatter gather list entries and needed unaligned - * buffers and one to actually map the SG list into R3. - */ - for (int i = 0; i < 2; i++) - { - fUnaligned = false; - cbUnaligned = 0; - fEndOfList = false; - - GCPhysSGEntryNext = GCPhysSGLStart; - uChainOffsetNext = uChainOffset; - GCPhysSegmentStart = GCPhysSGLStart; - - if (fDoMapping) - { - Log(("%s: cSGInfo=%u\n", __FUNCTION__, cSGInfo)); - - /* The number of needed SG entries in R3 is known. Allocate needed memory. */ - rc = lsilogicScatterGatherListAllocate(pTaskState, cSGInfo, cSGInfo, cbUnalignedComplete); - AssertMsgRC(rc, ("Failed to allocate scatter gather array rc=%Rrc\n", rc)); - - /* We are now able to map the pages into R3. */ - pSGInfoCurr = pTaskState->paSGEntries; - /* Initialize first segment to remove the need for additional if checks later in the code. */ - pSGInfoCurr->fGuestMemory= false; - pu8BufferUnalignedPos = (uint8_t *)pTaskState->pvBufferUnaligned; - pbBufferUnalignedSGInfoPos = pu8BufferUnalignedPos; - } - - /* Go through the list until we reach the end. */ - while (!fEndOfList) - { - bool fEndOfSegment = false; - - while (!fEndOfSegment) - { - MptSGEntryUnion SGEntry; - - Log(("%s: Reading SG entry from %RGp\n", __FUNCTION__, GCPhysSGEntryNext)); - - /* Read the entry. */ - PDMDevHlpPhysRead(pDevIns, GCPhysSGEntryNext, &SGEntry, sizeof(MptSGEntryUnion)); - -#ifdef DEBUG - lsilogicDumpSGEntry(&SGEntry); -#endif - - AssertMsg(SGEntry.Simple32.u2ElementType == MPTSGENTRYTYPE_SIMPLE, ("Invalid SG entry type\n")); - - /* Check if this is a zero element. */ - if ( !SGEntry.Simple32.u24Length - && SGEntry.Simple32.fEndOfList - && SGEntry.Simple32.fEndOfBuffer) - { - pTaskState->cSGListEntries = 0; - pTaskState->cSGInfoEntries = 0; - return VINF_SUCCESS; - } - - uint32_t cbDataToTransfer = SGEntry.Simple32.u24Length; - bool fBufferContainsData = !!SGEntry.Simple32.fBufferContainsData; - RTGCPHYS GCPhysAddrDataBuffer = SGEntry.Simple32.u32DataBufferAddressLow; - - if (SGEntry.Simple32.f64BitAddress) - { - GCPhysAddrDataBuffer |= ((uint64_t)SGEntry.Simple64.u32DataBufferAddressHigh) << 32; - GCPhysSGEntryNext += sizeof(MptSGEntrySimple64); - } - else - GCPhysSGEntryNext += sizeof(MptSGEntrySimple32); - - if (fDoMapping) - { - pSGInfoCurr->fGuestMemory = false; - pSGInfoCurr->fBufferContainsData = fBufferContainsData; - pSGInfoCurr->cbBuf = cbDataToTransfer; - pSGInfoCurr->pvBuf = pbBufferUnalignedSGInfoPos; - pbBufferUnalignedSGInfoPos += cbDataToTransfer; - pSGInfoCurr->u.GCPhysAddrBufferUnaligned = GCPhysAddrDataBuffer; - if (fBufferContainsData) - lsilogicCopyFromSGListIntoBuffer(pDevIns, pSGInfoCurr); - pSGInfoCurr++; - } - else - { - cbUnalignedComplete += cbDataToTransfer; - cSGInfo++; - } - - /* Check if we reached the end of the list. */ - if (SGEntry.Simple32.fEndOfList) - { - /* We finished. */ - fEndOfSegment = true; - fEndOfList = true; - } - else if (SGEntry.Simple32.fLastElement) - { - fEndOfSegment = true; - } - } /* while (!fEndOfSegment) */ - - /* Get next chain element. */ - if (uChainOffsetNext) - { - MptSGEntryChain SGEntryChain; - - PDMDevHlpPhysRead(pDevIns, GCPhysSegmentStart + uChainOffsetNext, &SGEntryChain, sizeof(MptSGEntryChain)); - - AssertMsg(SGEntryChain.u2ElementType == MPTSGENTRYTYPE_CHAIN, ("Invalid SG entry type\n")); - - /* Set the next address now. */ - GCPhysSGEntryNext = SGEntryChain.u32SegmentAddressLow; - if (SGEntryChain.f64BitAddress) - GCPhysSGEntryNext |= ((uint64_t)SGEntryChain.u32SegmentAddressHigh) << 32; - - GCPhysSegmentStart = GCPhysSGEntryNext; - uChainOffsetNext = SGEntryChain.u8NextChainOffset * sizeof(uint32_t); - } - - } /* while (!fEndOfList) */ - - fDoMapping = true; - if (fUnaligned) - cbUnalignedComplete += cbUnaligned; - } - - uint32_t cSGEntries; - PRTSGSEG pSGEntryCurr = pTaskState->pSGListHead; - pSGInfoCurr = pTaskState->paSGEntries; - - /* Initialize first entry. */ - pSGEntryCurr->pvSeg = pSGInfoCurr->pvBuf; - pSGEntryCurr->cbSeg = pSGInfoCurr->cbBuf; - pSGInfoCurr++; - cSGEntries = 1; - - /* Construct the scatter gather list. */ - for (unsigned i = 0; i < (pTaskState->cSGInfoEntries-1); i++) + if (pLsiReq->cbAlloc > cb) + pLsiReq->cAllocTooMuch++; + else if (pLsiReq->cbAlloc < cb) { - if (pSGEntryCurr->cbSeg % 512 != 0) - { - AssertMsg((uint8_t *)pSGEntryCurr->pvSeg + pSGEntryCurr->cbSeg == pSGInfoCurr->pvBuf, - ("Buffer ist not sector aligned but the buffer addresses are not adjacent\n")); - - pSGEntryCurr->cbSeg += pSGInfoCurr->cbBuf; - } - else - { - if (((uint8_t *)pSGEntryCurr->pvSeg + pSGEntryCurr->cbSeg) == pSGInfoCurr->pvBuf) - { - pSGEntryCurr->cbSeg += pSGInfoCurr->cbBuf; - } - else - { - pSGEntryCurr++; - cSGEntries++; - pSGEntryCurr->pvSeg = pSGInfoCurr->pvBuf; - pSGEntryCurr->cbSeg = pSGInfoCurr->cbBuf; - } - } - - pSGInfoCurr++; + if (pLsiReq->cbAlloc) + RTMemPageFree(pLsiReq->pvAlloc, pLsiReq->cbAlloc); + + pLsiReq->cbAlloc = RT_ALIGN_Z(cb, _4K); + pLsiReq->pvAlloc = RTMemPageAlloc(pLsiReq->cbAlloc); + pLsiReq->cAllocTooMuch = 0; + if (RT_UNLIKELY(!pLsiReq->pvAlloc)) + pLsiReq->cbAlloc = 0; } - pTaskState->cSGListEntries = cSGEntries; - - return rc; + return pLsiReq->pvAlloc; } -/* - * Disabled because the sense buffer provided by the LsiLogic driver for Windows XP - * crosses page boundaries. - */ -#if 0 /** - * Free the sense buffer. + * Frees memory allocated for the given request. * * @returns nothing. - * @param pTaskState Pointer to the task state. + * @param pLsiReq The request. */ -static void lsilogicFreeGCSenseBuffer(PLSILOGICSCSI pLsiLogic, PLSILOGICTASKSTATE pTaskState) +static void lsilogicReqMemFree(PLSILOGICREQ pLsiReq) { - PVM pVM = PDMDevHlpGetVM(pLsiLogic->CTX_SUFF(pDevIns)); - - PGMPhysReleasePageMappingLock(pVM, &pTaskState->PageLockSense); - pTaskState->pbSenseBuffer = NULL; + if (pLsiReq->cAllocTooMuch >= LSILOGIC_MAX_ALLOC_TOO_MUCH) + { + RTMemPageFree(pLsiReq->pvAlloc, pLsiReq->cbAlloc); + pLsiReq->cbAlloc = 0; + pLsiReq->cAllocTooMuch = 0; + } } /** - * Map the sense buffer into R3. + * Allocate I/O memory and copies the guest buffer for writes. * * @returns VBox status code. - * @param pTaskState Pointer to the task state. - * @note Current assumption is that the sense buffer is not scattered and does not cross a page boundary. + * @param pDevIns The device instance. + * @param pLsiReq The request state. + * @param cbTransfer Amount of bytes to allocate. */ -static int lsilogicMapGCSenseBufferIntoR3(PLSILOGICSCSI pLsiLogic, PLSILOGICTASKSTATE pTaskState) +static int lsilogicIoBufAllocate(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq, + size_t cbTransfer) { - int rc = VINF_SUCCESS; - PPDMDEVINS pDevIns = pLsiLogic->CTX_SUFF(pDevIns); - RTGCPHYS GCPhysAddrSenseBuffer; + uint8_t uTxDir = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pLsiReq->GuestRequest.SCSIIO.u32Control); - GCPhysAddrSenseBuffer = pTaskState->GuestRequest.SCSIIO.u32SenseBufferLowAddress; - GCPhysAddrSenseBuffer |= ((uint64_t)pLsiLogic->u32SenseBufferHighAddr << 32); + AssertMsg( uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE + || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ + || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE, + ("Allocating I/O memory for a non I/O request is not allowed\n")); -#ifdef RT_STRICT - uint32_t cbSenseBuffer = pTaskState->GuestRequest.SCSIIO.u8SenseBufferLength; -#endif - RTGCPHYS GCPhysAddrSenseBufferBase = PAGE_ADDRESS(GCPhysAddrSenseBuffer); + pLsiReq->SegIoBuf.pvSeg = lsilogicReqMemAlloc(pLsiReq, cbTransfer); + if (!pLsiReq->SegIoBuf.pvSeg) + return VERR_NO_MEMORY; - AssertMsg(GCPhysAddrSenseBuffer >= GCPhysAddrSenseBufferBase, - ("Impossible GCPhysAddrSenseBuffer < GCPhysAddrSenseBufferBase\n")); + pLsiReq->SegIoBuf.cbSeg = cbTransfer; + if ( uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE + || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE) + lsilogicCopyFromSgBuf(pDevIns, pLsiReq, cbTransfer); - /* Sanity checks for the assumption. */ - AssertMsg(((GCPhysAddrSenseBuffer + cbSenseBuffer) <= (GCPhysAddrSenseBufferBase + PAGE_SIZE)), - ("Sense buffer crosses page boundary\n")); + return VINF_SUCCESS; +} - rc = PDMDevHlpPhysGCPhys2CCPtr(pDevIns, GCPhysAddrSenseBufferBase, (void **)&pTaskState->pbSenseBuffer, &pTaskState->PageLockSense); - AssertMsgRC(rc, ("Mapping sense buffer failed rc=%Rrc\n", rc)); +/** + * Frees the I/O memory of the given request and updates the guest buffer if necessary. + * + * @returns nothing. + * @param pDevIns The device instance. + * @param pLsiReq The request state. + * @param fCopyToGuest Flag whether to update the guest buffer if necessary. + * Nothing is copied if false even if the request was a read. + */ +static void lsilogicIoBufFree(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq, + bool fCopyToGuest) +{ + uint8_t uTxDir = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pLsiReq->GuestRequest.SCSIIO.u32Control); - /* Correct start address of the sense buffer. */ - pTaskState->pbSenseBuffer += (GCPhysAddrSenseBuffer - GCPhysAddrSenseBufferBase); + AssertMsg( uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE + || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ + || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE, + ("Allocating I/O memory for a non I/O request is not allowed\n")); - return rc; + if ( ( uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ + || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE) + && fCopyToGuest) + lsilogicCopyToSgBuf(pDevIns, pLsiReq, pLsiReq->SegIoBuf.cbSeg); + + lsilogicReqMemFree(pLsiReq); + pLsiReq->SegIoBuf.pvSeg = NULL; + pLsiReq->SegIoBuf.cbSeg = 0; } -#endif -#ifdef DEBUG -static void lsilogicDumpSCSIIORequest(PMptSCSIIORequest pSCSIIORequest) +# ifdef LOG_ENABLED +static void lsilogicR3DumpSCSIIORequest(PMptSCSIIORequest pSCSIIORequest) { - Log(("%s: u8TargetID=%d\n", __FUNCTION__, pSCSIIORequest->u8TargetID)); - Log(("%s: u8Bus=%d\n", __FUNCTION__, pSCSIIORequest->u8Bus)); - Log(("%s: u8ChainOffset=%d\n", __FUNCTION__, pSCSIIORequest->u8ChainOffset)); - Log(("%s: u8Function=%d\n", __FUNCTION__, pSCSIIORequest->u8Function)); - Log(("%s: u8CDBLength=%d\n", __FUNCTION__, pSCSIIORequest->u8CDBLength)); - Log(("%s: u8SenseBufferLength=%d\n", __FUNCTION__, pSCSIIORequest->u8SenseBufferLength)); - Log(("%s: u8MessageFlags=%d\n", __FUNCTION__, pSCSIIORequest->u8MessageFlags)); - Log(("%s: u32MessageContext=%#x\n", __FUNCTION__, pSCSIIORequest->u32MessageContext)); - for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8LUN); i++) - Log(("%s: u8LUN[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8LUN[i])); - Log(("%s: u32Control=%#x\n", __FUNCTION__, pSCSIIORequest->u32Control)); - for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8CDB); i++) - Log(("%s: u8CDB[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8CDB[i])); - Log(("%s: u32DataLength=%#x\n", __FUNCTION__, pSCSIIORequest->u32DataLength)); - Log(("%s: u32SenseBufferLowAddress=%#x\n", __FUNCTION__, pSCSIIORequest->u32SenseBufferLowAddress)); + if (LogIsEnabled()) + { + Log(("%s: u8TargetID=%d\n", __FUNCTION__, pSCSIIORequest->u8TargetID)); + Log(("%s: u8Bus=%d\n", __FUNCTION__, pSCSIIORequest->u8Bus)); + Log(("%s: u8ChainOffset=%d\n", __FUNCTION__, pSCSIIORequest->u8ChainOffset)); + Log(("%s: u8Function=%d\n", __FUNCTION__, pSCSIIORequest->u8Function)); + Log(("%s: u8CDBLength=%d\n", __FUNCTION__, pSCSIIORequest->u8CDBLength)); + Log(("%s: u8SenseBufferLength=%d\n", __FUNCTION__, pSCSIIORequest->u8SenseBufferLength)); + Log(("%s: u8MessageFlags=%d\n", __FUNCTION__, pSCSIIORequest->u8MessageFlags)); + Log(("%s: u32MessageContext=%#x\n", __FUNCTION__, pSCSIIORequest->u32MessageContext)); + for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8LUN); i++) + Log(("%s: u8LUN[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8LUN[i])); + Log(("%s: u32Control=%#x\n", __FUNCTION__, pSCSIIORequest->u32Control)); + for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8CDB); i++) + Log(("%s: u8CDB[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8CDB[i])); + Log(("%s: u32DataLength=%#x\n", __FUNCTION__, pSCSIIORequest->u32DataLength)); + Log(("%s: u32SenseBufferLowAddress=%#x\n", __FUNCTION__, pSCSIIORequest->u32SenseBufferLowAddress)); + } } -#endif +# endif -static void lsilogicWarningDiskFull(PPDMDEVINS pDevIns) +static void lsilogicR3WarningDiskFull(PPDMDEVINS pDevIns) { int rc; LogRel(("LsiLogic#%d: Host disk full\n", pDevIns->iInstance)); @@ -1911,7 +2096,7 @@ static void lsilogicWarningDiskFull(PPDMDEVINS pDevIns) AssertRC(rc); } -static void lsilogicWarningFileTooBig(PPDMDEVINS pDevIns) +static void lsilogicR3WarningFileTooBig(PPDMDEVINS pDevIns) { int rc; LogRel(("LsiLogic#%d: File too big\n", pDevIns->iInstance)); @@ -1920,7 +2105,7 @@ static void lsilogicWarningFileTooBig(PPDMDEVINS pDevIns) AssertRC(rc); } -static void lsilogicWarningISCSI(PPDMDEVINS pDevIns) +static void lsilogicR3WarningISCSI(PPDMDEVINS pDevIns) { int rc; LogRel(("LsiLogic#%d: iSCSI target unavailable\n", pDevIns->iInstance)); @@ -1929,7 +2114,7 @@ static void lsilogicWarningISCSI(PPDMDEVINS pDevIns) AssertRC(rc); } -static void lsilogicWarningUnknown(PPDMDEVINS pDevIns, int rc) +static void lsilogicR3WarningUnknown(PPDMDEVINS pDevIns, int rc) { int rc2; LogRel(("LsiLogic#%d: Unknown but recoverable error has occurred (rc=%Rrc)\n", pDevIns->iInstance, rc)); @@ -1938,20 +2123,20 @@ static void lsilogicWarningUnknown(PPDMDEVINS pDevIns, int rc) AssertRC(rc2); } -static void lsilogicRedoSetWarning(PLSILOGICSCSI pThis, int rc) +static void lsilogicR3RedoSetWarning(PLSILOGICSCSI pThis, int rc) { if (rc == VERR_DISK_FULL) - lsilogicWarningDiskFull(pThis->CTX_SUFF(pDevIns)); + lsilogicR3WarningDiskFull(pThis->CTX_SUFF(pDevIns)); else if (rc == VERR_FILE_TOO_BIG) - lsilogicWarningFileTooBig(pThis->CTX_SUFF(pDevIns)); + lsilogicR3WarningFileTooBig(pThis->CTX_SUFF(pDevIns)); else if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED) { /* iSCSI connection abort (first error) or failure to reestablish * connection (second error). Pause VM. On resume we'll retry. */ - lsilogicWarningISCSI(pThis->CTX_SUFF(pDevIns)); + lsilogicR3WarningISCSI(pThis->CTX_SUFF(pDevIns)); } else - lsilogicWarningUnknown(pThis->CTX_SUFF(pDevIns), rc); + lsilogicR3WarningUnknown(pThis->CTX_SUFF(pDevIns), rc); } /** @@ -1962,216 +2147,223 @@ static void lsilogicRedoSetWarning(PLSILOGICSCSI pThis, int rc) * the request. * * @returns VBox status code. - * @param pLsiLogic Pointer to the device instance which sends the request. - * @param pTaskState Pointer to the task state data. + * @param pThis Pointer to the LsiLogic device state. + * @param pLsiReq Pointer to the task state data. */ -static int lsilogicProcessSCSIIORequest(PLSILOGICSCSI pLsiLogic, PLSILOGICTASKSTATE pTaskState) +static int lsilogicR3ProcessSCSIIORequest(PLSILOGICSCSI pThis, PLSILOGICREQ pLsiReq) { int rc = VINF_SUCCESS; -#ifdef DEBUG - lsilogicDumpSCSIIORequest(&pTaskState->GuestRequest.SCSIIO); -#endif +# ifdef LOG_ENABLED + lsilogicR3DumpSCSIIORequest(&pLsiReq->GuestRequest.SCSIIO); +# endif - pTaskState->fBIOS = false; + pLsiReq->fBIOS = false; + pLsiReq->GCPhysSgStart = pLsiReq->GCPhysMessageFrameAddr + sizeof(MptSCSIIORequest); + pLsiReq->cChainOffset = pLsiReq->GuestRequest.SCSIIO.u8ChainOffset; + if (pLsiReq->cChainOffset) + pLsiReq->cChainOffset = pLsiReq->cChainOffset * sizeof(uint32_t) - sizeof(MptSCSIIORequest); - if (RT_LIKELY( (pTaskState->GuestRequest.SCSIIO.u8TargetID < pLsiLogic->cDeviceStates) - && (pTaskState->GuestRequest.SCSIIO.u8Bus == 0))) + if (RT_LIKELY( (pLsiReq->GuestRequest.SCSIIO.u8TargetID < pThis->cDeviceStates) + && (pLsiReq->GuestRequest.SCSIIO.u8Bus == 0))) { PLSILOGICDEVICE pTargetDevice; - pTargetDevice = &pLsiLogic->paDeviceStates[pTaskState->GuestRequest.SCSIIO.u8TargetID]; + pTargetDevice = &pThis->paDeviceStates[pLsiReq->GuestRequest.SCSIIO.u8TargetID]; if (pTargetDevice->pDrvBase) { - uint32_t uChainOffset; - - /* Create Scatter gather list. */ - uChainOffset = pTaskState->GuestRequest.SCSIIO.u8ChainOffset; - if (uChainOffset) - uChainOffset = uChainOffset * sizeof(uint32_t) - sizeof(MptSCSIIORequest); - - rc = lsilogicScatterGatherListCreate(pLsiLogic, pTaskState, - pTaskState->GCPhysMessageFrameAddr + sizeof(MptSCSIIORequest), - uChainOffset); - AssertRC(rc); + if (pLsiReq->GuestRequest.SCSIIO.u32DataLength) + { -#if 0 - /* Map sense buffer. */ - rc = lsilogicMapGCSenseBufferIntoR3(pLsiLogic, pTaskState); - AssertRC(rc); -#endif + rc = lsilogicIoBufAllocate(pThis->CTX_SUFF(pDevIns), pLsiReq, + pLsiReq->GuestRequest.SCSIIO.u32DataLength); + AssertRC(rc); /** @todo: Insufficient resources error. */ + } /* Setup the SCSI request. */ - pTaskState->pTargetDevice = pTargetDevice; - pTaskState->PDMScsiRequest.uLogicalUnit = pTaskState->GuestRequest.SCSIIO.au8LUN[1]; + pLsiReq->pTargetDevice = pTargetDevice; + pLsiReq->PDMScsiRequest.uLogicalUnit = pLsiReq->GuestRequest.SCSIIO.au8LUN[1]; - uint8_t uDataDirection = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pTaskState->GuestRequest.SCSIIO.u32Control); + uint8_t uDataDirection = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pLsiReq->GuestRequest.SCSIIO.u32Control); if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE) - pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_NONE; + pLsiReq->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_NONE; else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE) - pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_TO_DEVICE; + pLsiReq->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_TO_DEVICE; else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ) - pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_FROM_DEVICE; - - pTaskState->PDMScsiRequest.cbCDB = pTaskState->GuestRequest.SCSIIO.u8CDBLength; - pTaskState->PDMScsiRequest.pbCDB = pTaskState->GuestRequest.SCSIIO.au8CDB; - pTaskState->PDMScsiRequest.cbScatterGather = pTaskState->GuestRequest.SCSIIO.u32DataLength; - pTaskState->PDMScsiRequest.cScatterGatherEntries = pTaskState->cSGListEntries; - pTaskState->PDMScsiRequest.paScatterGatherHead = pTaskState->pSGListHead; - pTaskState->PDMScsiRequest.cbSenseBuffer = sizeof(pTaskState->abSenseBuffer); - memset(pTaskState->abSenseBuffer, 0, pTaskState->PDMScsiRequest.cbSenseBuffer); - pTaskState->PDMScsiRequest.pbSenseBuffer = pTaskState->abSenseBuffer; - pTaskState->PDMScsiRequest.pvUser = pTaskState; + pLsiReq->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_FROM_DEVICE; + + pLsiReq->PDMScsiRequest.cbCDB = pLsiReq->GuestRequest.SCSIIO.u8CDBLength; + pLsiReq->PDMScsiRequest.pbCDB = pLsiReq->GuestRequest.SCSIIO.au8CDB; + pLsiReq->PDMScsiRequest.cbScatterGather = pLsiReq->GuestRequest.SCSIIO.u32DataLength; + if (pLsiReq->PDMScsiRequest.cbScatterGather) + { + pLsiReq->PDMScsiRequest.cScatterGatherEntries = 1; + pLsiReq->PDMScsiRequest.paScatterGatherHead = &pLsiReq->SegIoBuf; + } + else + { + pLsiReq->PDMScsiRequest.cScatterGatherEntries = 0; + pLsiReq->PDMScsiRequest.paScatterGatherHead = NULL; + } + pLsiReq->PDMScsiRequest.cbSenseBuffer = sizeof(pLsiReq->abSenseBuffer); + memset(pLsiReq->abSenseBuffer, 0, pLsiReq->PDMScsiRequest.cbSenseBuffer); + pLsiReq->PDMScsiRequest.pbSenseBuffer = pLsiReq->abSenseBuffer; + pLsiReq->PDMScsiRequest.pvUser = pLsiReq; ASMAtomicIncU32(&pTargetDevice->cOutstandingRequests); - rc = pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTargetDevice->pDrvSCSIConnector, &pTaskState->PDMScsiRequest); + rc = pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTargetDevice->pDrvSCSIConnector, &pLsiReq->PDMScsiRequest); AssertMsgRC(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc)); return VINF_SUCCESS; } else { /* Device is not present report SCSI selection timeout. */ - pTaskState->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_DEVICE_NOT_THERE; + pLsiReq->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_DEVICE_NOT_THERE; } } else { /* Report out of bounds target ID or bus. */ - if (pTaskState->GuestRequest.SCSIIO.u8Bus != 0) - pTaskState->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_BUS; + if (pLsiReq->GuestRequest.SCSIIO.u8Bus != 0) + pLsiReq->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_BUS; else - pTaskState->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_TARGETID; + pLsiReq->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_TARGETID; } static int g_cLogged = 0; if (g_cLogged++ < MAX_REL_LOG_ERRORS) { - LogRel(("LsiLogic#%d: %d/%d (Bus/Target) doesn't exist\n", pLsiLogic->CTX_SUFF(pDevIns)->iInstance, - pTaskState->GuestRequest.SCSIIO.u8TargetID, pTaskState->GuestRequest.SCSIIO.u8Bus)); + LogRel(("LsiLogic#%d: %d/%d (Bus/Target) doesn't exist\n", pThis->CTX_SUFF(pDevIns)->iInstance, + pLsiReq->GuestRequest.SCSIIO.u8TargetID, pLsiReq->GuestRequest.SCSIIO.u8Bus)); /* Log the CDB too */ LogRel(("LsiLogic#%d: Guest issued CDB {%#x", - pLsiLogic->CTX_SUFF(pDevIns)->iInstance, pTaskState->GuestRequest.SCSIIO.au8CDB[0])); - for (unsigned i = 1; i < pTaskState->GuestRequest.SCSIIO.u8CDBLength; i++) - LogRel((", %#x", pTaskState->GuestRequest.SCSIIO.au8CDB[i])); + pThis->CTX_SUFF(pDevIns)->iInstance, pLsiReq->GuestRequest.SCSIIO.au8CDB[0])); + for (unsigned i = 1; i < pLsiReq->GuestRequest.SCSIIO.u8CDBLength; i++) + LogRel((", %#x", pLsiReq->GuestRequest.SCSIIO.au8CDB[i])); LogRel(("}\n")); } /* The rest is equal to both errors. */ - pTaskState->IOCReply.SCSIIOError.u8TargetID = pTaskState->GuestRequest.SCSIIO.u8TargetID; - pTaskState->IOCReply.SCSIIOError.u8Bus = pTaskState->GuestRequest.SCSIIO.u8Bus; - pTaskState->IOCReply.SCSIIOError.u8MessageLength = sizeof(MptSCSIIOErrorReply) / 4; - pTaskState->IOCReply.SCSIIOError.u8Function = pTaskState->GuestRequest.SCSIIO.u8Function; - pTaskState->IOCReply.SCSIIOError.u8CDBLength = pTaskState->GuestRequest.SCSIIO.u8CDBLength; - pTaskState->IOCReply.SCSIIOError.u8SenseBufferLength = pTaskState->GuestRequest.SCSIIO.u8SenseBufferLength; - pTaskState->IOCReply.SCSIIOError.u32MessageContext = pTaskState->GuestRequest.SCSIIO.u32MessageContext; - pTaskState->IOCReply.SCSIIOError.u8SCSIStatus = SCSI_STATUS_OK; - pTaskState->IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_TERMINATED; - pTaskState->IOCReply.SCSIIOError.u32IOCLogInfo = 0; - pTaskState->IOCReply.SCSIIOError.u32TransferCount = 0; - pTaskState->IOCReply.SCSIIOError.u32SenseCount = 0; - pTaskState->IOCReply.SCSIIOError.u32ResponseInfo = 0; - - lsilogicFinishAddressReply(pLsiLogic, &pTaskState->IOCReply, false); - RTMemCacheFree(pLsiLogic->hTaskCache, pTaskState); + pLsiReq->IOCReply.SCSIIOError.u8TargetID = pLsiReq->GuestRequest.SCSIIO.u8TargetID; + pLsiReq->IOCReply.SCSIIOError.u8Bus = pLsiReq->GuestRequest.SCSIIO.u8Bus; + pLsiReq->IOCReply.SCSIIOError.u8MessageLength = sizeof(MptSCSIIOErrorReply) / 4; + pLsiReq->IOCReply.SCSIIOError.u8Function = pLsiReq->GuestRequest.SCSIIO.u8Function; + pLsiReq->IOCReply.SCSIIOError.u8CDBLength = pLsiReq->GuestRequest.SCSIIO.u8CDBLength; + pLsiReq->IOCReply.SCSIIOError.u8SenseBufferLength = pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength; + pLsiReq->IOCReply.SCSIIOError.u32MessageContext = pLsiReq->GuestRequest.SCSIIO.u32MessageContext; + pLsiReq->IOCReply.SCSIIOError.u8SCSIStatus = SCSI_STATUS_OK; + pLsiReq->IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_TERMINATED; + pLsiReq->IOCReply.SCSIIOError.u32IOCLogInfo = 0; + pLsiReq->IOCReply.SCSIIOError.u32TransferCount = 0; + pLsiReq->IOCReply.SCSIIOError.u32SenseCount = 0; + pLsiReq->IOCReply.SCSIIOError.u32ResponseInfo = 0; + + lsilogicFinishAddressReply(pThis, &pLsiReq->IOCReply, false); + RTMemCacheFree(pThis->hTaskCache, pLsiReq); return rc; } -static DECLCALLBACK(int) lsilogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest, - int rcCompletion, bool fRedo, int rcReq) +/** + * @interface_method_impl{PDMISCSIPORT,pfnSCSIRequestCompleted} + */ +static DECLCALLBACK(int) lsilogicR3DeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest, + int rcCompletion, bool fRedo, int rcReq) { - PLSILOGICTASKSTATE pTaskState = (PLSILOGICTASKSTATE)pSCSIRequest->pvUser; - PLSILOGICDEVICE pLsiLogicDevice = pTaskState->pTargetDevice; - PLSILOGICSCSI pLsiLogic = pLsiLogicDevice->CTX_SUFF(pLsiLogic); + PLSILOGICREQ pLsiReq = (PLSILOGICREQ)pSCSIRequest->pvUser; + PLSILOGICDEVICE pLsiLogicDevice = pLsiReq->pTargetDevice; + PLSILOGICSCSI pThis = pLsiLogicDevice->CTX_SUFF(pLsiLogic); /* If the task failed but it is possible to redo it again after a suspend * add it to the list. */ if (fRedo) { - if (!pTaskState->fBIOS) - lsilogicScatterGatherListDestroy(pLsiLogic, pTaskState); + if (!pLsiReq->fBIOS && pLsiReq->PDMScsiRequest.cbScatterGather) + lsilogicIoBufFree(pThis->CTX_SUFF(pDevIns), pLsiReq, false /* fCopyToGuest */); /* Add to the list. */ do { - pTaskState->pRedoNext = ASMAtomicReadPtrT(&pLsiLogic->pTasksRedoHead, PLSILOGICTASKSTATE); - } while (!ASMAtomicCmpXchgPtr(&pLsiLogic->pTasksRedoHead, pTaskState, pTaskState->pRedoNext)); + pLsiReq->pRedoNext = ASMAtomicReadPtrT(&pThis->pTasksRedoHead, PLSILOGICREQ); + } while (!ASMAtomicCmpXchgPtr(&pThis->pTasksRedoHead, pLsiReq, pLsiReq->pRedoNext)); /* Suspend the VM if not done already. */ - if (!ASMAtomicXchgBool(&pLsiLogic->fRedo, true)) - lsilogicRedoSetWarning(pLsiLogic, rcReq); + if (!ASMAtomicXchgBool(&pThis->fRedo, true)) + lsilogicR3RedoSetWarning(pThis, rcReq); } else { - if (RT_UNLIKELY(pTaskState->fBIOS)) + if (RT_UNLIKELY(pLsiReq->fBIOS)) { - int rc = vboxscsiRequestFinished(&pLsiLogic->VBoxSCSI, pSCSIRequest); + int rc = vboxscsiRequestFinished(&pThis->VBoxSCSI, pSCSIRequest, rcCompletion); AssertMsgRC(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc)); } else { -#if 0 - lsilogicFreeGCSenseBuffer(pLsiLogic, pTaskState); -#else RTGCPHYS GCPhysAddrSenseBuffer; - GCPhysAddrSenseBuffer = pTaskState->GuestRequest.SCSIIO.u32SenseBufferLowAddress; - GCPhysAddrSenseBuffer |= ((uint64_t)pLsiLogic->u32SenseBufferHighAddr << 32); + GCPhysAddrSenseBuffer = pLsiReq->GuestRequest.SCSIIO.u32SenseBufferLowAddress; + GCPhysAddrSenseBuffer |= ((uint64_t)pThis->u32SenseBufferHighAddr << 32); /* Copy the sense buffer over. */ - PDMDevHlpPhysWrite(pLsiLogic->CTX_SUFF(pDevIns), GCPhysAddrSenseBuffer, pTaskState->abSenseBuffer, - RT_UNLIKELY(pTaskState->GuestRequest.SCSIIO.u8SenseBufferLength < pTaskState->PDMScsiRequest.cbSenseBuffer) - ? pTaskState->GuestRequest.SCSIIO.u8SenseBufferLength - : pTaskState->PDMScsiRequest.cbSenseBuffer); -#endif - lsilogicScatterGatherListDestroy(pLsiLogic, pTaskState); + PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhysAddrSenseBuffer, pLsiReq->abSenseBuffer, + RT_UNLIKELY( pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength + < pLsiReq->PDMScsiRequest.cbSenseBuffer) + ? pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength + : pLsiReq->PDMScsiRequest.cbSenseBuffer); + + if (pLsiReq->PDMScsiRequest.cbScatterGather) + lsilogicIoBufFree(pThis->CTX_SUFF(pDevIns), pLsiReq, true /* fCopyToGuest */); if (RT_LIKELY(rcCompletion == SCSI_STATUS_OK)) - lsilogicFinishContextReply(pLsiLogic, pTaskState->GuestRequest.SCSIIO.u32MessageContext); + lsilogicR3FinishContextReply(pThis, pLsiReq->GuestRequest.SCSIIO.u32MessageContext); else { /* The SCSI target encountered an error during processing post a reply. */ - memset(&pTaskState->IOCReply, 0, sizeof(MptReplyUnion)); - pTaskState->IOCReply.SCSIIOError.u8TargetID = pTaskState->GuestRequest.SCSIIO.u8TargetID; - pTaskState->IOCReply.SCSIIOError.u8Bus = pTaskState->GuestRequest.SCSIIO.u8Bus; - pTaskState->IOCReply.SCSIIOError.u8MessageLength = 8; - pTaskState->IOCReply.SCSIIOError.u8Function = pTaskState->GuestRequest.SCSIIO.u8Function; - pTaskState->IOCReply.SCSIIOError.u8CDBLength = pTaskState->GuestRequest.SCSIIO.u8CDBLength; - pTaskState->IOCReply.SCSIIOError.u8SenseBufferLength = pTaskState->GuestRequest.SCSIIO.u8SenseBufferLength; - pTaskState->IOCReply.SCSIIOError.u8MessageFlags = pTaskState->GuestRequest.SCSIIO.u8MessageFlags; - pTaskState->IOCReply.SCSIIOError.u32MessageContext = pTaskState->GuestRequest.SCSIIO.u32MessageContext; - pTaskState->IOCReply.SCSIIOError.u8SCSIStatus = rcCompletion; - pTaskState->IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_AUTOSENSE_VALID; - pTaskState->IOCReply.SCSIIOError.u16IOCStatus = 0; - pTaskState->IOCReply.SCSIIOError.u32IOCLogInfo = 0; - pTaskState->IOCReply.SCSIIOError.u32TransferCount = 0; - pTaskState->IOCReply.SCSIIOError.u32SenseCount = sizeof(pTaskState->abSenseBuffer); - pTaskState->IOCReply.SCSIIOError.u32ResponseInfo = 0; - - lsilogicFinishAddressReply(pLsiLogic, &pTaskState->IOCReply, true); + memset(&pLsiReq->IOCReply, 0, sizeof(MptReplyUnion)); + pLsiReq->IOCReply.SCSIIOError.u8TargetID = pLsiReq->GuestRequest.SCSIIO.u8TargetID; + pLsiReq->IOCReply.SCSIIOError.u8Bus = pLsiReq->GuestRequest.SCSIIO.u8Bus; + pLsiReq->IOCReply.SCSIIOError.u8MessageLength = 8; + pLsiReq->IOCReply.SCSIIOError.u8Function = pLsiReq->GuestRequest.SCSIIO.u8Function; + pLsiReq->IOCReply.SCSIIOError.u8CDBLength = pLsiReq->GuestRequest.SCSIIO.u8CDBLength; + pLsiReq->IOCReply.SCSIIOError.u8SenseBufferLength = pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength; + pLsiReq->IOCReply.SCSIIOError.u8MessageFlags = pLsiReq->GuestRequest.SCSIIO.u8MessageFlags; + pLsiReq->IOCReply.SCSIIOError.u32MessageContext = pLsiReq->GuestRequest.SCSIIO.u32MessageContext; + pLsiReq->IOCReply.SCSIIOError.u8SCSIStatus = rcCompletion; + pLsiReq->IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_AUTOSENSE_VALID; + pLsiReq->IOCReply.SCSIIOError.u16IOCStatus = 0; + pLsiReq->IOCReply.SCSIIOError.u32IOCLogInfo = 0; + pLsiReq->IOCReply.SCSIIOError.u32TransferCount = 0; + pLsiReq->IOCReply.SCSIIOError.u32SenseCount = sizeof(pLsiReq->abSenseBuffer); + pLsiReq->IOCReply.SCSIIOError.u32ResponseInfo = 0; + + lsilogicFinishAddressReply(pThis, &pLsiReq->IOCReply, false); } } - RTMemCacheFree(pLsiLogic->hTaskCache, pTaskState); + RTMemCacheFree(pThis->hTaskCache, pLsiReq); } ASMAtomicDecU32(&pLsiLogicDevice->cOutstandingRequests); - if (pLsiLogicDevice->cOutstandingRequests == 0 && pLsiLogic->fSignalIdle) - PDMDevHlpAsyncNotificationCompleted(pLsiLogic->pDevInsR3); + if (pLsiLogicDevice->cOutstandingRequests == 0 && pThis->fSignalIdle) + PDMDevHlpAsyncNotificationCompleted(pThis->pDevInsR3); return VINF_SUCCESS; } -static DECLCALLBACK(int) lsilogicQueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController, - uint32_t *piInstance, uint32_t *piLUN) +/** + * @interface_method_impl{PDMISCSIPORT,pfnQueryDeviceLocation} + */ +static DECLCALLBACK(int) lsilogicR3QueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController, + uint32_t *piInstance, uint32_t *piLUN) { - PLSILOGICDEVICE pLsiLogicDevice = PDMISCSIPORT_2_PLSILOGICDEVICE(pInterface); + PLSILOGICDEVICE pLsiLogicDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, ISCSIPort); PPDMDEVINS pDevIns = pLsiLogicDevice->CTX_SUFF(pLsiLogic)->CTX_SUFF(pDevIns); AssertPtrReturn(ppcszController, VERR_INVALID_POINTER); @@ -2195,17 +2387,17 @@ static DECLCALLBACK(int) lsilogicQueryDeviceLocation(PPDMISCSIPORT pInterface, c * @param ppPageHeader Where to store the pointer to the page header. * @param ppbPageData Where to store the pointer to the page data. */ -static int lsilogicConfigurationIOUnitPageGetFromNumber(PLSILOGICSCSI pLsiLogic, - PMptConfigurationPagesSupported pPages, - uint8_t u8PageNumber, - PMptConfigurationPageHeader *ppPageHeader, - uint8_t **ppbPageData, size_t *pcbPage) +static int lsilogicR3ConfigurationIOUnitPageGetFromNumber(PLSILOGICSCSI pThis, + PMptConfigurationPagesSupported pPages, + uint8_t u8PageNumber, + PMptConfigurationPageHeader *ppPageHeader, + uint8_t **ppbPageData, size_t *pcbPage) { int rc = VINF_SUCCESS; - AssertMsg(VALID_PTR(ppPageHeader) && VALID_PTR(ppbPageData), ("Invalid parameters\n")); + AssertPtr(ppPageHeader); Assert(ppbPageData); - switch(u8PageNumber) + switch (u8PageNumber) { case 0: *ppPageHeader = &pPages->IOUnitPage0.u.fields.Header; @@ -2249,17 +2441,17 @@ static int lsilogicConfigurationIOUnitPageGetFromNumber(PLSILOGICSCSI pLsiLogic, * @param ppPageHeader Where to store the pointer to the page header. * @param ppbPageData Where to store the pointer to the page data. */ -static int lsilogicConfigurationIOCPageGetFromNumber(PLSILOGICSCSI pLsiLogic, - PMptConfigurationPagesSupported pPages, - uint8_t u8PageNumber, - PMptConfigurationPageHeader *ppPageHeader, - uint8_t **ppbPageData, size_t *pcbPage) +static int lsilogicR3ConfigurationIOCPageGetFromNumber(PLSILOGICSCSI pThis, + PMptConfigurationPagesSupported pPages, + uint8_t u8PageNumber, + PMptConfigurationPageHeader *ppPageHeader, + uint8_t **ppbPageData, size_t *pcbPage) { int rc = VINF_SUCCESS; - AssertMsg(VALID_PTR(ppPageHeader) && VALID_PTR(ppbPageData), ("Invalid parameters\n")); + AssertPtr(ppPageHeader); Assert(ppbPageData); - switch(u8PageNumber) + switch (u8PageNumber) { case 0: *ppPageHeader = &pPages->IOCPage0.u.fields.Header; @@ -2308,17 +2500,17 @@ static int lsilogicConfigurationIOCPageGetFromNumber(PLSILOGICSCSI pLsiLogic, * @param ppPageHeader Where to store the pointer to the page header. * @param ppbPageData Where to store the pointer to the page data. */ -static int lsilogicConfigurationManufacturingPageGetFromNumber(PLSILOGICSCSI pLsiLogic, - PMptConfigurationPagesSupported pPages, - uint8_t u8PageNumber, - PMptConfigurationPageHeader *ppPageHeader, - uint8_t **ppbPageData, size_t *pcbPage) +static int lsilogicR3ConfigurationManufacturingPageGetFromNumber(PLSILOGICSCSI pThis, + PMptConfigurationPagesSupported pPages, + uint8_t u8PageNumber, + PMptConfigurationPageHeader *ppPageHeader, + uint8_t **ppbPageData, size_t *pcbPage) { int rc = VINF_SUCCESS; - AssertMsg(VALID_PTR(ppPageHeader) && VALID_PTR(ppbPageData), ("Invalid parameters\n")); + AssertPtr(ppPageHeader); Assert(ppbPageData); - switch(u8PageNumber) + switch (u8PageNumber) { case 0: *ppPageHeader = &pPages->ManufacturingPage0.u.fields.Header; @@ -2356,7 +2548,7 @@ static int lsilogicConfigurationManufacturingPageGetFromNumber(PLSILOGICSCSI pLs *pcbPage = sizeof(pPages->ManufacturingPage6); break; case 7: - if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) + if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) { *ppPageHeader = &pPages->u.SasPages.pManufacturingPage7->u.fields.Header; *ppbPageData = pPages->u.SasPages.pManufacturingPage7->u.abPageData; @@ -2397,17 +2589,17 @@ static int lsilogicConfigurationManufacturingPageGetFromNumber(PLSILOGICSCSI pLs * @param ppPageHeader Where to store the pointer to the page header. * @param ppbPageData Where to store the pointer to the page data. */ -static int lsilogicConfigurationBiosPageGetFromNumber(PLSILOGICSCSI pLsiLogic, - PMptConfigurationPagesSupported pPages, - uint8_t u8PageNumber, - PMptConfigurationPageHeader *ppPageHeader, - uint8_t **ppbPageData, size_t *pcbPage) +static int lsilogicR3ConfigurationBiosPageGetFromNumber(PLSILOGICSCSI pThis, + PMptConfigurationPagesSupported pPages, + uint8_t u8PageNumber, + PMptConfigurationPageHeader *ppPageHeader, + uint8_t **ppbPageData, size_t *pcbPage) { int rc = VINF_SUCCESS; - AssertMsg(VALID_PTR(ppPageHeader) && VALID_PTR(ppbPageData), ("Invalid parameters\n")); + AssertPtr(ppPageHeader); Assert(ppbPageData); - switch(u8PageNumber) + switch (u8PageNumber) { case 1: *ppPageHeader = &pPages->BIOSPage1.u.fields.Header; @@ -2441,21 +2633,21 @@ static int lsilogicConfigurationBiosPageGetFromNumber(PLSILOGICSCSI pLsiLogic, * @param ppPageHeader Where to store the pointer to the page header. * @param ppbPageData Where to store the pointer to the page data. */ -static int lsilogicConfigurationSCSISPIPortPageGetFromNumber(PLSILOGICSCSI pLsiLogic, - PMptConfigurationPagesSupported pPages, - uint8_t u8Port, - uint8_t u8PageNumber, - PMptConfigurationPageHeader *ppPageHeader, - uint8_t **ppbPageData, size_t *pcbPage) +static int lsilogicR3ConfigurationSCSISPIPortPageGetFromNumber(PLSILOGICSCSI pThis, + PMptConfigurationPagesSupported pPages, + uint8_t u8Port, + uint8_t u8PageNumber, + PMptConfigurationPageHeader *ppPageHeader, + uint8_t **ppbPageData, size_t *pcbPage) { int rc = VINF_SUCCESS; + AssertPtr(ppPageHeader); Assert(ppbPageData); - AssertMsg(VALID_PTR(ppPageHeader) && VALID_PTR(ppbPageData), ("Invalid parameters\n")); if (u8Port >= RT_ELEMENTS(pPages->u.SpiPages.aPortPages)) return VERR_NOT_FOUND; - switch(u8PageNumber) + switch (u8PageNumber) { case 0: *ppPageHeader = &pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage0.u.fields.Header; @@ -2489,16 +2681,15 @@ static int lsilogicConfigurationSCSISPIPortPageGetFromNumber(PLSILOGICSCSI pLsiL * @param ppPageHeader Where to store the pointer to the page header. * @param ppbPageData Where to store the pointer to the page data. */ -static int lsilogicConfigurationSCSISPIDevicePageGetFromNumber(PLSILOGICSCSI pLsiLogic, - PMptConfigurationPagesSupported pPages, - uint8_t u8Bus, - uint8_t u8TargetID, uint8_t u8PageNumber, - PMptConfigurationPageHeader *ppPageHeader, - uint8_t **ppbPageData, size_t *pcbPage) +static int lsilogicR3ConfigurationSCSISPIDevicePageGetFromNumber(PLSILOGICSCSI pThis, + PMptConfigurationPagesSupported pPages, + uint8_t u8Bus, + uint8_t u8TargetID, uint8_t u8PageNumber, + PMptConfigurationPageHeader *ppPageHeader, + uint8_t **ppbPageData, size_t *pcbPage) { int rc = VINF_SUCCESS; - - AssertMsg(VALID_PTR(ppPageHeader) && VALID_PTR(ppbPageData), ("Invalid parameters\n")); + AssertPtr(ppPageHeader); Assert(ppbPageData); if (u8Bus >= RT_ELEMENTS(pPages->u.SpiPages.aBuses)) return VERR_NOT_FOUND; @@ -2506,7 +2697,7 @@ static int lsilogicConfigurationSCSISPIDevicePageGetFromNumber(PLSILOGICSCSI pLs if (u8TargetID >= RT_ELEMENTS(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages)) return VERR_NOT_FOUND; - switch(u8PageNumber) + switch (u8PageNumber) { case 0: *ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage0.u.fields.Header; @@ -2535,15 +2726,15 @@ static int lsilogicConfigurationSCSISPIDevicePageGetFromNumber(PLSILOGICSCSI pLs return rc; } -static int lsilogicConfigurationSASIOUnitPageGetFromNumber(PLSILOGICSCSI pLsiLogic, - PMptConfigurationPagesSupported pPages, - uint8_t u8PageNumber, - PMptExtendedConfigurationPageHeader *ppPageHeader, - uint8_t **ppbPageData, size_t *pcbPage) +static int lsilogicR3ConfigurationSASIOUnitPageGetFromNumber(PLSILOGICSCSI pThis, + PMptConfigurationPagesSupported pPages, + uint8_t u8PageNumber, + PMptExtendedConfigurationPageHeader *ppPageHeader, + uint8_t **ppbPageData, size_t *pcbPage) { int rc = VINF_SUCCESS; - switch(u8PageNumber) + switch (u8PageNumber) { case 0: *ppPageHeader = &pPages->u.SasPages.pSASIOUnitPage0->u.fields.ExtHeader; @@ -2572,12 +2763,12 @@ static int lsilogicConfigurationSASIOUnitPageGetFromNumber(PLSILOGICSCSI pLsiLog return rc; } -static int lsilogicConfigurationSASPHYPageGetFromNumber(PLSILOGICSCSI pLsiLogic, - PMptConfigurationPagesSupported pPages, - uint8_t u8PageNumber, - MptConfigurationPageAddress PageAddress, - PMptExtendedConfigurationPageHeader *ppPageHeader, - uint8_t **ppbPageData, size_t *pcbPage) +static int lsilogicR3ConfigurationSASPHYPageGetFromNumber(PLSILOGICSCSI pThis, + PMptConfigurationPagesSupported pPages, + uint8_t u8PageNumber, + MptConfigurationPageAddress PageAddress, + PMptExtendedConfigurationPageHeader *ppPageHeader, + uint8_t **ppbPageData, size_t *pcbPage) { int rc = VINF_SUCCESS; uint8_t uAddressForm = MPT_CONFIGURATION_PAGE_ADDRESS_GET_SAS_FORM(PageAddress); @@ -2613,7 +2804,7 @@ static int lsilogicConfigurationSASPHYPageGetFromNumber(PLSILOGICSCSI pLsiLogic, if (pPHYPages) { - switch(u8PageNumber) + switch (u8PageNumber) { case 0: *ppPageHeader = &pPHYPages->SASPHYPage0.u.fields.ExtHeader; @@ -2635,12 +2826,12 @@ static int lsilogicConfigurationSASPHYPageGetFromNumber(PLSILOGICSCSI pLsiLogic, return rc; } -static int lsilogicConfigurationSASDevicePageGetFromNumber(PLSILOGICSCSI pLsiLogic, - PMptConfigurationPagesSupported pPages, - uint8_t u8PageNumber, - MptConfigurationPageAddress PageAddress, - PMptExtendedConfigurationPageHeader *ppPageHeader, - uint8_t **ppbPageData, size_t *pcbPage) +static int lsilogicR3ConfigurationSASDevicePageGetFromNumber(PLSILOGICSCSI pThis, + PMptConfigurationPagesSupported pPages, + uint8_t u8PageNumber, + MptConfigurationPageAddress PageAddress, + PMptExtendedConfigurationPageHeader *ppPageHeader, + uint8_t **ppbPageData, size_t *pcbPage) { int rc = VINF_SUCCESS; uint8_t uAddressForm = MPT_CONFIGURATION_PAGE_ADDRESS_GET_SAS_FORM(PageAddress); @@ -2699,7 +2890,7 @@ static int lsilogicConfigurationSASDevicePageGetFromNumber(PLSILOGICSCSI pLsiLog if (pSASDevice) { - switch(u8PageNumber) + switch (u8PageNumber) { case 0: *ppPageHeader = &pSASDevice->SASDevicePage0.u.fields.ExtHeader; @@ -2730,15 +2921,15 @@ static int lsilogicConfigurationSASDevicePageGetFromNumber(PLSILOGICSCSI pLsiLog * Returns the extended configuration page header and data. * @returns VINF_SUCCESS if successful * VERR_NOT_FOUND if the requested page could be found. - * @param pLsiLogic The LsiLogic controller instance. + * @param pThis Pointer to the LsiLogic device state. * @param pConfigurationReq The configuration request. * @param u8PageNumber Number of the page to get. * @param ppPageHeader Where to store the pointer to the page header. * @param ppbPageData Where to store the pointer to the page data. */ -static int lsilogicConfigurationPageGetExtended(PLSILOGICSCSI pLsiLogic, PMptConfigurationRequest pConfigurationReq, - PMptExtendedConfigurationPageHeader *ppPageHeader, - uint8_t **ppbPageData, size_t *pcbPage) +static int lsilogicR3ConfigurationPageGetExtended(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq, + PMptExtendedConfigurationPageHeader *ppPageHeader, + uint8_t **ppbPageData, size_t *pcbPage) { int rc = VINF_SUCCESS; @@ -2750,16 +2941,16 @@ static int lsilogicConfigurationPageGetExtended(PLSILOGICSCSI pLsiLogic, PMptCon { case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT: { - rc = lsilogicConfigurationSASIOUnitPageGetFromNumber(pLsiLogic, - pLsiLogic->pConfigurationPages, + rc = lsilogicR3ConfigurationSASIOUnitPageGetFromNumber(pThis, + pThis->pConfigurationPages, pConfigurationReq->u8PageNumber, ppPageHeader, ppbPageData, pcbPage); break; } case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASPHYS: { - rc = lsilogicConfigurationSASPHYPageGetFromNumber(pLsiLogic, - pLsiLogic->pConfigurationPages, + rc = lsilogicR3ConfigurationSASPHYPageGetFromNumber(pThis, + pThis->pConfigurationPages, pConfigurationReq->u8PageNumber, pConfigurationReq->PageAddress, ppPageHeader, ppbPageData, pcbPage); @@ -2767,8 +2958,8 @@ static int lsilogicConfigurationPageGetExtended(PLSILOGICSCSI pLsiLogic, PMptCon } case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE: { - rc = lsilogicConfigurationSASDevicePageGetFromNumber(pLsiLogic, - pLsiLogic->pConfigurationPages, + rc = lsilogicR3ConfigurationSASDevicePageGetFromNumber(pThis, + pThis->pConfigurationPages, pConfigurationReq->u8PageNumber, pConfigurationReq->PageAddress, ppPageHeader, ppbPageData, pcbPage); @@ -2787,12 +2978,12 @@ static int lsilogicConfigurationPageGetExtended(PLSILOGICSCSI pLsiLogic, PMptCon * Processes a Configuration request. * * @returns VBox status code. - * @param pLsiLogic Pointer to the device instance which sends the request. - * @param pConfigurationReq Pointer to the request structure. - * @param pReply Pointer to the reply message frame + * @param pThis Pointer to the LsiLogic device state. + * @param pConfigurationReq Pointer to the request structure. + * @param pReply Pointer to the reply message frame */ -static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConfigurationRequest pConfigurationReq, - PMptConfigurationReply pReply) +static int lsilogicR3ProcessConfigurationRequest(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq, + PMptConfigurationReply pReply) { int rc = VINF_SUCCESS; uint8_t *pbPageData = NULL; @@ -2802,7 +2993,7 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf uint8_t u8PageAttribute; size_t cbPage = 0; - LogFlowFunc(("pLsiLogic=%#p\n", pLsiLogic)); + LogFlowFunc(("pThis=%#p\n", pThis)); u8PageType = MPT_CONFIGURATION_PAGE_TYPE_GET(pConfigurationReq->u8PageType); u8PageAttribute = MPT_CONFIGURATION_PAGE_ATTRIBUTE_GET(pConfigurationReq->u8PageType); @@ -2825,8 +3016,8 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf case MPT_CONFIGURATION_PAGE_TYPE_IO_UNIT: { /* Get the page data. */ - rc = lsilogicConfigurationIOUnitPageGetFromNumber(pLsiLogic, - pLsiLogic->pConfigurationPages, + rc = lsilogicR3ConfigurationIOUnitPageGetFromNumber(pThis, + pThis->pConfigurationPages, pConfigurationReq->u8PageNumber, &pPageHeader, &pbPageData, &cbPage); break; @@ -2834,8 +3025,8 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf case MPT_CONFIGURATION_PAGE_TYPE_IOC: { /* Get the page data. */ - rc = lsilogicConfigurationIOCPageGetFromNumber(pLsiLogic, - pLsiLogic->pConfigurationPages, + rc = lsilogicR3ConfigurationIOCPageGetFromNumber(pThis, + pThis->pConfigurationPages, pConfigurationReq->u8PageNumber, &pPageHeader, &pbPageData, &cbPage); break; @@ -2843,8 +3034,8 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf case MPT_CONFIGURATION_PAGE_TYPE_MANUFACTURING: { /* Get the page data. */ - rc = lsilogicConfigurationManufacturingPageGetFromNumber(pLsiLogic, - pLsiLogic->pConfigurationPages, + rc = lsilogicR3ConfigurationManufacturingPageGetFromNumber(pThis, + pThis->pConfigurationPages, pConfigurationReq->u8PageNumber, &pPageHeader, &pbPageData, &cbPage); break; @@ -2852,8 +3043,8 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf case MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT: { /* Get the page data. */ - rc = lsilogicConfigurationSCSISPIPortPageGetFromNumber(pLsiLogic, - pLsiLogic->pConfigurationPages, + rc = lsilogicR3ConfigurationSCSISPIPortPageGetFromNumber(pThis, + pThis->pConfigurationPages, pConfigurationReq->PageAddress.MPIPortNumber.u8PortNumber, pConfigurationReq->u8PageNumber, &pPageHeader, &pbPageData, &cbPage); @@ -2862,8 +3053,8 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf case MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE: { /* Get the page data. */ - rc = lsilogicConfigurationSCSISPIDevicePageGetFromNumber(pLsiLogic, - pLsiLogic->pConfigurationPages, + rc = lsilogicR3ConfigurationSCSISPIDevicePageGetFromNumber(pThis, + pThis->pConfigurationPages, pConfigurationReq->PageAddress.BusAndTargetId.u8Bus, pConfigurationReq->PageAddress.BusAndTargetId.u8TargetID, pConfigurationReq->u8PageNumber, @@ -2872,15 +3063,15 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf } case MPT_CONFIGURATION_PAGE_TYPE_BIOS: { - rc = lsilogicConfigurationBiosPageGetFromNumber(pLsiLogic, - pLsiLogic->pConfigurationPages, + rc = lsilogicR3ConfigurationBiosPageGetFromNumber(pThis, + pThis->pConfigurationPages, pConfigurationReq->u8PageNumber, &pPageHeader, &pbPageData, &cbPage); break; } case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED: { - rc = lsilogicConfigurationPageGetExtended(pLsiLogic, + rc = lsilogicR3ConfigurationPageGetExtended(pThis, pConfigurationReq, &pExtPageHeader, &pbPageData, &cbPage); break; @@ -2945,8 +3136,7 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf if (pConfigurationReq->SimpleSGElement.f64BitAddress) GCPhysAddrPageBuffer |= (uint64_t)pConfigurationReq->SimpleSGElement.u32DataBufferAddressHigh << 32; - PDMDevHlpPhysWrite(pLsiLogic->CTX_SUFF(pDevIns), GCPhysAddrPageBuffer, pbPageData, - RT_MIN(cbBuffer, cbPage)); + PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhysAddrPageBuffer, pbPageData, RT_MIN(cbBuffer, cbPage)); } break; } @@ -2962,7 +3152,7 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf LogFlow(("cbBuffer=%u cbPage=%u\n", cbBuffer, cbPage)); - PDMDevHlpPhysRead(pLsiLogic->CTX_SUFF(pDevIns), GCPhysAddrPageBuffer, pbPageData, + PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCPhysAddrPageBuffer, pbPageData, RT_MIN(cbBuffer, cbPage)); } break; @@ -2978,18 +3168,18 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf * Initializes the configuration pages for the SPI SCSI controller. * * @returns nothing - * @param pLsiLogic Pointer to the Lsilogic SCSI instance. + * @param pThis Pointer to the LsiLogic device state. */ -static void lsilogicInitializeConfigurationPagesSpi(PLSILOGICSCSI pLsiLogic) +static void lsilogicR3InitializeConfigurationPagesSpi(PLSILOGICSCSI pThis) { - PMptConfigurationPagesSpi pPages = &pLsiLogic->pConfigurationPages->u.SpiPages; + PMptConfigurationPagesSpi pPages = &pThis->pConfigurationPages->u.SpiPages; - AssertMsg(pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI, ("Controller is not the SPI SCSI one\n")); + AssertMsg(pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI, ("Controller is not the SPI SCSI one\n")); - LogFlowFunc(("pLsiLogic=%#p\n", pLsiLogic)); + LogFlowFunc(("pThis=%#p\n", pThis)); /* Clear everything first. */ - memset(pPages, 0, sizeof(PMptConfigurationPagesSpi)); + memset(pPages, 0, sizeof(MptConfigurationPagesSpi)); for (unsigned i = 0; i < RT_ELEMENTS(pPages->aPortPages); i++) { @@ -3069,7 +3259,7 @@ static void lsilogicInitializeConfigurationPagesSpi(PLSILOGICSCSI pLsiLogic) * Generates a handle. * * @returns the handle. - * @param pThis The LsiLogic instance. + * @param pThis Pointer to the LsiLogic device state. */ DECLINLINE(uint16_t) lsilogicGetHandle(PLSILOGICSCSI pThis) { @@ -3102,9 +3292,9 @@ void lsilogicSASAddressGenerate(PSASADDRESS pSASAddress, unsigned iId) * Initializes the configuration pages for the SAS SCSI controller. * * @returns nothing - * @param pThis Pointer to the Lsilogic SCSI instance. + * @param pThis Pointer to the LsiLogic device state. */ -static void lsilogicInitializeConfigurationPagesSas(PLSILOGICSCSI pThis) +static void lsilogicR3InitializeConfigurationPagesSas(PLSILOGICSCSI pThis) { PMptConfigurationPagesSas pPages = &pThis->pConfigurationPages->u.SasPages; @@ -3298,16 +3488,16 @@ static void lsilogicInitializeConfigurationPagesSas(PLSILOGICSCSI pThis) * Initializes the configuration pages. * * @returns nothing - * @param pLsiLogic Pointer to the Lsilogic SCSI instance. + * @param pThis Pointer to the LsiLogic device state. */ -static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic) +static void lsilogicR3InitializeConfigurationPages(PLSILOGICSCSI pThis) { /* Initialize the common pages. */ PMptConfigurationPagesSupported pPages = (PMptConfigurationPagesSupported)RTMemAllocZ(sizeof(MptConfigurationPagesSupported)); - pLsiLogic->pConfigurationPages = pPages; + pThis->pConfigurationPages = pPages; - LogFlowFunc(("pLsiLogic=%#p\n", pLsiLogic)); + LogFlowFunc(("pThis=%#p\n", pThis)); /* Clear everything first. */ memset(pPages, 0, sizeof(MptConfigurationPagesSupported)); @@ -3332,12 +3522,12 @@ static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic) MptConfigurationPageManufacturing2, 2, MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY); - if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) + if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) { pPages->ManufacturingPage2.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SPI_DEVICE_ID; pPages->ManufacturingPage2.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SPI_REVISION_ID; } - else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) + else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) { pPages->ManufacturingPage2.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SAS_DEVICE_ID; pPages->ManufacturingPage2.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SAS_REVISION_ID; @@ -3348,12 +3538,12 @@ static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic) MptConfigurationPageManufacturing3, 3, MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY); - if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) + if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) { pPages->ManufacturingPage3.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SPI_DEVICE_ID; pPages->ManufacturingPage3.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SPI_REVISION_ID; } - else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) + else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) { pPages->ManufacturingPage3.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SAS_DEVICE_ID; pPages->ManufacturingPage3.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SAS_REVISION_ID; @@ -3416,7 +3606,7 @@ static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic) pPages->IOUnitPage2.u.fields.aAdapterOrder[0].fAdapterEnabled = true; pPages->IOUnitPage2.u.fields.aAdapterOrder[0].fAdapterEmbedded = true; pPages->IOUnitPage2.u.fields.aAdapterOrder[0].u8PCIBusNumber = 0; - pPages->IOUnitPage2.u.fields.aAdapterOrder[0].u8PCIDevFn = pLsiLogic->PciDev.devfn; + pPages->IOUnitPage2.u.fields.aAdapterOrder[0].u8PCIDevFn = pThis->PciDev.devfn; /* I/O Unit page 3. */ MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage3, @@ -3436,7 +3626,7 @@ static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic) pPages->IOCPage0.u.fields.u32TotalNVStore = 0; pPages->IOCPage0.u.fields.u32FreeNVStore = 0; - if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) + if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) { pPages->IOCPage0.u.fields.u16VendorId = LSILOGICSCSI_PCI_VENDOR_ID; pPages->IOCPage0.u.fields.u16DeviceId = LSILOGICSCSI_PCI_SPI_DEVICE_ID; @@ -3445,7 +3635,7 @@ static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic) pPages->IOCPage0.u.fields.u16SubsystemVendorId = LSILOGICSCSI_PCI_SPI_SUBSYSTEM_VENDOR_ID; pPages->IOCPage0.u.fields.u16SubsystemId = LSILOGICSCSI_PCI_SPI_SUBSYSTEM_ID; } - else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) + else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) { pPages->IOCPage0.u.fields.u16VendorId = LSILOGICSCSI_PCI_VENDOR_ID; pPages->IOCPage0.u.fields.u16DeviceId = LSILOGICSCSI_PCI_SAS_DEVICE_ID; @@ -3502,122 +3692,26 @@ static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic) MptConfigurationPageBIOS4, 4, MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE); - if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) - lsilogicInitializeConfigurationPagesSpi(pLsiLogic); - else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) - lsilogicInitializeConfigurationPagesSas(pLsiLogic); + if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) + lsilogicR3InitializeConfigurationPagesSpi(pThis); + else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) + lsilogicR3InitializeConfigurationPagesSas(pThis); else - AssertMsgFailed(("Invalid controller type %d\n", pLsiLogic->enmCtrlType)); + AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType)); } /** - * Transmit queue consumer - * Queue a new async task. - * - * @returns Success indicator. - * If false the item will not be removed and the flushing will stop. - * @param pDevIns The device instance. - * @param pItem The item to consume. Upon return this item will be freed. + * @callback_method_impl{FNPDMQUEUEDEV, Transmit queue consumer.} */ -static DECLCALLBACK(bool) lsilogicNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem) +static DECLCALLBACK(bool) lsilogicR3NotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem) { - PLSILOGICSCSI pLsiLogic = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); + PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); int rc = VINF_SUCCESS; LogFlowFunc(("pDevIns=%#p pItem=%#p\n", pDevIns, pItem)); - /* Reset notification event. */ - ASMAtomicXchgBool(&pLsiLogic->fNotificationSend, false); - - /* Only process request which arrived before we received the notification. */ - uint32_t uRequestQueueNextEntryWrite = ASMAtomicReadU32(&pLsiLogic->uRequestQueueNextEntryFreeWrite); - - /* Go through the messages now and process them. */ - while ( RT_LIKELY(pLsiLogic->enmState == LSILOGICSTATE_OPERATIONAL) - && (pLsiLogic->uRequestQueueNextAddressRead != uRequestQueueNextEntryWrite)) - { - uint32_t u32RequestMessageFrameDesc = pLsiLogic->CTX_SUFF(pRequestQueueBase)[pLsiLogic->uRequestQueueNextAddressRead]; - RTGCPHYS GCPhysMessageFrameAddr = LSILOGIC_RTGCPHYS_FROM_U32(pLsiLogic->u32HostMFAHighAddr, - (u32RequestMessageFrameDesc & ~0x07)); - - PLSILOGICTASKSTATE pTaskState; - - /* Get new task state. */ - rc = RTMemCacheAllocEx(pLsiLogic->hTaskCache, (void **)&pTaskState); - AssertRC(rc); - - pTaskState->GCPhysMessageFrameAddr = GCPhysMessageFrameAddr; - - /* Read the message header from the guest first. */ - PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &pTaskState->GuestRequest, sizeof(MptMessageHdr)); - - /* Determine the size of the request. */ - uint32_t cbRequest = 0; - - switch (pTaskState->GuestRequest.Header.u8Function) - { - case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST: - cbRequest = sizeof(MptSCSIIORequest); - break; - case MPT_MESSAGE_HDR_FUNCTION_SCSI_TASK_MGMT: - cbRequest = sizeof(MptSCSITaskManagementRequest); - break; - case MPT_MESSAGE_HDR_FUNCTION_IOC_INIT: - cbRequest = sizeof(MptIOCInitRequest); - break; - case MPT_MESSAGE_HDR_FUNCTION_IOC_FACTS: - cbRequest = sizeof(MptIOCFactsRequest); - break; - case MPT_MESSAGE_HDR_FUNCTION_CONFIG: - cbRequest = sizeof(MptConfigurationRequest); - break; - case MPT_MESSAGE_HDR_FUNCTION_PORT_FACTS: - cbRequest = sizeof(MptPortFactsRequest); - break; - case MPT_MESSAGE_HDR_FUNCTION_PORT_ENABLE: - cbRequest = sizeof(MptPortEnableRequest); - break; - case MPT_MESSAGE_HDR_FUNCTION_EVENT_NOTIFICATION: - cbRequest = sizeof(MptEventNotificationRequest); - break; - case MPT_MESSAGE_HDR_FUNCTION_EVENT_ACK: - AssertMsgFailed(("todo\n")); - //cbRequest = sizeof(MptEventAckRequest); - break; - case MPT_MESSAGE_HDR_FUNCTION_FW_DOWNLOAD: - cbRequest = sizeof(MptFWDownloadRequest); - break; - case MPT_MESSAGE_HDR_FUNCTION_FW_UPLOAD: - cbRequest = sizeof(MptFWUploadRequest); - break; - default: - AssertMsgFailed(("Unknown function issued %u\n", pTaskState->GuestRequest.Header.u8Function)); - lsilogicSetIOCFaultCode(pLsiLogic, LSILOGIC_IOCSTATUS_INVALID_FUNCTION); - } - - if (cbRequest != 0) - { - /* Read the complete message frame from guest memory now. */ - PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &pTaskState->GuestRequest, cbRequest); - - /* Handle SCSI I/O requests now. */ - if (pTaskState->GuestRequest.Header.u8Function == MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST) - { - rc = lsilogicProcessSCSIIORequest(pLsiLogic, pTaskState); - AssertRC(rc); - } - else - { - MptReplyUnion Reply; - rc = lsilogicProcessMessageRequest(pLsiLogic, &pTaskState->GuestRequest.Header, &Reply); - AssertRC(rc); - RTMemCacheFree(pLsiLogic->hTaskCache, pTaskState); - } - - pLsiLogic->uRequestQueueNextAddressRead++; - pLsiLogic->uRequestQueueNextAddressRead %= pLsiLogic->cRequestQueueEntries; - } - } + rc = SUPSemEventSignal(pThis->pSupDrvSession, pThis->hEvtProcess); + AssertRC(rc); return true; } @@ -3627,10 +3721,10 @@ static DECLCALLBACK(bool) lsilogicNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQU * * @returns VBox status code. * - * @param pThis The LsiLogic devi state. - * @param pcszCtrlType The string to use. + * @param pThis Pointer to the LsiLogic device state. + * @param pcszCtrlType The string to use. */ -static int lsilogicGetCtrlTypeFromString(PLSILOGICSCSI pThis, const char *pcszCtrlType) +static int lsilogicR3GetCtrlTypeFromString(PLSILOGICSCSI pThis, const char *pcszCtrlType) { int rc = VERR_INVALID_PARAMETER; @@ -3649,28 +3743,18 @@ static int lsilogicGetCtrlTypeFromString(PLSILOGICSCSI pThis, const char *pcszCt } /** - * Port I/O Handler for IN operations - legacy port. - * - * @returns VBox status code. - * - * @param pDevIns The device instance. - * @param pvUser User argument. - * @param uPort Port number used for the IN operation. - * @param pu32 Where to store the result. - * @param cb Number of bytes read. + * @callback_method_impl{FNIOMIOPORTIN, Legacy ISA port.} */ -static int lsilogicIsaIOPortRead (PPDMDEVINS pDevIns, void *pvUser, - RTIOPORT Port, uint32_t *pu32, unsigned cb) +static DECLCALLBACK(int) lsilogicR3IsaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb) { - int rc; PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); Assert(cb == 1); - uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI - ? Port - LSILOGIC_BIOS_IO_PORT - : Port - LSILOGIC_SAS_BIOS_IO_PORT; - rc = vboxscsiReadRegister(&pThis->VBoxSCSI, iRegister, pu32); + uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI + ? Port - LSILOGIC_BIOS_IO_PORT + : Port - LSILOGIC_SAS_BIOS_IO_PORT; + int rc = vboxscsiReadRegister(&pThis->VBoxSCSI, iRegister, pu32); Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n", __FUNCTION__, pu32, 1, pu32, iRegister, rc)); @@ -3682,41 +3766,41 @@ static int lsilogicIsaIOPortRead (PPDMDEVINS pDevIns, void *pvUser, * Prepares a request from the BIOS. * * @returns VBox status code. - * @param pLsiLogic Pointer to the LsiLogic device instance. + * @param pThis Pointer to the LsiLogic device state. */ -static int lsilogicPrepareBIOSSCSIRequest(PLSILOGICSCSI pLsiLogic) +static int lsilogicR3PrepareBiosScsiRequest(PLSILOGICSCSI pThis) { int rc; - PLSILOGICTASKSTATE pTaskState; + PLSILOGICREQ pLsiReq; uint32_t uTargetDevice; - rc = RTMemCacheAllocEx(pLsiLogic->hTaskCache, (void **)&pTaskState); + rc = RTMemCacheAllocEx(pThis->hTaskCache, (void **)&pLsiReq); AssertMsgRCReturn(rc, ("Getting task from cache failed rc=%Rrc\n", rc), rc); - pTaskState->fBIOS = true; + pLsiReq->fBIOS = true; - rc = vboxscsiSetupRequest(&pLsiLogic->VBoxSCSI, &pTaskState->PDMScsiRequest, &uTargetDevice); + rc = vboxscsiSetupRequest(&pThis->VBoxSCSI, &pLsiReq->PDMScsiRequest, &uTargetDevice); AssertMsgRCReturn(rc, ("Setting up SCSI request failed rc=%Rrc\n", rc), rc); - pTaskState->PDMScsiRequest.pvUser = pTaskState; + pLsiReq->PDMScsiRequest.pvUser = pLsiReq; - if (uTargetDevice < pLsiLogic->cDeviceStates) + if (uTargetDevice < pThis->cDeviceStates) { - pTaskState->pTargetDevice = &pLsiLogic->paDeviceStates[uTargetDevice]; + pLsiReq->pTargetDevice = &pThis->paDeviceStates[uTargetDevice]; - if (pTaskState->pTargetDevice->pDrvBase) + if (pLsiReq->pTargetDevice->pDrvBase) { - ASMAtomicIncU32(&pTaskState->pTargetDevice->cOutstandingRequests); + ASMAtomicIncU32(&pLsiReq->pTargetDevice->cOutstandingRequests); - rc = pTaskState->pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTaskState->pTargetDevice->pDrvSCSIConnector, - &pTaskState->PDMScsiRequest); + rc = pLsiReq->pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pLsiReq->pTargetDevice->pDrvSCSIConnector, + &pLsiReq->PDMScsiRequest); AssertMsgRCReturn(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc), rc); return VINF_SUCCESS; } } /* Device is not present. */ - AssertMsg(pTaskState->PDMScsiRequest.pbCDB[0] == SCSI_INQUIRY, + AssertMsg(pLsiReq->PDMScsiRequest.pbCDB[0] == SCSI_INQUIRY, ("Device is not present but command is not inquiry\n")); SCSIINQUIRYDATA ScsiInquiryData; @@ -3725,44 +3809,32 @@ static int lsilogicPrepareBIOSSCSIRequest(PLSILOGICSCSI pLsiLogic) ScsiInquiryData.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN; ScsiInquiryData.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED; - memcpy(pLsiLogic->VBoxSCSI.pBuf, &ScsiInquiryData, 5); + memcpy(pThis->VBoxSCSI.pbBuf, &ScsiInquiryData, 5); - rc = vboxscsiRequestFinished(&pLsiLogic->VBoxSCSI, &pTaskState->PDMScsiRequest); + rc = vboxscsiRequestFinished(&pThis->VBoxSCSI, &pLsiReq->PDMScsiRequest, SCSI_STATUS_OK); AssertMsgRCReturn(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc), rc); - RTMemCacheFree(pLsiLogic->hTaskCache, pTaskState); + RTMemCacheFree(pThis->hTaskCache, pLsiReq); return rc; } /** - * Port I/O Handler for OUT operations - legacy port. - * - * @returns VBox status code. - * - * @param pDevIns The device instance. - * @param pvUser User argument. - * @param uPort Port number used for the IN operation. - * @param u32 The value to output. - * @param cb The value size in bytes. + * @callback_method_impl{FNIOMIOPORTOUT, Legacy ISA port.} */ -static int lsilogicIsaIOPortWrite (PPDMDEVINS pDevIns, void *pvUser, - RTIOPORT Port, uint32_t u32, unsigned cb) +static DECLCALLBACK(int) lsilogicR3IsaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb) { - int rc; PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); - - Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x\n", - pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port)); + Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x\n", pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port)); Assert(cb == 1); - uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI - ? Port - LSILOGIC_BIOS_IO_PORT - : Port - LSILOGIC_SAS_BIOS_IO_PORT; - rc = vboxscsiWriteRegister(&pThis->VBoxSCSI, iRegister, (uint8_t)u32); + uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI + ? Port - LSILOGIC_BIOS_IO_PORT + : Port - LSILOGIC_SAS_BIOS_IO_PORT; + int rc = vboxscsiWriteRegister(&pThis->VBoxSCSI, iRegister, (uint8_t)u32); if (rc == VERR_MORE_DATA) { - rc = lsilogicPrepareBIOSSCSIRequest(pThis); + rc = lsilogicR3PrepareBiosScsiRequest(pThis); AssertRC(rc); } else if (RT_FAILURE(rc)) @@ -3772,25 +3844,22 @@ static int lsilogicIsaIOPortWrite (PPDMDEVINS pDevIns, void *pvUser, } /** - * Port I/O Handler for primary port range OUT string operations. - * @see FNIOMIOPORTOUTSTRING for details. + * @callback_method_impl{FNIOMIOPORTOUTSTRING, + * Port I/O Handler for primary port range OUT string operations.} */ -static DECLCALLBACK(int) lsilogicIsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb) +static DECLCALLBACK(int) lsilogicR3IsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, + PRTGCPTR pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb) { PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); - int rc; - - Log2(("#%d %s: pvUser=%#p cb=%d Port=%#x\n", - pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port)); + Log2(("#%d %s: pvUser=%#p cb=%d Port=%#x\n", pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port)); - uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI - ? Port - LSILOGIC_BIOS_IO_PORT - : Port - LSILOGIC_SAS_BIOS_IO_PORT; - rc = vboxscsiWriteString(pDevIns, &pThis->VBoxSCSI, iRegister, - pGCPtrSrc, pcTransfer, cb); + uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI + ? Port - LSILOGIC_BIOS_IO_PORT + : Port - LSILOGIC_SAS_BIOS_IO_PORT; + int rc = vboxscsiWriteString(pDevIns, &pThis->VBoxSCSI, iRegister, pGCPtrSrc, pcTransfer, cb); if (rc == VERR_MORE_DATA) { - rc = lsilogicPrepareBIOSSCSIRequest(pThis); + rc = lsilogicR3PrepareBiosScsiRequest(pThis); AssertRC(rc); } else if (RT_FAILURE(rc)) @@ -3800,26 +3869,29 @@ static DECLCALLBACK(int) lsilogicIsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvU } /** - * Port I/O Handler for primary port range IN string operations. - * @see FNIOMIOPORTINSTRING for details. + * @callback_method_impl{FNIOMIOPORTINSTRING, + * Port I/O Handler for primary port range IN string operations.} */ -static DECLCALLBACK(int) lsilogicIsaIOPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb) +static DECLCALLBACK(int) lsilogicR3IsaIOPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, + PRTGCUINTREG pcTransfer, unsigned cb) { PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); LogFlowFunc(("#%d %s: pvUser=%#p cb=%d Port=%#x\n", pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port)); - uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI - ? Port - LSILOGIC_BIOS_IO_PORT - : Port - LSILOGIC_SAS_BIOS_IO_PORT; - return vboxscsiReadString(pDevIns, &pThis->VBoxSCSI, iRegister, - pGCPtrDst, pcTransfer, cb); + uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI + ? Port - LSILOGIC_BIOS_IO_PORT + : Port - LSILOGIC_SAS_BIOS_IO_PORT; + return vboxscsiReadString(pDevIns, &pThis->VBoxSCSI, iRegister, pGCPtrDst, pcTransfer, cb); } -static DECLCALLBACK(int) lsilogicMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, - RTGCPHYS GCPhysAddress, uint32_t cb, - PCIADDRESSSPACE enmType) +/** + * @callback_method_impl{FNPCIIOREGIONMAP} + */ +static DECLCALLBACK(int) lsilogicR3Map(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, + RTGCPHYS GCPhysAddress, uint32_t cb, + PCIADDRESSSPACE enmType) { PPDMDEVINS pDevIns = pPciDev->pDevIns; PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); @@ -3837,11 +3909,24 @@ static DECLCALLBACK(int) lsilogicMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegio || (enmType == PCI_ADDRESS_SPACE_IO && cb >= LSILOGIC_PCI_SPACE_IO_SIZE), ("PCI region type and size do not match\n")); - if ((enmType == PCI_ADDRESS_SPACE_MEM) && (iRegion == 1)) + if (enmType == PCI_ADDRESS_SPACE_MEM && iRegion == 1) { - /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */ + /* + * Non-4-byte read access to LSILOGIC_REG_REPLY_QUEUE may cause real strange behavior + * because the data is part of a physical guest address. But some drivers use 1-byte + * access to scan for SCSI controllers. So, we simplify our code by telling IOM to + * read DWORDs. + * + * Regarding writes, we couldn't find anything specific in the specs about what should + * happen. So far we've ignored unaligned writes and assumed the missing bytes of + * byte and word access to be zero. We suspect that IOMMMIO_FLAGS_WRITE_ONLY_DWORD + * or IOMMMIO_FLAGS_WRITE_DWORD_ZEROED would be the most appropriate here, but since we + * don't have real hw to test one, the old behavior is kept exactly like it used to be. + */ + /** @todo Check out unaligned writes and non-dword writes on real LsiLogic + * hardware. */ rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/, - IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, + IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_PASSTHRU, lsilogicMMIOWrite, lsilogicMMIORead, pcszCtrl); if (RT_FAILURE(rc)) return rc; @@ -3921,13 +4006,9 @@ static DECLCALLBACK(int) lsilogicMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegio } /** - * LsiLogic status info callback. - * - * @param pDevIns The device instance. - * @param pHlp The output helpers. - * @param pszArgs The arguments. + * @callback_method_impl{PFNDBGFHANDLERDEV} */ -static DECLCALLBACK(void) lsilogicInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs) +static DECLCALLBACK(void) lsilogicR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs) { PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); bool fVerbose = false; @@ -3955,7 +4036,7 @@ static DECLCALLBACK(void) lsilogicInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, c */ pHlp->pfnPrintf(pHlp, "enmState=%u\n", pThis->enmState); pHlp->pfnPrintf(pHlp, "enmWhoInit=%u\n", pThis->enmWhoInit); - pHlp->pfnPrintf(pHlp, "fDoorbellInProgress=%RTbool\n", pThis->fDoorbellInProgress); + pHlp->pfnPrintf(pHlp, "enmDoorbellState=%d\n", pThis->enmDoorbellState); pHlp->pfnPrintf(pHlp, "fDiagnosticEnabled=%RTbool\n", pThis->fDiagnosticEnabled); pHlp->pfnPrintf(pHlp, "fNotificationSend=%RTbool\n", pThis->fNotificationSend); pHlp->pfnPrintf(pHlp, "fEventNotificationEnabled=%RTbool\n", pThis->fEventNotificationEnabled); @@ -4019,9 +4100,9 @@ static DECLCALLBACK(void) lsilogicInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, c * * @returns VBox status code. * - * @param pThis The LsiLogic device instance. + * @param pThis Pointer to the LsiLogic device state. */ -static int lsilogicQueuesAlloc(PLSILOGICSCSI pThis) +static int lsilogicR3QueuesAlloc(PLSILOGICSCSI pThis) { PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3); uint32_t cbQueues; @@ -4053,9 +4134,9 @@ static int lsilogicQueuesAlloc(PLSILOGICSCSI pThis) * * @returns nothing. * - * @param pThis The LsiLogic device instance. + * @param pThis Pointer to the LsiLogic device state. */ -static void lsilogicQueuesFree(PLSILOGICSCSI pThis) +static void lsilogicR3QueuesFree(PLSILOGICSCSI pThis) { PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3); int rc = VINF_SUCCESS; @@ -4070,14 +4151,152 @@ static void lsilogicQueuesFree(PLSILOGICSCSI pThis) pThis->pRequestQueueBaseR3 = NULL; } + +/* The worker thread. */ +static DECLCALLBACK(int) lsilogicR3Worker(PPDMDEVINS pDevIns, PPDMTHREAD pThread) +{ + PLSILOGICSCSI pThis = (PLSILOGICSCSI)pThread->pvUser; + int rc = VINF_SUCCESS; + + if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) + return VINF_SUCCESS; + + while (pThread->enmState == PDMTHREADSTATE_RUNNING) + { + bool fNotificationSend; + + ASMAtomicWriteBool(&pThis->fWrkThreadSleeping, true); + fNotificationSend = ASMAtomicXchgBool(&pThis->fNotificationSend, false); + if (!fNotificationSend) + { + Assert(ASMAtomicReadBool(&pThis->fWrkThreadSleeping)); + rc = SUPSemEventWaitNoResume(pThis->pSupDrvSession, pThis->hEvtProcess, RT_INDEFINITE_WAIT); + AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc); + if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING)) + break; + LogFlowFunc(("Woken up with rc=%Rrc\n", rc)); + fNotificationSend = ASMAtomicXchgBool(&pThis->fNotificationSend, false); + } + + ASMAtomicWriteBool(&pThis->fWrkThreadSleeping, false); + + /* Only process request which arrived before we received the notification. */ + uint32_t uRequestQueueNextEntryWrite = ASMAtomicReadU32(&pThis->uRequestQueueNextEntryFreeWrite); + + /* Go through the messages now and process them. */ + while ( RT_LIKELY(pThis->enmState == LSILOGICSTATE_OPERATIONAL) + && (pThis->uRequestQueueNextAddressRead != uRequestQueueNextEntryWrite)) + { + uint32_t u32RequestMessageFrameDesc = pThis->CTX_SUFF(pRequestQueueBase)[pThis->uRequestQueueNextAddressRead]; + RTGCPHYS GCPhysMessageFrameAddr = LSILOGIC_RTGCPHYS_FROM_U32(pThis->u32HostMFAHighAddr, + (u32RequestMessageFrameDesc & ~0x07)); + + PLSILOGICREQ pLsiReq; + + /* Get new task state. */ + rc = RTMemCacheAllocEx(pThis->hTaskCache, (void **)&pLsiReq); + AssertRC(rc); + + pLsiReq->GCPhysMessageFrameAddr = GCPhysMessageFrameAddr; + + /* Read the message header from the guest first. */ + PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &pLsiReq->GuestRequest, sizeof(MptMessageHdr)); + + /* Determine the size of the request. */ + uint32_t cbRequest = 0; + + switch (pLsiReq->GuestRequest.Header.u8Function) + { + case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST: + cbRequest = sizeof(MptSCSIIORequest); + break; + case MPT_MESSAGE_HDR_FUNCTION_SCSI_TASK_MGMT: + cbRequest = sizeof(MptSCSITaskManagementRequest); + break; + case MPT_MESSAGE_HDR_FUNCTION_IOC_INIT: + cbRequest = sizeof(MptIOCInitRequest); + break; + case MPT_MESSAGE_HDR_FUNCTION_IOC_FACTS: + cbRequest = sizeof(MptIOCFactsRequest); + break; + case MPT_MESSAGE_HDR_FUNCTION_CONFIG: + cbRequest = sizeof(MptConfigurationRequest); + break; + case MPT_MESSAGE_HDR_FUNCTION_PORT_FACTS: + cbRequest = sizeof(MptPortFactsRequest); + break; + case MPT_MESSAGE_HDR_FUNCTION_PORT_ENABLE: + cbRequest = sizeof(MptPortEnableRequest); + break; + case MPT_MESSAGE_HDR_FUNCTION_EVENT_NOTIFICATION: + cbRequest = sizeof(MptEventNotificationRequest); + break; + case MPT_MESSAGE_HDR_FUNCTION_EVENT_ACK: + AssertMsgFailed(("todo\n")); + //cbRequest = sizeof(MptEventAckRequest); + break; + case MPT_MESSAGE_HDR_FUNCTION_FW_DOWNLOAD: + cbRequest = sizeof(MptFWDownloadRequest); + break; + case MPT_MESSAGE_HDR_FUNCTION_FW_UPLOAD: + cbRequest = sizeof(MptFWUploadRequest); + break; + default: + AssertMsgFailed(("Unknown function issued %u\n", pLsiReq->GuestRequest.Header.u8Function)); + lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INVALID_FUNCTION); + } + + if (cbRequest != 0) + { + /* Read the complete message frame from guest memory now. */ + PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &pLsiReq->GuestRequest, cbRequest); + + /* Handle SCSI I/O requests now. */ + if (pLsiReq->GuestRequest.Header.u8Function == MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST) + { + rc = lsilogicR3ProcessSCSIIORequest(pThis, pLsiReq); + AssertRC(rc); + } + else + { + MptReplyUnion Reply; + rc = lsilogicR3ProcessMessageRequest(pThis, &pLsiReq->GuestRequest.Header, &Reply); + AssertRC(rc); + RTMemCacheFree(pThis->hTaskCache, pLsiReq); + } + + pThis->uRequestQueueNextAddressRead++; + pThis->uRequestQueueNextAddressRead %= pThis->cRequestQueueEntries; + } + } /* While request frames available. */ + } /* While running */ + + return VINF_SUCCESS; +} + + +/** + * Unblock the worker thread so it can respond to a state change. + * + * @returns VBox status code. + * @param pDevIns The pcnet device instance. + * @param pThread The send thread. + */ +static DECLCALLBACK(int) lsilogicR3WorkerWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread) +{ + PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); + return SUPSemEventSignal(pThis->pSupDrvSession, pThis->hEvtProcess); +} + + /** * Kicks the controller to process pending tasks after the VM was resumed * or loaded from a saved state. * * @returns nothing. - * @param pThis The LsiLogic device instance. + * @param pThis Pointer to the LsiLogic device state. */ -static void lsilogicKick(PLSILOGICSCSI pThis) +static void lsilogicR3Kick(PLSILOGICSCSI pThis) { if (pThis->fNotificationSend) { @@ -4089,13 +4308,21 @@ static void lsilogicKick(PLSILOGICSCSI pThis) else if (pThis->VBoxSCSI.fBusy) { /* The BIOS had a request active when we got suspended. Resume it. */ - int rc = lsilogicPrepareBIOSSCSIRequest(pThis); + int rc = lsilogicR3PrepareBiosScsiRequest(pThis); AssertRC(rc); } } -static DECLCALLBACK(int) lsilogicLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass) + +/* + * Saved state. + */ + +/** + * @callback_method_impl{FNSSMDEVLIVEEXEC} + */ +static DECLCALLBACK(int) lsilogicR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass) { PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); @@ -4110,62 +4337,77 @@ static DECLCALLBACK(int) lsilogicLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u return VINF_SSM_DONT_CALL_AGAIN; } -static DECLCALLBACK(int) lsilogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) +/** + * @callback_method_impl{FNSSMDEVSAVEEXEC} + */ +static DECLCALLBACK(int) lsilogicR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) { - PLSILOGICSCSI pLsiLogic = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); + PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); /* Every device first. */ - lsilogicLiveExec(pDevIns, pSSM, SSM_PASS_FINAL); - for (unsigned i = 0; i < pLsiLogic->cDeviceStates; i++) + lsilogicR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL); + for (unsigned i = 0; i < pThis->cDeviceStates; i++) { - PLSILOGICDEVICE pDevice = &pLsiLogic->paDeviceStates[i]; + PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i]; AssertMsg(!pDevice->cOutstandingRequests, ("There are still outstanding requests on this device\n")); SSMR3PutU32(pSSM, pDevice->cOutstandingRequests); } /* Now the main device state. */ - SSMR3PutU32 (pSSM, pLsiLogic->enmState); - SSMR3PutU32 (pSSM, pLsiLogic->enmWhoInit); - SSMR3PutBool (pSSM, pLsiLogic->fDoorbellInProgress); - SSMR3PutBool (pSSM, pLsiLogic->fDiagnosticEnabled); - SSMR3PutBool (pSSM, pLsiLogic->fNotificationSend); - SSMR3PutBool (pSSM, pLsiLogic->fEventNotificationEnabled); - SSMR3PutU32 (pSSM, pLsiLogic->uInterruptMask); - SSMR3PutU32 (pSSM, pLsiLogic->uInterruptStatus); - for (unsigned i = 0; i < RT_ELEMENTS(pLsiLogic->aMessage); i++) - SSMR3PutU32 (pSSM, pLsiLogic->aMessage[i]); - SSMR3PutU32 (pSSM, pLsiLogic->iMessage); - SSMR3PutU32 (pSSM, pLsiLogic->cMessage); - SSMR3PutMem (pSSM, &pLsiLogic->ReplyBuffer, sizeof(pLsiLogic->ReplyBuffer)); - SSMR3PutU32 (pSSM, pLsiLogic->uNextReplyEntryRead); - SSMR3PutU32 (pSSM, pLsiLogic->cReplySize); - SSMR3PutU16 (pSSM, pLsiLogic->u16IOCFaultCode); - SSMR3PutU32 (pSSM, pLsiLogic->u32HostMFAHighAddr); - SSMR3PutU32 (pSSM, pLsiLogic->u32SenseBufferHighAddr); - SSMR3PutU8 (pSSM, pLsiLogic->cMaxDevices); - SSMR3PutU8 (pSSM, pLsiLogic->cMaxBuses); - SSMR3PutU16 (pSSM, pLsiLogic->cbReplyFrame); - SSMR3PutU32 (pSSM, pLsiLogic->iDiagnosticAccess); - SSMR3PutU32 (pSSM, pLsiLogic->cReplyQueueEntries); - SSMR3PutU32 (pSSM, pLsiLogic->cRequestQueueEntries); - SSMR3PutU32 (pSSM, pLsiLogic->uReplyFreeQueueNextEntryFreeWrite); - SSMR3PutU32 (pSSM, pLsiLogic->uReplyFreeQueueNextAddressRead); - SSMR3PutU32 (pSSM, pLsiLogic->uReplyPostQueueNextEntryFreeWrite); - SSMR3PutU32 (pSSM, pLsiLogic->uReplyPostQueueNextAddressRead); - SSMR3PutU32 (pSSM, pLsiLogic->uRequestQueueNextEntryFreeWrite); - SSMR3PutU32 (pSSM, pLsiLogic->uRequestQueueNextAddressRead); - - for (unsigned i = 0; i < pLsiLogic->cReplyQueueEntries; i++) - SSMR3PutU32(pSSM, pLsiLogic->pReplyFreeQueueBaseR3[i]); - for (unsigned i = 0; i < pLsiLogic->cReplyQueueEntries; i++) - SSMR3PutU32(pSSM, pLsiLogic->pReplyPostQueueBaseR3[i]); - for (unsigned i = 0; i < pLsiLogic->cRequestQueueEntries; i++) - SSMR3PutU32(pSSM, pLsiLogic->pRequestQueueBaseR3[i]); - - SSMR3PutU16 (pSSM, pLsiLogic->u16NextHandle); - - PMptConfigurationPagesSupported pPages = pLsiLogic->pConfigurationPages; + SSMR3PutU32 (pSSM, pThis->enmState); + SSMR3PutU32 (pSSM, pThis->enmWhoInit); + SSMR3PutU32 (pSSM, pThis->enmDoorbellState); + SSMR3PutBool (pSSM, pThis->fDiagnosticEnabled); + SSMR3PutBool (pSSM, pThis->fNotificationSend); + SSMR3PutBool (pSSM, pThis->fEventNotificationEnabled); + SSMR3PutU32 (pSSM, pThis->uInterruptMask); + SSMR3PutU32 (pSSM, pThis->uInterruptStatus); + for (unsigned i = 0; i < RT_ELEMENTS(pThis->aMessage); i++) + SSMR3PutU32 (pSSM, pThis->aMessage[i]); + SSMR3PutU32 (pSSM, pThis->iMessage); + SSMR3PutU32 (pSSM, pThis->cMessage); + SSMR3PutMem (pSSM, &pThis->ReplyBuffer, sizeof(pThis->ReplyBuffer)); + SSMR3PutU32 (pSSM, pThis->uNextReplyEntryRead); + SSMR3PutU32 (pSSM, pThis->cReplySize); + SSMR3PutU16 (pSSM, pThis->u16IOCFaultCode); + SSMR3PutU32 (pSSM, pThis->u32HostMFAHighAddr); + SSMR3PutU32 (pSSM, pThis->u32SenseBufferHighAddr); + SSMR3PutU8 (pSSM, pThis->cMaxDevices); + SSMR3PutU8 (pSSM, pThis->cMaxBuses); + SSMR3PutU16 (pSSM, pThis->cbReplyFrame); + SSMR3PutU32 (pSSM, pThis->iDiagnosticAccess); + SSMR3PutU32 (pSSM, pThis->cReplyQueueEntries); + SSMR3PutU32 (pSSM, pThis->cRequestQueueEntries); + SSMR3PutU32 (pSSM, pThis->uReplyFreeQueueNextEntryFreeWrite); + SSMR3PutU32 (pSSM, pThis->uReplyFreeQueueNextAddressRead); + SSMR3PutU32 (pSSM, pThis->uReplyPostQueueNextEntryFreeWrite); + SSMR3PutU32 (pSSM, pThis->uReplyPostQueueNextAddressRead); + SSMR3PutU32 (pSSM, pThis->uRequestQueueNextEntryFreeWrite); + SSMR3PutU32 (pSSM, pThis->uRequestQueueNextAddressRead); + + for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++) + SSMR3PutU32(pSSM, pThis->pReplyFreeQueueBaseR3[i]); + for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++) + SSMR3PutU32(pSSM, pThis->pReplyPostQueueBaseR3[i]); + for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++) + SSMR3PutU32(pSSM, pThis->pRequestQueueBaseR3[i]); + + SSMR3PutU16 (pSSM, pThis->u16NextHandle); + + /* Save diagnostic memory register and data regions. */ + SSMR3PutU32 (pSSM, pThis->u32DiagMemAddr); + SSMR3PutU32 (pSSM, lsilogicR3MemRegionsCount(pThis)); + + PLSILOGICMEMREGN pIt = NULL; + RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList) + { + SSMR3PutU32(pSSM, pIt->u32AddrStart); + SSMR3PutU32(pSSM, pIt->u32AddrEnd); + SSMR3PutMem(pSSM, &pIt->au32Data[0], (pIt->u32AddrEnd - pIt->u32AddrStart + 1) * sizeof(uint32_t)); + } + + PMptConfigurationPagesSupported pPages = pThis->pConfigurationPages; SSMR3PutMem (pSSM, &pPages->ManufacturingPage0, sizeof(MptConfigurationPageManufacturing0)); SSMR3PutMem (pSSM, &pPages->ManufacturingPage1, sizeof(MptConfigurationPageManufacturing1)); @@ -4193,7 +4435,7 @@ static DECLCALLBACK(int) lsilogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) SSMR3PutMem (pSSM, &pPages->BIOSPage4, sizeof(MptConfigurationPageBIOS4)); /* Device dependent pages */ - if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) + if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) { PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages; @@ -4209,7 +4451,7 @@ static DECLCALLBACK(int) lsilogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3, sizeof(MptConfigurationPageSCSISPIDevice3)); } } - else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) + else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) { PMptConfigurationPagesSas pSasPages = &pPages->u.SasPages; @@ -4246,39 +4488,47 @@ static DECLCALLBACK(int) lsilogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) } } else - AssertMsgFailed(("Invalid controller type %d\n", pLsiLogic->enmCtrlType)); + AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType)); /* Now the data for the BIOS interface. */ - SSMR3PutU8 (pSSM, pLsiLogic->VBoxSCSI.regIdentify); - SSMR3PutU8 (pSSM, pLsiLogic->VBoxSCSI.uTargetDevice); - SSMR3PutU8 (pSSM, pLsiLogic->VBoxSCSI.uTxDir); - SSMR3PutU8 (pSSM, pLsiLogic->VBoxSCSI.cbCDB); - SSMR3PutMem (pSSM, pLsiLogic->VBoxSCSI.aCDB, sizeof(pLsiLogic->VBoxSCSI.aCDB)); - SSMR3PutU8 (pSSM, pLsiLogic->VBoxSCSI.iCDB); - SSMR3PutU32 (pSSM, pLsiLogic->VBoxSCSI.cbBuf); - SSMR3PutU32 (pSSM, pLsiLogic->VBoxSCSI.iBuf); - SSMR3PutBool (pSSM, pLsiLogic->VBoxSCSI.fBusy); - SSMR3PutU8 (pSSM, pLsiLogic->VBoxSCSI.enmState); - if (pLsiLogic->VBoxSCSI.cbBuf) - SSMR3PutMem(pSSM, pLsiLogic->VBoxSCSI.pBuf, pLsiLogic->VBoxSCSI.cbBuf); + SSMR3PutU8 (pSSM, pThis->VBoxSCSI.regIdentify); + SSMR3PutU8 (pSSM, pThis->VBoxSCSI.uTargetDevice); + SSMR3PutU8 (pSSM, pThis->VBoxSCSI.uTxDir); + SSMR3PutU8 (pSSM, pThis->VBoxSCSI.cbCDB); + SSMR3PutMem (pSSM, pThis->VBoxSCSI.abCDB, sizeof(pThis->VBoxSCSI.abCDB)); + SSMR3PutU8 (pSSM, pThis->VBoxSCSI.iCDB); + SSMR3PutU32 (pSSM, pThis->VBoxSCSI.cbBuf); + SSMR3PutU32 (pSSM, pThis->VBoxSCSI.iBuf); + SSMR3PutBool (pSSM, pThis->VBoxSCSI.fBusy); + SSMR3PutU8 (pSSM, pThis->VBoxSCSI.enmState); + if (pThis->VBoxSCSI.cbBuf) + SSMR3PutMem(pSSM, pThis->VBoxSCSI.pbBuf, pThis->VBoxSCSI.cbBuf); return SSMR3PutU32(pSSM, ~0); } -static DECLCALLBACK(int) lsilogicLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) +/** + * @callback_method_impl{FNSSMDEVLOADDONE} + */ +static DECLCALLBACK(int) lsilogicR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) { PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); - lsilogicKick(pThis); + lsilogicR3Kick(pThis); return VINF_SUCCESS; } -static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass) +/** + * @callback_method_impl{FNSSMDEVLOADEXEC} + */ +static DECLCALLBACK(int) lsilogicR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass) { - PLSILOGICSCSI pLsiLogic = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); + PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); int rc; if ( uVersion != LSILOGIC_SAVED_STATE_VERSION + && uVersion != LSILOGIC_SAVED_STATE_VERSION_PRE_DIAG_MEM + && uVersion != LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL && uVersion != LSILOGIC_SAVED_STATE_VERSION_PRE_SAS && uVersion != LSILOGIC_SAVED_STATE_VERSION_VBOX_30) return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION; @@ -4296,96 +4546,111 @@ static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u rc = SSMR3GetU32(pSSM, &cPorts); AssertRCReturn(rc, rc); - if (enmCtrlType != pLsiLogic->enmCtrlType) + if (enmCtrlType != pThis->enmCtrlType) return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Controller type): config=%d state=%d"), - pLsiLogic->enmCtrlType, enmCtrlType); - if (cDeviceStates != pLsiLogic->cDeviceStates) + pThis->enmCtrlType, enmCtrlType); + if (cDeviceStates != pThis->cDeviceStates) return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Device states): config=%u state=%u"), - pLsiLogic->cDeviceStates, cDeviceStates); - if (cPorts != pLsiLogic->cPorts) + pThis->cDeviceStates, cDeviceStates); + if (cPorts != pThis->cPorts) return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Ports): config=%u state=%u"), - pLsiLogic->cPorts, cPorts); + pThis->cPorts, cPorts); } if (uVersion > LSILOGIC_SAVED_STATE_VERSION_VBOX_30) { - for (unsigned i = 0; i < pLsiLogic->cDeviceStates; i++) + for (unsigned i = 0; i < pThis->cDeviceStates; i++) { bool fPresent; rc = SSMR3GetBool(pSSM, &fPresent); AssertRCReturn(rc, rc); - if (fPresent != (pLsiLogic->paDeviceStates[i].pDrvBase != NULL)) + if (fPresent != (pThis->paDeviceStates[i].pDrvBase != NULL)) return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target %u config mismatch: config=%RTbool state=%RTbool"), - i, pLsiLogic->paDeviceStates[i].pDrvBase != NULL, fPresent); + i, pThis->paDeviceStates[i].pDrvBase != NULL, fPresent); } } if (uPass != SSM_PASS_FINAL) return VINF_SUCCESS; /* Every device first. */ - for (unsigned i = 0; i < pLsiLogic->cDeviceStates; i++) + for (unsigned i = 0; i < pThis->cDeviceStates; i++) { - PLSILOGICDEVICE pDevice = &pLsiLogic->paDeviceStates[i]; + PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i]; AssertMsg(!pDevice->cOutstandingRequests, ("There are still outstanding requests on this device\n")); SSMR3GetU32(pSSM, (uint32_t *)&pDevice->cOutstandingRequests); } /* Now the main device state. */ - SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->enmState); - SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->enmWhoInit); - SSMR3GetBool (pSSM, &pLsiLogic->fDoorbellInProgress); - SSMR3GetBool (pSSM, &pLsiLogic->fDiagnosticEnabled); - SSMR3GetBool (pSSM, &pLsiLogic->fNotificationSend); - SSMR3GetBool (pSSM, &pLsiLogic->fEventNotificationEnabled); - SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uInterruptMask); - SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uInterruptStatus); - for (unsigned i = 0; i < RT_ELEMENTS(pLsiLogic->aMessage); i++) - SSMR3GetU32 (pSSM, &pLsiLogic->aMessage[i]); - SSMR3GetU32 (pSSM, &pLsiLogic->iMessage); - SSMR3GetU32 (pSSM, &pLsiLogic->cMessage); - SSMR3GetMem (pSSM, &pLsiLogic->ReplyBuffer, sizeof(pLsiLogic->ReplyBuffer)); - SSMR3GetU32 (pSSM, &pLsiLogic->uNextReplyEntryRead); - SSMR3GetU32 (pSSM, &pLsiLogic->cReplySize); - SSMR3GetU16 (pSSM, &pLsiLogic->u16IOCFaultCode); - SSMR3GetU32 (pSSM, &pLsiLogic->u32HostMFAHighAddr); - SSMR3GetU32 (pSSM, &pLsiLogic->u32SenseBufferHighAddr); - SSMR3GetU8 (pSSM, &pLsiLogic->cMaxDevices); - SSMR3GetU8 (pSSM, &pLsiLogic->cMaxBuses); - SSMR3GetU16 (pSSM, &pLsiLogic->cbReplyFrame); - SSMR3GetU32 (pSSM, &pLsiLogic->iDiagnosticAccess); + SSMR3GetU32 (pSSM, (uint32_t *)&pThis->enmState); + SSMR3GetU32 (pSSM, (uint32_t *)&pThis->enmWhoInit); + if (uVersion <= LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL) + { + bool fDoorbellInProgress = false; + + /* + * The doorbell status flag distinguishes only between + * doorbell not in use or a Function handshake is currently in progress. + */ + SSMR3GetBool (pSSM, &fDoorbellInProgress); + if (fDoorbellInProgress) + pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_FN_HANDSHAKE; + else + pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE; + } + else + SSMR3GetU32(pSSM, (uint32_t *)&pThis->enmDoorbellState); + SSMR3GetBool (pSSM, &pThis->fDiagnosticEnabled); + SSMR3GetBool (pSSM, &pThis->fNotificationSend); + SSMR3GetBool (pSSM, &pThis->fEventNotificationEnabled); + SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uInterruptMask); + SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uInterruptStatus); + for (unsigned i = 0; i < RT_ELEMENTS(pThis->aMessage); i++) + SSMR3GetU32 (pSSM, &pThis->aMessage[i]); + SSMR3GetU32 (pSSM, &pThis->iMessage); + SSMR3GetU32 (pSSM, &pThis->cMessage); + SSMR3GetMem (pSSM, &pThis->ReplyBuffer, sizeof(pThis->ReplyBuffer)); + SSMR3GetU32 (pSSM, &pThis->uNextReplyEntryRead); + SSMR3GetU32 (pSSM, &pThis->cReplySize); + SSMR3GetU16 (pSSM, &pThis->u16IOCFaultCode); + SSMR3GetU32 (pSSM, &pThis->u32HostMFAHighAddr); + SSMR3GetU32 (pSSM, &pThis->u32SenseBufferHighAddr); + SSMR3GetU8 (pSSM, &pThis->cMaxDevices); + SSMR3GetU8 (pSSM, &pThis->cMaxBuses); + SSMR3GetU16 (pSSM, &pThis->cbReplyFrame); + SSMR3GetU32 (pSSM, &pThis->iDiagnosticAccess); uint32_t cReplyQueueEntries, cRequestQueueEntries; SSMR3GetU32 (pSSM, &cReplyQueueEntries); SSMR3GetU32 (pSSM, &cRequestQueueEntries); - if ( cReplyQueueEntries != pLsiLogic->cReplyQueueEntries - || cRequestQueueEntries != pLsiLogic->cRequestQueueEntries) + if ( cReplyQueueEntries != pThis->cReplyQueueEntries + || cRequestQueueEntries != pThis->cRequestQueueEntries) { LogFlow(("Reallocating queues cReplyQueueEntries=%u cRequestQueuEntries=%u\n", cReplyQueueEntries, cRequestQueueEntries)); - lsilogicQueuesFree(pLsiLogic); - pLsiLogic->cReplyQueueEntries = cReplyQueueEntries; - pLsiLogic->cRequestQueueEntries = cRequestQueueEntries; - rc = lsilogicQueuesAlloc(pLsiLogic); + lsilogicR3QueuesFree(pThis); + pThis->cReplyQueueEntries = cReplyQueueEntries; + pThis->cRequestQueueEntries = cRequestQueueEntries; + rc = lsilogicR3QueuesAlloc(pThis); if (RT_FAILURE(rc)) return rc; } - SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uReplyFreeQueueNextEntryFreeWrite); - SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uReplyFreeQueueNextAddressRead); - SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uReplyPostQueueNextEntryFreeWrite); - SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uReplyPostQueueNextAddressRead); - SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uRequestQueueNextEntryFreeWrite); - SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uRequestQueueNextAddressRead); + SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyFreeQueueNextEntryFreeWrite); + SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyFreeQueueNextAddressRead); + SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyPostQueueNextEntryFreeWrite); + SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyPostQueueNextAddressRead); + SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uRequestQueueNextEntryFreeWrite); + SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uRequestQueueNextAddressRead); - PMptConfigurationPagesSupported pPages = pLsiLogic->pConfigurationPages; + PMptConfigurationPagesSupported pPages = pThis->pConfigurationPages; if (uVersion <= LSILOGIC_SAVED_STATE_VERSION_PRE_SAS) { PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages; MptConfigurationPagesSupported_SSM_V2 ConfigPagesV2; - if (pLsiLogic->enmCtrlType != LSILOGICCTRLTYPE_SCSI_SPI) + if (pThis->enmCtrlType != LSILOGICCTRLTYPE_SCSI_SPI) return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch: Expected SPI SCSI controller")); SSMR3GetMem(pSSM, &ConfigPagesV2, @@ -4422,14 +4687,52 @@ static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u else { /* Queue content */ - for (unsigned i = 0; i < pLsiLogic->cReplyQueueEntries; i++) - SSMR3GetU32(pSSM, (uint32_t *)&pLsiLogic->pReplyFreeQueueBaseR3[i]); - for (unsigned i = 0; i < pLsiLogic->cReplyQueueEntries; i++) - SSMR3GetU32(pSSM, (uint32_t *)&pLsiLogic->pReplyPostQueueBaseR3[i]); - for (unsigned i = 0; i < pLsiLogic->cRequestQueueEntries; i++) - SSMR3GetU32(pSSM, (uint32_t *)&pLsiLogic->pRequestQueueBaseR3[i]); + for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++) + SSMR3GetU32(pSSM, (uint32_t *)&pThis->pReplyFreeQueueBaseR3[i]); + for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++) + SSMR3GetU32(pSSM, (uint32_t *)&pThis->pReplyPostQueueBaseR3[i]); + for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++) + SSMR3GetU32(pSSM, (uint32_t *)&pThis->pRequestQueueBaseR3[i]); + + SSMR3GetU16(pSSM, &pThis->u16NextHandle); + + if (uVersion > LSILOGIC_SAVED_STATE_VERSION_PRE_DIAG_MEM) + { + uint32_t cMemRegions = 0; + + /* Save diagnostic memory register and data regions. */ + SSMR3GetU32 (pSSM, &pThis->u32DiagMemAddr); + SSMR3GetU32 (pSSM, &cMemRegions); + + while (cMemRegions) + { + uint32_t u32AddrStart = 0; + uint32_t u32AddrEnd = 0; + uint32_t cRegion = 0; + PLSILOGICMEMREGN pRegion = NULL; - SSMR3GetU16(pSSM, &pLsiLogic->u16NextHandle); + SSMR3GetU32(pSSM, &u32AddrStart); + SSMR3GetU32(pSSM, &u32AddrEnd); + + cRegion = u32AddrEnd - u32AddrStart + 1; + pRegion = (PLSILOGICMEMREGN)RTMemAllocZ(RT_OFFSETOF(LSILOGICMEMREGN, au32Data[cRegion])); + if (pRegion) + { + pRegion->u32AddrStart = u32AddrStart; + pRegion->u32AddrEnd = u32AddrEnd; + SSMR3GetMem(pSSM, &pRegion->au32Data[0], cRegion * sizeof(uint32_t)); + lsilogicR3MemRegionInsert(pThis, pRegion); + pThis->cbMemRegns += cRegion * sizeof(uint32_t); + } + else + { + /* Leave a log message but continue. */ + LogRel(("LsiLogic: Out of memory while restoring the state, might not work as expected\n")); + SSMR3Skip(pSSM, cRegion * sizeof(uint32_t)); + } + cMemRegions--; + } + } /* Configuration pages */ SSMR3GetMem(pSSM, &pPages->ManufacturingPage0, sizeof(MptConfigurationPageManufacturing0)); @@ -4458,7 +4761,7 @@ static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u SSMR3GetMem(pSSM, &pPages->BIOSPage4, sizeof(MptConfigurationPageBIOS4)); /* Device dependent pages */ - if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) + if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) { PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages; @@ -4474,7 +4777,7 @@ static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3, sizeof(MptConfigurationPageSCSISPIDevice3)); } } - else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) + else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) { uint32_t cbPage0, cbPage1, cPHYs, cbManufacturingPage7; PMptConfigurationPagesSas pSasPages = &pPages->u.SasPages; @@ -4527,30 +4830,30 @@ static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u Assert(!pCurr); } else - AssertMsgFailed(("Invalid controller type %d\n", pLsiLogic->enmCtrlType)); + AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType)); } /* Now the data for the BIOS interface. */ - SSMR3GetU8 (pSSM, &pLsiLogic->VBoxSCSI.regIdentify); - SSMR3GetU8 (pSSM, &pLsiLogic->VBoxSCSI.uTargetDevice); - SSMR3GetU8 (pSSM, &pLsiLogic->VBoxSCSI.uTxDir); - SSMR3GetU8 (pSSM, &pLsiLogic->VBoxSCSI.cbCDB); - SSMR3GetMem (pSSM, pLsiLogic->VBoxSCSI.aCDB, sizeof(pLsiLogic->VBoxSCSI.aCDB)); - SSMR3GetU8 (pSSM, &pLsiLogic->VBoxSCSI.iCDB); - SSMR3GetU32 (pSSM, &pLsiLogic->VBoxSCSI.cbBuf); - SSMR3GetU32 (pSSM, &pLsiLogic->VBoxSCSI.iBuf); - SSMR3GetBool(pSSM, (bool *)&pLsiLogic->VBoxSCSI.fBusy); - SSMR3GetU8 (pSSM, (uint8_t *)&pLsiLogic->VBoxSCSI.enmState); - if (pLsiLogic->VBoxSCSI.cbBuf) + SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.regIdentify); + SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.uTargetDevice); + SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.uTxDir); + SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.cbCDB); + SSMR3GetMem (pSSM, pThis->VBoxSCSI.abCDB, sizeof(pThis->VBoxSCSI.abCDB)); + SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.iCDB); + SSMR3GetU32 (pSSM, &pThis->VBoxSCSI.cbBuf); + SSMR3GetU32 (pSSM, &pThis->VBoxSCSI.iBuf); + SSMR3GetBool(pSSM, (bool *)&pThis->VBoxSCSI.fBusy); + SSMR3GetU8 (pSSM, (uint8_t *)&pThis->VBoxSCSI.enmState); + if (pThis->VBoxSCSI.cbBuf) { - pLsiLogic->VBoxSCSI.pBuf = (uint8_t *)RTMemAllocZ(pLsiLogic->VBoxSCSI.cbBuf); - if (!pLsiLogic->VBoxSCSI.pBuf) + pThis->VBoxSCSI.pbBuf = (uint8_t *)RTMemAllocZ(pThis->VBoxSCSI.cbBuf); + if (!pThis->VBoxSCSI.pbBuf) { LogRel(("LsiLogic: Out of memory during restore.\n")); return PDMDEV_SET_ERROR(pDevIns, VERR_NO_MEMORY, N_("LsiLogic: Out of memory during restore\n")); } - SSMR3GetMem(pSSM, pLsiLogic->VBoxSCSI.pBuf, pLsiLogic->VBoxSCSI.cbBuf); + SSMR3GetMem(pSSM, pThis->VBoxSCSI.pbBuf, pThis->VBoxSCSI.cbBuf); } uint32_t u32; @@ -4562,18 +4865,19 @@ static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u return VINF_SUCCESS; } + +/* + * The device level IBASE and LED interfaces. + */ + /** - * Gets the pointer to the status LED of a device - called from the SCSi driver. + * @interface_method_impl{PDMILEDPORTS,pfnQueryInterface, For a SCSI device.} * - * @returns VBox status code. - * @param pInterface Pointer to the interface structure containing the called function pointer. - * @param iLUN The unit which status LED we desire. Always 0 here as the driver - * doesn't know about other LUN's. - * @param ppLed Where to store the LED pointer. + * @remarks Called by the scsi driver, proxying the main calls. */ -static DECLCALLBACK(int) lsilogicDeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed) +static DECLCALLBACK(int) lsilogicR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed) { - PLSILOGICDEVICE pDevice = PDMILEDPORTS_2_PLSILOGICDEVICE(pInterface); + PLSILOGICDEVICE pDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, ILed); if (iLUN == 0) { *ppLed = &pDevice->Led; @@ -4587,9 +4891,9 @@ static DECLCALLBACK(int) lsilogicDeviceQueryStatusLed(PPDMILEDPORTS pInterface, /** * @interface_method_impl{PDMIBASE,pfnQueryInterface} */ -static DECLCALLBACK(void *) lsilogicDeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID) +static DECLCALLBACK(void *) lsilogicR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID) { - PLSILOGICDEVICE pDevice = PDMIBASE_2_PLSILOGICDEVICE(pInterface); + PLSILOGICDEVICE pDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IBase); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevice->IBase); PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSIPORT, &pDevice->ISCSIPort); @@ -4597,6 +4901,11 @@ static DECLCALLBACK(void *) lsilogicDeviceQueryInterface(PPDMIBASE pInterface, c return NULL; } + +/* + * The controller level IBASE and LED interfaces. + */ + /** * Gets the pointer to the status LED of a unit. * @@ -4605,12 +4914,12 @@ static DECLCALLBACK(void *) lsilogicDeviceQueryInterface(PPDMIBASE pInterface, c * @param iLUN The unit which status LED we desire. * @param ppLed Where to store the LED pointer. */ -static DECLCALLBACK(int) lsilogicStatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed) +static DECLCALLBACK(int) lsilogicR3StatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed) { - PLSILOGICSCSI pLsiLogic = PDMILEDPORTS_2_PLSILOGICSCSI(pInterface); - if (iLUN < pLsiLogic->cDeviceStates) + PLSILOGICSCSI pThis = RT_FROM_MEMBER(pInterface, LSILOGICSCSI, ILeds); + if (iLUN < pThis->cDeviceStates) { - *ppLed = &pLsiLogic->paDeviceStates[iLUN].Led; + *ppLed = &pThis->paDeviceStates[iLUN].Led; Assert((*ppLed)->u32Magic == PDMLED_MAGIC); return VINF_SUCCESS; } @@ -4620,20 +4929,23 @@ static DECLCALLBACK(int) lsilogicStatusQueryStatusLed(PPDMILEDPORTS pInterface, /** * @interface_method_impl{PDMIBASE,pfnQueryInterface} */ -static DECLCALLBACK(void *) lsilogicStatusQueryInterface(PPDMIBASE pInterface, const char *pszIID) +static DECLCALLBACK(void *) lsilogicR3StatusQueryInterface(PPDMIBASE pInterface, const char *pszIID) { - PLSILOGICSCSI pThis = PDMIBASE_2_PLSILOGICSCSI(pInterface); + PLSILOGICSCSI pThis = RT_FROM_MEMBER(pInterface, LSILOGICSCSI, IBase); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase); PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds); return NULL; } -/* -=-=-=-=- Helper -=-=-=-=- */ + +/* + * The PDM device interface and some helpers. + */ /** * Checks if all asynchronous I/O is finished. * - * Used by lsilogicReset, lsilogicSuspend and lsilogicPowerOff. + * Used by lsilogicR3Reset, lsilogicR3Suspend and lsilogicR3PowerOff. * * @returns true if quiesced, false if busy. * @param pDevIns The device instance. @@ -4656,10 +4968,8 @@ static bool lsilogicR3AllAsyncIOIsFinished(PPDMDEVINS pDevIns) } /** - * Callback employed by lsilogicR3Suspend and lsilogicR3PowerOff.. - * - * @returns true if we've quiesced, false if we're still working. - * @param pDevIns The device instance. + * @callback_method_impl{FNPDMDEVASYNCNOTIFY, + * Callback employed by lsilogicR3Suspend and lsilogicR3PowerOff.} */ static DECLCALLBACK(bool) lsilogicR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns) { @@ -4695,19 +5005,19 @@ static void lsilogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns) * Guest execution is suspended at this point so there is no race between us and * lsilogicRegisterWrite. */ - PLSILOGICTASKSTATE pTaskState = pThis->pTasksRedoHead; + PLSILOGICREQ pLsiReq = pThis->pTasksRedoHead; pThis->pTasksRedoHead = NULL; - while (pTaskState) + while (pLsiReq) { - PLSILOGICTASKSTATE pFree; + PLSILOGICREQ pFree; - if (!pTaskState->fBIOS) + if (!pLsiReq->fBIOS) { /* Write only the lower 32bit part of the address. */ ASMAtomicWriteU32(&pThis->CTX_SUFF(pRequestQueueBase)[pThis->uRequestQueueNextEntryFreeWrite], - pTaskState->GCPhysMessageFrameAddr & UINT32_C(0xffffffff)); + pLsiReq->GCPhysMessageFrameAddr & UINT32_C(0xffffffff)); pThis->uRequestQueueNextEntryFreeWrite++; pThis->uRequestQueueNextEntryFreeWrite %= pThis->cRequestQueueEntries; @@ -4716,12 +5026,12 @@ static void lsilogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns) } else { - AssertMsg(!pTaskState->pRedoNext, ("Only one BIOS task can be active!\n")); - vboxscsiSetRequestRedo(&pThis->VBoxSCSI, &pTaskState->PDMScsiRequest); + AssertMsg(!pLsiReq->pRedoNext, ("Only one BIOS task can be active!\n")); + vboxscsiSetRequestRedo(&pThis->VBoxSCSI, &pLsiReq->PDMScsiRequest); } - pFree = pTaskState; - pTaskState = pTaskState->pRedoNext; + pFree = pLsiReq; + pLsiReq = pLsiReq->pRedoNext; RTMemCacheFree(pThis->hTaskCache, pFree); } @@ -4731,41 +5041,33 @@ static void lsilogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns) } /** - * Suspend notification. - * - * @param pDevIns The device instance data. + * @interface_method_impl{PDMDEVREG,pfnSuspend} */ -static DECLCALLBACK(void) lsilogicSuspend(PPDMDEVINS pDevIns) +static DECLCALLBACK(void) lsilogicR3Suspend(PPDMDEVINS pDevIns) { - Log(("lsilogicSuspend\n")); + Log(("lsilogicR3Suspend\n")); lsilogicR3SuspendOrPowerOff(pDevIns); } /** - * Resume notification. - * - * @param pDevIns The device instance data. + * @interface_method_impl{PDMDEVREG,pfnResume} */ -static DECLCALLBACK(void) lsilogicResume(PPDMDEVINS pDevIns) +static DECLCALLBACK(void) lsilogicR3Resume(PPDMDEVINS pDevIns) { PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); - Log(("lsilogicResume\n")); + Log(("lsilogicR3Resume\n")); - lsilogicKick(pThis); + lsilogicR3Kick(pThis); } /** - * Detach notification. + * @interface_method_impl{PDMDEVREG,pfnDetach} * * One harddisk at one port has been unplugged. * The VM is suspended at this point. - * - * @param pDevIns The device instance. - * @param iLUN The logical unit which is being detached. - * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines. */ -static DECLCALLBACK(void) lsilogicDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) +static DECLCALLBACK(void) lsilogicR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) { PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[iLUN]; @@ -4786,16 +5088,9 @@ static DECLCALLBACK(void) lsilogicDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint } /** - * Attach command. - * - * This is called when we change block driver. - * - * @returns VBox status code. - * @param pDevIns The device instance. - * @param iLUN The logical unit which is being detached. - * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines. + * @interface_method_impl{PDMDEVREG,pfnAttach} */ -static DECLCALLBACK(int) lsilogicAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) +static DECLCALLBACK(int) lsilogicR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) { PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[iLUN]; @@ -4842,20 +5137,18 @@ static DECLCALLBACK(int) lsilogicAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint */ static void lsilogicR3ResetCommon(PPDMDEVINS pDevIns) { - PLSILOGICSCSI pLsiLogic = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); + PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); int rc; - rc = lsilogicHardReset(pLsiLogic); + rc = lsilogicR3HardReset(pThis); AssertRC(rc); - vboxscsiInitialize(&pLsiLogic->VBoxSCSI); + vboxscsiInitialize(&pThis->VBoxSCSI); } /** - * Callback employed by lsilogicR3Reset. - * - * @returns true if we've quiesced, false if we're still working. - * @param pDevIns The device instance. + * @callback_method_impl{FNPDMDEVASYNCNOTIFY, + * Callback employed by lsilogicR3Reset.} */ static DECLCALLBACK(bool) lsilogicR3IsAsyncResetDone(PPDMDEVINS pDevIns) { @@ -4870,9 +5163,9 @@ static DECLCALLBACK(bool) lsilogicR3IsAsyncResetDone(PPDMDEVINS pDevIns) } /** - * @copydoc FNPDMDEVRESET + * @interface_method_impl{PDMDEVREG,pfnReset} */ -static DECLCALLBACK(void) lsilogicReset(PPDMDEVINS pDevIns) +static DECLCALLBACK(void) lsilogicR3Reset(PPDMDEVINS pDevIns) { PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); @@ -4887,9 +5180,9 @@ static DECLCALLBACK(void) lsilogicReset(PPDMDEVINS pDevIns) } /** - * @copydoc FNPDMDEVRELOCATE + * @interface_method_impl{PDMDEVREG,pfnRelocate} */ -static DECLCALLBACK(void) lsilogicRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta) +static DECLCALLBACK(void) lsilogicR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta) { PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); @@ -4903,9 +5196,18 @@ static DECLCALLBACK(void) lsilogicRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDel } /** - * @copydoc FNPDMDEVDESTRUCT + * @interface_method_impl{PDMDEVREG,pfnPowerOff} */ -static DECLCALLBACK(int) lsilogicDestruct(PPDMDEVINS pDevIns) +static DECLCALLBACK(void) lsilogicR3PowerOff(PPDMDEVINS pDevIns) +{ + Log(("lsilogicR3PowerOff\n")); + lsilogicR3SuspendOrPowerOff(pDevIns); +} + +/** + * @interface_method_impl{PDMDEVREG,pfnDestruct} + */ +static DECLCALLBACK(int) lsilogicR3Destruct(PPDMDEVINS pDevIns) { PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); @@ -4913,43 +5215,45 @@ static DECLCALLBACK(int) lsilogicDestruct(PPDMDEVINS pDevIns) PDMR3CritSectDelete(&pThis->ReplyFreeQueueCritSect); PDMR3CritSectDelete(&pThis->ReplyPostQueueCritSect); - if (pThis->paDeviceStates) - RTMemFree(pThis->paDeviceStates); + RTMemFree(pThis->paDeviceStates); + pThis->paDeviceStates = NULL; /* Destroy task cache. */ - int rc = VINF_SUCCESS; if (pThis->hTaskCache != NIL_RTMEMCACHE) - rc = RTMemCacheDestroy(pThis->hTaskCache); + { + int rc = RTMemCacheDestroy(pThis->hTaskCache); AssertRC(rc); + pThis->hTaskCache = NIL_RTMEMCACHE; + } - lsilogicConfigurationPagesFree(pThis); + if (pThis->hEvtProcess != NIL_SUPSEMEVENT) + { + SUPSemEventClose(pThis->pSupDrvSession, pThis->hEvtProcess); + pThis->hEvtProcess = NIL_SUPSEMEVENT; + } - return rc; -} + lsilogicR3ConfigurationPagesFree(pThis); + lsilogicR3MemRegionsFree(pThis); -/** - * Poweroff notification. - * - * @param pDevIns Pointer to the device instance - */ -static DECLCALLBACK(void) lsilogicPowerOff(PPDMDEVINS pDevIns) -{ - Log(("lsilogicPowerOff\n")); - lsilogicR3SuspendOrPowerOff(pDevIns); + return VINF_SUCCESS; } /** - * @copydoc FNPDMDEVCONSTRUCT + * @interface_method_impl{PDMDEVREG,pfnConstruct} */ -static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg) +static DECLCALLBACK(int) lsilogicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg) { PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI); - int rc = VINF_SUCCESS; - char *pszCtrlType = NULL; - char szDevTag[20]; - bool fBootable = true; + int rc = VINF_SUCCESS; PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); /* + * Initialize enought of the state to make the destructure not trip up. + */ + pThis->hTaskCache = NIL_RTMEMCACHE; + pThis->hEvtProcess = NIL_SUPSEMEVENT; + RTListInit(&pThis->ListMemRegns); + + /* * Validate and read configuration. */ rc = CFGMR3AreValuesValid(pCfg, "GCEnabled\0" @@ -4990,6 +5294,7 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC N_("LsiLogic configuration error: failed to read RequestQueue as integer")); Log(("%s: RequestQueueDepth=%u\n", __FUNCTION__, pThis->cRequestQueueEntries)); + char *pszCtrlType; rc = CFGMR3QueryStringAllocDef(pCfg, "ControllerType", &pszCtrlType, LSILOGICSCSI_PCI_SPI_CTRLNAME); if (RT_FAILURE(rc)) @@ -4997,9 +5302,10 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC N_("LsiLogic configuration error: failed to read ControllerType as string")); Log(("%s: ControllerType=%s\n", __FUNCTION__, pszCtrlType)); - rc = lsilogicGetCtrlTypeFromString(pThis, pszCtrlType); + rc = lsilogicR3GetCtrlTypeFromString(pThis, pszCtrlType); MMR3HeapFree(pszCtrlType); + char szDevTag[20]; RTStrPrintf(szDevTag, sizeof(szDevTag), "LSILOGIC%s-%u", pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI ? "SPI" : "SAS", iInstance); @@ -5024,6 +5330,7 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic configuration error: failed to read NumPorts as integer")); + bool fBootable; rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &fBootable, true); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, @@ -5053,56 +5360,72 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC PCIDevSetClassBase (&pThis->PciDev, 0x01); /* Mass storage */ PCIDevSetInterruptPin(&pThis->PciDev, 0x01); /* Interrupt pin A */ -#ifdef VBOX_WITH_MSI_DEVICES +# ifdef VBOX_WITH_MSI_DEVICES PCIDevSetStatus(&pThis->PciDev, VBOX_PCI_STATUS_CAP_LIST); PCIDevSetCapabilityList(&pThis->PciDev, 0x80); -#endif +# endif pThis->pDevInsR3 = pDevIns; pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns); pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); - pThis->IBase.pfnQueryInterface = lsilogicStatusQueryInterface; - pThis->ILeds.pfnQueryStatusLed = lsilogicStatusQueryStatusLed; + pThis->pSupDrvSession = PDMDevHlpGetSupDrvSession(pDevIns); + pThis->IBase.pfnQueryInterface = lsilogicR3StatusQueryInterface; + pThis->ILeds.pfnQueryStatusLed = lsilogicR3StatusQueryStatusLed; + + /* + * Create critical sections protecting the reply post and free queues. + * Note! We do our own syncronization, so NOP the default crit sect for the device. + */ + rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns)); + AssertRCReturn(rc, rc); + + rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyFreeQueueCritSect, RT_SRC_POS, "%sRFQ", szDevTag); + if (RT_FAILURE(rc)) + return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply free queue")); + + rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyPostQueueCritSect, RT_SRC_POS, "%sRPQ", szDevTag); + if (RT_FAILURE(rc)) + return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply post queue")); /* * Register the PCI device, it's I/O regions. */ - rc = PDMDevHlpPCIRegister (pDevIns, &pThis->PciDev); + rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev); if (RT_FAILURE(rc)) return rc; -#ifdef VBOX_WITH_MSI_DEVICES - PDMMSIREG aMsiReg; - RT_ZERO(aMsiReg); +# ifdef VBOX_WITH_MSI_DEVICES + PDMMSIREG MsiReg; + RT_ZERO(MsiReg); /* use this code for MSI-X support */ -#if 0 - aMsiReg.cMsixVectors = 1; - aMsiReg.iMsixCapOffset = 0x80; - aMsiReg.iMsixNextOffset = 0x0; - aMsiReg.iMsixBar = 3; -#else - aMsiReg.cMsiVectors = 1; - aMsiReg.iMsiCapOffset = 0x80; - aMsiReg.iMsiNextOffset = 0x0; -#endif - rc = PDMDevHlpPCIRegisterMsi(pDevIns, &aMsiReg); +# if 0 + MsiReg.cMsixVectors = 1; + MsiReg.iMsixCapOffset = 0x80; + MsiReg.iMsixNextOffset = 0x00; + MsiReg.iMsixBar = 3; +# else + MsiReg.cMsiVectors = 1; + MsiReg.iMsiCapOffset = 0x80; + MsiReg.iMsiNextOffset = 0x00; +# endif + rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg); if (RT_FAILURE (rc)) { LogRel(("Chipset cannot do MSI: %Rrc\n", rc)); /* That's OK, we can work without MSI */ PCIDevSetCapabilityList(&pThis->PciDev, 0x0); } -#endif +# endif - rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, LSILOGIC_PCI_SPACE_IO_SIZE, PCI_ADDRESS_SPACE_IO, lsilogicMap); + rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, LSILOGIC_PCI_SPACE_IO_SIZE, PCI_ADDRESS_SPACE_IO, lsilogicR3Map); if (RT_FAILURE(rc)) return rc; - rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicMap); + rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicR3Map); if (RT_FAILURE(rc)) return rc; - rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicMap); + rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicR3Map); if (RT_FAILURE(rc)) return rc; @@ -5110,7 +5433,7 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC char szTaggedText[64]; RTStrPrintf(szTaggedText, sizeof(szTaggedText), "%s-Task", szDevTag); rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 2, 0, - lsilogicNotifyQueueConsumer, true, + lsilogicR3NotifyQueueConsumer, true, szTaggedText, &pThis->pNotificationQueueR3); if (RT_FAILURE(rc)) @@ -5127,31 +5450,17 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC /* * Allocate memory for the queues. */ - rc = lsilogicQueuesAlloc(pThis); + rc = lsilogicR3QueuesAlloc(pThis); if (RT_FAILURE(rc)) return rc; /* - * Create critical sections protecting the reply post and free queues. - */ - rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyFreeQueueCritSect, RT_SRC_POS, "%sRFQ", szDevTag); - if (RT_FAILURE(rc)) - return PDMDEV_SET_ERROR(pDevIns, rc, - N_("LsiLogic: cannot create critical section for reply free queue")); - - rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyPostQueueCritSect, RT_SRC_POS, "%sRPQ", szDevTag); - if (RT_FAILURE(rc)) - return PDMDEV_SET_ERROR(pDevIns, rc, - N_("LsiLogic: cannot create critical section for reply post queue")); - - /* * Allocate task cache. */ - rc = RTMemCacheCreate(&pThis->hTaskCache, sizeof(LSILOGICTASKSTATE), 0, UINT32_MAX, - lsilogicTaskStateCtor, lsilogicTaskStateDtor, NULL, 0); + rc = RTMemCacheCreate(&pThis->hTaskCache, sizeof(LSILOGICREQ), 0, UINT32_MAX, + NULL, NULL, NULL, 0); if (RT_FAILURE(rc)) - return PDMDEV_SET_ERROR(pDevIns, rc, - N_("Cannot create task cache")); + return PDMDEV_SET_ERROR(pDevIns, rc, N_("Cannot create task cache")); if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) pThis->cDeviceStates = pThis->cPorts * LSILOGICSCSI_PCI_SPI_DEVICES_PER_BUS_MAX; @@ -5161,12 +5470,25 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC AssertMsgFailed(("Invalid controller type: %d\n", pThis->enmCtrlType)); /* + * Create event semaphore and worker thread. + */ + rc = PDMDevHlpThreadCreate(pDevIns, &pThis->pThreadWrk, pThis, lsilogicR3Worker, + lsilogicR3WorkerWakeUp, 0, RTTHREADTYPE_IO, szDevTag); + if (RT_FAILURE(rc)) + return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, + N_("LsiLogic: Failed to create worker thread %s"), szDevTag); + + rc = SUPSemEventCreate(pThis->pSupDrvSession, &pThis->hEvtProcess); + if (RT_FAILURE(rc)) + return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, + N_("LsiLogic: Failed to create SUP event semaphore")); + + /* * Allocate device states. */ pThis->paDeviceStates = (PLSILOGICDEVICE)RTMemAllocZ(sizeof(LSILOGICDEVICE) * pThis->cDeviceStates); if (!pThis->paDeviceStates) - return PDMDEV_SET_ERROR(pDevIns, rc, - N_("Failed to allocate memory for device states")); + return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to allocate memory for device states")); for (unsigned i = 0; i < pThis->cDeviceStates; i++) { @@ -5177,12 +5499,12 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC pDevice->iLUN = i; pDevice->pLsiLogicR3 = pThis; pDevice->Led.u32Magic = PDMLED_MAGIC; - pDevice->IBase.pfnQueryInterface = lsilogicDeviceQueryInterface; - pDevice->ISCSIPort.pfnSCSIRequestCompleted = lsilogicDeviceSCSIRequestCompleted; - pDevice->ISCSIPort.pfnQueryDeviceLocation = lsilogicQueryDeviceLocation; - pDevice->ILed.pfnQueryStatusLed = lsilogicDeviceQueryStatusLed; + pDevice->IBase.pfnQueryInterface = lsilogicR3DeviceQueryInterface; + pDevice->ISCSIPort.pfnSCSIRequestCompleted = lsilogicR3DeviceSCSIRequestCompleted; + pDevice->ISCSIPort.pfnQueryDeviceLocation = lsilogicR3QueryDeviceLocation; + pDevice->ILed.pfnQueryStatusLed = lsilogicR3DeviceQueryStatusLed; - RTStrPrintf(szName, sizeof(szName), "Device%d", i); + RTStrPrintf(szName, sizeof(szName), "Device%u", i); /* Attach SCSI driver. */ rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, szName); @@ -5230,13 +5552,13 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC { if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI) rc = PDMDevHlpIOPortRegister(pDevIns, LSILOGIC_BIOS_IO_PORT, 4, NULL, - lsilogicIsaIOPortWrite, lsilogicIsaIOPortRead, - lsilogicIsaIOPortWriteStr, lsilogicIsaIOPortReadStr, + lsilogicR3IsaIOPortWrite, lsilogicR3IsaIOPortRead, + lsilogicR3IsaIOPortWriteStr, lsilogicR3IsaIOPortReadStr, "LsiLogic BIOS"); else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS) rc = PDMDevHlpIOPortRegister(pDevIns, LSILOGIC_SAS_BIOS_IO_PORT, 4, NULL, - lsilogicIsaIOPortWrite, lsilogicIsaIOPortRead, - lsilogicIsaIOPortWriteStr, lsilogicIsaIOPortReadStr, + lsilogicR3IsaIOPortWrite, lsilogicR3IsaIOPortRead, + lsilogicR3IsaIOPortWriteStr, lsilogicR3IsaIOPortReadStr, "LsiLogic SAS BIOS"); else AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType)); @@ -5247,9 +5569,9 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC /* Register save state handlers. */ rc = PDMDevHlpSSMRegisterEx(pDevIns, LSILOGIC_SAVED_STATE_VERSION, sizeof(*pThis), NULL, - NULL, lsilogicLiveExec, NULL, - NULL, lsilogicSaveExec, NULL, - NULL, lsilogicLoadExec, lsilogicLoadDone); + NULL, lsilogicR3LiveExec, NULL, + NULL, lsilogicR3SaveExec, NULL, + NULL, lsilogicR3LoadExec, lsilogicR3LoadDone); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot register save state handlers")); @@ -5259,14 +5581,14 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC * Register the info item. */ char szTmp[128]; - RTStrPrintf(szTmp, sizeof(szTmp), "%s%d", pDevIns->pReg->szName, pDevIns->iInstance); + RTStrPrintf(szTmp, sizeof(szTmp), "%s%u", pDevIns->pReg->szName, pDevIns->iInstance); PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI ? "LsiLogic SPI info." - : "LsiLogic SAS info.", lsilogicInfo); + : "LsiLogic SAS info.", lsilogicR3Info); /* Perform hard reset. */ - rc = lsilogicHardReset(pThis); + rc = lsilogicR3HardReset(pThis); AssertRC(rc); return rc; @@ -5297,31 +5619,31 @@ const PDMDEVREG g_DeviceLsiLogicSCSI = /* cbInstance */ sizeof(LSILOGICSCSI), /* pfnConstruct */ - lsilogicConstruct, + lsilogicR3Construct, /* pfnDestruct */ - lsilogicDestruct, + lsilogicR3Destruct, /* pfnRelocate */ - lsilogicRelocate, - /* pfnIOCtl */ + lsilogicR3Relocate, + /* pfnMemSetup */ NULL, /* pfnPowerOn */ NULL, /* pfnReset */ - lsilogicReset, + lsilogicR3Reset, /* pfnSuspend */ - lsilogicSuspend, + lsilogicR3Suspend, /* pfnResume */ - lsilogicResume, + lsilogicR3Resume, /* pfnAttach */ - lsilogicAttach, + lsilogicR3Attach, /* pfnDetach */ - lsilogicDetach, + lsilogicR3Detach, /* pfnQueryInterface. */ NULL, /* pfnInitComplete */ NULL, /* pfnPowerOff */ - lsilogicPowerOff, + lsilogicR3PowerOff, /* pfnSoftReset */ NULL, /* u32VersionEnd */ @@ -5354,31 +5676,31 @@ const PDMDEVREG g_DeviceLsiLogicSAS = /* cbInstance */ sizeof(LSILOGICSCSI), /* pfnConstruct */ - lsilogicConstruct, + lsilogicR3Construct, /* pfnDestruct */ - lsilogicDestruct, + lsilogicR3Destruct, /* pfnRelocate */ - lsilogicRelocate, - /* pfnIOCtl */ + lsilogicR3Relocate, + /* pfnMemSetup */ NULL, /* pfnPowerOn */ NULL, /* pfnReset */ - lsilogicReset, + lsilogicR3Reset, /* pfnSuspend */ - lsilogicSuspend, + lsilogicR3Suspend, /* pfnResume */ - lsilogicResume, + lsilogicR3Resume, /* pfnAttach */ - lsilogicAttach, + lsilogicR3Attach, /* pfnDetach */ - lsilogicDetach, + lsilogicR3Detach, /* pfnQueryInterface. */ NULL, /* pfnInitComplete */ NULL, /* pfnPowerOff */ - lsilogicPowerOff, + lsilogicR3PowerOff, /* pfnSoftReset */ NULL, /* u32VersionEnd */ diff --git a/src/VBox/Devices/Storage/DevLsiLogicSCSI.h b/src/VBox/Devices/Storage/DevLsiLogicSCSI.h index d04a4e83..207d8e85 100644 --- a/src/VBox/Devices/Storage/DevLsiLogicSCSI.h +++ b/src/VBox/Devices/Storage/DevLsiLogicSCSI.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2009 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -24,8 +24,8 @@ * not be in the ISA range (below 400h) to avoid conflicts with ISA device * probing. Addresses in the 300h-340h range should be especially avoided. */ -#define LSILOGIC_BIOS_IO_PORT 0x340 -#define LSILOGIC_SAS_BIOS_IO_PORT 0x350 +#define LSILOGIC_BIOS_IO_PORT 0x434 +#define LSILOGIC_SAS_BIOS_IO_PORT 0x438 #define LSILOGICSCSI_REQUEST_QUEUE_DEPTH_DEFAULT 256 #define LSILOGICSCSI_REPLY_QUEUE_DEPTH_DEFAULT 256 @@ -1150,7 +1150,71 @@ typedef union MptReplyUnion MptFWDownloadReply FWDownload; MptFWUploadReply FWUpload; } MptReplyUnion, *PMptReplyUnion; +AssertCompileSize(MptReplyUnion, 60); +/** + * Firmware image header. + */ +#pragma pack(1) +typedef struct FwImageHdr +{ + /** ARM branch instruction. */ + uint32_t u32ArmBrInsn; + /** Signature part 1. */ + uint32_t u32Signature1; + /** Signature part 2. */ + uint32_t u32Signature2; + /** Signature part 3. */ + uint32_t u32Signature3; + /** Another ARM branch instruction. */ + uint32_t u32ArmBrInsn2; + /** Yet another ARM branch instruction. */ + uint32_t u32ArmBrInsn3; + /** Reserved. */ + uint32_t u32Reserved; + /** Checksum of the image. */ + uint32_t u32Checksum; + /** Vendor ID. */ + uint16_t u16VendorId; + /** Product ID. */ + uint16_t u16ProductId; + /** Firmware version. */ + uint32_t u32FwVersion; + /** Firmware sequencer Code version. */ + uint32_t u32SeqCodeVersion; + /** Image size in bytes including the header. */ + uint32_t u32ImageSize; + /** Offset of the first extended image header. */ + uint32_t u32NextImageHeaderOffset; + /** Start address of the image in IOC memory. */ + uint32_t u32LoadStartAddress; + /** Absolute start address of the Iop ARM. */ + uint32_t u32IopResetVectorValue; + /** Address of the IopResetVector register. */ + uint32_t u32IopResetVectorRegAddr; + /** Marker value for what utility. */ + uint32_t u32VersionNameWhat; + /** ASCII string of version. */ + uint8_t aszVersionName[256]; + /** Marker value for what utility. */ + uint32_t u32VendorNameWhat; + /** ASCII string of vendor name. */ + uint8_t aszVendorName[256]; +} FwImageHdr, *PFwImageHdr; +#pragma pack() +AssertCompileSize(FwImageHdr, 584); + +/** First part of the signature. */ +#define LSILOGIC_FWIMGHDR_SIGNATURE1 UINT32_C(0x5aeaa55a) +/** Second part of the signature. */ +#define LSILOGIC_FWIMGHDR_SIGNATURE2 UINT32_C(0xa55aeaa5) +/** Third part of the signature. */ +#define LSILOGIC_FWIMGHDR_SIGNATURE3 UINT32_C(0x5aa55aea) +/** Load address of the firmware image to watch for, + * seen used by Solaris 9. When this value is written to the + * diagnostic address register we know a firmware image is downloaded. + */ +#define LSILOGIC_FWIMGHDR_LOAD_ADDRESS UINT32_C(0x21ff5e00) /** * Configuration Page attributes. @@ -3465,6 +3529,32 @@ typedef enum LSILOGICWHOINIT /** + * Doorbell state. + */ +typedef enum LSILOGICDOORBELLSTATE +{ + /** Invalid value. */ + LSILOGICDOORBELLSTATE_INVALID = 0, + /** Doorbell not in use. */ + LSILOGICDOORBELLSTATE_NOT_IN_USE, + /** Reply frame removal, transfer number of entries, low 16bits. */ + LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_LOW, + /** Reply frame removal, transfer number of entries, high 16bits. */ + LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_HIGH, + /** Reply frame removal, remove next free frame, low part. */ + LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW, + /** Reply frame removal, remove next free frame, high part. */ + LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_HIGH, + /** Function handshake. */ + LSILOGICDOORBELLSTATE_FN_HANDSHAKE, + /** 32bit hack. */ + LSILOGICDOORBELLSTATE_32BIT_HACK = 0x7fffffff +} LSILOGICDOORBELLSTATE; +/** Pointer to a doorbell state. */ +typedef LSILOGICDOORBELLSTATE *PLSILOGICDOORBELLSTATE; + + +/** * IOC status codes. */ #define LSILOGIC_IOCSTATUS_SUCCESS 0x0000 @@ -3490,7 +3580,7 @@ typedef enum LSILOGICWHOINIT */ #define LSILOGIC_REG_DOORBELL 0x00 # define LSILOGIC_REG_DOORBELL_SET_STATE(enmState) (((enmState) & 0x0f) << 28) -# define LSILOGIC_REG_DOORBELL_SET_USED(fUsed) (((fUsed) ? 1 : 0) << 27) +# define LSILOGIC_REG_DOORBELL_SET_USED(enmDoorbell) (((enmDoorbell != LSILOGICDOORBELLSTATE_NOT_IN_USE) ? 1 : 0) << 27) # define LSILOGIC_REG_DOORBELL_SET_WHOINIT(enmWhoInit) (((enmWhoInit) & 0x07) << 24) # define LSILOGIC_REG_DOORBELL_SET_FAULT_CODE(u16Code) (u16Code) # define LSILOGIC_REG_DOORBELL_GET_FUNCTION(x) (((x) & 0xff000000) >> 24) diff --git a/src/VBox/Devices/Storage/DrvBlock.cpp b/src/VBox/Devices/Storage/DrvBlock.cpp index 8353d805..30dfb9ee 100644 --- a/src/VBox/Devices/Storage/DrvBlock.cpp +++ b/src/VBox/Devices/Storage/DrvBlock.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -264,6 +264,23 @@ static DECLCALLBACK(uint64_t) drvblockGetSize(PPDMIBLOCK pInterface) } +/** @copydoc PDMIBLOCK::pfnGetSize */ +static DECLCALLBACK(uint32_t) drvblockGetSectorSize(PPDMIBLOCK pInterface) +{ + PDRVBLOCK pThis = PDMIBLOCK_2_DRVBLOCK(pInterface); + + /* + * Check the state. + */ + if (!pThis->pDrvMedia) + return 0; + + uint32_t cb = pThis->pDrvMedia->pfnGetSectorSize(pThis->pDrvMedia); + LogFlowFunc(("returns %u\n", cb)); + return cb; +} + + /** @copydoc PDMIBLOCK::pfnGetType */ static DECLCALLBACK(PDMBLOCKTYPE) drvblockGetType(PPDMIBLOCK pInterface) { @@ -774,6 +791,7 @@ static DECLCALLBACK(void) drvblockDetach(PPDMDRVINS pDrvIns, uint32_t fFlags) NOREF(fFlags); } + /** * Reset notification. * @@ -787,6 +805,34 @@ static DECLCALLBACK(void) drvblockReset(PPDMDRVINS pDrvIns) pThis->fLocked = false; } + +/** + * Translates a PDMBLOCKTYPE value into a string. + * + * @returns Read only string. + * @param enmType The type value. + */ +static const char *drvblockGetTypeName(PDMBLOCKTYPE enmType) +{ + switch (enmType) + { + case PDMBLOCKTYPE_ERROR: return "ERROR"; + case PDMBLOCKTYPE_FLOPPY_360: return "FLOPPY_360"; + case PDMBLOCKTYPE_FLOPPY_720: return "FLOPPY_720"; + case PDMBLOCKTYPE_FLOPPY_1_20: return "FLOPPY_1_20"; + case PDMBLOCKTYPE_FLOPPY_1_44: return "FLOPPY_1_44"; + case PDMBLOCKTYPE_FLOPPY_2_88: return "FLOPPY_2_88"; + case PDMBLOCKTYPE_FLOPPY_FAKE_15_6: return "FLOPPY_FAKE_15_6"; + case PDMBLOCKTYPE_FLOPPY_FAKE_63_5: return "FLOPPY_FAKE_63_5"; + case PDMBLOCKTYPE_CDROM: return "CDROM"; + case PDMBLOCKTYPE_DVD: return "DVD"; + case PDMBLOCKTYPE_HARD_DISK: return "HARD_DISK"; + default: return "Unknown"; + + } +} + + /** * Construct a block driver instance. * @@ -823,6 +869,7 @@ static DECLCALLBACK(int) drvblockConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, u pThis->IBlock.pfnMerge = drvblockMerge; pThis->IBlock.pfnIsReadOnly = drvblockIsReadOnly; pThis->IBlock.pfnGetSize = drvblockGetSize; + pThis->IBlock.pfnGetSectorSize = drvblockGetSectorSize; pThis->IBlock.pfnGetType = drvblockGetType; pThis->IBlock.pfnGetUuid = drvblockGetUuid; @@ -889,6 +936,10 @@ static DECLCALLBACK(int) drvblockConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, u pThis->enmType = PDMBLOCKTYPE_FLOPPY_720; else if (!strcmp(psz, "Floppy 360")) pThis->enmType = PDMBLOCKTYPE_FLOPPY_360; + else if (!strcmp(psz, "Floppy 15.6")) + pThis->enmType = PDMBLOCKTYPE_FLOPPY_FAKE_15_6; + else if (!strcmp(psz, "Floppy 63.5")) + pThis->enmType = PDMBLOCKTYPE_FLOPPY_FAKE_63_5; else { PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_BLOCK_UNKNOWN_TYPE, RT_SRC_POS, @@ -1009,6 +1060,56 @@ static DECLCALLBACK(int) drvblockConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, u pThis->pDrvMedia->pfnGetUuid(pThis->pDrvMedia, &pThis->Uuid); } + /* + * Automatically upgrade the floppy drive if the specified one is too + * small to represent the whole boot time image. (We cannot do this later + * since the BIOS (and others) gets the info via CMOS.) + * + * This trick should make 2.88 images as well as the fake 15.6 and 63.5 MB + * images despite the hardcoded default 1.44 drive. + */ + if ( PDMBLOCKTYPE_IS_FLOPPY(pThis->enmType) + && pThis->pDrvMedia) + { + uint64_t const cbFloppyImg = pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia); + PDMBLOCKTYPE const enmCfgType = pThis->enmType; + switch (enmCfgType) + { + default: + AssertFailed(); + case PDMBLOCKTYPE_FLOPPY_360: + if (cbFloppyImg > 40 * 2 * 9 * 512) + pThis->enmType = PDMBLOCKTYPE_FLOPPY_360; + /* fall thru */ + case PDMBLOCKTYPE_FLOPPY_720: + if (cbFloppyImg > 80 * 2 * 14 * 512) + pThis->enmType = PDMBLOCKTYPE_FLOPPY_1_20; + /* fall thru */ + case PDMBLOCKTYPE_FLOPPY_1_20: + if (cbFloppyImg > 80 * 2 * 20 * 512) + pThis->enmType = PDMBLOCKTYPE_FLOPPY_1_44; + /* fall thru */ + case PDMBLOCKTYPE_FLOPPY_1_44: + if (cbFloppyImg > 80 * 2 * 24 * 512) + pThis->enmType = PDMBLOCKTYPE_FLOPPY_2_88; + /* fall thru */ + case PDMBLOCKTYPE_FLOPPY_2_88: + if (cbFloppyImg > 80 * 2 * 48 * 512) + pThis->enmType = PDMBLOCKTYPE_FLOPPY_FAKE_15_6; + /* fall thru */ + case PDMBLOCKTYPE_FLOPPY_FAKE_15_6: + if (cbFloppyImg > 255 * 2 * 63 * 512) + pThis->enmType = PDMBLOCKTYPE_FLOPPY_FAKE_63_5; + case PDMBLOCKTYPE_FLOPPY_FAKE_63_5: + if (cbFloppyImg > 255 * 2 * 255 * 512) + LogRel(("Warning: Floppy image is larger that 63.5 MB! (%llu bytes)\n", cbFloppyImg)); + break; + } + if (pThis->enmType != enmCfgType) + LogRel(("Automatically upgraded floppy drive from %s to %s to better support the %u byte image\n", + drvblockGetTypeName(enmCfgType), drvblockGetTypeName(pThis->enmType), cbFloppyImg)); + } + return VINF_SUCCESS; } diff --git a/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp b/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp index fa63d050..76419351 100644 --- a/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp +++ b/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 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; @@ -160,6 +160,8 @@ typedef struct DRVDISKINTEGRITY /** Flag whether consistency checks are enabled. */ bool fCheckConsistency; + /** Flag whether the RAM disk was prepopulated. */ + bool fPrepopulateRamDisk; /** AVL tree containing the disk blocks to check. */ PAVLRFOFFTREE pTreeSegments; @@ -437,6 +439,17 @@ static int drvdiskintReadVerify(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsign cbRange = cbLeft; else cbRange = pSeg->Core.Key - offCurr; + + if (pThis->fPrepopulateRamDisk) + { + /* No segment means everything should be 0 for this part. */ + if (!RTSgBufIsZero(&SgBuf, cbRange)) + { + RTMsgError("Corrupted disk at offset %llu (expected everything to be 0)!\n", + offCurr); + RTAssertDebugBreak(); + } + } } else { @@ -522,7 +535,7 @@ static int drvdiskintDiscardRecords(PDRVDISKINTEGRITY pThis, PCRTRANGE paRanges, { size_t cbPreLeft, cbPostLeft; - cbRange = RT_MIN(cbRange, pSeg->Core.KeyLast - offStart + 1); + cbRange = RT_MIN(cbLeft, pSeg->Core.KeyLast - offStart + 1); cbPreLeft = offStart - pSeg->Core.Key; cbPostLeft = pSeg->cbSeg - cbRange - cbPreLeft; @@ -1241,7 +1254,8 @@ static DECLCALLBACK(int) drvdiskintConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, "ExpireIntervalMs\0" "CheckDoubleCompletions\0" "HistorySize\0" - "IoLog\0")) + "IoLog\0" + "PrepopulateRamDisk\0")) return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES; rc = CFGMR3QueryBoolDef(pCfg, "CheckConsistency", &pThis->fCheckConsistency, false); @@ -1256,6 +1270,8 @@ static DECLCALLBACK(int) drvdiskintConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, AssertRC(rc); rc = CFGMR3QueryU32Def(pCfg, "HistorySize", &pThis->cEntries, 512); AssertRC(rc); + rc = CFGMR3QueryBoolDef(pCfg, "PrepopulateRamDisk", &pThis->fPrepopulateRamDisk, false); + AssertRC(rc); char *pszIoLogFilename = NULL; rc = CFGMR3QueryStringAlloc(pCfg, "IoLog", &pszIoLogFilename); @@ -1363,6 +1379,50 @@ static DECLCALLBACK(int) drvdiskintConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, MMR3HeapFree(pszIoLogFilename); } + /* Read in all data before the start if requested. */ + if (pThis->fPrepopulateRamDisk) + { + uint64_t cbDisk = 0; + + LogRel(("DiskIntegrity: Prepopulating RAM disk, this will take some time...\n")); + + cbDisk = pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia); + if (cbDisk) + { + uint64_t off = 0; + uint8_t abBuffer[_64K]; + RTSGSEG Seg; + + Seg.pvSeg = abBuffer; + + while (cbDisk) + { + size_t cbThisRead = RT_MIN(cbDisk, sizeof(abBuffer)); + + rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, abBuffer, cbThisRead); + if (RT_FAILURE(rc)) + break; + + if (ASMBitFirstSet(abBuffer, sizeof(abBuffer) * 8) != -1) + { + Seg.cbSeg = cbThisRead; + rc = drvdiskintWriteRecord(pThis, &Seg, 1, + off, cbThisRead); + if (RT_FAILURE(rc)) + break; + } + + cbDisk -= cbThisRead; + off += cbThisRead; + } + + LogRel(("DiskIntegrity: Prepopulating RAM disk finished with %Rrc\n", rc)); + } + else + return PDMDRV_SET_ERROR(pDrvIns, VERR_INTERNAL_ERROR, + N_("DiskIntegrity: Error querying the media size below")); + } + return rc; } diff --git a/src/VBox/Devices/Storage/DrvHostBase.cpp b/src/VBox/Devices/Storage/DrvHostBase.cpp index cf2cbdd1..5c588f20 100644 --- a/src/VBox/Devices/Storage/DrvHostBase.cpp +++ b/src/VBox/Devices/Storage/DrvHostBase.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2014 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -872,7 +872,7 @@ static int drvHostBaseOpen(PDRVHOSTBASE pThis, PRTFILE pFileDevice, bool fReadOn else { strcpy(szName1, *pszVendor ? pszVendor : pszProduct); - RTStrPrintf(szName2, sizeof(szName2), "%s %s (#%u)", *pszVendor ? pszVendor : pszProduct, i); + RTStrPrintf(szName2, sizeof(szName2), "%s (#%u)", *pszVendor ? pszVendor : pszProduct, i); } } else @@ -1486,7 +1486,7 @@ static LRESULT CALLBACK DeviceChangeWindowProc(HWND hwnd, UINT uMsg, WPARAM wPar Log2(("DeviceChangeWindowProc: hwnd=%08x uMsg=%08x\n", hwnd, uMsg)); if (uMsg == WM_DESTROY) { - PDRVHOSTBASE pThis = (PDRVHOSTBASE)GetWindowLong(hwnd, GWLP_USERDATA); + PDRVHOSTBASE pThis = (PDRVHOSTBASE)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (pThis) ASMAtomicXchgSize(&pThis->hwndDeviceChange, NULL); PostQuitMessage(0); @@ -1560,7 +1560,7 @@ static DECLCALLBACK(int) drvHostBaseMediaThread(RTTHREAD ThreadSelf, void *pvUse memset(&s_classDeviceChange, 0, sizeof(s_classDeviceChange)); s_classDeviceChange.lpfnWndProc = DeviceChangeWindowProc; s_classDeviceChange.lpszClassName = "VBOX_DeviceChangeClass"; - s_classDeviceChange.hInstance = GetModuleHandle("VBOXDD.DLL"); + s_classDeviceChange.hInstance = GetModuleHandle("VBoxDD.dll"); Assert(s_classDeviceChange.hInstance); s_hAtomDeviceChange = RegisterClassA(&s_classDeviceChange); Assert(s_hAtomDeviceChange); @@ -1877,6 +1877,7 @@ int DRVHostBaseInitData(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, PDMBLOCKTYPE enmType #endif pThis->enmType = enmType; //pThis->cErrors = 0; + pThis->fAttachFailError = true; /* It's an error until we've read the config. */ pThis->pfnGetMediaSize = drvHostBaseGetMediaSize; @@ -2078,6 +2079,8 @@ int DRVHostBaseInitFinish(PDRVHOSTBASE pThis) case PDMBLOCKTYPE_FLOPPY_1_20: case PDMBLOCKTYPE_FLOPPY_1_44: case PDMBLOCKTYPE_FLOPPY_2_88: + case PDMBLOCKTYPE_FLOPPY_FAKE_15_6: + case PDMBLOCKTYPE_FLOPPY_FAKE_63_5: if (uDriveType != DRIVE_REMOVABLE) { AssertMsgFailed(("Configuration error: '%s' is not a floppy (type=%d)\n", diff --git a/src/VBox/Devices/Storage/DrvHostBase.h b/src/VBox/Devices/Storage/DrvHostBase.h index c999fc05..04a560b5 100644 --- a/src/VBox/Devices/Storage/DrvHostBase.h +++ b/src/VBox/Devices/Storage/DrvHostBase.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-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; diff --git a/src/VBox/Devices/Storage/DrvHostDVD.cpp b/src/VBox/Devices/Storage/DrvHostDVD.cpp index 3d5858ad..8ed2c1b1 100644 --- a/src/VBox/Devices/Storage/DrvHostDVD.cpp +++ b/src/VBox/Devices/Storage/DrvHostDVD.cpp @@ -34,9 +34,6 @@ # include <mach/mach_error.h> # define USE_MEDIA_POLLING -#elif defined(RT_OS_L4) -/* nothing (yet). */ - #elif defined RT_OS_LINUX # include <sys/ioctl.h> # include <linux/version.h> @@ -437,10 +434,6 @@ static int drvHostDvdSendCmd(PPDMIBLOCK pInterface, const uint8_t *pbCmd, /* sense information set */ rc = VERR_DEV_IO_ERROR; -#elif defined(RT_OS_L4) - /* Not really ported to L4 yet. */ - rc = VERR_INTERNAL_ERROR; - #elif defined(RT_OS_LINUX) int direction; struct cdrom_generic_command cgc; @@ -758,62 +751,64 @@ static DECLCALLBACK(int) drvHostDvdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, LogFlow(("drvHostDvdConstruct: iInstance=%d\n", pDrvIns->iInstance)); /* - * Validate configuration. - */ - if (!CFGMR3AreValuesValid(pCfg, "Path\0Interval\0Locked\0BIOSVisible\0AttachFailError\0Passthrough\0")) - return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES; - - - /* * Init instance data. */ int rc = DRVHostBaseInitData(pDrvIns, pCfg, PDMBLOCKTYPE_DVD); if (RT_SUCCESS(rc)) { /* - * Override stuff. + * Validate configuration. */ + if (CFGMR3AreValuesValid(pCfg, "Path\0Interval\0Locked\0BIOSVisible\0AttachFailError\0Passthrough\0")) + { + /* + * Override stuff. + */ #ifdef RT_OS_LINUX - pThis->pbDoubleBuffer = (uint8_t *)RTMemAlloc(SCSI_MAX_BUFFER_SIZE); - if (!pThis->pbDoubleBuffer) - return VERR_NO_MEMORY; + pThis->pbDoubleBuffer = (uint8_t *)RTMemAlloc(SCSI_MAX_BUFFER_SIZE); + if (!pThis->pbDoubleBuffer) + return VERR_NO_MEMORY; #endif -#ifndef RT_OS_L4 /* Passthrough is not supported on L4 yet */ - bool fPassthrough; - rc = CFGMR3QueryBool(pCfg, "Passthrough", &fPassthrough); - if (RT_SUCCESS(rc) && fPassthrough) - { - pThis->IBlock.pfnSendCmd = drvHostDvdSendCmd; - /* Passthrough requires opening the device in R/W mode. */ - pThis->fReadOnlyConfig = false; -# ifdef VBOX_WITH_SUID_WRAPPER /* Solaris setuid for Passthrough mode. */ - rc = solarisCheckUserAuth(); - if (RT_FAILURE(rc)) + bool fPassthrough; + rc = CFGMR3QueryBool(pCfg, "Passthrough", &fPassthrough); + if (RT_SUCCESS(rc) && fPassthrough) { - Log(("DVD: solarisCheckUserAuth failed. Permission denied!\n")); - return rc; + pThis->IBlock.pfnSendCmd = drvHostDvdSendCmd; + /* Passthrough requires opening the device in R/W mode. */ + pThis->fReadOnlyConfig = false; +#ifdef VBOX_WITH_SUID_WRAPPER /* Solaris setuid for Passthrough mode. */ + rc = solarisCheckUserAuth(); + if (RT_FAILURE(rc)) + { + Log(("DVD: solarisCheckUserAuth failed. Permission denied!\n")); + return rc; + } +#endif /* VBOX_WITH_SUID_WRAPPER */ } -# endif /* VBOX_WITH_SUID_WRAPPER */ - } -#endif /* !RT_OS_L4 */ - pThis->IMount.pfnUnmount = drvHostDvdUnmount; - pThis->pfnDoLock = drvHostDvdDoLock; + pThis->IMount.pfnUnmount = drvHostDvdUnmount; + pThis->pfnDoLock = drvHostDvdDoLock; #ifdef USE_MEDIA_POLLING - if (!fPassthrough) - pThis->pfnPoll = drvHostDvdPoll; - else - pThis->pfnPoll = NULL; + if (!fPassthrough) + pThis->pfnPoll = drvHostDvdPoll; + else + pThis->pfnPoll = NULL; #endif #ifdef RT_OS_LINUX - pThis->pfnGetMediaSize = drvHostDvdGetMediaSize; + pThis->pfnGetMediaSize = drvHostDvdGetMediaSize; #endif - /* - * 2nd init part. - */ - rc = DRVHostBaseInitFinish(pThis); + /* + * 2nd init part. + */ + rc = DRVHostBaseInitFinish(pThis); + } + else + { + pThis->fAttachFailError = true; + rc = VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES; + } } if (RT_FAILURE(rc)) { diff --git a/src/VBox/Devices/Storage/DrvHostFloppy.cpp b/src/VBox/Devices/Storage/DrvHostFloppy.cpp index a65185b6..ba53e807 100644 --- a/src/VBox/Devices/Storage/DrvHostFloppy.cpp +++ b/src/VBox/Devices/Storage/DrvHostFloppy.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -91,7 +91,7 @@ static DECLCALLBACK(int) drvHostFloppyGetMediaSize(PDRVHOSTBASE pThis, uint64_t dwLastError = GetLastError(); rc = RTErrConvertFromWin32(dwLastError); - Log(("DrvHostFloppy: IOCTL_DISK_GET_DRIVE_GEOMETRY(%s) failed, LastError=%d rc=%Rrc\n", + Log(("DrvHostFloppy: IOCTL_DISK_GET_DRIVE_GEOMETRY(%s) failed, LastError=%d rc=%Rrc\n", pThis->pszDevice, dwLastError, rc)); return rc; } @@ -184,32 +184,37 @@ static DECLCALLBACK(int) drvHostFloppyConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pC LogFlow(("drvHostFloppyConstruct: iInstance=%d\n", pDrvIns->iInstance)); /* - * Validate configuration. - */ - if (!CFGMR3AreValuesValid(pCfg, "Path\0ReadOnly\0Interval\0Locked\0BIOSVisible\0")) - return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES; - - /* * Init instance data. */ int rc = DRVHostBaseInitData(pDrvIns, pCfg, PDMBLOCKTYPE_FLOPPY_1_44); if (RT_SUCCESS(rc)) { /* - * Override stuff. + * Validate configuration. */ + if (CFGMR3AreValuesValid(pCfg, "Path\0ReadOnly\0Interval\0Locked\0BIOSVisible\0")) + { + /* + * Override stuff. + */ #ifdef RT_OS_WINDOWS - pThis->Base.pfnGetMediaSize = drvHostFloppyGetMediaSize; + pThis->Base.pfnGetMediaSize = drvHostFloppyGetMediaSize; #endif #ifdef RT_OS_LINUX - pThis->Base.pfnPoll = drvHostFloppyPoll; - pThis->Base.pfnGetMediaSize = drvHostFloppyGetMediaSize; + pThis->Base.pfnPoll = drvHostFloppyPoll; + pThis->Base.pfnGetMediaSize = drvHostFloppyGetMediaSize; #endif - /* - * 2nd init part. - */ - rc = DRVHostBaseInitFinish(&pThis->Base); + /* + * 2nd init part. + */ + rc = DRVHostBaseInitFinish(&pThis->Base); + } + else + { + pThis->Base.fAttachFailError = true; + rc = VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES; + } } if (RT_FAILURE(rc)) { diff --git a/src/VBox/Devices/Storage/DrvMediaISO.cpp b/src/VBox/Devices/Storage/DrvMediaISO.cpp index f5fc8d24..c18a9dd2 100644 --- a/src/VBox/Devices/Storage/DrvMediaISO.cpp +++ b/src/VBox/Devices/Storage/DrvMediaISO.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -196,8 +196,11 @@ static DECLCALLBACK(void) drvMediaISODestruct(PPDMDRVINS pDrvIns) LogFlow(("drvMediaISODestruct: '%s'\n", pThis->pszFilename)); PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); - RTFileClose(pThis->hFile); - pThis->hFile = NIL_RTFILE; + if (pThis->hFile != NIL_RTFILE) + { + RTFileClose(pThis->hFile); + pThis->hFile = NIL_RTFILE; + } if (pThis->pszFilename) { diff --git a/src/VBox/Devices/Storage/DrvRawImage.cpp b/src/VBox/Devices/Storage/DrvRawImage.cpp index 72d96bb9..75a2ffc8 100644 --- a/src/VBox/Devices/Storage/DrvRawImage.cpp +++ b/src/VBox/Devices/Storage/DrvRawImage.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -234,8 +234,11 @@ static DECLCALLBACK(void) drvRawImageDestruct(PPDMDRVINS pDrvIns) LogFlow(("drvRawImageDestruct: '%s'\n", pThis->pszFilename)); PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); - RTFileClose(pThis->hFile); - pThis->hFile = NIL_RTFILE; + if (pThis->hFile != NIL_RTFILE) + { + RTFileClose(pThis->hFile); + pThis->hFile = NIL_RTFILE; + } if (pThis->pszFilename) { diff --git a/src/VBox/Devices/Storage/DrvSCSI.cpp b/src/VBox/Devices/Storage/DrvSCSI.cpp index e7ff7068..1b0ce172 100644 --- a/src/VBox/Devices/Storage/DrvSCSI.cpp +++ b/src/VBox/Devices/Storage/DrvSCSI.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -70,10 +70,8 @@ typedef struct DRVSCSI PDMIBLOCKPORT IPort; /** The optional block async port interface. */ PDMIBLOCKASYNCPORT IPortAsync; -#if 0 /* these interfaces aren't implemented */ /** The mount notify interface. */ PDMIMOUNTNOTIFY IMountNotify; -#endif /** Fallback status LED state for this drive. * This is used in case the device doesn't has a LED interface. */ PDMLED Led; @@ -114,6 +112,8 @@ typedef struct DRVSCSI #define PDMISCSICONNECTOR_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, ISCSIConnector)) ) /** Converts a pointer to DRVSCSI::IPortAsync to a PDRVSCSI. */ #define PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPortAsync)) ) +/** Converts a pointer to DRVSCSI::IMountNotify to PDRVSCSI. */ +#define PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IMountNotify)) ) /** Converts a pointer to DRVSCSI::IPort to a PDRVSCSI. */ #define PDMIBLOCKPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPort)) ) @@ -245,6 +245,27 @@ static DECLCALLBACK(int) drvscsiGetSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, return VINF_SUCCESS; } + +static DECLCALLBACK(int) drvscsiGetSectorSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint32_t *pcbSectorSize) +{ + PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser; + + *pcbSectorSize = pThis->pDrvBlock->pfnGetSectorSize(pThis->pDrvBlock); + + return VINF_SUCCESS; +} +static DECLCALLBACK(int) drvscsiSetLock(VSCSILUN hVScsiLun, void *pvScsiLunUser, bool fLocked) +{ + PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser; + + if (fLocked) + pThis->pDrvMount->pfnLock(pThis->pDrvMount); + else + pThis->pDrvMount->pfnUnlock(pThis->pDrvMount); + + return VINF_SUCCESS; +} + static int drvscsiTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser, int rc) { PDRVSCSI pThis = PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface); @@ -635,9 +656,12 @@ static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const c PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface); PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->pDrvMount); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKBIOS, pThis->pDrvBlockBios); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pThis->IPort); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pThis->IMountNotify); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKASYNCPORT, &pThis->IPortAsync); return NULL; } @@ -652,6 +676,38 @@ static DECLCALLBACK(int) drvscsiQueryDeviceLocation(PPDMIBLOCKPORT pInterface, c } /** + * Called when media is mounted. + * + * @param pInterface Pointer to the interface structure containing the called function pointer. + */ +static DECLCALLBACK(void) drvscsiMountNotify(PPDMIMOUNTNOTIFY pInterface) +{ + PDRVSCSI pThis = PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface); + LogFlowFunc(("mounting LUN#%p\n", pThis->hVScsiLun)); + + /* Ignore the call if we're called while being attached. */ + if (!pThis->pDrvBlock) + return; + + /* Let the LUN know that a medium was mounted. */ + VSCSILunMountNotify(pThis->hVScsiLun); +} + +/** + * Called when media is unmounted + * + * @param pInterface Pointer to the interface structure containing the called function pointer. + */ +static DECLCALLBACK(void) drvscsiUnmountNotify(PPDMIMOUNTNOTIFY pInterface) +{ + PDRVSCSI pThis = PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface); + LogFlowFunc(("unmounting LUN#%p\n", pThis->hVScsiLun)); + + /* Let the LUN know that the medium was unmounted. */ + VSCSILunUnmountNotify(pThis->hVScsiLun); +} + +/** * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff. * * @param pDrvIns The driver instance. @@ -800,15 +856,21 @@ static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns) } /* Free the VSCSI device and LUN handle. */ - VSCSILUN hVScsiLun; - int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun); - AssertRC(rc); - - Assert(hVScsiLun == pThis->hVScsiLun); - rc = VSCSILunDestroy(hVScsiLun); - AssertRC(rc); - rc = VSCSIDeviceDestroy(pThis->hVScsiDevice); - AssertRC(rc); + if (pThis->hVScsiDevice) + { + VSCSILUN hVScsiLun; + int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun); + AssertRC(rc); + + Assert(hVScsiLun == pThis->hVScsiLun); + rc = VSCSILunDestroy(hVScsiLun); + AssertRC(rc); + rc = VSCSIDeviceDestroy(pThis->hVScsiDevice); + AssertRC(rc); + + pThis->hVScsiDevice = NULL; + pThis->hVScsiLun = NULL; + } } /** @@ -831,6 +893,8 @@ static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, ui pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface; + pThis->IMountNotify.pfnMountNotify = drvscsiMountNotify; + pThis->IMountNotify.pfnUnmountNotify = drvscsiUnmountNotify; pThis->IPort.pfnQueryDeviceLocation = drvscsiQueryDeviceLocation; pThis->IPortAsync.pfnTransferCompleteNotify = drvscsiTransferCompleteNotify; pThis->hQueueRequests = NIL_RTREQQUEUE; @@ -896,24 +960,60 @@ static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, ui pThis->pDrvBlockAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKASYNC); PDMBLOCKTYPE enmType = pThis->pDrvBlock->pfnGetType(pThis->pDrvBlock); - if (enmType != PDMBLOCKTYPE_HARD_DISK) + VSCSILUNTYPE enmLunType; + switch (enmType) + { + case PDMBLOCKTYPE_HARD_DISK: + enmLunType = VSCSILUNTYPE_SBC; + break; + case PDMBLOCKTYPE_CDROM: + case PDMBLOCKTYPE_DVD: + enmLunType = VSCSILUNTYPE_MMC; + break; + default: return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS, - N_("Only hard disks are currently supported as SCSI devices (enmType=%d)"), + N_("Only hard disks and CD/DVD-ROMs are currently supported as SCSI devices (enmType=%d)"), enmType); + } + if ( ( enmType == PDMBLOCKTYPE_DVD + || enmType == PDMBLOCKTYPE_CDROM) + && !pThis->pDrvMount) + { + AssertMsgFailed(("Internal error: cdrom without a mountable interface\n")); + return VERR_INTERNAL_ERROR; + } /* Create VSCSI device and LUN. */ - pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize; - pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue; - pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags; + pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize; + pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSectorSize = drvscsiGetSectorSize; + pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue; + pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags; + pThis->VScsiIoCallbacks.pfnVScsiLunMediumSetLock = drvscsiSetLock; rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis); AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n"), rc); - rc = VSCSILunCreate(&pThis->hVScsiLun, VSCSILUNTYPE_SBC, &pThis->VScsiIoCallbacks, + rc = VSCSILunCreate(&pThis->hVScsiLun, enmLunType, &pThis->VScsiIoCallbacks, pThis); AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n"), rc); rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0); AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc); + //@todo: This is a very hacky way of telling the LUN whether a medium was mounted. + // The mount/unmount interface doesn't work in a very sensible manner! + if (pThis->pDrvMount) + { + if (pThis->pDrvBlock->pfnGetSize(pThis->pDrvBlock)) + { + rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun); + AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc); + } + else + { + rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun); + AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc); + } + } + /* Register statistics counter. */ /** @todo aeichner: Find a way to put the instance number of the attached * controller device when we support more than one controller of the same type. @@ -1001,4 +1101,3 @@ const PDMDRVREG g_DrvSCSI = /* u32EndVersion */ PDM_DRVREG_VERSION }; - diff --git a/src/VBox/Devices/Storage/DrvSCSIHost.cpp b/src/VBox/Devices/Storage/DrvSCSIHost.cpp index ac4b32c5..f2ddd25d 100644 --- a/src/VBox/Devices/Storage/DrvSCSIHost.cpp +++ b/src/VBox/Devices/Storage/DrvSCSIHost.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Devices/Storage/DrvVD.cpp b/src/VBox/Devices/Storage/DrvVD.cpp index d1decd9f..f4ba4967 100644 --- a/src/VBox/Devices/Storage/DrvVD.cpp +++ b/src/VBox/Devices/Storage/DrvVD.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 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; @@ -40,9 +40,13 @@ #ifdef VBOX_WITH_INIP /* All lwip header files are not C++ safe. So hack around this. */ RT_C_DECLS_BEGIN +#include <lwip/opt.h> #include <lwip/inet.h> #include <lwip/tcp.h> #include <lwip/sockets.h> +# ifdef VBOX_WITH_NEW_LWIP +# include <lwip/inet6.h> +# endif RT_C_DECLS_END #endif /* VBOX_WITH_INIP */ @@ -308,7 +312,7 @@ static DECLCALLBACK(void) drvvdAsyncTaskCompleted(PPDMDRVINS pDrvIns, void *pvTe { Assert(!pvUser); pStorageBackend->rcReqLast = rcReq; - pStorageBackend->fSyncIoPending = false; + ASMAtomicWriteBool(&pStorageBackend->fSyncIoPending, false); RTSemEventSignal(pStorageBackend->EventSem); } else @@ -345,9 +349,9 @@ static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation, drvvdAsyncTaskCompleted, pStorageBackend, "AsyncTaskCompleted"); if (RT_SUCCESS(rc)) { - uint32_t fFlags = (fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ - ? PDMACEP_FILE_FLAGS_READ_ONLY - : 0; + uint32_t fFlags = (fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ + ? PDMACEP_FILE_FLAGS_READ_ONLY + : 0; if (pThis->fShareable) { Assert((fOpen & RTFILE_O_DENY_MASK) == RTFILE_O_DENY_NONE); @@ -368,6 +372,8 @@ static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation, if (RT_SUCCESS(rc)) { + LogFlow(("drvvdAsyncIOOpen: Successfully opened '%s'; fOpen=%#x pStorage=%p\n", + pszLocation, fOpen, pStorageBackend)); *ppStorage = pStorageBackend; return VINF_SUCCESS; } @@ -408,8 +414,8 @@ static DECLCALLBACK(int) drvvdAsyncIOReadSync(void *pvUser, void *pStorage, uint RTSGSEG DataSeg; PPDMASYNCCOMPLETIONTASK pTask; - Assert(!pStorageBackend->fSyncIoPending); - ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true); + bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true); + Assert(!fOld); DataSeg.cbSeg = cbRead; DataSeg.pvSeg = pvBuf; @@ -440,8 +446,8 @@ static DECLCALLBACK(int) drvvdAsyncIOWriteSync(void *pvUser, void *pStorage, uin RTSGSEG DataSeg; PPDMASYNCCOMPLETIONTASK pTask; - Assert(!pStorageBackend->fSyncIoPending); - ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true); + bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true); + Assert(!fOld); DataSeg.cbSeg = cbWrite; DataSeg.pvSeg = (void *)pvBuf; @@ -472,8 +478,8 @@ static DECLCALLBACK(int) drvvdAsyncIOFlushSync(void *pvUser, void *pStorage) LogFlowFunc(("pvUser=%#p pStorage=%#p\n", pvUser, pStorage)); - Assert(!pStorageBackend->fSyncIoPending); - ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true); + bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true); + Assert(!fOld); int rc = PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, NULL, &pTask); if (RT_FAILURE(rc)) @@ -500,7 +506,7 @@ static DECLCALLBACK(int) drvvdAsyncIOReadAsync(void *pvUser, void *pStorage, uin PVBOXDISK pThis = (PVBOXDISK)pvUser; PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage; - int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, paSegments, cSegments, cbRead, + int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, paSegments, (unsigned)cSegments, cbRead, pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask); if (rc == VINF_AIO_TASK_PENDING) rc = VERR_VD_ASYNC_IO_IN_PROGRESS; @@ -516,7 +522,7 @@ static DECLCALLBACK(int) drvvdAsyncIOWriteAsync(void *pvUser, void *pStorage, ui PVBOXDISK pThis = (PVBOXDISK)pvUser; PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage; - int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, paSegments, cSegments, cbWrite, + int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, paSegments, (unsigned)cSegments, cbWrite, pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask); if (rc == VINF_AIO_TASK_PENDING) rc = VERR_VD_ASYNC_IO_IN_PROGRESS; @@ -615,10 +621,17 @@ static int drvvdCfgQuery(void *pvUser, const char *pszName, char *pszString, siz * VD TCP network stack interface implementation - INIP case * *******************************************************************************/ +/** + * vvl: this structure duplicate meaning of sockaddr, + * perhaps it'd be better to get rid of it. + */ typedef union INIPSOCKADDRUNION { struct sockaddr Addr; struct sockaddr_in Ipv4; +#ifdef VBOX_WITH_NEW_LWIP + struct sockaddr_in6 Ipv6; +#endif } INIPSOCKADDRUNION; typedef struct INIPSOCKET @@ -665,6 +678,11 @@ static DECLCALLBACK(int) drvvdINIPClientConnect(VDSOCKET Sock, const char *pszAd { int rc = VINF_SUCCESS; PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock; + int iInetFamily = PF_INET; + struct in_addr ip; +#ifdef VBOX_WITH_NEW_LWIP + ip6_addr_t ip6; +#endif /* Check whether lwIP is set up in this VM instance. */ if (!DevINIPConfigured()) @@ -674,21 +692,43 @@ static DECLCALLBACK(int) drvvdINIPClientConnect(VDSOCKET Sock, const char *pszAd } /* Resolve hostname. As there is no standard resolver for lwIP yet, * just accept numeric IP addresses for now. */ - struct in_addr ip; - if (!lwip_inet_aton(pszAddress, &ip)) +#ifdef VBOX_WITH_NEW_LWIP + if (inet6_aton(pszAddress, &ip6)) + iInetFamily = PF_INET6; + else /* concatination with if */ +#endif + if (!lwip_inet_aton(pszAddress, &ip)) { LogRelFunc(("cannot resolve IP %s\n", pszAddress)); return VERR_NET_HOST_UNREACHABLE; } /* Create socket and connect. */ - int iSock = lwip_socket(PF_INET, SOCK_STREAM, 0); + int iSock = lwip_socket(iInetFamily, SOCK_STREAM, 0); if (iSock != -1) { - struct sockaddr_in InAddr = {0}; - InAddr.sin_family = AF_INET; - InAddr.sin_port = htons(uPort); - InAddr.sin_addr = ip; - if (!lwip_connect(iSock, (struct sockaddr *)&InAddr, sizeof(InAddr))) + struct sockaddr *pSockAddr = NULL; + if (iInetFamily == PF_INET) + { + struct sockaddr_in InAddr = {0}; + InAddr.sin_family = AF_INET; + InAddr.sin_port = htons(uPort); + InAddr.sin_addr = ip; + InAddr.sin_len = sizeof(InAddr); + pSockAddr = (struct sockaddr *)&InAddr; + } +#ifdef VBOX_WITH_NEW_LWIP + else + { + struct sockaddr_in6 In6Addr = {0}; + In6Addr.sin6_family = AF_INET6; + In6Addr.sin6_port = htons(uPort); + memcpy(&In6Addr.sin6_addr, &ip6, sizeof(ip6)); + In6Addr.sin6_len = sizeof(In6Addr); + pSockAddr = (struct sockaddr *)&In6Addr; + } +#endif + if ( pSockAddr + && !lwip_connect(iSock, pSockAddr, pSockAddr->sa_len)) { pSocketInt->hSock = iSock; return VINF_SUCCESS; @@ -882,6 +922,16 @@ static DECLCALLBACK(int) drvvdINIPGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAdd pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port); pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr; } +#ifdef VBOX_WITH_NEW_LWIP + else if ( cbAddr == sizeof(struct sockaddr_in6) + && u.Addr.sa_family == AF_INET6) + { + RT_ZERO(*pAddr); + pAddr->enmType = RTNETADDRTYPE_IPV6; + pAddr->uPort = RT_N2H_U16(u.Ipv6.sin6_port); + memcpy(&pAddr->uAddr.IPv6, &u.Ipv6.sin6_addr, sizeof(RTNETADDRIPV6)); + } +#endif else return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED; return VINF_SUCCESS; @@ -909,6 +959,16 @@ static DECLCALLBACK(int) drvvdINIPGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port); pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr; } +#ifdef VBOX_WITH_NEW_LWIP + else if ( cbAddr == sizeof(struct sockaddr_in6) + && u.Addr.sa_family == AF_INET6) + { + RT_ZERO(*pAddr); + pAddr->enmType = RTNETADDRTYPE_IPV6; + pAddr->uPort = RT_N2H_U16(u.Ipv6.sin6_port); + memcpy(&pAddr->uAddr.IPv6, &u.Ipv6.sin6_addr, sizeof(RTNETADDRIPV6)); + } +#endif else return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED; return VINF_SUCCESS; @@ -1474,7 +1534,7 @@ static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface, } if (RT_SUCCESS(rc)) - Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Rhxd\n", __FUNCTION__, + Log2(("%s: off=%#llx pvBuf=%p cbRead=%d\n%.*Rhxd\n", __FUNCTION__, off, pvBuf, cbRead, cbRead, pvBuf)); LogFlowFunc(("returns %Rrc\n", rc)); return rc; @@ -1487,7 +1547,7 @@ static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface, { LogFlowFunc(("off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite)); PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface); - Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Rhxd\n", __FUNCTION__, + Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d\n%.*Rhxd\n", __FUNCTION__, off, pvBuf, cbWrite, cbWrite, pvBuf)); /* Invalidate any buffer if boot acceleration is enabled. */ @@ -1557,6 +1617,16 @@ static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface) return cb; } +/** @copydoc PDMIMEDIA::pfnGetSectorSize */ +static DECLCALLBACK(uint32_t) drvvdGetSectorSize(PPDMIMEDIA pInterface) +{ + LogFlowFunc(("\n")); + PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface); + uint32_t cb = VDGetSectorSize(pThis->pDisk, VD_LAST_IMAGE); + LogFlowFunc(("returns %u\n", cb)); + return cb; +} + /** @copydoc PDMIMEDIA::pfnIsReadOnly */ static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface) { @@ -2010,9 +2080,9 @@ static DECLCALLBACK(void) drvvdReset(PPDMDRVINS pDrvIns) */ static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns) { + PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK); LogFlowFunc(("\n")); - PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); RTSEMFASTMUTEX mutex; ASMAtomicXchgHandle(&pThis->MergeCompleteMutex, NIL_RTSEMFASTMUTEX, &mutex); @@ -2029,13 +2099,13 @@ static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns) AssertRC(rc); } - if (VALID_PTR(pThis->pBlkCache)) + if (RT_VALID_PTR(pThis->pBlkCache)) { PDMR3BlkCacheRelease(pThis->pBlkCache); pThis->pBlkCache = NULL; } - if (VALID_PTR(pThis->pDisk)) + if (RT_VALID_PTR(pThis->pDisk)) { VDDestroy(pThis->pDisk); pThis->pDisk = NULL; @@ -2049,7 +2119,10 @@ static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns) pThis->MergeLock = NIL_RTSEMRW; } if (pThis->pbData) + { RTMemFree(pThis->pbData); + pThis->pbData = NULL; + } if (pThis->pszBwGroup) { MMR3HeapFree(pThis->pszBwGroup); @@ -2062,11 +2135,10 @@ static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns) * * @copydoc FNPDMDRVCONSTRUCT */ -static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, - PCFGMNODE pCfg, - uint32_t fFlags) +static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags) { LogFlowFunc(("\n")); + PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns); PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK); int rc = VINF_SUCCESS; char *pszName = NULL; /**< The path of the disk image file. */ @@ -2076,7 +2148,6 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, bool fReadOnly; /**< True if the media is read-only. */ bool fMaybeReadOnly; /**< True if the media may or may not be read-only. */ bool fHonorZeroWrites; /**< True if zero blocks should be written. */ - PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns); /* * Init the static parts. @@ -2089,6 +2160,7 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, pThis->fShareable = false; pThis->fMergePending = false; pThis->MergeCompleteMutex = NIL_RTSEMFASTMUTEX; + pThis->MergeLock = NIL_RTSEMRW; pThis->uMergeSource = VD_LAST_IMAGE; pThis->uMergeTarget = VD_LAST_IMAGE; @@ -2098,6 +2170,7 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, pThis->IMedia.pfnFlush = drvvdFlush; pThis->IMedia.pfnMerge = drvvdMerge; pThis->IMedia.pfnGetSize = drvvdGetSize; + pThis->IMedia.pfnGetSectorSize = drvvdGetSectorSize; pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly; pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry; pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry; @@ -2141,6 +2214,7 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, bool fUseBlockCache = false; bool fDiscard = false; bool fInformAboutZeroBlocks = false; + bool fSkipConsistencyChecks = false; unsigned iLevel = 0; PCFGMNODE pCurNode = pCfg; VDTYPE enmType = VDTYPE_HDD; @@ -2158,7 +2232,8 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, "ReadOnly\0MaybeReadOnly\0TempReadOnly\0Shareable\0HonorZeroWrites\0" "HostIPStack\0UseNewIo\0BootAcceleration\0BootAccelerationBuffer\0" "SetupMerge\0MergeSource\0MergeTarget\0BwGroup\0Type\0BlockCache\0" - "CachePath\0CacheFormat\0Discard\0InformAboutZeroBlocks\0"); + "CachePath\0CacheFormat\0Discard\0InformAboutZeroBlocks\0" + "SkipConsistencyChecks\0"); } else { @@ -2300,6 +2375,13 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, N_("DrvVD: Configuration error: Querying \"InformAboutZeroBlocks\" as boolean failed")); break; } + rc = CFGMR3QueryBoolDef(pCurNode, "SkipConsistencyChecks", &fSkipConsistencyChecks, true); + if (RT_FAILURE(rc)) + { + rc = PDMDRV_SET_ERROR(pDrvIns, rc, + N_("DrvVD: Configuration error: Querying \"SKipConsistencyChecks\" as boolean failed")); + break; + } char *psz; rc = CFGMR3QueryStringAlloc(pCfg, "Type", &psz); @@ -2405,6 +2487,8 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, if (pThis->pDrvMediaAsyncPort && fUseNewIo) pThis->fAsyncIOSupported = true; + uint64_t tsStart = RTTimeNanoTS(); + unsigned iImageIdx = 0; while (pCurNode && RT_SUCCESS(rc)) { @@ -2602,6 +2686,9 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, uOpenFlags |= VD_OPEN_FLAGS_DISCARD; if (fInformAboutZeroBlocks) uOpenFlags |= VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS; + if ( (uOpenFlags & VD_OPEN_FLAGS_READONLY) + && fSkipConsistencyChecks) + uOpenFlags |= VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS; /* Try to open backend in async I/O mode first. */ rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage); @@ -2662,9 +2749,11 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, pCurNode = CFGMR3GetParent(pCurNode); } + LogRel(("VD: Opening the disk took %lld ns\n", RTTimeNanoTS() - tsStart)); + /* Open the cache image if set. */ if ( RT_SUCCESS(rc) - && VALID_PTR(pszCachePath)) + && RT_VALID_PTR(pszCachePath)) { /* Insert the custom I/O interface only if we're told to use new IO. * Since the I/O interface is per image we could make this more @@ -2697,9 +2786,9 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, rc = PDMDRV_SET_ERROR(pDrvIns, rc, N_("DrvVD: Could not open cache image")); } - if (VALID_PTR(pszCachePath)) + if (RT_VALID_PTR(pszCachePath)) MMR3HeapFree(pszCachePath); - if (VALID_PTR(pszCacheFormat)) + if (RT_VALID_PTR(pszCacheFormat)) MMR3HeapFree(pszCacheFormat); if ( RT_SUCCESS(rc) @@ -2791,9 +2880,9 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, if (RT_FAILURE(rc)) { - if (VALID_PTR(pszName)) + if (RT_VALID_PTR(pszName)) MMR3HeapFree(pszName); - if (VALID_PTR(pszFormat)) + if (RT_VALID_PTR(pszFormat)) MMR3HeapFree(pszFormat); /* drvvdDestruct does the rest. */ } diff --git a/src/VBox/Devices/Storage/PIIX3ATABmDma.h b/src/VBox/Devices/Storage/PIIX3ATABmDma.h index 652a2d1f..af7059a4 100644 --- a/src/VBox/Devices/Storage/PIIX3ATABmDma.h +++ b/src/VBox/Devices/Storage/PIIX3ATABmDma.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Devices/Storage/UsbMsd.cpp b/src/VBox/Devices/Storage/UsbMsd.cpp index d41b929a..52e9de8f 100644 --- a/src/VBox/Devices/Storage/UsbMsd.cpp +++ b/src/VBox/Devices/Storage/UsbMsd.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007-2010 Oracle Corporation + * Copyright (C) 2007-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -38,15 +38,24 @@ /** @name USB MSD string IDs * @{ */ #define USBMSD_STR_ID_MANUFACTURER 1 -#define USBMSD_STR_ID_PRODUCT 2 +#define USBMSD_STR_ID_PRODUCT_HD 2 +#define USBMSD_STR_ID_PRODUCT_CDROM 3 /** @} */ +/** @name USB MSD vendor and product IDs + * @{ */ +#define VBOX_USB_VENDOR 0x80EE +#define USBMSD_PID_HD 0x0030 +#define USBMSD_PID_CD 0x0031 +/** @} */ /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ + /** - * USB Command block wrapper (MSD/SCSI). + * USB MSD Command Block Wrapper or CBW. The command block + * itself (CBWCB) contains protocol-specific data (here SCSI). */ #pragma pack(1) typedef struct USBCBW @@ -71,7 +80,7 @@ typedef USBCBW *PUSBCBW; typedef const USBCBW *PCUSBCBW; /** - * USB Command Status Wrapper (MSD/SCSI). + * USB MSD Command Status Wrapper or CSW. */ #pragma pack(1) typedef struct USBCSW @@ -254,8 +263,9 @@ typedef USBMSD *PUSBMSD; *******************************************************************************/ static const PDMUSBDESCCACHESTRING g_aUsbMsdStrings_en_US[] = { - { USBMSD_STR_ID_MANUFACTURER, "VirtualBox" }, - { USBMSD_STR_ID_PRODUCT, "VirtualBox MSD" }, + { USBMSD_STR_ID_MANUFACTURER, "VirtualBox" }, + { USBMSD_STR_ID_PRODUCT_HD, "USB Harddisk" }, + { USBMSD_STR_ID_PRODUCT_CDROM, "USB CD-ROM" } }; static const PDMUSBDESCCACHELANG g_aUsbMsdLanguages[] = @@ -263,7 +273,7 @@ static const PDMUSBDESCCACHELANG g_aUsbMsdLanguages[] = { 0x0409, RT_ELEMENTS(g_aUsbMsdStrings_en_US), g_aUsbMsdStrings_en_US } }; -static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescs[2] = +static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescsFS[2] = { { { @@ -271,8 +281,8 @@ static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescs[2] = /* .bDescriptorType = */ VUSB_DT_ENDPOINT, /* .bEndpointAddress = */ 0x81 /* ep=1, in */, /* .bmAttributes = */ 2 /* bulk */, - /* .wMaxPacketSize = */ 0x200 /* or 64? */, - /* .bInterval = */ 0xff, + /* .wMaxPacketSize = */ 64 /* maximum possible */, + /* .bInterval = */ 0 /* not applicable for bulk EP */ }, /* .pvMore = */ NULL, /* .pvClass = */ NULL, @@ -284,8 +294,8 @@ static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescs[2] = /* .bDescriptorType = */ VUSB_DT_ENDPOINT, /* .bEndpointAddress = */ 0x02 /* ep=2, out */, /* .bmAttributes = */ 2 /* bulk */, - /* .wMaxPacketSize = */ 0x200 /* or 64? */, - /* .bInterval = */ 0xff, + /* .wMaxPacketSize = */ 64 /* maximum possible */, + /* .bInterval = */ 0 /* not applicable for bulk EP */ }, /* .pvMore = */ NULL, /* .pvClass = */ NULL, @@ -293,7 +303,58 @@ static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescs[2] = } }; -static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDesc = +static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescsHS[2] = +{ + { + { + /* .bLength = */ sizeof(VUSBDESCENDPOINT), + /* .bDescriptorType = */ VUSB_DT_ENDPOINT, + /* .bEndpointAddress = */ 0x81 /* ep=1, in */, + /* .bmAttributes = */ 2 /* bulk */, + /* .wMaxPacketSize = */ 512 /* HS bulk packet size */, + /* .bInterval = */ 0 /* no NAKs */ + }, + /* .pvMore = */ NULL, + /* .pvClass = */ NULL, + /* .cbClass = */ 0 + }, + { + { + /* .bLength = */ sizeof(VUSBDESCENDPOINT), + /* .bDescriptorType = */ VUSB_DT_ENDPOINT, + /* .bEndpointAddress = */ 0x02 /* ep=2, out */, + /* .bmAttributes = */ 2 /* bulk */, + /* .wMaxPacketSize = */ 512 /* HS bulk packet size */, + /* .bInterval = */ 0 /* no NAKs */ + }, + /* .pvMore = */ NULL, + /* .pvClass = */ NULL, + /* .cbClass = */ 0 + } +}; + +static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDescFS = +{ + { + /* .bLength = */ sizeof(VUSBDESCINTERFACE), + /* .bDescriptorType = */ VUSB_DT_INTERFACE, + /* .bInterfaceNumber = */ 0, + /* .bAlternateSetting = */ 0, + /* .bNumEndpoints = */ 2, + /* .bInterfaceClass = */ 8 /* Mass Storage */, + /* .bInterfaceSubClass = */ 6 /* SCSI transparent command set */, + /* .bInterfaceProtocol = */ 0x50 /* Bulk-Only Transport */, + /* .iInterface = */ 0 + }, + /* .pvMore = */ NULL, + /* .pvClass = */ NULL, + /* .cbClass = */ 0, + &g_aUsbMsdEndpointDescsFS[0], + /* .pIAD = */ NULL, + /* .cbIAD = */ 0 +}; + +static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDescHS = { { /* .bLength = */ sizeof(VUSBDESCINTERFACE), @@ -309,52 +370,100 @@ static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDesc = /* .pvMore = */ NULL, /* .pvClass = */ NULL, /* .cbClass = */ 0, - &g_aUsbMsdEndpointDescs[0] + &g_aUsbMsdEndpointDescsHS[0], + /* .pIAD = */ NULL, + /* .cbIAD = */ 0 }; -static const VUSBINTERFACE g_aUsbMsdInterfaces[2] = +static const VUSBINTERFACE g_aUsbMsdInterfacesFS[] = { - { &g_UsbMsdInterfaceDesc, /* .cSettings = */ 1 }, + { &g_UsbMsdInterfaceDescFS, /* .cSettings = */ 1 }, }; -static const VUSBDESCCONFIGEX g_UsbMsdConfigDesc = +static const VUSBINTERFACE g_aUsbMsdInterfacesHS[] = +{ + { &g_UsbMsdInterfaceDescHS, /* .cSettings = */ 1 }, +}; + +static const VUSBDESCCONFIGEX g_UsbMsdConfigDescFS = { { /* .bLength = */ sizeof(VUSBDESCCONFIG), /* .bDescriptorType = */ VUSB_DT_CONFIG, /* .wTotalLength = */ 0 /* recalculated on read */, - /* .bNumInterfaces = */ RT_ELEMENTS(g_aUsbMsdInterfaces), + /* .bNumInterfaces = */ RT_ELEMENTS(g_aUsbMsdInterfacesFS), /* .bConfigurationValue =*/ 1, /* .iConfiguration = */ 0, /* .bmAttributes = */ RT_BIT(7), /* .MaxPower = */ 50 /* 100mA */ }, - NULL, - &g_aUsbMsdInterfaces[0] + NULL, /* pvMore */ + &g_aUsbMsdInterfacesFS[0], + NULL /* pvOriginal */ +}; + +static const VUSBDESCCONFIGEX g_UsbMsdConfigDescHS = +{ + { + /* .bLength = */ sizeof(VUSBDESCCONFIG), + /* .bDescriptorType = */ VUSB_DT_CONFIG, + /* .wTotalLength = */ 0 /* recalculated on read */, + /* .bNumInterfaces = */ RT_ELEMENTS(g_aUsbMsdInterfacesHS), + /* .bConfigurationValue =*/ 1, + /* .iConfiguration = */ 0, + /* .bmAttributes = */ RT_BIT(7), + /* .MaxPower = */ 50 /* 100mA */ + }, + NULL, /* pvMore */ + &g_aUsbMsdInterfacesHS[0], + NULL /* pvOriginal */ }; static const VUSBDESCDEVICE g_UsbMsdDeviceDesc = { /* .bLength = */ sizeof(g_UsbMsdDeviceDesc), /* .bDescriptorType = */ VUSB_DT_DEVICE, - /* .bcdUsb = */ 0x200, + /* .bcdUsb = */ 0x200, /* USB 2.0 */ /* .bDeviceClass = */ 0 /* Class specified in the interface desc. */, /* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */, - /* .bDeviceProtocol = */ 0x50 /* Protocol specified in the interface desc. */, + /* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */, /* .bMaxPacketSize0 = */ 64, - /* .idVendor = */ 0x4200, - /* .idProduct = */ 0x0042, - /* .bcdDevice = */ 0x0100, + /* .idVendor = */ VBOX_USB_VENDOR, + /* .idProduct = */ USBMSD_PID_HD, + /* .bcdDevice = */ 0x0100, /* 1.0 */ /* .iManufacturer = */ USBMSD_STR_ID_MANUFACTURER, - /* .iProduct = */ USBMSD_STR_ID_PRODUCT, + /* .iProduct = */ USBMSD_STR_ID_PRODUCT_HD, /* .iSerialNumber = */ 0, /* .bNumConfigurations = */ 1 }; -static const PDMUSBDESCCACHE g_UsbMsdDescCache = +static const VUSBDEVICEQUALIFIER g_UsbMsdDeviceQualifier = +{ + /* .bLength = */ sizeof(g_UsbMsdDeviceQualifier), + /* .bDescriptorType = */ VUSB_DT_DEVICE_QUALIFIER, + /* .bcdUsb = */ 0x200, /* USB 2.0 */ + /* .bDeviceClass = */ 0 /* Class specified in the interface desc. */, + /* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */, + /* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */, + /* .bMaxPacketSize0 = */ 64, + /* .bNumConfigurations = */ 1, + /* .bReserved = */ 0 +}; + +static const PDMUSBDESCCACHE g_UsbMsdDescCacheFS = { /* .pDevice = */ &g_UsbMsdDeviceDesc, - /* .paConfigs = */ &g_UsbMsdConfigDesc, + /* .paConfigs = */ &g_UsbMsdConfigDescFS, + /* .paLanguages = */ g_aUsbMsdLanguages, + /* .cLanguages = */ RT_ELEMENTS(g_aUsbMsdLanguages), + /* .fUseCachedDescriptors = */ true, + /* .fUseCachedStringsDescriptors = */ true +}; + +static const PDMUSBDESCCACHE g_UsbMsdDescCacheHS = +{ + /* .pDevice = */ &g_UsbMsdDeviceDesc, + /* .paConfigs = */ &g_UsbMsdConfigDescHS, /* .paLanguages = */ g_aUsbMsdLanguages, /* .cLanguages = */ RT_ELEMENTS(g_aUsbMsdLanguages), /* .fUseCachedDescriptors = */ true, @@ -635,7 +744,7 @@ static int usbMsdCompleteOk(PUSBMSD pThis, PVUSBURB pUrb, size_t cbData) Log(("usbMsdCompleteOk/#%u: pUrb=%p:%s cbData=%#zx\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, cbData)); pUrb->enmStatus = VUSBSTATUS_OK; - pUrb->cbData = cbData; + pUrb->cbData = (uint32_t)cbData; usbMsdLinkDone(pThis, pUrb); return VINF_SUCCESS; @@ -664,7 +773,7 @@ static int usbMsdResetWorker(PUSBMSD pThis, PVUSBURB pUrb, bool fSetConfig) if ( pReq && pReq->enmState == USBMSDREQSTATE_EXECUTING) { - /* Don't try deal with the set config variant nor multiple build-only + /* Don't try to deal with the set config variant nor multiple build-only mass storage resets. */ if (pThis->pResetUrb && (pUrb || fSetConfig)) { @@ -784,7 +893,7 @@ static DECLCALLBACK(int) usbMsdLun0ScsiRequestCompleted(PPDMISCSIPORT pInterface if (!pUrb) break; - /* Process it as the normal way. */ + /* Process it the normal way. */ usbMsdHandleBulkDevToHost(pThis, &pThis->aEps[1], pUrb); } } @@ -913,13 +1022,13 @@ static int usbMsdHandleScsiReqestSense(PUSBMSD pThis, PUSBMSDREQ pReq, PCUSBCBW /* validation */ if ((pCbw->bmCBWFlags & USBCBW_DIR_MASK) != USBCBW_DIR_IN) return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INVALID_MESSAGE, 0, "direction"); - if (pCbw->bCBWCBLength != 6) + if (pCbw->bCBWCBLength < 6) return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INVALID_MESSAGE, 0, "length"); if ((pCbw->CBWCB[1] >> 5) != pCbw->bCBWLun) return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0, "lun"); if (pCbw->bCBWLun != 0) return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INVALID_MESSAGE, 0, "lun0"); - if ((pCbw->CBWCB[4] < 6) != pCbw->bCBWLun) + if (pCbw->CBWCB[4] < 6) return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0, "out length"); /* If the previous command succeeded successfully, whip up some sense data. */ @@ -1000,7 +1109,7 @@ static bool usbMsdIsValidCommand(PUSBMSD pThis, PCUSBCBW pCbw, PVUSBURB pUrb) /** - * Handles request sent to the out-bound (to device) bulk pipe. + * Handle requests sent to the outbound (to device) bulk pipe. */ static int usbMsdHandleBulkHostToDev(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb) { @@ -1034,13 +1143,13 @@ static int usbMsdHandleBulkHostToDev(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb Log(("usbMsd: Bad CBW: cbData=%#x < min=%#x\n", pUrb->cbData, RT_UOFFSETOF(USBCBW, CBWCB[1]) )); return usbMsdCompleteStall(pThis, NULL, pUrb, "BAD CBW"); } - Log(("usbMsd: CBW: dCBWSignature=%#x dCBWTag=%#x dCBWDataTransferLength=%#x bmCBWFlags=%#x bCBWLun=%#x bCBWCBLength=%#x cbData=%#x fShortNotOk=%RTbool\n", - pCbw->dCBWSignature, pCbw->dCBWTag, pCbw->dCBWDataTransferLength, pCbw->bmCBWFlags, pCbw->bCBWLun, pCbw->bCBWCBLength, pUrb->cbData, pUrb->fShortNotOk)); if (pCbw->dCBWSignature != USBCBW_SIGNATURE) { Log(("usbMsd: CBW: Invalid dCBWSignature value: %#x\n", pCbw->dCBWSignature)); return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad CBW"); } + Log(("usbMsd: CBW: dCBWTag=%#x dCBWDataTransferLength=%#x bmCBWFlags=%#x bCBWLun=%#x bCBWCBLength=%#x cbData=%#x fShortNotOk=%RTbool\n", + pCbw->dCBWTag, pCbw->dCBWDataTransferLength, pCbw->bmCBWFlags, pCbw->bCBWLun, pCbw->bCBWCBLength, pUrb->cbData, pUrb->fShortNotOk)); if (pCbw->bmCBWFlags & ~USBCBW_DIR_MASK) { Log(("usbMsd: CBW: Bad bmCBWFlags value: %#x\n", pCbw->bmCBWFlags)); @@ -1120,7 +1229,7 @@ static int usbMsdHandleBulkHostToDev(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb } } - return usbMsdCompleteOk(pThis, pUrb, 0); + return usbMsdCompleteOk(pThis, pUrb, pUrb->cbData); } /* @@ -1148,7 +1257,8 @@ static int usbMsdHandleBulkHostToDev(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb return usbMsdCompleteStall(pThis, NULL, pUrb, "SCSI Submit #2"); } } - return usbMsdCompleteOk(pThis, pUrb, 0); +LogRel(("DATA_FROM_HOST: %d bytes\n", cbData)); + return usbMsdCompleteOk(pThis, pUrb, cbData); } /* @@ -1168,7 +1278,7 @@ static int usbMsdHandleBulkHostToDev(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb /** - * Handles request sent to the in-bound (to host) bulk pipe. + * Handle requests sent to the inbound (to host) bulk pipe. */ static int usbMsdHandleBulkDevToHost(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb) { @@ -1208,6 +1318,7 @@ static int usbMsdHandleBulkDevToHost(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb Log(("usbMsdHandleBulkDevToHost: Entering STATUS\n")); pReq->enmState = USBMSDREQSTATE_STATUS; } +LogRel(("DATA_TO_HOST: %d bytes\n", cbCopy)); return usbMsdCompleteOk(pThis, pUrb, cbCopy); } @@ -1216,10 +1327,9 @@ static int usbMsdHandleBulkDevToHost(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb */ case USBMSDREQSTATE_STATUS: { - /** @todo !fShortNotOk and CSW request? */ - if (pUrb->cbData != sizeof(USBCSW)) + if ((pUrb->cbData < sizeof(USBCSW)) || (pUrb->cbData > sizeof(USBCSW) && pUrb->fShortNotOk)) { - Log(("usbMsd: Unexpected status request size: %#x (expected %#x)\n", pUrb->cbData, sizeof(USBCSW))); + Log(("usbMsd: Unexpected status request size: %#x (expected %#x), fShortNotOK=%RTbool\n", pUrb->cbData, sizeof(USBCSW), pUrb->fShortNotOk)); return usbMsdCompleteStall(pThis, NULL, pUrb, "Invalid CSW size"); } @@ -1232,15 +1342,17 @@ static int usbMsdHandleBulkDevToHost(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb : pReq->iScsiReqStatus >= 0 ? USBCSW_STATUS_FAILED : USBCSW_STATUS_PHASE_ERROR; + /** @todo the following is not always accurate; VSCSI needs + * to implement residual counts properly! */ if ((pReq->Cbw.bmCBWFlags & USBCBW_DIR_MASK) == USBCBW_DIR_OUT) pCsw->dCSWDataResidue = pCsw->bCSWStatus == USBCSW_STATUS_OK ? pReq->Cbw.dCBWDataTransferLength - pReq->ScsiReq.cbScatterGather : pReq->Cbw.dCBWDataTransferLength; else pCsw->dCSWDataResidue = pCsw->bCSWStatus == USBCSW_STATUS_OK - ? pReq->ScsiReq.cbScatterGather - : 0; - Log(("usbMsdHandleBulkDevToHost: CSW: dCSWTag=%#x bCSWStatus=%d dCSWDataResidue=%#x\n", + ? 0 + : pReq->ScsiReq.cbScatterGather; + Log(("usbMsd: CSW: dCSWTag=%#x bCSWStatus=%d dCSWDataResidue=%#x\n", pCsw->dCSWTag, pCsw->bCSWStatus, pCsw->dCSWDataResidue)); Log(("usbMsdHandleBulkDevToHost: Entering READY\n")); @@ -1333,9 +1445,18 @@ static int usbMsdHandleDefaultPipe(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb) switch (pSetup->wValue >> 8) { + uint32_t cbCopy; + case VUSB_DT_STRING: Log(("usbMsd: GET_DESCRIPTOR DT_STRING wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex)); break; + case VUSB_DT_DEVICE_QUALIFIER: + Log(("usbMsd: GET_DESCRIPTOR DT_DEVICE_QUALIFIER wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex)); + /* Returned data is written after the setup message. */ + cbCopy = pUrb->cbData - sizeof(*pSetup); + cbCopy = RT_MIN(cbCopy, sizeof(g_UsbMsdDeviceQualifier)); + memcpy(&pUrb->abData[sizeof(*pSetup)], &g_UsbMsdDeviceQualifier, cbCopy); + return usbMsdCompleteOk(pThis, pUrb, cbCopy + sizeof(*pSetup)); default: Log(("usbMsd: GET_DESCRIPTOR, huh? wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex)); break; @@ -1484,7 +1605,10 @@ static DECLCALLBACK(PCPDMUSBDESCCACHE) usbMsdUsbGetDescriptorCache(PPDMUSBINS pU { PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD); LogFlow(("usbMsdUsbGetDescriptorCache/#%u:\n", pUsbIns->iInstance)); - return &g_UsbMsdDescCache; + if (pThis->pUsbIns->iUsbHubVersion & VUSB_STDVER_20) + return &g_UsbMsdDescCacheHS; + else + return &g_UsbMsdDescCacheFS; } @@ -1602,7 +1726,7 @@ const PDMUSBREG g_UsbMsd = /* pszDescription */ "USB Mass Storage Device, one LUN.", /* fFlags */ - 0, + PDM_USBREG_HIGHSPEED_CAPABLE, /* cMaxInstances */ ~0U, /* cbInstance */ 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); } } - diff --git a/src/VBox/Devices/Storage/VBoxSCSI.h b/src/VBox/Devices/Storage/VBoxSCSI.h index c30749a5..5d055698 100644 --- a/src/VBox/Devices/Storage/VBoxSCSI.h +++ b/src/VBox/Devices/Storage/VBoxSCSI.h @@ -1,12 +1,10 @@ /* $Id: VBoxSCSI.h $ */ /** @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-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -17,14 +15,14 @@ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ -/** - * This is a simple interface to access SCSI devices from the BIOS - * which is shared between the BusLogic and the LsiLogic - * SCSI host adapters to simplify the BIOS part. +/** @page pg_drv_scsi Simple SCSI interface for BIOS access. + * + * This is a simple interface to access SCSI devices from the BIOS which is + * shared between the BusLogic and the LsiLogic SCSI host adapters to simplify + * the BIOS part. * - * The BusLogic interface if available will be starting at port 0x330 - * and the LsiLogic starts at 0x340 and each will have a size of 3 ports. - * The ports are used as described below: + * The first interface (if available) will be starting at port 0x430 and + * each will occupy 4 ports. The ports are used as described below: * * +--------+--------+----------+ * | Offset | Access | Purpose | @@ -39,22 +37,29 @@ * +--------+--------+----------+ * | 2 | R/W | Detect | * +--------+--------+----------+ + * | 3 | Read | SCSI rc | + * +--------+--------+----------+ * | 3 | Write | Reset | * +--------+--------+----------+ * - * The register at port 0 receives the SCSI CDB issued from the driver when writing to it but - * before writing the actual CDB the first write gives the size of the CDB in bytes. + * The register at port 0 receives the SCSI CDB issued from the driver when + * writing to it but before writing the actual CDB the first write gives the + * size of the CDB in bytes. * - * Reading the port at offset 0 gives status information about the adapter. - * If the busy bit is set the adapter is processing a previous issued request if it is + * Reading the port at offset 0 gives status information about the adapter. If + * the busy bit is set the adapter is processing a previous issued request if it is * cleared the command finished and the adapter can process another request. - * The driver has to poll this bit because the adapter will not assert an IRQ for simplicity reasons. + * The driver has to poll this bit because the adapter will not assert an IRQ + * for simplicity reasons. * - * The register at offset 2 is to detect if a host adapter is available - * If the driver writes a value to this port and gets the same value after reading it + * The register at offset 2 is to detect if a host adapter is available. If the + * driver writes a value to this port and gets the same value after reading it * again the adapter is available. * - * This part has no R0 or GC components. + * Any write to the register at offset 3 causes the interface to be reset. A + * read returns the SCSI status code of the last operation. + * + * This part has no R0 or RC components. */ #ifndef ___Storage_VBoxSCSI_h @@ -65,14 +70,15 @@ *******************************************************************************/ //#define DEBUG #include <VBox/vmm/pdmdev.h> +#include <VBox/scsi.h> typedef enum VBOXSCSISTATE { VBOXSCSISTATE_NO_COMMAND = 0x00, VBOXSCSISTATE_READ_TXDIR = 0x01, - VBOXSCSISTATE_READ_CDB_SIZE = 0x02, - VBOXSCSISTATE_READ_BUFFER_SIZE_LOW = 0x03, - VBOXSCSISTATE_READ_BUFFER_SIZE_HIGH = 0x04, + VBOXSCSISTATE_READ_CDB_SIZE_BUFHI = 0x02, + VBOXSCSISTATE_READ_BUFFER_SIZE_LSB = 0x03, + VBOXSCSISTATE_READ_BUFFER_SIZE_MID = 0x04, VBOXSCSISTATE_READ_COMMAND = 0x05, VBOXSCSISTATE_COMMAND_READY = 0x06 } VBOXSCSISTATE; @@ -94,7 +100,7 @@ typedef struct VBOXSCSI /** The size of the CDB we are issuing. */ uint8_t cbCDB; /** The command to issue. */ - uint8_t aCDB[12]; + uint8_t abCDB[12]; /** Current position in the array. */ uint8_t iCDB; @@ -103,18 +109,24 @@ typedef struct VBOXSCSI #endif /** Pointer to the buffer holding the data. */ - R3PTRTYPE(uint8_t *) pBuf; + R3PTRTYPE(uint8_t *) pbBuf; /** Size of the buffer in bytes. */ uint32_t cbBuf; - /** Current position in the buffer. */ + /** Current position in the buffer (offBuf if you like). */ uint32_t iBuf; + /** The result code of last operation. */ + int32_t rcCompletion; +#if HC_ARCH_BITS == 64 + uint32_t Alignment1; +#endif /** Flag whether a request is pending. */ volatile bool fBusy; /** The state we are in when fetching a command from the BIOS. */ VBOXSCSISTATE enmState; } VBOXSCSI, *PVBOXSCSI; -#define VBOX_SCSI_BUSY RT_BIT(0) +#define VBOX_SCSI_BUSY RT_BIT(0) +#define VBOX_SCSI_ERROR RT_BIT(1) #ifdef IN_RING3 RT_C_DECLS_BEGIN @@ -122,13 +134,14 @@ int vboxscsiInitialize(PVBOXSCSI pVBoxSCSI); int vboxscsiReadRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint32_t *pu32Value); int vboxscsiWriteRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint8_t uVal); int vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, uint32_t *puTargetDevice); -int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest); +int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, int rcCompletion); void vboxscsiSetRequestRedo(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest); int vboxscsiWriteString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb); int vboxscsiReadString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb); RT_C_DECLS_END -#endif +#endif /* IN_RING3 */ + +#endif /* !___Storage_VBoxSCSI_h */ -#endif /* ___Storage_VBoxSCSI_h */ diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp b/src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp index 4f300716..75f1b491 100644 --- a/src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp +++ b/src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2011 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -101,7 +101,11 @@ static bool vscsiDeviceReqProcess(PVSCSIDEVICEINT pVScsiDevice, PVSCSIREQINT pVS } case SCSI_TEST_UNIT_READY: { - *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq); + if ( vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun) + && pVScsiDevice->papVScsiLun[pVScsiReq->iLun]->fReady) + *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq); + else + fProcessed = false; /* The LUN (if present) will provide details. */ break; } case SCSI_REQUEST_SENSE: @@ -111,6 +115,7 @@ static bool vscsiDeviceReqProcess(PVSCSIDEVICEINT pVScsiDevice, PVSCSIREQINT pVS *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); else *prcReq = vscsiReqSenseCmd(&pVScsiDevice->VScsiSense, pVScsiReq); + break; } default: fProcessed = false; @@ -343,4 +348,3 @@ VBOXDDU_DECL(int) VSCSIDeviceReqCreate(VSCSIDEVICE hVScsiDevice, PVSCSIREQ phVSc return VINF_SUCCESS; } - diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSIInternal.h b/src/VBox/Devices/Storage/VSCSI/VSCSIInternal.h index 4fd696b0..8e086573 100644 --- a/src/VBox/Devices/Storage/VSCSI/VSCSIInternal.h +++ b/src/VBox/Devices/Storage/VSCSI/VSCSIInternal.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2011 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -85,6 +85,10 @@ typedef struct VSCSILUNINT PVSCSILUNIOCALLBACKS pVScsiLunIoCallbacks; /** Pointer to the LUN type descriptor. */ PVSCSILUNDESC pVScsiLunDesc; + /** Flag indicating whether LUN is ready. */ + bool fReady; + /** Flag indicating media presence in LUN. */ + bool fMediaPresent; /** Flags of supported features. */ uint64_t fFeatures; /** I/O request processing data */ @@ -359,6 +363,34 @@ DECLINLINE(int) vscsiLunMediumGetSize(PVSCSILUNINT pVScsiLun, uint64_t *pcbSize) } /** + * Wrapper for the get medium sector size I/O callback. + * + * @returns VBox status code. + * @param pVScsiLun The LUN. + * @param pcbSectorSize Where to store the sector size on success. + */ +DECLINLINE(int) vscsiLunMediumGetSectorSize(PVSCSILUNINT pVScsiLun, uint32_t *pcbSectorSize) +{ + return pVScsiLun->pVScsiLunIoCallbacks->pfnVScsiLunMediumGetSectorSize(pVScsiLun, + pVScsiLun->pvVScsiLunUser, + pcbSectorSize); +} + +/** + * Wrapper for the get medium lock/unlock I/O callback. + * + * @returns VBox status code. + * @param pVScsiLun The LUN. + * @param bool The new medium lock state. + */ +DECLINLINE(int) vscsiLunMediumSetLock(PVSCSILUNINT pVScsiLun, bool fLocked) +{ + return pVScsiLun->pVScsiLunIoCallbacks->pfnVScsiLunMediumSetLock(pVScsiLun, + pVScsiLun->pvVScsiLunUser, + fLocked); +} + +/** * Wrapper for the I/O request enqueue I/O callback. * * @returns VBox status code. diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSILun.cpp b/src/VBox/Devices/Storage/VSCSI/VSCSILun.cpp index 9a2f1f7c..24462db0 100644 --- a/src/VBox/Devices/Storage/VSCSI/VSCSILun.cpp +++ b/src/VBox/Devices/Storage/VSCSI/VSCSILun.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -115,3 +115,45 @@ VBOXDDU_DECL(int) VSCSILunDestroy(VSCSILUN hVScsiLun) return VINF_SUCCESS; } +/** + * Notify virtual SCSI LUN of media being mounted. + * + * @returns VBox status code. + * @param hVScsiLun The virtual SCSI LUN + * mounting the medium. + */ +VBOXDDU_DECL(int) VSCSILunMountNotify(VSCSILUN hVScsiLun) +{ + PVSCSILUNINT pVScsiLun = (PVSCSILUNINT)hVScsiLun; + + LogFlowFunc(("hVScsiLun=%p\n", hVScsiLun)); + AssertPtrReturn(pVScsiLun, VERR_INVALID_HANDLE); + AssertReturn(vscsiIoReqOutstandingCountGet(pVScsiLun) == 0, VERR_VSCSI_LUN_BUSY); + + /* Mark the LUN as not ready so that LUN specific code can do its job. */ + pVScsiLun->fReady = false; + pVScsiLun->fMediaPresent = true; + + return VINF_SUCCESS; +} + +/** + * Notify virtual SCSI LUN of media being unmounted. + * + * @returns VBox status code. + * @param hVScsiLun The virtual SCSI LUN + * mounting the medium. + */ +VBOXDDU_DECL(int) VSCSILunUnmountNotify(VSCSILUN hVScsiLun) +{ + PVSCSILUNINT pVScsiLun = (PVSCSILUNINT)hVScsiLun; + + LogFlowFunc(("hVScsiLun=%p\n", hVScsiLun)); + AssertPtrReturn(pVScsiLun, VERR_INVALID_HANDLE); + AssertReturn(vscsiIoReqOutstandingCountGet(pVScsiLun) == 0, VERR_VSCSI_LUN_BUSY); + + pVScsiLun->fReady = false; + pVScsiLun->fMediaPresent = false; + + return VINF_SUCCESS; +} diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp b/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp index 6da3833b..10330cdf 100644 --- a/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp +++ b/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2011 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -44,6 +44,116 @@ typedef struct VSCSILUNMMC bool fLocked; } VSCSILUNMMC, *PVSCSILUNMMC; + +DECLINLINE(void) mmcLBA2MSF(uint8_t *pbBuf, uint32_t iLBA) +{ + iLBA += 150; + pbBuf[0] = (iLBA / 75) / 60; + pbBuf[1] = (iLBA / 75) % 60; + pbBuf[2] = iLBA % 75; +} + +DECLINLINE(uint32_t) mmcMSF2LBA(const uint8_t *pbBuf) +{ + return (pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2]; +} + + +/* Fabricate normal TOC information. */ +static int mmcReadTOCNormal(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF) +{ + PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun; + uint8_t aReply[32]; + uint8_t *pbBuf = aReply; + uint8_t *q; + uint8_t iStartTrack; + uint32_t cbSize; + + iStartTrack = pVScsiReq->pbCDB[6]; + if (iStartTrack > 1 && iStartTrack != 0xaa) + { + return vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); + } + q = pbBuf + 2; + *q++ = 1; /* first session */ + *q++ = 1; /* last session */ + if (iStartTrack <= 1) + { + *q++ = 0; /* reserved */ + *q++ = 0x14; /* ADR, CONTROL */ + *q++ = 1; /* track number */ + *q++ = 0; /* reserved */ + if (fMSF) + { + *q++ = 0; /* reserved */ + mmcLBA2MSF(q, 0); + q += 3; + } + else + { + /* sector 0 */ + vscsiH2BEU32(q, 0); + q += 4; + } + } + /* lead out track */ + *q++ = 0; /* reserved */ + *q++ = 0x14; /* ADR, CONTROL */ + *q++ = 0xaa; /* track number */ + *q++ = 0; /* reserved */ + if (fMSF) + { + *q++ = 0; /* reserved */ + mmcLBA2MSF(q, pVScsiLunMmc->cSectors); + q += 3; + } + else + { + vscsiH2BEU32(q, pVScsiLunMmc->cSectors); + q += 4; + } + cbSize = q - pbBuf; + Assert(cbSize <= sizeof(aReply)); + vscsiH2BEU16(pbBuf, cbSize - 2); + if (cbSize < cbMaxTransfer) + cbMaxTransfer = cbSize; + + RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, cbMaxTransfer); + + return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); +} + +/* Fabricate session information. */ +static int mmcReadTOCMulti(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF) +{ + PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun; + uint8_t aReply[32]; + uint8_t *pbBuf = aReply; + + /* multi session: only a single session defined */ + memset(pbBuf, 0, 12); + pbBuf[1] = 0x0a; + pbBuf[2] = 0x01; /* first complete session number */ + pbBuf[3] = 0x01; /* last complete session number */ + pbBuf[5] = 0x14; /* ADR, CONTROL */ + pbBuf[6] = 1; /* first track in last complete session */ + + if (fMSF) + { + pbBuf[8] = 0; /* reserved */ + mmcLBA2MSF(pbBuf + 8, 0); + } + else + { + /* sector 0 */ + vscsiH2BEU32(pbBuf + 8, 0); + } + + RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, 12); + + return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); +} + static int vscsiLunMmcInit(PVSCSILUNINT pVScsiLun) { PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun; @@ -73,9 +183,40 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq) uint32_t cSectorTransfer = 0; int rc = VINF_SUCCESS; int rcReq = SCSI_STATUS_OK; + unsigned uCmd = pVScsiReq->pbCDB[0]; - switch(pVScsiReq->pbCDB[0]) + /* + * GET CONFIGURATION, GET EVENT/STATUS NOTIFICATION, INQUIRY, and REQUEST SENSE commands + * operate even when a unit attention condition exists for initiator; every other command + * needs to report CHECK CONDITION in that case. + */ + if (!pVScsiLunMmc->Core.fReady && uCmd != SCSI_INQUIRY) { + /* + * A note on media changes: As long as a medium is not present, the unit remains in + * the 'not ready' state. Technically the unit becomes 'ready' soon after a medium + * is inserted; however, we internally keep the 'not ready' state until we've had + * a chance to report the UNIT ATTENTION status indicating a media change. + */ + if (pVScsiLunMmc->Core.fMediaPresent) + { + rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_UNIT_ATTENTION, + SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED, 0x00); + pVScsiLunMmc->Core.fReady = true; + } + else + rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY, + SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00); + } + else + { + switch (uCmd) + { + case SCSI_TEST_UNIT_READY: + Assert(!pVScsiLunMmc->Core.fReady); /* Only should get here if LUN isn't ready. */ + rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00); + break; + case SCSI_INQUIRY: { SCSIINQUIRYDATA ScsiInquiryReply; @@ -120,6 +261,7 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq) uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f; uint8_t aReply[24]; uint8_t *pu8ReplyPos; + bool fValid = false; memset(aReply, 0, sizeof(aReply)); aReply[0] = 4; /* Reply length 4. */ @@ -135,10 +277,18 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq) *pu8ReplyPos++ = 0x08; /* Page code. */ *pu8ReplyPos++ = 0x12; /* Size of the page. */ *pu8ReplyPos++ = 0x4; /* Write cache enabled. */ + fValid = true; + } else if (uModePage == 0) { + fValid = true; } - RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply)); - rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); + /* Querying unknown pages must fail. */ + if (fValid) { + RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply)); + rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); + } else { + rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); + } break; } case SCSI_MODE_SELECT_6: @@ -267,13 +417,37 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq) case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL: { pVScsiLunMmc->fLocked = pVScsiReq->pbCDB[4] & 1; + vscsiLunMediumSetLock(pVScsiLun, pVScsiLunMmc->fLocked); rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); break; } + case SCSI_READ_TOC_PMA_ATIP: + { + uint8_t format; + uint16_t cbMax; + bool fMSF; + + format = pVScsiReq->pbCDB[2] & 0x0f; + cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]); + fMSF = (pVScsiReq->pbCDB[1] >> 1) & 1; + switch (format) + { + case 0x00: + mmcReadTOCNormal(pVScsiLun, pVScsiReq, cbMax, fMSF); + break; + case 0x01: + mmcReadTOCMulti(pVScsiLun, pVScsiReq, cbMax, fMSF); + break; + default: + rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); + } + break; + } default: - //AssertMsgFailed(("Command %#x [%s] not implemented\n", pRequest->pbCDB[0], SCSICmdText(pRequest->pbCDB[0]))); + //AssertMsgFailed(("Command %#x [%s] not implemented\n", pVScsiReq->pbCDB[0], SCSICmdText(pVScsiReq->pbCDB[0]))); rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00); + } } if (enmTxDir != VSCSIIOREQTXDIR_INVALID) @@ -286,7 +460,7 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq) rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00); vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS); } - else if (!cSectorTransfer) + else if (!cSectorTransfer) { /* A 0 transfer length is not an error. */ rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSILunSbc.cpp b/src/VBox/Devices/Storage/VSCSI/VSCSILunSbc.cpp index 81b8ce25..d5a3e68f 100644 --- a/src/VBox/Devices/Storage/VSCSI/VSCSILunSbc.cpp +++ b/src/VBox/Devices/Storage/VSCSI/VSCSILunSbc.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2011 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -32,7 +32,7 @@ #include "VSCSIInternal.h" /** Maximum of amount of LBAs to unmap with one command. */ -#define VSCSI_UNMAP_LBAS_MAX ((10*_1M) / 512) +#define VSCSI_UNMAP_LBAS_MAX(a_cbSector) ((10*_1M) / a_cbSector) /** * SBC LUN instance @@ -41,6 +41,8 @@ typedef struct VSCSILUNSBC { /** Core LUN structure */ VSCSILUNINT Core; + /** Sector size of the medium. */ + uint32_t cbSector; /** Size of the virtual disk. */ uint64_t cSectors; /** VPD page pool. */ @@ -56,9 +58,13 @@ static int vscsiLunSbcInit(PVSCSILUNINT pVScsiLun) int rc = VINF_SUCCESS; int cVpdPages = 0; - rc = vscsiLunMediumGetSize(pVScsiLun, &cbDisk); + rc = vscsiLunMediumGetSectorSize(pVScsiLun, &pVScsiLunSbc->cbSector); if (RT_SUCCESS(rc)) - pVScsiLunSbc->cSectors = cbDisk / 512; /* Fixed sector size */ + { + rc = vscsiLunMediumGetSize(pVScsiLun, &cbDisk); + if (RT_SUCCESS(rc)) + pVScsiLunSbc->cSectors = cbDisk / pVScsiLunSbc->cbSector; + } if (RT_SUCCESS(rc)) rc = vscsiVpdPagePoolInit(&pVScsiLunSbc->VpdPagePool); @@ -99,7 +105,7 @@ static int vscsiLunSbcInit(PVSCSILUNINT pVScsiLun) pBlkPage->u32MaxTrfLength = 0; pBlkPage->u32OptTrfLength = 0; pBlkPage->u32MaxPreXdTrfLength = 0; - pBlkPage->u32MaxUnmapLbaCount = RT_H2BE_U32(VSCSI_UNMAP_LBAS_MAX); + pBlkPage->u32MaxUnmapLbaCount = RT_H2BE_U32(VSCSI_UNMAP_LBAS_MAX(pVScsiLunSbc->cbSector)); pBlkPage->u32MaxUnmapBlkDescCount = UINT32_C(0xffffffff); pBlkPage->u32OptUnmapGranularity = 0; pBlkPage->u32UnmapGranularityAlignment = 0; @@ -167,6 +173,9 @@ static int vscsiLunSbcInit(PVSCSILUNINT pVScsiLun) } } + /* For SBC LUNs, there will be no ready state transitions. */ + pVScsiLunSbc->Core.fReady = true; + return rc; } @@ -242,7 +251,7 @@ static int vscsiLunSbcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq) vscsiH2BEU32(aReply, UINT32_C(0xffffffff)); else vscsiH2BEU32(aReply, pVScsiLunSbc->cSectors - 1); - vscsiH2BEU32(&aReply[4], 512); + vscsiH2BEU32(&aReply[4], pVScsiLunSbc->cbSector); RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply)); rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); break; @@ -252,6 +261,7 @@ static int vscsiLunSbcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq) uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f; uint8_t aReply[24]; uint8_t *pu8ReplyPos; + bool fValid = false; memset(aReply, 0, sizeof(aReply)); aReply[0] = 4; /* Reply length 4. */ @@ -270,10 +280,47 @@ static int vscsiLunSbcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq) *pu8ReplyPos++ = 0x08; /* Page code. */ *pu8ReplyPos++ = 0x12; /* Size of the page. */ *pu8ReplyPos++ = 0x4; /* Write cache enabled. */ + fValid = true; + } else if (uModePage == 0) { + fValid = true; } - RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply)); - rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); + /* Querying unknown pages must fail. */ + if (fValid) { + RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply)); + rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); + } else { + rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); + } + break; + } + case SCSI_MODE_SELECT_6: + { + uint8_t abParms[12]; + size_t cbCopied; + size_t cbList = pVScsiReq->pbCDB[4]; + + /* Copy the parameters. */ + cbCopied = RTSgBufCopyToBuf(&pVScsiReq->SgBuf, &abParms[0], sizeof(abParms)); + + /* Handle short LOGICAL BLOCK LENGTH parameter. */ + if ( !(pVScsiReq->pbCDB[1] & 0x01) + && cbCopied == sizeof(abParms) + && cbList >= 12 + && abParms[3] == 8) + { + uint32_t cbBlock; + + cbBlock = vscsiBE2HU24(&abParms[4 + 5]); + Log2(("SBC: set LOGICAL BLOCK LENGTH to %u\n", cbBlock)); + if (cbBlock == 512) /* Fixed block size. */ + { + rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); + break; + } + } + /* Fail any other requests. */ + rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); break; } case SCSI_READ_6: @@ -445,7 +492,7 @@ static int vscsiLunSbcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq) && cbCopied == sizeof(abHdr) && cbList >= 8) { - size_t cBlkDesc = vscsiBE2HU16(&abHdr[2]) / 16; + uint32_t cBlkDesc = vscsiBE2HU16(&abHdr[2]) / 16; if (cBlkDesc) { diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSISense.cpp b/src/VBox/Devices/Storage/VSCSI/VSCSISense.cpp index c90cd673..673ee0b8 100644 --- a/src/VBox/Devices/Storage/VSCSI/VSCSISense.cpp +++ b/src/VBox/Devices/Storage/VSCSI/VSCSISense.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2011 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -24,6 +24,12 @@ void vscsiSenseInit(PVSCSISENSE pVScsiSense) { memset(pVScsiSense->abSenseBuf, 0, sizeof(pVScsiSense->abSenseBuf)); + + /* Fill in valid sense information (can't be just zeros). */ + pVScsiSense->abSenseBuf[0] = (1 << 7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED; /* Fixed format */ + pVScsiSense->abSenseBuf[2] = SCSI_SENSE_NONE; + pVScsiSense->abSenseBuf[7] = 10; + pVScsiSense->abSenseBuf[12] = SCSI_ASC_NONE; } int vscsiReqSenseOkSet(PVSCSISENSE pVScsiSense, PVSCSIREQINT pVScsiReq) diff --git a/src/VBox/Devices/Storage/swab.h b/src/VBox/Devices/Storage/swab.h index 66086fd4..32c80a2e 100644 --- a/src/VBox/Devices/Storage/swab.h +++ b/src/VBox/Devices/Storage/swab.h @@ -14,7 +14,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; |
