diff options
| author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-03-26 19:21:20 +0000 |
|---|---|---|
| committer | <> | 2014-05-08 15:03:54 +0000 |
| commit | fb123f93f9f5ce42c8e5785d2f8e0edaf951740e (patch) | |
| tree | c2103d76aec5f1f10892cd1d3a38e24f665ae5db /src/VBox/Devices/Storage/DevATA.cpp | |
| parent | 58ed4748338f9466599adfc8a9171280ed99e23f (diff) | |
| download | VirtualBox-master.tar.gz | |
Imported from /home/lorry/working-area/delta_VirtualBox/VirtualBox-4.3.10.tar.bz2.HEADVirtualBox-4.3.10master
Diffstat (limited to 'src/VBox/Devices/Storage/DevATA.cpp')
| -rw-r--r-- | src/VBox/Devices/Storage/DevATA.cpp | 1473 |
1 files changed, 685 insertions, 788 deletions
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, |
