VirtualBox

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

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