VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VSCSI/VSCSILunSbc.cpp@ 94368

Last change on this file since 94368 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.9 KB
Line 
1/* $Id: VSCSILunSbc.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * Virtual SCSI driver: SBC LUN implementation (hard disks)
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 <iprt/errcore.h>
25#include <VBox/types.h>
26#include <VBox/vscsi.h>
27#include <iprt/cdefs.h>
28#include <iprt/asm.h>
29#include <iprt/assert.h>
30#include <iprt/mem.h>
31#include <iprt/string.h>
32
33#include "VSCSIInternal.h"
34
35/** Maximum of amount of LBAs to unmap with one command. */
36#define VSCSI_UNMAP_LBAS_MAX(a_cbSector) ((10*_1M) / a_cbSector)
37
38/**
39 * SBC LUN instance
40 */
41typedef struct VSCSILUNSBC
42{
43 /** Core LUN structure */
44 VSCSILUNINT Core;
45 /** Sector size of the medium. */
46 uint64_t cbSector;
47 /** Size of the virtual disk. */
48 uint64_t cSectors;
49 /** VPD page pool. */
50 VSCSIVPDPOOL VpdPagePool;
51} VSCSILUNSBC;
52/** Pointer to a SBC LUN instance */
53typedef VSCSILUNSBC *PVSCSILUNSBC;
54
55static DECLCALLBACK(int) vscsiLunSbcInit(PVSCSILUNINT pVScsiLun)
56{
57 PVSCSILUNSBC pVScsiLunSbc = (PVSCSILUNSBC)pVScsiLun;
58 int rc = VINF_SUCCESS;
59 int cVpdPages = 0;
60
61 uint32_t cRegions = vscsiLunMediumGetRegionCount(pVScsiLun);
62 if (cRegions != 1)
63 rc = VERR_INVALID_PARAMETER;
64
65 if (RT_SUCCESS(rc))
66 rc = vscsiLunMediumQueryRegionProperties(pVScsiLun, 0, NULL, &pVScsiLunSbc->cSectors,
67 &pVScsiLunSbc->cbSector, NULL);
68 if (RT_SUCCESS(rc))
69 rc = vscsiVpdPagePoolInit(&pVScsiLunSbc->VpdPagePool);
70
71 /* Create device identification page - mandatory. */
72 if (RT_SUCCESS(rc))
73 {
74 PVSCSIVPDPAGEDEVID pDevIdPage;
75
76 rc = vscsiVpdPagePoolAllocNewPage(&pVScsiLunSbc->VpdPagePool, VSCSI_VPD_DEVID_NUMBER,
77 VSCSI_VPD_DEVID_SIZE, (uint8_t **)&pDevIdPage);
78 if (RT_SUCCESS(rc))
79 {
80 /** @todo Not conforming to the SPC spec but Solaris needs at least a stub to work. */
81 pDevIdPage->u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS;
82 pDevIdPage->u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
83 pDevIdPage->u16PageLength = RT_H2BE_U16(0x0);
84 cVpdPages++;
85 }
86 }
87
88 if ( RT_SUCCESS(rc)
89 && (pVScsiLun->fFeatures & VSCSI_LUN_FEATURE_UNMAP))
90 {
91 PVSCSIVPDPAGEBLOCKLIMITS pBlkPage;
92 PVSCSIVPDPAGEBLOCKPROV pBlkProvPage;
93
94 /* Create the page and fill it. */
95 rc = vscsiVpdPagePoolAllocNewPage(&pVScsiLunSbc->VpdPagePool, VSCSI_VPD_BLOCK_LIMITS_NUMBER,
96 VSCSI_VPD_BLOCK_LIMITS_SIZE, (uint8_t **)&pBlkPage);
97 if (RT_SUCCESS(rc))
98 {
99 pBlkPage->u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS;
100 pBlkPage->u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
101 pBlkPage->u16PageLength = RT_H2BE_U16(0x3c);
102 pBlkPage->u8MaxCmpWriteLength = 0;
103 pBlkPage->u16OptTrfLengthGran = 0;
104 pBlkPage->u32MaxTrfLength = 0;
105 pBlkPage->u32OptTrfLength = 0;
106 pBlkPage->u32MaxPreXdTrfLength = 0;
107 pBlkPage->u32MaxUnmapLbaCount = RT_H2BE_U32(VSCSI_UNMAP_LBAS_MAX(pVScsiLunSbc->cbSector));
108 pBlkPage->u32MaxUnmapBlkDescCount = UINT32_C(0xffffffff);
109 pBlkPage->u32OptUnmapGranularity = 0;
110 pBlkPage->u32UnmapGranularityAlignment = 0;
111 cVpdPages++;
112 }
113
114 if (RT_SUCCESS(rc))
115 {
116 rc = vscsiVpdPagePoolAllocNewPage(&pVScsiLunSbc->VpdPagePool, VSCSI_VPD_BLOCK_PROV_NUMBER,
117 VSCSI_VPD_BLOCK_PROV_SIZE, (uint8_t **)&pBlkProvPage);
118 if (RT_SUCCESS(rc))
119 {
120 pBlkProvPage->u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS;
121 pBlkProvPage->u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
122 pBlkProvPage->u16PageLength = RT_H2BE_U16(0x4);
123 pBlkProvPage->u8ThresholdExponent = 1;
124 pBlkProvPage->fLBPU = true;
125 cVpdPages++;
126 }
127 }
128 }
129
130 if ( RT_SUCCESS(rc)
131 && (pVScsiLun->fFeatures & VSCSI_LUN_FEATURE_NON_ROTATIONAL))
132 {
133 PVSCSIVPDPAGEBLOCKCHARACTERISTICS pBlkPage;
134
135 /* Create the page and fill it. */
136 rc = vscsiVpdPagePoolAllocNewPage(&pVScsiLunSbc->VpdPagePool, VSCSI_VPD_BLOCK_CHARACTERISTICS_NUMBER,
137 VSCSI_VPD_BLOCK_CHARACTERISTICS_SIZE, (uint8_t **)&pBlkPage);
138 if (RT_SUCCESS(rc))
139 {
140 pBlkPage->u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS;
141 pBlkPage->u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
142 pBlkPage->u16PageLength = RT_H2BE_U16(0x3c);
143 pBlkPage->u16MediumRotationRate = RT_H2BE_U16(VSCSI_VPD_BLOCK_CHARACT_MEDIUM_ROTATION_RATE_NON_ROTATING);
144 cVpdPages++;
145 }
146 }
147
148 if ( RT_SUCCESS(rc)
149 && cVpdPages)
150 {
151 PVSCSIVPDPAGESUPPORTEDPAGES pVpdPages;
152
153 rc = vscsiVpdPagePoolAllocNewPage(&pVScsiLunSbc->VpdPagePool, VSCSI_VPD_SUPPORTED_PAGES_NUMBER,
154 VSCSI_VPD_SUPPORTED_PAGES_SIZE + cVpdPages, (uint8_t **)&pVpdPages);
155 if (RT_SUCCESS(rc))
156 {
157 unsigned idxVpdPage = 0;
158 pVpdPages->u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS;
159 pVpdPages->u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
160 pVpdPages->u16PageLength = RT_H2BE_U16(cVpdPages);
161
162 pVpdPages->abVpdPages[idxVpdPage++] = VSCSI_VPD_DEVID_NUMBER;
163
164 if (pVScsiLun->fFeatures & VSCSI_LUN_FEATURE_UNMAP)
165 {
166 pVpdPages->abVpdPages[idxVpdPage++] = VSCSI_VPD_BLOCK_LIMITS_NUMBER;
167 pVpdPages->abVpdPages[idxVpdPage++] = VSCSI_VPD_BLOCK_PROV_NUMBER;
168 }
169
170 if (pVScsiLun->fFeatures & VSCSI_LUN_FEATURE_NON_ROTATIONAL)
171 pVpdPages->abVpdPages[idxVpdPage++] = VSCSI_VPD_BLOCK_CHARACTERISTICS_NUMBER;
172 }
173 }
174
175 /* For SBC LUNs, there will be no ready state transitions. */
176 pVScsiLunSbc->Core.fReady = true;
177
178 return rc;
179}
180
181static DECLCALLBACK(int) vscsiLunSbcDestroy(PVSCSILUNINT pVScsiLun)
182{
183 PVSCSILUNSBC pVScsiLunSbc = (PVSCSILUNSBC)pVScsiLun;
184
185 vscsiVpdPagePoolDestroy(&pVScsiLunSbc->VpdPagePool);
186
187 return VINF_SUCCESS;
188}
189
190static DECLCALLBACK(int) vscsiLunSbcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
191{
192 PVSCSILUNSBC pVScsiLunSbc = (PVSCSILUNSBC)pVScsiLun;
193 int rc = VINF_SUCCESS;
194 int rcReq = SCSI_STATUS_OK;
195 uint64_t uLbaStart = 0;
196 uint32_t cSectorTransfer = 0;
197 VSCSIIOREQTXDIR enmTxDir = VSCSIIOREQTXDIR_INVALID;
198
199 switch(pVScsiReq->pbCDB[0])
200 {
201 case SCSI_INQUIRY:
202 {
203 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
204
205 /* Check for EVPD bit. */
206 if (pVScsiReq->pbCDB[1] & 0x1)
207 {
208 rc = vscsiVpdPagePoolQueryPage(&pVScsiLunSbc->VpdPagePool, pVScsiReq, pVScsiReq->pbCDB[2]);
209 if (RT_UNLIKELY(rc == VERR_NOT_FOUND))
210 {
211 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
212 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
213 rc = VINF_SUCCESS;
214 }
215 else
216 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
217 }
218 else if (pVScsiReq->pbCDB[2] != 0) /* A non zero page code is an error. */
219 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
220 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
221 else
222 {
223 SCSIINQUIRYDATA ScsiInquiryReply;
224
225 vscsiReqSetXferSize(pVScsiReq, RT_MIN(sizeof(SCSIINQUIRYDATA), scsiBE2H_U16(&pVScsiReq->pbCDB[3])));
226 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
227
228 ScsiInquiryReply.cbAdditional = 31;
229 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS;
230 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
231 ScsiInquiryReply.u3AnsiVersion = 0x05; /* SPC-4 compliant */
232 ScsiInquiryReply.fCmdQue = 1; /* Command queuing supported. */
233 ScsiInquiryReply.fWBus16 = 1;
234
235 const char *pszVendorId = "VBOX";
236 const char *pszProductId = "HARDDISK";
237 const char *pszProductLevel = "1.0";
238 int rcTmp = vscsiLunQueryInqStrings(pVScsiLun, &pszVendorId, &pszProductId, &pszProductLevel);
239 Assert(RT_SUCCESS(rcTmp) || rcTmp == VERR_NOT_FOUND); RT_NOREF(rcTmp);
240
241 scsiPadStrS(ScsiInquiryReply.achVendorId, pszVendorId, 8);
242 scsiPadStrS(ScsiInquiryReply.achProductId, pszProductId, 16);
243 scsiPadStrS(ScsiInquiryReply.achProductLevel, pszProductLevel, 4);
244
245 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
246 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
247 }
248 break;
249 }
250 case SCSI_READ_CAPACITY:
251 {
252 uint8_t aReply[8];
253 memset(aReply, 0, sizeof(aReply));
254
255 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
256 vscsiReqSetXferSize(pVScsiReq, sizeof(aReply));
257
258 /*
259 * If sector size exceeds the maximum value that is
260 * able to be stored in 4 bytes return 0xffffffff in this field
261 */
262 if (pVScsiLunSbc->cSectors > UINT32_C(0xffffffff))
263 scsiH2BE_U32(aReply, UINT32_C(0xffffffff));
264 else
265 scsiH2BE_U32(aReply, pVScsiLunSbc->cSectors - 1);
266 scsiH2BE_U32(&aReply[4], (uint32_t)pVScsiLunSbc->cbSector);
267 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
268 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
269 break;
270 }
271 case SCSI_MODE_SENSE_6:
272 {
273 uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
274 uint8_t aReply[24];
275 uint8_t *pu8ReplyPos;
276 bool fValid = false;
277
278 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
279 vscsiReqSetXferSize(pVScsiReq, pVScsiReq->pbCDB[4]);
280 memset(aReply, 0, sizeof(aReply));
281 aReply[0] = 4; /* Reply length 4. */
282 aReply[1] = 0; /* Default media type. */
283 aReply[2] = RT_BIT(4); /* Caching supported. */
284 aReply[3] = 0; /* Block descriptor length. */
285
286 if (pVScsiLun->fFeatures & VSCSI_LUN_FEATURE_READONLY)
287 aReply[2] |= RT_BIT(7); /* Set write protect bit */
288
289 pu8ReplyPos = aReply + 4;
290
291 if ((uModePage == 0x08) || (uModePage == 0x3f))
292 {
293 memset(pu8ReplyPos, 0, 20);
294 *pu8ReplyPos++ = 0x08; /* Page code. */
295 *pu8ReplyPos++ = 0x12; /* Size of the page. */
296 *pu8ReplyPos++ = 0x4; /* Write cache enabled. */
297 fValid = true;
298 } else if (uModePage == 0) {
299 fValid = true;
300 }
301
302 /* Querying unknown pages must fail. */
303 if (fValid) {
304 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
305 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
306 } else {
307 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
308 }
309 break;
310 }
311 case SCSI_MODE_SELECT_6:
312 {
313 uint8_t abParms[12];
314 size_t cbCopied;
315 size_t cbList = pVScsiReq->pbCDB[4];
316
317 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_I2T);
318 vscsiReqSetXferSize(pVScsiReq, pVScsiReq->pbCDB[4]);
319
320 /* Copy the parameters. */
321 cbCopied = RTSgBufCopyToBuf(&pVScsiReq->SgBuf, &abParms[0], sizeof(abParms));
322
323 /* Handle short LOGICAL BLOCK LENGTH parameter. */
324 if ( !(pVScsiReq->pbCDB[1] & 0x01)
325 && cbCopied == sizeof(abParms)
326 && cbList >= 12
327 && abParms[3] == 8)
328 {
329 uint32_t cbBlock;
330
331 cbBlock = scsiBE2H_U24(&abParms[4 + 5]);
332 Log2(("SBC: set LOGICAL BLOCK LENGTH to %u\n", cbBlock));
333 if (cbBlock == 512) /* Fixed block size. */
334 {
335 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
336 break;
337 }
338 }
339 /* Fail any other requests. */
340 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
341 break;
342 }
343 case SCSI_READ_6:
344 {
345 enmTxDir = VSCSIIOREQTXDIR_READ;
346 uLbaStart = ((uint64_t) pVScsiReq->pbCDB[3]
347 | (pVScsiReq->pbCDB[2] << 8)
348 | ((pVScsiReq->pbCDB[1] & 0x1f) << 16));
349 cSectorTransfer = pVScsiReq->pbCDB[4];
350 break;
351 }
352 case SCSI_READ_10:
353 {
354 enmTxDir = VSCSIIOREQTXDIR_READ;
355 uLbaStart = scsiBE2H_U32(&pVScsiReq->pbCDB[2]);
356 cSectorTransfer = scsiBE2H_U16(&pVScsiReq->pbCDB[7]);
357 break;
358 }
359 case SCSI_READ_12:
360 {
361 enmTxDir = VSCSIIOREQTXDIR_READ;
362 uLbaStart = scsiBE2H_U32(&pVScsiReq->pbCDB[2]);
363 cSectorTransfer = scsiBE2H_U32(&pVScsiReq->pbCDB[6]);
364 break;
365 }
366 case SCSI_READ_16:
367 {
368 enmTxDir = VSCSIIOREQTXDIR_READ;
369 uLbaStart = scsiBE2H_U64(&pVScsiReq->pbCDB[2]);
370 cSectorTransfer = scsiBE2H_U32(&pVScsiReq->pbCDB[10]);
371 break;
372 }
373 case SCSI_WRITE_6:
374 {
375 enmTxDir = VSCSIIOREQTXDIR_WRITE;
376 uLbaStart = ((uint64_t) pVScsiReq->pbCDB[3]
377 | (pVScsiReq->pbCDB[2] << 8)
378 | ((pVScsiReq->pbCDB[1] & 0x1f) << 16));
379 cSectorTransfer = pVScsiReq->pbCDB[4];
380 break;
381 }
382 case SCSI_WRITE_10:
383 {
384 enmTxDir = VSCSIIOREQTXDIR_WRITE;
385 uLbaStart = scsiBE2H_U32(&pVScsiReq->pbCDB[2]);
386 cSectorTransfer = scsiBE2H_U16(&pVScsiReq->pbCDB[7]);
387 break;
388 }
389 case SCSI_WRITE_12:
390 {
391 enmTxDir = VSCSIIOREQTXDIR_WRITE;
392 uLbaStart = scsiBE2H_U32(&pVScsiReq->pbCDB[2]);
393 cSectorTransfer = scsiBE2H_U32(&pVScsiReq->pbCDB[6]);
394 break;
395 }
396 case SCSI_WRITE_16:
397 {
398 enmTxDir = VSCSIIOREQTXDIR_WRITE;
399 uLbaStart = scsiBE2H_U64(&pVScsiReq->pbCDB[2]);
400 cSectorTransfer = scsiBE2H_U32(&pVScsiReq->pbCDB[10]);
401 break;
402 }
403 case SCSI_SYNCHRONIZE_CACHE:
404 {
405 break; /* Handled below */
406 }
407 case SCSI_READ_BUFFER:
408 {
409 uint8_t uDataMode = pVScsiReq->pbCDB[1] & 0x1f;
410
411 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
412 vscsiReqSetXferSize(pVScsiReq, scsiBE2H_U16(&pVScsiReq->pbCDB[6]));
413
414 switch (uDataMode)
415 {
416 case 0x00:
417 case 0x01:
418 case 0x02:
419 case 0x03:
420 case 0x0a:
421 break;
422 case 0x0b:
423 {
424 uint8_t aReply[4];
425
426 /* We do not implement an echo buffer. */
427 memset(aReply, 0, sizeof(aReply));
428
429 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
430 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
431 break;
432 }
433 case 0x1a:
434 case 0x1c:
435 break;
436 default:
437 AssertMsgFailed(("Invalid data mode\n"));
438 }
439 break;
440 }
441 case SCSI_VERIFY_10:
442 case SCSI_START_STOP_UNIT:
443 {
444 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_NONE);
445 vscsiReqSetXferSize(pVScsiReq, 0);
446 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
447 break;
448 }
449 case SCSI_LOG_SENSE:
450 {
451 uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f;
452 uint8_t uSubPageCode = pVScsiReq->pbCDB[3];
453
454 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
455 vscsiReqSetXferSize(pVScsiReq, scsiBE2H_U16(&pVScsiReq->pbCDB[7]));
456
457 switch (uPageCode)
458 {
459 case 0x00:
460 {
461 if (uSubPageCode == 0)
462 {
463 uint8_t aReply[4];
464
465 aReply[0] = 0;
466 aReply[1] = 0;
467 aReply[2] = 0;
468 aReply[3] = 0;
469 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
470 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
471 break;
472 }
473 }
474 RT_FALL_THRU();
475 default:
476 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
477 }
478 break;
479 }
480 case SCSI_SERVICE_ACTION_IN_16:
481 {
482 switch (pVScsiReq->pbCDB[1] & 0x1f)
483 {
484 case SCSI_SVC_ACTION_IN_READ_CAPACITY_16:
485 {
486 uint8_t aReply[32];
487
488 memset(aReply, 0, sizeof(aReply));
489 scsiH2BE_U64(aReply, pVScsiLunSbc->cSectors - 1);
490 scsiH2BE_U32(&aReply[8], 512);
491 if (pVScsiLun->fFeatures & VSCSI_LUN_FEATURE_UNMAP)
492 aReply[14] = 0x80; /* LPME enabled */
493 /* Leave the rest 0 */
494
495 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
496 vscsiReqSetXferSize(pVScsiReq, sizeof(aReply));
497 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
498 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
499 break;
500 }
501 default:
502 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); /* Don't know if this is correct */
503 }
504 break;
505 }
506 case SCSI_UNMAP:
507 {
508 if (pVScsiLun->fFeatures & VSCSI_LUN_FEATURE_UNMAP)
509 {
510 uint8_t abHdr[8];
511 size_t cbCopied;
512 size_t cbList = scsiBE2H_U16(&pVScsiReq->pbCDB[7]);
513
514 /* Copy the header. */
515 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_I2T);
516 vscsiReqSetXferSize(pVScsiReq, cbList);
517 cbCopied = RTSgBufCopyToBuf(&pVScsiReq->SgBuf, &abHdr[0], sizeof(abHdr));
518
519 /* Using the anchor bit is not supported. */
520 if ( !(pVScsiReq->pbCDB[1] & 0x01)
521 && cbCopied == sizeof(abHdr)
522 && cbList >= 8)
523 {
524 uint32_t cBlkDesc = scsiBE2H_U16(&abHdr[2]) / 16;
525
526 if (cBlkDesc)
527 {
528 PRTRANGE paRanges = (PRTRANGE)RTMemAllocZ(cBlkDesc * sizeof(RTRANGE));
529 if (paRanges)
530 {
531 for (unsigned i = 0; i < cBlkDesc; i++)
532 {
533 uint8_t abBlkDesc[16];
534
535 cbCopied = RTSgBufCopyToBuf(&pVScsiReq->SgBuf, &abBlkDesc[0], sizeof(abBlkDesc));
536 if (RT_UNLIKELY(cbCopied != sizeof(abBlkDesc)))
537 {
538 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
539 break;
540 }
541
542 paRanges[i].offStart = scsiBE2H_U64(&abBlkDesc[0]) * 512;
543 paRanges[i].cbRange = scsiBE2H_U32(&abBlkDesc[8]) * 512;
544 }
545
546 if (rcReq == SCSI_STATUS_OK)
547 rc = vscsiIoReqUnmapEnqueue(pVScsiLun, pVScsiReq, paRanges, cBlkDesc);
548 if ( rcReq != SCSI_STATUS_OK
549 || RT_FAILURE(rc))
550 RTMemFree(paRanges);
551 }
552 else /* Out of memory. */
553 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_HARDWARE_ERROR, SCSI_ASC_SYSTEM_RESOURCE_FAILURE,
554 SCSI_ASCQ_SYSTEM_BUFFER_FULL);
555 }
556 else /* No block descriptors is not an error condition. */
557 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
558 }
559 else /* Invalid CDB. */
560 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
561 }
562 else
563 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
564
565 break;
566 }
567 default:
568 //AssertMsgFailed(("Command %#x [%s] not implemented\n", pRequest->pbCDB[0], SCSICmdText(pRequest->pbCDB[0])));
569 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
570 }
571
572 if (enmTxDir != VSCSIIOREQTXDIR_INVALID)
573 {
574 LogFlow(("%s: uLbaStart=%llu cSectorTransfer=%u\n",
575 __FUNCTION__, uLbaStart, cSectorTransfer));
576
577 vscsiReqSetXferSize(pVScsiReq, cSectorTransfer * 512);
578
579 if (RT_UNLIKELY(uLbaStart + cSectorTransfer > pVScsiLunSbc->cSectors))
580 {
581 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_NONE);
582 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
583 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
584 }
585 else if (!cSectorTransfer)
586 {
587 /* A 0 transfer length is not an error. */
588 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_NONE);
589 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
590 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
591 }
592 else
593 {
594 /* Enqueue new I/O request */
595 if ( ( enmTxDir == VSCSIIOREQTXDIR_WRITE
596 || enmTxDir == VSCSIIOREQTXDIR_FLUSH)
597 && (pVScsiLun->fFeatures & VSCSI_LUN_FEATURE_READONLY))
598 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_DATA_PROTECT, SCSI_ASC_WRITE_PROTECTED, 0x00);
599 else
600 {
601 vscsiReqSetXferDir(pVScsiReq, enmTxDir == VSCSIIOREQTXDIR_WRITE ? VSCSIXFERDIR_I2T : VSCSIXFERDIR_T2I);
602 rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir,
603 uLbaStart * 512, cSectorTransfer * 512);
604 }
605 }
606 }
607 else if (pVScsiReq->pbCDB[0] == SCSI_SYNCHRONIZE_CACHE)
608 {
609 /* Enqueue flush */
610 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_NONE);
611 vscsiReqSetXferSize(pVScsiReq, 0);
612 rc = vscsiIoReqFlushEnqueue(pVScsiLun, pVScsiReq);
613 }
614 else if (pVScsiReq->pbCDB[0] != SCSI_UNMAP) /* Request completed */
615 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
616
617 return rc;
618}
619
620VSCSILUNDESC g_VScsiLunTypeSbc =
621{
622 /** enmLunType */
623 VSCSILUNTYPE_SBC,
624 /** pcszDescName */
625 "SBC",
626 /** cbLun */
627 sizeof(VSCSILUNSBC),
628 /** cSupOpcInfo */
629 0,
630 /** paSupOpcInfo */
631 NULL,
632 /** pfnVScsiLunInit */
633 vscsiLunSbcInit,
634 /** pfnVScsiLunDestroy */
635 vscsiLunSbcDestroy,
636 /** pfnVScsiLunReqProcess */
637 vscsiLunSbcReqProcess,
638 /** pfnVScsiLunReqFree */
639 NULL,
640 /** pfnVScsiLunMediumInserted */
641 NULL,
642 /** pfnVScsiLunMediumRemoved */
643 NULL
644};
645
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