diff options
| author | Gianfranco Costamagna <costamagnagianfranco@yahoo.it> | 2014-03-10 10:14:28 +0100 |
|---|---|---|
| committer | Gianfranco Costamagna <costamagnagianfranco@yahoo.it> | 2014-03-10 10:14:28 +0100 |
| commit | 1e85aed889b772c2f2daa7a6d9e8bd967aa213d8 (patch) | |
| tree | c1f9c44de9363e60eb2bf3f2ddf7f6b3c0c574cb /src/VBox/Devices/Storage | |
| parent | a0f18d47861c16d7dc3a1b7b8f177a076b14051e (diff) | |
| download | virtualbox-1e85aed889b772c2f2daa7a6d9e8bd967aa213d8.tar.gz | |
Imported Upstream version 4.3.8-dfsg
Diffstat (limited to 'src/VBox/Devices/Storage')
| -rw-r--r-- | src/VBox/Devices/Storage/DevAHCI.cpp | 701 | ||||
| -rw-r--r-- | src/VBox/Devices/Storage/DevATA.cpp | 546 | ||||
| -rw-r--r-- | src/VBox/Devices/Storage/DrvSCSI.cpp | 1 |
3 files changed, 678 insertions, 570 deletions
diff --git a/src/VBox/Devices/Storage/DevAHCI.cpp b/src/VBox/Devices/Storage/DevAHCI.cpp index e05f7f46e..50b0ca8b2 100644 --- a/src/VBox/Devices/Storage/DevAHCI.cpp +++ b/src/VBox/Devices/Storage/DevAHCI.cpp @@ -445,8 +445,6 @@ 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; /** Flag whether the worker thread is sleeping. */ @@ -479,13 +477,15 @@ 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. * Holds the command slot of the command processed at the moment. */ volatile uint32_t u32CurrentCommandSlot; -#if HC_ARCH_BITS == 32 +#if HC_ARCH_BITS == 64 uint32_t u32Alignment2; #endif @@ -674,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 */ @@ -698,6 +700,10 @@ 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; @@ -1284,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, @@ -1319,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) @@ -1331,11 +1340,36 @@ 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 { @@ -1734,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 } @@ -1951,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; @@ -2391,6 +2439,8 @@ PDMBOTHCBDECL(int) ahciIdxDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Po 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 @@ -4112,41 +4162,41 @@ static int atapiReadDVDStructureSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_ switch (format) { case 0x0: /* Physical format information */ - { - int layer = pAhciReq->aATAPICmd[6]; - uint64_t total_sectors; + { + int layer = pAhciReq->aATAPICmd[6]; + uint64_t total_sectors; - if (layer != 0) - { - uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET; - break; - } + 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; - } + 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 */ + 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 */ + /* 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); + /* Size of buffer, not including 2 byte size field */ + ataH2BE_U32(&buf[0], 2048 + 2); - /* 2k data + 4 byte header */ - uASC = (2048 + 4); - } + /* 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 */ @@ -4316,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; @@ -4344,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); @@ -4393,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; - } - 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 s_uLastLogTS = 0; - if (RTTimeMilliTS() >= s_uLastLogTS + 1000) - { - LogRel(("AHCI ATAPI: LUN#%d: CD-ROM block number %Ld invalid (READ)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA + cSectors)); - s_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 s_uLastLogTS = 0; - if (RTTimeMilliTS() >= s_uLastLogTS + 1000) - { - LogRel(("AHCI ATA: LUN#%d: CD-ROM block number %Ld invalid (READ CD)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA + cSectors)); - s_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 s_uLastLogTS = 0; - if (RTTimeMilliTS() >= s_uLastLogTS + 1000) - { - LogRel(("AHCI ATAPI: LUN#%d: CD-ROM block number %Ld invalid (SEEK)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA)); - s_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) { @@ -4673,7 +4723,7 @@ static AHCITXDIR atapiParseCmdVirtualATAPI(PAHCIPort pAhciPort, PAHCIREQ pAhciRe break; } - return rc; + return enmTxDir; } /* @@ -5896,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 { @@ -5996,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; } @@ -6015,11 +6068,9 @@ static DECLCALLBACK(int) ahciR3TransferCompleteNotify(PPDMIBLOCKASYNCPORT pInter 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; } /** @@ -6030,7 +6081,7 @@ static DECLCALLBACK(int) ahciR3TransferCompleteNotify(PPDMIBLOCKASYNCPORT pInter */ 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; @@ -6080,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) { @@ -6121,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) @@ -6130,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) @@ -6185,7 +6237,7 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t { pAhciReq->cbTransfer = ahciGetNSectors(pCmdFis, fLBA48) * pAhciPort->cbSector; pAhciReq->uOffset = ahciGetSector(pAhciPort, pCmdFis, fLBA48) * pAhciPort->cbSector; - rc = AHCITXDIR_READ; + enmTxDir = AHCITXDIR_READ; break; } case ATA_WRITE_DMA_EXT: @@ -6194,7 +6246,7 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t { pAhciReq->cbTransfer = ahciGetNSectors(pCmdFis, fLBA48) * pAhciPort->cbSector; pAhciReq->uOffset = ahciGetSector(pAhciPort, pCmdFis, fLBA48) * pAhciPort->cbSector; - rc = AHCITXDIR_WRITE; + enmTxDir = AHCITXDIR_WRITE; break; } case ATA_READ_FPDMA_QUEUED: @@ -6202,7 +6254,7 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t 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: @@ -6210,7 +6262,7 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t 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: @@ -6291,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. */ @@ -6310,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. @@ -6342,13 +6396,18 @@ static void ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq) 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; @@ -6394,6 +6453,8 @@ static void ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq) GCPhysPrdtl += sizeof(SGLEntry); } #endif + + return true; } /** @@ -6441,6 +6502,7 @@ static DECLCALLBACK(int) ahciAsyncIOLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread) { unsigned idx = 0; uint32_t u32Tasks = 0; + uint32_t u32RegHbaCtrl = 0; ASMAtomicWriteBool(&pAhciPort->fWrkThreadSleeping, true); u32Tasks = ASMAtomicXchgU32(&pAhciPort->u32TasksNew, 0); @@ -6456,9 +6518,24 @@ static DECLCALLBACK(int) ahciAsyncIOLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread) } 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; @@ -6493,7 +6570,23 @@ static DECLCALLBACK(int) ahciAsyncIOLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread) 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)) @@ -6510,21 +6603,15 @@ static DECLCALLBACK(int) ahciAsyncIOLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread) 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")); - break; } 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")); - break; - } 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 { @@ -6585,9 +6672,9 @@ static DECLCALLBACK(int) ahciAsyncIOLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread) pAhciReq); } if (rc == VINF_VD_ASYNC_IO_FINISHED) - rc = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true); + fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true); else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS) - rc = ahciTransferComplete(pAhciPort, pAhciReq, rc, true); + fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc, true); } else { @@ -6620,17 +6707,34 @@ static DECLCALLBACK(int) ahciAsyncIOLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread) pAhciReq->cbTransfer); pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 0; } - rc = ahciTransferComplete(pAhciPort, pAhciReq, rc, true); + fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc, true); } } } else - rc = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true); + fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true); } /* Command */ + /* + * Don't process other requests if the last one was canceled, + * the others are not valid anymore. + */ + if (fReqCanceled) + break; + u32Tasks &= ~RT_BIT_32(idx); /* Clear task bit. */ idx = ASMBitFirstSetU32(u32Tasks); } /* while tasks available */ + + /* + * 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)); @@ -6720,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"); } } @@ -6740,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; } } @@ -7421,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; @@ -7597,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)) @@ -7967,6 +8073,8 @@ 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++) { @@ -8124,7 +8232,6 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG pAhciPort->IPort.pfnQueryDeviceLocation = ahciR3PortQueryDeviceLocation; pAhciPort->IMountNotify.pfnMountNotify = ahciR3MountNotify; pAhciPort->IMountNotify.pfnUnmountNotify = ahciR3UnmountNotify; - pAhciPort->fAsyncIOThreadIdle = true; /* * Attach the block driver diff --git a/src/VBox/Devices/Storage/DevATA.cpp b/src/VBox/Devices/Storage/DevATA.cpp index 818785420..b87bdc271 100644 --- a/src/VBox/Devices/Storage/DevATA.cpp +++ b/src/VBox/Devices/Storage/DevATA.cpp @@ -1861,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; } @@ -2270,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) + { + uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET; + break; + } + + total_sectors = s->cTotalSectors; + total_sectors >>= 2; + if (total_sectors == 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_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 */ @@ -3069,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); @@ -3118,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) { diff --git a/src/VBox/Devices/Storage/DrvSCSI.cpp b/src/VBox/Devices/Storage/DrvSCSI.cpp index e7faa97de..1b0ce172d 100644 --- a/src/VBox/Devices/Storage/DrvSCSI.cpp +++ b/src/VBox/Devices/Storage/DrvSCSI.cpp @@ -657,6 +657,7 @@ static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const c 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); |
