VirtualBox

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

Last change on this file since 90330 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.5 KB
Line 
1/* $Id: ATAPIPassthrough.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * VBox storage devices: ATAPI emulation (common code for DevATA and DevAHCI).
4 */
5
6/*
7 * Copyright (C) 2012-2020 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 * @param cbBuf The buffer size (max).
253 */
254static int atapiTrackListUpdateFromSendCueSheet(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf, size_t cbBuf)
255{
256 int rc;
257 unsigned cbCueSheet = scsiBE2H_U24(pbCDB + 6);
258 unsigned cTracks = cbCueSheet / 8;
259
260 AssertReturn(cbCueSheet % 8 == 0 && cTracks, VERR_INVALID_PARAMETER);
261
262 rc = atapiTrackListReallocate(pTrackList, cTracks, 0);
263 if (RT_SUCCESS(rc))
264 {
265 const uint8_t *pbCueSheet = (uint8_t *)pvBuf;
266 PTRACK pTrack = pTrackList->paTracks;
267 AssertLogRelReturn(cTracks <= cbBuf, VERR_BUFFER_OVERFLOW);
268
269 for (unsigned i = 0; i < cTracks; i++)
270 {
271 atapiTrackListEntryCreateFromCueSheetEntry(pTrack, pbCueSheet);
272 if (i == 0)
273 pTrack->fFlags |= TRACK_FLAGS_LEAD_IN;
274 pTrack++;
275 pbCueSheet += 8;
276 }
277 }
278
279 return rc;
280}
281
282static int atapiTrackListUpdateFromSendDvdStructure(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf, size_t cbBuf)
283{
284 RT_NOREF(pTrackList, pbCDB, pvBuf, cbBuf);
285 return VERR_NOT_IMPLEMENTED;
286}
287
288/**
289 * Update track list from formatted TOC data.
290 *
291 * @returns VBox status code.
292 * @param pTrackList The track list to update.
293 * @param iTrack The first track the TOC has data for.
294 * @param fMSF Flag whether block addresses are in MSF or LBA format.
295 * @param pbBuf Buffer holding the formatted TOC.
296 * @param cbBuffer Size of the buffer.
297 */
298static int atapiTrackListUpdateFromFormattedToc(PTRACKLIST pTrackList, uint8_t iTrack,
299 bool fMSF, const uint8_t *pbBuf, uint32_t cbBuffer)
300{
301 RT_NOREF(iTrack, cbBuffer); /** @todo unused parameters */
302 int rc;
303 unsigned cbToc = scsiBE2H_U16(pbBuf);
304 uint8_t iTrackFirst = pbBuf[2];
305 unsigned cTracks;
306
307 cbToc -= 2;
308 pbBuf += 4;
309 AssertReturn(cbToc % 8 == 0, VERR_INVALID_PARAMETER);
310
311 cTracks = cbToc / 8 + iTrackFirst;
312
313 rc = atapiTrackListReallocate(pTrackList, iTrackFirst + cTracks, ATAPI_TRACK_LIST_REALLOCATE_FLAGS_DONT_CLEAR);
314 if (RT_SUCCESS(rc))
315 {
316 PTRACK pTrack = &pTrackList->paTracks[iTrackFirst];
317
318 for (unsigned i = iTrackFirst; i < cTracks; i++)
319 {
320 if (pbBuf[1] & 0x4)
321 pTrack->enmMainDataForm = TRACKDATAFORM_MODE1_2048;
322 else
323 pTrack->enmMainDataForm = TRACKDATAFORM_CDDA;
324
325 pTrack->enmSubChnDataForm = SUBCHNDATAFORM_0;
326 if (fMSF)
327 pTrack->iLbaStart = scsiMSF2LBA(&pbBuf[4]);
328 else
329 pTrack->iLbaStart = scsiBE2H_U32(&pbBuf[4]);
330
331 if (pbBuf[2] != 0xaa)
332 {
333 /* Calculate number of sectors from the next entry. */
334 int64_t iLbaNext;
335
336 if (fMSF)
337 iLbaNext = scsiMSF2LBA(&pbBuf[4+8]);
338 else
339 iLbaNext = scsiBE2H_U32(&pbBuf[4+8]);
340
341 pTrack->cSectors = iLbaNext - pTrack->iLbaStart;
342 }
343 else
344 pTrack->cSectors = 0;
345
346 pTrack->fFlags &= ~TRACK_FLAGS_UNDETECTED;
347 pbBuf += 8;
348 pTrack++;
349 }
350 }
351
352 return rc;
353}
354
355static int atapiTrackListUpdateFromReadTocPmaAtip(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf, size_t cbBuf)
356{
357 int rc;
358 uint16_t cbBuffer = (uint16_t)RT_MIN(scsiBE2H_U16(&pbCDB[7]), cbBuf);
359 bool fMSF = (pbCDB[1] & 0x2) != 0;
360 uint8_t uFmt = pbCDB[2] & 0xf;
361 uint8_t iTrack = pbCDB[6];
362
363 switch (uFmt)
364 {
365 case 0x00:
366 rc = atapiTrackListUpdateFromFormattedToc(pTrackList, iTrack, fMSF, (uint8_t *)pvBuf, cbBuffer);
367 break;
368 case 0x01:
369 case 0x02:
370 case 0x03:
371 case 0x04:
372 rc = VERR_NOT_IMPLEMENTED;
373 break;
374 case 0x05:
375 rc = VINF_SUCCESS; /* Does not give information about the tracklist. */
376 break;
377 default:
378 rc = VERR_INVALID_PARAMETER;
379 }
380
381 return rc;
382}
383
384static int atapiTrackListUpdateFromReadTrackInformation(PTRACKLIST pTrackList, const uint8_t *pbCDB,
385 const void *pvBuf, size_t cbBuf)
386{
387 RT_NOREF(pTrackList, pbCDB, pvBuf, cbBuf);
388 return VERR_NOT_IMPLEMENTED;
389}
390
391static int atapiTrackListUpdateFromReadDvdStructure(PTRACKLIST pTrackList, const uint8_t *pbCDB,
392 const void *pvBuf, size_t cbBuf)
393{
394 RT_NOREF(pTrackList, pbCDB, pvBuf, cbBuf);
395 return VERR_NOT_IMPLEMENTED;
396}
397
398static int atapiTrackListUpdateFromReadDiscInformation(PTRACKLIST pTrackList, const uint8_t *pbCDB,
399 const void *pvBuf, size_t cbBuf)
400{
401 RT_NOREF(pTrackList, pbCDB, pvBuf, cbBuf);
402 return VERR_NOT_IMPLEMENTED;
403}
404
405#ifdef LOG_ENABLED
406
407/**
408 * Converts the given track data form to a string.
409 *
410 * @returns Track data form as a string.
411 * @param enmTrackDataForm The track main data form.
412 */
413static const char *atapiTrackListMainDataFormToString(TRACKDATAFORM enmTrackDataForm)
414{
415 switch (enmTrackDataForm)
416 {
417 case TRACKDATAFORM_CDDA:
418 return "CD-DA";
419 case TRACKDATAFORM_CDDA_PAUSE:
420 return "CD-DA Pause";
421 case TRACKDATAFORM_MODE1_2048:
422 return "Mode 1 (2048 bytes)";
423 case TRACKDATAFORM_MODE1_2352:
424 return "Mode 1 (2352 bytes)";
425 case TRACKDATAFORM_MODE1_0:
426 return "Mode 1 (0 bytes)";
427 case TRACKDATAFORM_XA_2336:
428 return "XA (2336 bytes)";
429 case TRACKDATAFORM_XA_2352:
430 return "XA (2352 bytes)";
431 case TRACKDATAFORM_XA_0:
432 return "XA (0 bytes)";
433 case TRACKDATAFORM_MODE2_2336:
434 return "Mode 2 (2336 bytes)";
435 case TRACKDATAFORM_MODE2_2352:
436 return "Mode 2 (2352 bytes)";
437 case TRACKDATAFORM_MODE2_0:
438 return "Mode 2 (0 bytes)";
439 case TRACKDATAFORM_INVALID:
440 default:
441 return "Invalid";
442 }
443}
444
445/**
446 * Converts the given subchannel data form to a string.
447 *
448 * @returns Subchannel data form as a string.
449 * @param enmSubChnDataForm The subchannel main data form.
450 */
451static const char *atapiTrackListSubChnDataFormToString(SUBCHNDATAFORM enmSubChnDataForm)
452{
453 switch (enmSubChnDataForm)
454 {
455 case SUBCHNDATAFORM_0:
456 return "0";
457 case SUBCHNDATAFORM_96:
458 return "96";
459 case SUBCHNDATAFORM_INVALID:
460 default:
461 return "Invalid";
462 }
463}
464
465/**
466 * Dump the complete track list to the release log.
467 *
468 * @returns nothing.
469 * @param pTrackList The track list to dump.
470 */
471static void atapiTrackListDump(PTRACKLIST pTrackList)
472{
473 LogRel(("Track List: cTracks=%u\n", pTrackList->cTracksCurrent));
474 for (unsigned i = 0; i < pTrackList->cTracksCurrent; i++)
475 {
476 PTRACK pTrack = &pTrackList->paTracks[i];
477
478 LogRel((" Track %u: LBAStart=%lld cSectors=%u enmMainDataForm=%s enmSubChnDataForm=%s fFlags=[%s%s%s]\n",
479 i, pTrack->iLbaStart, pTrack->cSectors, atapiTrackListMainDataFormToString(pTrack->enmMainDataForm),
480 atapiTrackListSubChnDataFormToString(pTrack->enmSubChnDataForm),
481 pTrack->fFlags & TRACK_FLAGS_UNDETECTED ? "UNDETECTED " : "",
482 pTrack->fFlags & TRACK_FLAGS_LEAD_IN ? "Lead-In " : "",
483 pTrack->fFlags & TRACK_FLAGS_LEAD_OUT ? "Lead-Out" : ""));
484 }
485}
486
487#endif /* LOG_ENABLED */
488
489/**
490 * Creates an empty track list handle.
491 *
492 * @returns VBox status code.
493 * @param ppTrackList Where to store the track list handle on success.
494 */
495DECLHIDDEN(int) ATAPIPassthroughTrackListCreateEmpty(PTRACKLIST *ppTrackList)
496{
497 PTRACKLIST pTrackList = (PTRACKLIST)RTMemAllocZ(sizeof(TRACKLIST));
498 if (pTrackList)
499 {
500 *ppTrackList = pTrackList;
501 return VINF_SUCCESS;
502 }
503 return VERR_NO_MEMORY;
504}
505
506/**
507 * Destroys the allocated task list handle.
508 *
509 * @returns nothing.
510 * @param pTrackList The track list handle to destroy.
511 */
512DECLHIDDEN(void) ATAPIPassthroughTrackListDestroy(PTRACKLIST pTrackList)
513{
514 if (pTrackList->paTracks)
515 RTMemFree(pTrackList->paTracks);
516 RTMemFree(pTrackList);
517}
518
519/**
520 * Clears all tracks from the given task list.
521 *
522 * @returns nothing.
523 * @param pTrackList The track list to clear.
524 */
525DECLHIDDEN(void) ATAPIPassthroughTrackListClear(PTRACKLIST pTrackList)
526{
527 AssertPtrReturnVoid(pTrackList);
528
529 pTrackList->cTracksCurrent = 0;
530
531 /* Mark all tracks as undetected. */
532 for (unsigned i = 0; i < pTrackList->cTracksMax; i++)
533 pTrackList->paTracks[i].fFlags |= TRACK_FLAGS_UNDETECTED;
534}
535
536/**
537 * Updates the track list from the given CDB and data buffer.
538 *
539 * @returns VBox status code.
540 * @param pTrackList The track list to update.
541 * @param pbCDB The CDB buffer.
542 * @param pvBuf The data buffer.
543 * @param cbBuf The buffer isze.
544 */
545DECLHIDDEN(int) ATAPIPassthroughTrackListUpdate(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf, size_t cbBuf)
546{
547 int rc;
548
549 switch (pbCDB[0])
550 {
551 case SCSI_SEND_CUE_SHEET:
552 rc = atapiTrackListUpdateFromSendCueSheet(pTrackList, pbCDB, pvBuf, cbBuf);
553 break;
554 case SCSI_SEND_DVD_STRUCTURE:
555 rc = atapiTrackListUpdateFromSendDvdStructure(pTrackList, pbCDB, pvBuf, cbBuf);
556 break;
557 case SCSI_READ_TOC_PMA_ATIP:
558 rc = atapiTrackListUpdateFromReadTocPmaAtip(pTrackList, pbCDB, pvBuf, cbBuf);
559 break;
560 case SCSI_READ_TRACK_INFORMATION:
561 rc = atapiTrackListUpdateFromReadTrackInformation(pTrackList, pbCDB, pvBuf, cbBuf);
562 break;
563 case SCSI_READ_DVD_STRUCTURE:
564 rc = atapiTrackListUpdateFromReadDvdStructure(pTrackList, pbCDB, pvBuf, cbBuf);
565 break;
566 case SCSI_READ_DISC_INFORMATION:
567 rc = atapiTrackListUpdateFromReadDiscInformation(pTrackList, pbCDB, pvBuf, cbBuf);
568 break;
569 default:
570 LogRel(("ATAPI: Invalid opcode %#x while determining media layout\n", pbCDB[0]));
571 rc = VERR_INVALID_PARAMETER;
572 }
573
574#ifdef LOG_ENABLED
575 atapiTrackListDump(pTrackList);
576#endif
577
578 return rc;
579}
580
581/**
582 * Return the sector size from the track matching the LBA in the given track list.
583 *
584 * @returns Sector size.
585 * @param pTrackList The track list to use.
586 * @param iAtapiLba The start LBA to get the sector size for.
587 */
588DECLHIDDEN(uint32_t) ATAPIPassthroughTrackListGetSectorSizeFromLba(PTRACKLIST pTrackList, uint32_t iAtapiLba)
589{
590 PTRACK pTrack = NULL;
591 uint32_t cbAtapiSector = 2048;
592
593 if (pTrackList->cTracksCurrent)
594 {
595 if ( iAtapiLba > UINT32_C(0xffff4fa1)
596 && (int32_t)iAtapiLba < -150)
597 {
598 /* Lead-In area, this is always the first entry in the cue sheet. */
599 pTrack = pTrackList->paTracks;
600 Assert(pTrack->fFlags & TRACK_FLAGS_LEAD_IN);
601 LogFlowFunc(("Selected Lead-In area\n"));
602 }
603 else
604 {
605 int64_t iAtapiLba64 = (int32_t)iAtapiLba;
606 pTrack = &pTrackList->paTracks[1];
607
608 /* Go through the track list and find the correct entry. */
609 for (unsigned i = 1; i < pTrackList->cTracksCurrent - 1; i++)
610 {
611 if (pTrack->fFlags & TRACK_FLAGS_UNDETECTED)
612 continue;
613
614 if ( pTrack->iLbaStart <= iAtapiLba64
615 && iAtapiLba64 < pTrack->iLbaStart + pTrack->cSectors)
616 break;
617
618 pTrack++;
619 }
620 }
621
622 if (pTrack)
623 {
624 switch (pTrack->enmMainDataForm)
625 {
626 case TRACKDATAFORM_CDDA:
627 case TRACKDATAFORM_MODE1_2352:
628 case TRACKDATAFORM_XA_2352:
629 case TRACKDATAFORM_MODE2_2352:
630 cbAtapiSector = 2352;
631 break;
632 case TRACKDATAFORM_MODE1_2048:
633 cbAtapiSector = 2048;
634 break;
635 case TRACKDATAFORM_CDDA_PAUSE:
636 case TRACKDATAFORM_MODE1_0:
637 case TRACKDATAFORM_XA_0:
638 case TRACKDATAFORM_MODE2_0:
639 cbAtapiSector = 0;
640 break;
641 case TRACKDATAFORM_XA_2336:
642 case TRACKDATAFORM_MODE2_2336:
643 cbAtapiSector = 2336;
644 break;
645 case TRACKDATAFORM_INVALID:
646 default:
647 AssertMsgFailed(("Invalid track data form %d\n", pTrack->enmMainDataForm));
648 }
649
650 switch (pTrack->enmSubChnDataForm)
651 {
652 case SUBCHNDATAFORM_0:
653 break;
654 case SUBCHNDATAFORM_96:
655 cbAtapiSector += 96;
656 break;
657 case SUBCHNDATAFORM_INVALID:
658 default:
659 AssertMsgFailed(("Invalid subchannel data form %d\n", pTrack->enmSubChnDataForm));
660 }
661 }
662 }
663
664 return cbAtapiSector;
665}
666
667
668static uint8_t atapiPassthroughCmdErrorSimple(uint8_t *pbSense, size_t cbSense, uint8_t uATAPISenseKey, uint8_t uATAPIASC)
669{
670 memset(pbSense, '\0', cbSense);
671 if (RT_LIKELY(cbSense >= 13))
672 {
673 pbSense[0] = 0x70 | (1 << 7);
674 pbSense[2] = uATAPISenseKey & 0x0f;
675 pbSense[7] = 10;
676 pbSense[12] = uATAPIASC;
677 }
678 return SCSI_STATUS_CHECK_CONDITION;
679}
680
681
682/**
683 * Parses the given CDB and returns whether it is safe to pass it through to the host drive.
684 *
685 * @returns Flag whether passing the CDB through to the host drive is safe.
686 * @param pbCdb The CDB to parse.
687 * @param cbCdb Size of the CDB in bytes.
688 * @param cbBuf Size of the guest buffer.
689 * @param pTrackList The track list for the current medium if available (optional).
690 * @param pbSense Pointer to the sense buffer.
691 * @param cbSense Size of the sense buffer.
692 * @param penmTxDir Where to store the transfer direction (guest to host or vice versa).
693 * @param pcbXfer Where to store the transfer size encoded in the CDB.
694 * @param pcbSector Where to store the sector size used for the transfer.
695 * @param pu8ScsiSts Where to store the SCSI status code.
696 */
697DECLHIDDEN(bool) ATAPIPassthroughParseCdb(const uint8_t *pbCdb, size_t cbCdb, size_t cbBuf,
698 PTRACKLIST pTrackList, uint8_t *pbSense, size_t cbSense,
699 PDMMEDIATXDIR *penmTxDir, size_t *pcbXfer,
700 size_t *pcbSector, uint8_t *pu8ScsiSts)
701{
702 uint32_t uLba = 0;
703 uint32_t cSectors = 0;
704 size_t cbSector = 0;
705 size_t cbXfer = 0;
706 bool fPassthrough = false;
707 PDMMEDIATXDIR enmTxDir = PDMMEDIATXDIR_NONE;
708
709 RT_NOREF(cbCdb);
710
711 switch (pbCdb[0])
712 {
713 /* First the commands we can pass through without further processing. */
714 case SCSI_BLANK:
715 case SCSI_CLOSE_TRACK_SESSION:
716 case SCSI_LOAD_UNLOAD_MEDIUM:
717 case SCSI_PAUSE_RESUME:
718 case SCSI_PLAY_AUDIO_10:
719 case SCSI_PLAY_AUDIO_12:
720 case SCSI_PLAY_AUDIO_MSF:
721 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
722 case SCSI_REPAIR_TRACK:
723 case SCSI_RESERVE_TRACK:
724 case SCSI_SCAN:
725 case SCSI_SEEK_10:
726 case SCSI_SET_CD_SPEED:
727 case SCSI_SET_READ_AHEAD:
728 case SCSI_START_STOP_UNIT:
729 case SCSI_STOP_PLAY_SCAN:
730 case SCSI_SYNCHRONIZE_CACHE:
731 case SCSI_TEST_UNIT_READY:
732 case SCSI_VERIFY_10:
733 fPassthrough = true;
734 break;
735 case SCSI_ERASE_10:
736 uLba = scsiBE2H_U32(pbCdb + 2);
737 cbXfer = scsiBE2H_U16(pbCdb + 7);
738 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
739 fPassthrough = true;
740 break;
741 case SCSI_FORMAT_UNIT:
742 cbXfer = cbBuf;
743 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
744 fPassthrough = true;
745 break;
746 case SCSI_GET_CONFIGURATION:
747 cbXfer = scsiBE2H_U16(pbCdb + 7);
748 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
749 fPassthrough = true;
750 break;
751 case SCSI_GET_EVENT_STATUS_NOTIFICATION:
752 cbXfer = scsiBE2H_U16(pbCdb + 7);
753 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
754 fPassthrough = true;
755 break;
756 case SCSI_GET_PERFORMANCE:
757 cbXfer = cbBuf;
758 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
759 fPassthrough = true;
760 break;
761 case SCSI_INQUIRY:
762 cbXfer = scsiBE2H_U16(pbCdb + 3);
763 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
764 fPassthrough = true;
765 break;
766 case SCSI_MECHANISM_STATUS:
767 cbXfer = scsiBE2H_U16(pbCdb + 8);
768 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
769 fPassthrough = true;
770 break;
771 case SCSI_MODE_SELECT_10:
772 cbXfer = scsiBE2H_U16(pbCdb + 7);
773 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
774 fPassthrough = true;
775 break;
776 case SCSI_MODE_SENSE_10:
777 cbXfer = scsiBE2H_U16(pbCdb + 7);
778 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
779 fPassthrough = true;
780 break;
781 case SCSI_READ_10:
782 uLba = scsiBE2H_U32(pbCdb + 2);
783 cSectors = scsiBE2H_U16(pbCdb + 7);
784 cbSector = 2048;
785 cbXfer = cSectors * cbSector;
786 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
787 fPassthrough = true;
788 break;
789 case SCSI_READ_12:
790 uLba = scsiBE2H_U32(pbCdb + 2);
791 cSectors = scsiBE2H_U32(pbCdb + 6);
792 cbSector = 2048;
793 cbXfer = cSectors * cbSector;
794 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
795 fPassthrough = true;
796 break;
797 case SCSI_READ_BUFFER:
798 cbXfer = scsiBE2H_U24(pbCdb + 6);
799 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
800 fPassthrough = true;
801 break;
802 case SCSI_READ_BUFFER_CAPACITY:
803 cbXfer = scsiBE2H_U16(pbCdb + 7);
804 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
805 fPassthrough = true;
806 break;
807 case SCSI_READ_CAPACITY:
808 cbXfer = 8;
809 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
810 fPassthrough = true;
811 break;
812 case SCSI_READ_CD:
813 case SCSI_READ_CD_MSF:
814 {
815 /* Get sector size based on the expected sector type field. */
816 switch ((pbCdb[1] >> 2) & 0x7)
817 {
818 case 0x0: /* All types. */
819 {
820 uint32_t iLbaStart;
821
822 if (pbCdb[0] == SCSI_READ_CD)
823 iLbaStart = scsiBE2H_U32(&pbCdb[2]);
824 else
825 iLbaStart = scsiMSF2LBA(&pbCdb[3]);
826
827 if (pTrackList)
828 cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pTrackList, iLbaStart);
829 else
830 cbSector = 2048; /* Might be incorrect if we couldn't determine the type. */
831 break;
832 }
833 case 0x1: /* CD-DA */
834 cbSector = 2352;
835 break;
836 case 0x2: /* Mode 1 */
837 cbSector = 2048;
838 break;
839 case 0x3: /* Mode 2 formless */
840 cbSector = 2336;
841 break;
842 case 0x4: /* Mode 2 form 1 */
843 cbSector = 2048;
844 break;
845 case 0x5: /* Mode 2 form 2 */
846 cbSector = 2324;
847 break;
848 default: /* Reserved */
849 AssertMsgFailed(("Unknown sector type\n"));
850 cbSector = 0; /** @todo we should probably fail the command here already. */
851 }
852
853 if (pbCdb[0] == SCSI_READ_CD)
854 cbXfer = scsiBE2H_U24(pbCdb + 6) * cbSector;
855 else /* SCSI_READ_MSF */
856 {
857 cSectors = scsiMSF2LBA(pbCdb + 6) - scsiMSF2LBA(pbCdb + 3);
858 if (cSectors > 32)
859 cSectors = 32; /* Limit transfer size to 64~74K. Safety first. In any case this can only harm software doing CDDA extraction. */
860 cbXfer = cSectors * cbSector;
861 }
862 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
863 fPassthrough = true;
864 break;
865 }
866 case SCSI_READ_DISC_INFORMATION:
867 cbXfer = scsiBE2H_U16(pbCdb + 7);
868 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
869 fPassthrough = true;
870 break;
871 case SCSI_READ_DVD_STRUCTURE:
872 cbXfer = scsiBE2H_U16(pbCdb + 8);
873 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
874 fPassthrough = true;
875 break;
876 case SCSI_READ_FORMAT_CAPACITIES:
877 cbXfer = scsiBE2H_U16(pbCdb + 7);
878 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
879 fPassthrough = true;
880 break;
881 case SCSI_READ_SUBCHANNEL:
882 cbXfer = scsiBE2H_U16(pbCdb + 7);
883 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
884 fPassthrough = true;
885 break;
886 case SCSI_READ_TOC_PMA_ATIP:
887 cbXfer = scsiBE2H_U16(pbCdb + 7);
888 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
889 fPassthrough = true;
890 break;
891 case SCSI_READ_TRACK_INFORMATION:
892 cbXfer = scsiBE2H_U16(pbCdb + 7);
893 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
894 fPassthrough = true;
895 break;
896 case SCSI_REPORT_KEY:
897 cbXfer = scsiBE2H_U16(pbCdb + 8);
898 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
899 fPassthrough = true;
900 break;
901 case SCSI_REQUEST_SENSE:
902 cbXfer = pbCdb[4];
903 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
904 fPassthrough = true;
905 break;
906 case SCSI_SEND_CUE_SHEET:
907 cbXfer = scsiBE2H_U24(pbCdb + 6);
908 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
909 fPassthrough = true;
910 break;
911 case SCSI_SEND_DVD_STRUCTURE:
912 cbXfer = scsiBE2H_U16(pbCdb + 8);
913 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
914 fPassthrough = true;
915 break;
916 case SCSI_SEND_EVENT:
917 cbXfer = scsiBE2H_U16(pbCdb + 8);
918 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
919 fPassthrough = true;
920 break;
921 case SCSI_SEND_KEY:
922 cbXfer = scsiBE2H_U16(pbCdb + 8);
923 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
924 fPassthrough = true;
925 break;
926 case SCSI_SEND_OPC_INFORMATION:
927 cbXfer = scsiBE2H_U16(pbCdb + 7);
928 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
929 fPassthrough = true;
930 break;
931 case SCSI_SET_STREAMING:
932 cbXfer = scsiBE2H_U16(pbCdb + 9);
933 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
934 fPassthrough = true;
935 break;
936 case SCSI_WRITE_10:
937 case SCSI_WRITE_AND_VERIFY_10:
938 uLba = scsiBE2H_U32(pbCdb + 2);
939 cSectors = scsiBE2H_U16(pbCdb + 7);
940 if (pTrackList)
941 cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pTrackList, uLba);
942 else
943 cbSector = 2048;
944 cbXfer = cSectors * cbSector;
945 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
946 fPassthrough = true;
947 break;
948 case SCSI_WRITE_12:
949 uLba = scsiBE2H_U32(pbCdb + 2);
950 cSectors = scsiBE2H_U32(pbCdb + 6);
951 if (pTrackList)
952 cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pTrackList, uLba);
953 else
954 cbSector = 2048;
955 cbXfer = cSectors * cbSector;
956 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
957 fPassthrough = true;
958 break;
959 case SCSI_WRITE_BUFFER:
960 switch (pbCdb[1] & 0x1f)
961 {
962 case 0x04: /* download microcode */
963 case 0x05: /* download microcode and save */
964 case 0x06: /* download microcode with offsets */
965 case 0x07: /* download microcode with offsets and save */
966 case 0x0e: /* download microcode with offsets and defer activation */
967 case 0x0f: /* activate deferred microcode */
968 LogRel(("ATAPI: CD-ROM passthrough command attempted to update firmware, blocked\n"));
969 *pu8ScsiSts = atapiPassthroughCmdErrorSimple(pbSense, cbSense, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
970 break;
971 default:
972 cbXfer = scsiBE2H_U16(pbCdb + 6);
973 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
974 fPassthrough = true;
975 break;
976 }
977 break;
978 case SCSI_REPORT_LUNS: /* Not part of MMC-3, but used by Windows. */
979 cbXfer = scsiBE2H_U32(pbCdb + 6);
980 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
981 fPassthrough = true;
982 break;
983 case SCSI_REZERO_UNIT:
984 /* Obsolete command used by cdrecord. What else would one expect?
985 * This command is not sent to the drive, it is handled internally,
986 * as the Linux kernel doesn't like it (message "scsi: unknown
987 * opcode 0x01" in syslog) and replies with a sense code of 0,
988 * which sends cdrecord to an endless loop. */
989 *pu8ScsiSts = atapiPassthroughCmdErrorSimple(pbSense, cbSense, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
990 break;
991 default:
992 LogRel(("ATAPI: Passthrough unimplemented for command %#x\n", pbCdb[0]));
993 *pu8ScsiSts = atapiPassthroughCmdErrorSimple(pbSense, cbSense, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
994 break;
995 }
996
997 if (fPassthrough)
998 {
999 *penmTxDir = enmTxDir;
1000 *pcbXfer = cbXfer;
1001 *pcbSector = cbSector;
1002 }
1003
1004 return fPassthrough;
1005}
1006
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