VirtualBox

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

Last change on this file since 103882 was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use