VirtualBox

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

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

Fix comment

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

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