VirtualBox

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

Last change on this file was 99739, checked in by vboxsync, 12 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use