VirtualBox

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

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

scm copyright and license note update

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