VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp@ 63933

Last change on this file since 63933 was 63562, checked in by vboxsync, 9 years ago

scm: cleaning up todos

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.1 KB
Line 
1/* $Id: VSCSILunMmc.cpp 63562 2016-08-16 14:04:03Z vboxsync $ */
2/** @file
3 * Virtual SCSI driver: MMC LUN implementation (CD/DVD-ROM)
4 */
5
6/*
7 * Copyright (C) 2006-2016 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 <VBox/err.h>
25#include <VBox/types.h>
26#include <VBox/vscsi.h>
27#include <iprt/assert.h>
28#include <iprt/mem.h>
29#include <iprt/string.h>
30
31#include "VSCSIInternal.h"
32
33/**
34 * MMC LUN instance
35 */
36typedef struct VSCSILUNMMC
37{
38 /** Core LUN structure */
39 VSCSILUNINT Core;
40 /** Size of the virtual disk. */
41 uint64_t cSectors;
42 /** Sector size. */
43 uint32_t cbSector;
44 /** Medium locked indicator. */
45 bool fLocked;
46} VSCSILUNMMC, *PVSCSILUNMMC;
47
48
49DECLINLINE(void) mmcLBA2MSF(uint8_t *pbBuf, uint32_t iLBA)
50{
51 iLBA += 150;
52 pbBuf[0] = (iLBA / 75) / 60;
53 pbBuf[1] = (iLBA / 75) % 60;
54 pbBuf[2] = iLBA % 75;
55}
56
57#if 0 /* unused */
58DECLINLINE(uint32_t) mmcMSF2LBA(const uint8_t *pbBuf)
59{
60 return (pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2];
61}
62#endif
63
64
65/* Fabricate normal TOC information. */
66static int mmcReadTOCNormal(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
67{
68 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
69 uint8_t aReply[32];
70 uint8_t *pbBuf = aReply;
71 uint8_t *q;
72 uint8_t iStartTrack;
73 uint32_t cbSize;
74
75 iStartTrack = pVScsiReq->pbCDB[6];
76 if (iStartTrack > 1 && iStartTrack != 0xaa)
77 {
78 return vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
79 }
80 q = pbBuf + 2;
81 *q++ = 1; /* first session */
82 *q++ = 1; /* last session */
83 if (iStartTrack <= 1)
84 {
85 *q++ = 0; /* reserved */
86 *q++ = 0x14; /* ADR, CONTROL */
87 *q++ = 1; /* track number */
88 *q++ = 0; /* reserved */
89 if (fMSF)
90 {
91 *q++ = 0; /* reserved */
92 mmcLBA2MSF(q, 0);
93 q += 3;
94 }
95 else
96 {
97 /* sector 0 */
98 vscsiH2BEU32(q, 0);
99 q += 4;
100 }
101 }
102 /* lead out track */
103 *q++ = 0; /* reserved */
104 *q++ = 0x14; /* ADR, CONTROL */
105 *q++ = 0xaa; /* track number */
106 *q++ = 0; /* reserved */
107 if (fMSF)
108 {
109 *q++ = 0; /* reserved */
110 mmcLBA2MSF(q, pVScsiLunMmc->cSectors);
111 q += 3;
112 }
113 else
114 {
115 vscsiH2BEU32(q, pVScsiLunMmc->cSectors);
116 q += 4;
117 }
118 cbSize = q - pbBuf;
119 Assert(cbSize <= sizeof(aReply));
120 vscsiH2BEU16(pbBuf, cbSize - 2);
121 if (cbSize < cbMaxTransfer)
122 cbMaxTransfer = cbSize;
123
124 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, cbMaxTransfer);
125
126 return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
127}
128
129/* Fabricate session information. */
130static int mmcReadTOCMulti(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
131{
132 RT_NOREF1(cbMaxTransfer);
133 uint8_t aReply[32];
134 uint8_t *pbBuf = aReply;
135
136 /* multi session: only a single session defined */
137 memset(pbBuf, 0, 12);
138 pbBuf[1] = 0x0a;
139 pbBuf[2] = 0x01; /* first complete session number */
140 pbBuf[3] = 0x01; /* last complete session number */
141 pbBuf[5] = 0x14; /* ADR, CONTROL */
142 pbBuf[6] = 1; /* first track in last complete session */
143
144 if (fMSF)
145 {
146 pbBuf[8] = 0; /* reserved */
147 mmcLBA2MSF(pbBuf + 8, 0);
148 }
149 else
150 {
151 /* sector 0 */
152 vscsiH2BEU32(pbBuf + 8, 0);
153 }
154
155 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, 12);
156
157 return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
158}
159
160static DECLCALLBACK(int) vscsiLunMmcInit(PVSCSILUNINT pVScsiLun)
161{
162 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
163 uint64_t cbDisk = 0;
164 int rc = VINF_SUCCESS;
165
166 pVScsiLunMmc->cbSector = 2048; /* Default to 2K sectors. */
167 rc = vscsiLunMediumGetSize(pVScsiLun, &cbDisk);
168 if (RT_SUCCESS(rc))
169 pVScsiLunMmc->cSectors = cbDisk / pVScsiLunMmc->cbSector;
170
171 return rc;
172}
173
174static DECLCALLBACK(int) vscsiLunMmcDestroy(PVSCSILUNINT pVScsiLun)
175{
176 RT_NOREF1(pVScsiLun);
177 return VINF_SUCCESS;
178}
179
180static DECLCALLBACK(int) vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
181{
182 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
183 VSCSIIOREQTXDIR enmTxDir = VSCSIIOREQTXDIR_INVALID;
184 uint64_t uLbaStart = 0;
185 uint32_t cSectorTransfer = 0;
186 int rc = VINF_SUCCESS;
187 int rcReq = SCSI_STATUS_OK;
188 unsigned uCmd = pVScsiReq->pbCDB[0];
189
190 /*
191 * GET CONFIGURATION, GET EVENT/STATUS NOTIFICATION, INQUIRY, and REQUEST SENSE commands
192 * operate even when a unit attention condition exists for initiator; every other command
193 * needs to report CHECK CONDITION in that case.
194 */
195 if (!pVScsiLunMmc->Core.fReady && uCmd != SCSI_INQUIRY)
196 {
197 /*
198 * A note on media changes: As long as a medium is not present, the unit remains in
199 * the 'not ready' state. Technically the unit becomes 'ready' soon after a medium
200 * is inserted; however, we internally keep the 'not ready' state until we've had
201 * a chance to report the UNIT ATTENTION status indicating a media change.
202 */
203 if (pVScsiLunMmc->Core.fMediaPresent)
204 {
205 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_UNIT_ATTENTION,
206 SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED, 0x00);
207 pVScsiLunMmc->Core.fReady = true;
208 }
209 else
210 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY,
211 SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
212 }
213 else
214 {
215 switch (uCmd)
216 {
217 case SCSI_TEST_UNIT_READY:
218 Assert(!pVScsiLunMmc->Core.fReady); /* Only should get here if LUN isn't ready. */
219 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
220 break;
221
222 case SCSI_INQUIRY:
223 {
224 SCSIINQUIRYDATA ScsiInquiryReply;
225
226 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
227
228 ScsiInquiryReply.cbAdditional = 31;
229 ScsiInquiryReply.fRMB = 1; /* Removable. */
230 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_CD_DVD;
231 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
232 ScsiInquiryReply.u3AnsiVersion = 0x05; /* MMC-?? compliant */
233 ScsiInquiryReply.fCmdQue = 1; /* Command queuing supported. */
234 ScsiInquiryReply.fWBus16 = 1;
235 vscsiPadStr(ScsiInquiryReply.achVendorId, "VBOX", 8);
236 vscsiPadStr(ScsiInquiryReply.achProductId, "CD-ROM", 16);
237 vscsiPadStr(ScsiInquiryReply.achProductLevel, "1.0", 4);
238
239 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
240 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
241 break;
242 }
243 case SCSI_READ_CAPACITY:
244 {
245 uint8_t aReply[8];
246 memset(aReply, 0, sizeof(aReply));
247
248 /*
249 * If sector size exceeds the maximum value that is
250 * able to be stored in 4 bytes return 0xffffffff in this field
251 */
252 if (pVScsiLunMmc->cSectors > UINT32_C(0xffffffff))
253 vscsiH2BEU32(aReply, UINT32_C(0xffffffff));
254 else
255 vscsiH2BEU32(aReply, pVScsiLunMmc->cSectors - 1);
256 vscsiH2BEU32(&aReply[4], pVScsiLunMmc->cbSector);
257 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
258 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
259 break;
260 }
261 case SCSI_MODE_SENSE_6:
262 {
263 uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
264 uint8_t aReply[24];
265 uint8_t *pu8ReplyPos;
266 bool fValid = false;
267
268 memset(aReply, 0, sizeof(aReply));
269 aReply[0] = 4; /* Reply length 4. */
270 aReply[1] = 0; /* Default media type. */
271 aReply[2] = RT_BIT(4); /* Caching supported. */
272 aReply[3] = 0; /* Block descriptor length. */
273
274 pu8ReplyPos = aReply + 4;
275
276 if ((uModePage == 0x08) || (uModePage == 0x3f))
277 {
278 memset(pu8ReplyPos, 0, 20);
279 *pu8ReplyPos++ = 0x08; /* Page code. */
280 *pu8ReplyPos++ = 0x12; /* Size of the page. */
281 *pu8ReplyPos++ = 0x4; /* Write cache enabled. */
282 fValid = true;
283 } else if (uModePage == 0) {
284 fValid = true;
285 }
286
287 /* Querying unknown pages must fail. */
288 if (fValid) {
289 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
290 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
291 } else {
292 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
293 }
294 break;
295 }
296 case SCSI_MODE_SELECT_6:
297 {
298 /** @todo implement!! */
299 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
300 break;
301 }
302 case SCSI_READ_6:
303 {
304 enmTxDir = VSCSIIOREQTXDIR_READ;
305 uLbaStart = ((uint64_t) pVScsiReq->pbCDB[3]
306 | (pVScsiReq->pbCDB[2] << 8)
307 | ((pVScsiReq->pbCDB[1] & 0x1f) << 16));
308 cSectorTransfer = pVScsiReq->pbCDB[4];
309 break;
310 }
311 case SCSI_READ_10:
312 {
313 enmTxDir = VSCSIIOREQTXDIR_READ;
314 uLbaStart = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
315 cSectorTransfer = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
316 break;
317 }
318 case SCSI_READ_12:
319 {
320 enmTxDir = VSCSIIOREQTXDIR_READ;
321 uLbaStart = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
322 cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[6]);
323 break;
324 }
325 case SCSI_READ_16:
326 {
327 enmTxDir = VSCSIIOREQTXDIR_READ;
328 uLbaStart = vscsiBE2HU64(&pVScsiReq->pbCDB[2]);
329 cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[10]);
330 break;
331 }
332 case SCSI_READ_BUFFER:
333 {
334 uint8_t uDataMode = pVScsiReq->pbCDB[1] & 0x1f;
335
336 switch (uDataMode)
337 {
338 case 0x00:
339 case 0x01:
340 case 0x02:
341 case 0x03:
342 case 0x0a:
343 break;
344 case 0x0b:
345 {
346 uint8_t aReply[4];
347
348 /* We do not implement an echo buffer. */
349 memset(aReply, 0, sizeof(aReply));
350
351 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
352 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
353 break;
354 }
355 case 0x1a:
356 case 0x1c:
357 break;
358 default:
359 AssertMsgFailed(("Invalid data mode\n"));
360 }
361 break;
362 }
363 case SCSI_VERIFY_10:
364 case SCSI_START_STOP_UNIT:
365 {
366 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
367 break;
368 }
369 case SCSI_LOG_SENSE:
370 {
371 uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f;
372 uint8_t uSubPageCode = pVScsiReq->pbCDB[3];
373
374 switch (uPageCode)
375 {
376 case 0x00:
377 {
378 if (uSubPageCode == 0)
379 {
380 uint8_t aReply[4];
381
382 aReply[0] = 0;
383 aReply[1] = 0;
384 aReply[2] = 0;
385 aReply[3] = 0;
386
387 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
388 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
389 break;
390 }
391 }
392 default:
393 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
394 }
395 break;
396 }
397 case SCSI_SERVICE_ACTION_IN_16:
398 {
399 switch (pVScsiReq->pbCDB[1] & 0x1f)
400 {
401 case SCSI_SVC_ACTION_IN_READ_CAPACITY_16:
402 {
403 uint8_t aReply[32];
404
405 memset(aReply, 0, sizeof(aReply));
406 vscsiH2BEU64(aReply, pVScsiLunMmc->cSectors - 1);
407 vscsiH2BEU32(&aReply[8], pVScsiLunMmc->cbSector);
408 /* Leave the rest 0 */
409 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
410 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
411 break;
412 }
413 default:
414 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); /* Don't know if this is correct */
415 }
416 break;
417 }
418 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
419 {
420 pVScsiLunMmc->fLocked = pVScsiReq->pbCDB[4] & 1;
421 vscsiLunMediumSetLock(pVScsiLun, pVScsiLunMmc->fLocked);
422 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
423 break;
424 }
425 case SCSI_READ_TOC_PMA_ATIP:
426 {
427 uint8_t format;
428 uint16_t cbMax;
429 bool fMSF;
430
431 format = pVScsiReq->pbCDB[2] & 0x0f;
432 cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
433 fMSF = (pVScsiReq->pbCDB[1] >> 1) & 1;
434 switch (format)
435 {
436 case 0x00:
437 mmcReadTOCNormal(pVScsiLun, pVScsiReq, cbMax, fMSF);
438 break;
439 case 0x01:
440 mmcReadTOCMulti(pVScsiLun, pVScsiReq, cbMax, fMSF);
441 break;
442 default:
443 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
444 }
445 break;
446 }
447
448 default:
449 //AssertMsgFailed(("Command %#x [%s] not implemented\n", pVScsiReq->pbCDB[0], SCSICmdText(pVScsiReq->pbCDB[0])));
450 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
451 }
452 }
453
454 if (enmTxDir != VSCSIIOREQTXDIR_INVALID)
455 {
456 LogFlow(("%s: uLbaStart=%llu cSectorTransfer=%u\n",
457 __FUNCTION__, uLbaStart, cSectorTransfer));
458
459 if (RT_UNLIKELY(uLbaStart + cSectorTransfer > pVScsiLunMmc->cSectors))
460 {
461 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
462 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
463 }
464 else if (!cSectorTransfer)
465 {
466 /* A 0 transfer length is not an error. */
467 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
468 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
469 }
470 else
471 {
472 /* Enqueue new I/O request */
473 rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir,
474 uLbaStart * pVScsiLunMmc->cbSector,
475 cSectorTransfer * pVScsiLunMmc->cbSector);
476 }
477 }
478 else /* Request completed */
479 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
480
481 return rc;
482}
483
484VSCSILUNDESC g_VScsiLunTypeMmc =
485{
486 /** enmLunType */
487 VSCSILUNTYPE_MMC,
488 /** pcszDescName */
489 "MMC",
490 /** cbLun */
491 sizeof(VSCSILUNMMC),
492 /** pfnVScsiLunInit */
493 vscsiLunMmcInit,
494 /** pfnVScsiLunDestroy */
495 vscsiLunMmcDestroy,
496 /** pfnVScsiLunReqProcess */
497 vscsiLunMmcReqProcess
498};
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette