VirtualBox

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

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

Virtio_1_0,DevVirtioScsi: Started converting to new PDM model. (untested) bugref:9218

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

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