From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Devices/Storage/VSCSI/VSCSILunSsc.cpp | 469 +++++++++++++++++++++++++ 1 file changed, 469 insertions(+) create mode 100644 src/VBox/Devices/Storage/VSCSI/VSCSILunSsc.cpp (limited to 'src/VBox/Devices/Storage/VSCSI/VSCSILunSsc.cpp') diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSILunSsc.cpp b/src/VBox/Devices/Storage/VSCSI/VSCSILunSsc.cpp new file mode 100644 index 00000000..4c659a2d --- /dev/null +++ b/src/VBox/Devices/Storage/VSCSI/VSCSILunSsc.cpp @@ -0,0 +1,469 @@ +/* $Id: VSCSILunSsc.cpp $ */ +/** @file + * Virtual SCSI driver: SSC LUN implementation (Streaming tape) + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_VSCSI +#include +#include +#include +#include +#include +#include +#include + +#include "VSCSIInternal.h" + +/** + * SSC LUN instance + */ +typedef struct VSCSILUNSSC +{ + /** Core LUN structure */ + VSCSILUNINT Core; + /** Size of the virtual tape. */ + uint64_t cbTape; + /** Current position. */ + uint64_t uCurPos; + /** Number of blocks. */ + uint64_t cBlocks; + /** Block size. */ + uint64_t cbBlock; + /** Medium locked indicator. */ + bool fLocked; +} VSCSILUNSSC, *PVSCSILUNSSC; + + +static int vscsiLUNSSCInit(PVSCSILUNINT pVScsiLun) +{ + PVSCSILUNSSC pVScsiLunSsc = (PVSCSILUNSSC)pVScsiLun; + int rc = VINF_SUCCESS; + + pVScsiLunSsc->cbBlock = 512; /* Default to 512-byte blocks. */ + pVScsiLunSsc->uCurPos = 0; /* Start at beginning of tape. */ + pVScsiLunSsc->cbTape = 0; + + uint32_t cRegions = vscsiLunMediumGetRegionCount(pVScsiLun); + if (cRegions != 1) + rc = VERR_INVALID_PARAMETER; + + if (RT_SUCCESS(rc)) + rc = vscsiLunMediumQueryRegionProperties(pVScsiLun, 0, NULL, &pVScsiLunSsc->cBlocks, + &pVScsiLunSsc->cbBlock, NULL); + + if (RT_SUCCESS(rc)) + pVScsiLunSsc->cbTape = pVScsiLunSsc->cBlocks * pVScsiLunSsc->cbBlock; + + return rc; +} + +static int vscsiLUNSSCDestroy(PVSCSILUNINT pVScsiLun) +{ + PVSCSILUNSSC pVScsiLUNSSC = (PVSCSILUNSSC)pVScsiLun; + + pVScsiLUNSSC->uCurPos = 0; // shut compiler up + + return VINF_SUCCESS; +} + +static int vscsiLUNSSCReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq) +{ + PVSCSILUNSSC pVScsiLUNSSC = (PVSCSILUNSSC)pVScsiLun; + VSCSIIOREQTXDIR enmTxDir = VSCSIIOREQTXDIR_INVALID; + uint64_t uTransferStart = 0; + uint32_t cBlocksTransfer = 0; + uint32_t cbTransfer = 0; + int rc = VINF_SUCCESS; + int rcReq = SCSI_STATUS_OK; + unsigned uCmd = pVScsiReq->pbCDB[0]; + + /* + * GET CONFIGURATION, GET EVENT/STATUS NOTIFICATION, INQUIRY, and REQUEST SENSE commands + * operate even when a unit attention condition exists for initiator; every other command + * needs to report CHECK CONDITION in that case. + */ + if (!pVScsiLUNSSC->Core.fReady && uCmd != SCSI_INQUIRY) + { + /* + * A note on media changes: As long as a medium is not present, the unit remains in + * the 'not ready' state. Technically the unit becomes 'ready' soon after a medium + * is inserted; however, we internally keep the 'not ready' state until we've had + * a chance to report the UNIT ATTENTION status indicating a media change. + */ + if (pVScsiLUNSSC->Core.fMediaPresent) + { + rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_UNIT_ATTENTION, + SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED, 0x00); + pVScsiLUNSSC->Core.fReady = true; + } + else + rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY, + SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00); + } + else + { + switch (uCmd) + { + case SCSI_TEST_UNIT_READY: + Assert(!pVScsiLUNSSC->Core.fReady); /* Only should get here if LUN isn't ready. */ + rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00); + break; + + 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_SEQUENTIAL_ACCESS; + ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED; + ScsiInquiryReply.u3AnsiVersion = 0x05; /* SSC-?? compliant */ + ScsiInquiryReply.fCmdQue = 1; /* Command queuing supported. */ + ScsiInquiryReply.fWBus16 = 1; + scsiPadStrS(ScsiInquiryReply.achVendorId, "VBOX", 8); + scsiPadStrS(ScsiInquiryReply.achProductId, "TAPE DRIVE", 16); + scsiPadStrS(ScsiInquiryReply.achProductLevel, "1.0", 4); + + RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA)); + rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); + break; + } + case SCSI_MODE_SENSE_6: + { +// uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f; + uint8_t aReply[24]; + uint8_t *pu8ReplyPos; + uint8_t uReplyLen; + bool fWantBlkDesc = !!(pVScsiReq->pbCDB[1] & RT_BIT(3)); /* DBD bit. */ + + memset(aReply, 0, sizeof(aReply)); + if (fWantBlkDesc) + uReplyLen = 4 + 8; + else + uReplyLen = 4; + + aReply[0] = uReplyLen - 1; /* Reply length. */ + aReply[1] = 0xB6; /* Travan TR-4 medium (whatever). */ + aReply[2] = 0; //RT_BIT(7); /* Write Protected. */ //@todo! + aReply[3] = uReplyLen - 4; /* Block descriptor length. */ + + pu8ReplyPos = aReply + 4; + +#if 0 + if ((uModePage == 0x08) || (uModePage == 0x3f)) + { + memset(pu8ReplyPos, 0, 20); + *pu8ReplyPos++ = 0x08; /* Page code. */ + *pu8ReplyPos++ = 0x12; /* Size of the page. */ + *pu8ReplyPos++ = 0x4; /* Write cache enabled. */ + } +#endif + + /* Fill out the Block Descriptor. */ + if (fWantBlkDesc) + { + *pu8ReplyPos++ = 0x45; /* Travan TR-4 density. */ + *pu8ReplyPos++ = 0; /* All blocks are the same. */ + *pu8ReplyPos++ = 0; /// @todo this calls for some macros! + *pu8ReplyPos++ = 0; + *pu8ReplyPos++ = 0; /* Reserved. */ + *pu8ReplyPos++ = 0x00; /* Block length (512). */ + *pu8ReplyPos++ = 0x02; + *pu8ReplyPos++ = 0x00; + } + + 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; + cbTransfer = ((uint32_t) pVScsiReq->pbCDB[4] + | (pVScsiReq->pbCDB[3] << 8) + | (pVScsiReq->pbCDB[2] << 16)); + cBlocksTransfer = pVScsiReq->pbCDB[4]; + uTransferStart = pVScsiLUNSSC->uCurPos; + pVScsiLUNSSC->uCurPos += cbTransfer; + break; + } + case SCSI_WRITE_6: + { + enmTxDir = VSCSIIOREQTXDIR_WRITE; + cbTransfer = ((uint32_t) pVScsiReq->pbCDB[4] + | (pVScsiReq->pbCDB[3] << 8) + | (pVScsiReq->pbCDB[2] << 16)); + cBlocksTransfer = pVScsiReq->pbCDB[4]; + uTransferStart = pVScsiLUNSSC->uCurPos; + pVScsiLUNSSC->uCurPos += cbTransfer; + 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_LOAD_UNLOAD: + { + /// @todo should load/unload do anyhting? is verify even supported? + rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); + break; + } + case SCSI_LOG_SENSE: + { + 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) + { + 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: + { + pVScsiLUNSSC->fLocked = pVScsiReq->pbCDB[4] & 1; + vscsiLunMediumSetLock(pVScsiLun, pVScsiLUNSSC->fLocked); + rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); + break; + } + case SCSI_REWIND: + /// @todo flush data + write EOD? immed bit? partitions? + pVScsiLUNSSC->uCurPos = 0; + rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); + break; + case SCSI_RESERVE_6: + /// @todo perform actual reservation + rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); + break; + case SCSI_RELEASE_6: + /// @todo perform actual release + rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); + break; + case SCSI_READ_BLOCK_LIMITS: + { + uint8_t aReply[6]; + + /* Report unrestricted block sizes (1-FFFFFFh). */ + memset(aReply, 0, sizeof(aReply)); + /// @todo Helpers for big-endian 16-bit/24-bit/32-bit constants? + aReply[1] = aReply[2] = aReply[3] = 0xff; + aReply[5] = 0x01; + RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply)); + rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq); + break; + } + default: + //AssertMsgFailed(("Command %#x [%s] not implemented\n", pVScsiReq->pbCDB[0], SCSICmdText(pVScsiReq->pbCDB[0]))); + rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00); + } + } + + if (enmTxDir != VSCSIIOREQTXDIR_INVALID) + { + LogFlow(("%s: uTransferStart=%llu cbTransfer=%u\n", + __FUNCTION__, uTransferStart, cbTransfer)); + + if (RT_UNLIKELY(uTransferStart + cbTransfer > pVScsiLUNSSC->cbTape)) + { + uint64_t cbResidue = uTransferStart + cbTransfer - pVScsiLUNSSC->cbTape; + + if (enmTxDir == VSCSIIOREQTXDIR_READ && cbResidue < cbTransfer) + { + /* If it's a read and some data is still available, read what we can. */ + rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir, + uTransferStart, cbTransfer - cbResidue); + rcReq = vscsiLunReqSenseErrorInfoSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NONE | SCSI_SENSE_FLAG_FILEMARK, SCSI_ASC_NONE, SCSI_ASCQ_FILEMARK_DETECTED, cbResidue); + } + else + { +// rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_, SCSI_ASC_NONE, SCSI_ASCQ_END_OF_DATA_DETECTED); + /* Report a file mark. */ + rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NONE | SCSI_SENSE_FLAG_FILEMARK, SCSI_ASC_NONE, SCSI_ASCQ_FILEMARK_DETECTED); + vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS); + } + } + else if (!cbTransfer) + { + /* 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, + uTransferStart, cbTransfer); + } + } + else /* Request completed */ + vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS); + + return rc; +} + +/** @interface_method_impl{VSCSILUNDESC,pfnVScsiLunMediumInserted} */ +static DECLCALLBACK(int) vscsiLunSSCMediumInserted(PVSCSILUNINT pVScsiLun) +{ + int rc = VINF_SUCCESS; + uint32_t cRegions = vscsiLunMediumGetRegionCount(pVScsiLun); + if (cRegions != 1) + rc = VERR_INVALID_PARAMETER; + + if (RT_SUCCESS(rc)) + { +#if 0 + PVSCSILUNSSC pVScsiLUNSSC = (PVSCSILUNSSC)pVScsiLun; + + pVScsiLUNSSC->cSectors = cbDisk / pVScsiLUNSSC->cbSector; + + uint32_t OldStatus, NewStatus; + do + { + OldStatus = ASMAtomicReadU32((volatile uint32_t *)&pVScsiLUNSSC->MediaEventStatus); + switch (OldStatus) + { + case MMCEVENTSTATUSTYPE_MEDIA_CHANGED: + case MMCEVENTSTATUSTYPE_MEDIA_REMOVED: + /* no change, we will send "medium removed" + "medium inserted" */ + NewStatus = MMCEVENTSTATUSTYPE_MEDIA_CHANGED; + break; + default: + NewStatus = MMCEVENTSTATUSTYPE_MEDIA_NEW; + break; + } + } while (!ASMAtomicCmpXchgU32((volatile uint32_t *)&pVScsiLUNSSC->MediaEventStatus, + NewStatus, OldStatus)); + + ASMAtomicXchgU32(&pVScsiLUNSSC->u32MediaTrackType, MMC_MEDIA_TYPE_UNKNOWN); +#endif + } + + return rc; +} + +/** @interface_method_impl{VSCSILUNDESC,pfnVScsiLunMediumRemoved} */ +static DECLCALLBACK(int) vscsiLunSSCMediumRemoved(PVSCSILUNINT pVScsiLun) +{ + NOREF(pVScsiLun); +#if 0 + PVSCSILUNSSC pVScsiLUNSSC = (PVSCSILUNSSC)pVScsiLun; + + ASMAtomicWriteU32((volatile uint32_t *)&pVScsiLUNSSC->MediaEventStatus, MMCEVENTSTATUSTYPE_MEDIA_REMOVED); + ASMAtomicXchgU32(&pVScsiLUNSSC->u32MediaTrackType, MMC_MEDIA_TYPE_NO_DISC); +#endif + return VINF_SUCCESS; +} + +VSCSILUNDESC g_VScsiLunTypeSsc = +{ + /** enmLunType */ + VSCSILUNTYPE_SSC, + /** pcszDescName */ + "SSC", + /** cbLun */ + sizeof(VSCSILUNSSC), + /** cSupOpcInfo */ + 0, + /** paSupOpcInfo */ + NULL, + /** pfnVScsiLunInit */ + vscsiLUNSSCInit, + /** pfnVScsiLunDestroy */ + vscsiLUNSSCDestroy, + /** pfnVScsiLunReqProcess */ + vscsiLUNSSCReqProcess, + /** pfnVScsiLunReqFree */ + NULL, + /** pfnVScsiLunMediumInserted */ + vscsiLunSSCMediumInserted, + /** pfnVScsiLunMediumRemoved */ + vscsiLunSSCMediumRemoved +}; -- cgit v1.2.3