VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp@ 98110

Last change on this file since 98110 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.5 KB
Line 
1/* $Id: VSCSIDevice.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * Virtual SCSI driver: Device handling
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_VSCSI
33#include <VBox/log.h>
34#include <VBox/err.h>
35#include <VBox/types.h>
36#include <VBox/vscsi.h>
37#include <iprt/assert.h>
38#include <iprt/mem.h>
39#include <iprt/string.h>
40
41#include "VSCSIInternal.h"
42
43/**
44 * Checks if a specific LUN exists fir the SCSI device
45 *
46 * @returns true if the LUN is present
47 * false otherwise
48 * @param pVScsiDevice The SCSI device instance.
49 * @param iLun The LUN to check for.
50 */
51DECLINLINE(bool) vscsiDeviceLunIsPresent(PVSCSIDEVICEINT pVScsiDevice, uint32_t iLun)
52{
53 return ( iLun < pVScsiDevice->cLunsMax
54 && pVScsiDevice->papVScsiLun[iLun] != NULL);
55}
56
57/**
58 * Process a request common for all device types.
59 *
60 * @returns Flag whether we could handle the request.
61 * @param pVScsiDevice The virtual SCSI device instance.
62 * @param pVScsiReq The SCSi request.
63 * @param prcReq The final return value if the request was handled.
64 */
65static bool vscsiDeviceReqProcess(PVSCSIDEVICEINT pVScsiDevice, PVSCSIREQINT pVScsiReq,
66 int *prcReq)
67{
68 bool fProcessed = true;
69
70 switch (pVScsiReq->pbCDB[0])
71 {
72 case SCSI_INQUIRY:
73 {
74 if (!vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun))
75 {
76 size_t cbData;
77 SCSIINQUIRYDATA ScsiInquiryReply;
78
79 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
80 vscsiReqSetXferSize(pVScsiReq, RT_MIN(sizeof(SCSIINQUIRYDATA), scsiBE2H_U16(&pVScsiReq->pbCDB[3])));
81 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
82 ScsiInquiryReply.cbAdditional = 31;
83 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
84 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
85 cbData = RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
86 *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
87 }
88 else
89 fProcessed = false; /* Let the LUN process the request because it will provide LUN specific data */
90
91 break;
92 }
93 case SCSI_REPORT_LUNS:
94 {
95 /*
96 * If allocation length is less than 16 bytes SPC compliant devices have
97 * to return an error.
98 */
99 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
100 vscsiReqSetXferSize(pVScsiReq, scsiBE2H_U32(&pVScsiReq->pbCDB[6]));
101 if (pVScsiReq->cbXfer < 16)
102 *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
103 else
104 {
105 size_t cbData;
106 uint8_t aReply[16]; /* We report only one LUN. */
107
108 memset(aReply, 0, sizeof(aReply));
109 scsiH2BE_U32(&aReply[0], 8); /* List length starts at position 0. */
110 cbData = RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
111 if (cbData < 16)
112 *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
113 else
114 *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
115 }
116 break;
117 }
118 case SCSI_TEST_UNIT_READY:
119 {
120 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_NONE);
121 if ( vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun)
122 && pVScsiDevice->papVScsiLun[pVScsiReq->iLun]->fReady)
123 *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
124 else
125 fProcessed = false; /* The LUN (if present) will provide details. */
126 break;
127 }
128 case SCSI_REQUEST_SENSE:
129 {
130 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
131 vscsiReqSetXferSize(pVScsiReq, pVScsiReq->pbCDB[4]);
132
133 /* Descriptor format sense data is not supported and results in an error. */
134 if ((pVScsiReq->pbCDB[1] & 0x1) != 0)
135 *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
136 else
137 *prcReq = vscsiReqSenseCmd(&pVScsiDevice->VScsiSense, pVScsiReq);
138 break;
139 }
140#if 0
141 case SCSI_MAINTENANCE_IN:
142 {
143 if (pVScsiReq->pbCDB[1] == SCSI_MAINTENANCE_IN_REPORT_SUPP_OPC)
144 {
145 /*
146 * If the LUN is present and has the CDB info set we will execute the command, otherwise
147 * just fail with an illegal request error.
148 */
149 if (vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun))
150 {
151 PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[pVScsiReq->iLun];
152 if (pVScsiLun->pVScsiLunDesc->paSupOpcInfo)
153 {
154 bool fTimeoutDesc = RT_BOOL(pVScsiReq->pbCDB[2] & 0x80);
155 uint8_t u8ReportMode = pVScsiReq->pbCDB[2] & 0x7;
156 uint8_t u8Opc = pVScsiReq->pbCDB[3];
157 uint16_t u16SvcAction = scsiBE2H_U16(&pVScsiReq->pbCDB[4]);
158 uint16_t cbData = scsiBE2H_U16(&pVScsiReq->pbCDB[6]);
159
160 switch (u8ReportMode)
161 {
162 case 0:
163 *prcReq = vscsiDeviceReportAllSupportedOpc(pVScsiLun, pVScsiReq, fTimeoutDesc, cbData);
164 break;
165 case 1:
166 *prcReq = vscsiDeviceReportOpc(pVScsiLun, pVScsiReq, u8Opc, fTimeoutDesc, cbData);
167 break;
168 case 2:
169 *prcReq = vscsiDeviceReportOpc(pVScsiLun, pVScsiReq, u8Opc, fTimeoutDesc, cbData);
170 break;
171 default:
172 *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq,
173 SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
174 }
175 }
176 else
177 *prcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
178 }
179 else
180 *prcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
181 }
182 else
183 fProcessed = false; /* Might also be the SEND KEY MMC command. */
184 }
185#endif
186 default:
187 fProcessed = false;
188 }
189
190 return fProcessed;
191}
192
193
194void vscsiDeviceReqComplete(PVSCSIDEVICEINT pVScsiDevice, PVSCSIREQINT pVScsiReq,
195 int rcScsiCode, bool fRedoPossible, int rcReq)
196{
197 pVScsiDevice->pfnVScsiReqCompleted(pVScsiDevice, pVScsiDevice->pvVScsiDeviceUser,
198 pVScsiReq->pvVScsiReqUser, rcScsiCode, fRedoPossible,
199 rcReq, pVScsiReq->cbXfer, pVScsiReq->enmXferDir, pVScsiReq->cbSenseWritten);
200
201 if (pVScsiReq->pvLun)
202 {
203 if (vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun))
204 {
205 PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[pVScsiReq->iLun];
206 pVScsiLun->pVScsiLunDesc->pfnVScsiLunReqFree(pVScsiLun, pVScsiReq, pVScsiReq->pvLun);
207 }
208 else
209 AssertLogRelMsgFailed(("vscsiDeviceReqComplete: LUN %u for VSCSI request %#p is not present but there is LUN specific data allocated\n",
210 pVScsiReq->iLun, pVScsiReq));
211
212 pVScsiReq->pvLun = NULL;
213 }
214
215 RTMemCacheFree(pVScsiDevice->hCacheReq, pVScsiReq);
216}
217
218
219VBOXDDU_DECL(int) VSCSIDeviceCreate(PVSCSIDEVICE phVScsiDevice,
220 PFNVSCSIREQCOMPLETED pfnVScsiReqCompleted,
221 void *pvVScsiDeviceUser)
222{
223 int rc = VINF_SUCCESS;
224 PVSCSIDEVICEINT pVScsiDevice = NULL;
225
226 AssertPtrReturn(phVScsiDevice, VERR_INVALID_POINTER);
227 AssertPtrReturn(pfnVScsiReqCompleted, VERR_INVALID_POINTER);
228
229 pVScsiDevice = (PVSCSIDEVICEINT)RTMemAllocZ(sizeof(VSCSIDEVICEINT));
230 if (!pVScsiDevice)
231 return VERR_NO_MEMORY;
232
233 pVScsiDevice->pfnVScsiReqCompleted = pfnVScsiReqCompleted;
234 pVScsiDevice->pvVScsiDeviceUser = pvVScsiDeviceUser;
235 pVScsiDevice->cLunsAttached = 0;
236 pVScsiDevice->cLunsMax = 0;
237 pVScsiDevice->papVScsiLun = NULL;
238 vscsiSenseInit(&pVScsiDevice->VScsiSense);
239
240 rc = RTMemCacheCreate(&pVScsiDevice->hCacheReq, sizeof(VSCSIREQINT), 0, UINT32_MAX,
241 NULL, NULL, NULL, 0);
242 if (RT_SUCCESS(rc))
243 {
244 *phVScsiDevice = pVScsiDevice;
245 LogFlow(("%s: hVScsiDevice=%#p -> VINF_SUCCESS\n", __FUNCTION__, pVScsiDevice));
246 return VINF_SUCCESS;
247 }
248
249 RTMemFree(pVScsiDevice);
250
251 return rc;
252}
253
254
255VBOXDDU_DECL(int) VSCSIDeviceDestroy(VSCSIDEVICE hVScsiDevice)
256{
257 AssertPtrReturn(hVScsiDevice, VERR_INVALID_HANDLE);
258
259 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
260
261 if (pVScsiDevice->cLunsAttached > 0)
262 return VERR_VSCSI_LUN_ATTACHED_TO_DEVICE;
263
264 if (pVScsiDevice->papVScsiLun)
265 RTMemFree(pVScsiDevice->papVScsiLun);
266
267 RTMemCacheDestroy(pVScsiDevice->hCacheReq);
268 RTMemFree(pVScsiDevice);
269
270 return VINF_SUCCESS;;
271}
272
273
274VBOXDDU_DECL(int) VSCSIDeviceLunAttach(VSCSIDEVICE hVScsiDevice, VSCSILUN hVScsiLun, uint32_t iLun)
275{
276 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
277 PVSCSILUNINT pVScsiLun = (PVSCSILUNINT)hVScsiLun;
278 int rc = VINF_SUCCESS;
279
280 /* Parameter checks */
281 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
282 AssertPtrReturn(pVScsiLun, VERR_INVALID_HANDLE);
283 AssertReturn(iLun < VSCSI_DEVICE_LUN_MAX, VERR_VSCSI_LUN_INVALID);
284 AssertReturn(!pVScsiLun->pVScsiDevice, VERR_VSCSI_LUN_ATTACHED_TO_DEVICE);
285
286 if (iLun >= pVScsiDevice->cLunsMax)
287 {
288 PPVSCSILUNINT papLunOld = pVScsiDevice->papVScsiLun;
289
290 pVScsiDevice->papVScsiLun = (PPVSCSILUNINT)RTMemAllocZ((iLun + 1) * sizeof(PVSCSILUNINT));
291 if (pVScsiDevice->papVScsiLun)
292 {
293 for (uint32_t i = 0; i < pVScsiDevice->cLunsMax; i++)
294 pVScsiDevice->papVScsiLun[i] = papLunOld[i];
295
296 if (papLunOld)
297 RTMemFree(papLunOld);
298
299 pVScsiDevice->cLunsMax = iLun + 1;
300 }
301 else
302 rc = VERR_NO_MEMORY;
303 }
304
305 if (RT_SUCCESS(rc))
306 {
307 pVScsiLun->pVScsiDevice = pVScsiDevice;
308 pVScsiDevice->papVScsiLun[iLun] = pVScsiLun;
309 pVScsiDevice->cLunsAttached++;
310 }
311
312 return rc;
313}
314
315
316VBOXDDU_DECL(int) VSCSIDeviceLunDetach(VSCSIDEVICE hVScsiDevice, uint32_t iLun,
317 PVSCSILUN phVScsiLun)
318{
319 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
320
321 /* Parameter checks */
322 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
323 AssertPtrReturn(phVScsiLun, VERR_INVALID_POINTER);
324 AssertReturn(iLun < VSCSI_DEVICE_LUN_MAX, VERR_VSCSI_LUN_INVALID);
325 AssertReturn(iLun < pVScsiDevice->cLunsMax, VERR_VSCSI_LUN_NOT_ATTACHED);
326 AssertPtrReturn(pVScsiDevice->papVScsiLun[iLun], VERR_VSCSI_LUN_NOT_ATTACHED);
327
328 PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[iLun];
329
330 pVScsiLun->pVScsiDevice = NULL;
331 *phVScsiLun = pVScsiLun;
332 pVScsiDevice->papVScsiLun[iLun] = NULL;
333 pVScsiDevice->cLunsAttached--;
334
335 return VINF_SUCCESS;
336}
337
338
339VBOXDDU_DECL(int) VSCSIDeviceLunQueryType(VSCSIDEVICE hVScsiDevice, uint32_t iLun,
340 PVSCSILUNTYPE pEnmLunType)
341{
342 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
343
344 /* Parameter checks */
345 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
346 AssertPtrReturn(pEnmLunType, VERR_INVALID_POINTER);
347 AssertReturn(iLun < VSCSI_DEVICE_LUN_MAX, VERR_VSCSI_LUN_INVALID);
348 AssertReturn(iLun < pVScsiDevice->cLunsMax, VERR_VSCSI_LUN_NOT_ATTACHED);
349 AssertPtrReturn(pVScsiDevice->papVScsiLun[iLun], VERR_VSCSI_LUN_NOT_ATTACHED);
350
351 PVSCSILUNINT hVScsiLun = pVScsiDevice->papVScsiLun[iLun];
352 *pEnmLunType = hVScsiLun->pVScsiLunDesc->enmLunType;
353
354 return VINF_SUCCESS;
355}
356
357
358VBOXDDU_DECL(int) VSCSIDeviceReqEnqueue(VSCSIDEVICE hVScsiDevice, VSCSIREQ hVScsiReq)
359{
360 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
361 PVSCSIREQINT pVScsiReq = (PVSCSIREQINT)hVScsiReq;
362 int rc = VINF_SUCCESS;
363
364 /* Parameter checks */
365 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
366 AssertPtrReturn(pVScsiReq, VERR_INVALID_HANDLE);
367
368 /* Check if this request can be handled by us */
369 int rcReq;
370 bool fProcessed = vscsiDeviceReqProcess(pVScsiDevice, pVScsiReq, &rcReq);
371 if (!fProcessed)
372 {
373 /* Pass to the LUN driver */
374 if (vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun))
375 {
376 PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[pVScsiReq->iLun];
377 rc = pVScsiLun->pVScsiLunDesc->pfnVScsiLunReqProcess(pVScsiLun, pVScsiReq);
378 }
379 else
380 {
381 /* LUN not present, report error. */
382 vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq,
383 SCSI_SENSE_ILLEGAL_REQUEST,
384 SCSI_ASC_LOGICAL_UNIT_DOES_NOT_RESPOND_TO_SELECTION,
385 0x00);
386
387 vscsiDeviceReqComplete(pVScsiDevice, pVScsiReq,
388 SCSI_STATUS_CHECK_CONDITION, false, VINF_SUCCESS);
389 }
390 }
391 else
392 vscsiDeviceReqComplete(pVScsiDevice, pVScsiReq,
393 rcReq, false, VINF_SUCCESS);
394
395 return rc;
396}
397
398
399VBOXDDU_DECL(int) VSCSIDeviceReqCreate(VSCSIDEVICE hVScsiDevice, PVSCSIREQ phVScsiReq,
400 uint32_t iLun, uint8_t *pbCDB, size_t cbCDB,
401 size_t cbSGList, unsigned cSGListEntries,
402 PCRTSGSEG paSGList, uint8_t *pbSense,
403 size_t cbSense, void *pvVScsiReqUser)
404{
405 RT_NOREF1(cbSGList);
406 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
407 PVSCSIREQINT pVScsiReq = NULL;
408
409 /* Parameter checks */
410 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
411 AssertPtrReturn(phVScsiReq, VERR_INVALID_POINTER);
412 AssertPtrReturn(pbCDB, VERR_INVALID_PARAMETER);
413 AssertReturn(cbCDB > 0, VERR_INVALID_PARAMETER);
414
415 pVScsiReq = (PVSCSIREQINT)RTMemCacheAlloc(pVScsiDevice->hCacheReq);
416 if (!pVScsiReq)
417 return VERR_NO_MEMORY;
418
419 pVScsiReq->iLun = iLun;
420 pVScsiReq->pbCDB = pbCDB;
421 pVScsiReq->cbCDB = cbCDB;
422 pVScsiReq->pbSense = pbSense;
423 pVScsiReq->cbSense = cbSense;
424 pVScsiReq->pvVScsiReqUser = pvVScsiReqUser;
425 pVScsiReq->cbXfer = 0;
426 pVScsiReq->pvLun = NULL;
427 pVScsiReq->enmXferDir = VSCSIXFERDIR_UNKNOWN;
428 pVScsiReq->cbSenseWritten = 0;
429 RTSgBufInit(&pVScsiReq->SgBuf, paSGList, cSGListEntries);
430
431 *phVScsiReq = pVScsiReq;
432
433 return VINF_SUCCESS;
434}
Note: See TracBrowser for help on using the repository browser.

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