VirtualBox

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

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

Network/DevVirtioNet_1_0.cpp: Fixed R0->R3 degradation in I/O path by implementing R0 MMIO, also allows worker thread synchronization via notification from guest to run in R0. Fixed bug where wasn't properly detecting when guest suppressed interrupts, and various minor fixes. See bugref:8651, Comment #70

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

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