VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DevVirtioSCSI.cpp@ 80931

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

Storage/DevVirtioSCSI.cpp: Fixed errors that prevented it from building on Windows and tested. See bugref:9440, Comment #93

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 98.9 KB
Line 
1/* $Id: DevVirtioSCSI.cpp 80931 2019-09-22 09:56:37Z vboxsync $ $Revision: 80931 $ $Date: 2019-09-22 09:56:37 +0000 (Sun, 22 Sep 2019) $ $Author: vboxsync $ */
2/** @file
3 * VBox storage devices - Virtio SCSI Driver
4 *
5 */
6
7/*
8 * Copyright (C) 2006-2019 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*********************************************************************************************************************************
21* Header Files *
22*********************************************************************************************************************************/
23//#define LOG_GROUP LOG_GROUP_DRV_SCSI
24#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
25
26#include <VBox/vmm/pdmdev.h>
27#include <VBox/vmm/pdmstorageifs.h>
28#include <VBox/vmm/pdmcritsect.h>
29#include <VBox/version.h>
30#include <VBox/log.h>
31#include <iprt/errcore.h>
32#include <iprt/assert.h>
33#include <iprt/string.h>
34#include "../build/VBoxDD.h"
35#include <VBox/scsi.h>
36#ifdef IN_RING3
37# include <iprt/alloc.h>
38# include <iprt/memcache.h>
39# include <iprt/semaphore.h>
40# include <iprt/sg.h>
41# include <iprt/param.h>
42# include <iprt/uuid.h>
43#endif
44#include "../VirtIO/Virtio_1_0.h"
45
46#include "VBoxSCSI.h"
47#include "VBoxDD.h"
48
49/*
50 * RT log-levels used:
51 *
52 * Level 1: The most important (but usually rare) things to note
53 * Level 2: SCSI command logging
54 * Level 3: Vector and I/O transfer summary (shows what client sent an expects and fulfillment)
55 * Level 6: Device ⟷ Guest Driver negotation, traffic, notifications and state handling
56 * Level 12: Brief formatted hex dumps of I/O data
57 */
58
59#define LUN0 0
60/**
61 * @name VirtIO 1.0 SCSI Host feature bits (See VirtIO 1.0 specification, Section 5.6.3)
62 * @{ */
63#define VIRTIO_SCSI_F_INOUT RT_BIT_64(0) /** Request is device readable AND writeable */
64#define VIRTIO_SCSI_F_HOTPLUG RT_BIT_64(1) /** Host allows hotplugging SCSI LUNs & targets */
65#define VIRTIO_SCSI_F_CHANGE RT_BIT_64(2) /** Host LUNs chgs via VIRTIOSCSI_T_PARAM_CHANGE evt */
66#define VIRTIO_SCSI_F_T10_PI RT_BIT_64(3) /** Add T10 port info (DIF/DIX) in SCSI req hdr */
67/** @} */
68
69
70#define VIRTIOSCSI_HOST_SCSI_FEATURES_ALL \
71 (VIRTIO_SCSI_F_INOUT | VIRTIO_SCSI_F_HOTPLUG | VIRTIO_SCSI_F_CHANGE | VIRTIO_SCSI_F_T10_PI)
72
73#define VIRTIOSCSI_HOST_SCSI_FEATURES_NONE 0
74
75#define VIRTIOSCSI_HOST_SCSI_FEATURES_OFFERED \
76 VIRTIOSCSI_HOST_SCSI_FEATURES_NONE
77
78/*
79 * TEMPORARY NOTE: following parameter is set to 1 for early development. Will be increased later
80 */
81#define VIRTIOSCSI_REQ_QUEUE_CNT 2 /**< Number of req queues exposed by dev. */
82#define VIRTIOSCSI_QUEUE_CNT VIRTIOSCSI_REQ_QUEUE_CNT + 2
83#define VIRTIOSCSI_MAX_LUN 256 /* < VirtIO specification, section 5.6.4 */
84#define VIRTIOSCSI_MAX_COMMANDS_PER_LUN 1 /* < T.B.D. What is a good value for this? */
85#define VIRTIOSCSI_MAX_SEG_COUNT 1024 /* < T.B.D. What is a good value for this? */
86#define VIRTIOSCSI_MAX_SECTORS_HINT 0x10000 /* < VirtIO specification, section 5.6.4 */
87#define VIRTIOSCSI_MAX_CHANNEL_HINT 0 /* < VirtIO specification, section 5.6.4 should be 0 */
88#define VIRTIOSCSI_SAVED_STATE_MINOR_VERSION 0x01 /**< SSM version # */
89
90#define PCI_DEVICE_ID_VIRTIOSCSI_HOST 0x1048 /**< Informs guest driver of type of VirtIO device */
91#define PCI_CLASS_BASE_MASS_STORAGE 0x01 /**< PCI Mass Storage device class */
92#define PCI_CLASS_SUB_SCSI_STORAGE_CONTROLLER 0x00 /**< PCI SCSI Controller subclass */
93#define PCI_CLASS_PROG_UNSPECIFIED 0x00 /**< Programming interface. N/A. */
94#define VIRTIOSCSI_PCI_CLASS 0x01 /**< Base class Mass Storage? */
95
96
97#define VIRTIOSCSI_SENSE_SIZE_DEFAULT 96 /**< VirtIO 1.0: 96 on reset, guest can change */
98#define VIRTIOSCSI_CDB_SIZE_DEFAULT 32 /**< VirtIO 1.0: 32 on reset, guest can change */
99#define VIRTIOSCSI_PI_BYTES_IN 1 /**< Value TBD (see section 5.6.6.1) */
100#define VIRTIOSCSI_PI_BYTES_OUT 1 /**< Value TBD (see section 5.6.6.1) */
101#define VIRTIOSCSI_DATA_OUT 512 /**< Value TBD (see section 5.6.6.1) */
102
103/**
104 * VirtIO SCSI Host Device device-specific queue indicies
105 *
106 * Virtqs (and their indices) are specified for a SCSI Host Device as described in the VirtIO 1.0 specification
107 * section 5.6. Thus there is no need to explicitly indicate the number of queues needed by this device. The number
108 * of req queues is variable and determined by virtio_scsi_config.num_queues. See VirtIO 1.0 spec section 5.6.4
109 */
110#define CONTROLQ_IDX 0 /**< Spec-defined Index of control queue */
111#define EVENTQ_IDX 1 /**< Spec-defined Index of event queue */
112#define VIRTQ_REQ_BASE 2 /**< Spec-defined base index of request queues */
113
114#define QUEUENAME(qIdx) (pThis->szQueueNames[qIdx]) /**< Macro to get queue name from its index */
115#define CBQUEUENAME(qIdx) RTStrNLen(QUEUENAME(qIdx), sizeof(QUEUENAME(qIdx)))
116
117
118#define IS_REQ_QUEUE(qIdx) (qIdx >= VIRTQ_REQ_BASE && qIdx < VIRTIOSCSI_QUEUE_CNT)
119
120/**
121 * Resolves to boolean true if uOffset matches a field offset and size exactly,
122 * (or if 64-bit field, if it accesses either 32-bit part as a 32-bit access)
123 * It is *assumed* this critereon is mandated by section 4.1.3.1 of the VirtIO 1.0 spec)
124 * This macro can be re-written to allow unaligned access to a field (within bounds).
125 *
126 * @param member - Member of VIRTIO_PCI_COMMON_CFG_T
127 * @result - true or false
128 */
129#define MATCH_SCSI_CONFIG(member) \
130 (RT_SIZEOFMEMB(VIRTIOSCSI_CONFIG_T, member) == 8 \
131 && ( uOffset == RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member) \
132 || uOffset == RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member) + sizeof(uint32_t)) \
133 && cb == sizeof(uint32_t)) \
134 || (uOffset == RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member) \
135 && cb == RT_SIZEOFMEMB(VIRTIOSCSI_CONFIG_T, member))
136
137#define LOG_ACCESSOR(member) \
138 virtioLogMappedIoValue(__FUNCTION__, #member, RT_SIZEOFMEMB(VIRTIOSCSI_CONFIG_T, member), \
139 pv, cb, uIntraOffset, fWrite, false, 0);
140
141#define SCSI_CONFIG_ACCESSOR(member) \
142 do \
143 { \
144 uint32_t uIntraOffset = uOffset - RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member); \
145 if (fWrite) \
146 memcpy(((char *)&pThis->virtioScsiConfig.member) + uIntraOffset, (const char *)pv, cb); \
147 else \
148 memcpy((char *)pv, (const char *)(((char *)&pThis->virtioScsiConfig.member) + uIntraOffset), cb); \
149 LOG_ACCESSOR(member); \
150 } while(0)
151
152#define SCSI_CONFIG_ACCESSOR_READONLY(member) \
153 do \
154 { \
155 uint32_t uIntraOffset = uOffset - RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member); \
156 if (fWrite) \
157 LogFunc(("Guest attempted to write readonly virtio_pci_common_cfg.%s\n", #member)); \
158 else \
159 { \
160 memcpy((char *)pv, (const char *)(((char *)&pThis->virtioScsiConfig.member) + uIntraOffset), cb); \
161 LOG_ACCESSOR(member); \
162 } \
163 } while(0)
164
165#define VIRTIO_IN_DIRECTION(pMediaExTxDirEnumValue) \
166 pMediaExTxDirEnumValue == PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE
167
168#define VIRTIO_OUT_DIRECTION(pMediaExTxDirEnumValue) \
169 pMediaExTxDirEnumValue == PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE
170/**
171 * Following struct is the VirtIO SCSI Host Device device-specific configuration described in section 5.6.4
172 * of the VirtIO 1.0 spec. Layout maps an MMIO area shared VirtIO guest driver. The VBox VirtIO
173 * this virtual controller device implementation is a client of. Framework does a callback whenever
174 * guest driver accesses any part of field in this struct
175 */
176typedef struct virtio_scsi_config
177{
178 uint32_t uNumQueues; /**< num_queues # of req q's exposed by dev */
179 uint32_t uSegMax; /**< seg_max Max # of segs allowed in cmd */
180 uint32_t uMaxSectors; /**< max_sectors Hint to guest max xfer to use */
181 uint32_t uCmdPerLun; /**< cmd_per_lun Max # of link cmd sent per lun */
182 uint32_t uEventInfoSize; /**< event_info_size Fill max, evtq bufs */
183 uint32_t uSenseSize; /**< sense_size Max sense data size dev writes */
184 uint32_t uCdbSize; /**< cdb_size Max CDB size driver writes */
185 uint16_t uMaxChannel; /**< max_channel Hint to guest driver */
186 uint16_t uMaxTarget; /**< max_target Hint to guest driver */
187 uint32_t uMaxLun; /**< max_lun Hint to guest driver */
188} VIRTIOSCSI_CONFIG_T, PVIRTIOSCSI_CONFIG_T;
189
190
191/**
192 * @name VirtIO 1.0 SCSI Host Device device specific control types
193 * @{ */
194#define VIRTIOSCSI_T_NO_EVENT 0
195#define VIRTIOSCSI_T_TRANSPORT_RESET 1
196#define VIRTIOSCSI_T_ASYNC_NOTIFY 2 /**< Asynchronous notification */
197#define VIRTIOSCSI_T_PARAM_CHANGE 3
198/** @} */
199
200/**
201 * Device operation: eventq
202 */
203#define VIRTIOSCSI_T_EVENTS_MISSED 0x80000000
204typedef struct virtio_scsi_event {
205 // Device-writable part
206 uint32_t uEvent; /**< event: */
207 uint8_t uVirtioLun[8]; /**< lun */
208 uint32_t uReason; /**< reason */
209} VIRTIOSCSI_EVENT_T, *PVIRTIOSCSI_EVENT_T;
210
211/**
212 * @name VirtIO 1.0 SCSI Host Device device specific event types
213 * @{ */
214#define VIRTIOSCSI_EVT_RESET_HARD 0 /**< */
215#define VIRTIOSCSI_EVT_RESET_RESCAN 1 /**< */
216#define VIRTIOSCSI_EVT_RESET_REMOVED 2 /**< */
217/** @} */
218
219
220#pragma pack(1)
221
222/**
223 * Device operation: reqestq
224 */
225struct REQ_CMD_HDR
226{
227 uint8_t uVirtioLun[8]; /**< lun */
228 uint64_t uId; /**< id */
229 uint8_t uTaskAttr; /**< task_attr */
230 uint8_t uPrio; /**< prio */
231 uint8_t uCrn; /**< crn */
232};
233
234struct REQ_CMD_PI
235{
236 uint32_t uPiBytesOut; /**< pi_bytesout */
237 uint32_t uPiBytesIn; /**< pi_bytesin */
238};
239
240struct REQ_RESP_HDR
241{
242 uint32_t uSenseLen; /**< sense_len */
243 uint32_t uResidual; /**< residual */
244 uint16_t uStatusQualifier; /**< status_qualifier */
245 uint8_t uStatus; /**< status SCSI status code */
246 uint8_t uResponse; /**< response */
247};
248
249typedef struct virtio_scsi_req_cmd
250{
251 /* Device-readable section */
252
253 struct REQ_CMD_HDR cmdHdr;
254 uint8_t uCdb[1]; /**< cdb */
255
256 struct REQ_CMD_PI piHdr; /** T10 Pi block integrity (optional feature) */
257 uint8_t uPiOut[1]; /**< pi_out[] T10 pi block integrity */
258 uint8_t uDataOut[1]; /**< dataout */
259
260 /** Device writable section */
261
262 struct REQ_RESP_HDR respHdr;
263 uint8_t uSense[1]; /**< sense */
264 uint8_t uPiIn[1]; /**< pi_in[] T10 Pi block integrity */
265 uint8_t uDataIn[1]; /**< detain; */
266
267} VIRTIOSCSI_REQ_CMD_T, *PVIRTIOSCSI_REQ_CMD_T;
268#pragma pack()
269
270/**
271 * @name VirtIO 1.0 SCSI Host Device Req command-specific response values
272 * @{ */
273#define VIRTIOSCSI_S_OK 0 /**< control, command */
274#define VIRTIOSCSI_S_OVERRUN 1 /**< control */
275#define VIRTIOSCSI_S_ABORTED 2 /**< control */
276#define VIRTIOSCSI_S_BAD_TARGET 3 /**< control, command */
277#define VIRTIOSCSI_S_RESET 4 /**< control */
278#define VIRTIOSCSI_S_BUSY 5 /**< control, command */
279#define VIRTIOSCSI_S_TRANSPORT_FAILURE 6 /**< control, command */
280#define VIRTIOSCSI_S_TARGET_FAILURE 7 /**< control, command */
281#define VIRTIOSCSI_S_NEXUS_FAILURE 8 /**< control, command */
282#define VIRTIOSCSI_S_FAILURE 9 /**< control, command */
283#define VIRTIOSCSI_S_INCORRECT_LUN 12 /**< command */
284/** @} */
285
286/**
287 * @name VirtIO 1.0 SCSI Host Device command-specific task_attr values
288 * @{ */
289#define VIRTIOSCSI_S_SIMPLE 0 /**< */
290#define VIRTIOSCSI_S_ORDERED 1 /**< */
291#define VIRTIOSCSI_S_HEAD 2 /**< */
292#define VIRTIOSCSI_S_ACA 3 /**< */
293/** @} */
294
295/**
296 * @name VirtIO 1.0 SCSI Host Device Control command before we know type (5.6.6.2)
297 * @{ */
298typedef struct virtio_scsi_ctrl
299{
300 uint32_t uType;
301} VIRTIOSCSI_CTRL_T, *PVIRTIOSCSI_CTRL_T;
302
303/**
304 * @name VirtIO 1.0 SCSI Host Device command-specific TMF values
305 * @{ */
306#define VIRTIOSCSI_T_TMF 0 /**< */
307#define VIRTIOSCSI_T_TMF_ABORT_TASK 0 /**< */
308#define VIRTIOSCSI_T_TMF_ABORT_TASK_SET 1 /**< */
309#define VIRTIOSCSI_T_TMF_CLEAR_ACA 2 /**< */
310#define VIRTIOSCSI_T_TMF_CLEAR_TASK_SET 3 /**< */
311#define VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET 4 /**< */
312#define VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET 5 /**< */
313#define VIRTIOSCSI_T_TMF_QUERY_TASK 6 /**< */
314#define VIRTIOSCSI_T_TMF_QUERY_TASK_SET 7 /**< */
315/*** @} */
316
317#pragma pack(1)
318typedef struct virtio_scsi_ctrl_tmf
319{
320 // Device-readable part
321 uint32_t uType; /** type */
322 uint32_t uSubtype; /** subtype */
323 uint8_t uScsiLun[8]; /** lun */
324 uint64_t uId; /** id */
325 // Device-writable part
326 uint8_t uResponse; /** response */
327} VIRTIOSCSI_CTRL_TMF_T, *PVIRTIOSCSI_CTRL_TMF_T;
328#pragma pack()
329
330/**
331 * @name VirtIO 1.0 SCSI Host Device device specific tmf control response values
332 * @{ */
333#define VIRTIOSCSI_S_FUNCTION_COMPLETE 0 /**< */
334#define VIRTIOSCSI_S_FUNCTION_SUCCEEDED 10 /**< */
335#define VIRTIOSCSI_S_FUNCTION_REJECTED 11 /**< */
336/** @} */
337
338#define VIRTIOSCSI_T_AN_QUERY 1 /** Asynchronous notification query */
339#define VIRTIOSCSI_T_AN_SUBSCRIBE 2 /** Asynchronous notification subscription */
340
341#pragma pack(1)
342typedef struct virtio_scsi_ctrl_an
343{
344 // Device-readable part
345 uint32_t uType; /** type */
346 uint8_t uScsiLun[8]; /** lun */
347 uint32_t uEventsRequested; /** event_requested */
348 // Device-writable part
349 uint32_t uEventActual; /** event_actual */
350 uint8_t uResponse; /** response */
351} VIRTIOSCSI_CTRL_AN, *PVIRTIOSCSI_CTRL_AN_T;
352#pragma pack()
353
354/**
355 * @name VirtIO 1.0 SCSI Host Device device specific tmf control response values
356 * @{ */
357#define VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE 2 /**< */
358#define VIRTIOSCSI_EVT_ASYNC_POWER_MGMT 4 /**< */
359#define VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST 8 /**< */
360#define VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE 16 /**< */
361#define VIRTIOSCSI_EVT_ASYNC_MULTI_HOST 32 /**< */
362#define VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY 64 /**< */
363/** @} */
364
365#define SUBSCRIBABLE_EVENTS \
366 VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE \
367 & VIRTIOSCSI_EVT_ASYNC_POWER_MGMT \
368 & VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST \
369 & VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE \
370 & VIRTIOSCSI_EVT_ASYNC_MULTI_HOST \
371 & VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY
372
373/**
374 * Worker thread context
375 */
376typedef struct WORKER
377{
378 R3PTRTYPE(PPDMTHREAD) pThread; /**< pointer to worker thread's handle */
379 SUPSEMEVENT hEvtProcess; /**< handle of associated sleep/wake-up semaphore */
380 bool fSleeping; /**< Flags whether worker thread is sleeping or not */
381 bool fNotified; /**< Flags whether worker thread notified */
382} WORKER, *PWORKER;
383
384/**
385 * State of a target attached to the VirtIO SCSI Host
386 */
387typedef struct VIRTIOSCSITARGET
388{
389 /** Pointer to PCI device that owns this target instance. - R3 pointer */
390 R3PTRTYPE(struct VIRTIOSCSI *) pVirtioScsi;
391
392 /** Pointer to attached driver's base interface. */
393 R3PTRTYPE(PPDMIBASE) pDrvBase;
394
395 /** Target number (PDM LUN) */
396 RTUINT iTarget;
397
398 /** Target Description */
399 char * pszTargetName;
400
401 /** Target base interface. */
402 PDMIBASE IBase;
403
404 /** Flag whether device is present. */
405 bool fPresent;
406
407 /** Media port interface. */
408 PDMIMEDIAPORT IMediaPort;
409
410 /** Pointer to the attached driver's media interface. */
411 R3PTRTYPE(PPDMIMEDIA) pDrvMedia;
412
413 /** Extended media port interface. */
414 PDMIMEDIAEXPORT IMediaExPort;
415
416 PPDMIMEDIANOTIFY pMediaNotify;
417
418 /** Pointer to the attached driver's extended media interface. */
419 R3PTRTYPE(PPDMIMEDIAEX) pDrvMediaEx;
420
421 /** Status LED interface */
422 PDMILEDPORTS ILed;
423
424 /** The status LED state for this device. */
425 PDMLED led;
426
427} VIRTIOSCSITARGET, *PVIRTIOSCSITARGET;
428
429/**
430 * PDM instance data (state) for VirtIO Host SCSI device
431 *
432 * @extends PDMPCIDEV
433 */
434typedef struct VIRTIOSCSI
435{
436 /* Opaque handle to VirtIO common framework (must be first item
437 * in this struct so PDMINS_2_DATA macro's casting works) */
438 VIRTIOHANDLE hVirtio;
439
440 /** Number of targets detected */
441 uint64_t cTargets;
442
443 R3PTRTYPE(PVIRTIOSCSITARGET) paTargetInstances;
444#if HC_ARCH_BITS == 32
445 RTR3PTR R3PtrPadding0;
446#endif
447
448 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
449 WORKER aWorker[VIRTIOSCSI_QUEUE_CNT];
450
451 bool fBootable;
452 bool fRCEnabled;
453 bool fR0Enabled;
454 /** Instance name */
455 const char szInstance[16];
456
457 /** Device-specific spec-based VirtIO queuenames */
458 const char szQueueNames[VIRTIOSCSI_QUEUE_CNT][VIRTIO_MAX_QUEUE_NAME_SIZE];
459
460 /** Track which VirtIO queues we've attached to */
461 bool fQueueAttached[VIRTIOSCSI_QUEUE_CNT];
462
463 /** Device base interface. */
464 PDMIBASE IBase;
465
466 /** Pointer to the device instance. - R3 ptr. */
467 PPDMDEVINSR3 pDevInsR3;
468
469 /** Pointer to the device instance. - R0 ptr. */
470 PPDMDEVINSR0 pDevInsR0;
471
472 /** Pointer to the device instance. - RC ptr. */
473 PPDMDEVINSRC pDevInsRC;
474
475 /** Status Target: LEDs port interface. */
476 PDMILEDPORTS ILeds;
477
478 /** Status Target: Partner of ILeds. */
479 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
480
481 /** Base address of the memory mapping. */
482 RTGCPHYS GCPhysMMIOBase;
483
484 /** IMediaExPort: Media ejection notification */
485 R3PTRTYPE(PPDMIMEDIANOTIFY) pMediaNotify;
486
487 /** Queue to send tasks to R3. - HC ptr */
488 R3PTRTYPE(PPDMQUEUE) pNotifierQueueR3;
489
490 /** The support driver session handle. */
491 R3R0PTRTYPE(PSUPDRVSESSION) pSupDrvSession;
492
493 /** Mask of VirtIO Async Event types this device will deliver */
494 uint32_t uAsyncEvtsEnabled;
495
496 /** The event semaphore the processing thread waits on. */
497
498
499 /** Total number of requests active across all targets */
500 volatile uint32_t cActiveReqs;
501
502 /** True if PDMDevHlpAsyncNotificationCompleted should be called when port goes idle */
503 bool volatile fSignalIdle;
504
505 /** Events the guest has subscribed to get notifications of */
506 uint32_t uSubscribedEvents;
507
508 /** Set if events missed due to lack of bufs avail on eventq */
509 bool fEventsMissed;
510
511 /** VirtIO Host SCSI device runtime configuration parameters */
512 VIRTIOSCSI_CONFIG_T virtioScsiConfig;
513
514 /** True if the guest/driver and VirtIO framework are in the ready state */
515 unsigned fVirtioReady;
516
517 /** True if VIRTIO_SCSI_F_T10_PI was negotiated */
518 unsigned fHasT10pi;
519
520 /** True if VIRTIO_SCSI_F_T10_PI was negotiated */
521 unsigned fHasHotplug;
522
523 /** True if VIRTIO_SCSI_F_T10_PI was negotiated */
524 unsigned fHasInOutBufs;
525
526 /** True if VIRTIO_SCSI_F_T10_PI was negotiated */
527 unsigned fHasLunChange;
528
529 /** True if in the process of resetting */
530 unsigned fResetting;
531
532 /** True if in the process of quiescing I/O */
533 unsigned fQuiescing;
534
535} VIRTIOSCSI, *PVIRTIOSCSI;
536
537/**
538 * Request structure for IMediaEx (Associated Interfaces implemented by DrvSCSI)
539 * (NOTE: cbIn, cbOUt, cbDataOut mostly for debugging)
540 */
541typedef struct VIRTIOSCSIREQ
542{
543 PDMMEDIAEXIOREQ hIoReq; /**< Handle of I/O request */
544 PVIRTIOSCSITARGET pTarget; /**< Target */
545 uint16_t qIdx; /**< Index of queue this request arrived on */
546 uint32_t cbPiOut; /**< Size of T10 pi in buffer */
547 uint8_t *pbPiOut; /**< Address of pi out buffer */
548 uint8_t *pbDataOut; /**< dataout */
549 uint32_t cbDataOut; /**< size of dataout buffer */
550 uint32_t cbPiIn; /**< Size of T10 pi buffer */
551 uint8_t *pbPiIn; /**< Address of pi in buffer */
552 uint32_t cbDataIn; /**< Size of datain buffer */
553 uint8_t *pbDataIn; /**< datain */
554 uint32_t cbSense; /**< Size of sense buffer */
555 size_t uSenseLen; /**< Receives # bytes written into sense buffer */
556 uint8_t *pbSense; /**< Pointer to R3 sense buffer */
557 PDMMEDIAEXIOREQSCSITXDIR enmTxDir; /**< Receives transfer direction of I/O req */
558 uint8_t uStatus; /**< SCSI status code */
559 PVIRTIO_DESC_CHAIN_T pDescChain; /**< Prepared desc chain pulled from virtq avail ring */
560} VIRTIOSCSIREQ;
561
562DECLINLINE(bool) isBufZero(uint8_t *pv, uint32_t cb)
563{
564 for (uint32_t i = 0; i < cb; i++)
565 if (pv[i])
566 return false;
567 return true;
568}
569
570DECLINLINE(const char *) virtioGetTxDirText(uint32_t enmTxDir)
571{
572 switch (enmTxDir)
573 {
574 case PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN: return "<UNKNOWN>";
575 case PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE: return "<DEV-TO-GUEST>";
576 case PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE: return "<GUEST-TO-DEV>";
577 case PDMMEDIAEXIOREQSCSITXDIR_NONE: return "<NONE>";
578 default: return "<BAD ENUM>";
579 }
580}
581
582DECLINLINE(const char *) virtioGetTMFTypeText(uint32_t uSubType)
583{
584 switch (uSubType)
585 {
586 case VIRTIOSCSI_T_TMF_ABORT_TASK: return "ABORT TASK";
587 case VIRTIOSCSI_T_TMF_ABORT_TASK_SET: return "ABORT TASK SET";
588 case VIRTIOSCSI_T_TMF_CLEAR_ACA: return "CLEAR ACA";
589 case VIRTIOSCSI_T_TMF_CLEAR_TASK_SET: return "CLEAR TASK SET";
590 case VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET: return "I T NEXUS RESET";
591 case VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET: return "LOGICAL UNIT RESET";
592 case VIRTIOSCSI_T_TMF_QUERY_TASK: return "QUERY TASK";
593 case VIRTIOSCSI_T_TMF_QUERY_TASK_SET: return "QUERY TASK SET";
594 default: return "<unknown>";
595 }
596}
597
598DECLINLINE(const char *) virtioGetReqRespText(uint32_t vboxRc)
599{
600 switch (vboxRc)
601 {
602 case VIRTIOSCSI_S_OK: return "OK";
603 case VIRTIOSCSI_S_OVERRUN: return "OVERRRUN";
604 case VIRTIOSCSI_S_ABORTED: return "ABORTED";
605 case VIRTIOSCSI_S_BAD_TARGET: return "BAD TARGET";
606 case VIRTIOSCSI_S_RESET: return "RESET";
607 case VIRTIOSCSI_S_TRANSPORT_FAILURE: return "TRANSPORT FAILURE";
608 case VIRTIOSCSI_S_TARGET_FAILURE: return "TARGET FAILURE";
609 case VIRTIOSCSI_S_NEXUS_FAILURE: return "NEXUS FAILURE";
610 case VIRTIOSCSI_S_BUSY: return "BUSY";
611 case VIRTIOSCSI_S_FAILURE: return "FAILURE";
612 default: return "<unknown>";
613 }
614}
615
616DECLINLINE(const char *) virtioGetCtrlRespText(uint32_t vboxRc)
617{
618 switch (vboxRc)
619 {
620 case VIRTIOSCSI_S_OK: return "OK/COMPLETE";
621 case VIRTIOSCSI_S_BAD_TARGET: return "BAD TARGET";
622 case VIRTIOSCSI_S_BUSY: return "BUSY";
623 case VIRTIOSCSI_S_NEXUS_FAILURE: return "NEXUS FAILURE";
624 case VIRTIOSCSI_S_TRANSPORT_FAILURE: return "TRANSPORT FAILURE";
625 case VIRTIOSCSI_S_FAILURE: return "FAILURE";
626 case VIRTIOSCSI_S_INCORRECT_LUN: return "INCORRECT LUN";
627 case VIRTIOSCSI_S_FUNCTION_SUCCEEDED: return "FUNCTION SUCCEEDED";
628 case VIRTIOSCSI_S_FUNCTION_REJECTED: return "FUNCTION REJECTED";
629 default: return "<unknown>";
630 }
631}
632
633DECLINLINE(void) virtioGetControlAsyncMaskText(char *pszOutput, uint32_t cbOutput, uint32_t uAsyncTypesMask)
634{
635 RTStrPrintf(pszOutput, cbOutput, "%s%s%s%s%s%s",
636 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE) ? "CHANGE_OPERATION " : "",
637 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_POWER_MGMT) ? "POWER_MGMT " : "",
638 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST) ? "EXTERNAL_REQ " : "",
639 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE) ? "MEDIA_CHANGE " : "",
640 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_MULTI_HOST) ? "MULTI_HOST " : "",
641 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY) ? "DEVICE_BUSY " : "");
642}
643
644uint8_t virtioScsiEstimateCdbLen(uint8_t uCmd, uint8_t cbMax)
645{
646 if (uCmd < 0x1f)
647 return 6;
648 else if (uCmd >= 0x20 && uCmd < 0x60)
649 return 10;
650 else if (uCmd >= 0x60 && uCmd < 0x80)
651 return cbMax;
652 else if (uCmd >= 0x80 && uCmd < 0xa0)
653 return 16;
654 else if (uCmd >= 0xa0 && uCmd < 0xC0)
655 return 12;
656 else
657 return cbMax;
658}
659
660typedef struct VIRTIOSCSIREQ *PVIRTIOSCSIREQ;
661
662#ifdef BOOTABLE_SUPPORT_TBD
663/** @callback_method_impl{FNIOMIOPORTIN} */
664static DECLCALLBACK(int) virtioScsiBiosIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint8_t *pbDst,
665 uint32_t *pcTransfers, unsigned cb);
666{
667}
668/** @callback_method_impl{FNIOMIOPORTOUT} */
669static DECLCALLBACK(int) virtioScsiBiosIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb);
670{
671}
672/** @callback_method_impl{FNIOMIOPORTOUTSTRING} */
673static DECLCALLBACK(int) virtioScsiBiosIoPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, const uint8_t *pbSrc,
674 uint32_t *pcTransfers, unsigned cb);
675{
676}
677/** @callback_method_impl{FNIOMIOPORTINSTRING} */
678static DECLCALLBACK(int) virtioScsiBiosIoPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint8_t *pbDst,
679 uint32_t *pcTransfers, unsigned cb);
680{
681}
682#endif
683
684/**
685 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
686 */
687static DECLCALLBACK(int) virtioScsiIoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
688 void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf, size_t cbCopy)
689{
690 RT_NOREF2(hIoReq, pInterface);
691 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
692 if (pReq->pbDataIn)
693 RTSgBufCopyToBuf(pSgBuf, pReq->pbDataIn + offDst, cbCopy);
694 return VINF_SUCCESS;
695}
696
697/**
698 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
699 */
700static DECLCALLBACK(int) virtioScsiIoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
701 void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf, size_t cbCopy)
702{
703 RT_NOREF2(hIoReq, pInterface);
704 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
705 if (pReq->pbDataOut)
706 RTSgBufCopyFromBuf(pSgBuf, pReq->pbDataOut + offSrc, cbCopy);
707
708 return VINF_SUCCESS;
709}
710
711static int virtioScsiSendEvent(PVIRTIOSCSI pThis, uint16_t uTarget, uint32_t uEventType, uint32_t uReason)
712{
713
714 VIRTIOSCSI_EVENT_T event;
715 event.uEvent = uEventType;
716 event.uReason = uReason;
717 event.uVirtioLun[0] = 1;
718 event.uVirtioLun[1] = uTarget;
719 event.uVirtioLun[2] = (LUN0 >> 8) & 0x40;
720 event.uVirtioLun[3] = LUN0 & 0xff;
721 event.uVirtioLun[4] = event.uVirtioLun[5] = event.uVirtioLun[6] = event.uVirtioLun[7] = 0;
722
723 switch(uEventType)
724 {
725 case VIRTIOSCSI_T_NO_EVENT:
726 if (uEventType & VIRTIOSCSI_T_EVENTS_MISSED)
727 Log6Func(("(target=%d, LUN=%d) Warning driver that events were missed\n", uTarget, LUN0));
728 else
729 Log6Func(("(target=%d, LUN=%d): Warning event info guest queued is shorter than configured\n",
730 uTarget, LUN0));
731 break;
732 case VIRTIOSCSI_T_TRANSPORT_RESET:
733 switch(uReason)
734 {
735 case VIRTIOSCSI_EVT_RESET_REMOVED:
736 Log6Func(("(target=%d, LUN=%d): Target or LUN removed\n", uTarget, LUN0));
737 break;
738 case VIRTIOSCSI_EVT_RESET_RESCAN:
739 Log6Func(("(target=%d, LUN=%d): Target or LUN added\n", uTarget, LUN0));
740 break;
741 case VIRTIOSCSI_EVT_RESET_HARD:
742 Log6Func(("(target=%d, LUN=%d): Target was reset\n", uTarget, LUN0));
743 break;
744 }
745 break;
746 case VIRTIOSCSI_T_ASYNC_NOTIFY:
747 char szTypeText[128];
748 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), uReason);
749 Log6Func(("(target=%d, LUN=%d): Delivering subscribed async notification %s\n", uTarget, LUN0));
750 break;
751 case VIRTIOSCSI_T_PARAM_CHANGE:
752 LogFunc(("(target=%d, LUN=%d): PARAM_CHANGE sense code: 0x%x sense qualifier: 0x%x\n",
753 uTarget, LUN0, uReason & 0xff, (uReason >> 8) & 0xff));
754 break;
755 default:
756 Log6Func(("(target=%d, LUN=%d): Unknown event type: %d, ignoring\n", uTarget, LUN0));
757 return VINF_SUCCESS;
758 }
759
760 if (virtioQueueIsEmpty(pThis->hVirtio, EVENTQ_IDX))
761 {
762 LogFunc(("eventq is empty, events missed (driver didn't preload queue)!\n"));
763 ASMAtomicWriteBool(&pThis->fEventsMissed, true);
764 return VINF_SUCCESS;
765 }
766
767 PVIRTIO_DESC_CHAIN_T pDescChain;
768 virtioQueueGet(pThis->hVirtio, EVENTQ_IDX, &pDescChain, true);
769
770 RTSGBUF reqSegBuf;
771 RTSGSEG aReqSegs[] = { { &event, sizeof(event) } };
772 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
773
774 virtioQueuePut (pThis->hVirtio, EVENTQ_IDX, &reqSegBuf, pDescChain, true);
775 virtioQueueSync(pThis->hVirtio, EVENTQ_IDX);
776
777 return VINF_SUCCESS;
778}
779
780static void virtioScsiFreeReq(PVIRTIOSCSITARGET pTarget, PVIRTIOSCSIREQ pReq)
781{
782 RTMemFree(pReq->pbSense);
783 RTMemFree(pReq->pbPiIn);
784 RTMemFree(pReq->pbDataIn);
785 pTarget->pDrvMediaEx->pfnIoReqFree(pTarget->pDrvMediaEx, pReq->hIoReq);
786}
787
788/**
789 * This is called to complete a request immediately
790 *
791 * @param pThis - PDM driver instance state
792 * @param qIdx - Queue index
793 * @param pDescChain - Pointer to pre-processed descriptor chain pulled from virtq
794 * @param pRespHdr - Response header
795 * @param pbSense - Pointer to sense buffer or NULL if none.
796 *
797 * @returns virtual box status code
798 */
799static int virtioScsiReqErr(PVIRTIOSCSI pThis, uint16_t qIdx, PVIRTIO_DESC_CHAIN_T pDescChain,
800 struct REQ_RESP_HDR *pRespHdr, uint8_t *pbSense)
801{
802 uint8_t *abSenseBuf = (uint8_t *)RTMemAllocZ(pThis->virtioScsiConfig.uSenseSize);
803 AssertReturn(abSenseBuf, VERR_NO_MEMORY);
804
805 Log2Func((" status: %s response: %s\n",
806 SCSIStatusText(pRespHdr->uStatus), virtioGetReqRespText(pRespHdr->uResponse)));
807
808 RTSGSEG aReqSegs[2];
809 aReqSegs[0].cbSeg = sizeof(pRespHdr);
810 aReqSegs[0].pvSeg = pRespHdr;
811 aReqSegs[1].cbSeg = pThis->virtioScsiConfig.uSenseSize;
812 aReqSegs[1].pvSeg = abSenseBuf;
813
814 if (pbSense && pRespHdr->uSenseLen)
815 memcpy(abSenseBuf, pbSense, pRespHdr->uSenseLen);
816 else
817 pRespHdr->uSenseLen = 0;
818
819 RTSGBUF reqSegBuf;
820 RTSgBufInit(&reqSegBuf, aReqSegs, RT_ELEMENTS(aReqSegs));
821
822 if (pThis->fResetting)
823 pRespHdr->uResponse = VIRTIOSCSI_S_RESET;
824
825 virtioQueuePut(pThis->hVirtio, qIdx, &reqSegBuf, pDescChain, true /* fFence */);
826 virtioQueueSync(pThis->hVirtio, qIdx);
827
828 RTMemFree(abSenseBuf);
829
830 if (!ASMAtomicDecU32(&pThis->cActiveReqs) && pThis->fQuiescing)
831 PDMDevHlpAsyncNotificationCompleted(pThis->CTX_SUFF(pDevIns));
832
833 Log2(("---------------------------------------------------------------------------------\n"));
834
835 return VINF_SUCCESS;
836}
837
838/**
839 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
840 */
841static DECLCALLBACK(int) virtioScsiIoReqFinish(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
842 void *pvIoReqAlloc, int rcReq)
843{
844 RT_NOREF(pInterface);
845
846 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
847 PVIRTIOSCSITARGET pTarget = pReq->pTarget;
848 PVIRTIOSCSI pThis = pTarget->pVirtioScsi;
849 PPDMIMEDIAEX pIMediaEx = pTarget->pDrvMediaEx;
850
851 size_t cbResidual = 0;
852 size_t cbXfer = 0;
853 int rc = pIMediaEx->pfnIoReqQueryResidual(pIMediaEx, hIoReq, &cbResidual);
854 AssertRC(rc);
855
856 rc = pIMediaEx->pfnIoReqQueryXferSize(pIMediaEx, hIoReq, &cbXfer);
857 AssertRC(rc);
858
859 struct REQ_RESP_HDR respHdr = { 0 };
860 respHdr.uSenseLen = pReq->pbSense[2] == SCSI_SENSE_NONE ? 0 : (uint32_t)pReq->uSenseLen;
861 AssertMsg(!(cbResidual & 0xffffffff00000000),
862 ("WARNING: Residual size larger than sizeof(uint32_t), truncating"));
863 respHdr.uResidual = (uint32_t)(cbResidual & 0xffffffff);
864 respHdr.uStatus = pReq->uStatus;
865
866 /* VirtIO 1.0 spec 5.6.6.1.1 says device MUST return a VirtIO response byte value.
867 * Some are returned during the submit phase, and a few are not mapped at all,
868 * wherein anything that can't map specifically gets mapped to VIRTIOSCSI_S_FAILURE
869 */
870 if (pThis->fResetting)
871 respHdr.uResponse = VIRTIOSCSI_S_RESET;
872 else
873 {
874 switch(rcReq)
875 {
876 case SCSI_STATUS_OK:
877 if (pReq->uStatus != SCSI_STATUS_CHECK_CONDITION)
878 {
879 respHdr.uResponse = VIRTIOSCSI_S_OK;
880 break;
881 }
882 case SCSI_STATUS_CHECK_CONDITION:
883 {
884 uint8_t uSenseKey = pReq->pbSense[2];
885 switch (uSenseKey)
886 {
887 case SCSI_SENSE_ABORTED_COMMAND:
888 respHdr.uResponse = VIRTIOSCSI_S_ABORTED;
889 break;
890 case SCSI_SENSE_COPY_ABORTED:
891 respHdr.uResponse = VIRTIOSCSI_S_ABORTED;
892 break;
893 case SCSI_SENSE_UNIT_ATTENTION:
894 respHdr.uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
895 break;
896 case SCSI_SENSE_HARDWARE_ERROR:
897 respHdr.uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
898 break;
899 case SCSI_SENSE_NOT_READY:
900 respHdr.uResponse = VIRTIOSCSI_S_BUSY; /* e.g. re-tryable */
901 break;
902 default:
903 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
904 break;
905 }
906 }
907 break;
908
909 default:
910 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
911 break;
912 }
913 }
914
915 Log2Func(("status: (%d) %s, response: (%d) %s\n",
916 pReq->uStatus, SCSIStatusText(pReq->uStatus),
917 respHdr.uResponse, virtioGetReqRespText(respHdr.uResponse)));
918
919 if (RT_FAILURE(rcReq))
920 Log2Func(("rcReq: %s\n", RTErrGetDefine(rcReq)));
921
922 if (LogIs3Enabled())
923 {
924 LogFunc(("cbDataIn = %u, cbDataOut = %u (cbIn = %u, cbOut = %u)\n",
925 pReq->cbDataIn, pReq->cbDataOut, pReq->pDescChain->cbPhysDst, pReq->pDescChain->cbVirtSrc));
926 LogFunc(("xfer = %lu, residual = %u\n", cbXfer, cbResidual));
927 LogFunc(("xfer direction: %s, sense written = %d, sense size = %d\n",
928 virtioGetTxDirText(pReq->enmTxDir), respHdr.uSenseLen, pThis->virtioScsiConfig.uSenseSize));
929 }
930
931 if (respHdr.uSenseLen && LogIs2Enabled())
932 {
933 LogFunc(("Sense: %s\n", SCSISenseText(pReq->pbSense[2])));
934 LogFunc(("Sense Ext3: %s\n", SCSISenseExtText(pReq->pbSense[12], pReq->pbSense[13])));
935 }
936
937 if (LogIs12Enabled())
938 {
939 Assert(!(cbXfer & 0xffffffff00000000));
940 uint32_t cbXfer32 = cbXfer & 0xffffffff;
941 uint32_t cb = RT_MIN(cbXfer32, 256);
942 if (VIRTIO_IN_DIRECTION(pReq->enmTxDir))
943 {
944 Log(("datain[%d of %d total bytes xferred]:\n", cb, cbXfer));
945 if (!isBufZero(pReq->pbDataIn, cb))
946 VIRTIO_HEX_DUMP(RTLOGGRPFLAGS_LEVEL_12, pReq->pbDataIn, cb, 0, 0);
947 else
948 Log12(("-- %d zeroes --\n", cb));
949 }
950 else
951 if (VIRTIO_OUT_DIRECTION(pReq->enmTxDir))
952 {
953 Log(("dataout[%d of %d total bytes xferred]:\n", cb, cbXfer));
954 if (!isBufZero(pReq->pbDataOut, cb))
955 VIRTIO_HEX_DUMP(RTLOGGRPFLAGS_LEVEL_12, pReq->pbDataOut, cb, 0, 0);
956 else
957 Log12(("-- %d zeroes --\n", cb));
958 }
959 }
960
961 int cSegs = 0;
962
963 if ( (VIRTIO_IN_DIRECTION(pReq->enmTxDir) && cbXfer > pReq->cbDataIn)
964 || (VIRTIO_OUT_DIRECTION(pReq->enmTxDir) && cbXfer > pReq->cbDataOut))
965 {
966 /* TBD try to figure out optimal sense info to send back besides response of VIRTIOSCSI_S_OVERRUN */
967 Log2Func((" * * * * Data overrun, returning sense\n"));
968 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
969 0, SCSI_SENSE_ILLEGAL_REQUEST, 0, 0, 0, 0, 10, 0, 0, 0 };
970 respHdr.uSenseLen = sizeof(abSense);
971 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
972 respHdr.uResponse = VIRTIOSCSI_S_OVERRUN;
973 respHdr.uResidual = pReq->cbDataIn;
974
975 virtioScsiReqErr(pThis, pReq->qIdx, pReq->pDescChain, &respHdr, abSense);
976 return VINF_SUCCESS;
977 }
978 else
979 {
980 Assert(pReq->pbSense != NULL);
981
982 RTSGSEG aReqSegs[4];
983 aReqSegs[cSegs].pvSeg = &respHdr;
984 aReqSegs[cSegs++].cbSeg = sizeof(respHdr);
985
986 aReqSegs[cSegs].pvSeg = pReq->pbSense;
987 aReqSegs[cSegs++].cbSeg = pReq->cbSense; /* VirtIO 1.0 spec 5.6.4/5.6.6.1 */
988
989 if (pReq->cbPiIn)
990 {
991 aReqSegs[cSegs].pvSeg = pReq->pbPiIn;
992 aReqSegs[cSegs++].cbSeg = pReq->cbPiIn;
993 }
994 if (pReq->cbDataIn)
995 {
996 aReqSegs[cSegs].pvSeg = pReq->pbDataIn;
997 aReqSegs[cSegs++].cbSeg = cbXfer;
998 }
999 RTSGBUF reqSegBuf;
1000 RTSgBufInit(&reqSegBuf, aReqSegs, cSegs);
1001
1002 /*
1003 * Fill in the request queue current descriptor chain's IN queue entry/entries
1004 * (phys. memory) with the Req response data in virtual memory.
1005 */
1006 size_t cbReqSgBuf = RTSgBufCalcTotalLength(&reqSegBuf);
1007 AssertMsgReturn(cbReqSgBuf <= pReq->pDescChain->cbPhysDst,
1008 ("Guest expected less req data (space needed: %ld, avail: %d)\n",
1009 cbReqSgBuf, pReq->pDescChain->cbPhysDst),
1010 VERR_BUFFER_OVERFLOW);
1011
1012 virtioQueuePut(pThis->hVirtio, pReq->qIdx, &reqSegBuf, pReq->pDescChain, true /* fFence TBD */);
1013 virtioQueueSync(pThis->hVirtio, pReq->qIdx);
1014
1015
1016 Log2(("-----------------------------------------------------------------------------------------\n"));
1017 }
1018
1019 virtioScsiFreeReq(pTarget, pReq);
1020
1021 if (!ASMAtomicDecU32(&pThis->cActiveReqs) && pThis->fQuiescing)
1022 PDMDevHlpAsyncNotificationCompleted(pThis->CTX_SUFF(pDevIns));
1023
1024 return VINF_SUCCESS;
1025}
1026
1027static int virtioScsiReqSubmit(PVIRTIOSCSI pThis, uint16_t qIdx, PVIRTIO_DESC_CHAIN_T pDescChain)
1028{
1029 ASMAtomicIncU32(&pThis->cActiveReqs);
1030
1031 PVIRTIOSCSI_REQ_CMD_T pVirtqReq = (PVIRTIOSCSI_REQ_CMD_T)pDescChain->pVirtSrc;
1032
1033 uint8_t uTarget = pVirtqReq->cmdHdr.uVirtioLun[1];
1034 uint32_t uScsiLun = (pVirtqReq->cmdHdr.uVirtioLun[2] << 8 | pVirtqReq->cmdHdr.uVirtioLun[3]) & 0x3fff;
1035 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[uTarget];
1036
1037 LogFunc(("[%s] (Target: %d LUN: %d) CDB: %.*Rhxs\n",
1038 SCSICmdText(pVirtqReq->uCdb[0]), uTarget, uScsiLun,
1039 virtioScsiEstimateCdbLen(pVirtqReq->uCdb[0],
1040 pThis->virtioScsiConfig.uCdbSize), pVirtqReq->uCdb));
1041
1042 Log3Func(("id: %RX64, attr: %x, prio: %d, crn: %x\n",
1043 pVirtqReq->cmdHdr.uId, pVirtqReq->cmdHdr.uTaskAttr, pVirtqReq->cmdHdr.uPrio, pVirtqReq->cmdHdr.uCrn));
1044
1045 /*
1046 * Calculate request offsets
1047 */
1048 uint32_t cbPiIn = 0, cbPiOut = 0, cbPiHdr = 0;
1049 off_t piOutOff = 0;
1050
1051 if (pThis->fHasT10pi)
1052 {
1053 cbPiIn = pVirtqReq->piHdr.uPiBytesOut;
1054 cbPiOut = pVirtqReq->piHdr.uPiBytesIn;
1055 cbPiHdr = sizeof(REQ_CMD_PI) + cbPiOut;
1056 piOutOff = sizeof(REQ_CMD_HDR) + pThis->virtioScsiConfig.uCdbSize + sizeof(REQ_CMD_PI);
1057 }
1058
1059 off_t uDataOutOff = sizeof(REQ_CMD_HDR) + pThis->virtioScsiConfig.uCdbSize + cbPiHdr;
1060 off_t uDataInOff = sizeof(REQ_RESP_HDR) + pThis->virtioScsiConfig.uSenseSize + cbPiIn;
1061 uint8_t *pbDataOut = (uint8_t *)((uint64_t)pVirtqReq + uDataOutOff);
1062 uint32_t cbDataOut = pDescChain->cbVirtSrc - uDataOutOff;
1063 uint32_t cbDataIn = pDescChain->cbPhysDst - uDataInOff;
1064
1065 /**
1066 * Handle submission errors
1067 */
1068 if (pThis->fResetting)
1069 {
1070 Log2Func(("Aborting req submission because reset is in progress\n"));
1071 struct REQ_RESP_HDR respHdr = { 0 };
1072 respHdr.uSenseLen = 0;
1073 respHdr.uStatus = SCSI_STATUS_OK;
1074 respHdr.uResponse = VIRTIOSCSI_S_RESET;
1075 respHdr.uResidual = cbDataIn + cbDataOut;
1076 virtioScsiReqErr(pThis, qIdx, pDescChain, &respHdr, NULL);
1077 return VINF_SUCCESS;
1078 }
1079 else
1080 if (uTarget >= pThis->cTargets || uScsiLun != 0)
1081 {
1082 Log2Func(("Error submitting request to bad target (%d) or bad LUN (%d)\n", uTarget, uScsiLun));
1083 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1084 0, SCSI_SENSE_ILLEGAL_REQUEST,
1085 0, 0, 0, 0, 10, SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED, 0, 0 };
1086 struct REQ_RESP_HDR respHdr = { 0 };
1087 respHdr.uSenseLen = sizeof(abSense);
1088 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1089 respHdr.uResponse = (uTarget > pThis->cTargets) ? VIRTIOSCSI_S_BAD_TARGET : VIRTIOSCSI_S_OK;
1090 respHdr.uResidual = cbDataOut + cbDataIn;
1091 virtioScsiReqErr(pThis, qIdx, pDescChain, &respHdr, abSense);
1092 return VINF_SUCCESS;
1093 }
1094 else
1095 if (RT_UNLIKELY(!pTarget->fPresent))
1096 {
1097 Log2Func(("Error submitting request, target not present!!\n"));
1098 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1099 0, SCSI_SENSE_NOT_READY, 0, 0, 0, 0, 10, 0, 0, 0 };
1100 struct REQ_RESP_HDR respHdr = { 0 };
1101 respHdr.uSenseLen = sizeof(abSense);
1102 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1103 respHdr.uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
1104 respHdr.uResidual = cbDataIn + cbDataOut;
1105 virtioScsiReqErr(pThis, qIdx, pDescChain, &respHdr , abSense);
1106 return VINF_SUCCESS;
1107 }
1108
1109 /*
1110 * Have underlying driver allocate a req of size set during initialization of this device.
1111 */
1112 PDMMEDIAEXIOREQ hIoReq = NULL;
1113 PVIRTIOSCSIREQ pReq;
1114 PPDMIMEDIAEX pIMediaEx = pTarget->pDrvMediaEx;
1115
1116 int rc = pIMediaEx->pfnIoReqAlloc(pIMediaEx, &hIoReq, (void **)&pReq, 0 /* uIoReqId */,
1117 PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR);
1118
1119 AssertMsgRCReturn(rc, ("Failed to allocate I/O request, rc=%Rrc\n", rc), rc);
1120
1121 /*
1122 * Prepare req's parameters for submission
1123 */
1124 if (pThis->fHasT10pi)
1125 {
1126 pReq->cbPiOut = cbPiOut;
1127 pReq->pbPiOut = (uint8_t *)((uint64_t)pVirtqReq + piOutOff);
1128 pReq->cbPiIn = cbPiIn;
1129 pReq->pbPiIn = (uint8_t *)RTMemAlloc(cbPiIn);
1130 AssertMsgReturn(pReq->pbPiIn, ("Out of memory allocating pi_in buffer"), VERR_NO_MEMORY);
1131 }
1132
1133 pReq->hIoReq = hIoReq;
1134 pReq->pTarget = pTarget;
1135 pReq->qIdx = qIdx;
1136 pReq->cbDataOut = cbDataOut;
1137 pReq->pbDataOut = cbDataOut ? pbDataOut : 0;
1138 pReq->pDescChain = pDescChain;
1139 pReq->cbSense = pThis->virtioScsiConfig.uSenseSize;
1140 pReq->pbSense = (uint8_t *)RTMemAlloc(pReq->cbSense);
1141 AssertMsgReturn(pReq->pbSense, ("Out of memory allocating sense buffer"), VERR_NO_MEMORY);
1142
1143 if (cbDataIn)
1144 {
1145 pReq->cbDataIn = cbDataIn;
1146 pReq->pbDataIn = (uint8_t *)RTMemAlloc(cbDataIn);
1147 AssertMsgReturn(pReq->pbDataIn, ("Out of memory allocating datain buffer"), VERR_NO_MEMORY);
1148 }
1149
1150 rc = pIMediaEx->pfnIoReqSendScsiCmd(pIMediaEx, pReq->hIoReq, uScsiLun,
1151 pVirtqReq->uCdb, (size_t)pThis->virtioScsiConfig.uCdbSize,
1152 PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN, &pReq->enmTxDir,
1153 (size_t)RT_MAX(cbDataIn, cbDataOut),
1154 pReq->pbSense, (size_t)pReq->cbSense, &pReq->uSenseLen,
1155 &pReq->uStatus, 30 * RT_MS_1SEC);
1156
1157 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
1158 {
1159 /*
1160 * Getting here means the request failed in early in the submission to the lower level driver,
1161 * and there will be no callback to the finished/completion function for this request
1162 */
1163 Log2Func(("Request submission error from lower-level driver\n"));
1164 uint8_t uASC, uASCQ = 0;
1165 switch (rc)
1166 {
1167 case VERR_NO_MEMORY:
1168 uASC = SCSI_ASC_SYSTEM_RESOURCE_FAILURE;
1169 break;
1170 default:
1171 uASC = SCSI_ASC_INTERNAL_TARGET_FAILURE;
1172 break;
1173 }
1174 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1175 0, SCSI_SENSE_VENDOR_SPECIFIC,
1176 0, 0, 0, 0, 10, uASC, uASCQ, 0 };
1177 struct REQ_RESP_HDR respHdr = { 0 };
1178 respHdr.uSenseLen = sizeof(abSense);
1179 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1180 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
1181 respHdr.uResidual = cbDataIn + cbDataOut;
1182 virtioScsiReqErr(pThis, qIdx, pDescChain, &respHdr, abSense);
1183 virtioScsiFreeReq(pTarget, pReq);
1184 return VINF_SUCCESS;
1185 }
1186
1187 return VINF_SUCCESS;
1188}
1189
1190static int virtioScsiCtrl(PVIRTIOSCSI pThis, uint16_t qIdx, PVIRTIO_DESC_CHAIN_T pDescChain)
1191{
1192 RT_NOREF2(pThis, qIdx);
1193
1194 uint8_t uResponse = VIRTIOSCSI_S_OK;
1195
1196 PVIRTIOSCSI_CTRL_T pScsiCtrl = (PVIRTIOSCSI_CTRL_T)pDescChain->pVirtSrc;
1197
1198 /*
1199 * Mask of events to tell guest driver this device supports
1200 * See VirtIO 1.0 specification section 5.6.6.2
1201 */
1202 uint32_t uSubscribedEvents =
1203 VIRTIOSCSI_EVT_ASYNC_POWER_MGMT
1204 | VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST
1205 | VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE
1206 | VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY;
1207
1208 RTSGBUF reqSegBuf;
1209
1210 switch(pScsiCtrl->uType)
1211 {
1212 case VIRTIOSCSI_T_TMF: /* Task Management Functions */
1213 {
1214 PVIRTIOSCSI_CTRL_TMF_T pScsiCtrlTmf = (PVIRTIOSCSI_CTRL_TMF_T)pScsiCtrl;
1215 LogFunc(("%s, VirtIO LUN: %.8Rhxs\n%*sTask Mgt Function: %s (not yet implemented)\n",
1216 QUEUENAME(qIdx), pScsiCtrlTmf->uScsiLun,
1217 CBQUEUENAME(qIdx) + 18, "", virtioGetTMFTypeText(pScsiCtrlTmf->uSubtype)));
1218
1219 switch(pScsiCtrlTmf->uSubtype)
1220 {
1221 case VIRTIOSCSI_T_TMF_ABORT_TASK:
1222 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1223 break;
1224 case VIRTIOSCSI_T_TMF_ABORT_TASK_SET:
1225 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1226 break;
1227 case VIRTIOSCSI_T_TMF_CLEAR_ACA:
1228 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1229 break;
1230 case VIRTIOSCSI_T_TMF_CLEAR_TASK_SET:
1231 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1232 break;
1233 case VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET:
1234 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1235 break;
1236 case VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET:
1237 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1238 break;
1239 case VIRTIOSCSI_T_TMF_QUERY_TASK:
1240 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1241 break;
1242 case VIRTIOSCSI_T_TMF_QUERY_TASK_SET:
1243 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1244 break;
1245 default:
1246 LogFunc(("Unknown TMF type\n"));
1247 uResponse = VIRTIOSCSI_S_FAILURE;
1248 }
1249
1250 RTSGSEG aReqSegs[] = { { &uResponse, sizeof(uResponse) } };
1251 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1252
1253 break;
1254 }
1255 case VIRTIOSCSI_T_AN_QUERY: /* Guest SCSI driver is querying supported async event notifications */
1256 {
1257 PVIRTIOSCSI_CTRL_AN_T pScsiCtrlAnQuery = (PVIRTIOSCSI_CTRL_AN_T)pScsiCtrl;
1258
1259 uSubscribedEvents &= pScsiCtrlAnQuery->uEventsRequested;
1260 uResponse = VIRTIOSCSI_S_FUNCTION_COMPLETE;
1261
1262 if (LogIs3Enabled())
1263 {
1264 char szTypeText[128];
1265 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), pScsiCtrlAnQuery->uEventsRequested);
1266
1267 LogFunc(("%s, VirtIO LUN: %.8Rhxs\n%*sAsync Query, types: %s\n",
1268 QUEUENAME(qIdx), pScsiCtrlAnQuery->uScsiLun, CBQUEUENAME(qIdx) + 30, "", szTypeText));
1269 }
1270
1271 RTSGSEG aReqSegs[] = { { &uSubscribedEvents, sizeof(uSubscribedEvents) }, { &uResponse, sizeof(uResponse) } };
1272 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1273
1274 break;
1275 }
1276 case VIRTIOSCSI_T_AN_SUBSCRIBE: /* Guest SCSI driver is subscribing to async event notification(s) */
1277 {
1278 PVIRTIOSCSI_CTRL_AN_T pScsiCtrlAnSubscribe = (PVIRTIOSCSI_CTRL_AN_T)pScsiCtrl;
1279
1280 if (pScsiCtrlAnSubscribe->uEventsRequested & ~SUBSCRIBABLE_EVENTS)
1281 LogFunc(("Unsupported bits in event subscription event mask: 0x%x\n", pScsiCtrlAnSubscribe->uEventsRequested));
1282
1283 uSubscribedEvents &= pScsiCtrlAnSubscribe->uEventsRequested;
1284 pThis->uAsyncEvtsEnabled = uSubscribedEvents;
1285
1286 if (LogIs3Enabled())
1287 {
1288 char szTypeText[128];
1289 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), pScsiCtrlAnSubscribe->uEventsRequested);
1290
1291 LogFunc(("%s, VirtIO LUN: %.8Rhxs\n%*sAsync Subscribe, types: %s\n",
1292 QUEUENAME(qIdx), pScsiCtrlAnSubscribe->uScsiLun, CBQUEUENAME(qIdx) + 30, "", szTypeText));
1293 }
1294
1295 /*
1296 * TBD: Verify correct status code if request mask is only partially fulfillable
1297 * and confirm when to use 'complete' vs. 'succeeded' See VirtIO 1.0 spec section 5.6.6.2
1298 * and read SAM docs*/
1299 if (uSubscribedEvents == pScsiCtrlAnSubscribe->uEventsRequested)
1300 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1301 else
1302 uResponse = VIRTIOSCSI_S_FUNCTION_COMPLETE;
1303
1304 RTSGSEG aReqSegs[] = { { &uSubscribedEvents, sizeof(uSubscribedEvents) }, { &uResponse, sizeof(uResponse) } };
1305 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1306
1307 break;
1308 }
1309 default:
1310 LogFunc(("Unknown control type extracted from %s: %d\n", QUEUENAME(qIdx), pScsiCtrl->uType));
1311
1312 uResponse = VIRTIOSCSI_S_FAILURE;
1313
1314 RTSGSEG aReqSegs[] = { { &uResponse, sizeof(uResponse) } };
1315 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1316 }
1317
1318 LogFunc(("Response code: %s\n", virtioGetCtrlRespText(uResponse)));
1319 virtioQueuePut (pThis->hVirtio, qIdx, &reqSegBuf, pDescChain, true);
1320 virtioQueueSync(pThis->hVirtio, qIdx);
1321
1322 return VINF_SUCCESS;
1323}
1324
1325/*
1326 * Unblock the worker thread so it can respond to a state change.
1327 *
1328 * @returns VBox status code.
1329 * @param pDevIns The pcnet device instance.
1330 * @param pThread The send thread.
1331 */
1332static DECLCALLBACK(int) virtioScsiWorkerWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1333{
1334 RT_NOREF(pThread);
1335 uint16_t qIdx = ((uint64_t)pThread->pvUser) & 0xffff;
1336 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1337 return SUPSemEventSignal(pThis->pSupDrvSession, pThis->aWorker[qIdx].hEvtProcess);
1338}
1339
1340static int virtioScsiWorker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1341{
1342 int rc;
1343 uint16_t qIdx = ((uint64_t)pThread->pvUser) & 0xffff;
1344 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1345 PWORKER pWorker = &pThis->aWorker[qIdx];
1346
1347 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
1348 return VINF_SUCCESS;
1349
1350 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
1351 {
1352 if (virtioQueueIsEmpty(pThis->hVirtio, qIdx))
1353 {
1354 /* Atomic interlocks avoid missing alarm while going to sleep & notifier waking the awoken */
1355 ASMAtomicWriteBool(&pWorker->fSleeping, true);
1356 bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
1357 if (!fNotificationSent)
1358 {
1359 Log6Func(("%s worker sleeping...\n", QUEUENAME(qIdx)));
1360 Assert(ASMAtomicReadBool(&pWorker->fSleeping));
1361 rc = SUPSemEventWaitNoResume(pThis->pSupDrvSession, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
1362 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
1363 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
1364 break;
1365 Log6Func(("%s worker woken\n", QUEUENAME(qIdx)));
1366 ASMAtomicWriteBool(&pWorker->fNotified, false);
1367 }
1368 ASMAtomicWriteBool(&pWorker->fSleeping, false);
1369 }
1370
1371 if (!pThis->fQueueAttached[qIdx])
1372 {
1373 LogFunc(("%s queue not attached, worker aborting...\n", QUEUENAME(qIdx)));
1374 break;
1375 } else {
1376 LogFunc(("%s queue is still attached!\n", QUEUENAME(qIdx)));
1377 }
1378
1379 if (!pThis->fQuiescing)
1380 {
1381 Log6Func(("fetching next descriptor chain from %s\n", QUEUENAME(qIdx)));
1382 PVIRTIO_DESC_CHAIN_T pDescChain;
1383 rc = virtioQueueGet(pThis->hVirtio, qIdx, &pDescChain, true);
1384 if (rc == VERR_NOT_AVAILABLE)
1385 {
1386 Log6Func(("Nothing found in %s\n", QUEUENAME(qIdx)));
1387 continue;
1388 }
1389
1390 AssertRC(rc);
1391 if (qIdx == CONTROLQ_IDX)
1392 virtioScsiCtrl(pThis, qIdx, pDescChain);
1393 else /* request queue index */
1394 {
1395 rc = virtioScsiReqSubmit(pThis, qIdx, pDescChain);
1396 if (RT_FAILURE(rc))
1397 {
1398 LogRel(("Error submitting req packet, resetting %Rrc", rc));
1399 }
1400 }
1401 }
1402 }
1403 return VINF_SUCCESS;
1404}
1405
1406
1407DECLINLINE(void) virtioScsiReportEventsMissed(PVIRTIOSCSI pThis, uint16_t uTarget)
1408{
1409 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_NO_EVENT | VIRTIOSCSI_T_EVENTS_MISSED, 0);
1410}
1411
1412/* Only invoke this if VIRTIOSCSI_F_HOTPLUG is negotiated during intiailization
1413 * This effectively removes the SCSI Target/LUN on the guest side
1414 */
1415DECLINLINE(void) virtioScsiReportTargetRemoved(PVIRTIOSCSI pThis, uint16_t uTarget)
1416{
1417 if (pThis->fHasHotplug)
1418 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET,
1419 VIRTIOSCSI_EVT_RESET_REMOVED);
1420}
1421
1422/* Only invoke thi if VIRTIOSCSI_F_HOTPLUG is negotiated during intiailization
1423 * This effectively adds the SCSI Target/LUN on the guest side
1424 */
1425DECLINLINE(void) virtioScsiReportTargetAdded(PVIRTIOSCSI pThis, uint16_t uTarget)
1426{
1427 if (pThis->fHasHotplug)
1428 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET,
1429 VIRTIOSCSI_EVT_RESET_RESCAN);
1430}
1431
1432DECLINLINE(void) virtioScsiReportTargetReset(PVIRTIOSCSI pThis, uint16_t uTarget)
1433{
1434 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET,
1435 VIRTIOSCSI_EVT_RESET_HARD);
1436}
1437
1438DECLINLINE(void) virtioScsiReportOperChange(PVIRTIOSCSI pThis, uint16_t uTarget)
1439{
1440 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE)
1441 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1442 VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE);
1443}
1444
1445DECLINLINE(void) virtioScsiReportPowerMsg(PVIRTIOSCSI pThis, uint16_t uTarget)
1446{
1447 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_POWER_MGMT)
1448 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1449 VIRTIOSCSI_EVT_ASYNC_POWER_MGMT);
1450}
1451
1452DECLINLINE(void) virtioScsiReportExtReq(PVIRTIOSCSI pThis, uint16_t uTarget)
1453{
1454 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST)
1455 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1456 VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST);
1457}
1458
1459DECLINLINE(void) virtioScsiReportMediaChange(PVIRTIOSCSI pThis, uint16_t uTarget)
1460{
1461 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE)
1462 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1463 VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE);
1464}
1465
1466DECLINLINE(void) virtioScsiReportMultiHost(PVIRTIOSCSI pThis, uint16_t uTarget)
1467{
1468 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_MULTI_HOST)
1469 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1470 VIRTIOSCSI_EVT_ASYNC_MULTI_HOST);
1471}
1472
1473DECLINLINE(void) virtioScsiReportDeviceBusy(PVIRTIOSCSI pThis, uint16_t uTarget)
1474{
1475 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY)
1476 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1477 VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY);
1478}
1479
1480
1481DECLINLINE(void) virtioScsiReportParamChange(PVIRTIOSCSI pThis, uint16_t uTarget, uint32_t uSenseCode, uint32_t uSenseQualifier)
1482{
1483 uint32_t uReason = uSenseQualifier << 8 | uSenseCode;
1484 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_PARAM_CHANGE, uReason);
1485
1486}
1487
1488static DECLCALLBACK(void) virtioScsiNotified(VIRTIOHANDLE hVirtio, void *pClient, uint16_t qIdx)
1489{
1490 RT_NOREF(hVirtio);
1491
1492 AssertReturnVoid(qIdx < VIRTIOSCSI_QUEUE_CNT);
1493 PVIRTIOSCSI pThis = (PVIRTIOSCSI)pClient;
1494 PWORKER pWorker = &pThis->aWorker[qIdx];
1495
1496 RTLogFlush(RTLogDefaultInstanceEx(RT_MAKE_U32(0, UINT16_MAX)));
1497
1498 if (qIdx == CONTROLQ_IDX || IS_REQ_QUEUE(qIdx))
1499 {
1500 Log6Func(("%s has available data\n", QUEUENAME(qIdx)));
1501 /* Wake queue's worker thread up if sleeping */
1502 if (!ASMAtomicXchgBool(&pWorker->fNotified, true))
1503 {
1504 if (ASMAtomicReadBool(&pWorker->fSleeping))
1505 {
1506 Log6Func(("waking %s worker.\n", QUEUENAME(qIdx)));
1507 int rc = SUPSemEventSignal(pThis->pSupDrvSession, pWorker->hEvtProcess);
1508 AssertRC(rc);
1509 }
1510 }
1511 }
1512 else if (qIdx == EVENTQ_IDX)
1513 {
1514 Log3Func(("Driver queued buffer(s) to %s\n"));
1515 if (ASMAtomicXchgBool(&pThis->fEventsMissed, false))
1516 virtioScsiReportEventsMissed(pThis, 0);
1517 }
1518 else
1519 LogFunc(("Unexpected queue idx (ignoring): %d\n", qIdx));
1520}
1521
1522static DECLCALLBACK(void) virtioScsiStatusChanged(VIRTIOHANDLE hVirtio, void *pClient, uint32_t fVirtioReady)
1523{
1524 RT_NOREF(hVirtio);
1525 PVIRTIOSCSI pThis = (PVIRTIOSCSI)pClient;
1526
1527 pThis->fVirtioReady = fVirtioReady;
1528
1529 if (fVirtioReady)
1530 {
1531 LogFunc(("VirtIO ready\n-----------------------------------------------------------------------------------------\n"));
1532 uint64_t features = virtioGetNegotiatedFeatures(hVirtio);
1533 pThis->fHasT10pi = features & VIRTIO_SCSI_F_T10_PI;
1534 pThis->fHasHotplug = features & VIRTIO_SCSI_F_HOTPLUG;
1535 pThis->fHasInOutBufs = features & VIRTIO_SCSI_F_INOUT;
1536 pThis->fHasLunChange = features & VIRTIO_SCSI_F_CHANGE;
1537 pThis->fQuiescing = false;
1538 pThis->fResetting = false;
1539
1540 for (int i = 0; i < VIRTIOSCSI_QUEUE_CNT; i++)
1541 pThis->fQueueAttached[i] = true;
1542 }
1543 else
1544 {
1545 LogFunc(("VirtIO is resetting\n"));
1546 for (int i = 0; i < VIRTIOSCSI_QUEUE_CNT; i++)
1547 pThis->fQueueAttached[i] = false;
1548 }
1549}
1550
1551/**
1552 * virtio-scsi debugger info callback.
1553 *
1554 * @param pDevIns The device instance.
1555 * @param pHlp The output helpers.
1556 * @param pszArgs The arguments.
1557 */
1558static DECLCALLBACK(void) virtioScsiInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1559{
1560 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1561 bool fVerbose = false;
1562
1563 /* Parse arguments. */
1564 if (pszArgs)
1565 fVerbose = strstr(pszArgs, "verbose") != NULL;
1566
1567 /* Show basic information. */
1568 pHlp->pfnPrintf(pHlp, "%s#%d: virtio-scsci ",
1569 pDevIns->pReg->szName,
1570 pDevIns->iInstance);
1571 pHlp->pfnPrintf(pHlp, "numTargets=%lu", pThis->cTargets);
1572}
1573
1574/**
1575 * @interface_method_impl{PDMIMEDIAEXPORT,pfnMediumEjected}
1576 */
1577static DECLCALLBACK(void) virtioScsiMediumEjected(PPDMIMEDIAEXPORT pInterface)
1578{
1579 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
1580 PVIRTIOSCSI pThis = pTarget->pVirtioScsi;
1581 LogFunc(("LUN %d Ejected!\n", pTarget->iTarget));
1582 if (pThis->pMediaNotify)
1583 {
1584 int rc = VMR3ReqCallNoWait(PDMDevHlpGetVM(pThis->CTX_SUFF(pDevIns)), VMCPUID_ANY,
1585 (PFNRT)pThis->pMediaNotify->pfnEjected, 2,
1586 pThis->pMediaNotify, pTarget->iTarget);
1587 AssertRC(rc);
1588 }
1589}
1590
1591/** @callback_method_impl{FNSSMDEVLIVEEXEC} */
1592static DECLCALLBACK(int) virtioScsiLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1593{
1594 LogFunc(("callback"));
1595 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1596 RT_NOREF(pThis);
1597 RT_NOREF(uPass);
1598 RT_NOREF(pSSM);
1599 return VINF_SSM_DONT_CALL_AGAIN;
1600}
1601
1602/** @callback_method_impl{FNSSMDEVLOADEXEC} */
1603static DECLCALLBACK(int) virtioScsiLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1604{
1605 LogFunc(("callback"));
1606 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1607 RT_NOREF(pThis);
1608 RT_NOREF(uPass);
1609 RT_NOREF(pSSM);
1610 RT_NOREF(uVersion);
1611 return VINF_SSM_DONT_CALL_AGAIN;
1612}
1613
1614/** @callback_method_impl{FNSSMDEVSAVEEXEC} */
1615static DECLCALLBACK(int) virtioScsiSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1616{
1617 LogFunc(("callback"));
1618 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1619
1620 RT_NOREF(pThis);
1621 RT_NOREF(pSSM);
1622 return VINF_SUCCESS;
1623}
1624
1625/** @callback_method_impl{FNSSMDEVLOADDONE} */
1626static DECLCALLBACK(int) virtioScsiLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1627{
1628 LogFunc(("callback"));
1629 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1630 RT_NOREF(pThis);
1631 RT_NOREF(pSSM);
1632 return VINF_SUCCESS;
1633}
1634
1635/**
1636 * Is asynchronous handling of suspend or power off notification completed?
1637 *
1638 * This is called to check whether the device has quiesced. Don't deadlock.
1639 * Avoid blocking. Do NOT wait for anything.
1640 *
1641 * @returns true if done, false if more work to be done.
1642 *
1643 * @param pDevIns The device instance.
1644 * @remarks The caller will enter the device critical section.
1645 * @thread EMT(0)
1646 */
1647static DECLCALLBACK(bool) virtioScsiDeviceQuiesced(PPDMDEVINS pDevIns)
1648{
1649 LogFunc(("Device I/O activity quiesced.\n"));
1650 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1651
1652 pThis->fQuiescing = false;
1653
1654 return true;
1655}
1656
1657static void virtioScsiQuiesceDevice(PPDMDEVINS pDevIns)
1658{
1659 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1660
1661 /* Prevent worker threads from removing/processing elements from virtq's */
1662 pThis->fQuiescing = true;
1663
1664 PDMDevHlpSetAsyncNotification(pDevIns, virtioScsiDeviceQuiesced);
1665
1666 /* If already quiesced invoke async callback. */
1667 if (!ASMAtomicReadU32(&pThis->cActiveReqs))
1668 PDMDevHlpAsyncNotificationCompleted(pDevIns);
1669}
1670
1671/**
1672 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
1673 */
1674static DECLCALLBACK(void) virtioScsiIoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1675 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
1676{
1677 RT_NOREF4(pInterface, hIoReq, pvIoReqAlloc, enmState);
1678 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, IBase);
1679
1680 switch (enmState)
1681 {
1682 case PDMMEDIAEXIOREQSTATE_SUSPENDED:
1683 {
1684 /* Stop considering this request active */
1685 if (!ASMAtomicDecU32(&pThis->cActiveReqs) && pThis->fQuiescing)
1686 PDMDevHlpAsyncNotificationCompleted(pThis->CTX_SUFF(pDevIns));
1687 break;
1688 }
1689 case PDMMEDIAEXIOREQSTATE_ACTIVE:
1690 ASMAtomicIncU32(&pThis->cActiveReqs);
1691 break;
1692 default:
1693 AssertMsgFailed(("Invalid request state given %u\n", enmState));
1694 }
1695}
1696
1697/**
1698 * @copydoc FNPDMDEVRESET
1699 */
1700static DECLCALLBACK(void) virtioScsiReset(PPDMDEVINS pDevIns)
1701{
1702 LogFunc(("\n"));
1703 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1704 pThis->fResetting = true;
1705 virtioScsiQuiesceDevice(pDevIns);
1706}
1707
1708/**
1709 * @interface_method_impl{PDMDEVREG,pfnResume}
1710 */
1711static DECLCALLBACK(void) virtioScsiResume(PPDMDEVINS pDevIns)
1712{
1713 LogFunc(("\n"));
1714
1715 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1716
1717 pThis->fQuiescing = false;
1718
1719 /* Wake worker threads flagged to skip pulling queue entries during quiesce
1720 * to ensure they re-check their queues. Active request queues may already
1721 * be awake due to new reqs coming in.
1722 */
1723 for (uint16_t qIdx = 0; qIdx < VIRTIOSCSI_REQ_QUEUE_CNT; qIdx++)
1724 {
1725 PWORKER pWorker = &pThis->aWorker[qIdx];
1726
1727 if (ASMAtomicReadBool(&pWorker->fSleeping))
1728 {
1729 Log6Func(("waking %s worker.\n", QUEUENAME(qIdx)));
1730 int rc = SUPSemEventSignal(pThis->pSupDrvSession, pWorker->hEvtProcess);
1731 AssertRC(rc);
1732 }
1733 }
1734
1735 /* Ensure guest is working the queues too. */
1736 virtioPropagateResumeNotification(pThis->hVirtio);
1737}
1738
1739/**
1740 * @interface_method_impl{PDMDEVREG,pfnSuspend}
1741 */
1742static DECLCALLBACK(void) virtioScsiSuspendOrPoweroff(PPDMDEVINS pDevIns)
1743{
1744 LogFunc(("\n"));
1745
1746 virtioScsiQuiesceDevice(pDevIns);
1747
1748 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1749
1750 /* VM is halted, thus no new I/O being dumped into queues by the guest.
1751 * Workers have been flagged to stop pulling stuff already queued-up by the guest.
1752 * Now tell lower-level to to suspend reqs (for example, DrvVD suspends all reqs
1753 * on its wait queue, and we will get a callback as the state changes to
1754 * suspended (and later, resumed) for each).
1755 */
1756 for (uint32_t i = 0; i < pThis->cTargets; i++)
1757 {
1758 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[i];
1759 if (pTarget->pDrvBase)
1760 if (pTarget->pDrvMediaEx)
1761 pTarget->pDrvMediaEx->pfnNotifySuspend(pTarget->pDrvMediaEx);
1762 }
1763}
1764
1765/**
1766 * Turns on/off the write status LED.
1767 *
1768 * @param pTarget Pointer to the target device
1769 * @param fOn New LED state.
1770 */
1771void virtioScsiSetWriteLed(PVIRTIOSCSITARGET pTarget, bool fOn)
1772{
1773 LogFlow(("%s virtioSetWriteLed: %s\n", pTarget->pszTargetName, fOn ? "on" : "off"));
1774 if (fOn)
1775 pTarget->led.Asserted.s.fWriting = pTarget->led.Actual.s.fWriting = 1;
1776 else
1777 pTarget->led.Actual.s.fWriting = fOn;
1778}
1779
1780/**
1781 * Turns on/off the read status LED.
1782 *
1783 * @param pTarget Pointer to the device state structure.
1784 * @param fOn New LED state.
1785 */
1786void virtioScsiSetReadLed(PVIRTIOSCSITARGET pTarget, bool fOn)
1787{
1788 LogFlow(("%s virtioSetReadLed: %s\n", pTarget->pszTargetName, fOn ? "on" : "off"));
1789 if (fOn)
1790 pTarget->led.Asserted.s.fReading = pTarget->led.Actual.s.fReading = 1;
1791 else
1792 pTarget->led.Actual.s.fReading = fOn;
1793}
1794
1795/**
1796 * Gets the pointer to the status LED of a unit.
1797 *
1798 * @returns VBox status code.
1799 * @param pInterface Pointer to the interface structure containing the called function pointer.
1800 * @param iTarget The unit which status LED we desire.
1801 * @param ppLed Where to store the LED pointer.
1802 */
1803static DECLCALLBACK(int) virtioScsiTargetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iTarget, PPDMLED *ppLed)
1804{
1805 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, ILed);
1806 if (iTarget == 0)
1807 {
1808 *ppLed = &pTarget->led;
1809 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
1810 return VINF_SUCCESS;
1811 }
1812 return VERR_PDM_LUN_NOT_FOUND;
1813}
1814
1815/**
1816 * Gets the pointer to the status LED of a unit.
1817 *
1818 * @returns VBox status code.
1819 * @param pInterface Pointer to the interface structure containing the called function pointer.
1820 * @param iTarget The unit which status LED we desire.
1821 * @param ppLed Where to store the LED pointer.
1822 */
1823static DECLCALLBACK(int) virtioScsiDeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iTarget, PPDMLED *ppLed)
1824{
1825 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, ILeds);
1826 if (iTarget < pThis->cTargets)
1827 {
1828 *ppLed = &pThis->paTargetInstances[iTarget].led;
1829 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
1830 return VINF_SUCCESS;
1831 }
1832 return VERR_PDM_LUN_NOT_FOUND;
1833 }
1834
1835static int virtioScsiCfgAccessed(PVIRTIOSCSI pThis, uint32_t uOffset,
1836 const void *pv, uint32_t cb, bool fWrite)
1837{
1838 int rc = VINF_SUCCESS;
1839 if (MATCH_SCSI_CONFIG(uNumQueues))
1840 SCSI_CONFIG_ACCESSOR_READONLY(uNumQueues);
1841 else
1842 if (MATCH_SCSI_CONFIG(uSegMax))
1843 SCSI_CONFIG_ACCESSOR_READONLY(uSegMax);
1844 else
1845 if (MATCH_SCSI_CONFIG(uMaxSectors))
1846 SCSI_CONFIG_ACCESSOR_READONLY(uMaxSectors);
1847 else
1848 if (MATCH_SCSI_CONFIG(uCmdPerLun))
1849 SCSI_CONFIG_ACCESSOR_READONLY(uCmdPerLun);
1850 else
1851 if (MATCH_SCSI_CONFIG(uEventInfoSize))
1852 SCSI_CONFIG_ACCESSOR_READONLY(uEventInfoSize);
1853 else
1854 if (MATCH_SCSI_CONFIG(uSenseSize))
1855 SCSI_CONFIG_ACCESSOR(uSenseSize);
1856 else
1857 if (MATCH_SCSI_CONFIG(uCdbSize))
1858 SCSI_CONFIG_ACCESSOR(uCdbSize);
1859 else
1860 if (MATCH_SCSI_CONFIG(uMaxChannel))
1861 SCSI_CONFIG_ACCESSOR_READONLY(uMaxChannel);
1862 else
1863 if (MATCH_SCSI_CONFIG(uMaxTarget))
1864 SCSI_CONFIG_ACCESSOR_READONLY(uMaxTarget);
1865 else
1866 if (MATCH_SCSI_CONFIG(uMaxLun))
1867 SCSI_CONFIG_ACCESSOR_READONLY(uMaxLun);
1868 else
1869 {
1870 LogFunc(("Bad access by guest to virtio_scsi_config: uoff=%d, cb=%d\n", uOffset, cb));
1871 rc = VERR_ACCESS_DENIED;
1872 }
1873 return rc;
1874}
1875
1876/**
1877 * virtio-scsi VirtIO Device-specific capabilities read callback
1878 * (other VirtIO capabilities and features are handled in VirtIO implementation)
1879 *
1880 * @param pDevIns The device instance.
1881 * @param uOffset Offset within device specific capabilities struct
1882 * @param pv Buffer in which to save read data
1883 * @param cb Number of bytes to read
1884 */
1885static DECLCALLBACK(int) virtioScsiDevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
1886{
1887 int rc = VINF_SUCCESS;
1888 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1889
1890 rc = virtioScsiCfgAccessed(pThis, uOffset, pv, cb, false);
1891
1892 return rc;
1893}
1894
1895/**
1896 * virtio-scsi VirtIO Device-specific capabilities read callback
1897 * (other VirtIO capabilities and features are handled in VirtIO implementation)
1898 *
1899 * @param pDevIns The device instance.
1900 * @param uOffset Offset within device specific capabilities struct
1901 * @param pv Buffer in which to save read data
1902 * @param cb Number of bytes to write
1903 */
1904static DECLCALLBACK(int) virtioScsiDevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
1905{
1906 int rc = VINF_SUCCESS;
1907 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1908
1909 rc = virtioScsiCfgAccessed(pThis, uOffset, pv, cb, true);
1910
1911 return rc;
1912}
1913
1914/**
1915 * Device relocation callback.
1916 *
1917 * When this callback is called the device instance data, and if the
1918 * device have a GC component, is being relocated, or/and the selectors
1919 * have been changed. The device must use the chance to perform the
1920 * necessary pointer relocations and data updates.
1921 *
1922 * Before the GC code is executed the first time, this function will be
1923 * called with a 0 delta so GC pointer calculations can be one in one place.
1924 *
1925 * @param pDevIns Pointer to the device instance.
1926 * @param offDelta The relocation delta relative to the old location.
1927 *
1928 * @remark A relocation CANNOT fail.
1929 */
1930static DECLCALLBACK(void) virtioScsiRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1931{
1932 LogFunc(("Relocating virtio-scsi"));
1933 RT_NOREF(offDelta);
1934 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1935
1936 pThis->pDevInsR3 = pDevIns;
1937
1938 for (uint32_t i = 0; i < pThis->cTargets; i++)
1939 {
1940 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[i];
1941 pTarget->pVirtioScsi = pThis;
1942 }
1943
1944 /*
1945 * Important: Forward to virtio framework!
1946 */
1947 virtioRelocate(pDevIns, offDelta);
1948
1949}
1950
1951static DECLCALLBACK(int) virtioScsiQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
1952 uint32_t *piInstance, uint32_t *piTarget)
1953{
1954 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaPort);
1955 PPDMDEVINS pDevIns = pTarget->pVirtioScsi->CTX_SUFF(pDevIns);
1956
1957 AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
1958 AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
1959 AssertPtrReturn(piTarget, VERR_INVALID_POINTER);
1960
1961 *ppcszController = pDevIns->pReg->szName;
1962 *piInstance = pDevIns->iInstance;
1963 *piTarget = pTarget->iTarget;
1964
1965 return VINF_SUCCESS;
1966}
1967
1968/**
1969 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1970 */
1971static DECLCALLBACK(void *) virtioScsiTargetQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1972{
1973 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IBase);
1974 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pTarget->IBase);
1975 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pTarget->IMediaPort);
1976 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pTarget->IMediaExPort);
1977 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pTarget->ILed);
1978 return NULL;
1979}
1980
1981/**
1982 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1983 */
1984static DECLCALLBACK(void *) virtioScsiDeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1985{
1986 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, IBase);
1987
1988 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
1989 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
1990
1991 return NULL;
1992}
1993
1994/**
1995 * Detach notification.
1996 *
1997 * One harddisk at one port has been unplugged.
1998 * The VM is suspended at this point.
1999 *
2000 * @param pDevIns The device instance.
2001 * @param iTarget The logical unit which is being detached.
2002 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2003 */
2004static DECLCALLBACK(void) virtioScsiDetach(PPDMDEVINS pDevIns, unsigned iTarget, uint32_t fFlags)
2005{
2006 RT_NOREF(fFlags);
2007 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2008 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[iTarget];
2009
2010 LogFunc((""));
2011
2012 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2013 ("virtio-scsi: Device does not support hotplugging\n"));
2014
2015 /*
2016 * Zero some important members.
2017 */
2018 pTarget->fPresent = false;
2019 pTarget->pDrvBase = NULL;
2020}
2021
2022/**
2023 * Attach command.
2024 *
2025 * This is called when we change block driver.
2026 *
2027 * @returns VBox status code.
2028 * @param pDevIns The device instance.
2029 * @param iTarget The logical unit which is being detached.
2030 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2031 */
2032static DECLCALLBACK(int) virtioScsiAttach(PPDMDEVINS pDevIns, unsigned iTarget, uint32_t fFlags)
2033{
2034 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2035 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[iTarget];
2036 int rc;
2037
2038 pThis->pDevInsR3 = pDevIns;
2039 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
2040 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
2041
2042 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2043 ("virtio-scsi: Device does not support hotplugging\n"),
2044 VERR_INVALID_PARAMETER);
2045
2046 /* the usual paranoia */
2047 AssertRelease(!pTarget->pDrvBase);
2048 Assert(pTarget->iTarget == iTarget);
2049
2050 /*
2051 * Try attach the SCSI driver and get the interfaces,
2052 * required as well as optional.
2053 */
2054 rc = PDMDevHlpDriverAttach(pDevIns, pTarget->iTarget, &pDevIns->IBase,
2055 &pTarget->pDrvBase, (const char *)&pTarget->pszTargetName);
2056 if (RT_SUCCESS(rc))
2057 pTarget->fPresent = true;
2058 else
2059 AssertMsgFailed(("Failed to attach %s. rc=%Rrc\n", pTarget->pszTargetName, rc));
2060
2061 if (RT_FAILURE(rc))
2062 {
2063 pTarget->fPresent = false;
2064 pTarget->pDrvBase = NULL;
2065 }
2066 return rc;
2067}
2068
2069static DECLCALLBACK(int) virtioScsiDestruct(PPDMDEVINS pDevIns)
2070{
2071 /*
2072 * Check the versions here as well since the destructor is *always* called.
2073 */
2074
2075 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
2076
2077 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2078
2079 RTMemFree(pThis->paTargetInstances);
2080 pThis->paTargetInstances = NULL;
2081 for (int qIdx = 0; qIdx < VIRTIOSCSI_QUEUE_CNT; qIdx++)
2082 {
2083 PWORKER pWorker = &pThis->aWorker[qIdx];
2084 if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
2085 {
2086 SUPSemEventClose(pThis->pSupDrvSession, pWorker->hEvtProcess);
2087 pWorker->hEvtProcess = NIL_SUPSEMEVENT;
2088 }
2089 }
2090 return VINF_SUCCESS;
2091}
2092
2093static DECLCALLBACK(int) virtioScsiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg){
2094
2095 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2096
2097 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2098 int rc = VINF_SUCCESS;
2099
2100 pThis->pDevInsR3 = pDevIns;
2101 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
2102 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
2103 pThis->pSupDrvSession = PDMDevHlpGetSupDrvSession(pDevIns);
2104
2105 LogFunc(("PDM device instance: %d\n", iInstance));
2106 RTStrPrintf((char *)pThis->szInstance, sizeof(pThis->szInstance), "VIRTIOSCSI%d", iInstance);
2107
2108 /* Usable defaults */
2109 pThis->cTargets = 1;
2110
2111 /*
2112 * Validate and read configuration.
2113 */
2114 if (!CFGMR3AreValuesValid(pCfg,"NumTargets\0"
2115 "Bootable\0"
2116 /* "GCEnabled\0" TBD */
2117 /* "R0Enabled\0" TBD */
2118 ))
2119 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
2120 N_("virtio-scsi configuration error: unknown option specified"));
2121
2122 rc = CFGMR3QueryIntegerDef(pCfg, "NumTargets", &pThis->cTargets, true);
2123 if (RT_FAILURE(rc))
2124 return PDMDEV_SET_ERROR(pDevIns, rc,
2125 N_("virtio-scsi configuration error: failed to read NumTargets as integer"));
2126 LogFunc(("NumTargets=%d\n", pThis->cTargets));
2127
2128 rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &pThis->fBootable, true);
2129 if (RT_FAILURE(rc))
2130 return PDMDEV_SET_ERROR(pDevIns, rc,
2131 N_("virtio-scsi configuration error: failed to read Bootable as boolean"));
2132 LogFunc(("Bootable=%RTbool (unimplemented)\n", pThis->fBootable));
2133
2134 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, false);
2135 if (RT_FAILURE(rc))
2136 return PDMDEV_SET_ERROR(pDevIns, rc,
2137 N_("virtio-scsi configuration error: failed to read R0Enabled as boolean"));
2138
2139 rc = CFGMR3QueryBoolDef(pCfg, "RCEnabled", &pThis->fRCEnabled, false);
2140 if (RT_FAILURE(rc))
2141 return PDMDEV_SET_ERROR(pDevIns, rc,
2142 N_("virtio-scsi configuration error: failed to read RCEnabled as boolean"));
2143
2144 VIRTIOPCIPARAMS virtioPciParams, *pVirtioPciParams = &virtioPciParams;
2145 pVirtioPciParams->uDeviceId = PCI_DEVICE_ID_VIRTIOSCSI_HOST;
2146 pVirtioPciParams->uClassBase = PCI_CLASS_BASE_MASS_STORAGE;
2147 pVirtioPciParams->uClassSub = PCI_CLASS_SUB_SCSI_STORAGE_CONTROLLER;
2148 pVirtioPciParams->uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
2149 pVirtioPciParams->uSubsystemId = PCI_DEVICE_ID_VIRTIOSCSI_HOST; /* Virtio 1.0 spec allows PCI Device ID here */
2150 pVirtioPciParams->uInterruptLine = 0x00;
2151 pVirtioPciParams->uInterruptPin = 0x01;
2152
2153 pThis->IBase.pfnQueryInterface = virtioScsiDeviceQueryInterface;
2154
2155 /* Configure virtio_scsi_config that transacts via VirtIO implementation's Dev. Specific Cap callbacks */
2156 pThis->virtioScsiConfig.uNumQueues = VIRTIOSCSI_REQ_QUEUE_CNT;
2157 pThis->virtioScsiConfig.uSegMax = VIRTIOSCSI_MAX_SEG_COUNT;
2158 pThis->virtioScsiConfig.uMaxSectors = VIRTIOSCSI_MAX_SECTORS_HINT;
2159 pThis->virtioScsiConfig.uCmdPerLun = VIRTIOSCSI_MAX_COMMANDS_PER_LUN;
2160 pThis->virtioScsiConfig.uEventInfoSize = sizeof(VIRTIOSCSI_EVENT_T); /* Spec says at least this size! */
2161 pThis->virtioScsiConfig.uSenseSize = VIRTIOSCSI_SENSE_SIZE_DEFAULT;
2162 pThis->virtioScsiConfig.uCdbSize = VIRTIOSCSI_CDB_SIZE_DEFAULT;
2163 pThis->virtioScsiConfig.uMaxChannel = VIRTIOSCSI_MAX_CHANNEL_HINT;
2164 pThis->virtioScsiConfig.uMaxTarget = pThis->cTargets;
2165 pThis->virtioScsiConfig.uMaxLun = VIRTIOSCSI_MAX_LUN;
2166
2167 rc = virtioConstruct(pDevIns, pThis, &pThis->hVirtio, pVirtioPciParams, pThis->szInstance,
2168 VIRTIOSCSI_HOST_SCSI_FEATURES_OFFERED,
2169 virtioScsiDevCapRead,
2170 virtioScsiDevCapWrite,
2171 virtioScsiStatusChanged,
2172 virtioScsiNotified,
2173 virtioScsiLiveExec,
2174 virtioScsiSaveExec,
2175 virtioScsiLoadExec,
2176 virtioScsiLoadDone,
2177 sizeof(VIRTIOSCSI_CONFIG_T) /* cbDevSpecificCap */,
2178 (void *)&pThis->virtioScsiConfig /* pDevSpecificCap */);
2179
2180 if (RT_FAILURE(rc))
2181 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi: failed to initialize VirtIO"));
2182
2183 RTStrCopy((char *)pThis->szQueueNames[CONTROLQ_IDX], VIRTIO_MAX_QUEUE_NAME_SIZE, "controlq");
2184 RTStrCopy((char *)pThis->szQueueNames[EVENTQ_IDX], VIRTIO_MAX_QUEUE_NAME_SIZE, "eventq");
2185 for (uint16_t qIdx = VIRTQ_REQ_BASE; qIdx < VIRTQ_REQ_BASE + VIRTIOSCSI_REQ_QUEUE_CNT; qIdx++)
2186 RTStrPrintf((char *)pThis->szQueueNames[qIdx], VIRTIO_MAX_QUEUE_NAME_SIZE,
2187 "requestq<%d>", qIdx - VIRTQ_REQ_BASE);
2188
2189 for (uint16_t qIdx = 0; qIdx < VIRTIOSCSI_QUEUE_CNT; qIdx++)
2190 {
2191 rc = virtioQueueAttach(pThis->hVirtio, qIdx, QUEUENAME(qIdx));
2192 AssertMsgReturn(rc == VINF_SUCCESS, ("Failed to attach queue %s\n", QUEUENAME(qIdx)), rc);
2193 pThis->fQueueAttached[qIdx] = (rc == VINF_SUCCESS);
2194
2195 if (qIdx == CONTROLQ_IDX || IS_REQ_QUEUE(qIdx))
2196 {
2197 rc = PDMDevHlpThreadCreate(pDevIns, &pThis->aWorker[qIdx].pThread,
2198 (void *)(uint64_t)qIdx, virtioScsiWorker,
2199 virtioScsiWorkerWakeUp, 0, RTTHREADTYPE_IO, QUEUENAME(qIdx));
2200 if (rc != VINF_SUCCESS)
2201 {
2202 LogRel(("Error creating thread for Virtual Queue %s\n", QUEUENAME(qIdx)));
2203 return rc;
2204 }
2205
2206 rc = SUPSemEventCreate(pThis->pSupDrvSession, &pThis->aWorker[qIdx].hEvtProcess);
2207 if (RT_FAILURE(rc))
2208 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2209 N_("DevVirtioSCSI: Failed to create SUP event semaphore"));
2210 }
2211 }
2212
2213#ifdef BOOTABLE_SUPPORT_TBD
2214 if (fBootable)
2215 {
2216 /* Register I/O port space for BIOS access. */
2217 rc = PDMDevHlpIOPortRegister(pDevIns, VIRTIOSCSI_BIOS_IO_PORT, 4, NULL,
2218 virtioScsiBiosIoPortWrite, virtioScsiBiosIoPortRead,
2219 virtioScsiBiosIoPortWriteStr, virtioScsiBiosIoPortReadStr,
2220 "virtio-scsi BIOS");
2221 if (RT_FAILURE(rc))
2222 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi cannot register BIOS I/O handlers"));
2223 }
2224#endif
2225
2226 /* Initialize per device instance. */
2227
2228 Log2Func(("Found %d targets attached to controller\n", pThis->cTargets));
2229
2230 pThis->paTargetInstances = (PVIRTIOSCSITARGET)RTMemAllocZ(sizeof(VIRTIOSCSITARGET) * pThis->cTargets);
2231 if (!pThis->paTargetInstances)
2232 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to allocate memory for target states"));
2233
2234 for (RTUINT iTarget = 0; iTarget < pThis->cTargets; iTarget++)
2235 {
2236 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[iTarget];
2237
2238 if (RTStrAPrintf(&pTarget->pszTargetName, "VSCSI%u", iTarget) < 0)
2239 AssertLogRelFailedReturn(VERR_NO_MEMORY);
2240
2241 /* Initialize static parts of the device. */
2242 pTarget->iTarget = iTarget;
2243 pTarget->pVirtioScsi = pThis;
2244 pTarget->led.u32Magic = PDMLED_MAGIC;
2245
2246 pTarget->IBase.pfnQueryInterface = virtioScsiTargetQueryInterface;
2247
2248 /* IMediaPort and IMediaExPort interfaces provide callbacks for VD media and downstream driver access */
2249 pTarget->IMediaPort.pfnQueryDeviceLocation = virtioScsiQueryDeviceLocation;
2250 pTarget->IMediaExPort.pfnIoReqCompleteNotify = virtioScsiIoReqFinish;
2251 pTarget->IMediaExPort.pfnIoReqCopyFromBuf = virtioScsiIoReqCopyFromBuf;
2252 pTarget->IMediaExPort.pfnIoReqCopyToBuf = virtioScsiIoReqCopyToBuf;
2253 pTarget->IMediaExPort.pfnIoReqStateChanged = virtioScsiIoReqStateChanged;
2254 pTarget->IMediaExPort.pfnMediumEjected = virtioScsiMediumEjected;
2255 pTarget->IMediaExPort.pfnIoReqQueryBuf = NULL; /* When used avoids copyFromBuf CopyToBuf*/
2256 pTarget->IMediaExPort.pfnIoReqQueryDiscardRanges = NULL;
2257
2258
2259 pTarget->IBase.pfnQueryInterface = virtioScsiTargetQueryInterface;
2260 pTarget->ILed.pfnQueryStatusLed = virtioScsiTargetQueryStatusLed;
2261 pThis->ILeds.pfnQueryStatusLed = virtioScsiDeviceQueryStatusLed;
2262 pTarget->led.u32Magic = PDMLED_MAGIC;
2263
2264 LogFunc(("Attaching LUN: %s\n", pTarget->pszTargetName));
2265
2266 AssertReturn(iTarget < pThis->cTargets, VERR_PDM_NO_SUCH_LUN);
2267 rc = PDMDevHlpDriverAttach(pDevIns, iTarget, &pTarget->IBase, &pTarget->pDrvBase, pTarget->pszTargetName);
2268 if (RT_SUCCESS(rc))
2269 {
2270 pTarget->fPresent = true;
2271
2272 pTarget->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIA);
2273 AssertMsgReturn(VALID_PTR(pTarget->pDrvMedia),
2274 ("virtio-scsi configuration error: LUN#%d missing basic media interface!\n", iTarget),
2275 VERR_PDM_MISSING_INTERFACE);
2276
2277 /* Get the extended media interface. */
2278 pTarget->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIAEX);
2279 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
2280 ("virtio-scsi configuration error: LUN#%d missing extended media interface!\n", iTarget),
2281 VERR_PDM_MISSING_INTERFACE);
2282
2283 rc = pTarget->pDrvMediaEx->pfnIoReqAllocSizeSet(pTarget->pDrvMediaEx, sizeof(VIRTIOSCSIREQ));
2284 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
2285 ("virtio-scsi configuration error: LUN#%u: Failed to set I/O request size!\n", iTarget),
2286 rc);
2287
2288 pTarget->pMediaNotify = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIANOTIFY);
2289 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
2290 ("virtio-scsi configuration error: LUN#%u: Failed to get set Media notify obj!\n",
2291 iTarget), rc);
2292
2293 }
2294 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2295 {
2296 pTarget->fPresent = false;
2297 pTarget->pDrvBase = NULL;
2298 rc = VINF_SUCCESS;
2299 Log(("virtio-scsi: no driver attached to device %s\n", pTarget->pszTargetName));
2300 }
2301 else
2302 {
2303 AssertLogRelMsgFailed(("virtio-scsi: Failed to attach %s: %Rrc\n", pTarget->pszTargetName, rc));
2304 return rc;
2305 }
2306 }
2307
2308 /* Status driver */
2309 PPDMIBASE pUpBase;
2310 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pUpBase, "Status Port");
2311 if (RT_FAILURE(rc))
2312 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
2313
2314 /*
2315 * Register the debugger info callback.
2316 */
2317 char szTmp[128];
2318 RTStrPrintf(szTmp, sizeof(szTmp), "%s%d", pDevIns->pReg->szName, pDevIns->iInstance);
2319 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "virtio-scsi info", virtioScsiInfo);
2320
2321 return rc;
2322}
2323
2324/**
2325 * The device registration structure.
2326 */
2327const PDMDEVREG g_DeviceVirtioSCSI =
2328{
2329 /* .u32Version = */ PDM_DEVREG_VERSION,
2330 /* .uReserved0 = */ 0,
2331 /* .szName = */ "virtio-scsi",
2332#ifdef VIRTIOSCSI_GC_SUPPORT
2333 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0
2334 | PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION
2335 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
2336#else
2337 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS
2338 | PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION
2339 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
2340#endif
2341 /* .fClass = */ PDM_DEVREG_CLASS_MISC,
2342 /* .cMaxInstances = */ ~0U,
2343 /* .uSharedVersion = */ 42,
2344 /* .cbInstanceShared = */ sizeof(VIRTIOSCSI),
2345 /* .cbInstanceCC = */ 0,
2346 /* .cbInstanceRC = */ 0,
2347 /* .cMaxPciDevices = */ 1,
2348 /* .cMaxMsixVectors = */ 0,
2349 /* .pszDescription = */ "Virtio Host SCSI.\n",
2350#if defined(IN_RING3)
2351 /* .pszRCMod = */ "VBoxDDRC.rc",
2352 /* .pszR0Mod = */ "VBoxDDR0.r0",
2353 /* .pfnConstruct = */ virtioScsiConstruct,
2354 /* .pfnDestruct = */ virtioScsiDestruct,
2355 /* .pfnRelocate = */ virtioScsiRelocate,
2356 /* .pfnMemSetup = */ NULL,
2357 /* .pfnPowerOn = */ NULL,
2358 /* .pfnReset = */ virtioScsiReset,
2359 /* .pfnSuspend = */ virtioScsiSuspendOrPoweroff,
2360 /* .pfnResume = */ virtioScsiResume,
2361 /* .pfnAttach = */ virtioScsiAttach,
2362 /* .pfnDetach = */ virtioScsiDetach,
2363 /* .pfnQueryInterface = */ NULL,
2364 /* .pfnInitComplete = */ NULL,
2365 /* .pfnPowerOff = */ virtioScsiSuspendOrPoweroff,
2366 /* .pfnSoftReset = */ NULL,
2367 /* .pfnReserved0 = */ NULL,
2368 /* .pfnReserved1 = */ NULL,
2369 /* .pfnReserved2 = */ NULL,
2370 /* .pfnReserved3 = */ NULL,
2371 /* .pfnReserved4 = */ NULL,
2372 /* .pfnReserved5 = */ NULL,
2373 /* .pfnReserved6 = */ NULL,
2374 /* .pfnReserved7 = */ NULL,
2375#elif defined(IN_RING0)
2376 /* .pfnEarlyConstruct = */ NULL,
2377 /* .pfnConstruct = */ NULL,
2378 /* .pfnDestruct = */ NULL,
2379 /* .pfnFinalDestruct = */ NULL,
2380 /* .pfnRequest = */ NULL,
2381 /* .pfnReserved0 = */ NULL,
2382 /* .pfnReserved1 = */ NULL,
2383 /* .pfnReserved2 = */ NULL,
2384 /* .pfnReserved3 = */ NULL,
2385 /* .pfnReserved4 = */ NULL,
2386 /* .pfnReserved5 = */ NULL,
2387 /* .pfnReserved6 = */ NULL,
2388 /* .pfnReserved7 = */ NULL,
2389#elif defined(IN_RC)
2390 /* .pfnConstruct = */ NULL,
2391 /* .pfnReserved0 = */ NULL,
2392 /* .pfnReserved1 = */ NULL,
2393 /* .pfnReserved2 = */ NULL,
2394 /* .pfnReserved3 = */ NULL,
2395 /* .pfnReserved4 = */ NULL,
2396 /* .pfnReserved5 = */ NULL,
2397 /* .pfnReserved6 = */ NULL,
2398 /* .pfnReserved7 = */ NULL,
2399#else
2400# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2401#endif
2402 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2403};
2404
Note: See TracBrowser for help on using the repository browser.

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