VirtualBox

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

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

Fix more windows compiler warnings

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

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