VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/ATAPIPassthrough.cpp@ 78229

Last change on this file since 78229 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.4 KB
Line 
1/* $Id: ATAPIPassthrough.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * VBox storage devices: ATAPI emulation (common code for DevATA and DevAHCI).
4 */
5
6/*
7 * Copyright (C) 2012-2019 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#define LOG_GROUP LOG_GROUP_DEV_IDE
18#include <iprt/log.h>
19#include <iprt/assert.h>
20#include <iprt/mem.h>
21#include <iprt/string.h>
22
23#include <VBox/log.h>
24#include <iprt/errcore.h>
25#include <VBox/cdefs.h>
26#include <VBox/scsi.h>
27#include <VBox/scsiinline.h>
28
29#include "ATAPIPassthrough.h"
30
31/** The track was not detected yet. */
32#define TRACK_FLAGS_UNDETECTED RT_BIT_32(0)
33/** The track is the lead in track of the medium. */
34#define TRACK_FLAGS_LEAD_IN RT_BIT_32(1)
35/** The track is the lead out track of the medium. */
36#define TRACK_FLAGS_LEAD_OUT RT_BIT_32(2)
37
38/** Don't clear already detected tracks on the medium. */
39#define ATAPI_TRACK_LIST_REALLOCATE_FLAGS_DONT_CLEAR RT_BIT_32(0)
40
41/**
42 * Track main data form.
43 */
44typedef enum TRACKDATAFORM
45{
46 /** Invalid data form. */
47 TRACKDATAFORM_INVALID = 0,
48 /** 2352 bytes of data. */
49 TRACKDATAFORM_CDDA,
50 /** CDDA data is pause. */
51 TRACKDATAFORM_CDDA_PAUSE,
52 /** Mode 1 with 2048 bytes sector size. */
53 TRACKDATAFORM_MODE1_2048,
54 /** Mode 1 with 2352 bytes sector size. */
55 TRACKDATAFORM_MODE1_2352,
56 /** Mode 1 with 0 bytes sector size (generated by the drive). */
57 TRACKDATAFORM_MODE1_0,
58 /** XA Mode with 2336 bytes sector size. */
59 TRACKDATAFORM_XA_2336,
60 /** XA Mode with 2352 bytes sector size. */
61 TRACKDATAFORM_XA_2352,
62 /** XA Mode with 0 bytes sector size (generated by the drive). */
63 TRACKDATAFORM_XA_0,
64 /** Mode 2 with 2336 bytes sector size. */
65 TRACKDATAFORM_MODE2_2336,
66 /** Mode 2 with 2352 bytes sector size. */
67 TRACKDATAFORM_MODE2_2352,
68 /** Mode 2 with 0 bytes sector size (generated by the drive). */
69 TRACKDATAFORM_MODE2_0
70} TRACKDATAFORM;
71
72/**
73 * Subchannel data form.
74 */
75typedef enum SUBCHNDATAFORM
76{
77 /** Invalid subchannel data form. */
78 SUBCHNDATAFORM_INVALID = 0,
79 /** 0 bytes for the subchannel (generated by the drive). */
80 SUBCHNDATAFORM_0,
81 /** 96 bytes of data for the subchannel. */
82 SUBCHNDATAFORM_96
83} SUBCHNDATAFORM;
84
85/**
86 * Track entry.
87 */
88typedef struct TRACK
89{
90 /** Start LBA of the track. */
91 int64_t iLbaStart;
92 /** Number of sectors in the track. */
93 uint32_t cSectors;
94 /** Data form of main data. */
95 TRACKDATAFORM enmMainDataForm;
96 /** Data form of sub channel. */
97 SUBCHNDATAFORM enmSubChnDataForm;
98 /** Flags for the track. */
99 uint32_t fFlags;
100} TRACK, *PTRACK;
101
102/**
103 * Media track list.
104 */
105typedef struct TRACKLIST
106{
107 /** Number of detected tracks of the current medium. */
108 unsigned cTracksCurrent;
109 /** Maximum number of tracks the list can contain. */
110 unsigned cTracksMax;
111 /** Variable list of tracks. */
112 PTRACK paTracks;
113} TRACKLIST, *PTRACKLIST;
114
115
116/**
117 * Reallocate the given track list to be able to hold the given number of tracks.
118 *
119 * @returns VBox status code.
120 * @param pTrackList The track list to reallocate.
121 * @param cTracks Number of tracks the list must be able to hold.
122 * @param fFlags Flags for the reallocation.
123 */
124static int atapiTrackListReallocate(PTRACKLIST pTrackList, unsigned cTracks, uint32_t fFlags)
125{
126 int rc = VINF_SUCCESS;
127
128 if (!(fFlags & ATAPI_TRACK_LIST_REALLOCATE_FLAGS_DONT_CLEAR))
129 ATAPIPassthroughTrackListClear(pTrackList);
130
131 if (pTrackList->cTracksMax < cTracks)
132 {
133 PTRACK paTracksNew = (PTRACK)RTMemRealloc(pTrackList->paTracks, cTracks * sizeof(TRACK));
134 if (paTracksNew)
135 {
136 pTrackList->paTracks = paTracksNew;
137
138 /* Mark new tracks as undetected. */
139 for (unsigned i = pTrackList->cTracksMax; i < cTracks; i++)
140 pTrackList->paTracks[i].fFlags |= TRACK_FLAGS_UNDETECTED;
141
142 pTrackList->cTracksMax = cTracks;
143 }
144 else
145 rc = VERR_NO_MEMORY;
146 }
147
148 if (RT_SUCCESS(rc))
149 pTrackList->cTracksCurrent = cTracks;
150
151 return rc;
152}
153
154/**
155 * Initilizes the given track from the given CUE sheet entry.
156 *
157 * @returns nothing.
158 * @param pTrack The track to initialize.
159 * @param pbCueSheetEntry CUE sheet entry to use.
160 */
161static void atapiTrackListEntryCreateFromCueSheetEntry(PTRACK pTrack, const uint8_t *pbCueSheetEntry)
162{
163 TRACKDATAFORM enmTrackDataForm = TRACKDATAFORM_INVALID;
164 SUBCHNDATAFORM enmSubChnDataForm = SUBCHNDATAFORM_INVALID;
165
166 /* Determine size of main data based on the data form field. */
167 switch (pbCueSheetEntry[3] & 0x3f)
168 {
169 case 0x00: /* CD-DA with data. */
170 enmTrackDataForm = TRACKDATAFORM_CDDA;
171 break;
172 case 0x01: /* CD-DA without data (used for pauses between tracks). */
173 enmTrackDataForm = TRACKDATAFORM_CDDA_PAUSE;
174 break;
175 case 0x10: /* CD-ROM mode 1 */
176 case 0x12:
177 enmTrackDataForm = TRACKDATAFORM_MODE1_2048;
178 break;
179 case 0x11:
180 case 0x13:
181 enmTrackDataForm = TRACKDATAFORM_MODE1_2352;
182 break;
183 case 0x14:
184 enmTrackDataForm = TRACKDATAFORM_MODE1_0;
185 break;
186 case 0x20: /* CD-ROM XA, CD-I */
187 case 0x22:
188 enmTrackDataForm = TRACKDATAFORM_XA_2336;
189 break;
190 case 0x21:
191 case 0x23:
192 enmTrackDataForm = TRACKDATAFORM_XA_2352;
193 break;
194 case 0x24:
195 enmTrackDataForm = TRACKDATAFORM_XA_0;
196 break;
197 case 0x31: /* CD-ROM Mode 2 */
198 case 0x33:
199 enmTrackDataForm = TRACKDATAFORM_MODE2_2352;
200 break;
201 case 0x30:
202 case 0x32:
203 enmTrackDataForm = TRACKDATAFORM_MODE2_2336;
204 break;
205 case 0x34:
206 enmTrackDataForm = TRACKDATAFORM_MODE2_0;
207 break;
208 default: /* Reserved, invalid mode. Log and leave default sector size. */
209 LogRel(("ATA: Invalid data form mode %d for current CUE sheet\n",
210 pbCueSheetEntry[3] & 0x3f));
211 }
212
213 /* Determine size of sub channel data based on data form field. */
214 switch ((pbCueSheetEntry[3] & 0xc0) >> 6)
215 {
216 case 0x00: /* Sub channel all zeroes, autogenerated by the drive. */
217 enmSubChnDataForm = SUBCHNDATAFORM_0;
218 break;
219 case 0x01:
220 case 0x03:
221 enmSubChnDataForm = SUBCHNDATAFORM_96;
222 break;
223 default:
224 LogRel(("ATA: Invalid sub-channel data form mode %u for current CUE sheet\n",
225 pbCueSheetEntry[3] & 0xc0));
226 }
227
228 pTrack->enmMainDataForm = enmTrackDataForm;
229 pTrack->enmSubChnDataForm = enmSubChnDataForm;
230 pTrack->iLbaStart = scsiMSF2LBA(&pbCueSheetEntry[5]);
231 if (pbCueSheetEntry[1] != 0xaa)
232 {
233 /* Calculate number of sectors from the next entry. */
234 int64_t iLbaNext = scsiMSF2LBA(&pbCueSheetEntry[5+8]);
235 pTrack->cSectors = iLbaNext - pTrack->iLbaStart;
236 }
237 else
238 {
239 pTrack->fFlags |= TRACK_FLAGS_LEAD_OUT;
240 pTrack->cSectors = 0;
241 }
242 pTrack->fFlags &= ~TRACK_FLAGS_UNDETECTED;
243}
244
245/**
246 * Update the track list from a SEND CUE SHEET request.
247 *
248 * @returns VBox status code.
249 * @param pTrackList Track list to update.
250 * @param pbCDB CDB of the SEND CUE SHEET request.
251 * @param pvBuf The CUE sheet.
252 */
253static int atapiTrackListUpdateFromSendCueSheet(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
254{
255 int rc = VINF_SUCCESS;
256 unsigned cbCueSheet = scsiBE2H_U24(pbCDB + 6);
257 unsigned cTracks = cbCueSheet / 8;
258
259 AssertReturn(cbCueSheet % 8 == 0 && cTracks, VERR_INVALID_PARAMETER);
260
261 rc = atapiTrackListReallocate(pTrackList, cTracks, 0);
262 if (RT_SUCCESS(rc))
263 {
264 const uint8_t *pbCueSheet = (uint8_t *)pvBuf;
265 PTRACK pTrack = pTrackList->paTracks;
266
267 for (unsigned i = 0; i < cTracks; i++)
268 {
269 atapiTrackListEntryCreateFromCueSheetEntry(pTrack, pbCueSheet);
270 if (i == 0)
271 pTrack->fFlags |= TRACK_FLAGS_LEAD_IN;
272 pTrack++;
273 pbCueSheet += 8;
274 }
275 }
276
277 return rc;
278}
279
280static int atapiTrackListUpdateFromSendDvdStructure(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
281{
282 RT_NOREF(pTrackList, pbCDB, pvBuf);
283 return VERR_NOT_IMPLEMENTED;
284}
285
286/**
287 * Update track list from formatted TOC data.
288 *
289 * @returns VBox status code.
290 * @param pTrackList The track list to update.
291 * @param iTrack The first track the TOC has data for.
292 * @param fMSF Flag whether block addresses are in MSF or LBA format.
293 * @param pbBuf Buffer holding the formatted TOC.
294 * @param cbBuffer Size of the buffer.
295 */
296static int atapiTrackListUpdateFromFormattedToc(PTRACKLIST pTrackList, uint8_t iTrack,
297 bool fMSF, const uint8_t *pbBuf, uint32_t cbBuffer)
298{
299 RT_NOREF(iTrack, cbBuffer); /** @todo unused parameters */
300 int rc = VINF_SUCCESS;
301 unsigned cbToc = scsiBE2H_U16(pbBuf);
302 uint8_t iTrackFirst = pbBuf[2];
303 unsigned cTracks;
304
305 cbToc -= 2;
306 pbBuf += 4;
307 AssertReturn(cbToc % 8 == 0, VERR_INVALID_PARAMETER);
308
309 cTracks = cbToc / 8 + iTrackFirst;
310
311 rc = atapiTrackListReallocate(pTrackList, iTrackFirst + cTracks, ATAPI_TRACK_LIST_REALLOCATE_FLAGS_DONT_CLEAR);
312 if (RT_SUCCESS(rc))
313 {
314 PTRACK pTrack = &pTrackList->paTracks[iTrackFirst];
315
316 for (unsigned i = iTrackFirst; i < cTracks; i++)
317 {
318 if (pbBuf[1] & 0x4)
319 pTrack->enmMainDataForm = TRACKDATAFORM_MODE1_2048;
320 else
321 pTrack->enmMainDataForm = TRACKDATAFORM_CDDA;
322
323 pTrack->enmSubChnDataForm = SUBCHNDATAFORM_0;
324 if (fMSF)
325 pTrack->iLbaStart = scsiMSF2LBA(&pbBuf[4]);
326 else
327 pTrack->iLbaStart = scsiBE2H_U32(&pbBuf[4]);
328
329 if (pbBuf[2] != 0xaa)
330 {
331 /* Calculate number of sectors from the next entry. */
332 int64_t iLbaNext;
333
334 if (fMSF)
335 iLbaNext = scsiMSF2LBA(&pbBuf[4+8]);
336 else
337 iLbaNext = scsiBE2H_U32(&pbBuf[4+8]);
338
339 pTrack->cSectors = iLbaNext - pTrack->iLbaStart;
340 }
341 else
342 pTrack->cSectors = 0;
343
344 pTrack->fFlags &= ~TRACK_FLAGS_UNDETECTED;
345 pbBuf += 8;
346 pTrack++;
347 }
348 }
349
350 return rc;
351}
352
353static int atapiTrackListUpdateFromReadTocPmaAtip(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
354{
355 int rc = VINF_SUCCESS;
356 uint16_t cbBuffer = scsiBE2H_U16(&pbCDB[7]);
357 bool fMSF = (pbCDB[1] & 0x2) != 0;
358 uint8_t uFmt = pbCDB[2] & 0xf;
359 uint8_t iTrack = pbCDB[6];
360
361 switch (uFmt)
362 {
363 case 0x00:
364 rc = atapiTrackListUpdateFromFormattedToc(pTrackList, iTrack, fMSF, (uint8_t *)pvBuf, cbBuffer);
365 break;
366 case 0x01:
367 case 0x02:
368 case 0x03:
369 case 0x04:
370 rc = VERR_NOT_IMPLEMENTED;
371 break;
372 case 0x05:
373 rc = VINF_SUCCESS; /* Does not give information about the tracklist. */
374 break;
375 default:
376 rc = VERR_INVALID_PARAMETER;
377 }
378
379 return rc;
380}
381
382static int atapiTrackListUpdateFromReadTrackInformation(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
383{
384 RT_NOREF(pTrackList, pbCDB, pvBuf);
385 return VERR_NOT_IMPLEMENTED;
386}
387
388static int atapiTrackListUpdateFromReadDvdStructure(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
389{
390 RT_NOREF(pTrackList, pbCDB, pvBuf);
391 return VERR_NOT_IMPLEMENTED;
392}
393
394static int atapiTrackListUpdateFromReadDiscInformation(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
395{
396 RT_NOREF(pTrackList, pbCDB, pvBuf);
397 return VERR_NOT_IMPLEMENTED;
398}
399
400#ifdef LOG_ENABLED
401
402/**
403 * Converts the given track data form to a string.
404 *
405 * @returns Track data form as a string.
406 * @param enmTrackDataForm The track main data form.
407 */
408static const char *atapiTrackListMainDataFormToString(TRACKDATAFORM enmTrackDataForm)
409{
410 switch (enmTrackDataForm)
411 {
412 case TRACKDATAFORM_CDDA:
413 return "CD-DA";
414 case TRACKDATAFORM_CDDA_PAUSE:
415 return "CD-DA Pause";
416 case TRACKDATAFORM_MODE1_2048:
417 return "Mode 1 (2048 bytes)";
418 case TRACKDATAFORM_MODE1_2352:
419 return "Mode 1 (2352 bytes)";
420 case TRACKDATAFORM_MODE1_0:
421 return "Mode 1 (0 bytes)";
422 case TRACKDATAFORM_XA_2336:
423 return "XA (2336 bytes)";
424 case TRACKDATAFORM_XA_2352:
425 return "XA (2352 bytes)";
426 case TRACKDATAFORM_XA_0:
427 return "XA (0 bytes)";
428 case TRACKDATAFORM_MODE2_2336:
429 return "Mode 2 (2336 bytes)";
430 case TRACKDATAFORM_MODE2_2352:
431 return "Mode 2 (2352 bytes)";
432 case TRACKDATAFORM_MODE2_0:
433 return "Mode 2 (0 bytes)";
434 case TRACKDATAFORM_INVALID:
435 default:
436 return "Invalid";
437 }
438}
439
440/**
441 * Converts the given subchannel data form to a string.
442 *
443 * @returns Subchannel data form as a string.
444 * @param enmSubChnDataForm The subchannel main data form.
445 */
446static const char *atapiTrackListSubChnDataFormToString(SUBCHNDATAFORM enmSubChnDataForm)
447{
448 switch (enmSubChnDataForm)
449 {
450 case SUBCHNDATAFORM_0:
451 return "0";
452 case SUBCHNDATAFORM_96:
453 return "96";
454 case SUBCHNDATAFORM_INVALID:
455 default:
456 return "Invalid";
457 }
458}
459
460/**
461 * Dump the complete track list to the release log.
462 *
463 * @returns nothing.
464 * @param pTrackList The track list to dump.
465 */
466static void atapiTrackListDump(PTRACKLIST pTrackList)
467{
468 LogRel(("Track List: cTracks=%u\n", pTrackList->cTracksCurrent));
469 for (unsigned i = 0; i < pTrackList->cTracksCurrent; i++)
470 {
471 PTRACK pTrack = &pTrackList->paTracks[i];
472
473 LogRel((" Track %u: LBAStart=%lld cSectors=%u enmMainDataForm=%s enmSubChnDataForm=%s fFlags=[%s%s%s]\n",
474 i, pTrack->iLbaStart, pTrack->cSectors, atapiTrackListMainDataFormToString(pTrack->enmMainDataForm),
475 atapiTrackListSubChnDataFormToString(pTrack->enmSubChnDataForm),
476 pTrack->fFlags & TRACK_FLAGS_UNDETECTED ? "UNDETECTED " : "",
477 pTrack->fFlags & TRACK_FLAGS_LEAD_IN ? "Lead-In " : "",
478 pTrack->fFlags & TRACK_FLAGS_LEAD_OUT ? "Lead-Out" : ""));
479 }
480}
481
482#endif /* LOG_ENABLED */
483
484DECLHIDDEN(int) ATAPIPassthroughTrackListCreateEmpty(PTRACKLIST *ppTrackList)
485{
486 int rc = VERR_NO_MEMORY;
487 PTRACKLIST pTrackList = (PTRACKLIST)RTMemAllocZ(sizeof(TRACKLIST));
488
489 if (pTrackList)
490 {
491 rc = VINF_SUCCESS;
492 *ppTrackList = pTrackList;
493 }
494
495 return rc;
496}
497
498DECLHIDDEN(void) ATAPIPassthroughTrackListDestroy(PTRACKLIST pTrackList)
499{
500 if (pTrackList->paTracks)
501 RTMemFree(pTrackList->paTracks);
502 RTMemFree(pTrackList);
503}
504
505DECLHIDDEN(void) ATAPIPassthroughTrackListClear(PTRACKLIST pTrackList)
506{
507 AssertPtrReturnVoid(pTrackList);
508
509 pTrackList->cTracksCurrent = 0;
510
511 /* Mark all tracks as undetected. */
512 for (unsigned i = 0; i < pTrackList->cTracksMax; i++)
513 pTrackList->paTracks[i].fFlags |= TRACK_FLAGS_UNDETECTED;
514}
515
516DECLHIDDEN(int) ATAPIPassthroughTrackListUpdate(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
517{
518 int rc = VINF_SUCCESS;
519
520 switch (pbCDB[0])
521 {
522 case SCSI_SEND_CUE_SHEET:
523 rc = atapiTrackListUpdateFromSendCueSheet(pTrackList, pbCDB, pvBuf);
524 break;
525 case SCSI_SEND_DVD_STRUCTURE:
526 rc = atapiTrackListUpdateFromSendDvdStructure(pTrackList, pbCDB, pvBuf);
527 break;
528 case SCSI_READ_TOC_PMA_ATIP:
529 rc = atapiTrackListUpdateFromReadTocPmaAtip(pTrackList, pbCDB, pvBuf);
530 break;
531 case SCSI_READ_TRACK_INFORMATION:
532 rc = atapiTrackListUpdateFromReadTrackInformation(pTrackList, pbCDB, pvBuf);
533 break;
534 case SCSI_READ_DVD_STRUCTURE:
535 rc = atapiTrackListUpdateFromReadDvdStructure(pTrackList, pbCDB, pvBuf);
536 break;
537 case SCSI_READ_DISC_INFORMATION:
538 rc = atapiTrackListUpdateFromReadDiscInformation(pTrackList, pbCDB, pvBuf);
539 break;
540 default:
541 LogRel(("ATAPI: Invalid opcode %#x while determining media layout\n", pbCDB[0]));
542 rc = VERR_INVALID_PARAMETER;
543 }
544
545#ifdef LOG_ENABLED
546 atapiTrackListDump(pTrackList);
547#endif
548
549 return rc;
550}
551
552DECLHIDDEN(uint32_t) ATAPIPassthroughTrackListGetSectorSizeFromLba(PTRACKLIST pTrackList, uint32_t iAtapiLba)
553{
554 PTRACK pTrack = NULL;
555 uint32_t cbAtapiSector = 2048;
556
557 if (pTrackList->cTracksCurrent)
558 {
559 if ( iAtapiLba > UINT32_C(0xffff4fa1)
560 && (int32_t)iAtapiLba < -150)
561 {
562 /* Lead-In area, this is always the first entry in the cue sheet. */
563 pTrack = pTrackList->paTracks;
564 Assert(pTrack->fFlags & TRACK_FLAGS_LEAD_IN);
565 LogFlowFunc(("Selected Lead-In area\n"));
566 }
567 else
568 {
569 int64_t iAtapiLba64 = (int32_t)iAtapiLba;
570 pTrack = &pTrackList->paTracks[1];
571
572 /* Go through the track list and find the correct entry. */
573 for (unsigned i = 1; i < pTrackList->cTracksCurrent - 1; i++)
574 {
575 if (pTrack->fFlags & TRACK_FLAGS_UNDETECTED)
576 continue;
577
578 if ( pTrack->iLbaStart <= iAtapiLba64
579 && iAtapiLba64 < pTrack->iLbaStart + pTrack->cSectors)
580 break;
581
582 pTrack++;
583 }
584 }
585
586 if (pTrack)
587 {
588 switch (pTrack->enmMainDataForm)
589 {
590 case TRACKDATAFORM_CDDA:
591 case TRACKDATAFORM_MODE1_2352:
592 case TRACKDATAFORM_XA_2352:
593 case TRACKDATAFORM_MODE2_2352:
594 cbAtapiSector = 2352;
595 break;
596 case TRACKDATAFORM_MODE1_2048:
597 cbAtapiSector = 2048;
598 break;
599 case TRACKDATAFORM_CDDA_PAUSE:
600 case TRACKDATAFORM_MODE1_0:
601 case TRACKDATAFORM_XA_0:
602 case TRACKDATAFORM_MODE2_0:
603 cbAtapiSector = 0;
604 break;
605 case TRACKDATAFORM_XA_2336:
606 case TRACKDATAFORM_MODE2_2336:
607 cbAtapiSector = 2336;
608 break;
609 case TRACKDATAFORM_INVALID:
610 default:
611 AssertMsgFailed(("Invalid track data form %d\n", pTrack->enmMainDataForm));
612 }
613
614 switch (pTrack->enmSubChnDataForm)
615 {
616 case SUBCHNDATAFORM_0:
617 break;
618 case SUBCHNDATAFORM_96:
619 cbAtapiSector += 96;
620 break;
621 case SUBCHNDATAFORM_INVALID:
622 default:
623 AssertMsgFailed(("Invalid subchannel data form %d\n", pTrack->enmSubChnDataForm));
624 }
625 }
626 }
627
628 return cbAtapiSector;
629}
630
631
632static uint8_t atapiPassthroughCmdErrorSimple(uint8_t *pbSense, size_t cbSense, uint8_t uATAPISenseKey, uint8_t uATAPIASC)
633{
634 memset(pbSense, '\0', cbSense);
635 if (RT_LIKELY(cbSense >= 13))
636 {
637 pbSense[0] = 0x70 | (1 << 7);
638 pbSense[2] = uATAPISenseKey & 0x0f;
639 pbSense[7] = 10;
640 pbSense[12] = uATAPIASC;
641 }
642 return SCSI_STATUS_CHECK_CONDITION;
643}
644
645
646DECLHIDDEN(bool) ATAPIPassthroughParseCdb(const uint8_t *pbCdb, size_t cbCdb, size_t cbBuf,
647 PTRACKLIST pTrackList, uint8_t *pbSense, size_t cbSense,
648 PDMMEDIATXDIR *penmTxDir, size_t *pcbXfer,
649 size_t *pcbSector, uint8_t *pu8ScsiSts)
650{
651 uint32_t uLba = 0;
652 uint32_t cSectors = 0;
653 size_t cbSector = 0;
654 size_t cbXfer = 0;
655 bool fPassthrough = false;
656 PDMMEDIATXDIR enmTxDir = PDMMEDIATXDIR_NONE;
657
658 RT_NOREF(cbCdb);
659
660 switch (pbCdb[0])
661 {
662 /* First the commands we can pass through without further processing. */
663 case SCSI_BLANK:
664 case SCSI_CLOSE_TRACK_SESSION:
665 case SCSI_LOAD_UNLOAD_MEDIUM:
666 case SCSI_PAUSE_RESUME:
667 case SCSI_PLAY_AUDIO_10:
668 case SCSI_PLAY_AUDIO_12:
669 case SCSI_PLAY_AUDIO_MSF:
670 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
671 case SCSI_REPAIR_TRACK:
672 case SCSI_RESERVE_TRACK:
673 case SCSI_SCAN:
674 case SCSI_SEEK_10:
675 case SCSI_SET_CD_SPEED:
676 case SCSI_SET_READ_AHEAD:
677 case SCSI_START_STOP_UNIT:
678 case SCSI_STOP_PLAY_SCAN:
679 case SCSI_SYNCHRONIZE_CACHE:
680 case SCSI_TEST_UNIT_READY:
681 case SCSI_VERIFY_10:
682 fPassthrough = true;
683 break;
684 case SCSI_ERASE_10:
685 uLba = scsiBE2H_U32(pbCdb + 2);
686 cbXfer = scsiBE2H_U16(pbCdb + 7);
687 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
688 fPassthrough = true;
689 break;
690 case SCSI_FORMAT_UNIT:
691 cbXfer = cbBuf;
692 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
693 fPassthrough = true;
694 break;
695 case SCSI_GET_CONFIGURATION:
696 cbXfer = scsiBE2H_U16(pbCdb + 7);
697 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
698 fPassthrough = true;
699 break;
700 case SCSI_GET_EVENT_STATUS_NOTIFICATION:
701 cbXfer = scsiBE2H_U16(pbCdb + 7);
702 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
703 fPassthrough = true;
704 break;
705 case SCSI_GET_PERFORMANCE:
706 cbXfer = cbBuf;
707 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
708 fPassthrough = true;
709 break;
710 case SCSI_INQUIRY:
711 cbXfer = scsiBE2H_U16(pbCdb + 3);
712 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
713 fPassthrough = true;
714 break;
715 case SCSI_MECHANISM_STATUS:
716 cbXfer = scsiBE2H_U16(pbCdb + 8);
717 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
718 fPassthrough = true;
719 break;
720 case SCSI_MODE_SELECT_10:
721 cbXfer = scsiBE2H_U16(pbCdb + 7);
722 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
723 fPassthrough = true;
724 break;
725 case SCSI_MODE_SENSE_10:
726 cbXfer = scsiBE2H_U16(pbCdb + 7);
727 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
728 fPassthrough = true;
729 break;
730 case SCSI_READ_10:
731 uLba = scsiBE2H_U32(pbCdb + 2);
732 cSectors = scsiBE2H_U16(pbCdb + 7);
733 cbSector = 2048;
734 cbXfer = cSectors * cbSector;
735 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
736 fPassthrough = true;
737 break;
738 case SCSI_READ_12:
739 uLba = scsiBE2H_U32(pbCdb + 2);
740 cSectors = scsiBE2H_U32(pbCdb + 6);
741 cbSector = 2048;
742 cbXfer = cSectors * cbSector;
743 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
744 fPassthrough = true;
745 break;
746 case SCSI_READ_BUFFER:
747 cbXfer = scsiBE2H_U24(pbCdb + 6);
748 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
749 fPassthrough = true;
750 break;
751 case SCSI_READ_BUFFER_CAPACITY:
752 cbXfer = scsiBE2H_U16(pbCdb + 7);
753 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
754 fPassthrough = true;
755 break;
756 case SCSI_READ_CAPACITY:
757 cbXfer = 8;
758 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
759 fPassthrough = true;
760 break;
761 case SCSI_READ_CD:
762 case SCSI_READ_CD_MSF:
763 {
764 /* Get sector size based on the expected sector type field. */
765 switch ((pbCdb[1] >> 2) & 0x7)
766 {
767 case 0x0: /* All types. */
768 {
769 uint32_t iLbaStart;
770
771 if (pbCdb[0] == SCSI_READ_CD)
772 iLbaStart = scsiBE2H_U32(&pbCdb[2]);
773 else
774 iLbaStart = scsiMSF2LBA(&pbCdb[3]);
775
776 if (pTrackList)
777 cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pTrackList, iLbaStart);
778 else
779 cbSector = 2048; /* Might be incorrect if we couldn't determine the type. */
780 break;
781 }
782 case 0x1: /* CD-DA */
783 cbSector = 2352;
784 break;
785 case 0x2: /* Mode 1 */
786 cbSector = 2048;
787 break;
788 case 0x3: /* Mode 2 formless */
789 cbSector = 2336;
790 break;
791 case 0x4: /* Mode 2 form 1 */
792 cbSector = 2048;
793 break;
794 case 0x5: /* Mode 2 form 2 */
795 cbSector = 2324;
796 break;
797 default: /* Reserved */
798 AssertMsgFailed(("Unknown sector type\n"));
799 cbSector = 0; /** @todo we should probably fail the command here already. */
800 }
801
802 if (pbCdb[0] == SCSI_READ_CD)
803 cbXfer = scsiBE2H_U24(pbCdb + 6) * cbSector;
804 else /* SCSI_READ_MSF */
805 {
806 cSectors = scsiMSF2LBA(pbCdb + 6) - scsiMSF2LBA(pbCdb + 3);
807 if (cSectors > 32)
808 cSectors = 32; /* Limit transfer size to 64~74K. Safety first. In any case this can only harm software doing CDDA extraction. */
809 cbXfer = cSectors * cbSector;
810 }
811 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
812 fPassthrough = true;
813 break;
814 }
815 case SCSI_READ_DISC_INFORMATION:
816 cbXfer = scsiBE2H_U16(pbCdb + 7);
817 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
818 fPassthrough = true;
819 break;
820 case SCSI_READ_DVD_STRUCTURE:
821 cbXfer = scsiBE2H_U16(pbCdb + 8);
822 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
823 fPassthrough = true;
824 break;
825 case SCSI_READ_FORMAT_CAPACITIES:
826 cbXfer = scsiBE2H_U16(pbCdb + 7);
827 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
828 fPassthrough = true;
829 break;
830 case SCSI_READ_SUBCHANNEL:
831 cbXfer = scsiBE2H_U16(pbCdb + 7);
832 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
833 fPassthrough = true;
834 break;
835 case SCSI_READ_TOC_PMA_ATIP:
836 cbXfer = scsiBE2H_U16(pbCdb + 7);
837 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
838 fPassthrough = true;
839 break;
840 case SCSI_READ_TRACK_INFORMATION:
841 cbXfer = scsiBE2H_U16(pbCdb + 7);
842 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
843 fPassthrough = true;
844 break;
845 case SCSI_REPORT_KEY:
846 cbXfer = scsiBE2H_U16(pbCdb + 8);
847 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
848 fPassthrough = true;
849 break;
850 case SCSI_REQUEST_SENSE:
851 cbXfer = pbCdb[4];
852 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
853 fPassthrough = true;
854 break;
855 case SCSI_SEND_CUE_SHEET:
856 cbXfer = scsiBE2H_U24(pbCdb + 6);
857 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
858 fPassthrough = true;
859 break;
860 case SCSI_SEND_DVD_STRUCTURE:
861 cbXfer = scsiBE2H_U16(pbCdb + 8);
862 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
863 fPassthrough = true;
864 break;
865 case SCSI_SEND_EVENT:
866 cbXfer = scsiBE2H_U16(pbCdb + 8);
867 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
868 fPassthrough = true;
869 break;
870 case SCSI_SEND_KEY:
871 cbXfer = scsiBE2H_U16(pbCdb + 8);
872 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
873 fPassthrough = true;
874 break;
875 case SCSI_SEND_OPC_INFORMATION:
876 cbXfer = scsiBE2H_U16(pbCdb + 7);
877 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
878 fPassthrough = true;
879 break;
880 case SCSI_SET_STREAMING:
881 cbXfer = scsiBE2H_U16(pbCdb + 9);
882 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
883 fPassthrough = true;
884 break;
885 case SCSI_WRITE_10:
886 case SCSI_WRITE_AND_VERIFY_10:
887 uLba = scsiBE2H_U32(pbCdb + 2);
888 cSectors = scsiBE2H_U16(pbCdb + 7);
889 if (pTrackList)
890 cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pTrackList, uLba);
891 else
892 cbSector = 2048;
893 cbXfer = cSectors * cbSector;
894 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
895 fPassthrough = true;
896 break;
897 case SCSI_WRITE_12:
898 uLba = scsiBE2H_U32(pbCdb + 2);
899 cSectors = scsiBE2H_U32(pbCdb + 6);
900 if (pTrackList)
901 cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pTrackList, uLba);
902 else
903 cbSector = 2048;
904 cbXfer = cSectors * cbSector;
905 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
906 fPassthrough = true;
907 break;
908 case SCSI_WRITE_BUFFER:
909 switch (pbCdb[1] & 0x1f)
910 {
911 case 0x04: /* download microcode */
912 case 0x05: /* download microcode and save */
913 case 0x06: /* download microcode with offsets */
914 case 0x07: /* download microcode with offsets and save */
915 case 0x0e: /* download microcode with offsets and defer activation */
916 case 0x0f: /* activate deferred microcode */
917 LogRel(("ATAPI: CD-ROM passthrough command attempted to update firmware, blocked\n"));
918 *pu8ScsiSts = atapiPassthroughCmdErrorSimple(pbSense, cbSense, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
919 break;
920 default:
921 cbXfer = scsiBE2H_U16(pbCdb + 6);
922 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
923 fPassthrough = true;
924 break;
925 }
926 break;
927 case SCSI_REPORT_LUNS: /* Not part of MMC-3, but used by Windows. */
928 cbXfer = scsiBE2H_U32(pbCdb + 6);
929 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
930 fPassthrough = true;
931 break;
932 case SCSI_REZERO_UNIT:
933 /* Obsolete command used by cdrecord. What else would one expect?
934 * This command is not sent to the drive, it is handled internally,
935 * as the Linux kernel doesn't like it (message "scsi: unknown
936 * opcode 0x01" in syslog) and replies with a sense code of 0,
937 * which sends cdrecord to an endless loop. */
938 *pu8ScsiSts = atapiPassthroughCmdErrorSimple(pbSense, cbSense, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
939 break;
940 default:
941 LogRel(("ATAPI: Passthrough unimplemented for command %#x\n", pbCdb[0]));
942 *pu8ScsiSts = atapiPassthroughCmdErrorSimple(pbSense, cbSense, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
943 break;
944 }
945
946 if (fPassthrough)
947 {
948 *penmTxDir = enmTxDir;
949 *pcbXfer = cbXfer;
950 *pcbSector = cbSector;
951 }
952
953 return fPassthrough;
954}
955
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