VirtualBox

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

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

Virtio_1_0,DevVirtioScsi: s/virtio/virtioCore/ + s/VIRTIOSTATE/VIRTIOCORE/. bugref:9218 bugref:9440

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

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