VirtualBox

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

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

DevVirtioSCSI: Only 7-bit ASCII if possible.

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

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