VirtualBox

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

Last change on this file since 56992 was 56292, checked in by vboxsync, 10 years ago

Devices: Updated (C) year.

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