VirtualBox

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

Last change on this file since 64154 was 64154, checked in by vboxsync, 9 years ago

SCSI: Fix retrieving the media interface

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette