VirtualBox

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

Last change on this file since 82244 was 80589, checked in by vboxsync, 5 years ago

Devices/Storage: Add two new parameters to PDMIMEDIAEX::pfnIoReqSendScsiCmd to return the amount ofsense data written and the determined transfer direction from parsed CDB, bugref:9440

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