VirtualBox

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

Last change on this file since 84803 was 84803, checked in by vboxsync, 4 years ago

Network/DevVirtioNet_1_0.cpp: Rename API functions to refer to Virtq instead of Queue to be more Virtio like and disambiguate from other queue types. Temporarily enable build in Config.kmk to let the build machines catch errors

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