VirtualBox

Changeset 65964 in vbox for trunk/src/VBox/Devices


Ignore:
Timestamp:
Mar 7, 2017 10:44:12 AM (8 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
113790
Message:

Devices/Storage/ATAPIPassthrough: Add method to parse a CDB and return whether it is safe to pass it through to a host device along with some extracted information

Location:
trunk/src/VBox/Devices/Storage
Files:
2 edited

Legend:

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

    r65108 r65964  
    1919#include <iprt/assert.h>
    2020#include <iprt/mem.h>
     21#include <iprt/string.h>
    2122
    2223#include <VBox/log.h>
     
    628629}
    629630
     631
     632static uint8_t atapiPassthroughCmdErrorSimple(uint8_t *pbSense, size_t cbSense, uint8_t uATAPISenseKey, uint8_t uATAPIASC)
     633{
     634    memset(pbSense, '\0', cbSense);
     635    if (RT_LIKELY(cbSense >= 13))
     636    {
     637        pbSense[0] = 0x70 | (1 << 7);
     638        pbSense[2] = uATAPISenseKey & 0x0f;
     639        pbSense[7] = 10;
     640        pbSense[12] = uATAPIASC;
     641    }
     642    return SCSI_STATUS_CHECK_CONDITION;
     643}
     644
     645
     646DECLHIDDEN(bool) ATAPIPassthroughParseCdb(const uint8_t *pbCdb, size_t cbCdb, size_t cbBuf,
     647                                          PTRACKLIST pTrackList, uint8_t *pbSense, size_t cbSense,
     648                                          PDMMEDIATXDIR *penmTxDir, size_t *pcbXfer,
     649                                          size_t *pcbSector, uint8_t *pu8ScsiSts)
     650{
     651    uint32_t uLba = 0;
     652    uint32_t cSectors = 0;
     653    size_t cbSector = 0;
     654    size_t cbXfer = 0;
     655    bool fPassthrough = false;
     656    PDMMEDIATXDIR enmTxDir = PDMMEDIATXDIR_NONE;
     657
     658    RT_NOREF(cbCdb);
     659
     660    switch (pbCdb[0])
     661    {
     662        /* First the commands we can pass through without further processing. */
     663        case SCSI_BLANK:
     664        case SCSI_CLOSE_TRACK_SESSION:
     665        case SCSI_LOAD_UNLOAD_MEDIUM:
     666        case SCSI_PAUSE_RESUME:
     667        case SCSI_PLAY_AUDIO_10:
     668        case SCSI_PLAY_AUDIO_12:
     669        case SCSI_PLAY_AUDIO_MSF:
     670        case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
     671        case SCSI_REPAIR_TRACK:
     672        case SCSI_RESERVE_TRACK:
     673        case SCSI_SCAN:
     674        case SCSI_SEEK_10:
     675        case SCSI_SET_CD_SPEED:
     676        case SCSI_SET_READ_AHEAD:
     677        case SCSI_START_STOP_UNIT:
     678        case SCSI_STOP_PLAY_SCAN:
     679        case SCSI_SYNCHRONIZE_CACHE:
     680        case SCSI_TEST_UNIT_READY:
     681        case SCSI_VERIFY_10:
     682            fPassthrough = true;
     683            break;
     684        case SCSI_ERASE_10:
     685            uLba = scsiBE2H_U32(pbCdb + 2);
     686            cbXfer = scsiBE2H_U16(pbCdb + 7);
     687            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     688            fPassthrough = true;
     689            break;
     690        case SCSI_FORMAT_UNIT:
     691            cbXfer = cbBuf;
     692            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     693            fPassthrough = true;
     694            break;
     695        case SCSI_GET_CONFIGURATION:
     696            cbXfer = scsiBE2H_U16(pbCdb + 7);
     697            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     698            fPassthrough = true;
     699            break;
     700        case SCSI_GET_EVENT_STATUS_NOTIFICATION:
     701            cbXfer = scsiBE2H_U16(pbCdb + 7);
     702            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     703            fPassthrough = true;
     704            break;
     705        case SCSI_GET_PERFORMANCE:
     706            cbXfer = cbBuf;
     707            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     708            fPassthrough = true;
     709            break;
     710        case SCSI_INQUIRY:
     711            cbXfer = scsiBE2H_U16(pbCdb + 3);
     712            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     713            fPassthrough = true;
     714            break;
     715        case SCSI_MECHANISM_STATUS:
     716            cbXfer = scsiBE2H_U16(pbCdb + 8);
     717            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     718            fPassthrough = true;
     719            break;
     720        case SCSI_MODE_SELECT_10:
     721            cbXfer = scsiBE2H_U16(pbCdb + 7);
     722            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     723            fPassthrough = true;
     724            break;
     725        case SCSI_MODE_SENSE_10:
     726            cbXfer = scsiBE2H_U16(pbCdb + 7);
     727            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     728            fPassthrough = true;
     729            break;
     730        case SCSI_READ_10:
     731            uLba = scsiBE2H_U32(pbCdb + 2);
     732            cSectors = scsiBE2H_U16(pbCdb + 7);
     733            cbSector = 2048;
     734            cbXfer = cSectors * cbSector;
     735            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     736            fPassthrough = true;
     737            break;
     738        case SCSI_READ_12:
     739            uLba = scsiBE2H_U32(pbCdb + 2);
     740            cSectors = scsiBE2H_U32(pbCdb + 6);
     741            cbSector = 2048;
     742            cbXfer = cSectors * cbSector;
     743            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     744            fPassthrough = true;
     745            break;
     746        case SCSI_READ_BUFFER:
     747            cbXfer = scsiBE2H_U24(pbCdb + 6);
     748            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     749            fPassthrough = true;
     750            break;
     751        case SCSI_READ_BUFFER_CAPACITY:
     752            cbXfer = scsiBE2H_U16(pbCdb + 7);
     753            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     754            fPassthrough = true;
     755            break;
     756        case SCSI_READ_CAPACITY:
     757            cbXfer = 8;
     758            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     759            fPassthrough = true;
     760            break;
     761        case SCSI_READ_CD:
     762        case SCSI_READ_CD_MSF:
     763        {
     764            /* Get sector size based on the expected sector type field. */
     765            switch ((pbCdb[1] >> 2) & 0x7)
     766            {
     767                case 0x0: /* All types. */
     768                {
     769                    uint32_t iLbaStart;
     770
     771                    if (pbCdb[0] == SCSI_READ_CD)
     772                        iLbaStart = scsiBE2H_U32(&pbCdb[2]);
     773                    else
     774                        iLbaStart = scsiMSF2LBA(&pbCdb[3]);
     775
     776                    if (pTrackList)
     777                        cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pTrackList, iLbaStart);
     778                    else
     779                        cbSector = 2048; /* Might be incorrect if we couldn't determine the type. */
     780                    break;
     781                }
     782                case 0x1: /* CD-DA */
     783                    cbSector = 2352;
     784                    break;
     785                case 0x2: /* Mode 1 */
     786                    cbSector = 2048;
     787                    break;
     788                case 0x3: /* Mode 2 formless */
     789                    cbSector = 2336;
     790                    break;
     791                case 0x4: /* Mode 2 form 1 */
     792                    cbSector = 2048;
     793                    break;
     794                case 0x5: /* Mode 2 form 2 */
     795                    cbSector = 2324;
     796                    break;
     797                default: /* Reserved */
     798                    AssertMsgFailed(("Unknown sector type\n"));
     799                    cbSector = 0; /** @todo we should probably fail the command here already. */
     800            }
     801
     802            if (pbCdb[0] == SCSI_READ_CD)
     803                cbXfer = scsiBE2H_U24(pbCdb + 6) * cbSector;
     804            else /* SCSI_READ_MSF */
     805            {
     806                cSectors = scsiMSF2LBA(pbCdb + 6) - scsiMSF2LBA(pbCdb + 3);
     807                if (cSectors > 32)
     808                    cSectors = 32; /* Limit transfer size to 64~74K. Safety first. In any case this can only harm software doing CDDA extraction. */
     809                cbXfer = cSectors * cbSector;
     810            }
     811            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     812            fPassthrough = true;
     813            break;
     814        }
     815        case SCSI_READ_DISC_INFORMATION:
     816            cbXfer = scsiBE2H_U16(pbCdb + 7);
     817            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     818            fPassthrough = true;
     819            break;
     820        case SCSI_READ_DVD_STRUCTURE:
     821            cbXfer = scsiBE2H_U16(pbCdb + 8);
     822            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     823            fPassthrough = true;
     824            break;
     825        case SCSI_READ_FORMAT_CAPACITIES:
     826            cbXfer = scsiBE2H_U16(pbCdb + 7);
     827            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     828            fPassthrough = true;
     829            break;
     830        case SCSI_READ_SUBCHANNEL:
     831            cbXfer = scsiBE2H_U16(pbCdb + 7);
     832            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     833            fPassthrough = true;
     834            break;
     835        case SCSI_READ_TOC_PMA_ATIP:
     836            cbXfer = scsiBE2H_U16(pbCdb + 7);
     837            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     838            fPassthrough = true;
     839            break;
     840        case SCSI_READ_TRACK_INFORMATION:
     841            cbXfer = scsiBE2H_U16(pbCdb + 7);
     842            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     843            fPassthrough = true;
     844            break;
     845        case SCSI_REPORT_KEY:
     846            cbXfer = scsiBE2H_U16(pbCdb + 8);
     847            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     848            fPassthrough = true;
     849            break;
     850        case SCSI_REQUEST_SENSE:
     851            cbXfer = pbCdb[4];
     852            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     853            fPassthrough = true;
     854            break;
     855        case SCSI_SEND_CUE_SHEET:
     856            cbXfer = scsiBE2H_U24(pbCdb + 6);
     857            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     858            fPassthrough = true;
     859            break;
     860        case SCSI_SEND_DVD_STRUCTURE:
     861            cbXfer = scsiBE2H_U16(pbCdb + 8);
     862            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     863            fPassthrough = true;
     864            break;
     865        case SCSI_SEND_EVENT:
     866            cbXfer = scsiBE2H_U16(pbCdb + 8);
     867            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     868            fPassthrough = true;
     869            break;
     870        case SCSI_SEND_KEY:
     871            cbXfer = scsiBE2H_U16(pbCdb + 8);
     872            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     873            fPassthrough = true;
     874            break;
     875        case SCSI_SEND_OPC_INFORMATION:
     876            cbXfer = scsiBE2H_U16(pbCdb + 7);
     877            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     878            fPassthrough = true;
     879            break;
     880        case SCSI_SET_STREAMING:
     881            cbXfer = scsiBE2H_U16(pbCdb + 9);
     882            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     883            fPassthrough = true;
     884            break;
     885        case SCSI_WRITE_10:
     886        case SCSI_WRITE_AND_VERIFY_10:
     887            uLba = scsiBE2H_U32(pbCdb + 2);
     888            cSectors = scsiBE2H_U16(pbCdb + 7);
     889            if (pTrackList)
     890                cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pTrackList, uLba);
     891            else
     892                cbSector = 2048;
     893            cbXfer = cSectors * cbSector;
     894            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     895            fPassthrough = true;
     896            break;
     897        case SCSI_WRITE_12:
     898            uLba = scsiBE2H_U32(pbCdb + 2);
     899            cSectors = scsiBE2H_U32(pbCdb + 6);
     900            if (pTrackList)
     901                cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pTrackList, uLba);
     902            else
     903                cbSector = 2048;
     904            cbXfer = cSectors * cbSector;
     905            enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     906            fPassthrough = true;
     907            break;
     908        case SCSI_WRITE_BUFFER:
     909            switch (pbCdb[1] & 0x1f)
     910            {
     911                case 0x04: /* download microcode */
     912                case 0x05: /* download microcode and save */
     913                case 0x06: /* download microcode with offsets */
     914                case 0x07: /* download microcode with offsets and save */
     915                case 0x0e: /* download microcode with offsets and defer activation */
     916                case 0x0f: /* activate deferred microcode */
     917                    LogRel(("ATAPI: CD-ROM passthrough command attempted to update firmware, blocked\n"));
     918                    *pu8ScsiSts = atapiPassthroughCmdErrorSimple(pbSense, cbSense, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
     919                    break;
     920                default:
     921                    cbXfer = scsiBE2H_U16(pbCdb + 6);
     922                    enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
     923                    fPassthrough = true;
     924                    break;
     925            }
     926            break;
     927        case SCSI_REPORT_LUNS: /* Not part of MMC-3, but used by Windows. */
     928            cbXfer = scsiBE2H_U32(pbCdb + 6);
     929            enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
     930            fPassthrough = true;
     931            break;
     932        case SCSI_REZERO_UNIT:
     933            /* Obsolete command used by cdrecord. What else would one expect?
     934             * This command is not sent to the drive, it is handled internally,
     935             * as the Linux kernel doesn't like it (message "scsi: unknown
     936             * opcode 0x01" in syslog) and replies with a sense code of 0,
     937             * which sends cdrecord to an endless loop. */
     938            *pu8ScsiSts = atapiPassthroughCmdErrorSimple(pbSense, cbSense, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
     939            break;
     940        default:
     941            LogRel(("ATAPI: Passthrough unimplemented for command %#x\n", pbCdb[0]));
     942            *pu8ScsiSts = atapiPassthroughCmdErrorSimple(pbSense, cbSense, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
     943            break;
     944    }
     945
     946    if (fPassthrough)
     947    {
     948        *penmTxDir = enmTxDir;
     949        *pcbXfer   = cbXfer;
     950        *pcbSector = cbSector;
     951    }
     952
     953    return fPassthrough;
     954}
     955
  • trunk/src/VBox/Devices/Storage/ATAPIPassthrough.h

    r62506 r65964  
    1919
    2020#include <VBox/cdefs.h>
     21#include <VBox/vmm/pdmifs.h>
     22#include <VBox/vmm/pdmstorageifs.h>
    2123
    2224RT_C_DECLS_BEGIN
     
    7072DECLHIDDEN(uint32_t) ATAPIPassthroughTrackListGetSectorSizeFromLba(PTRACKLIST pTrackList, uint32_t iAtapiLba);
    7173
     74/**
     75 * Parses the given CDB and returns whether it is safe to pass it through to the host drive.
     76 *
     77 * @returns Flag whether passing the CDB through to the host drive is safe.
     78 * @param   pbCdb         The CDB to parse.
     79 * @param   cbCdb         Size of the CDB in bytes.
     80 * @param   cbBuf         Size of the guest buffer.
     81 * @param   pTrackList    The track list for the current medium if available (optional).
     82 * @param   pbSense       Pointer to the sense buffer.
     83 * @param   cbSense       Size of the sense buffer.
     84 * @param   penmTxDir     Where to store the transfer direction (guest to host or vice versa).
     85 * @param   pcbXfer       Where to store the transfer size encoded in the CDB.
     86 * @param   pcbSector     Where to store the sector size used for the transfer.
     87 * @param   pu8ScsiSts    Where to store the SCSI status code.
     88 */
     89DECLHIDDEN(bool) ATAPIPassthroughParseCdb(const uint8_t *pbCdb, size_t cbCdb, size_t cbBuf,
     90                                          PTRACKLIST pTrackList, uint8_t *pbSense, size_t cbSense,
     91                                          PDMMEDIATXDIR *penmTxDir, size_t *pcbXfer,
     92                                          size_t *pcbSector, uint8_t *pu8ScsiSts);
     93
    7294RT_C_DECLS_END
    7395
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