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/VSCSIDevice.cpp | 434 +++++++++++++++++++++++++ 1 file changed, 434 insertions(+) create mode 100644 src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp (limited to 'src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp') diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp b/src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp new file mode 100644 index 00000000..a2864b84 --- /dev/null +++ b/src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp @@ -0,0 +1,434 @@ +/* $Id: VSCSIDevice.cpp $ */ +/** @file + * Virtual SCSI driver: Device handling + */ + +/* + * 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" + +/** + * Checks if a specific LUN exists fir the SCSI device + * + * @returns true if the LUN is present + * false otherwise + * @param pVScsiDevice The SCSI device instance. + * @param iLun The LUN to check for. + */ +DECLINLINE(bool) vscsiDeviceLunIsPresent(PVSCSIDEVICEINT pVScsiDevice, uint32_t iLun) +{ + return ( iLun < pVScsiDevice->cLunsMax + && pVScsiDevice->papVScsiLun[iLun] != NULL); +} + +/** + * 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; + + vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I); + vscsiReqSetXferSize(pVScsiReq, RT_MIN(sizeof(SCSIINQUIRYDATA), scsiBE2H_U16(&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. + */ + vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I); + vscsiReqSetXferSize(pVScsiReq, scsiBE2H_U32(&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)); + scsiH2BE_U32(&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: + { + vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_NONE); + 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: + { + vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I); + 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 = scsiBE2H_U16(&pVScsiReq->pbCDB[4]); + uint16_t cbData = scsiBE2H_U16(&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; +} + + +void vscsiDeviceReqComplete(PVSCSIDEVICEINT pVScsiDevice, PVSCSIREQINT pVScsiReq, + int rcScsiCode, bool fRedoPossible, int rcReq) +{ + pVScsiDevice->pfnVScsiReqCompleted(pVScsiDevice, pVScsiDevice->pvVScsiDeviceUser, + pVScsiReq->pvVScsiReqUser, rcScsiCode, fRedoPossible, + rcReq, pVScsiReq->cbXfer, pVScsiReq->enmXferDir, pVScsiReq->cbSenseWritten); + + if (pVScsiReq->pvLun) + { + if (vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun)) + { + PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[pVScsiReq->iLun]; + pVScsiLun->pVScsiLunDesc->pfnVScsiLunReqFree(pVScsiLun, pVScsiReq, pVScsiReq->pvLun); + } + else + AssertLogRelMsgFailed(("vscsiDeviceReqComplete: LUN %u for VSCSI request %#p is not present but there is LUN specific data allocated\n", + pVScsiReq->iLun, pVScsiReq)); + + pVScsiReq->pvLun = NULL; + } + + RTMemCacheFree(pVScsiDevice->hCacheReq, pVScsiReq); +} + + +VBOXDDU_DECL(int) VSCSIDeviceCreate(PVSCSIDEVICE phVScsiDevice, + PFNVSCSIREQCOMPLETED pfnVScsiReqCompleted, + void *pvVScsiDeviceUser) +{ + int rc = VINF_SUCCESS; + PVSCSIDEVICEINT pVScsiDevice = NULL; + + AssertPtrReturn(phVScsiDevice, VERR_INVALID_POINTER); + AssertPtrReturn(pfnVScsiReqCompleted, VERR_INVALID_POINTER); + + pVScsiDevice = (PVSCSIDEVICEINT)RTMemAllocZ(sizeof(VSCSIDEVICEINT)); + if (!pVScsiDevice) + return VERR_NO_MEMORY; + + pVScsiDevice->pfnVScsiReqCompleted = pfnVScsiReqCompleted; + pVScsiDevice->pvVScsiDeviceUser = pvVScsiDeviceUser; + pVScsiDevice->cLunsAttached = 0; + pVScsiDevice->cLunsMax = 0; + pVScsiDevice->papVScsiLun = NULL; + vscsiSenseInit(&pVScsiDevice->VScsiSense); + + rc = RTMemCacheCreate(&pVScsiDevice->hCacheReq, sizeof(VSCSIREQINT), 0, UINT32_MAX, + NULL, NULL, NULL, 0); + if (RT_SUCCESS(rc)) + { + *phVScsiDevice = pVScsiDevice; + LogFlow(("%s: hVScsiDevice=%#p -> VINF_SUCCESS\n", __FUNCTION__, pVScsiDevice)); + return VINF_SUCCESS; + } + + RTMemFree(pVScsiDevice); + + return rc; +} + + +VBOXDDU_DECL(int) VSCSIDeviceDestroy(VSCSIDEVICE hVScsiDevice) +{ + AssertPtrReturn(hVScsiDevice, VERR_INVALID_HANDLE); + + PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice; + + if (pVScsiDevice->cLunsAttached > 0) + return VERR_VSCSI_LUN_ATTACHED_TO_DEVICE; + + if (pVScsiDevice->papVScsiLun) + RTMemFree(pVScsiDevice->papVScsiLun); + + RTMemCacheDestroy(pVScsiDevice->hCacheReq); + RTMemFree(pVScsiDevice); + + return VINF_SUCCESS;; +} + + +VBOXDDU_DECL(int) VSCSIDeviceLunAttach(VSCSIDEVICE hVScsiDevice, VSCSILUN hVScsiLun, uint32_t iLun) +{ + PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice; + PVSCSILUNINT pVScsiLun = (PVSCSILUNINT)hVScsiLun; + int rc = VINF_SUCCESS; + + /* Parameter checks */ + AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE); + AssertPtrReturn(pVScsiLun, VERR_INVALID_HANDLE); + AssertReturn(iLun < VSCSI_DEVICE_LUN_MAX, VERR_VSCSI_LUN_INVALID); + AssertReturn(!pVScsiLun->pVScsiDevice, VERR_VSCSI_LUN_ATTACHED_TO_DEVICE); + + if (iLun >= pVScsiDevice->cLunsMax) + { + PPVSCSILUNINT papLunOld = pVScsiDevice->papVScsiLun; + + pVScsiDevice->papVScsiLun = (PPVSCSILUNINT)RTMemAllocZ((iLun + 1) * sizeof(PVSCSILUNINT)); + if (pVScsiDevice->papVScsiLun) + { + for (uint32_t i = 0; i < pVScsiDevice->cLunsMax; i++) + pVScsiDevice->papVScsiLun[i] = papLunOld[i]; + + if (papLunOld) + RTMemFree(papLunOld); + + pVScsiDevice->cLunsMax = iLun + 1; + } + else + rc = VERR_NO_MEMORY; + } + + if (RT_SUCCESS(rc)) + { + pVScsiLun->pVScsiDevice = pVScsiDevice; + pVScsiDevice->papVScsiLun[iLun] = pVScsiLun; + pVScsiDevice->cLunsAttached++; + } + + return rc; +} + + +VBOXDDU_DECL(int) VSCSIDeviceLunDetach(VSCSIDEVICE hVScsiDevice, uint32_t iLun, + PVSCSILUN phVScsiLun) +{ + PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice; + + /* Parameter checks */ + AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE); + AssertPtrReturn(phVScsiLun, VERR_INVALID_POINTER); + AssertReturn(iLun < VSCSI_DEVICE_LUN_MAX, VERR_VSCSI_LUN_INVALID); + AssertReturn(iLun < pVScsiDevice->cLunsMax, VERR_VSCSI_LUN_NOT_ATTACHED); + AssertPtrReturn(pVScsiDevice->papVScsiLun[iLun], VERR_VSCSI_LUN_NOT_ATTACHED); + + PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[iLun]; + + pVScsiLun->pVScsiDevice = NULL; + *phVScsiLun = pVScsiLun; + pVScsiDevice->papVScsiLun[iLun] = NULL; + pVScsiDevice->cLunsAttached--; + + return VINF_SUCCESS; +} + + +VBOXDDU_DECL(int) VSCSIDeviceLunQueryType(VSCSIDEVICE hVScsiDevice, uint32_t iLun, + PVSCSILUNTYPE pEnmLunType) +{ + PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice; + + /* Parameter checks */ + AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE); + AssertPtrReturn(pEnmLunType, VERR_INVALID_POINTER); + AssertReturn(iLun < VSCSI_DEVICE_LUN_MAX, VERR_VSCSI_LUN_INVALID); + AssertReturn(iLun < pVScsiDevice->cLunsMax, VERR_VSCSI_LUN_NOT_ATTACHED); + AssertPtrReturn(pVScsiDevice->papVScsiLun[iLun], VERR_VSCSI_LUN_NOT_ATTACHED); + + PVSCSILUNINT hVScsiLun = pVScsiDevice->papVScsiLun[iLun]; + *pEnmLunType = hVScsiLun->pVScsiLunDesc->enmLunType; + + return VINF_SUCCESS; +} + + +VBOXDDU_DECL(int) VSCSIDeviceReqEnqueue(VSCSIDEVICE hVScsiDevice, VSCSIREQ hVScsiReq) +{ + PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice; + PVSCSIREQINT pVScsiReq = (PVSCSIREQINT)hVScsiReq; + int rc = VINF_SUCCESS; + + /* Parameter checks */ + AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE); + AssertPtrReturn(pVScsiReq, VERR_INVALID_HANDLE); + + /* Check if this request can be handled by us */ + int rcReq; + bool fProcessed = vscsiDeviceReqProcess(pVScsiDevice, pVScsiReq, &rcReq); + if (!fProcessed) + { + /* Pass to the LUN driver */ + if (vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun)) + { + PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[pVScsiReq->iLun]; + rc = pVScsiLun->pVScsiLunDesc->pfnVScsiLunReqProcess(pVScsiLun, pVScsiReq); + } + else + { + /* LUN not present, report error. */ + vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, + SCSI_SENSE_ILLEGAL_REQUEST, + SCSI_ASC_LOGICAL_UNIT_DOES_NOT_RESPOND_TO_SELECTION, + 0x00); + + vscsiDeviceReqComplete(pVScsiDevice, pVScsiReq, + SCSI_STATUS_CHECK_CONDITION, false, VINF_SUCCESS); + } + } + else + vscsiDeviceReqComplete(pVScsiDevice, pVScsiReq, + rcReq, false, VINF_SUCCESS); + + return rc; +} + + +VBOXDDU_DECL(int) VSCSIDeviceReqCreate(VSCSIDEVICE hVScsiDevice, PVSCSIREQ phVScsiReq, + uint32_t iLun, uint8_t *pbCDB, size_t cbCDB, + size_t cbSGList, unsigned cSGListEntries, + PCRTSGSEG paSGList, uint8_t *pbSense, + size_t cbSense, void *pvVScsiReqUser) +{ + RT_NOREF1(cbSGList); + PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice; + PVSCSIREQINT pVScsiReq = NULL; + + /* Parameter checks */ + AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE); + AssertPtrReturn(phVScsiReq, VERR_INVALID_POINTER); + AssertPtrReturn(pbCDB, VERR_INVALID_PARAMETER); + AssertReturn(cbCDB > 0, VERR_INVALID_PARAMETER); + + pVScsiReq = (PVSCSIREQINT)RTMemCacheAlloc(pVScsiDevice->hCacheReq); + if (!pVScsiReq) + return VERR_NO_MEMORY; + + pVScsiReq->iLun = iLun; + pVScsiReq->pbCDB = pbCDB; + pVScsiReq->cbCDB = cbCDB; + pVScsiReq->pbSense = pbSense; + pVScsiReq->cbSense = cbSense; + pVScsiReq->pvVScsiReqUser = pvVScsiReqUser; + pVScsiReq->cbXfer = 0; + pVScsiReq->pvLun = NULL; + pVScsiReq->enmXferDir = VSCSIXFERDIR_UNKNOWN; + pVScsiReq->cbSenseWritten = 0; + RTSgBufInit(&pVScsiReq->SgBuf, paSGList, cSGListEntries); + + *phVScsiReq = pVScsiReq; + + return VINF_SUCCESS; +} -- cgit v1.2.3