VirtualBox

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

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

DevVirtioSCSI.cpp: Defined and enforced limits on virtioScsiConfig.uCdbSize and uSenseSize. Use stack buffer for the request in virtioScsiR3ReqSubmit, as it cannot be more than 274 bytes big now. bugref:9440

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