VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvSCSI.cpp@ 78787

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

DrvSCSI.cpp: Quick crash prevention for some other issue.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.3 KB
Line 
1/* $Id: DrvSCSI.cpp 78787 2019-05-27 17:08:54Z vboxsync $ */
2/** @file
3 * VBox storage drivers: Generic SCSI command parser and execution driver
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22//#define DEBUG
23#define LOG_GROUP LOG_GROUP_DRV_SCSI
24#include <VBox/vmm/pdmdrv.h>
25#include <VBox/vmm/pdmifs.h>
26#include <VBox/vmm/pdmqueue.h>
27#include <VBox/vmm/pdmstorageifs.h>
28#include <VBox/vmm/pdmthread.h>
29#include <VBox/vscsi.h>
30#include <VBox/scsi.h>
31#include <iprt/asm.h>
32#include <iprt/assert.h>
33#include <iprt/mem.h>
34#include <iprt/req.h>
35#include <iprt/semaphore.h>
36#include <iprt/string.h>
37#include <iprt/uuid.h>
38
39#include "VBoxDD.h"
40
41/** The maximum number of release log entries per device. */
42#define MAX_LOG_REL_ERRORS 1024
43
44/**
45 * Eject state.
46 */
47typedef struct DRVSCSIEJECTSTATE
48{
49 /** The item core for the PDM queue. */
50 PDMQUEUEITEMCORE Core;
51 /** Event semaphore to signal when complete. */
52 RTSEMEVENT hSemEvt;
53 /** Status of the eject operation. */
54 int rcReq;
55} DRVSCSIEJECTSTATE;
56typedef DRVSCSIEJECTSTATE *PDRVSCSIEJECTSTATE;
57
58/**
59 * SCSI driver private per request data.
60 */
61typedef struct DRVSCSIREQ
62{
63 /** Size of the guest buffer. */
64 size_t cbBuf;
65 /** Temporary buffer holding the data. */
66 void *pvBuf;
67 /** Data segment. */
68 RTSGSEG Seg;
69 /** Transfer direction. */
70 PDMMEDIAEXIOREQSCSITXDIR enmXferDir;
71 /** The VSCSI request handle. */
72 VSCSIREQ hVScsiReq;
73 /** Where to store the SCSI status code. */
74 uint8_t *pu8ScsiSts;
75 /** Transfer size determined by the VSCSI layer. */
76 size_t cbXfer;
77 /** Start of the request data for the device above us. */
78 uint8_t abAlloc[1];
79} DRVSCSIREQ;
80/** Pointer to the driver private per request data. */
81typedef DRVSCSIREQ *PDRVSCSIREQ;
82
83/**
84 * SCSI driver instance data.
85 *
86 * @implements PDMIMEDIAEXPORT
87 * @implements PDMIMEDIAEX
88 * @implements PDMIMOUNTNOTIFY
89 */
90typedef struct DRVSCSI
91{
92 /** Pointer driver instance. */
93 PPDMDRVINS pDrvIns;
94
95 /** Pointer to the attached driver's base interface. */
96 PPDMIBASE pDrvBase;
97 /** Pointer to the attached driver's block interface. */
98 PPDMIMEDIA pDrvMedia;
99 /** Pointer to the attached driver's extended media interface. */
100 PPDMIMEDIAEX pDrvMediaEx;
101 /** Pointer to the attached driver's mount interface. */
102 PPDMIMOUNT pDrvMount;
103 /** Pointer to the extended media port interface of the device above. */
104 PPDMIMEDIAEXPORT pDevMediaExPort;
105 /** Pointer to the media port interface of the device above. */
106 PPDMIMEDIAPORT pDevMediaPort;
107 /** pointer to the Led port interface of the dveice above. */
108 PPDMILEDPORTS pLedPort;
109 /** The media interface for the device above. */
110 PDMIMEDIA IMedia;
111 /** The extended media interface for the device above. */
112 PDMIMEDIAEX IMediaEx;
113 /** The media port interface. */
114 PDMIMEDIAPORT IPort;
115 /** The optional extended media port interface. */
116 PDMIMEDIAEXPORT IPortEx;
117 /** The mount notify interface. */
118 PDMIMOUNTNOTIFY IMountNotify;
119 /** Fallback status LED state for this drive.
120 * This is used in case the device doesn't has a LED interface. */
121 PDMLED Led;
122 /** Pointer to the status LED for this drive. */
123 PPDMLED pLed;
124
125 /** VSCSI device handle. */
126 VSCSIDEVICE hVScsiDevice;
127 /** VSCSI LUN handle. */
128 VSCSILUN hVScsiLun;
129 /** I/O callbacks. */
130 VSCSILUNIOCALLBACKS VScsiIoCallbacks;
131
132 /** Indicates whether PDMDrvHlpAsyncNotificationCompleted should be called by
133 * any of the dummy functions. */
134 bool volatile fDummySignal;
135 /** Current I/O depth. */
136 volatile uint32_t StatIoDepth;
137 /** Errors printed in the release log. */
138 unsigned cErrors;
139
140 /** Size of the I/O request to allocate. */
141 size_t cbIoReqAlloc;
142 /** Size of a VSCSI I/O request. */
143 size_t cbVScsiIoReqAlloc;
144 /** Queue to defer unmounting to EMT. */
145 PPDMQUEUE pQueue;
146} DRVSCSI, *PDRVSCSI;
147
148/** Convert a VSCSI I/O request handle to the associated PDMIMEDIAEX I/O request. */
149#define DRVSCSI_VSCSIIOREQ_2_PDMMEDIAEXIOREQ(a_hVScsiIoReq) (*(PPDMMEDIAEXIOREQ)((uint8_t *)(a_hVScsiIoReq) - sizeof(PDMMEDIAEXIOREQ)))
150/** Convert a PDMIMEDIAEX I/O additional request memory to a VSCSI I/O request. */
151#define DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(a_pvIoReqAlloc) ((VSCSIIOREQ)((uint8_t *)(a_pvIoReqAlloc) + sizeof(PDMMEDIAEXIOREQ)))
152
153/**
154 * Returns whether the given status code indicates a non fatal error.
155 *
156 * @returns True if the error can be fixed by the user after the VM was suspended.
157 * False if not and the error should be reported to the guest.
158 * @param rc The status code to check.
159 */
160DECLINLINE(bool) drvscsiIsRedoPossible(int rc)
161{
162 if ( rc == VERR_DISK_FULL
163 || rc == VERR_FILE_TOO_BIG
164 || rc == VERR_BROKEN_PIPE
165 || rc == VERR_NET_CONNECTION_REFUSED
166 || rc == VERR_VD_DEK_MISSING)
167 return true;
168
169 return false;
170}
171
172/* -=-=-=-=- VScsiIoCallbacks -=-=-=-=- */
173
174/**
175 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqAllocSizeSet}
176 */
177static DECLCALLBACK(int) drvscsiReqAllocSizeSet(VSCSILUN hVScsiLun, void *pvScsiLunUser, size_t cbVScsiIoReqAlloc)
178{
179 RT_NOREF(hVScsiLun);
180 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
181
182 /* We need to store the I/O request handle so we can get it when VSCSI queues an I/O request. */
183 int rc = pThis->pDrvMediaEx->pfnIoReqAllocSizeSet(pThis->pDrvMediaEx, cbVScsiIoReqAlloc + sizeof(PDMMEDIAEXIOREQ));
184 if (RT_SUCCESS(rc))
185 pThis->cbVScsiIoReqAlloc = cbVScsiIoReqAlloc + sizeof(PDMMEDIAEXIOREQ);
186
187 return rc;
188}
189
190/**
191 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqAlloc}
192 */
193static DECLCALLBACK(int) drvscsiReqAlloc(VSCSILUN hVScsiLun, void *pvScsiLunUser,
194 uint64_t u64Tag, PVSCSIIOREQ phVScsiIoReq)
195{
196 RT_NOREF(hVScsiLun);
197 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
198 PDMMEDIAEXIOREQ hIoReq;
199 void *pvIoReqAlloc;
200 int rc = pThis->pDrvMediaEx->pfnIoReqAlloc(pThis->pDrvMediaEx, &hIoReq, &pvIoReqAlloc, u64Tag,
201 PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR);
202 if (RT_SUCCESS(rc))
203 {
204 PPDMMEDIAEXIOREQ phIoReq = (PPDMMEDIAEXIOREQ)pvIoReqAlloc;
205
206 *phIoReq = hIoReq;
207 *phVScsiIoReq = (VSCSIIOREQ)(phIoReq + 1);
208 }
209
210 return rc;
211}
212
213/**
214 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqFree}
215 */
216static DECLCALLBACK(int) drvscsiReqFree(VSCSILUN hVScsiLun, void *pvScsiLunUser, VSCSIIOREQ hVScsiIoReq)
217{
218 RT_NOREF(hVScsiLun);
219 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
220 PDMMEDIAEXIOREQ hIoReq = DRVSCSI_VSCSIIOREQ_2_PDMMEDIAEXIOREQ(hVScsiIoReq);
221
222 return pThis->pDrvMediaEx->pfnIoReqFree(pThis->pDrvMediaEx, hIoReq);
223}
224
225/**
226 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumGetRegionCount}
227 */
228static DECLCALLBACK(uint32_t) drvscsiGetRegionCount(VSCSILUN hVScsiLun, void *pvScsiLunUser)
229{
230 RT_NOREF(hVScsiLun);
231 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
232
233 return pThis->pDrvMedia->pfnGetRegionCount(pThis->pDrvMedia);
234}
235
236/** @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumQueryRegionProperties} */
237static DECLCALLBACK(int) drvscsiQueryRegionProperties(VSCSILUN hVScsiLun, void *pvScsiLunUser,
238 uint32_t uRegion, uint64_t *pu64LbaStart,
239 uint64_t *pcBlocks, uint64_t *pcbBlock,
240 PVDREGIONDATAFORM penmDataForm)
241{
242 RT_NOREF(hVScsiLun);
243 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
244
245 return pThis->pDrvMedia->pfnQueryRegionProperties(pThis->pDrvMedia, uRegion, pu64LbaStart,
246 pcBlocks, pcbBlock, penmDataForm);
247}
248
249/** @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumQueryRegionPropertiesForLba} */
250static DECLCALLBACK(int) drvscsiQueryRegionPropertiesForLba(VSCSILUN hVScsiLun, void *pvScsiLunUser,
251 uint64_t u64LbaStart, uint32_t *puRegion,
252 uint64_t *pcBlocks, uint64_t *pcbBlock,
253 PVDREGIONDATAFORM penmDataForm)
254{
255 RT_NOREF(hVScsiLun);
256 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
257
258 return pThis->pDrvMedia->pfnQueryRegionPropertiesForLba(pThis->pDrvMedia, u64LbaStart, puRegion,
259 pcBlocks, pcbBlock, penmDataForm);
260}
261
262/**
263 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumSetLock}
264 */
265static DECLCALLBACK(int) drvscsiSetLock(VSCSILUN hVScsiLun, void *pvScsiLunUser, bool fLocked)
266{
267 RT_NOREF(hVScsiLun);
268 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
269
270 if (fLocked)
271 pThis->pDrvMount->pfnLock(pThis->pDrvMount);
272 else
273 pThis->pDrvMount->pfnUnlock(pThis->pDrvMount);
274
275 return VINF_SUCCESS;
276}
277
278/** @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumEject} */
279static DECLCALLBACK(int) drvscsiEject(VSCSILUN hVScsiLun, void *pvScsiLunUser)
280{
281 RT_NOREF(hVScsiLun);
282 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
283 int rc = VINF_SUCCESS;
284 RTSEMEVENT hSemEvt = NIL_RTSEMEVENT;
285
286 /* This must be done from EMT. */
287 rc = RTSemEventCreate(&hSemEvt);
288 if (RT_SUCCESS(rc))
289 {
290 PDRVSCSIEJECTSTATE pEjectState = (PDRVSCSIEJECTSTATE)PDMQueueAlloc(pThis->pQueue);
291 if (pEjectState)
292 {
293 pEjectState->hSemEvt = hSemEvt;
294 PDMQueueInsert(pThis->pQueue, &pEjectState->Core);
295
296 /* Wait for completion. */
297 rc = RTSemEventWait(pEjectState->hSemEvt, RT_INDEFINITE_WAIT);
298 if (RT_SUCCESS(rc))
299 rc = pEjectState->rcReq;
300 }
301 else
302 rc = VERR_NO_MEMORY;
303
304 RTSemEventDestroy(pEjectState->hSemEvt);
305 }
306
307 return rc;
308}
309
310/**
311 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqTransferEnqueue}
312 */
313static DECLCALLBACK(int) drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun, void *pvScsiLunUser, VSCSIIOREQ hVScsiIoReq)
314{
315 RT_NOREF(hVScsiLun);
316 int rc = VINF_SUCCESS;
317 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
318 PDMMEDIAEXIOREQ hIoReq = DRVSCSI_VSCSIIOREQ_2_PDMMEDIAEXIOREQ(hVScsiIoReq);
319
320 LogFlowFunc(("Enqueuing hVScsiIoReq=%#p\n", hVScsiIoReq));
321
322 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
323 switch (enmTxDir)
324 {
325 case VSCSIIOREQTXDIR_FLUSH:
326 {
327 rc = pThis->pDrvMediaEx->pfnIoReqFlush(pThis->pDrvMediaEx, hIoReq);
328 if ( RT_FAILURE(rc)
329 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
330 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
331 pThis->pDrvIns->iInstance, rc));
332 break;
333 }
334 case VSCSIIOREQTXDIR_UNMAP:
335 {
336 PCRTRANGE paRanges;
337 unsigned cRanges;
338
339 rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRanges, &cRanges);
340 AssertRC(rc);
341
342 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
343 rc = pThis->pDrvMediaEx->pfnIoReqDiscard(pThis->pDrvMediaEx, hIoReq, cRanges);
344 if ( RT_FAILURE(rc)
345 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
346 LogRel(("SCSI#%u: Discard returned rc=%Rrc\n",
347 pThis->pDrvIns->iInstance, rc));
348 break;
349 }
350 case VSCSIIOREQTXDIR_READ:
351 case VSCSIIOREQTXDIR_WRITE:
352 {
353 uint64_t uOffset = 0;
354 size_t cbTransfer = 0;
355 size_t cbSeg = 0;
356 PCRTSGSEG paSeg = NULL;
357 unsigned cSeg = 0;
358
359 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
360 &cSeg, &cbSeg, &paSeg);
361 AssertRC(rc);
362
363 if (enmTxDir == VSCSIIOREQTXDIR_READ)
364 {
365 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
366 rc = pThis->pDrvMediaEx->pfnIoReqRead(pThis->pDrvMediaEx, hIoReq, uOffset, cbTransfer);
367 }
368 else
369 {
370 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
371 rc = pThis->pDrvMediaEx->pfnIoReqWrite(pThis->pDrvMediaEx, hIoReq, uOffset, cbTransfer);
372 }
373
374 if ( RT_FAILURE(rc)
375 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
376 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
377 pThis->pDrvIns->iInstance,
378 enmTxDir == VSCSIIOREQTXDIR_READ
379 ? "Read"
380 : "Write",
381 uOffset,
382 cbTransfer, rc));
383 break;
384 }
385 default:
386 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
387 }
388
389 if (rc == VINF_SUCCESS)
390 {
391 if (enmTxDir == VSCSIIOREQTXDIR_READ)
392 pThis->pLed->Actual.s.fReading = 0;
393 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
394 pThis->pLed->Actual.s.fWriting = 0;
395 else
396 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
397
398 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS, false);
399 rc = VINF_SUCCESS;
400 }
401 else if (rc == VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
402 rc = VINF_SUCCESS;
403 else if (RT_FAILURE(rc))
404 {
405 if (enmTxDir == VSCSIIOREQTXDIR_READ)
406 pThis->pLed->Actual.s.fReading = 0;
407 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
408 pThis->pLed->Actual.s.fWriting = 0;
409 else
410 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
411
412 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
413 rc = VINF_SUCCESS;
414 }
415 else
416 AssertMsgFailed(("Invalid return code rc=%Rrc\n", rc));
417
418 return rc;
419}
420
421/**
422 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunGetFeatureFlags}
423 */
424static DECLCALLBACK(int) drvscsiGetFeatureFlags(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pfFeatures)
425{
426 RT_NOREF(hVScsiLun);
427 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
428
429 *pfFeatures = 0;
430
431 uint32_t fFeatures = 0;
432 int rc = pThis->pDrvMediaEx->pfnQueryFeatures(pThis->pDrvMediaEx, &fFeatures);
433 if (RT_SUCCESS(rc) && (fFeatures & PDMIMEDIAEX_FEATURE_F_DISCARD))
434 *pfFeatures |= VSCSI_LUN_FEATURE_UNMAP;
435
436 if ( pThis->pDrvMedia
437 && pThis->pDrvMedia->pfnIsNonRotational(pThis->pDrvMedia))
438 *pfFeatures |= VSCSI_LUN_FEATURE_NON_ROTATIONAL;
439
440 if (pThis->pDrvMedia->pfnIsReadOnly(pThis->pDrvMedia))
441 *pfFeatures |= VSCSI_LUN_FEATURE_READONLY;
442
443 return VINF_SUCCESS;
444}
445
446/**
447 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunQueryInqStrings}
448 */
449static DECLCALLBACK(int) drvscsiQueryInqStrings(VSCSILUN hVScsiLun, void *pvScsiLunUser, const char **ppszVendorId,
450 const char **ppszProductId, const char **ppszProductLevel)
451{
452 RT_NOREF(hVScsiLun);
453 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
454
455 if (pThis->pDevMediaPort->pfnQueryScsiInqStrings)
456 return pThis->pDevMediaPort->pfnQueryScsiInqStrings(pThis->pDevMediaPort, ppszVendorId,
457 ppszProductId, ppszProductLevel);
458
459 return VERR_NOT_FOUND;
460}
461
462/* -=-=-=-=- IPortEx -=-=-=-=- */
463
464/**
465 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
466 */
467static DECLCALLBACK(int) drvscsiIoReqCompleteNotify(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
468 void *pvIoReqAlloc, int rcReq)
469{
470 RT_NOREF1(hIoReq);
471
472 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IPortEx);
473 VSCSIIOREQ hVScsiIoReq = DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(pvIoReqAlloc);
474 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
475
476 LogFlowFunc(("Request hVScsiIoReq=%#p completed\n", hVScsiIoReq));
477
478 if (enmTxDir == VSCSIIOREQTXDIR_READ)
479 pThis->pLed->Actual.s.fReading = 0;
480 else if ( enmTxDir == VSCSIIOREQTXDIR_WRITE
481 || enmTxDir == VSCSIIOREQTXDIR_UNMAP)
482 pThis->pLed->Actual.s.fWriting = 0;
483 else
484 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
485
486 if (RT_SUCCESS(rcReq))
487 VSCSIIoReqCompleted(hVScsiIoReq, rcReq, false /* fRedoPossible */);
488 else
489 {
490 pThis->cErrors++;
491 if (pThis->cErrors < MAX_LOG_REL_ERRORS)
492 {
493 if (enmTxDir == VSCSIIOREQTXDIR_FLUSH)
494 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
495 pThis->pDrvIns->iInstance, rcReq));
496 else if (enmTxDir == VSCSIIOREQTXDIR_UNMAP)
497 LogRel(("SCSI#%u: Unmap returned rc=%Rrc\n",
498 pThis->pDrvIns->iInstance, rcReq));
499 else
500 {
501 uint64_t uOffset = 0;
502 size_t cbTransfer = 0;
503 size_t cbSeg = 0;
504 PCRTSGSEG paSeg = NULL;
505 unsigned cSeg = 0;
506
507 VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
508 &cSeg, &cbSeg, &paSeg);
509
510 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
511 pThis->pDrvIns->iInstance,
512 enmTxDir == VSCSIIOREQTXDIR_READ
513 ? "Read"
514 : "Write",
515 uOffset,
516 cbTransfer, rcReq));
517 }
518 }
519
520 VSCSIIoReqCompleted(hVScsiIoReq, rcReq, drvscsiIsRedoPossible(rcReq));
521 }
522
523 return VINF_SUCCESS;
524}
525
526/**
527 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
528 */
529static DECLCALLBACK(int) drvscsiIoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
530 void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf,
531 size_t cbCopy)
532{
533 RT_NOREF2(pInterface, hIoReq);
534
535 VSCSIIOREQ hVScsiIoReq = DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(pvIoReqAlloc);
536 uint64_t uOffset = 0;
537 size_t cbTransfer = 0;
538 size_t cbSeg = 0;
539 PCRTSGSEG paSeg = NULL;
540 unsigned cSeg = 0;
541 size_t cbCopied = 0;
542
543 int rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg, &paSeg);
544 if (RT_SUCCESS(rc))
545 {
546 RTSGBUF SgBuf;
547 RTSgBufInit(&SgBuf, paSeg, cSeg);
548
549 RTSgBufAdvance(&SgBuf, offDst);
550 cbCopied = RTSgBufCopy(&SgBuf, pSgBuf, cbCopy);
551 }
552
553 return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_OVERFLOW;
554}
555
556/**
557 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
558 */
559static DECLCALLBACK(int) drvscsiIoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
560 void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf,
561 size_t cbCopy)
562{
563 RT_NOREF2(pInterface, hIoReq);
564
565 VSCSIIOREQ hVScsiIoReq = DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(pvIoReqAlloc);
566 uint64_t uOffset = 0;
567 size_t cbTransfer = 0;
568 size_t cbSeg = 0;
569 PCRTSGSEG paSeg = NULL;
570 unsigned cSeg = 0;
571 size_t cbCopied = 0;
572
573 int rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg, &paSeg);
574 if (RT_SUCCESS(rc))
575 {
576 RTSGBUF SgBuf;
577 RTSgBufInit(&SgBuf, paSeg, cSeg);
578
579 RTSgBufAdvance(&SgBuf, offSrc);
580 cbCopied = RTSgBufCopy(pSgBuf, &SgBuf, cbCopy);
581 }
582
583 return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_UNDERRUN;
584}
585
586/**
587 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqQueryDiscardRanges}
588 */
589static DECLCALLBACK(int) drvscsiIoReqQueryDiscardRanges(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
590 void *pvIoReqAlloc, uint32_t idxRangeStart,
591 uint32_t cRanges, PRTRANGE paRanges,
592 uint32_t *pcRanges)
593{
594 RT_NOREF2(pInterface, hIoReq);
595
596 VSCSIIOREQ hVScsiIoReq = DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(pvIoReqAlloc);
597 PCRTRANGE paRangesVScsi;
598 unsigned cRangesVScsi;
599
600 int rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRangesVScsi, &cRangesVScsi);
601 if (RT_SUCCESS(rc))
602 {
603 uint32_t cRangesCopy = RT_MIN(cRangesVScsi - idxRangeStart, cRanges);
604 Assert( idxRangeStart < cRangesVScsi
605 && (idxRangeStart + cRanges) <= cRangesVScsi);
606
607 memcpy(paRanges, &paRangesVScsi[idxRangeStart], cRangesCopy * sizeof(RTRANGE));
608 *pcRanges = cRangesCopy;
609 }
610 return rc;
611}
612
613/**
614 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
615 */
616static DECLCALLBACK(void) drvscsiIoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
617 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
618{
619 RT_NOREF2(hIoReq, pvIoReqAlloc);
620 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IPortEx);
621
622 switch (enmState)
623 {
624 case PDMMEDIAEXIOREQSTATE_SUSPENDED:
625 {
626 /* Make sure the request is not accounted for so the VM can suspend successfully. */
627 uint32_t cTasksActive = ASMAtomicDecU32(&pThis->StatIoDepth);
628 if (!cTasksActive && pThis->fDummySignal)
629 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
630 break;
631 }
632 case PDMMEDIAEXIOREQSTATE_ACTIVE:
633 /* Make sure the request is accounted for so the VM suspends only when the request is complete. */
634 ASMAtomicIncU32(&pThis->StatIoDepth);
635 break;
636 default:
637 AssertMsgFailed(("Invalid request state given %u\n", enmState));
638 }
639
640 pThis->pDevMediaExPort->pfnIoReqStateChanged(pThis->pDevMediaExPort, hIoReq, pvIoReqAlloc, enmState);
641}
642
643
644/* -=-=-=-=- IMedia -=-=-=-=- */
645
646/** @interface_method_impl{PDMIMEDIA,pfnGetSize} */
647static DECLCALLBACK(uint64_t) drvscsiGetSize(PPDMIMEDIA pInterface)
648{
649 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
650 return pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
651}
652
653/** @interface_method_impl{PDMIMEDIA,pfnGetSectorSize} */
654static DECLCALLBACK(uint32_t) drvscsiGetSectorSize(PPDMIMEDIA pInterface)
655{
656 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
657 return pThis->pDrvMedia->pfnGetSectorSize(pThis->pDrvMedia);
658}
659
660/** @interface_method_impl{PDMIMEDIA,pfnIsReadOnly} */
661static DECLCALLBACK(bool) drvscsiIsReadOnly(PPDMIMEDIA pInterface)
662{
663 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
664 return pThis->pDrvMedia->pfnIsReadOnly(pThis->pDrvMedia);
665}
666
667/** @interface_method_impl{PDMIMEDIA,pfnIsNonRotational} */
668static DECLCALLBACK(bool) drvscsiIsNonRotational(PPDMIMEDIA pInterface)
669{
670 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
671 return pThis->pDrvMedia->pfnIsNonRotational(pThis->pDrvMedia);
672}
673
674/** @interface_method_impl{PDMIMEDIA,pfnBiosGetPCHSGeometry} */
675static DECLCALLBACK(int) drvscsiBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
676 PPDMMEDIAGEOMETRY pPCHSGeometry)
677{
678 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
679 return pThis->pDrvMedia->pfnBiosGetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
680}
681
682/** @interface_method_impl{PDMIMEDIA,pfnBiosSetPCHSGeometry} */
683static DECLCALLBACK(int) drvscsiBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
684 PCPDMMEDIAGEOMETRY pPCHSGeometry)
685{
686 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
687 return pThis->pDrvMedia->pfnBiosSetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
688}
689
690/** @interface_method_impl{PDMIMEDIA,pfnBiosGetLCHSGeometry} */
691static DECLCALLBACK(int) drvscsiBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
692 PPDMMEDIAGEOMETRY pLCHSGeometry)
693{
694 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
695 return pThis->pDrvMedia->pfnBiosGetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
696}
697
698/** @interface_method_impl{PDMIMEDIA,pfnBiosSetLCHSGeometry} */
699static DECLCALLBACK(int) drvscsiBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
700 PCPDMMEDIAGEOMETRY pLCHSGeometry)
701{
702 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
703 return pThis->pDrvMedia->pfnBiosSetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
704}
705
706/** @interface_method_impl{PDMIMEDIA,pfnBiosIsVisible} */
707static DECLCALLBACK(bool) drvscsiBiosIsVisible(PPDMIMEDIA pInterface)
708{
709 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
710 return pThis->pDrvMedia->pfnBiosIsVisible(pThis->pDrvMedia);
711}
712
713/** @interface_method_impl{PDMIMEDIA,pfnGetType} */
714static DECLCALLBACK(PDMMEDIATYPE) drvscsiGetType(PPDMIMEDIA pInterface)
715{
716 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
717 VSCSILUNTYPE enmLunType;
718 PDMMEDIATYPE enmMediaType = PDMMEDIATYPE_ERROR;
719
720 int rc = VSCSIDeviceLunQueryType(pThis->hVScsiDevice, 0, &enmLunType);
721 if (RT_SUCCESS(rc))
722 {
723 switch (enmLunType)
724 {
725 case VSCSILUNTYPE_SBC:
726 enmMediaType = PDMMEDIATYPE_HARD_DISK;
727 break;
728 case VSCSILUNTYPE_MMC:
729 enmMediaType = PDMMEDIATYPE_CDROM;
730 break;
731 default:
732 enmMediaType = PDMMEDIATYPE_ERROR;
733 break;
734 }
735 }
736
737 return enmMediaType;
738}
739
740/** @interface_method_impl{PDMIMEDIA,pfnGetUuid} */
741static DECLCALLBACK(int) drvscsiGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
742{
743 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMedia);
744
745 int rc = VINF_SUCCESS;
746 if (pThis->pDrvMedia)
747 rc = pThis->pDrvMedia->pfnGetUuid(pThis->pDrvMedia, pUuid);
748 else
749 RTUuidClear(pUuid);
750
751 return rc;
752}
753
754/* -=-=-=-=- IMediaEx -=-=-=-=- */
755
756/** @interface_method_impl{PDMIMEDIAEX,pfnQueryFeatures} */
757static DECLCALLBACK(int) drvscsiQueryFeatures(PPDMIMEDIAEX pInterface, uint32_t *pfFeatures)
758{
759 RT_NOREF1(pInterface);
760
761 *pfFeatures = PDMIMEDIAEX_FEATURE_F_RAWSCSICMD;
762 return VINF_SUCCESS;
763}
764
765/** @interface_method_impl{PDMIMEDIAEX,pfnNotifySuspend} */
766static DECLCALLBACK(void) drvscsiNotifySuspend(PPDMIMEDIAEX pInterface)
767{
768 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMediaEx);
769
770 /** @todo Don't crash if someone screws this up... Recreated a VISO while it
771 * was mounted and asked the GUI to use it. Got forced umount question.
772 * Said yes. Ended up here with a NULL pointer. */
773 PPDMIMEDIAEX pDrvMediaEx = pThis->pDrvMediaEx;
774 if (pThis)
775 pDrvMediaEx->pfnNotifySuspend(pDrvMediaEx);
776}
777
778/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqAllocSizeSet} */
779static DECLCALLBACK(int) drvscsiIoReqAllocSizeSet(PPDMIMEDIAEX pInterface, size_t cbIoReqAlloc)
780{
781 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMediaEx);
782
783 pThis->cbIoReqAlloc = RT_UOFFSETOF_DYN(DRVSCSIREQ, abAlloc[cbIoReqAlloc]);
784 return VINF_SUCCESS;
785}
786
787/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqAlloc} */
788static DECLCALLBACK(int) drvscsiIoReqAlloc(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq, void **ppvIoReqAlloc,
789 PDMMEDIAEXIOREQID uIoReqId, uint32_t fFlags)
790{
791 RT_NOREF2(uIoReqId, fFlags);
792
793 int rc = VINF_SUCCESS;
794 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMediaEx);
795 PDRVSCSIREQ pReq = (PDRVSCSIREQ)RTMemAllocZ(pThis->cbIoReqAlloc);
796 if (RT_LIKELY(pReq))
797 {
798 *phIoReq = (PDMMEDIAEXIOREQ)pReq;
799 *ppvIoReqAlloc = &pReq->abAlloc[0];
800 }
801 else
802 rc = VERR_NO_MEMORY;
803
804 return rc;
805}
806
807/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqFree} */
808static DECLCALLBACK(int) drvscsiIoReqFree(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
809{
810 RT_NOREF1(pInterface);
811 PDRVSCSIREQ pReq = (PDRVSCSIREQ)hIoReq;
812
813 RTMemFree(pReq);
814 return VINF_SUCCESS;
815}
816
817/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqQueryResidual} */
818static DECLCALLBACK(int) drvscsiIoReqQueryResidual(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, size_t *pcbResidual)
819{
820 RT_NOREF1(pInterface);
821 PDRVSCSIREQ pReq = (PDRVSCSIREQ)hIoReq;
822
823 if (pReq->cbXfer && pReq->cbXfer <= pReq->cbBuf)
824 *pcbResidual = pReq->cbBuf - pReq->cbXfer;
825 else
826 *pcbResidual = 0; /* Overflow/Underrun error or no data transfers. */
827 return VINF_SUCCESS;
828}
829
830/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqQueryXferSize} */
831static DECLCALLBACK(int) drvscsiIoReqQueryXferSize(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, size_t *pcbXfer)
832{
833 RT_NOREF1(pInterface);
834 PDRVSCSIREQ pReq = (PDRVSCSIREQ)hIoReq;
835
836 *pcbXfer = pReq->cbXfer;
837 return VINF_SUCCESS;
838}
839
840/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancelAll} */
841static DECLCALLBACK(int) drvscsiIoReqCancelAll(PPDMIMEDIAEX pInterface)
842{
843 RT_NOREF1(pInterface);
844 return VINF_SUCCESS;
845}
846
847/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancel} */
848static DECLCALLBACK(int) drvscsiIoReqCancel(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQID uIoReqId)
849{
850 RT_NOREF2(pInterface, uIoReqId);
851 return VERR_PDM_MEDIAEX_IOREQID_NOT_FOUND;
852}
853
854/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqRead} */
855static DECLCALLBACK(int) drvscsiIoReqRead(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbRead)
856{
857 RT_NOREF4(pInterface, hIoReq, off, cbRead);
858 return VERR_NOT_SUPPORTED;
859}
860
861/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqWrite} */
862static DECLCALLBACK(int) drvscsiIoReqWrite(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbWrite)
863{
864 RT_NOREF4(pInterface, hIoReq, off, cbWrite);
865 return VERR_NOT_SUPPORTED;
866}
867
868/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqFlush} */
869static DECLCALLBACK(int) drvscsiIoReqFlush(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
870{
871 RT_NOREF2(pInterface, hIoReq);
872 return VERR_NOT_SUPPORTED;
873}
874
875/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqDiscard} */
876static DECLCALLBACK(int) drvscsiIoReqDiscard(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, unsigned cRangesMax)
877{
878 RT_NOREF3(pInterface, hIoReq, cRangesMax);
879 return VERR_NOT_SUPPORTED;
880}
881
882/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqSendScsiCmd} */
883static DECLCALLBACK(int) drvscsiIoReqSendScsiCmd(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint32_t uLun,
884 const uint8_t *pbCdb, size_t cbCdb, PDMMEDIAEXIOREQSCSITXDIR enmTxDir,
885 size_t cbBuf, uint8_t *pabSense, size_t cbSense, uint8_t *pu8ScsiSts,
886 uint32_t cTimeoutMillies)
887{
888 RT_NOREF1(cTimeoutMillies);
889
890 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMediaEx);
891 PDRVSCSIREQ pReq = (PDRVSCSIREQ)hIoReq;
892 int rc = VINF_SUCCESS;
893
894 Log(("Dump for pReq=%#p Command: %s\n", pReq, SCSICmdText(pbCdb[0])));
895 Log(("cbCdb=%u\n", cbCdb));
896 for (uint32_t i = 0; i < cbCdb; i++)
897 Log(("pbCdb[%u]=%#x\n", i, pbCdb[i]));
898 Log(("cbBuf=%zu\n", cbBuf));
899
900 pReq->enmXferDir = enmTxDir;
901 pReq->cbBuf = cbBuf;
902 pReq->pu8ScsiSts = pu8ScsiSts;
903
904 /* Allocate and sync buffers if a data transfer is indicated. */
905 if (cbBuf)
906 {
907 pReq->pvBuf = RTMemAlloc(cbBuf);
908 if (RT_UNLIKELY(!pReq->pvBuf))
909 rc = VERR_NO_MEMORY;
910 }
911
912 if (RT_SUCCESS(rc))
913 {
914 pReq->Seg.pvSeg = pReq->pvBuf;
915 pReq->Seg.cbSeg = cbBuf;
916
917 if ( cbBuf
918 && ( enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN
919 || enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE))
920 {
921 RTSGBUF SgBuf;
922 RTSgBufInit(&SgBuf, &pReq->Seg, 1);
923 rc = pThis->pDevMediaExPort->pfnIoReqCopyToBuf(pThis->pDevMediaExPort, hIoReq, &pReq->abAlloc[0],
924 0, &SgBuf, cbBuf);
925 }
926
927 if (RT_SUCCESS(rc))
928 {
929 rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &pReq->hVScsiReq,
930 uLun, (uint8_t *)pbCdb, cbCdb, cbBuf, 1, &pReq->Seg,
931 pabSense, cbSense, pReq);
932 if (RT_SUCCESS(rc))
933 {
934 ASMAtomicIncU32(&pThis->StatIoDepth);
935 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, pReq->hVScsiReq);
936 if (RT_SUCCESS(rc))
937 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
938 }
939 }
940 }
941
942 return rc;
943}
944
945/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetActiveCount} */
946static DECLCALLBACK(uint32_t) drvscsiIoReqGetActiveCount(PPDMIMEDIAEX pInterface)
947{
948 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMediaEx);
949 return pThis->StatIoDepth;
950}
951
952/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetSuspendedCount} */
953static DECLCALLBACK(uint32_t) drvscsiIoReqGetSuspendedCount(PPDMIMEDIAEX pInterface)
954{
955 RT_NOREF1(pInterface);
956 return 0;
957}
958
959/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedStart} */
960static DECLCALLBACK(int) drvscsiIoReqQuerySuspendedStart(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq, void **ppvIoReqAlloc)
961{
962 RT_NOREF3(pInterface, phIoReq, ppvIoReqAlloc);
963 return VERR_NOT_IMPLEMENTED;
964}
965
966/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedNext} */
967static DECLCALLBACK(int) drvscsiIoReqQuerySuspendedNext(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq,
968 PPDMMEDIAEXIOREQ phIoReqNext, void **ppvIoReqAllocNext)
969{
970 RT_NOREF4(pInterface, hIoReq, phIoReqNext, ppvIoReqAllocNext);
971 return VERR_NOT_IMPLEMENTED;
972}
973
974/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedSave} */
975static DECLCALLBACK(int) drvscsiIoReqSuspendedSave(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
976{
977 RT_NOREF3(pInterface, pSSM, hIoReq);
978 return VERR_NOT_IMPLEMENTED;
979}
980
981/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedLoad} */
982static DECLCALLBACK(int) drvscsiIoReqSuspendedLoad(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
983{
984 RT_NOREF3(pInterface, pSSM, hIoReq);
985 return VERR_NOT_IMPLEMENTED;
986}
987
988
989static DECLCALLBACK(void) drvscsiIoReqVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
990 void *pVScsiReqUser, int rcScsiCode, bool fRedoPossible,
991 int rcReq, size_t cbXfer)
992{
993 RT_NOREF2(hVScsiDevice, fRedoPossible);
994 PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
995 PDRVSCSIREQ pReq = (PDRVSCSIREQ)pVScsiReqUser;
996
997 ASMAtomicDecU32(&pThis->StatIoDepth);
998
999 /* Sync buffers. */
1000 if ( RT_SUCCESS(rcReq)
1001 && pReq->cbBuf
1002 && ( pReq->enmXferDir == PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN
1003 || pReq->enmXferDir == PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE))
1004 {
1005 RTSGBUF SgBuf;
1006 RTSgBufInit(&SgBuf, &pReq->Seg, 1);
1007 int rcCopy = pThis->pDevMediaExPort->pfnIoReqCopyFromBuf(pThis->pDevMediaExPort, (PDMMEDIAEXIOREQ)pReq,
1008 &pReq->abAlloc[0], 0, &SgBuf, pReq->cbBuf);
1009 if (RT_FAILURE(rcCopy))
1010 rcReq = rcCopy;
1011 }
1012
1013 if (pReq->pvBuf)
1014 {
1015 RTMemFree(pReq->pvBuf);
1016 pReq->pvBuf = NULL;
1017 }
1018
1019 *pReq->pu8ScsiSts = (uint8_t)rcScsiCode;
1020 pReq->cbXfer = cbXfer;
1021 int rc = pThis->pDevMediaExPort->pfnIoReqCompleteNotify(pThis->pDevMediaExPort, (PDMMEDIAEXIOREQ)pReq,
1022 &pReq->abAlloc[0], rcReq);
1023 AssertRC(rc); RT_NOREF(rc);
1024
1025 if (RT_UNLIKELY(pThis->fDummySignal) && !pThis->StatIoDepth)
1026 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
1027}
1028
1029/**
1030 * Consumer for the queue
1031 *
1032 * @returns Success indicator.
1033 * If false the item will not be removed and the flushing will stop.
1034 * @param pDrvIns The driver instance.
1035 * @param pItem The item to consume. Upon return this item will be freed.
1036 */
1037static DECLCALLBACK(bool) drvscsiR3NotifyQueueConsumer(PPDMDRVINS pDrvIns, PPDMQUEUEITEMCORE pItem)
1038{
1039 PDRVSCSIEJECTSTATE pEjectState = (PDRVSCSIEJECTSTATE)pItem;
1040 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1041
1042 int rc = pThis->pDrvMount->pfnUnmount(pThis->pDrvMount, false/*=fForce*/, true/*=fEject*/);
1043 Assert(RT_SUCCESS(rc) || rc == VERR_PDM_MEDIA_LOCKED || rc == VERR_PDM_MEDIA_NOT_MOUNTED);
1044 if (RT_SUCCESS(rc))
1045 pThis->pDevMediaExPort->pfnMediumEjected(pThis->pDevMediaExPort);
1046
1047 pEjectState->rcReq = rc;
1048 RTSemEventSignal(pEjectState->hSemEvt);
1049 return true;
1050}
1051
1052/* -=-=-=-=- IBase -=-=-=-=- */
1053
1054/**
1055 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1056 */
1057static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1058{
1059 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1060 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1061
1062 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->pDrvMount);
1063 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1064 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEX, pThis->pDevMediaExPort ? &pThis->IMediaEx : NULL);
1065 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, pThis->pDrvMedia ? &pThis->IMedia : NULL);
1066 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pThis->IPort);
1067 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pThis->IMountNotify);
1068 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pThis->IPortEx);
1069 return NULL;
1070}
1071
1072static DECLCALLBACK(int) drvscsiQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
1073 uint32_t *piInstance, uint32_t *piLUN)
1074{
1075 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IPort);
1076
1077 return pThis->pDevMediaPort->pfnQueryDeviceLocation(pThis->pDevMediaPort, ppcszController,
1078 piInstance, piLUN);
1079}
1080
1081/**
1082 * Called when media is mounted.
1083 *
1084 * @param pInterface Pointer to the interface structure containing the called function pointer.
1085 */
1086static DECLCALLBACK(void) drvscsiMountNotify(PPDMIMOUNTNOTIFY pInterface)
1087{
1088 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMountNotify);
1089 LogFlowFunc(("mounting LUN#%p\n", pThis->hVScsiLun));
1090
1091 /* Ignore the call if we're called while being attached. */
1092 if (!pThis->pDrvMedia)
1093 return;
1094
1095 /* Let the LUN know that a medium was mounted. */
1096 VSCSILunMountNotify(pThis->hVScsiLun);
1097}
1098
1099/**
1100 * Called when media is unmounted
1101 *
1102 * @param pInterface Pointer to the interface structure containing the called function pointer.
1103 */
1104static DECLCALLBACK(void) drvscsiUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
1105{
1106 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMountNotify);
1107 LogFlowFunc(("unmounting LUN#%p\n", pThis->hVScsiLun));
1108
1109 /* Let the LUN know that the medium was unmounted. */
1110 VSCSILunUnmountNotify(pThis->hVScsiLun);
1111}
1112
1113/**
1114 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
1115 *
1116 * @param pDrvIns The driver instance.
1117 * @param pfnAsyncNotify The async callback.
1118 */
1119static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
1120{
1121 RT_NOREF1(pfnAsyncNotify);
1122
1123 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1124
1125 if (pThis->StatIoDepth > 0)
1126 ASMAtomicWriteBool(&pThis->fDummySignal, true);
1127}
1128
1129/**
1130 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
1131 *
1132 * @returns true if we've quiesced, false if we're still working.
1133 * @param pDrvIns The driver instance.
1134 */
1135static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
1136{
1137 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1138
1139 if (pThis->StatIoDepth > 0)
1140 return false;
1141 else
1142 return true;
1143}
1144
1145/**
1146 * @copydoc FNPDMDRVPOWEROFF
1147 */
1148static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
1149{
1150 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
1151}
1152
1153/**
1154 * @copydoc FNPDMDRVSUSPEND
1155 */
1156static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
1157{
1158 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
1159}
1160
1161/**
1162 * Callback employed by drvscsiReset.
1163 *
1164 * @returns true if we've quiesced, false if we're still working.
1165 * @param pDrvIns The driver instance.
1166 */
1167static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
1168{
1169 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1170
1171 if (pThis->StatIoDepth > 0)
1172 return false;
1173 else
1174 return true;
1175}
1176
1177/** @copydoc FNPDMDRVATTACH */
1178static DECLCALLBACK(int) drvscsiAttach(PPDMDRVINS pDrvIns, uint32_t fFlags)
1179{
1180 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1181
1182 LogFlowFunc(("pDrvIns=%#p fFlags=%#x\n", pDrvIns, fFlags));
1183
1184 AssertMsgReturn((fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG),
1185 ("SCSI: Hotplugging is not supported\n"),
1186 VERR_INVALID_PARAMETER);
1187
1188 /*
1189 * Try attach driver below and query it's media interface.
1190 */
1191 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
1192 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
1193
1194 /*
1195 * Query the media interface.
1196 */
1197 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIA);
1198 AssertMsgReturn(VALID_PTR(pThis->pDrvMedia), ("VSCSI configuration error: No media interface!\n"),
1199 VERR_PDM_MISSING_INTERFACE);
1200
1201 /* Query the extended media interface. */
1202 pThis->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIAEX);
1203 AssertMsgReturn(VALID_PTR(pThis->pDrvMediaEx), ("VSCSI configuration error: No extended media interface!\n"),
1204 VERR_PDM_MISSING_INTERFACE);
1205
1206 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
1207
1208 if (pThis->cbVScsiIoReqAlloc)
1209 {
1210 rc = pThis->pDrvMediaEx->pfnIoReqAllocSizeSet(pThis->pDrvMediaEx, pThis->cbVScsiIoReqAlloc);
1211 AssertMsgReturn(RT_SUCCESS(rc), ("Setting the I/O request allocation size failed with rc=%Rrc\n", rc), rc);
1212 }
1213
1214 if (pThis->pDrvMount)
1215 {
1216 if (pThis->pDrvMount->pfnIsMounted(pThis->pDrvMount))
1217 {
1218 rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
1219 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
1220 }
1221 else
1222 {
1223 rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
1224 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
1225 }
1226 }
1227
1228 return rc;
1229}
1230
1231/** @copydoc FNPDMDRVDETACH */
1232static DECLCALLBACK(void) drvscsiDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
1233{
1234 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1235
1236 RT_NOREF(fFlags);
1237 LogFlowFunc(("pDrvIns=%#p fFlags=%#x\n", pDrvIns, fFlags));
1238
1239 /*
1240 * Zero some important members.
1241 */
1242 pThis->pDrvBase = NULL;
1243 pThis->pDrvMedia = NULL;
1244 pThis->pDrvMediaEx = NULL;
1245 pThis->pDrvMount = NULL;
1246
1247 VSCSILunUnmountNotify(pThis->hVScsiLun);
1248}
1249
1250/**
1251 * @copydoc FNPDMDRVRESET
1252 */
1253static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
1254{
1255 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
1256}
1257
1258/**
1259 * Destruct a driver instance.
1260 *
1261 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1262 * resources can be freed correctly.
1263 *
1264 * @param pDrvIns The driver instance data.
1265 */
1266static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
1267{
1268 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1269 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1270
1271 /* Free the VSCSI device and LUN handle. */
1272 if (pThis->hVScsiDevice)
1273 {
1274 VSCSILUN hVScsiLun;
1275 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
1276 AssertRC(rc);
1277
1278 Assert(hVScsiLun == pThis->hVScsiLun);
1279 rc = VSCSILunDestroy(hVScsiLun);
1280 AssertRC(rc);
1281 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
1282 AssertRC(rc);
1283
1284 pThis->hVScsiDevice = NULL;
1285 pThis->hVScsiLun = NULL;
1286 }
1287}
1288
1289/**
1290 * Construct a block driver instance.
1291 *
1292 * @copydoc FNPDMDRVCONSTRUCT
1293 */
1294static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1295{
1296 int rc = VINF_SUCCESS;
1297 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
1298 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
1299 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1300
1301 /*
1302 * Initialize the instance data.
1303 */
1304 pThis->pDrvIns = pDrvIns;
1305
1306 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
1307
1308 /* IMedia */
1309 pThis->IMedia.pfnRead = NULL;
1310 pThis->IMedia.pfnReadPcBios = NULL;
1311 pThis->IMedia.pfnWrite = NULL;
1312 pThis->IMedia.pfnFlush = NULL;
1313 pThis->IMedia.pfnSendCmd = NULL;
1314 pThis->IMedia.pfnMerge = NULL;
1315 pThis->IMedia.pfnSetSecKeyIf = NULL;
1316 pThis->IMedia.pfnGetSize = drvscsiGetSize;
1317 pThis->IMedia.pfnGetSectorSize = drvscsiGetSectorSize;
1318 pThis->IMedia.pfnIsReadOnly = drvscsiIsReadOnly;
1319 pThis->IMedia.pfnIsNonRotational = drvscsiIsNonRotational;
1320 pThis->IMedia.pfnBiosGetPCHSGeometry = drvscsiBiosGetPCHSGeometry;
1321 pThis->IMedia.pfnBiosSetPCHSGeometry = drvscsiBiosSetPCHSGeometry;
1322 pThis->IMedia.pfnBiosGetLCHSGeometry = drvscsiBiosGetLCHSGeometry;
1323 pThis->IMedia.pfnBiosSetLCHSGeometry = drvscsiBiosSetLCHSGeometry;
1324 pThis->IMedia.pfnBiosIsVisible = drvscsiBiosIsVisible;
1325 pThis->IMedia.pfnGetType = drvscsiGetType;
1326 pThis->IMedia.pfnGetUuid = drvscsiGetUuid;
1327 pThis->IMedia.pfnDiscard = NULL;
1328
1329 /* IMediaEx */
1330 pThis->IMediaEx.pfnQueryFeatures = drvscsiQueryFeatures;
1331 pThis->IMediaEx.pfnNotifySuspend = drvscsiNotifySuspend;
1332 pThis->IMediaEx.pfnIoReqAllocSizeSet = drvscsiIoReqAllocSizeSet;
1333 pThis->IMediaEx.pfnIoReqAlloc = drvscsiIoReqAlloc;
1334 pThis->IMediaEx.pfnIoReqFree = drvscsiIoReqFree;
1335 pThis->IMediaEx.pfnIoReqQueryResidual = drvscsiIoReqQueryResidual;
1336 pThis->IMediaEx.pfnIoReqQueryXferSize = drvscsiIoReqQueryXferSize;
1337 pThis->IMediaEx.pfnIoReqCancelAll = drvscsiIoReqCancelAll;
1338 pThis->IMediaEx.pfnIoReqCancel = drvscsiIoReqCancel;
1339 pThis->IMediaEx.pfnIoReqRead = drvscsiIoReqRead;
1340 pThis->IMediaEx.pfnIoReqWrite = drvscsiIoReqWrite;
1341 pThis->IMediaEx.pfnIoReqFlush = drvscsiIoReqFlush;
1342 pThis->IMediaEx.pfnIoReqDiscard = drvscsiIoReqDiscard;
1343 pThis->IMediaEx.pfnIoReqSendScsiCmd = drvscsiIoReqSendScsiCmd;
1344 pThis->IMediaEx.pfnIoReqGetActiveCount = drvscsiIoReqGetActiveCount;
1345 pThis->IMediaEx.pfnIoReqGetSuspendedCount = drvscsiIoReqGetSuspendedCount;
1346 pThis->IMediaEx.pfnIoReqQuerySuspendedStart = drvscsiIoReqQuerySuspendedStart;
1347 pThis->IMediaEx.pfnIoReqQuerySuspendedNext = drvscsiIoReqQuerySuspendedNext;
1348 pThis->IMediaEx.pfnIoReqSuspendedSave = drvscsiIoReqSuspendedSave;
1349 pThis->IMediaEx.pfnIoReqSuspendedLoad = drvscsiIoReqSuspendedLoad;
1350
1351 pThis->IMountNotify.pfnMountNotify = drvscsiMountNotify;
1352 pThis->IMountNotify.pfnUnmountNotify = drvscsiUnmountNotify;
1353 pThis->IPort.pfnQueryDeviceLocation = drvscsiQueryDeviceLocation;
1354 pThis->IPortEx.pfnIoReqCompleteNotify = drvscsiIoReqCompleteNotify;
1355 pThis->IPortEx.pfnIoReqCopyFromBuf = drvscsiIoReqCopyFromBuf;
1356 pThis->IPortEx.pfnIoReqCopyToBuf = drvscsiIoReqCopyToBuf;
1357 pThis->IPortEx.pfnIoReqQueryBuf = NULL;
1358 pThis->IPortEx.pfnIoReqQueryDiscardRanges = drvscsiIoReqQueryDiscardRanges;
1359 pThis->IPortEx.pfnIoReqStateChanged = drvscsiIoReqStateChanged;
1360
1361 /* Query the optional media port interface above. */
1362 pThis->pDevMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
1363
1364 /* Query the optional extended media port interface above. */
1365 pThis->pDevMediaExPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAEXPORT);
1366
1367 AssertMsgReturn(pThis->pDevMediaExPort,
1368 ("Missing extended media port interface above\n"), VERR_PDM_MISSING_INTERFACE);
1369
1370 /* Query the optional LED interface above. */
1371 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
1372 if (pThis->pLedPort != NULL)
1373 {
1374 /* Get The Led. */
1375 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
1376 if (RT_FAILURE(rc))
1377 pThis->pLed = &pThis->Led;
1378 }
1379 else
1380 pThis->pLed = &pThis->Led;
1381
1382 /*
1383 * Validate and read configuration.
1384 */
1385 if (!CFGMR3AreValuesValid(pCfg, ""))
1386 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1387 N_("SCSI configuration error: unknown option specified"));
1388
1389 /*
1390 * Try attach driver below and query it's media interface.
1391 */
1392 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
1393 if (RT_FAILURE(rc))
1394 return rc;
1395
1396 /*
1397 * Query the media interface.
1398 */
1399 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIA);
1400 AssertMsgReturn(VALID_PTR(pThis->pDrvMedia), ("VSCSI configuration error: No media interface!\n"),
1401 VERR_PDM_MISSING_INTERFACE);
1402
1403 /* Query the extended media interface. */
1404 pThis->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIAEX);
1405 AssertMsgReturn(VALID_PTR(pThis->pDrvMediaEx), ("VSCSI configuration error: No extended media interface!\n"),
1406 VERR_PDM_MISSING_INTERFACE);
1407
1408 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
1409
1410 PDMMEDIATYPE enmType = pThis->pDrvMedia->pfnGetType(pThis->pDrvMedia);
1411 VSCSILUNTYPE enmLunType;
1412 switch (enmType)
1413 {
1414 case PDMMEDIATYPE_HARD_DISK:
1415 enmLunType = VSCSILUNTYPE_SBC;
1416 break;
1417 case PDMMEDIATYPE_CDROM:
1418 case PDMMEDIATYPE_DVD:
1419 enmLunType = VSCSILUNTYPE_MMC;
1420 break;
1421 default:
1422 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
1423 N_("Only hard disks and CD/DVD-ROMs are currently supported as SCSI devices (enmType=%d)"),
1424 enmType);
1425 }
1426 if ( ( enmType == PDMMEDIATYPE_DVD
1427 || enmType == PDMMEDIATYPE_CDROM)
1428 && !pThis->pDrvMount)
1429 {
1430 AssertMsgFailed(("Internal error: cdrom without a mountable interface\n"));
1431 return VERR_INTERNAL_ERROR;
1432 }
1433
1434 /* Create VSCSI device and LUN. */
1435 pThis->VScsiIoCallbacks.pfnVScsiLunReqAllocSizeSet = drvscsiReqAllocSizeSet;
1436 pThis->VScsiIoCallbacks.pfnVScsiLunReqAlloc = drvscsiReqAlloc;
1437 pThis->VScsiIoCallbacks.pfnVScsiLunReqFree = drvscsiReqFree;
1438 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetRegionCount = drvscsiGetRegionCount;
1439 pThis->VScsiIoCallbacks.pfnVScsiLunMediumQueryRegionProperties = drvscsiQueryRegionProperties;
1440 pThis->VScsiIoCallbacks.pfnVScsiLunMediumQueryRegionPropertiesForLba = drvscsiQueryRegionPropertiesForLba;
1441 pThis->VScsiIoCallbacks.pfnVScsiLunMediumEject = drvscsiEject;
1442 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
1443 pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags;
1444 pThis->VScsiIoCallbacks.pfnVScsiLunMediumSetLock = drvscsiSetLock;
1445 pThis->VScsiIoCallbacks.pfnVScsiLunQueryInqStrings = drvscsiQueryInqStrings;
1446
1447 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiIoReqVScsiReqCompleted, pThis);
1448 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n", rc), rc);
1449 rc = VSCSILunCreate(&pThis->hVScsiLun, enmLunType, &pThis->VScsiIoCallbacks,
1450 pThis);
1451 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n", rc), rc);
1452 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
1453 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
1454
1455 /// @todo This is a very hacky way of telling the LUN whether a medium was mounted.
1456 // The mount/unmount interface doesn't work in a very sensible manner!
1457 if (pThis->pDrvMount)
1458 {
1459 if (pThis->pDrvMount->pfnIsMounted(pThis->pDrvMount))
1460 {
1461 rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
1462 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
1463 }
1464 else
1465 {
1466 rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
1467 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
1468 }
1469 }
1470
1471 uint32_t fFeatures = 0;
1472 rc = pThis->pDrvMediaEx->pfnQueryFeatures(pThis->pDrvMediaEx, &fFeatures);
1473 if (RT_FAILURE(rc))
1474 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1475 N_("VSCSI configuration error: Failed to query features of device"));
1476 if (fFeatures & PDMIMEDIAEX_FEATURE_F_DISCARD)
1477 LogRel(("SCSI#%d: Enabled UNMAP support\n", pDrvIns->iInstance));
1478
1479 rc = PDMDrvHlpQueueCreate(pDrvIns, sizeof(DRVSCSIEJECTSTATE), 1, 0, drvscsiR3NotifyQueueConsumer,
1480 "SCSI-Eject", &pThis->pQueue);
1481 if (RT_FAILURE(rc))
1482 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1483 N_("VSCSI configuration error: Failed to create notification queue"));
1484
1485 return VINF_SUCCESS;
1486}
1487
1488/**
1489 * SCSI driver registration record.
1490 */
1491const PDMDRVREG g_DrvSCSI =
1492{
1493 /* u32Version */
1494 PDM_DRVREG_VERSION,
1495 /* szName */
1496 "SCSI",
1497 /* szRCMod */
1498 "",
1499 /* szR0Mod */
1500 "",
1501 /* pszDescription */
1502 "Generic SCSI driver.",
1503 /* fFlags */
1504 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1505 /* fClass. */
1506 PDM_DRVREG_CLASS_SCSI,
1507 /* cMaxInstances */
1508 ~0U,
1509 /* cbInstance */
1510 sizeof(DRVSCSI),
1511 /* pfnConstruct */
1512 drvscsiConstruct,
1513 /* pfnDestruct */
1514 drvscsiDestruct,
1515 /* pfnRelocate */
1516 NULL,
1517 /* pfnIOCtl */
1518 NULL,
1519 /* pfnPowerOn */
1520 NULL,
1521 /* pfnReset */
1522 drvscsiReset,
1523 /* pfnSuspend */
1524 drvscsiSuspend,
1525 /* pfnResume */
1526 NULL,
1527 /* pfnAttach */
1528 drvscsiAttach,
1529 /* pfnDetach */
1530 drvscsiDetach,
1531 /* pfnPowerOff */
1532 drvscsiPowerOff,
1533 /* pfnSoftReset */
1534 NULL,
1535 /* u32EndVersion */
1536 PDM_DRVREG_VERSION
1537};
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