VirtualBox

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

Last change on this file since 62506 was 62506, checked in by vboxsync, 8 years ago

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.6 KB
Line 
1/* $Id: VSCSIDevice.cpp 62506 2016-07-22 19:09:44Z vboxsync $ */
2/** @file
3 * Virtual SCSI driver: Device handling
4 */
5
6/*
7 * Copyright (C) 2006-2016 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 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
70 ScsiInquiryReply.cbAdditional = 31;
71 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
72 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
73 cbData = RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
74 *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
75 }
76 else
77 fProcessed = false; /* Let the LUN process the request because it will provide LUN specific data */
78
79 break;
80 }
81 case SCSI_REPORT_LUNS:
82 {
83 /*
84 * If allocation length is less than 16 bytes SPC compliant devices have
85 * to return an error.
86 */
87 if (vscsiBE2HU32(&pVScsiReq->pbCDB[6]) < 16)
88 *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
89 else
90 {
91 size_t cbData;
92 uint8_t aReply[16]; /* We report only one LUN. */
93
94 memset(aReply, 0, sizeof(aReply));
95 vscsiH2BEU32(&aReply[0], 8); /* List length starts at position 0. */
96 cbData = RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
97 if (cbData < 16)
98 *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
99 else
100 *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
101 }
102 break;
103 }
104 case SCSI_TEST_UNIT_READY:
105 {
106 if ( vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun)
107 && pVScsiDevice->papVScsiLun[pVScsiReq->iLun]->fReady)
108 *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
109 else
110 fProcessed = false; /* The LUN (if present) will provide details. */
111 break;
112 }
113 case SCSI_REQUEST_SENSE:
114 {
115 /* Descriptor format sense data is not supported and results in an error. */
116 if ((pVScsiReq->pbCDB[1] & 0x1) != 0)
117 *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
118 else
119 *prcReq = vscsiReqSenseCmd(&pVScsiDevice->VScsiSense, pVScsiReq);
120 break;
121 }
122 default:
123 fProcessed = false;
124 }
125
126 return fProcessed;
127}
128
129
130void vscsiDeviceReqComplete(PVSCSIDEVICEINT pVScsiDevice, PVSCSIREQINT pVScsiReq,
131 int rcScsiCode, bool fRedoPossible, int rcReq)
132{
133 pVScsiDevice->pfnVScsiReqCompleted(pVScsiDevice, pVScsiDevice->pvVScsiDeviceUser,
134 pVScsiReq->pvVScsiReqUser, rcScsiCode, fRedoPossible,
135 rcReq);
136
137 RTMemCacheFree(pVScsiDevice->hCacheReq, pVScsiReq);
138}
139
140
141VBOXDDU_DECL(int) VSCSIDeviceCreate(PVSCSIDEVICE phVScsiDevice,
142 PFNVSCSIREQCOMPLETED pfnVScsiReqCompleted,
143 void *pvVScsiDeviceUser)
144{
145 int rc = VINF_SUCCESS;
146 PVSCSIDEVICEINT pVScsiDevice = NULL;
147
148 AssertPtrReturn(phVScsiDevice, VERR_INVALID_POINTER);
149 AssertPtrReturn(pfnVScsiReqCompleted, VERR_INVALID_POINTER);
150
151 pVScsiDevice = (PVSCSIDEVICEINT)RTMemAllocZ(sizeof(VSCSIDEVICEINT));
152 if (!pVScsiDevice)
153 return VERR_NO_MEMORY;
154
155 pVScsiDevice->pfnVScsiReqCompleted = pfnVScsiReqCompleted;
156 pVScsiDevice->pvVScsiDeviceUser = pvVScsiDeviceUser;
157 pVScsiDevice->cLunsAttached = 0;
158 pVScsiDevice->cLunsMax = 0;
159 pVScsiDevice->papVScsiLun = NULL;
160 vscsiSenseInit(&pVScsiDevice->VScsiSense);
161
162 rc = RTMemCacheCreate(&pVScsiDevice->hCacheReq, sizeof(VSCSIREQINT), 0, UINT32_MAX,
163 NULL, NULL, NULL, 0);
164 if (RT_SUCCESS(rc))
165 {
166 *phVScsiDevice = pVScsiDevice;
167 LogFlow(("%s: hVScsiDevice=%#p -> VINF_SUCCESS\n", __FUNCTION__, pVScsiDevice));
168 return VINF_SUCCESS;
169 }
170
171 RTMemFree(pVScsiDevice);
172
173 return rc;
174}
175
176
177VBOXDDU_DECL(int) VSCSIDeviceDestroy(VSCSIDEVICE hVScsiDevice)
178{
179 AssertPtrReturn(hVScsiDevice, VERR_INVALID_HANDLE);
180
181 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
182
183 if (pVScsiDevice->cLunsAttached > 0)
184 return VERR_VSCSI_LUN_ATTACHED_TO_DEVICE;
185
186 if (pVScsiDevice->papVScsiLun)
187 RTMemFree(pVScsiDevice->papVScsiLun);
188
189 RTMemCacheDestroy(pVScsiDevice->hCacheReq);
190 RTMemFree(pVScsiDevice);
191
192 return VINF_SUCCESS;;
193}
194
195
196VBOXDDU_DECL(int) VSCSIDeviceLunAttach(VSCSIDEVICE hVScsiDevice, VSCSILUN hVScsiLun, uint32_t iLun)
197{
198 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
199 PVSCSILUNINT pVScsiLun = (PVSCSILUNINT)hVScsiLun;
200 int rc = VINF_SUCCESS;
201
202 /* Parameter checks */
203 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
204 AssertPtrReturn(pVScsiLun, VERR_INVALID_HANDLE);
205 AssertReturn(iLun < VSCSI_DEVICE_LUN_MAX, VERR_VSCSI_LUN_INVALID);
206 AssertReturn(!pVScsiLun->pVScsiDevice, VERR_VSCSI_LUN_ATTACHED_TO_DEVICE);
207
208 if (iLun >= pVScsiDevice->cLunsMax)
209 {
210 PPVSCSILUNINT papLunOld = pVScsiDevice->papVScsiLun;
211
212 pVScsiDevice->papVScsiLun = (PPVSCSILUNINT)RTMemAllocZ((iLun + 1) * sizeof(PVSCSILUNINT));
213 if (pVScsiDevice->papVScsiLun)
214 {
215 for (uint32_t i = 0; i < pVScsiDevice->cLunsMax; i++)
216 pVScsiDevice->papVScsiLun[i] = papLunOld[i];
217
218 if (papLunOld)
219 RTMemFree(papLunOld);
220
221 pVScsiDevice->cLunsMax = iLun + 1;
222 }
223 else
224 rc = VERR_NO_MEMORY;
225 }
226
227 if (RT_SUCCESS(rc))
228 {
229 pVScsiLun->pVScsiDevice = pVScsiDevice;
230 pVScsiDevice->papVScsiLun[iLun] = pVScsiLun;
231 pVScsiDevice->cLunsAttached++;
232 }
233
234 return rc;
235}
236
237
238VBOXDDU_DECL(int) VSCSIDeviceLunDetach(VSCSIDEVICE hVScsiDevice, uint32_t iLun,
239 PVSCSILUN phVScsiLun)
240{
241 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
242
243 /* Parameter checks */
244 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
245 AssertPtrReturn(phVScsiLun, VERR_INVALID_POINTER);
246 AssertReturn(iLun < VSCSI_DEVICE_LUN_MAX, VERR_VSCSI_LUN_INVALID);
247 AssertReturn(iLun < pVScsiDevice->cLunsMax, VERR_VSCSI_LUN_NOT_ATTACHED);
248 AssertPtrReturn(pVScsiDevice->papVScsiLun[iLun], VERR_VSCSI_LUN_NOT_ATTACHED);
249
250 PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[iLun];
251
252 pVScsiLun->pVScsiDevice = NULL;
253 *phVScsiLun = pVScsiLun;
254 pVScsiDevice->papVScsiLun[iLun] = NULL;
255 pVScsiDevice->cLunsAttached--;
256
257 return VINF_SUCCESS;
258}
259
260
261VBOXDDU_DECL(int) VSCSIDeviceLunQueryType(VSCSIDEVICE hVScsiDevice, uint32_t iLun,
262 PVSCSILUNTYPE pEnmLunType)
263{
264 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
265
266 /* Parameter checks */
267 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
268 AssertPtrReturn(pEnmLunType, VERR_INVALID_POINTER);
269 AssertReturn(iLun < VSCSI_DEVICE_LUN_MAX, VERR_VSCSI_LUN_INVALID);
270 AssertReturn(iLun < pVScsiDevice->cLunsMax, VERR_VSCSI_LUN_NOT_ATTACHED);
271 AssertPtrReturn(pVScsiDevice->papVScsiLun[iLun], VERR_VSCSI_LUN_NOT_ATTACHED);
272
273 PVSCSILUNINT hVScsiLun = pVScsiDevice->papVScsiLun[iLun];
274 *pEnmLunType = hVScsiLun->pVScsiLunDesc->enmLunType;
275
276 return VINF_SUCCESS;
277}
278
279
280VBOXDDU_DECL(int) VSCSIDeviceReqEnqueue(VSCSIDEVICE hVScsiDevice, VSCSIREQ hVScsiReq)
281{
282 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
283 PVSCSIREQINT pVScsiReq = (PVSCSIREQINT)hVScsiReq;
284 int rc = VINF_SUCCESS;
285
286 /* Parameter checks */
287 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
288 AssertPtrReturn(pVScsiReq, VERR_INVALID_HANDLE);
289
290 /* Check if this request can be handled by us */
291 int rcReq;
292 bool fProcessed = vscsiDeviceReqProcess(pVScsiDevice, pVScsiReq, &rcReq);
293 if (!fProcessed)
294 {
295 /* Pass to the LUN driver */
296 if (vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun))
297 {
298 PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[pVScsiReq->iLun];
299 rc = pVScsiLun->pVScsiLunDesc->pfnVScsiLunReqProcess(pVScsiLun, pVScsiReq);
300 }
301 else
302 {
303 /* LUN not present, report error. */
304 vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq,
305 SCSI_SENSE_ILLEGAL_REQUEST,
306 SCSI_ASC_LOGICAL_UNIT_DOES_NOT_RESPOND_TO_SELECTION,
307 0x00);
308
309 vscsiDeviceReqComplete(pVScsiDevice, pVScsiReq,
310 SCSI_STATUS_CHECK_CONDITION, false, VINF_SUCCESS);
311 }
312 }
313 else
314 vscsiDeviceReqComplete(pVScsiDevice, pVScsiReq,
315 rcReq, false, VINF_SUCCESS);
316
317 return rc;
318}
319
320
321VBOXDDU_DECL(int) VSCSIDeviceReqCreate(VSCSIDEVICE hVScsiDevice, PVSCSIREQ phVScsiReq,
322 uint32_t iLun, uint8_t *pbCDB, size_t cbCDB,
323 size_t cbSGList, unsigned cSGListEntries,
324 PCRTSGSEG paSGList, uint8_t *pbSense,
325 size_t cbSense, void *pvVScsiReqUser)
326{
327 PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
328 PVSCSIREQINT pVScsiReq = NULL;
329
330 /* Parameter checks */
331 AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
332 AssertPtrReturn(phVScsiReq, VERR_INVALID_POINTER);
333 AssertPtrReturn(pbCDB, VERR_INVALID_PARAMETER);
334 AssertReturn(cbCDB > 0, VERR_INVALID_PARAMETER);
335
336 pVScsiReq = (PVSCSIREQINT)RTMemCacheAlloc(pVScsiDevice->hCacheReq);
337 if (!pVScsiReq)
338 return VERR_NO_MEMORY;
339
340 pVScsiReq->iLun = iLun;
341 pVScsiReq->pbCDB = pbCDB;
342 pVScsiReq->cbCDB = cbCDB;
343 pVScsiReq->pbSense = pbSense;
344 pVScsiReq->cbSense = cbSense;
345 pVScsiReq->pvVScsiReqUser = pvVScsiReqUser;
346 RTSgBufInit(&pVScsiReq->SgBuf, paSGList, cSGListEntries);
347
348 *phVScsiReq = pVScsiReq;
349
350 return VINF_SUCCESS;
351}
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