VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 6 days ago

Copyright year updates by scm.

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