VirtualBox

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

Last change on this file since 104222 was 104222, checked in by vboxsync, 8 months ago

Devices/Storage/VSCSI: Be more strict when it comes to CDB lengths for individual commands, bugref:10613 [scm]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.2 KB
Line 
1/* $Id: VSCSILunSbc.cpp 104222 2024-04-08 10:21:06Z vboxsync $ */
2/** @file
3 * Virtual SCSI driver: SBC LUN implementation (hard disks)
4 */
5
6/*
7 * Copyright (C) 2006-2023 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 if (pVScsiReq->cbCDB >= 5)
234 {
235 SCSIINQUIRYDATA ScsiInquiryReply;
236
237 vscsiReqSetXferSize(pVScsiReq, RT_MIN(sizeof(SCSIINQUIRYDATA), scsiBE2H_U16(&pVScsiReq->pbCDB[3])));
238 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
239
240 ScsiInquiryReply.cbAdditional = 31;
241 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS;
242 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
243 ScsiInquiryReply.u3AnsiVersion = 0x05; /* SPC-4 compliant */
244 ScsiInquiryReply.fCmdQue = 1; /* Command queuing supported. */
245 ScsiInquiryReply.fWBus16 = 1;
246
247 const char *pszVendorId = "VBOX";
248 const char *pszProductId = "HARDDISK";
249 const char *pszProductLevel = "1.0";
250 int rcTmp = vscsiLunQueryInqStrings(pVScsiLun, &pszVendorId, &pszProductId, &pszProductLevel);
251 Assert(RT_SUCCESS(rcTmp) || rcTmp == VERR_NOT_FOUND); RT_NOREF(rcTmp);
252
253 scsiPadStrS(ScsiInquiryReply.achVendorId, pszVendorId, 8);
254 scsiPadStrS(ScsiInquiryReply.achProductId, pszProductId, 16);
255 scsiPadStrS(ScsiInquiryReply.achProductLevel, pszProductLevel, 4);
256
257 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
258 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
259 }
260 else
261 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INVALID_MESSAGE, 0x00);
262 }
263 break;
264 }
265 case SCSI_READ_CAPACITY:
266 {
267 uint8_t aReply[8];
268 memset(aReply, 0, sizeof(aReply));
269
270 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
271 vscsiReqSetXferSize(pVScsiReq, sizeof(aReply));
272
273 /*
274 * If sector size exceeds the maximum value that is
275 * able to be stored in 4 bytes return 0xffffffff in this field
276 */
277 if (pVScsiLunSbc->cSectors > UINT32_C(0xffffffff))
278 scsiH2BE_U32(aReply, UINT32_C(0xffffffff));
279 else
280 scsiH2BE_U32(aReply, pVScsiLunSbc->cSectors - 1);
281 scsiH2BE_U32(&aReply[4], (uint32_t)pVScsiLunSbc->cbSector);
282 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
283 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
284 break;
285 }
286 case SCSI_MODE_SENSE_6:
287 {
288 uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
289 uint8_t aReply[24];
290 uint8_t *pu8ReplyPos;
291 bool fValid = false;
292
293 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
294 vscsiReqSetXferSize(pVScsiReq, pVScsiReq->pbCDB[4]);
295 memset(aReply, 0, sizeof(aReply));
296 aReply[0] = 4; /* Reply length 4. */
297 aReply[1] = 0; /* Default media type. */
298 aReply[2] = RT_BIT(4); /* Caching supported. */
299 aReply[3] = 0; /* Block descriptor length. */
300
301 if (pVScsiLun->fFeatures & VSCSI_LUN_FEATURE_READONLY)
302 aReply[2] |= RT_BIT(7); /* Set write protect bit */
303
304 pu8ReplyPos = aReply + 4;
305
306 if ((uModePage == 0x08) || (uModePage == 0x3f))
307 {
308 memset(pu8ReplyPos, 0, 20);
309 *pu8ReplyPos++ = 0x08; /* Page code. */
310 *pu8ReplyPos++ = 0x12; /* Size of the page. */
311 *pu8ReplyPos++ = 0x4; /* Write cache enabled. */
312 fValid = true;
313 } else if (uModePage == 0) {
314 fValid = true;
315 }
316
317 /* Querying unknown pages must fail. */
318 if (fValid) {
319 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
320 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
321 } else {
322 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
323 }
324 break;
325 }
326 case SCSI_MODE_SELECT_6:
327 {
328 uint8_t abParms[12];
329 size_t cbCopied;
330 size_t cbList = pVScsiReq->pbCDB[4];
331
332 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_I2T);
333 vscsiReqSetXferSize(pVScsiReq, pVScsiReq->pbCDB[4]);
334
335 /* Copy the parameters. */
336 cbCopied = RTSgBufCopyToBuf(&pVScsiReq->SgBuf, &abParms[0], sizeof(abParms));
337
338 /* Handle short LOGICAL BLOCK LENGTH parameter. */
339 if ( !(pVScsiReq->pbCDB[1] & 0x01)
340 && cbCopied == sizeof(abParms)
341 && cbList >= 12
342 && abParms[3] == 8)
343 {
344 uint32_t cbBlock;
345
346 cbBlock = scsiBE2H_U24(&abParms[4 + 5]);
347 Log2(("SBC: set LOGICAL BLOCK LENGTH to %u\n", cbBlock));
348 if (cbBlock == 512) /* Fixed block size. */
349 {
350 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
351 break;
352 }
353 }
354 /* Fail any other requests. */
355 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
356 break;
357 }
358 case SCSI_READ_6:
359 {
360 enmTxDir = VSCSIIOREQTXDIR_READ;
361 uLbaStart = ((uint64_t) pVScsiReq->pbCDB[3]
362 | (pVScsiReq->pbCDB[2] << 8)
363 | ((pVScsiReq->pbCDB[1] & 0x1f) << 16));
364 cSectorTransfer = pVScsiReq->pbCDB[4];
365 cSectorTransfer = cSectorTransfer ? cSectorTransfer : 256; /* Zero blocks means 256 */
366 break;
367 }
368 case SCSI_READ_10:
369 {
370 enmTxDir = VSCSIIOREQTXDIR_READ;
371 uLbaStart = scsiBE2H_U32(&pVScsiReq->pbCDB[2]);
372 cSectorTransfer = scsiBE2H_U16(&pVScsiReq->pbCDB[7]);
373 break;
374 }
375 case SCSI_READ_12:
376 {
377 enmTxDir = VSCSIIOREQTXDIR_READ;
378 uLbaStart = scsiBE2H_U32(&pVScsiReq->pbCDB[2]);
379 cSectorTransfer = scsiBE2H_U32(&pVScsiReq->pbCDB[6]);
380 break;
381 }
382 case SCSI_READ_16:
383 {
384 enmTxDir = VSCSIIOREQTXDIR_READ;
385 uLbaStart = scsiBE2H_U64(&pVScsiReq->pbCDB[2]);
386 cSectorTransfer = scsiBE2H_U32(&pVScsiReq->pbCDB[10]);
387 break;
388 }
389 case SCSI_WRITE_6:
390 {
391 enmTxDir = VSCSIIOREQTXDIR_WRITE;
392 uLbaStart = ((uint64_t) pVScsiReq->pbCDB[3]
393 | (pVScsiReq->pbCDB[2] << 8)
394 | ((pVScsiReq->pbCDB[1] & 0x1f) << 16));
395 cSectorTransfer = pVScsiReq->pbCDB[4];
396 cSectorTransfer = cSectorTransfer ? cSectorTransfer : 256; /* Zero blocks means 256 */
397 break;
398 }
399 case SCSI_WRITE_10:
400 {
401 enmTxDir = VSCSIIOREQTXDIR_WRITE;
402 uLbaStart = scsiBE2H_U32(&pVScsiReq->pbCDB[2]);
403 cSectorTransfer = scsiBE2H_U16(&pVScsiReq->pbCDB[7]);
404 break;
405 }
406 case SCSI_WRITE_12:
407 {
408 enmTxDir = VSCSIIOREQTXDIR_WRITE;
409 uLbaStart = scsiBE2H_U32(&pVScsiReq->pbCDB[2]);
410 cSectorTransfer = scsiBE2H_U32(&pVScsiReq->pbCDB[6]);
411 break;
412 }
413 case SCSI_WRITE_16:
414 {
415 enmTxDir = VSCSIIOREQTXDIR_WRITE;
416 uLbaStart = scsiBE2H_U64(&pVScsiReq->pbCDB[2]);
417 cSectorTransfer = scsiBE2H_U32(&pVScsiReq->pbCDB[10]);
418 break;
419 }
420 case SCSI_SYNCHRONIZE_CACHE:
421 {
422 break; /* Handled below */
423 }
424 case SCSI_READ_BUFFER:
425 {
426 uint8_t uDataMode = pVScsiReq->pbCDB[1] & 0x1f;
427
428 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
429 vscsiReqSetXferSize(pVScsiReq, scsiBE2H_U24(&pVScsiReq->pbCDB[6]));
430
431 switch (uDataMode)
432 {
433 case 0x00:
434 case 0x01:
435 case 0x02:
436 case 0x03:
437 case 0x0a:
438 break;
439 case 0x0b:
440 {
441 uint8_t aReply[4];
442
443 /* We do not implement an echo buffer. */
444 memset(aReply, 0, sizeof(aReply));
445
446 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
447 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
448 break;
449 }
450 case 0x1a:
451 case 0x1c:
452 break;
453 default:
454 AssertMsgFailed(("Invalid data mode\n"));
455 }
456 break;
457 }
458 case SCSI_VERIFY_10:
459 case SCSI_START_STOP_UNIT:
460 {
461 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_NONE);
462 vscsiReqSetXferSize(pVScsiReq, 0);
463 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
464 break;
465 }
466 case SCSI_LOG_SENSE:
467 {
468 uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f;
469 uint8_t uSubPageCode = pVScsiReq->pbCDB[3];
470
471 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
472 vscsiReqSetXferSize(pVScsiReq, scsiBE2H_U16(&pVScsiReq->pbCDB[7]));
473
474 switch (uPageCode)
475 {
476 case 0x00:
477 {
478 if (uSubPageCode == 0)
479 {
480 uint8_t aReply[4];
481
482 aReply[0] = 0;
483 aReply[1] = 0;
484 aReply[2] = 0;
485 aReply[3] = 0;
486 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
487 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
488 break;
489 }
490 }
491 RT_FALL_THRU();
492 default:
493 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
494 }
495 break;
496 }
497 case SCSI_SERVICE_ACTION_IN_16:
498 {
499 switch (pVScsiReq->pbCDB[1] & 0x1f)
500 {
501 case SCSI_SVC_ACTION_IN_READ_CAPACITY_16:
502 {
503 uint8_t aReply[32];
504
505 memset(aReply, 0, sizeof(aReply));
506 scsiH2BE_U64(aReply, pVScsiLunSbc->cSectors - 1);
507 scsiH2BE_U32(&aReply[8], 512);
508 if (pVScsiLun->fFeatures & VSCSI_LUN_FEATURE_UNMAP)
509 aReply[14] = 0x80; /* LPME enabled */
510 /* Leave the rest 0 */
511
512 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_T2I);
513 vscsiReqSetXferSize(pVScsiReq, sizeof(aReply));
514 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
515 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
516 break;
517 }
518 default:
519 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); /* Don't know if this is correct */
520 }
521 break;
522 }
523 case SCSI_UNMAP:
524 {
525 if (pVScsiLun->fFeatures & VSCSI_LUN_FEATURE_UNMAP)
526 {
527 uint8_t abHdr[8];
528 size_t cbCopied;
529 size_t cbList = scsiBE2H_U16(&pVScsiReq->pbCDB[7]);
530
531 /* Copy the header. */
532 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_I2T);
533 vscsiReqSetXferSize(pVScsiReq, cbList);
534 cbCopied = RTSgBufCopyToBuf(&pVScsiReq->SgBuf, &abHdr[0], sizeof(abHdr));
535
536 /* Using the anchor bit is not supported. */
537 if ( !(pVScsiReq->pbCDB[1] & 0x01)
538 && cbCopied == sizeof(abHdr)
539 && cbList >= 8)
540 {
541 uint32_t cBlkDesc = scsiBE2H_U16(&abHdr[2]) / 16;
542
543 if (cBlkDesc)
544 {
545 PRTRANGE paRanges = (PRTRANGE)RTMemAllocZ(cBlkDesc * sizeof(RTRANGE));
546 if (paRanges)
547 {
548 for (unsigned i = 0; i < cBlkDesc; i++)
549 {
550 uint8_t abBlkDesc[16];
551
552 cbCopied = RTSgBufCopyToBuf(&pVScsiReq->SgBuf, &abBlkDesc[0], sizeof(abBlkDesc));
553 if (RT_UNLIKELY(cbCopied != sizeof(abBlkDesc)))
554 {
555 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
556 break;
557 }
558
559 paRanges[i].offStart = scsiBE2H_U64(&abBlkDesc[0]) * 512;
560 paRanges[i].cbRange = scsiBE2H_U32(&abBlkDesc[8]) * 512;
561 }
562
563 if (rcReq == SCSI_STATUS_OK)
564 rc = vscsiIoReqUnmapEnqueue(pVScsiLun, pVScsiReq, paRanges, cBlkDesc);
565 if ( rcReq != SCSI_STATUS_OK
566 || RT_FAILURE(rc))
567 RTMemFree(paRanges);
568 }
569 else /* Out of memory. */
570 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_HARDWARE_ERROR, SCSI_ASC_SYSTEM_RESOURCE_FAILURE,
571 SCSI_ASCQ_SYSTEM_BUFFER_FULL);
572 }
573 else /* No block descriptors is not an error condition. */
574 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
575 }
576 else /* Invalid CDB. */
577 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
578 }
579 else
580 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
581
582 break;
583 }
584 default:
585 //AssertMsgFailed(("Command %#x [%s] not implemented\n", pRequest->pbCDB[0], SCSICmdText(pRequest->pbCDB[0])));
586 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
587 }
588
589 if (enmTxDir != VSCSIIOREQTXDIR_INVALID)
590 {
591 LogFlow(("%s: uLbaStart=%llu cSectorTransfer=%u\n",
592 __FUNCTION__, uLbaStart, cSectorTransfer));
593
594 vscsiReqSetXferSize(pVScsiReq, cSectorTransfer * 512);
595
596 if (RT_UNLIKELY(uLbaStart + cSectorTransfer > pVScsiLunSbc->cSectors))
597 {
598 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_NONE);
599 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
600 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
601 }
602 else if (!cSectorTransfer)
603 {
604 /* A 0 transfer length is not an error. */
605 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_NONE);
606 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
607 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
608 }
609 else
610 {
611 /* Enqueue new I/O request */
612 if ( ( enmTxDir == VSCSIIOREQTXDIR_WRITE
613 || enmTxDir == VSCSIIOREQTXDIR_FLUSH)
614 && (pVScsiLun->fFeatures & VSCSI_LUN_FEATURE_READONLY))
615 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_DATA_PROTECT, SCSI_ASC_WRITE_PROTECTED, 0x00);
616 else
617 {
618 vscsiReqSetXferDir(pVScsiReq, enmTxDir == VSCSIIOREQTXDIR_WRITE ? VSCSIXFERDIR_I2T : VSCSIXFERDIR_T2I);
619 rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir,
620 uLbaStart * 512, cSectorTransfer * 512);
621 }
622 }
623 }
624 else if (pVScsiReq->pbCDB[0] == SCSI_SYNCHRONIZE_CACHE)
625 {
626 /* Enqueue flush */
627 vscsiReqSetXferDir(pVScsiReq, VSCSIXFERDIR_NONE);
628 vscsiReqSetXferSize(pVScsiReq, 0);
629 rc = vscsiIoReqFlushEnqueue(pVScsiLun, pVScsiReq);
630 }
631 else if (pVScsiReq->pbCDB[0] != SCSI_UNMAP) /* Request completed */
632 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
633
634 return rc;
635}
636
637
638/**
639 * The supported operation codes for the SBC LUN type.
640 *
641 * @note This gives the minimum size required by our implementation
642 * which may be smaller than what the spec defines (for example
643 * we do not access the control byte at the end).
644 */
645static uint8_t s_acbCdbOpc[] =
646{
647 VSCSI_LUN_CDB_SZ_INVALID_X8, /**< 0x00 - 0x07 Invalid */
648 5, /**< 0x08 READ (6) */
649 VSCSI_LUN_CDB_SZ_INVALID, /**< 0x09 Invalid */
650 5, /**< 0x0a WRITE (6) */
651 VSCSI_LUN_CDB_SZ_INVALID, /**< 0x0b Invalid */
652 VSCSI_LUN_CDB_SZ_INVALID_X4, /**< 0x0c - 0x0f Invalid */
653
654 VSCSI_LUN_CDB_SZ_INVALID_X2, /**< 0x10 - 0x11 Invalid */
655 3, /**< 0x12 INQUIRY (at least 3) */
656 VSCSI_LUN_CDB_SZ_INVALID_X2, /**< 0x13 - 0x14 Invalid */
657 5, /**< 0x15 MODE SELECT (6) */
658 VSCSI_LUN_CDB_SZ_INVALID_X4, /**< 0x16 - 0x19 Invalid */
659 5, /**< 0x1a MODE SENSE (6) */
660 1, /**< 0x1b START STOP UNIT */
661 VSCSI_LUN_CDB_SZ_INVALID_X4, /**< 0x1c - 0x1f Invalid */
662
663 VSCSI_LUN_CDB_SZ_INVALID_X4, /**< 0x20 - 0x23 Invalid */
664 VSCSI_LUN_CDB_SZ_INVALID, /**< 0x24 Invalid */
665 1, /**< 0x25 READ CAPACITY */
666 VSCSI_LUN_CDB_SZ_INVALID_X2, /**< 0x26 - 0x27 Invalid */
667 9, /**< 0x28 READ (10) */
668 VSCSI_LUN_CDB_SZ_INVALID, /**< 0x29 Invalid */
669 9, /**< 0x2a WRITE (10) */
670 VSCSI_LUN_CDB_SZ_INVALID_X4, /**< 0x2b - 0x2e Invalid */
671 1, /**< 0x2f VERIFY (10) */
672
673 VSCSI_LUN_CDB_SZ_INVALID_X4, /**< 0x30 - 0x33 Invalid */
674 VSCSI_LUN_CDB_SZ_INVALID, /**< 0x34 Invalid */
675 1, /**< 0x35 SYNCHRONIZE CACHE */
676 VSCSI_LUN_CDB_SZ_INVALID_X4, /**< 0x36 - 0x39 Invalid */
677 VSCSI_LUN_CDB_SZ_INVALID_X2, /**< 0x3a - 0x3b Invalid */
678 8, /**< 0x3c READ BUFFER */
679 VSCSI_LUN_CDB_SZ_INVALID, /**< 0x3d Invalid */
680 VSCSI_LUN_CDB_SZ_INVALID_X2, /**< 0x3e - 0x3f Invalid */
681
682 VSCSI_LUN_CDB_SZ_INVALID_X2, /**< 0x40 - 0x41 Invalid */
683 9, /**< 0x42 UNMAP */
684 VSCSI_LUN_CDB_SZ_INVALID, /**< 0x43 Invalid */
685 VSCSI_LUN_CDB_SZ_INVALID_X8, /**< 0x44 - 0x4b Invalid */
686 VSCSI_LUN_CDB_SZ_INVALID, /**< 0x4c Invalid */
687 9, /**< 0x4d LOG SENSE */
688 VSCSI_LUN_CDB_SZ_INVALID_X2, /**< 0x4e - 0x4f Invalid */
689
690 VSCSI_LUN_CDB_SZ_INVALID_X16, /**< 0x50 - 0x5f Invalid */
691 VSCSI_LUN_CDB_SZ_INVALID_X16, /**< 0x60 - 0x6f Invalid */
692 VSCSI_LUN_CDB_SZ_INVALID_X16, /**< 0x70 - 0x7f Invalid */
693
694 VSCSI_LUN_CDB_SZ_INVALID_X8, /**< 0x80 - 0x87 Invalid */
695 14, /**< 0x88 READ (16) */
696 VSCSI_LUN_CDB_SZ_INVALID, /**< 0x89 Invalid */
697 14, /**< 0x8a WRITE (16) */
698 VSCSI_LUN_CDB_SZ_INVALID, /**< 0x8b Invalid */
699 VSCSI_LUN_CDB_SZ_INVALID_X4, /**< 0x8c - 0x8f Invalid */
700
701 VSCSI_LUN_CDB_SZ_INVALID_X8, /**< 0x90 - 0x97 Invalid */
702 VSCSI_LUN_CDB_SZ_INVALID_X4, /**< 0x98 - 0x9b Invalid */
703 VSCSI_LUN_CDB_SZ_INVALID_X2, /**< 0x9c - 0x9d Invalid */
704 2, /**< 0x9e SERVICE ACTION IN (16) (at least 2). */
705 VSCSI_LUN_CDB_SZ_INVALID, /**< 0x9f Invalid */
706
707 VSCSI_LUN_CDB_SZ_INVALID_X8, /**< 0xa0 - 0xa7 Invalid */
708 10, /**< 0xa8 READ (12) */
709 VSCSI_LUN_CDB_SZ_INVALID, /**< 0xa9 Invalid */
710 10, /**< 0xaa WRITE (12) */
711 VSCSI_LUN_CDB_SZ_INVALID, /**< 0xab Invalid */
712 VSCSI_LUN_CDB_SZ_INVALID_X4, /**< 0xac - 0xaf Invalid */
713
714 VSCSI_LUN_CDB_SZ_INVALID_X16, /**< 0xb0 - 0xbf Invalid */
715 VSCSI_LUN_CDB_SZ_INVALID_X16, /**< 0xc0 - 0xcf Invalid */
716 VSCSI_LUN_CDB_SZ_INVALID_X16, /**< 0xd0 - 0xdf Invalid */
717 VSCSI_LUN_CDB_SZ_INVALID_X16, /**< 0xe0 - 0xef Invalid */
718 VSCSI_LUN_CDB_SZ_INVALID_X16 /**< 0xf0 - 0xff Invalid */
719};
720AssertCompileSize(s_acbCdbOpc, 256 * sizeof(uint8_t));
721
722
723VSCSILUNDESC g_VScsiLunTypeSbc =
724{
725 /** enmLunType */
726 VSCSILUNTYPE_SBC,
727 /** pcszDescName */
728 "SBC",
729 /** cbLun */
730 sizeof(VSCSILUNSBC),
731 /** pacbCdbOpc */
732 &s_acbCdbOpc[0],
733 /** cSupOpcInfo */
734 0,
735 /** paSupOpcInfo */
736 NULL,
737 /** pfnVScsiLunInit */
738 vscsiLunSbcInit,
739 /** pfnVScsiLunDestroy */
740 vscsiLunSbcDestroy,
741 /** pfnVScsiLunReqProcess */
742 vscsiLunSbcReqProcess,
743 /** pfnVScsiLunReqFree */
744 NULL,
745 /** pfnVScsiLunMediumInserted */
746 NULL,
747 /** pfnVScsiLunMediumRemoved */
748 NULL
749};
750
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