VirtualBox

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

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

Copyright year updates by scm.

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

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