VirtualBox

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

Last change on this file since 77329 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.8 KB
Line 
1/* $Id: VSCSIDevice.cpp 76553 2019-01-01 01:45:53Z 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 vscsiReqSetXferSize(pVScsiReq, scsiBE2H_U16(&pVScsiReq->pbCDB[3]));
70 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
71 ScsiInquiryReply.cbAdditional = 31;
72 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
73 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
74 cbData = RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
75 *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
76 }
77 else
78 fProcessed = false; /* Let the LUN process the request because it will provide LUN specific data */
79
80 break;
81 }
82 case SCSI_REPORT_LUNS:
83 {
84 /*
85 * If allocation length is less than 16 bytes SPC compliant devices have
86 * to return an error.
87 */
88 vscsiReqSetXferSize(pVScsiReq, scsiBE2H_U32(&pVScsiReq->pbCDB[6]));
89 if (pVScsiReq->cbXfer < 16)
90 *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
91 else
92 {
93 size_t cbData;
94 uint8_t aReply[16]; /* We report only one LUN. */
95
96 memset(aReply, 0, sizeof(aReply));
97 scsiH2BE_U32(&aReply[0], 8); /* List length starts at position 0. */
98 cbData = RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
99 if (cbData < 16)
100 *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
101 else
102 *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
103 }
104 break;
105 }
106 case SCSI_TEST_UNIT_READY:
107 {
108 if ( vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun)
109 && pVScsiDevice->papVScsiLun[pVScsiReq->iLun]->fReady)
110 *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
111 else
112 fProcessed = false; /* The LUN (if present) will provide details. */
113 break;
114 }
115 case SCSI_REQUEST_SENSE:
116 {
117 vscsiReqSetXferSize(pVScsiReq, pVScsiReq->pbCDB[4]);
118
119 /* Descriptor format sense data is not supported and results in an error. */
120 if ((pVScsiReq->pbCDB[1] & 0x1) != 0)
121 *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
122 else
123 *prcReq = vscsiReqSenseCmd(&pVScsiDevice->VScsiSense, pVScsiReq);
124 break;
125 }
126#if 0
127 case SCSI_MAINTENANCE_IN:
128 {
129 if (pVScsiReq->pbCDB[1] == SCSI_MAINTENANCE_IN_REPORT_SUPP_OPC)
130 {
131 /*
132 * If the LUN is present and has the CDB info set we will execute the command, otherwise
133 * just fail with an illegal request error.
134 */
135 if (vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun))
136 {
137 PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[pVScsiReq->iLun];
138 if (pVScsiLun->pVScsiLunDesc->paSupOpcInfo)
139 {
140 bool fTimeoutDesc = RT_BOOL(pVScsiReq->pbCDB[2] & 0x80);
141 uint8_t u8ReportMode = pVScsiReq->pbCDB[2] & 0x7;
142 uint8_t u8Opc = pVScsiReq->pbCDB[3];
143 uint16_t u16SvcAction = scsiBE2H_U16(&pVScsiReq->pbCDB[4]);
144 uint16_t cbData = scsiBE2H_U16(&pVScsiReq->pbCDB[6]);
145
146 switch (u8ReportMode)
147 {
148 case 0:
149 *prcReq = vscsiDeviceReportAllSupportedOpc(pVScsiLun, pVScsiReq, fTimeoutDesc, cbData);
150 break;
151 case 1:
152 *prcReq = vscsiDeviceReportOpc(pVScsiLun, pVScsiReq, u8Opc, fTimeoutDesc, cbData);
153 break;
154 case 2:
155 *prcReq = vscsiDeviceReportOpc(pVScsiLun, pVScsiReq, u8Opc, fTimeoutDesc, cbData);
156 break;
157 default:
158 *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq,
159 SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
160 }
161 }
162 else
163 *prcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
164 }
165 else
166 *prcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
167 }
168 else
169 fProcessed = false; /* Might also be the SEND KEY MMC command. */
170 }
171#endif
172 default:
173 fProcessed = false;
174 }
175
176 return fProcessed;
177}
178
179
180void vscsiDeviceReqComplete(PVSCSIDEVICEINT pVScsiDevice, PVSCSIREQINT pVScsiReq,
181 int rcScsiCode, bool fRedoPossible, int rcReq)
182{
183 pVScsiDevice->pfnVScsiReqCompleted(pVScsiDevice, pVScsiDevice->pvVScsiDeviceUser,
184 pVScsiReq->pvVScsiReqUser, rcScsiCode, fRedoPossible,
185 rcReq, pVScsiReq->cbXfer);
186
187 if (pVScsiReq->pvLun)
188 {
189 if (vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun))
190 {
191 PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[pVScsiReq->iLun];
192 pVScsiLun->pVScsiLunDesc->pfnVScsiLunReqFree(pVScsiLun, pVScsiReq, pVScsiReq->pvLun);
193 }
194 else
195 AssertLogRelMsgFailed(("vscsiDeviceReqComplete: LUN %u for VSCSI request %#p is not present but there is LUN specific data allocated\n",
196 pVScsiReq->iLun, pVScsiReq));
197
198 pVScsiReq->pvLun = NULL;
199 }
200
201 RTMemCacheFree(pVScsiDevice->hCacheReq, pVScsiReq);
202}
203
204
205VBOXDDU_DECL(int) VSCSIDeviceCreate(PVSCSIDEVICE phVScsiDevice,
206 PFNVSCSIREQCOMPLETED pfnVScsiReqCompleted,
207 void *pvVScsiDeviceUser)
208{
209 int rc = VINF_SUCCESS;
210 PVSCSIDEVICEINT pVScsiDevice = NULL;
211
212 AssertPtrReturn(phVScsiDevice, VERR_INVALID_POINTER);
213 AssertPtrReturn(pfnVScsiReqCompleted, VERR_INVALID_POINTER);
214
215 pVScsiDevice = (PVSCSIDEVICEINT)RTMemAllocZ(sizeof(VSCSIDEVICEINT));
216 if (!pVScsiDevice)
217 return VERR_NO_MEMORY;
218
219 pVScsiDevice->pfnVScsiReqCompleted = pfnVScsiReqCompleted;
220 pVScsiDevice->pvVScsiDeviceUser = pvVScsiDeviceUser;
221 pVScsiDevice->cLunsAttached = 0;
222 pVScsiDevice->cLunsMax = 0;
223 pVScsiDevice->papVScsiLun = NULL;
224 vscsiSenseInit(&pVScsiDevice->VScsiSense);
225
226 rc = RTMemCacheCreate(&pVScsiDevice->hCacheReq, sizeof(VSCSIREQINT), 0, UINT32_MAX,
227 NULL, NULL, NULL, 0);
228 if (RT_SUCCESS(rc))
229 {
230 *phVScsiDevice = pVScsiDevice;
231 LogFlow(("%s: hVScsiDevice=%#p -> VINF_SUCCESS\n", __FUNCTION__, pVScsiDevice));
232 return VINF_SUCCESS;
233 }
234
235 RTMemFree(pVScsiDevice);
236
237 return rc;
238}
239
240
241VBOXDDU_DECL(int) VSCSIDeviceDestroy(VSCSIDEVICE hVScsiDevice)
242{
243 AssertPtrReturn(hVScsiDevice, VERR_INVALID_HANDLE);
244
245 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
246
247 if (pVScsiDevice->cLunsAttached > 0)
248 return VERR_VSCSI_LUN_ATTACHED_TO_DEVICE;
249
250 if (pVScsiDevice->papVScsiLun)
251 RTMemFree(pVScsiDevice->papVScsiLun);
252
253 RTMemCacheDestroy(pVScsiDevice->hCacheReq);
254 RTMemFree(pVScsiDevice);
255
256 return VINF_SUCCESS;;
257}
258
259
260VBOXDDU_DECL(int) VSCSIDeviceLunAttach(VSCSIDEVICE hVScsiDevice, VSCSILUN hVScsiLun, uint32_t iLun)
261{
262 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
263 PVSCSILUNINT pVScsiLun = (PVSCSILUNINT)hVScsiLun;
264 int rc = VINF_SUCCESS;
265
266 /* Parameter checks */
267 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
268 AssertPtrReturn(pVScsiLun, VERR_INVALID_HANDLE);
269 AssertReturn(iLun < VSCSI_DEVICE_LUN_MAX, VERR_VSCSI_LUN_INVALID);
270 AssertReturn(!pVScsiLun->pVScsiDevice, VERR_VSCSI_LUN_ATTACHED_TO_DEVICE);
271
272 if (iLun >= pVScsiDevice->cLunsMax)
273 {
274 PPVSCSILUNINT papLunOld = pVScsiDevice->papVScsiLun;
275
276 pVScsiDevice->papVScsiLun = (PPVSCSILUNINT)RTMemAllocZ((iLun + 1) * sizeof(PVSCSILUNINT));
277 if (pVScsiDevice->papVScsiLun)
278 {
279 for (uint32_t i = 0; i < pVScsiDevice->cLunsMax; i++)
280 pVScsiDevice->papVScsiLun[i] = papLunOld[i];
281
282 if (papLunOld)
283 RTMemFree(papLunOld);
284
285 pVScsiDevice->cLunsMax = iLun + 1;
286 }
287 else
288 rc = VERR_NO_MEMORY;
289 }
290
291 if (RT_SUCCESS(rc))
292 {
293 pVScsiLun->pVScsiDevice = pVScsiDevice;
294 pVScsiDevice->papVScsiLun[iLun] = pVScsiLun;
295 pVScsiDevice->cLunsAttached++;
296 }
297
298 return rc;
299}
300
301
302VBOXDDU_DECL(int) VSCSIDeviceLunDetach(VSCSIDEVICE hVScsiDevice, uint32_t iLun,
303 PVSCSILUN phVScsiLun)
304{
305 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
306
307 /* Parameter checks */
308 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
309 AssertPtrReturn(phVScsiLun, VERR_INVALID_POINTER);
310 AssertReturn(iLun < VSCSI_DEVICE_LUN_MAX, VERR_VSCSI_LUN_INVALID);
311 AssertReturn(iLun < pVScsiDevice->cLunsMax, VERR_VSCSI_LUN_NOT_ATTACHED);
312 AssertPtrReturn(pVScsiDevice->papVScsiLun[iLun], VERR_VSCSI_LUN_NOT_ATTACHED);
313
314 PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[iLun];
315
316 pVScsiLun->pVScsiDevice = NULL;
317 *phVScsiLun = pVScsiLun;
318 pVScsiDevice->papVScsiLun[iLun] = NULL;
319 pVScsiDevice->cLunsAttached--;
320
321 return VINF_SUCCESS;
322}
323
324
325VBOXDDU_DECL(int) VSCSIDeviceLunQueryType(VSCSIDEVICE hVScsiDevice, uint32_t iLun,
326 PVSCSILUNTYPE pEnmLunType)
327{
328 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
329
330 /* Parameter checks */
331 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
332 AssertPtrReturn(pEnmLunType, VERR_INVALID_POINTER);
333 AssertReturn(iLun < VSCSI_DEVICE_LUN_MAX, VERR_VSCSI_LUN_INVALID);
334 AssertReturn(iLun < pVScsiDevice->cLunsMax, VERR_VSCSI_LUN_NOT_ATTACHED);
335 AssertPtrReturn(pVScsiDevice->papVScsiLun[iLun], VERR_VSCSI_LUN_NOT_ATTACHED);
336
337 PVSCSILUNINT hVScsiLun = pVScsiDevice->papVScsiLun[iLun];
338 *pEnmLunType = hVScsiLun->pVScsiLunDesc->enmLunType;
339
340 return VINF_SUCCESS;
341}
342
343
344VBOXDDU_DECL(int) VSCSIDeviceReqEnqueue(VSCSIDEVICE hVScsiDevice, VSCSIREQ hVScsiReq)
345{
346 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
347 PVSCSIREQINT pVScsiReq = (PVSCSIREQINT)hVScsiReq;
348 int rc = VINF_SUCCESS;
349
350 /* Parameter checks */
351 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
352 AssertPtrReturn(pVScsiReq, VERR_INVALID_HANDLE);
353
354 /* Check if this request can be handled by us */
355 int rcReq;
356 bool fProcessed = vscsiDeviceReqProcess(pVScsiDevice, pVScsiReq, &rcReq);
357 if (!fProcessed)
358 {
359 /* Pass to the LUN driver */
360 if (vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun))
361 {
362 PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[pVScsiReq->iLun];
363 rc = pVScsiLun->pVScsiLunDesc->pfnVScsiLunReqProcess(pVScsiLun, pVScsiReq);
364 }
365 else
366 {
367 /* LUN not present, report error. */
368 vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq,
369 SCSI_SENSE_ILLEGAL_REQUEST,
370 SCSI_ASC_LOGICAL_UNIT_DOES_NOT_RESPOND_TO_SELECTION,
371 0x00);
372
373 vscsiDeviceReqComplete(pVScsiDevice, pVScsiReq,
374 SCSI_STATUS_CHECK_CONDITION, false, VINF_SUCCESS);
375 }
376 }
377 else
378 vscsiDeviceReqComplete(pVScsiDevice, pVScsiReq,
379 rcReq, false, VINF_SUCCESS);
380
381 return rc;
382}
383
384
385VBOXDDU_DECL(int) VSCSIDeviceReqCreate(VSCSIDEVICE hVScsiDevice, PVSCSIREQ phVScsiReq,
386 uint32_t iLun, uint8_t *pbCDB, size_t cbCDB,
387 size_t cbSGList, unsigned cSGListEntries,
388 PCRTSGSEG paSGList, uint8_t *pbSense,
389 size_t cbSense, void *pvVScsiReqUser)
390{
391 RT_NOREF1(cbSGList);
392 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
393 PVSCSIREQINT pVScsiReq = NULL;
394
395 /* Parameter checks */
396 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
397 AssertPtrReturn(phVScsiReq, VERR_INVALID_POINTER);
398 AssertPtrReturn(pbCDB, VERR_INVALID_PARAMETER);
399 AssertReturn(cbCDB > 0, VERR_INVALID_PARAMETER);
400
401 pVScsiReq = (PVSCSIREQINT)RTMemCacheAlloc(pVScsiDevice->hCacheReq);
402 if (!pVScsiReq)
403 return VERR_NO_MEMORY;
404
405 pVScsiReq->iLun = iLun;
406 pVScsiReq->pbCDB = pbCDB;
407 pVScsiReq->cbCDB = cbCDB;
408 pVScsiReq->pbSense = pbSense;
409 pVScsiReq->cbSense = cbSense;
410 pVScsiReq->pvVScsiReqUser = pvVScsiReqUser;
411 pVScsiReq->cbXfer = 0;
412 pVScsiReq->pvLun = NULL;
413 RTSgBufInit(&pVScsiReq->SgBuf, paSGList, cSGListEntries);
414
415 *phVScsiReq = pVScsiReq;
416
417 return VINF_SUCCESS;
418}
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