VirtualBox

Changeset 64360 in vbox for trunk/src/VBox/Devices/Storage


Ignore:
Timestamp:
Oct 22, 2016 2:47:10 PM (8 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
111465
Message:

Devices/Storage/HostDVD: Updates for passthrough, implement command processing and splitting

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Storage/DrvHostDVD.cpp

    r64320 r64360  
    3636#include "ATAPIPassthrough.h"
    3737
     38/** ATAPI sense info size. */
     39#define ATAPI_SENSE_SIZE  64
     40/** Size of an ATAPI packet. */
     41#define ATAPI_PACKET_SIZE 12
     42
    3843/**
    3944 * Host DVD driver instance data.
     
    4550    /** The current tracklist of the loaded medium if passthrough is used. */
    4651    PTRACKLIST              pTrackList;
     52    /** ATAPI sense data. */
     53    uint8_t                 abATAPISense[ATAPI_SENSE_SIZE];
    4754} DRVHOSTDVD;
    4855/** Pointer to the host DVD driver instance data. */
     
    5360*********************************************************************************************************************************/
    5461
    55 
    56 static PDMMEDIATXDIR drvHostDvdGetTxDirFromCmd(uint8_t bCdb)
    57 {
     62DECLINLINE(void) drvHostDvdH2BE_U16(uint8_t *pbBuf, uint16_t val)
     63{
     64    pbBuf[0] = val >> 8;
     65    pbBuf[1] = val;
     66}
     67
     68
     69DECLINLINE(void) drvHostDvdH2BE_U24(uint8_t *pbBuf, uint32_t val)
     70{
     71    pbBuf[0] = val >> 16;
     72    pbBuf[1] = val >> 8;
     73    pbBuf[2] = val;
     74}
     75
     76
     77DECLINLINE(void) drvHostDvdH2BE_U32(uint8_t *pbBuf, uint32_t val)
     78{
     79    pbBuf[0] = val >> 24;
     80    pbBuf[1] = val >> 16;
     81    pbBuf[2] = val >> 8;
     82    pbBuf[3] = val;
     83}
     84
     85
     86DECLINLINE(uint16_t) drvHostDvdBE2H_U16(const uint8_t *pbBuf)
     87{
     88    return (pbBuf[0] << 8) | pbBuf[1];
     89}
     90
     91
     92DECLINLINE(uint32_t) drvHostDvdBE2H_U24(const uint8_t *pbBuf)
     93{
     94    return (pbBuf[0] << 16) | (pbBuf[1] << 8) | pbBuf[2];
     95}
     96
     97
     98DECLINLINE(uint32_t) drvHostDvdBE2H_U32(const uint8_t *pbBuf)
     99{
     100    return (pbBuf[0] << 24) | (pbBuf[1] << 16) | (pbBuf[2] << 8) | pbBuf[3];
     101}
     102
     103
     104DECLINLINE(void) drvHostDvdLBA2MSF(uint8_t *pbBuf, uint32_t iATAPILBA)
     105{
     106    iATAPILBA += 150;
     107    pbBuf[0] = (iATAPILBA / 75) / 60;
     108    pbBuf[1] = (iATAPILBA / 75) % 60;
     109    pbBuf[2] = iATAPILBA % 75;
     110}
     111
     112
     113DECLINLINE(uint32_t) drvHostDvdMSF2LBA(const uint8_t *pbBuf)
     114{
     115    return (pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2];
     116}
     117
     118static void drvHostDvdCmdOK(PDRVHOSTDVD pThis)
     119{
     120    memset(pThis->abATAPISense, '\0', sizeof(pThis->abATAPISense));
     121    pThis->abATAPISense[0] = 0x70;
     122    pThis->abATAPISense[7] = 10;
     123}
     124
     125static void drvHostDvdCmdError(PDRVHOSTDVD pThis, const uint8_t *pabATAPISense, size_t cbATAPISense)
     126{
     127    Log(("%s: sense=%#x (%s) asc=%#x ascq=%#x (%s)\n", __FUNCTION__, pabATAPISense[2] & 0x0f, SCSISenseText(pabATAPISense[2] & 0x0f),
     128             pabATAPISense[12], pabATAPISense[13], SCSISenseExtText(pabATAPISense[12], pabATAPISense[13])));
     129    memset(pThis->abATAPISense, '\0', sizeof(pThis->abATAPISense));
     130    memcpy(pThis->abATAPISense, pabATAPISense, RT_MIN(cbATAPISense, sizeof(pThis->abATAPISense)));
     131}
     132
     133/** @todo deprecated function - doesn't provide enough info. Replace by direct
     134 * calls to drvHostDvdCmdError()  with full data. */
     135static void drvHostDvdCmdErrorSimple(PDRVHOSTDVD pThis, uint8_t uATAPISenseKey, uint8_t uATAPIASC)
     136{
     137    uint8_t abATAPISense[ATAPI_SENSE_SIZE];
     138    memset(abATAPISense, '\0', sizeof(abATAPISense));
     139    abATAPISense[0] = 0x70 | (1 << 7);
     140    abATAPISense[2] = uATAPISenseKey & 0x0f;
     141    abATAPISense[7] = 10;
     142    abATAPISense[12] = uATAPIASC;
     143    drvHostDvdCmdError(pThis, abATAPISense, sizeof(abATAPISense));
     144}
     145
     146static void drvHostDvdSCSIPadStr(uint8_t *pbDst, const char *pbSrc, uint32_t cbSize)
     147{
     148    for (uint32_t i = 0; i < cbSize; i++)
     149    {
     150        if (*pbSrc)
     151            pbDst[i] = *pbSrc++;
     152        else
     153            pbDst[i] = ' ';
     154    }
     155}
     156
     157
     158static bool drvHostDvdParseCdb(PDRVHOSTDVD pThis, PDRVHOSTBASEREQ pReq,
     159                               const uint8_t *pbCdb, size_t cbCdb, size_t cbBuf,
     160                               PDMMEDIATXDIR *penmTxDir, size_t *pcbXfer,
     161                               size_t *pcbSector, uint8_t *pu8ScsiSts)
     162{
     163    uint32_t uLba = 0;
     164    uint32_t cSectors = 0;
     165    size_t cbSector = 0;
     166    size_t cbXfer = 0;
     167    bool fPassthrough = false;
    58168    PDMMEDIATXDIR enmTxDir = PDMMEDIATXDIR_NONE;
    59169
    60     switch (bCdb)
     170    RT_NOREF(cbCdb);
     171
     172    switch (pbCdb[0])
    61173    {
     174        /* First the commands we can pass through without further processing. */
    62175        case SCSI_BLANK:
    63176        case SCSI_CLOSE_TRACK_SESSION:
     
    68181        case SCSI_PLAY_AUDIO_MSF:
    69182        case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
     183        case SCSI_REPAIR_TRACK:
    70184        case SCSI_RESERVE_TRACK:
    71185        case SCSI_SCAN:
    72186        case SCSI_SEEK_10:
    73         case SCSI_REPAIR_TRACK:
    74187        case SCSI_SET_CD_SPEED:
    75188        case SCSI_SET_READ_AHEAD:
     
    79192        case SCSI_TEST_UNIT_READY:
    80193        case SCSI_VERIFY_10:
    81             enmTxDir = PDMMEDIATXDIR_NONE;
     194            fPassthrough = true;
     195            break;
     196        case SCSI_ERASE_10:
     197            uLba = drvHostDvdBE2H_U32(pbCdb + 2);
     198            cbXfer = drvHostDvdBE2H_U16(pbCdb + 7);
     199            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     200            fPassthrough = true;
     201            break;
     202        case SCSI_FORMAT_UNIT:
     203            cbXfer = cbBuf;
     204            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     205            fPassthrough = true;
     206            break;
     207        case SCSI_GET_CONFIGURATION:
     208            cbXfer = drvHostDvdBE2H_U16(pbCdb + 7);
     209            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     210            fPassthrough = true;
     211            break;
     212        case SCSI_GET_EVENT_STATUS_NOTIFICATION:
     213            cbXfer = drvHostDvdBE2H_U16(pbCdb + 7);
     214            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     215            fPassthrough = true;
     216            break;
     217        case SCSI_GET_PERFORMANCE:
     218            cbXfer = cbBuf;
     219            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     220            fPassthrough = true;
     221            break;
     222        case SCSI_INQUIRY:
     223            cbXfer = drvHostDvdBE2H_U16(pbCdb + 3);
     224            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     225            fPassthrough = true;
     226            break;
     227        case SCSI_MECHANISM_STATUS:
     228            cbXfer = drvHostDvdBE2H_U16(pbCdb + 8);
     229            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     230            fPassthrough = true;
     231            break;
     232        case SCSI_MODE_SELECT_10:
     233            cbXfer = drvHostDvdBE2H_U16(pbCdb + 7);
     234            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     235            fPassthrough = true;
     236            break;
     237        case SCSI_MODE_SENSE_10:
     238            cbXfer = drvHostDvdBE2H_U16(pbCdb + 7);
     239            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     240            fPassthrough = true;
     241            break;
     242        case SCSI_READ_10:
     243            uLba = drvHostDvdBE2H_U32(pbCdb + 2);
     244            cSectors = drvHostDvdBE2H_U16(pbCdb + 7);
     245            cbSector = 2048;
     246            cbXfer = cSectors * cbSector;
     247            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     248            fPassthrough = true;
     249            break;
     250        case SCSI_READ_12:
     251            uLba = drvHostDvdBE2H_U32(pbCdb + 2);
     252            cSectors = drvHostDvdBE2H_U32(pbCdb + 6);
     253            cbSector = 2048;
     254            cbXfer = cSectors * cbSector;
     255            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     256            fPassthrough = true;
     257            break;
     258        case SCSI_READ_BUFFER:
     259            cbXfer = drvHostDvdBE2H_U24(pbCdb + 6);
     260            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     261            fPassthrough = true;
     262            break;
     263        case SCSI_READ_BUFFER_CAPACITY:
     264            cbXfer = drvHostDvdBE2H_U16(pbCdb + 7);
     265            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     266            fPassthrough = true;
     267            break;
     268        case SCSI_READ_CAPACITY:
     269            cbXfer = 8;
     270            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     271            fPassthrough = true;
     272            break;
     273        case SCSI_READ_CD:
     274        case SCSI_READ_CD_MSF:
     275        {
     276            /* Get sector size based on the expected sector type field. */
     277            switch ((pbCdb[1] >> 2) & 0x7)
     278            {
     279                case 0x0: /* All types. */
     280                {
     281                    uint32_t iLbaStart;
     282
     283                    if (pbCdb[0] == SCSI_READ_CD)
     284                        iLbaStart = drvHostDvdBE2H_U32(&pbCdb[2]);
     285                    else
     286                        iLbaStart = drvHostDvdMSF2LBA(&pbCdb[3]);
     287
     288                    if (pThis->pTrackList)
     289                        cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pThis->pTrackList, iLbaStart);
     290                    else
     291                        cbSector = 2048; /* Might be incorrect if we couldn't determine the type. */
     292                    break;
     293                }
     294                case 0x1: /* CD-DA */
     295                    cbSector = 2352;
     296                    break;
     297                case 0x2: /* Mode 1 */
     298                    cbSector = 2048;
     299                    break;
     300                case 0x3: /* Mode 2 formless */
     301                    cbSector = 2336;
     302                    break;
     303                case 0x4: /* Mode 2 form 1 */
     304                    cbSector = 2048;
     305                    break;
     306                case 0x5: /* Mode 2 form 2 */
     307                    cbSector = 2324;
     308                    break;
     309                default: /* Reserved */
     310                    AssertMsgFailed(("Unknown sector type\n"));
     311                    cbSector = 0; /** @todo we should probably fail the command here already. */
     312            }
     313
     314            if (pbCdb[0] == SCSI_READ_CD)
     315                cbXfer = drvHostDvdBE2H_U24(pbCdb + 6) * cbSector;
     316            else /* SCSI_READ_MSF */
     317            {
     318                cSectors = drvHostDvdMSF2LBA(pbCdb + 6) - drvHostDvdMSF2LBA(pbCdb + 3);
     319                if (cSectors > 32)
     320                    cSectors = 32; /* Limit transfer size to 64~74K. Safety first. In any case this can only harm software doing CDDA extraction. */
     321                cbXfer = cSectors * cbSector;
     322            }
     323            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     324            fPassthrough = true;
     325            break;
     326        }
     327        case SCSI_READ_DISC_INFORMATION:
     328            cbXfer = drvHostDvdBE2H_U16(pbCdb + 7);
     329            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     330            fPassthrough = true;
     331            break;
     332        case SCSI_READ_DVD_STRUCTURE:
     333            cbXfer = drvHostDvdBE2H_U16(pbCdb + 8);
     334            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     335            fPassthrough = true;
     336            break;
     337        case SCSI_READ_FORMAT_CAPACITIES:
     338            cbXfer = drvHostDvdBE2H_U16(pbCdb + 7);
     339            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     340            fPassthrough = true;
     341            break;
     342        case SCSI_READ_SUBCHANNEL:
     343            cbXfer = drvHostDvdBE2H_U16(pbCdb + 7);
     344            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     345            fPassthrough = true;
     346            break;
     347        case SCSI_READ_TOC_PMA_ATIP:
     348            cbXfer = drvHostDvdBE2H_U16(pbCdb + 7);
     349            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     350            fPassthrough = true;
     351            break;
     352        case SCSI_READ_TRACK_INFORMATION:
     353            cbXfer = drvHostDvdBE2H_U16(pbCdb + 7);
     354            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     355            fPassthrough = true;
     356            break;
     357        case SCSI_REPORT_KEY:
     358            cbXfer = drvHostDvdBE2H_U16(pbCdb + 8);
     359            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     360            fPassthrough = true;
     361            break;
     362        case SCSI_REQUEST_SENSE:
     363            cbXfer = pbCdb[4];
     364            if ((pThis->abATAPISense[2] & 0x0f) != SCSI_SENSE_NONE)
     365            {
     366                /* Copy sense data over. */
     367                void *pvBuf = NULL;
     368                int rc = drvHostBaseBufferRetain(&pThis->Core, pReq, cbBuf, false /*fWrite*/, &pvBuf);
     369                if (RT_SUCCESS(rc))
     370                {
     371                    memcpy(pvBuf, &pThis->abATAPISense[0], RT_MIN(sizeof(pThis->abATAPISense), cbBuf));
     372                    rc = drvHostBaseBufferRelease(&pThis->Core, pReq, cbBuf, false /* fWrite */, pvBuf);
     373                    AssertRC(rc);
     374                    drvHostDvdCmdOK(pThis);
     375                    *pu8ScsiSts = SCSI_STATUS_OK;
     376                }
     377                break;
     378            }
     379            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     380            fPassthrough = true;
     381            break;
     382        case SCSI_SEND_CUE_SHEET:
     383            cbXfer = drvHostDvdBE2H_U24(pbCdb + 6);
     384            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     385            fPassthrough = true;
     386            break;
     387        case SCSI_SEND_DVD_STRUCTURE:
     388            cbXfer = drvHostDvdBE2H_U16(pbCdb + 8);
     389            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     390            fPassthrough = true;
     391            break;
     392        case SCSI_SEND_EVENT:
     393            cbXfer = drvHostDvdBE2H_U16(pbCdb + 8);
     394            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     395            fPassthrough = true;
     396            break;
     397        case SCSI_SEND_KEY:
     398            cbXfer = drvHostDvdBE2H_U16(pbCdb + 8);
     399            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     400            fPassthrough = true;
     401            break;
     402        case SCSI_SEND_OPC_INFORMATION:
     403            cbXfer = drvHostDvdBE2H_U16(pbCdb + 7);
     404            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     405            fPassthrough = true;
    82406            break;
    83407        case SCSI_SET_STREAMING:
    84         case SCSI_ERASE_10:
    85         case SCSI_FORMAT_UNIT:
    86         case SCSI_MODE_SELECT_10:
    87         case SCSI_SEND_CUE_SHEET:
    88         case SCSI_SEND_DVD_STRUCTURE:
    89         case SCSI_SEND_EVENT:
    90         case SCSI_SEND_KEY:
    91         case SCSI_SEND_OPC_INFORMATION:
     408            cbXfer = drvHostDvdBE2H_U16(pbCdb + 9);
     409            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     410            fPassthrough = true;
     411            break;
    92412        case SCSI_WRITE_10:
    93413        case SCSI_WRITE_AND_VERIFY_10:
     414            uLba = drvHostDvdBE2H_U32(pbCdb + 2);
     415            cSectors = drvHostDvdBE2H_U16(pbCdb + 7);
     416            if (pThis->pTrackList)
     417                cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pThis->pTrackList, uLba);
     418            else
     419                cbSector = 2048;
     420            cbXfer = cSectors * cbSector;
     421            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     422            fPassthrough = true;
     423            break;
    94424        case SCSI_WRITE_12:
    95             enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
    96             break;
    97         case SCSI_GET_CONFIGURATION:
    98         case SCSI_GET_EVENT_STATUS_NOTIFICATION:
    99         case SCSI_GET_PERFORMANCE:
    100         case SCSI_INQUIRY:
    101         case SCSI_MECHANISM_STATUS:
    102         case SCSI_MODE_SENSE_10:
    103         case SCSI_READ_10:
    104         case SCSI_READ_12:
    105         case SCSI_READ_BUFFER:
    106         case SCSI_READ_BUFFER_CAPACITY:
    107         case SCSI_READ_CAPACITY:
    108         case SCSI_READ_CD:
    109         case SCSI_READ_CD_MSF:
    110         case SCSI_READ_DISC_INFORMATION:
    111         case SCSI_READ_DVD_STRUCTURE:
    112         case SCSI_READ_FORMAT_CAPACITIES:
    113         case SCSI_READ_SUBCHANNEL:
    114         case SCSI_READ_TOC_PMA_ATIP:
    115         case SCSI_READ_TRACK_INFORMATION:
    116         case SCSI_REPORT_KEY:
    117         case SCSI_REQUEST_SENSE:
    118         case SCSI_REPORT_LUNS: /* Not part of MMC-3, but used by Windows. */
    119             enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
    120             break;
    121 
     425            uLba = drvHostDvdBE2H_U32(pbCdb + 2);
     426            cSectors = drvHostDvdBE2H_U32(pbCdb + 6);
     427            if (pThis->pTrackList)
     428                cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pThis->pTrackList, uLba);
     429            else
     430                cbSector = 2048;
     431            cbXfer = cSectors * cbSector;
     432            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     433            fPassthrough = true;
     434            break;
    122435        case SCSI_WRITE_BUFFER:
    123 #if 0
    124             switch (pbPacket[1] & 0x1f)
     436            switch (pbCdb[1] & 0x1f)
    125437            {
    126438                case 0x04: /* download microcode */
     
    130442                case 0x0e: /* download microcode with offsets and defer activation */
    131443                case 0x0f: /* activate deferred microcode */
    132                     LogRel(("PIIX3 ATA: LUN#%d: CD-ROM passthrough command attempted to update firmware, blocked\n", s->iLUN));
    133                     atapiR3CmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
     444                    LogRel(("HostDVD#%u: CD-ROM passthrough command attempted to update firmware, blocked\n", pThis->Core.pDrvIns->iInstance));
     445                    drvHostDvdCmdErrorSimple(pThis, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
     446                    *pu8ScsiSts = SCSI_STATUS_CHECK_CONDITION;
    134447                    break;
    135448                default:
     449                    cbXfer = drvHostDvdBE2H_U16(pbCdb + 6);
    136450                    enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     451                    fPassthrough = true;
    137452                    break;
    138453            }
    139 #endif
     454            break;
     455        case SCSI_REPORT_LUNS: /* Not part of MMC-3, but used by Windows. */
     456            cbXfer = drvHostDvdBE2H_U32(pbCdb + 6);
     457            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     458            fPassthrough = true;
    140459            break;
    141460        case SCSI_REZERO_UNIT:
     
    145464             * opcode 0x01" in syslog) and replies with a sense code of 0,
    146465             * which sends cdrecord to an endless loop. */
    147             //atapiR3CmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
     466            drvHostDvdCmdErrorSimple(pThis, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
     467            *pu8ScsiSts = SCSI_STATUS_CHECK_CONDITION;
    148468            break;
    149469        default:
    150             LogRel(("HostDVD: passthrough unimplemented for command %#x\n", bCdb));
    151             //atapiR3CmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
     470            LogRel(("HostDVD#%u: Passthrough unimplemented for command %#x\n", pThis->Core.pDrvIns->iInstance, pbCdb[0]));
     471            drvHostDvdCmdErrorSimple(pThis, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
     472            *pu8ScsiSts = SCSI_STATUS_CHECK_CONDITION;
    152473            break;
    153474    }
    154475
    155     return enmTxDir;
     476    if (fPassthrough)
     477    {
     478        *penmTxDir = enmTxDir;
     479        *pcbXfer   = cbXfer;
     480        *pcbSector = cbSector;
     481    }
     482
     483    return fPassthrough;
    156484}
    157485
     
    215543    RT_NOREF2(uLun, cTimeoutMillies);
    216544
    217     PDRVHOSTBASE pThis = RT_FROM_MEMBER(pInterface, DRVHOSTBASE, IMediaEx);
     545    PDRVHOSTDVD pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDVD, Core.IMediaEx);
    218546    PDRVHOSTBASEREQ pReq = (PDRVHOSTBASEREQ)hIoReq;
    219547    int rc = VINF_SUCCESS;
    220     void *pvBuf = NULL;
    221 
    222     LogFlow(("%s: pbCdb[0]=%#04x enmTxDir=%d cbBuf=%zu timeout=%u\n", __FUNCTION__, pbCdb[0], enmTxDir, cbBuf, cTimeoutMillies));
    223 
    224     RTCritSectEnter(&pThis->CritSect);
    225     if (cbBuf)
     548
     549    LogFlow(("%s: pbCdb[0]=%#04x{%s} enmTxDir=%d cbBuf=%zu timeout=%u\n",
     550             __FUNCTION__, pbCdb[0], SCSICmdText(pbCdb[0]), enmTxDir, cbBuf, cTimeoutMillies));
     551
     552    RTCritSectEnter(&pThis->Core.CritSect);
     553
     554    /*
     555     * Parse the command first to fend off any illegal or dangeroups commands we don't want the guest
     556     * to execute on the host drive.
     557     */
     558    PDMMEDIATXDIR enmXferDir = PDMMEDIATXDIR_NONE;
     559    size_t cbXfer = 0;
     560    size_t cbSector = 0;
     561    bool fPassthrough = drvHostDvdParseCdb(pThis, pReq, pbCdb, cbCdb, cbBuf,
     562                                           &enmXferDir, &cbXfer, &cbSector, pu8ScsiSts);
     563    if (fPassthrough)
    226564    {
    227         bool fWrite = (enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN || enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE);
    228         rc = drvHostBaseBufferRetain(pThis, pReq, cbBuf, fWrite, &pvBuf);
    229     }
    230 
    231     if (RT_SUCCESS(rc))
    232     {
    233         uint32_t cbBufTmp = (uint32_t)cbBuf;
    234         PDMMEDIATXDIR enmMediaTxDir = PDMMEDIATXDIR_NONE;
    235         /*
    236          * Pass the request on to the internal scsi command interface.
    237          * The command seems to be 12 bytes long, the docs a bit copy&pasty on the command length point...
    238          */
    239         if (enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN || enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE)
    240             memset(pvBuf, '\0', cbBuf); /* we got read size, but zero it anyway. */
    241 
    242         if (cbBuf)
     565        void *pvBuf = NULL;
     566        size_t cbXferCur = cbXfer;
     567
     568        if (cbXfer)
     569            rc = drvHostBaseBufferRetain(&pThis->Core, pReq, cbXfer, enmXferDir == PDMMEDIATXDIR_TO_DEVICE, &pvBuf);
     570
     571        if (cbXfer > SCSI_MAX_BUFFER_SIZE)
    243572        {
    244             switch (enmTxDir)
     573            /* Linux accepts commands with up to 100KB of data, but expects
     574             * us to handle commands with up to 128KB of data. The usual
     575             * imbalance of powers. */
     576            uint8_t aATAPICmd[ATAPI_PACKET_SIZE];
     577            uint32_t iATAPILBA, cSectors;
     578            uint8_t *pbBuf = (uint8_t *)pvBuf;
     579
     580            switch (pbCdb[0])
    245581            {
    246                 case PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE:
    247                     enmMediaTxDir = PDMMEDIATXDIR_FROM_DEVICE;
    248                     break;
    249                 case PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE:
    250                     enmMediaTxDir = PDMMEDIATXDIR_TO_DEVICE;
    251                     break;
    252                 case PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN:
    253                     enmMediaTxDir = drvHostDvdGetTxDirFromCmd(pbCdb[0]);
    254                     break;
    255                 case PDMMEDIAEXIOREQSCSITXDIR_NONE:
     582                case SCSI_READ_10:
     583                case SCSI_WRITE_10:
     584                case SCSI_WRITE_AND_VERIFY_10:
     585                    iATAPILBA = drvHostDvdBE2H_U32(pbCdb + 2);
     586                    cSectors = drvHostDvdBE2H_U16(pbCdb + 7);
     587                    break;
     588                case SCSI_READ_12:
     589                case SCSI_WRITE_12:
     590                    iATAPILBA = drvHostDvdBE2H_U32(pbCdb + 2);
     591                    cSectors = drvHostDvdBE2H_U32(pbCdb + 6);
     592                    break;
     593                case SCSI_READ_CD:
     594                    iATAPILBA = drvHostDvdBE2H_U32(pbCdb + 2);
     595                    cSectors = drvHostDvdBE2H_U24(pbCdb + 6);
     596                    break;
     597                case SCSI_READ_CD_MSF:
     598                    iATAPILBA = drvHostDvdMSF2LBA(pbCdb + 3);
     599                    cSectors = drvHostDvdMSF2LBA(pbCdb + 6) - iATAPILBA;
     600                    break;
    256601                default:
    257                     enmMediaTxDir = PDMMEDIATXDIR_NONE;
    258                     break;
     602                    AssertMsgFailed(("Don't know how to split command %#04x\n", pbCdb[0]));
     603                    LogRelMax(10, ("HostDVD#%u: CD-ROM passthrough split error\n", pThis->Core.pDrvIns->iInstance));
     604                    drvHostDvdCmdErrorSimple(pThis, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
     605                    *pu8ScsiSts = SCSI_STATUS_CHECK_CONDITION;
     606                    rc = drvHostBaseBufferRelease(&pThis->Core, pReq, cbBuf, enmXferDir == PDMMEDIATXDIR_TO_DEVICE, pvBuf);
     607                    RTCritSectLeave(&pThis->Core.CritSect);
     608                    return VINF_SUCCESS;
     609            }
     610            memcpy(aATAPICmd, pbCdb, RT_MIN(cbCdb, ATAPI_PACKET_SIZE));
     611            uint32_t cReqSectors = 0;
     612            for (uint32_t i = cSectors; i > 0; i -= cReqSectors)
     613            {
     614                if (i * cbSector > SCSI_MAX_BUFFER_SIZE)
     615                    cReqSectors = SCSI_MAX_BUFFER_SIZE / cbSector;
     616                else
     617                    cReqSectors = i;
     618                uint32_t cbCurrTX = cbSector * cReqSectors;
     619                switch (pbCdb[0])
     620                {
     621                    case SCSI_READ_10:
     622                    case SCSI_WRITE_10:
     623                    case SCSI_WRITE_AND_VERIFY_10:
     624                        drvHostDvdH2BE_U32(aATAPICmd + 2, iATAPILBA);
     625                        drvHostDvdH2BE_U16(aATAPICmd + 7, cReqSectors);
     626                        break;
     627                    case SCSI_READ_12:
     628                    case SCSI_WRITE_12:
     629                        drvHostDvdH2BE_U32(aATAPICmd + 2, iATAPILBA);
     630                        drvHostDvdH2BE_U32(aATAPICmd + 6, cReqSectors);
     631                        break;
     632                    case SCSI_READ_CD:
     633                        drvHostDvdH2BE_U32(aATAPICmd + 2, iATAPILBA);
     634                        drvHostDvdH2BE_U24(aATAPICmd + 6, cReqSectors);
     635                        break;
     636                    case SCSI_READ_CD_MSF:
     637                        drvHostDvdLBA2MSF(aATAPICmd + 3, iATAPILBA);
     638                        drvHostDvdLBA2MSF(aATAPICmd + 6, iATAPILBA + cReqSectors);
     639                        break;
     640                }
     641                rc = drvHostBaseScsiCmdOs(&pThis->Core, aATAPICmd, sizeof(aATAPICmd),
     642                                          enmXferDir, pbBuf, &cbCurrTX,
     643                                          &pThis->abATAPISense[0], sizeof(pThis->abATAPISense),
     644                                          cTimeoutMillies /**< @todo timeout */);
     645                if (rc != VINF_SUCCESS)
     646                    break;
     647                iATAPILBA += cReqSectors;
     648                pbBuf += cbSector * cReqSectors;
    259649            }
    260650        }
    261 
    262         rc = drvHostBaseScsiCmdOs(pThis, pbCdb, cbCdb, enmMediaTxDir, pvBuf, &cbBufTmp, pabSense, cbSense, cTimeoutMillies);
    263         if (rc == VERR_UNRESOLVED_ERROR)
     651        else
    264652        {
    265             /* sense information set */
    266             rc = VERR_DEV_IO_ERROR;
     653            uint32_t cbXferTmp = (uint32_t)cbXferCur;
     654            rc = drvHostBaseScsiCmdOs(&pThis->Core, pbCdb, cbCdb, enmXferDir, pvBuf, &cbXferTmp,
     655                                      &pThis->abATAPISense[0], sizeof(pThis->abATAPISense), cTimeoutMillies);
     656        }
     657
     658        if (RT_SUCCESS(rc))
     659        {
     660            /* Do post processing for certain commands. */
     661            switch (pbCdb[0])
     662            {
     663                case SCSI_SEND_CUE_SHEET:
     664                case SCSI_READ_TOC_PMA_ATIP:
     665                {
     666                    if (!pThis->pTrackList)
     667                        rc = ATAPIPassthroughTrackListCreateEmpty(&pThis->pTrackList);
     668
     669                    if (RT_SUCCESS(rc))
     670                        rc = ATAPIPassthroughTrackListUpdate(pThis->pTrackList, pbCdb, pvBuf);
     671
     672                    if (RT_FAILURE(rc))
     673                        LogRelMax(10, ("HostDVD#%u: Error (%Rrc) while updating the tracklist during %s, burning the disc might fail\n",
     674                                  pThis->Core.pDrvIns->iInstance, rc,
     675                                  pbCdb[0] == SCSI_SEND_CUE_SHEET ? "SEND CUE SHEET" : "READ TOC/PMA/ATIP"));
     676                    break;
     677                }
     678                case SCSI_SYNCHRONIZE_CACHE:
     679                {
     680                    if (pThis->pTrackList)
     681                        ATAPIPassthroughTrackListClear(pThis->pTrackList);
     682                    break;
     683                }
     684            }
     685
     686            if (enmXferDir == PDMMEDIATXDIR_FROM_DEVICE)
     687            {
     688               Assert(cbXferCur <= cbXfer);
     689
     690                if (pbCdb[0] == SCSI_INQUIRY)
     691                {
     692                    /* Make sure that the real drive cannot be identified.
     693                     * Motivation: changing the VM configuration should be as
     694                     *             invisible as possible to the guest. */
     695                    if (cbXferCur >= 8 + 8)
     696                        drvHostDvdSCSIPadStr((uint8_t *)pvBuf + 8, "VBOX", 8);
     697                    if (cbXferCur >= 16 + 16)
     698                        drvHostDvdSCSIPadStr((uint8_t *)pvBuf + 16, "CD-ROM", 16);
     699                    if (cbXferCur >= 32 + 4)
     700                        drvHostDvdSCSIPadStr((uint8_t *)pvBuf + 32, "1.0", 4);
     701                }
     702
     703                if (cbXferCur)
     704                    Log3(("ATAPI PT data read (%d): %.*Rhxs\n", cbXferCur, cbXferCur, (uint8_t *)pvBuf));
     705            }
     706
     707            drvHostDvdCmdOK(pThis);
     708            *pu8ScsiSts = SCSI_STATUS_OK;
     709        }
     710        else
     711        {
     712            do
     713            {
     714                /* don't log superfluous errors */
     715                if (    rc == VERR_DEV_IO_ERROR
     716                    && (   pbCdb[0] == SCSI_TEST_UNIT_READY
     717                        || pbCdb[0] == SCSI_READ_CAPACITY
     718                        || pbCdb[0] == SCSI_READ_DVD_STRUCTURE
     719                        || pbCdb[0] == SCSI_READ_TOC_PMA_ATIP))
     720                    break;
     721                LogRelMax(10, ("HostDVD#%u: CD-ROM passthrough cmd=%#04x sense=%d ASC=%#02x ASCQ=%#02x %Rrc\n",
     722                          pThis->Core.pDrvIns->iInstance, pbCdb[0], pThis->abATAPISense[2] & 0x0f,
     723                          pThis->abATAPISense[12], pThis->abATAPISense[13], rc));
     724            } while (0);
     725            drvHostDvdCmdError(pThis, &pThis->abATAPISense[0], sizeof(pThis->abATAPISense));
    267726            *pu8ScsiSts = SCSI_STATUS_CHECK_CONDITION;
     727            rc = VINF_SUCCESS;
    268728        }
    269729
    270         if (pbCdb[0] == SCSI_GET_EVENT_STATUS_NOTIFICATION)
    271         {
    272             uint8_t *pbBuf = (uint8_t*)pvBuf;
    273             Log2(("Event Status Notification class=%#02x supported classes=%#02x\n", pbBuf[2], pbBuf[3]));
    274             if (RT_BE2H_U16(*(uint16_t*)pbBuf) >= 6)
    275                 Log2(("  event %#02x %#02x %#02x %#02x\n", pbBuf[4], pbBuf[5], pbBuf[6], pbBuf[7]));
    276         }
    277 
    278         if (cbBuf)
    279         {
    280             bool fWrite = (enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN || enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE);
    281             rc = drvHostBaseBufferRelease(pThis, pReq, cbBuf, fWrite, pvBuf);
    282         }
     730        if (cbXfer)
     731            rc = drvHostBaseBufferRelease(&pThis->Core, pReq, cbXfer, enmXferDir == PDMMEDIATXDIR_TO_DEVICE, pvBuf);
    283732    }
    284     RTCritSectLeave(&pThis->CritSect);
     733
     734    /*
     735     * We handled the command, check the status code and copy over the sense data if
     736     * it is CHECK CONDITION.
     737     */
     738    if (   *pu8ScsiSts == SCSI_STATUS_CHECK_CONDITION
     739        && VALID_PTR(pabSense)
     740        && cbSense > 0)
     741        memcpy(pabSense, &pThis->abATAPISense[0], RT_MIN(cbSense, sizeof(pThis->abATAPISense)));
     742
     743    RTCritSectLeave(&pThis->Core.CritSect);
    285744
    286745    LogFlow(("%s: rc=%Rrc\n", __FUNCTION__, rc));
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette