VirtualBox

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

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

Storage/DevVirtioSCSI.cpp: Took care of more to do items from bird's review. Worked on suspend logic and state change logic. Restoring after save state is still buggy

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

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