VBOXDDU_DECL(int) VSCSIIoReqCompleted(VSCSIIOREQ hVScsiIoReq, int rcIoReq, bool fRedoPossible) { PVSCSIIOREQINT pVScsiIoReq = hVScsiIoReq; PVSCSILUNINT pVScsiLun; PVSCSIREQINT pVScsiReq; int rcReq = SCSI_STATUS_OK; AssertPtrReturn(pVScsiIoReq, VERR_INVALID_HANDLE); LogFlowFunc(("hVScsiIoReq=%#p rcIoReq=%Rrc\n", hVScsiIoReq, rcIoReq)); pVScsiLun = pVScsiIoReq->pVScsiLun; pVScsiReq = pVScsiIoReq->pVScsiReq; AssertMsg(pVScsiLun->IoReq.cReqOutstanding > 0, ("Unregistered I/O request completed\n")); ASMAtomicDecU32(&pVScsiLun->IoReq.cReqOutstanding); if (RT_SUCCESS(rcIoReq)) rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); else if (!fRedoPossible) { /** @todo Not 100% correct for the write case as the 0x00 ASCQ for write errors * is not used for SBC devices. */ rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_MEDIUM_ERROR, pVScsiIoReq->enmTxDir == VSCSIIOREQTXDIR_READ ? SCSI_ASC_READ_ERROR : SCSI_ASC_WRITE_ERROR, 0x00); } else rcReq = SCSI_STATUS_CHECK_CONDITION; if (pVScsiIoReq->enmTxDir == VSCSIIOREQTXDIR_UNMAP) RTMemFree(pVScsiIoReq->u.Unmap.paRanges); /* Free the I/O request */ RTMemFree(pVScsiIoReq); /* Notify completion of the SCSI request. */ vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, fRedoPossible, rcIoReq); return VINF_SUCCESS; }
static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq) { PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun; VSCSIIOREQTXDIR enmTxDir = VSCSIIOREQTXDIR_INVALID; uint64_t uLbaStart = 0; uint32_t cSectorTransfer = 0; int rc = VINF_SUCCESS; int rcReq = SCSI_STATUS_OK; switch(pVScsiReq->pbCDB[0]) { case SCSI_INQUIRY: { SCSIINQUIRYDATA ScsiInquiryReply; memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply)); ScsiInquiryReply.cbAdditional = 31; ScsiInquiryReply.fRMB = 1; /* Removable. */ ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_CD_DVD; ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED; ScsiInquiryReply.u3AnsiVersion = 0x05; /* MMC-?? compliant */ ScsiInquiryReply.fCmdQue = 1; /* Command queuing supported. */ ScsiInquiryReply.fWBus16 = 1; vscsiPadStr(ScsiInquiryReply.achVendorId, "VBOX", 8); vscsiPadStr(ScsiInquiryReply.achProductId, "CD-ROM", 16); vscsiPadStr(ScsiInquiryReply.achProductLevel, "1.0", 4); RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA)); rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); break; } case SCSI_READ_CAPACITY: { uint8_t aReply[8]; memset(aReply, 0, sizeof(aReply)); /* * If sector size exceeds the maximum value that is * able to be stored in 4 bytes return 0xffffffff in this field */ if (pVScsiLunMmc->cSectors > UINT32_C(0xffffffff)) vscsiH2BEU32(aReply, UINT32_C(0xffffffff)); else vscsiH2BEU32(aReply, pVScsiLunMmc->cSectors - 1); vscsiH2BEU32(&aReply[4], pVScsiLunMmc->cbSector); RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply)); rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); break; } case SCSI_MODE_SENSE_6: { uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f; uint8_t aReply[24]; uint8_t *pu8ReplyPos; memset(aReply, 0, sizeof(aReply)); aReply[0] = 4; /* Reply length 4. */ aReply[1] = 0; /* Default media type. */ aReply[2] = RT_BIT(4); /* Caching supported. */ aReply[3] = 0; /* Block descriptor length. */ pu8ReplyPos = aReply + 4; if ((uModePage == 0x08) || (uModePage == 0x3f)) { memset(pu8ReplyPos, 0, 20); *pu8ReplyPos++ = 0x08; /* Page code. */ *pu8ReplyPos++ = 0x12; /* Size of the page. */ *pu8ReplyPos++ = 0x4; /* Write cache enabled. */ } RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply)); rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); break; } case SCSI_MODE_SELECT_6: { /* @todo: implement!! */ rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); break; } case SCSI_READ_6: { enmTxDir = VSCSIIOREQTXDIR_READ; uLbaStart = ((uint64_t) pVScsiReq->pbCDB[3] | (pVScsiReq->pbCDB[2] << 8) | ((pVScsiReq->pbCDB[1] & 0x1f) << 16)); cSectorTransfer = pVScsiReq->pbCDB[4]; break; } case SCSI_READ_10: { enmTxDir = VSCSIIOREQTXDIR_READ; uLbaStart = vscsiBE2HU32(&pVScsiReq->pbCDB[2]); cSectorTransfer = vscsiBE2HU16(&pVScsiReq->pbCDB[7]); break; } case SCSI_READ_12: { enmTxDir = VSCSIIOREQTXDIR_READ; uLbaStart = vscsiBE2HU32(&pVScsiReq->pbCDB[2]); cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[6]); break; } case SCSI_READ_16: { enmTxDir = VSCSIIOREQTXDIR_READ; uLbaStart = vscsiBE2HU64(&pVScsiReq->pbCDB[2]); cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[10]); break; } case SCSI_READ_BUFFER: { uint8_t uDataMode = pVScsiReq->pbCDB[1] & 0x1f; switch (uDataMode) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x0a: break; case 0x0b: { uint8_t aReply[4]; /* We do not implement an echo buffer. */ memset(aReply, 0, sizeof(aReply)); RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply)); rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); break; } case 0x1a: case 0x1c: break; default: AssertMsgFailed(("Invalid data mode\n")); } break; } case SCSI_VERIFY_10: case SCSI_START_STOP_UNIT: { rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); break; } case SCSI_LOG_SENSE: { uint16_t cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]); uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f; uint8_t uSubPageCode = pVScsiReq->pbCDB[3]; switch (uPageCode) { case 0x00: { if (uSubPageCode == 0) { uint8_t aReply[4]; aReply[0] = 0; aReply[1] = 0; aReply[2] = 0; aReply[3] = 0; RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply)); rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); break; } } default: rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); } break; } case SCSI_SERVICE_ACTION_IN_16: { switch (pVScsiReq->pbCDB[1] & 0x1f) { case SCSI_SVC_ACTION_IN_READ_CAPACITY_16: { uint8_t aReply[32]; memset(aReply, 0, sizeof(aReply)); vscsiH2BEU64(aReply, pVScsiLunMmc->cSectors - 1); vscsiH2BEU32(&aReply[8], pVScsiLunMmc->cbSector); /* Leave the rest 0 */ RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply)); rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); break; } default: rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); /* Don't know if this is correct */ } break; } case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL: { pVScsiLunMmc->fLocked = pVScsiReq->pbCDB[4] & 1; rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); break; } default: //AssertMsgFailed(("Command %#x [%s] not implemented\n", pRequest->pbCDB[0], SCSICmdText(pRequest->pbCDB[0]))); rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00); } if (enmTxDir != VSCSIIOREQTXDIR_INVALID) { LogFlow(("%s: uLbaStart=%llu cSectorTransfer=%u\n", __FUNCTION__, uLbaStart, cSectorTransfer)); if (RT_UNLIKELY(uLbaStart + cSectorTransfer > pVScsiLunMmc->cSectors)) { rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00); vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS); } else if (!cSectorTransfer) { /* A 0 transfer length is not an error. */ rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS); } else { /* Enqueue new I/O request */ rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir, uLbaStart * pVScsiLunMmc->cbSector, cSectorTransfer * pVScsiLunMmc->cbSector); } } else /* Request completed */ vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS); return rc; }
/** * Process a request common for all device types. * * @returns Flag whether we could handle the request. * @param pVScsiDevice The virtual SCSI device instance. * @param pVScsiReq The SCSi request. * @param prcReq The final return value if the request was handled. */ static bool vscsiDeviceReqProcess(PVSCSIDEVICEINT pVScsiDevice, PVSCSIREQINT pVScsiReq, int *prcReq) { bool fProcessed = true; switch (pVScsiReq->pbCDB[0]) { case SCSI_INQUIRY: { if (!vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun)) { size_t cbData; SCSIINQUIRYDATA ScsiInquiryReply; vscsiReqSetXferSize(pVScsiReq, vscsiBE2HU16(&pVScsiReq->pbCDB[3])); memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply)); ScsiInquiryReply.cbAdditional = 31; ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN; ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED; cbData = RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA)); *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq); } else fProcessed = false; /* Let the LUN process the request because it will provide LUN specific data */ break; } case SCSI_REPORT_LUNS: { /* * If allocation length is less than 16 bytes SPC compliant devices have * to return an error. */ vscsiReqSetXferSize(pVScsiReq, vscsiBE2HU32(&pVScsiReq->pbCDB[6])); if (pVScsiReq->cbXfer < 16) *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); else { size_t cbData; uint8_t aReply[16]; /* We report only one LUN. */ memset(aReply, 0, sizeof(aReply)); vscsiH2BEU32(&aReply[0], 8); /* List length starts at position 0. */ cbData = RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply)); if (cbData < 16) *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); else *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq); } break; } case SCSI_TEST_UNIT_READY: { if ( vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun) && pVScsiDevice->papVScsiLun[pVScsiReq->iLun]->fReady) *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq); else fProcessed = false; /* The LUN (if present) will provide details. */ break; } case SCSI_REQUEST_SENSE: { vscsiReqSetXferSize(pVScsiReq, pVScsiReq->pbCDB[4]); /* Descriptor format sense data is not supported and results in an error. */ if ((pVScsiReq->pbCDB[1] & 0x1) != 0) *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); else *prcReq = vscsiReqSenseCmd(&pVScsiDevice->VScsiSense, pVScsiReq); break; } #if 0 case SCSI_MAINTENANCE_IN: { if (pVScsiReq->pbCDB[1] == SCSI_MAINTENANCE_IN_REPORT_SUPP_OPC) { /* * If the LUN is present and has the CDB info set we will execute the command, otherwise * just fail with an illegal request error. */ if (vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun)) { PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[pVScsiReq->iLun]; if (pVScsiLun->pVScsiLunDesc->paSupOpcInfo) { bool fTimeoutDesc = RT_BOOL(pVScsiReq->pbCDB[2] & 0x80); uint8_t u8ReportMode = pVScsiReq->pbCDB[2] & 0x7; uint8_t u8Opc = pVScsiReq->pbCDB[3]; uint16_t u16SvcAction = vscsiBE2HU16(&pVScsiReq->pbCDB[4]); uint16_t cbData = vscsiBE2HU16(&pVScsiReq->pbCDB[6]); switch (u8ReportMode) { case 0: *prcReq = vscsiDeviceReportAllSupportedOpc(pVScsiLun, pVScsiReq, fTimeoutDesc, cbData); break; case 1: *prcReq = vscsiDeviceReportOpc(pVScsiLun, pVScsiReq, u8Opc, fTimeoutDesc, cbData); break; case 2: *prcReq = vscsiDeviceReportOpc(pVScsiLun, pVScsiReq, u8Opc, fTimeoutDesc, cbData); break; default: *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); } } else *prcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00); } else *prcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00); } else fProcessed = false; /* Might also be the SEND KEY MMC command. */ } #endif default: fProcessed = false; } return fProcessed; }