VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvHostDVD.cpp@ 65819

Last change on this file since 65819 was 65113, checked in by vboxsync, 8 years ago

Devices/Storage/DrvHostDVD: Missed replacing drvHostDvdSCSIPadStr with scsiPadStr

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.4 KB
Line 
1/* $Id: DrvHostDVD.cpp 65113 2017-01-04 14:17:40Z vboxsync $ */
2/** @file
3 * DrvHostDVD - Host DVD block driver.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_HOST_DVD
23#include <iprt/asm.h>
24#include <VBox/vmm/pdmdrv.h>
25#include <VBox/vmm/pdmstorageifs.h>
26#include <iprt/asm.h>
27#include <iprt/assert.h>
28#include <iprt/file.h>
29#include <iprt/string.h>
30#include <iprt/thread.h>
31#include <iprt/critsect.h>
32#include <VBox/scsi.h>
33#include <VBox/scsiinline.h>
34
35#include "VBoxDD.h"
36#include "DrvHostBase.h"
37#include "ATAPIPassthrough.h"
38
39/** ATAPI sense info size. */
40#define ATAPI_SENSE_SIZE 64
41/** Size of an ATAPI packet. */
42#define ATAPI_PACKET_SIZE 12
43
44/**
45 * Host DVD driver instance data.
46 */
47typedef struct DRVHOSTDVD
48{
49 /** Base drivr data. */
50 DRVHOSTBASE Core;
51 /** The current tracklist of the loaded medium if passthrough is used. */
52 PTRACKLIST pTrackList;
53 /** ATAPI sense data. */
54 uint8_t abATAPISense[ATAPI_SENSE_SIZE];
55} DRVHOSTDVD;
56/** Pointer to the host DVD driver instance data. */
57typedef DRVHOSTDVD *PDRVHOSTDVD;
58
59/*********************************************************************************************************************************
60* Internal Functions *
61*********************************************************************************************************************************/
62
63
64static uint8_t drvHostDvdCmdOK(PDRVHOSTDVD pThis)
65{
66 memset(pThis->abATAPISense, '\0', sizeof(pThis->abATAPISense));
67 pThis->abATAPISense[0] = 0x70;
68 pThis->abATAPISense[7] = 10;
69 return SCSI_STATUS_OK;
70}
71
72static uint8_t drvHostDvdCmdError(PDRVHOSTDVD pThis, const uint8_t *pabATAPISense, size_t cbATAPISense)
73{
74 Log(("%s: sense=%#x (%s) asc=%#x ascq=%#x (%s)\n", __FUNCTION__, pabATAPISense[2] & 0x0f, SCSISenseText(pabATAPISense[2] & 0x0f),
75 pabATAPISense[12], pabATAPISense[13], SCSISenseExtText(pabATAPISense[12], pabATAPISense[13])));
76 memset(pThis->abATAPISense, '\0', sizeof(pThis->abATAPISense));
77 memcpy(pThis->abATAPISense, pabATAPISense, RT_MIN(cbATAPISense, sizeof(pThis->abATAPISense)));
78 return SCSI_STATUS_CHECK_CONDITION;
79}
80
81/** @todo deprecated function - doesn't provide enough info. Replace by direct
82 * calls to drvHostDvdCmdError() with full data. */
83static uint8_t drvHostDvdCmdErrorSimple(PDRVHOSTDVD pThis, uint8_t uATAPISenseKey, uint8_t uATAPIASC)
84{
85 uint8_t abATAPISense[ATAPI_SENSE_SIZE];
86 memset(abATAPISense, '\0', sizeof(abATAPISense));
87 abATAPISense[0] = 0x70 | (1 << 7);
88 abATAPISense[2] = uATAPISenseKey & 0x0f;
89 abATAPISense[7] = 10;
90 abATAPISense[12] = uATAPIASC;
91 return drvHostDvdCmdError(pThis, abATAPISense, sizeof(abATAPISense));
92}
93
94
95static bool drvHostDvdParseCdb(PDRVHOSTDVD pThis, PDRVHOSTBASEREQ pReq,
96 const uint8_t *pbCdb, size_t cbCdb, size_t cbBuf,
97 PDMMEDIATXDIR *penmTxDir, size_t *pcbXfer,
98 size_t *pcbSector, uint8_t *pu8ScsiSts)
99{
100 uint32_t uLba = 0;
101 uint32_t cSectors = 0;
102 size_t cbSector = 0;
103 size_t cbXfer = 0;
104 bool fPassthrough = false;
105 PDMMEDIATXDIR enmTxDir = PDMMEDIATXDIR_NONE;
106
107 RT_NOREF(cbCdb);
108
109 switch (pbCdb[0])
110 {
111 /* First the commands we can pass through without further processing. */
112 case SCSI_BLANK:
113 case SCSI_CLOSE_TRACK_SESSION:
114 case SCSI_LOAD_UNLOAD_MEDIUM:
115 case SCSI_PAUSE_RESUME:
116 case SCSI_PLAY_AUDIO_10:
117 case SCSI_PLAY_AUDIO_12:
118 case SCSI_PLAY_AUDIO_MSF:
119 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
120 case SCSI_REPAIR_TRACK:
121 case SCSI_RESERVE_TRACK:
122 case SCSI_SCAN:
123 case SCSI_SEEK_10:
124 case SCSI_SET_CD_SPEED:
125 case SCSI_SET_READ_AHEAD:
126 case SCSI_START_STOP_UNIT:
127 case SCSI_STOP_PLAY_SCAN:
128 case SCSI_SYNCHRONIZE_CACHE:
129 case SCSI_TEST_UNIT_READY:
130 case SCSI_VERIFY_10:
131 fPassthrough = true;
132 break;
133 case SCSI_ERASE_10:
134 uLba = scsiBE2H_U32(pbCdb + 2);
135 cbXfer = scsiBE2H_U16(pbCdb + 7);
136 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
137 fPassthrough = true;
138 break;
139 case SCSI_FORMAT_UNIT:
140 cbXfer = cbBuf;
141 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
142 fPassthrough = true;
143 break;
144 case SCSI_GET_CONFIGURATION:
145 cbXfer = scsiBE2H_U16(pbCdb + 7);
146 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
147 fPassthrough = true;
148 break;
149 case SCSI_GET_EVENT_STATUS_NOTIFICATION:
150 cbXfer = scsiBE2H_U16(pbCdb + 7);
151 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
152 fPassthrough = true;
153 break;
154 case SCSI_GET_PERFORMANCE:
155 cbXfer = cbBuf;
156 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
157 fPassthrough = true;
158 break;
159 case SCSI_INQUIRY:
160 cbXfer = scsiBE2H_U16(pbCdb + 3);
161 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
162 fPassthrough = true;
163 break;
164 case SCSI_MECHANISM_STATUS:
165 cbXfer = scsiBE2H_U16(pbCdb + 8);
166 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
167 fPassthrough = true;
168 break;
169 case SCSI_MODE_SELECT_10:
170 cbXfer = scsiBE2H_U16(pbCdb + 7);
171 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
172 fPassthrough = true;
173 break;
174 case SCSI_MODE_SENSE_10:
175 cbXfer = scsiBE2H_U16(pbCdb + 7);
176 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
177 fPassthrough = true;
178 break;
179 case SCSI_READ_10:
180 uLba = scsiBE2H_U32(pbCdb + 2);
181 cSectors = scsiBE2H_U16(pbCdb + 7);
182 cbSector = 2048;
183 cbXfer = cSectors * cbSector;
184 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
185 fPassthrough = true;
186 break;
187 case SCSI_READ_12:
188 uLba = scsiBE2H_U32(pbCdb + 2);
189 cSectors = scsiBE2H_U32(pbCdb + 6);
190 cbSector = 2048;
191 cbXfer = cSectors * cbSector;
192 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
193 fPassthrough = true;
194 break;
195 case SCSI_READ_BUFFER:
196 cbXfer = scsiBE2H_U24(pbCdb + 6);
197 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
198 fPassthrough = true;
199 break;
200 case SCSI_READ_BUFFER_CAPACITY:
201 cbXfer = scsiBE2H_U16(pbCdb + 7);
202 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
203 fPassthrough = true;
204 break;
205 case SCSI_READ_CAPACITY:
206 cbXfer = 8;
207 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
208 fPassthrough = true;
209 break;
210 case SCSI_READ_CD:
211 case SCSI_READ_CD_MSF:
212 {
213 /* Get sector size based on the expected sector type field. */
214 switch ((pbCdb[1] >> 2) & 0x7)
215 {
216 case 0x0: /* All types. */
217 {
218 uint32_t iLbaStart;
219
220 if (pbCdb[0] == SCSI_READ_CD)
221 iLbaStart = scsiBE2H_U32(&pbCdb[2]);
222 else
223 iLbaStart = scsiMSF2LBA(&pbCdb[3]);
224
225 if (pThis->pTrackList)
226 cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pThis->pTrackList, iLbaStart);
227 else
228 cbSector = 2048; /* Might be incorrect if we couldn't determine the type. */
229 break;
230 }
231 case 0x1: /* CD-DA */
232 cbSector = 2352;
233 break;
234 case 0x2: /* Mode 1 */
235 cbSector = 2048;
236 break;
237 case 0x3: /* Mode 2 formless */
238 cbSector = 2336;
239 break;
240 case 0x4: /* Mode 2 form 1 */
241 cbSector = 2048;
242 break;
243 case 0x5: /* Mode 2 form 2 */
244 cbSector = 2324;
245 break;
246 default: /* Reserved */
247 AssertMsgFailed(("Unknown sector type\n"));
248 cbSector = 0; /** @todo we should probably fail the command here already. */
249 }
250
251 if (pbCdb[0] == SCSI_READ_CD)
252 cbXfer = scsiBE2H_U24(pbCdb + 6) * cbSector;
253 else /* SCSI_READ_MSF */
254 {
255 cSectors = scsiMSF2LBA(pbCdb + 6) - scsiMSF2LBA(pbCdb + 3);
256 if (cSectors > 32)
257 cSectors = 32; /* Limit transfer size to 64~74K. Safety first. In any case this can only harm software doing CDDA extraction. */
258 cbXfer = cSectors * cbSector;
259 }
260 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
261 fPassthrough = true;
262 break;
263 }
264 case SCSI_READ_DISC_INFORMATION:
265 cbXfer = scsiBE2H_U16(pbCdb + 7);
266 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
267 fPassthrough = true;
268 break;
269 case SCSI_READ_DVD_STRUCTURE:
270 cbXfer = scsiBE2H_U16(pbCdb + 8);
271 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
272 fPassthrough = true;
273 break;
274 case SCSI_READ_FORMAT_CAPACITIES:
275 cbXfer = scsiBE2H_U16(pbCdb + 7);
276 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
277 fPassthrough = true;
278 break;
279 case SCSI_READ_SUBCHANNEL:
280 cbXfer = scsiBE2H_U16(pbCdb + 7);
281 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
282 fPassthrough = true;
283 break;
284 case SCSI_READ_TOC_PMA_ATIP:
285 cbXfer = scsiBE2H_U16(pbCdb + 7);
286 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
287 fPassthrough = true;
288 break;
289 case SCSI_READ_TRACK_INFORMATION:
290 cbXfer = scsiBE2H_U16(pbCdb + 7);
291 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
292 fPassthrough = true;
293 break;
294 case SCSI_REPORT_KEY:
295 cbXfer = scsiBE2H_U16(pbCdb + 8);
296 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
297 fPassthrough = true;
298 break;
299 case SCSI_REQUEST_SENSE:
300 cbXfer = pbCdb[4];
301 if ((pThis->abATAPISense[2] & 0x0f) != SCSI_SENSE_NONE)
302 {
303 /* Copy sense data over. */
304 void *pvBuf = NULL;
305 int rc = drvHostBaseBufferRetain(&pThis->Core, pReq, cbBuf, false /*fWrite*/, &pvBuf);
306 if (RT_SUCCESS(rc))
307 {
308 memcpy(pvBuf, &pThis->abATAPISense[0], RT_MIN(sizeof(pThis->abATAPISense), cbBuf));
309 rc = drvHostBaseBufferRelease(&pThis->Core, pReq, cbBuf, false /* fWrite */, pvBuf);
310 AssertRC(rc);
311 drvHostDvdCmdOK(pThis);
312 *pu8ScsiSts = SCSI_STATUS_OK;
313 }
314 break;
315 }
316 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
317 fPassthrough = true;
318 break;
319 case SCSI_SEND_CUE_SHEET:
320 cbXfer = scsiBE2H_U24(pbCdb + 6);
321 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
322 fPassthrough = true;
323 break;
324 case SCSI_SEND_DVD_STRUCTURE:
325 cbXfer = scsiBE2H_U16(pbCdb + 8);
326 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
327 fPassthrough = true;
328 break;
329 case SCSI_SEND_EVENT:
330 cbXfer = scsiBE2H_U16(pbCdb + 8);
331 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
332 fPassthrough = true;
333 break;
334 case SCSI_SEND_KEY:
335 cbXfer = scsiBE2H_U16(pbCdb + 8);
336 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
337 fPassthrough = true;
338 break;
339 case SCSI_SEND_OPC_INFORMATION:
340 cbXfer = scsiBE2H_U16(pbCdb + 7);
341 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
342 fPassthrough = true;
343 break;
344 case SCSI_SET_STREAMING:
345 cbXfer = scsiBE2H_U16(pbCdb + 9);
346 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
347 fPassthrough = true;
348 break;
349 case SCSI_WRITE_10:
350 case SCSI_WRITE_AND_VERIFY_10:
351 uLba = scsiBE2H_U32(pbCdb + 2);
352 cSectors = scsiBE2H_U16(pbCdb + 7);
353 if (pThis->pTrackList)
354 cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pThis->pTrackList, uLba);
355 else
356 cbSector = 2048;
357 cbXfer = cSectors * cbSector;
358 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
359 fPassthrough = true;
360 break;
361 case SCSI_WRITE_12:
362 uLba = scsiBE2H_U32(pbCdb + 2);
363 cSectors = scsiBE2H_U32(pbCdb + 6);
364 if (pThis->pTrackList)
365 cbSector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pThis->pTrackList, uLba);
366 else
367 cbSector = 2048;
368 cbXfer = cSectors * cbSector;
369 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
370 fPassthrough = true;
371 break;
372 case SCSI_WRITE_BUFFER:
373 switch (pbCdb[1] & 0x1f)
374 {
375 case 0x04: /* download microcode */
376 case 0x05: /* download microcode and save */
377 case 0x06: /* download microcode with offsets */
378 case 0x07: /* download microcode with offsets and save */
379 case 0x0e: /* download microcode with offsets and defer activation */
380 case 0x0f: /* activate deferred microcode */
381 LogRel(("HostDVD#%u: CD-ROM passthrough command attempted to update firmware, blocked\n", pThis->Core.pDrvIns->iInstance));
382 *pu8ScsiSts = drvHostDvdCmdErrorSimple(pThis, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
383 break;
384 default:
385 cbXfer = scsiBE2H_U16(pbCdb + 6);
386 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
387 fPassthrough = true;
388 break;
389 }
390 break;
391 case SCSI_REPORT_LUNS: /* Not part of MMC-3, but used by Windows. */
392 cbXfer = scsiBE2H_U32(pbCdb + 6);
393 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
394 fPassthrough = true;
395 break;
396 case SCSI_REZERO_UNIT:
397 /* Obsolete command used by cdrecord. What else would one expect?
398 * This command is not sent to the drive, it is handled internally,
399 * as the Linux kernel doesn't like it (message "scsi: unknown
400 * opcode 0x01" in syslog) and replies with a sense code of 0,
401 * which sends cdrecord to an endless loop. */
402 *pu8ScsiSts = drvHostDvdCmdErrorSimple(pThis, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
403 break;
404 default:
405 LogRel(("HostDVD#%u: Passthrough unimplemented for command %#x\n", pThis->Core.pDrvIns->iInstance, pbCdb[0]));
406 *pu8ScsiSts = drvHostDvdCmdErrorSimple(pThis, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
407 break;
408 }
409
410 if (fPassthrough)
411 {
412 *penmTxDir = enmTxDir;
413 *pcbXfer = cbXfer;
414 *pcbSector = cbSector;
415 }
416
417 return fPassthrough;
418}
419
420/**
421 * Locks or unlocks the drive.
422 *
423 * @returns VBox status code.
424 * @param pThis The instance data.
425 * @param fLock True if the request is to lock the drive, false if to unlock.
426 */
427static DECLCALLBACK(int) drvHostDvdDoLock(PDRVHOSTBASE pThis, bool fLock)
428{
429 int rc = drvHostBaseDoLockOs(pThis, fLock);
430
431 LogFlow(("drvHostDvdDoLock(, fLock=%RTbool): returns %Rrc\n", fLock, rc));
432 return rc;
433}
434
435
436/** @interface_method_impl{PDMIMEDIA,pfnSendCmd} */
437static DECLCALLBACK(int) drvHostDvdSendCmd(PPDMIMEDIA pInterface, const uint8_t *pbCdb, size_t cbCdb,
438 PDMMEDIATXDIR enmTxDir, void *pvBuf, uint32_t *pcbBuf,
439 uint8_t *pabSense, size_t cbSense, uint32_t cTimeoutMillies)
440{
441 PDRVHOSTBASE pThis = RT_FROM_MEMBER(pInterface, DRVHOSTBASE, IMedia);
442 int rc;
443 LogFlow(("%s: cmd[0]=%#04x txdir=%d pcbBuf=%d timeout=%d\n", __FUNCTION__, pbCdb[0], enmTxDir, *pcbBuf, cTimeoutMillies));
444
445 RTCritSectEnter(&pThis->CritSect);
446 /* Pass the request on to the internal scsi command interface. */
447 if (enmTxDir == PDMMEDIATXDIR_FROM_DEVICE)
448 memset(pvBuf, '\0', *pcbBuf); /* we got read size, but zero it anyway. */
449 rc = drvHostBaseScsiCmdOs(pThis, pbCdb, cbCdb, enmTxDir, pvBuf, pcbBuf, pabSense, cbSense, cTimeoutMillies);
450 if (rc == VERR_UNRESOLVED_ERROR)
451 /* sense information set */
452 rc = VERR_DEV_IO_ERROR;
453
454 if (pbCdb[0] == SCSI_GET_EVENT_STATUS_NOTIFICATION)
455 {
456 uint8_t *pbBuf = (uint8_t*)pvBuf;
457 Log2(("Event Status Notification class=%#02x supported classes=%#02x\n", pbBuf[2], pbBuf[3]));
458 if (RT_BE2H_U16(*(uint16_t*)pbBuf) >= 6)
459 Log2((" event %#02x %#02x %#02x %#02x\n", pbBuf[4], pbBuf[5], pbBuf[6], pbBuf[7]));
460 }
461 RTCritSectLeave(&pThis->CritSect);
462
463 LogFlow(("%s: rc=%Rrc\n", __FUNCTION__, rc));
464 return rc;
465}
466
467
468/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqSendScsiCmd} */
469static DECLCALLBACK(int) drvHostDvdIoReqSendScsiCmd(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint32_t uLun,
470 const uint8_t *pbCdb, size_t cbCdb, PDMMEDIAEXIOREQSCSITXDIR enmTxDir,
471 size_t cbBuf, uint8_t *pabSense, size_t cbSense, uint8_t *pu8ScsiSts,
472 uint32_t cTimeoutMillies)
473{
474 RT_NOREF3(uLun, cTimeoutMillies, enmTxDir);
475
476 PDRVHOSTDVD pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDVD, Core.IMediaEx);
477 PDRVHOSTBASEREQ pReq = (PDRVHOSTBASEREQ)hIoReq;
478 int rc = VINF_SUCCESS;
479
480 LogFlow(("%s: pbCdb[0]=%#04x{%s} enmTxDir=%d cbBuf=%zu timeout=%u\n",
481 __FUNCTION__, pbCdb[0], SCSICmdText(pbCdb[0]), enmTxDir, cbBuf, cTimeoutMillies));
482
483 RTCritSectEnter(&pThis->Core.CritSect);
484
485 /*
486 * Parse the command first to fend off any illegal or dangerous commands we don't want the guest
487 * to execute on the host drive.
488 */
489 PDMMEDIATXDIR enmXferDir = PDMMEDIATXDIR_NONE;
490 size_t cbXfer = 0;
491 size_t cbSector = 0;
492 size_t cbScsiCmdBufLimit = drvHostBaseScsiCmdGetBufLimitOs(&pThis->Core);
493 bool fPassthrough = drvHostDvdParseCdb(pThis, pReq, pbCdb, cbCdb, cbBuf,
494 &enmXferDir, &cbXfer, &cbSector, pu8ScsiSts);
495 if (fPassthrough)
496 {
497 void *pvBuf = NULL;
498 size_t cbXferCur = cbXfer;
499
500 pReq->cbReq = cbXfer;
501 pReq->cbResidual = cbXfer;
502
503 if (cbXfer)
504 rc = drvHostBaseBufferRetain(&pThis->Core, pReq, cbXfer, enmXferDir == PDMMEDIATXDIR_TO_DEVICE, &pvBuf);
505
506 if (cbXfer > cbScsiCmdBufLimit)
507 {
508 /* Linux accepts commands with up to 100KB of data, but expects
509 * us to handle commands with up to 128KB of data. The usual
510 * imbalance of powers. */
511 uint8_t aATAPICmd[ATAPI_PACKET_SIZE];
512 uint32_t iATAPILBA, cSectors;
513 uint8_t *pbBuf = (uint8_t *)pvBuf;
514
515 switch (pbCdb[0])
516 {
517 case SCSI_READ_10:
518 case SCSI_WRITE_10:
519 case SCSI_WRITE_AND_VERIFY_10:
520 iATAPILBA = scsiBE2H_U32(pbCdb + 2);
521 cSectors = scsiBE2H_U16(pbCdb + 7);
522 break;
523 case SCSI_READ_12:
524 case SCSI_WRITE_12:
525 iATAPILBA = scsiBE2H_U32(pbCdb + 2);
526 cSectors = scsiBE2H_U32(pbCdb + 6);
527 break;
528 case SCSI_READ_CD:
529 iATAPILBA = scsiBE2H_U32(pbCdb + 2);
530 cSectors = scsiBE2H_U24(pbCdb + 6);
531 break;
532 case SCSI_READ_CD_MSF:
533 iATAPILBA = scsiMSF2LBA(pbCdb + 3);
534 cSectors = scsiMSF2LBA(pbCdb + 6) - iATAPILBA;
535 break;
536 default:
537 AssertMsgFailed(("Don't know how to split command %#04x\n", pbCdb[0]));
538 LogRelMax(10, ("HostDVD#%u: CD-ROM passthrough split error\n", pThis->Core.pDrvIns->iInstance));
539 *pu8ScsiSts = drvHostDvdCmdErrorSimple(pThis, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
540 rc = drvHostBaseBufferRelease(&pThis->Core, pReq, cbBuf, enmXferDir == PDMMEDIATXDIR_TO_DEVICE, pvBuf);
541 RTCritSectLeave(&pThis->Core.CritSect);
542 return VINF_SUCCESS;
543 }
544 memcpy(aATAPICmd, pbCdb, RT_MIN(cbCdb, ATAPI_PACKET_SIZE));
545 uint32_t cReqSectors = 0;
546 for (uint32_t i = cSectors; i > 0; i -= cReqSectors)
547 {
548 if (i * cbSector > cbScsiCmdBufLimit)
549 cReqSectors = (uint32_t)(cbScsiCmdBufLimit / cbSector);
550 else
551 cReqSectors = i;
552 uint32_t cbCurrTX = (uint32_t)cbSector * cReqSectors;
553 switch (pbCdb[0])
554 {
555 case SCSI_READ_10:
556 case SCSI_WRITE_10:
557 case SCSI_WRITE_AND_VERIFY_10:
558 scsiH2BE_U32(aATAPICmd + 2, iATAPILBA);
559 scsiH2BE_U16(aATAPICmd + 7, cReqSectors);
560 break;
561 case SCSI_READ_12:
562 case SCSI_WRITE_12:
563 scsiH2BE_U32(aATAPICmd + 2, iATAPILBA);
564 scsiH2BE_U32(aATAPICmd + 6, cReqSectors);
565 break;
566 case SCSI_READ_CD:
567 scsiH2BE_U32(aATAPICmd + 2, iATAPILBA);
568 scsiH2BE_U24(aATAPICmd + 6, cReqSectors);
569 break;
570 case SCSI_READ_CD_MSF:
571 scsiLBA2MSF(aATAPICmd + 3, iATAPILBA);
572 scsiLBA2MSF(aATAPICmd + 6, iATAPILBA + cReqSectors);
573 break;
574 }
575 rc = drvHostBaseScsiCmdOs(&pThis->Core, aATAPICmd, sizeof(aATAPICmd),
576 enmXferDir, pbBuf, &cbCurrTX,
577 &pThis->abATAPISense[0], sizeof(pThis->abATAPISense),
578 cTimeoutMillies /**< @todo timeout */);
579 if (rc != VINF_SUCCESS)
580 break;
581
582 pReq->cbResidual -= cbCurrTX;
583 iATAPILBA += cReqSectors;
584 pbBuf += cbSector * cReqSectors;
585 }
586 }
587 else
588 {
589 uint32_t cbXferTmp = (uint32_t)cbXferCur;
590 rc = drvHostBaseScsiCmdOs(&pThis->Core, pbCdb, cbCdb, enmXferDir, pvBuf, &cbXferTmp,
591 &pThis->abATAPISense[0], sizeof(pThis->abATAPISense), cTimeoutMillies);
592 if (RT_SUCCESS(rc))
593 pReq->cbResidual -= cbXferTmp;
594 }
595
596 if (RT_SUCCESS(rc))
597 {
598 /* Do post processing for certain commands. */
599 switch (pbCdb[0])
600 {
601 case SCSI_SEND_CUE_SHEET:
602 case SCSI_READ_TOC_PMA_ATIP:
603 {
604 if (!pThis->pTrackList)
605 rc = ATAPIPassthroughTrackListCreateEmpty(&pThis->pTrackList);
606
607 if (RT_SUCCESS(rc))
608 rc = ATAPIPassthroughTrackListUpdate(pThis->pTrackList, pbCdb, pvBuf);
609
610 if (RT_FAILURE(rc))
611 LogRelMax(10, ("HostDVD#%u: Error (%Rrc) while updating the tracklist during %s, burning the disc might fail\n",
612 pThis->Core.pDrvIns->iInstance, rc,
613 pbCdb[0] == SCSI_SEND_CUE_SHEET ? "SEND CUE SHEET" : "READ TOC/PMA/ATIP"));
614 break;
615 }
616 case SCSI_SYNCHRONIZE_CACHE:
617 {
618 if (pThis->pTrackList)
619 ATAPIPassthroughTrackListClear(pThis->pTrackList);
620 break;
621 }
622 }
623
624 if (enmXferDir == PDMMEDIATXDIR_FROM_DEVICE)
625 {
626 Assert(cbXferCur <= cbXfer);
627
628 if (pbCdb[0] == SCSI_INQUIRY)
629 {
630 /* Make sure that the real drive cannot be identified.
631 * Motivation: changing the VM configuration should be as
632 * invisible as possible to the guest. */
633 if (cbXferCur >= 8 + 8)
634 scsiPadStr((uint8_t *)pvBuf + 8, "VBOX", 8);
635 if (cbXferCur >= 16 + 16)
636 scsiPadStr((uint8_t *)pvBuf + 16, "CD-ROM", 16);
637 if (cbXferCur >= 32 + 4)
638 scsiPadStr((uint8_t *)pvBuf + 32, "1.0", 4);
639 }
640
641 if (cbXferCur)
642 Log3(("ATAPI PT data read (%d): %.*Rhxs\n", cbXferCur, cbXferCur, (uint8_t *)pvBuf));
643 }
644
645 *pu8ScsiSts = drvHostDvdCmdOK(pThis);
646 }
647 else
648 {
649 do
650 {
651 /* don't log superfluous errors */
652 if ( rc == VERR_DEV_IO_ERROR
653 && ( pbCdb[0] == SCSI_TEST_UNIT_READY
654 || pbCdb[0] == SCSI_READ_CAPACITY
655 || pbCdb[0] == SCSI_READ_DVD_STRUCTURE
656 || pbCdb[0] == SCSI_READ_TOC_PMA_ATIP))
657 break;
658 LogRelMax(10, ("HostDVD#%u: CD-ROM passthrough cmd=%#04x sense=%d ASC=%#02x ASCQ=%#02x %Rrc\n",
659 pThis->Core.pDrvIns->iInstance, pbCdb[0], pThis->abATAPISense[2] & 0x0f,
660 pThis->abATAPISense[12], pThis->abATAPISense[13], rc));
661 } while (0);
662 *pu8ScsiSts = SCSI_STATUS_CHECK_CONDITION;
663 rc = VINF_SUCCESS;
664 }
665
666 if (cbXfer)
667 rc = drvHostBaseBufferRelease(&pThis->Core, pReq, cbXfer, enmXferDir == PDMMEDIATXDIR_TO_DEVICE, pvBuf);
668 }
669
670 /*
671 * We handled the command, check the status code and copy over the sense data if
672 * it is CHECK CONDITION.
673 */
674 if ( *pu8ScsiSts == SCSI_STATUS_CHECK_CONDITION
675 && VALID_PTR(pabSense)
676 && cbSense > 0)
677 memcpy(pabSense, &pThis->abATAPISense[0], RT_MIN(cbSense, sizeof(pThis->abATAPISense)));
678
679 RTCritSectLeave(&pThis->Core.CritSect);
680
681 LogFlow(("%s: rc=%Rrc\n", __FUNCTION__, rc));
682 return rc;
683}
684
685
686/* -=-=-=-=- driver interface -=-=-=-=- */
687
688
689/** @interface_method_impl{PDMDRVREG,pfnDestruct} */
690static DECLCALLBACK(void) drvHostDvdDestruct(PPDMDRVINS pDrvIns)
691{
692 PDRVHOSTDVD pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDVD);
693
694 if (pThis->pTrackList)
695 {
696 ATAPIPassthroughTrackListDestroy(pThis->pTrackList);
697 pThis->pTrackList = NULL;
698 }
699
700 DRVHostBaseDestruct(pDrvIns);
701}
702
703/**
704 * Construct a host dvd drive driver instance.
705 *
706 * @copydoc FNPDMDRVCONSTRUCT
707 */
708static DECLCALLBACK(int) drvHostDvdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
709{
710 RT_NOREF(fFlags);
711 PDRVHOSTDVD pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDVD);
712 LogFlow(("drvHostDvdConstruct: iInstance=%d\n", pDrvIns->iInstance));
713
714 bool fPassthrough;
715 int rc = CFGMR3QueryBool(pCfg, "Passthrough", &fPassthrough);
716 if (RT_SUCCESS(rc) && fPassthrough)
717 {
718 pThis->Core.IMedia.pfnSendCmd = drvHostDvdSendCmd;
719 pThis->Core.IMediaEx.pfnIoReqSendScsiCmd = drvHostDvdIoReqSendScsiCmd;
720 /* Passthrough requires opening the device in R/W mode. */
721 pThis->Core.fReadOnlyConfig = false;
722 }
723
724 pThis->Core.pfnDoLock = drvHostDvdDoLock;
725
726 /*
727 * Init instance data.
728 */
729 rc = DRVHostBaseInit(pDrvIns, pCfg, "Path\0Interval\0Locked\0BIOSVisible\0AttachFailError\0Passthrough\0",
730 PDMMEDIATYPE_DVD);
731 LogFlow(("drvHostDvdConstruct: returns %Rrc\n", rc));
732 return rc;
733}
734
735
736/**
737 * Block driver registration record.
738 */
739const PDMDRVREG g_DrvHostDVD =
740{
741 /* u32Version */
742 PDM_DRVREG_VERSION,
743 /* szName */
744 "HostDVD",
745 /* szRCMod */
746 "",
747 /* szR0Mod */
748 "",
749 /* pszDescription */
750 "Host DVD Block Driver.",
751 /* fFlags */
752 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
753 /* fClass. */
754 PDM_DRVREG_CLASS_BLOCK,
755 /* cMaxInstances */
756 ~0U,
757 /* cbInstance */
758 sizeof(DRVHOSTDVD),
759 /* pfnConstruct */
760 drvHostDvdConstruct,
761 /* pfnDestruct */
762 drvHostDvdDestruct,
763 /* pfnRelocate */
764 NULL,
765 /* pfnIOCtl */
766 NULL,
767 /* pfnPowerOn */
768 NULL,
769 /* pfnReset */
770 NULL,
771 /* pfnSuspend */
772 NULL,
773 /* pfnResume */
774 NULL,
775 /* pfnAttach */
776 NULL,
777 /* pfnDetach */
778 NULL,
779 /* pfnPowerOff */
780 NULL,
781 /* pfnSoftReset */
782 NULL,
783 /* u32EndVersion */
784 PDM_DRVREG_VERSION
785};
786
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