VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VSCSI/VSCSILunSsc.cpp@ 103882

Last change on this file since 103882 was 103882, checked in by vboxsync, 3 months ago

VSCSI: For READ(6) and WRITE(6), a request to transfer zero blocks means transferring 256 blocks.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.3 KB
Line 
1/* $Id: VSCSILunSsc.cpp 103882 2024-03-18 09:38:17Z vboxsync $ */
2/** @file
3 * Virtual SCSI driver: SSC LUN implementation (Streaming tape)
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/assert.h>
38#include <iprt/mem.h>
39#include <iprt/string.h>
40
41#include "VSCSIInternal.h"
42
43/**
44 * SSC LUN instance
45 */
46typedef struct VSCSILUNSSC
47{
48 /** Core LUN structure */
49 VSCSILUNINT Core;
50 /** Size of the virtual tape. */
51 uint64_t cbTape;
52 /** Current position. */
53 uint64_t uCurPos;
54 /** Number of blocks. */
55 uint64_t cBlocks;
56 /** Block size. */
57 uint64_t cbBlock;
58 /** Medium locked indicator. */
59 bool fLocked;
60} VSCSILUNSSC, *PVSCSILUNSSC;
61
62
63static int vscsiLUNSSCInit(PVSCSILUNINT pVScsiLun)
64{
65 PVSCSILUNSSC pVScsiLunSsc = (PVSCSILUNSSC)pVScsiLun;
66 int rc = VINF_SUCCESS;
67
68 pVScsiLunSsc->cbBlock = 512; /* Default to 512-byte blocks. */
69 pVScsiLunSsc->uCurPos = 0; /* Start at beginning of tape. */
70 pVScsiLunSsc->cbTape = 0;
71
72 uint32_t cRegions = vscsiLunMediumGetRegionCount(pVScsiLun);
73 if (cRegions != 1)
74 rc = VERR_INVALID_PARAMETER;
75
76 if (RT_SUCCESS(rc))
77 rc = vscsiLunMediumQueryRegionProperties(pVScsiLun, 0, NULL, &pVScsiLunSsc->cBlocks,
78 &pVScsiLunSsc->cbBlock, NULL);
79
80 if (RT_SUCCESS(rc))
81 pVScsiLunSsc->cbTape = pVScsiLunSsc->cBlocks * pVScsiLunSsc->cbBlock;
82
83 return rc;
84}
85
86static int vscsiLUNSSCDestroy(PVSCSILUNINT pVScsiLun)
87{
88 PVSCSILUNSSC pVScsiLUNSSC = (PVSCSILUNSSC)pVScsiLun;
89
90 pVScsiLUNSSC->uCurPos = 0; // shut compiler up
91
92 return VINF_SUCCESS;
93}
94
95static int vscsiLUNSSCReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
96{
97 PVSCSILUNSSC pVScsiLUNSSC = (PVSCSILUNSSC)pVScsiLun;
98 VSCSIIOREQTXDIR enmTxDir = VSCSIIOREQTXDIR_INVALID;
99 uint64_t uTransferStart = 0;
100 uint32_t cBlocksTransfer = 0;
101 uint32_t cbTransfer = 0;
102 int rc = VINF_SUCCESS;
103 int rcReq = SCSI_STATUS_OK;
104 unsigned uCmd = pVScsiReq->pbCDB[0];
105
106 /*
107 * GET CONFIGURATION, GET EVENT/STATUS NOTIFICATION, INQUIRY, and REQUEST SENSE commands
108 * operate even when a unit attention condition exists for initiator; every other command
109 * needs to report CHECK CONDITION in that case.
110 */
111 if (!pVScsiLUNSSC->Core.fReady && uCmd != SCSI_INQUIRY)
112 {
113 /*
114 * A note on media changes: As long as a medium is not present, the unit remains in
115 * the 'not ready' state. Technically the unit becomes 'ready' soon after a medium
116 * is inserted; however, we internally keep the 'not ready' state until we've had
117 * a chance to report the UNIT ATTENTION status indicating a media change.
118 */
119 if (pVScsiLUNSSC->Core.fMediaPresent)
120 {
121 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_UNIT_ATTENTION,
122 SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED, 0x00);
123 pVScsiLUNSSC->Core.fReady = true;
124 }
125 else
126 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY,
127 SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
128 }
129 else
130 {
131 switch (uCmd)
132 {
133 case SCSI_TEST_UNIT_READY:
134 Assert(!pVScsiLUNSSC->Core.fReady); /* Only should get here if LUN isn't ready. */
135 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
136 break;
137
138 case SCSI_INQUIRY:
139 {
140 SCSIINQUIRYDATA ScsiInquiryReply;
141
142 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
143
144 ScsiInquiryReply.cbAdditional = 31;
145 ScsiInquiryReply.fRMB = 1; /* Removable. */
146 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS;
147 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
148 ScsiInquiryReply.u3AnsiVersion = 0x05; /* SSC-?? compliant */
149 ScsiInquiryReply.fCmdQue = 1; /* Command queuing supported. */
150 ScsiInquiryReply.fWBus16 = 1;
151 scsiPadStrS(ScsiInquiryReply.achVendorId, "VBOX", 8);
152 scsiPadStrS(ScsiInquiryReply.achProductId, "TAPE DRIVE", 16);
153 scsiPadStrS(ScsiInquiryReply.achProductLevel, "1.0", 4);
154
155 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
156 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
157 break;
158 }
159 case SCSI_MODE_SENSE_6:
160 {
161// uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
162 uint8_t aReply[24];
163 uint8_t *pu8ReplyPos;
164 uint8_t uReplyLen;
165 bool fWantBlkDesc = !!(pVScsiReq->pbCDB[1] & RT_BIT(3)); /* DBD bit. */
166
167 memset(aReply, 0, sizeof(aReply));
168 if (fWantBlkDesc)
169 uReplyLen = 4 + 8;
170 else
171 uReplyLen = 4;
172
173 aReply[0] = uReplyLen - 1; /* Reply length. */
174 aReply[1] = 0xB6; /* Travan TR-4 medium (whatever). */
175 aReply[2] = 0; //RT_BIT(7); /* Write Protected. */ //@todo!
176 aReply[3] = uReplyLen - 4; /* Block descriptor length. */
177
178 pu8ReplyPos = aReply + 4;
179
180#if 0
181 if ((uModePage == 0x08) || (uModePage == 0x3f))
182 {
183 memset(pu8ReplyPos, 0, 20);
184 *pu8ReplyPos++ = 0x08; /* Page code. */
185 *pu8ReplyPos++ = 0x12; /* Size of the page. */
186 *pu8ReplyPos++ = 0x4; /* Write cache enabled. */
187 }
188#endif
189
190 /* Fill out the Block Descriptor. */
191 if (fWantBlkDesc)
192 {
193 *pu8ReplyPos++ = 0x45; /* Travan TR-4 density. */
194 *pu8ReplyPos++ = 0; /* All blocks are the same. */
195 *pu8ReplyPos++ = 0; /// @todo this calls for some macros!
196 *pu8ReplyPos++ = 0;
197 *pu8ReplyPos++ = 0; /* Reserved. */
198 *pu8ReplyPos++ = 0x00; /* Block length (512). */
199 *pu8ReplyPos++ = 0x02;
200 *pu8ReplyPos++ = 0x00;
201 }
202
203 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
204 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
205 break;
206 }
207 case SCSI_MODE_SELECT_6:
208 {
209 /** @todo implement!! */
210 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
211 break;
212 }
213 case SCSI_READ_6:
214 {
215 enmTxDir = VSCSIIOREQTXDIR_READ;
216 cbTransfer = ((uint32_t) pVScsiReq->pbCDB[4]
217 | (pVScsiReq->pbCDB[3] << 8)
218 | (pVScsiReq->pbCDB[2] << 16));
219 cBlocksTransfer = pVScsiReq->pbCDB[4];
220 cSectorTransfer = cSectorTransfer ? cSectorTransfer : 256; /* Zero blocks means 256 */
221 uTransferStart = pVScsiLUNSSC->uCurPos;
222 pVScsiLUNSSC->uCurPos += cbTransfer;
223 break;
224 }
225 case SCSI_WRITE_6:
226 {
227 enmTxDir = VSCSIIOREQTXDIR_WRITE;
228 cbTransfer = ((uint32_t) pVScsiReq->pbCDB[4]
229 | (pVScsiReq->pbCDB[3] << 8)
230 | (pVScsiReq->pbCDB[2] << 16));
231 cBlocksTransfer = pVScsiReq->pbCDB[4];
232 cSectorTransfer = cSectorTransfer ? cSectorTransfer : 256; /* Zero blocks means 256 */
233 uTransferStart = pVScsiLUNSSC->uCurPos;
234 pVScsiLUNSSC->uCurPos += cbTransfer;
235 break;
236 }
237 case SCSI_READ_BUFFER:
238 {
239 uint8_t uDataMode = pVScsiReq->pbCDB[1] & 0x1f;
240
241 switch (uDataMode)
242 {
243 case 0x00:
244 case 0x01:
245 case 0x02:
246 case 0x03:
247 case 0x0a:
248 break;
249 case 0x0b:
250 {
251 uint8_t aReply[4];
252
253 /* We do not implement an echo buffer. */
254 memset(aReply, 0, sizeof(aReply));
255
256 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
257 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
258 break;
259 }
260 case 0x1a:
261 case 0x1c:
262 break;
263 default:
264 AssertMsgFailed(("Invalid data mode\n"));
265 }
266 break;
267 }
268 case SCSI_VERIFY_10:
269 case SCSI_LOAD_UNLOAD:
270 {
271 /// @todo should load/unload do anyhting? is verify even supported?
272 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
273 break;
274 }
275 case SCSI_LOG_SENSE:
276 {
277 uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f;
278 uint8_t uSubPageCode = pVScsiReq->pbCDB[3];
279
280 switch (uPageCode)
281 {
282 case 0x00:
283 {
284 if (uSubPageCode == 0)
285 {
286 uint8_t aReply[4];
287
288 aReply[0] = 0;
289 aReply[1] = 0;
290 aReply[2] = 0;
291 aReply[3] = 0;
292
293 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
294 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
295 break;
296 }
297 }
298 default:
299 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
300 }
301 break;
302 }
303 case SCSI_SERVICE_ACTION_IN_16:
304 {
305 switch (pVScsiReq->pbCDB[1] & 0x1f)
306 {
307 default:
308 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); /* Don't know if this is correct */
309 }
310 break;
311 }
312 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
313 {
314 pVScsiLUNSSC->fLocked = pVScsiReq->pbCDB[4] & 1;
315 vscsiLunMediumSetLock(pVScsiLun, pVScsiLUNSSC->fLocked);
316 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
317 break;
318 }
319 case SCSI_REWIND:
320 /// @todo flush data + write EOD? immed bit? partitions?
321 pVScsiLUNSSC->uCurPos = 0;
322 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
323 break;
324 case SCSI_RESERVE_6:
325 /// @todo perform actual reservation
326 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
327 break;
328 case SCSI_RELEASE_6:
329 /// @todo perform actual release
330 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
331 break;
332 case SCSI_READ_BLOCK_LIMITS:
333 {
334 uint8_t aReply[6];
335
336 /* Report unrestricted block sizes (1-FFFFFFh). */
337 memset(aReply, 0, sizeof(aReply));
338 /// @todo Helpers for big-endian 16-bit/24-bit/32-bit constants?
339 aReply[1] = aReply[2] = aReply[3] = 0xff;
340 aReply[5] = 0x01;
341 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
342 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
343 break;
344 }
345 default:
346 //AssertMsgFailed(("Command %#x [%s] not implemented\n", pVScsiReq->pbCDB[0], SCSICmdText(pVScsiReq->pbCDB[0])));
347 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
348 }
349 }
350
351 if (enmTxDir != VSCSIIOREQTXDIR_INVALID)
352 {
353 LogFlow(("%s: uTransferStart=%llu cbTransfer=%u\n",
354 __FUNCTION__, uTransferStart, cbTransfer));
355
356 if (RT_UNLIKELY(uTransferStart + cbTransfer > pVScsiLUNSSC->cbTape))
357 {
358 uint64_t cbResidue = uTransferStart + cbTransfer - pVScsiLUNSSC->cbTape;
359
360 if (enmTxDir == VSCSIIOREQTXDIR_READ && cbResidue < cbTransfer)
361 {
362 /* If it's a read and some data is still available, read what we can. */
363 rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir,
364 uTransferStart, cbTransfer - cbResidue);
365 rcReq = vscsiLunReqSenseErrorInfoSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NONE | SCSI_SENSE_FLAG_FILEMARK, SCSI_ASC_NONE, SCSI_ASCQ_FILEMARK_DETECTED, cbResidue);
366 }
367 else
368 {
369// rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_, SCSI_ASC_NONE, SCSI_ASCQ_END_OF_DATA_DETECTED);
370 /* Report a file mark. */
371 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NONE | SCSI_SENSE_FLAG_FILEMARK, SCSI_ASC_NONE, SCSI_ASCQ_FILEMARK_DETECTED);
372 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
373 }
374 }
375 else if (!cbTransfer)
376 {
377 /* A 0 transfer length is not an error. */
378 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
379 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
380 }
381 else
382 {
383 /* Enqueue new I/O request */
384 rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir,
385 uTransferStart, cbTransfer);
386 }
387 }
388 else /* Request completed */
389 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
390
391 return rc;
392}
393
394/** @interface_method_impl{VSCSILUNDESC,pfnVScsiLunMediumInserted} */
395static DECLCALLBACK(int) vscsiLunSSCMediumInserted(PVSCSILUNINT pVScsiLun)
396{
397 int rc = VINF_SUCCESS;
398 uint32_t cRegions = vscsiLunMediumGetRegionCount(pVScsiLun);
399 if (cRegions != 1)
400 rc = VERR_INVALID_PARAMETER;
401
402 if (RT_SUCCESS(rc))
403 {
404#if 0
405 PVSCSILUNSSC pVScsiLUNSSC = (PVSCSILUNSSC)pVScsiLun;
406
407 pVScsiLUNSSC->cSectors = cbDisk / pVScsiLUNSSC->cbSector;
408
409 uint32_t OldStatus, NewStatus;
410 do
411 {
412 OldStatus = ASMAtomicReadU32((volatile uint32_t *)&pVScsiLUNSSC->MediaEventStatus);
413 switch (OldStatus)
414 {
415 case MMCEVENTSTATUSTYPE_MEDIA_CHANGED:
416 case MMCEVENTSTATUSTYPE_MEDIA_REMOVED:
417 /* no change, we will send "medium removed" + "medium inserted" */
418 NewStatus = MMCEVENTSTATUSTYPE_MEDIA_CHANGED;
419 break;
420 default:
421 NewStatus = MMCEVENTSTATUSTYPE_MEDIA_NEW;
422 break;
423 }
424 } while (!ASMAtomicCmpXchgU32((volatile uint32_t *)&pVScsiLUNSSC->MediaEventStatus,
425 NewStatus, OldStatus));
426
427 ASMAtomicXchgU32(&pVScsiLUNSSC->u32MediaTrackType, MMC_MEDIA_TYPE_UNKNOWN);
428#endif
429 }
430
431 return rc;
432}
433
434/** @interface_method_impl{VSCSILUNDESC,pfnVScsiLunMediumRemoved} */
435static DECLCALLBACK(int) vscsiLunSSCMediumRemoved(PVSCSILUNINT pVScsiLun)
436{
437 NOREF(pVScsiLun);
438#if 0
439 PVSCSILUNSSC pVScsiLUNSSC = (PVSCSILUNSSC)pVScsiLun;
440
441 ASMAtomicWriteU32((volatile uint32_t *)&pVScsiLUNSSC->MediaEventStatus, MMCEVENTSTATUSTYPE_MEDIA_REMOVED);
442 ASMAtomicXchgU32(&pVScsiLUNSSC->u32MediaTrackType, MMC_MEDIA_TYPE_NO_DISC);
443#endif
444 return VINF_SUCCESS;
445}
446
447VSCSILUNDESC g_VScsiLunTypeSsc =
448{
449 /** enmLunType */
450 VSCSILUNTYPE_SSC,
451 /** pcszDescName */
452 "SSC",
453 /** cbLun */
454 sizeof(VSCSILUNSSC),
455 /** cSupOpcInfo */
456 0,
457 /** paSupOpcInfo */
458 NULL,
459 /** pfnVScsiLunInit */
460 vscsiLUNSSCInit,
461 /** pfnVScsiLunDestroy */
462 vscsiLUNSSCDestroy,
463 /** pfnVScsiLunReqProcess */
464 vscsiLUNSSCReqProcess,
465 /** pfnVScsiLunReqFree */
466 NULL,
467 /** pfnVScsiLunMediumInserted */
468 vscsiLunSSCMediumInserted,
469 /** pfnVScsiLunMediumRemoved */
470 vscsiLunSSCMediumRemoved
471};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use