VirtualBox

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

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

Virtio_1_0,DevVirtioScsi: More common virtio code cleanups. bugref:9218 bugref:9440

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 108.2 KB
Line 
1/* $Id: DevVirtioSCSI.cpp 81660 2019-11-04 21:46:54Z vboxsync $ $Revision: 81660 $ $Date: 2019-11-04 21:46:54 +0000 (Mon, 04 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_LUN 256 /**< VirtIO specification, section 5.6.4 */
85#define VIRTIOSCSI_MAX_COMMANDS_PER_LUN 128 /**< T.B.D. What is a good value for this? */
86#define VIRTIOSCSI_MAX_SEG_COUNT 126 /**< T.B.D. What is a good value for this? */
87#define VIRTIOSCSI_MAX_SECTORS_HINT 0x10000 /**< VirtIO specification, section 5.6.4 */
88#define VIRTIOSCSI_MAX_CHANNEL_HINT 0 /**< VirtIO specification, section 5.6.4 should be 0 */
89#define VIRTIOSCSI_SAVED_STATE_MINOR_VERSION 0x01 /**< SSM version # */
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 /** Number of targets detected */
419 uint64_t cTargets;
420
421 R3PTRTYPE(PVIRTIOSCSITARGET) paTargetInstances;
422#if HC_ARCH_BITS == 32
423 RTR3PTR R3PtrPadding0;
424#endif
425
426 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
427 VIRTIOSCSIWORKER aWorkers[VIRTIOSCSI_QUEUE_CNT];
428
429 bool fBootable;
430 bool fRCEnabled;
431 bool fR0Enabled;
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 LOG_ENABLED
535
536DECLINLINE(const char *) virtioGetTxDirText(uint32_t enmTxDir)
537{
538 switch (enmTxDir)
539 {
540 case PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN: return "<UNKNOWN>";
541 case PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE: return "<DEV-TO-GUEST>";
542 case PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE: return "<GUEST-TO-DEV>";
543 case PDMMEDIAEXIOREQSCSITXDIR_NONE: return "<NONE>";
544 default: return "<BAD ENUM>";
545 }
546}
547
548DECLINLINE(const char *) virtioGetTMFTypeText(uint32_t uSubType)
549{
550 switch (uSubType)
551 {
552 case VIRTIOSCSI_T_TMF_ABORT_TASK: return "ABORT TASK";
553 case VIRTIOSCSI_T_TMF_ABORT_TASK_SET: return "ABORT TASK SET";
554 case VIRTIOSCSI_T_TMF_CLEAR_ACA: return "CLEAR ACA";
555 case VIRTIOSCSI_T_TMF_CLEAR_TASK_SET: return "CLEAR TASK SET";
556 case VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET: return "I T NEXUS RESET";
557 case VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET: return "LOGICAL UNIT RESET";
558 case VIRTIOSCSI_T_TMF_QUERY_TASK: return "QUERY TASK";
559 case VIRTIOSCSI_T_TMF_QUERY_TASK_SET: return "QUERY TASK SET";
560 default: return "<unknown>";
561 }
562}
563
564DECLINLINE(const char *) virtioGetReqRespText(uint32_t vboxRc)
565{
566 switch (vboxRc)
567 {
568 case VIRTIOSCSI_S_OK: return "OK/COMPLETE";
569 case VIRTIOSCSI_S_OVERRUN: return "OVERRRUN";
570 case VIRTIOSCSI_S_ABORTED: return "ABORTED";
571 case VIRTIOSCSI_S_BAD_TARGET: return "BAD TARGET";
572 case VIRTIOSCSI_S_RESET: return "RESET";
573 case VIRTIOSCSI_S_TRANSPORT_FAILURE: return "TRANSPORT FAILURE";
574 case VIRTIOSCSI_S_TARGET_FAILURE: return "TARGET FAILURE";
575 case VIRTIOSCSI_S_NEXUS_FAILURE: return "NEXUS FAILURE";
576 case VIRTIOSCSI_S_BUSY: return "BUSY";
577 case VIRTIOSCSI_S_FAILURE: return "FAILURE";
578 case VIRTIOSCSI_S_INCORRECT_LUN: return "INCORRECT LUN";
579 case VIRTIOSCSI_S_FUNCTION_SUCCEEDED: return "FUNCTION SUCCEEDED";
580 case VIRTIOSCSI_S_FUNCTION_REJECTED: return "FUNCTION REJECTED";
581 default: return "<unknown>";
582 }
583}
584
585DECLINLINE(void) virtioGetControlAsyncMaskText(char *pszOutput, uint32_t cbOutput, uint32_t fAsyncTypesMask)
586{
587 RTStrPrintf(pszOutput, cbOutput, "%s%s%s%s%s%s",
588 fAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE ? "CHANGE_OPERATION " : "",
589 fAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_POWER_MGMT ? "POWER_MGMT " : "",
590 fAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST ? "EXTERNAL_REQ " : "",
591 fAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE ? "MEDIA_CHANGE " : "",
592 fAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_MULTI_HOST ? "MULTI_HOST " : "",
593 fAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY ? "DEVICE_BUSY " : "");
594}
595
596static uint8_t virtioScsiEstimateCdbLen(uint8_t uCmd, uint8_t cbMax)
597{
598 if (uCmd < 0x1f)
599 return 6;
600 if (uCmd >= 0x20 && uCmd < 0x60)
601 return 10;
602 if (uCmd >= 0x60 && uCmd < 0x80)
603 return cbMax;
604 if (uCmd >= 0x80 && uCmd < 0xa0)
605 return 16;
606 if (uCmd >= 0xa0 && uCmd < 0xc0)
607 return 12;
608 return cbMax;
609}
610
611#endif /* LOG_ENABLED */
612
613static int virtioScsiR3SendEvent(PVIRTIOSCSI pThis, uint16_t uTarget, uint32_t uEventType, uint32_t uReason)
614{
615
616 VIRTIOSCSI_EVENT_T event;
617 event.uEvent = uEventType;
618 event.uReason = uReason;
619 event.abVirtioLun[0] = 1;
620 event.abVirtioLun[1] = uTarget;
621 event.abVirtioLun[2] = (LUN0 >> 8) & 0x40;
622 event.abVirtioLun[3] = LUN0 & 0xff;
623 event.abVirtioLun[4] = event.abVirtioLun[5] = event.abVirtioLun[6] = event.abVirtioLun[7] = 0;
624
625 /** @todo r=bird: This switch is missing some masking, right? Because 'VIRTIOSCSI_T_NO_EVENT | VIRTIOSCSI_T_EVENTS_MISSED'
626 * will never end up here but be disregarded in the 'default' case. Given that the only caller of this function
627 * is virtioScsiR3ReportEventsMissed(), I find this a bit confusing.
628 *
629 * For the time being I've added a VIRTIOSCSI_T_NO_EVENT | VIRTIOSCSI_T_EVENTS_MISSED case to make the code make sense,
630 * but it migth not be what you had in mind. I've also changed uEventType to fEventType since that's more appropriate. */
631 switch (uEventType)
632 {
633 case VIRTIOSCSI_T_NO_EVENT:
634 Log6Func(("(target=%d, LUN=%d): Warning event info guest queued is shorter than configured\n", uTarget, LUN0));
635 break;
636 case VIRTIOSCSI_T_NO_EVENT | VIRTIOSCSI_T_EVENTS_MISSED:
637 Log6Func(("(target=%d, LUN=%d): Warning driver that events were missed\n", uTarget, LUN0));
638 break;
639 case VIRTIOSCSI_T_TRANSPORT_RESET:
640 switch (uReason)
641 {
642 case VIRTIOSCSI_EVT_RESET_REMOVED:
643 Log6Func(("(target=%d, LUN=%d): Target or LUN removed\n", uTarget, LUN0));
644 break;
645 case VIRTIOSCSI_EVT_RESET_RESCAN:
646 Log6Func(("(target=%d, LUN=%d): Target or LUN added\n", uTarget, LUN0));
647 break;
648 case VIRTIOSCSI_EVT_RESET_HARD:
649 Log6Func(("(target=%d, LUN=%d): Target was reset\n", uTarget, LUN0));
650 break;
651 }
652 break;
653 case VIRTIOSCSI_T_ASYNC_NOTIFY:
654 {
655#ifdef LOG_ENABLED
656 char szTypeText[128];
657 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), uReason);
658 Log6Func(("(target=%d, LUN=%d): Delivering subscribed async notification %s\n", uTarget, LUN0, szTypeText));
659#endif
660 break;
661 }
662 case VIRTIOSCSI_T_PARAM_CHANGE:
663 LogFunc(("(target=%d, LUN=%d): PARAM_CHANGE sense code: 0x%x sense qualifier: 0x%x\n",
664 uTarget, LUN0, uReason & 0xff, (uReason >> 8) & 0xff));
665 break;
666 default:
667 Log6Func(("(target=%d, LUN=%d): Unknown event type: %d, ignoring\n", uTarget, LUN0, uEventType));
668 return VINF_SUCCESS;
669 }
670
671 if (virtioQueueIsEmpty(&pThis->Virtio, EVENTQ_IDX))
672 {
673 LogFunc(("eventq is empty, events missed (driver didn't preload queue)!\n"));
674 ASMAtomicWriteBool(&pThis->fEventsMissed, true);
675 return VINF_SUCCESS;
676 }
677
678 PVIRTIO_DESC_CHAIN_T pDescChain;
679 virtioR3QueueGet(&pThis->Virtio, EVENTQ_IDX, &pDescChain, true);
680
681 RTSGBUF reqSegBuf;
682 RTSGSEG aReqSegs[] = { { &event, sizeof(event) } };
683 RTSgBufInit(&reqSegBuf, aReqSegs, RT_ELEMENTS(aReqSegs));
684
685 virtioR3QueuePut( &pThis->Virtio, EVENTQ_IDX, &reqSegBuf, pDescChain, true);
686 virtioQueueSync(&pThis->Virtio, EVENTQ_IDX);
687
688 return VINF_SUCCESS;
689}
690
691/** Internal worker. */
692static void virtioScsiR3FreeReq(PVIRTIOSCSITARGET pTarget, PVIRTIOSCSIREQ pReq)
693{
694 RTMemFree(pReq->pbSense);
695 pReq->pbSense = NULL;
696 pTarget->pDrvMediaEx->pfnIoReqFree(pTarget->pDrvMediaEx, pReq->hIoReq);
697}
698
699/**
700 * This is called to complete a request immediately
701 *
702 * @param pThis PDM driver instance state
703 * @param qIdx Queue index
704 * @param pDescChain Pointer to pre-processed descriptor chain pulled from virtq
705 * @param pRespHdr Response header
706 * @param pbSense Pointer to sense buffer or NULL if none.
707 *
708 * @returns VBox status code.
709 */
710static int virtioScsiR3ReqErr(PVIRTIOSCSI pThis, uint16_t qIdx, PVIRTIO_DESC_CHAIN_T pDescChain,
711 REQ_RESP_HDR_T *pRespHdr, uint8_t *pbSense)
712{
713 uint8_t *pabSenseBuf = (uint8_t *)RTMemAllocZ(pThis->virtioScsiConfig.uSenseSize);
714 AssertReturn(pabSenseBuf, VERR_NO_MEMORY);
715
716 Log2Func((" status: %s response: %s\n", SCSIStatusText(pRespHdr->uStatus), virtioGetReqRespText(pRespHdr->uResponse)));
717
718 RTSGSEG aReqSegs[2];
719 aReqSegs[0].cbSeg = sizeof(pRespHdr);
720 aReqSegs[0].pvSeg = pRespHdr;
721 aReqSegs[1].cbSeg = pThis->virtioScsiConfig.uSenseSize;
722 aReqSegs[1].pvSeg = pabSenseBuf;
723
724 if (pbSense && pRespHdr->cbSenseLen)
725 memcpy(pabSenseBuf, pbSense, pRespHdr->cbSenseLen);
726 else
727 pRespHdr->cbSenseLen = 0;
728
729 RTSGBUF reqSegBuf;
730 RTSgBufInit(&reqSegBuf, aReqSegs, RT_ELEMENTS(aReqSegs));
731
732 if (pThis->fResetting)
733 pRespHdr->uResponse = VIRTIOSCSI_S_RESET;
734
735 virtioR3QueuePut(&pThis->Virtio, qIdx, &reqSegBuf, pDescChain, true /* fFence */);
736 virtioQueueSync(&pThis->Virtio, qIdx);
737
738 RTMemFree(pabSenseBuf);
739
740 if (!ASMAtomicDecU32(&pThis->cActiveReqs) && pThis->fQuiescing)
741 PDMDevHlpAsyncNotificationCompleted(pThis->CTX_SUFF(pDevIns));
742
743 Log2(("---------------------------------------------------------------------------------\n"));
744
745 return VINF_SUCCESS;
746}
747
748static void virtioScsiR3SenseKeyToVirtioResp(REQ_RESP_HDR_T *respHdr, uint8_t uSenseKey)
749{
750 switch (uSenseKey)
751 {
752 case SCSI_SENSE_ABORTED_COMMAND:
753 respHdr->uResponse = VIRTIOSCSI_S_ABORTED;
754 break;
755 case SCSI_SENSE_COPY_ABORTED:
756 respHdr->uResponse = VIRTIOSCSI_S_ABORTED;
757 break;
758 case SCSI_SENSE_UNIT_ATTENTION:
759 respHdr->uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
760 break;
761 case SCSI_SENSE_HARDWARE_ERROR:
762 respHdr->uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
763 break;
764 case SCSI_SENSE_NOT_READY:
765 respHdr->uResponse = VIRTIOSCSI_S_BUSY; /* e.g. re-tryable */
766 break;
767 default:
768 respHdr->uResponse = VIRTIOSCSI_S_FAILURE;
769 break;
770 }
771}
772
773/**
774 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
775 */
776static DECLCALLBACK(int) virtioScsiR3IoReqFinish(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
777 void *pvIoReqAlloc, int rcReq)
778{
779 RT_NOREF(pInterface);
780
781 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
782 PVIRTIOSCSITARGET pTarget = pReq->pTarget;
783 PVIRTIOSCSI pThis = pTarget->pVirtioScsi;
784 PPDMIMEDIAEX pIMediaEx = pTarget->pDrvMediaEx;
785
786 size_t cbResidual = 0;
787 int rc = pIMediaEx->pfnIoReqQueryResidual(pIMediaEx, hIoReq, &cbResidual);
788 AssertRC(rc);
789
790 size_t cbXfer = 0;
791 rc = pIMediaEx->pfnIoReqQueryXferSize(pIMediaEx, hIoReq, &cbXfer);
792 AssertRC(rc);
793
794 /* Masking used to deal with datatype size differences between APIs (Windows complains otherwise) */
795 Assert(!(cbXfer & 0xffffffff00000000));
796 uint32_t cbXfer32 = cbXfer & 0xffffffff;
797 REQ_RESP_HDR_T respHdr = { 0 };
798 respHdr.cbSenseLen = pReq->pbSense[2] == SCSI_SENSE_NONE ? 0 : (uint32_t)pReq->cbSenseLen;
799 AssertMsg(!(cbResidual & 0xffffffff00000000),
800 ("WARNING: Residual size larger than sizeof(uint32_t), truncating"));
801 respHdr.uResidual = (uint32_t)(cbResidual & 0xffffffff);
802 respHdr.uStatus = pReq->uStatus;
803
804 /* VirtIO 1.0 spec 5.6.6.1.1 says device MUST return a VirtIO response byte value.
805 * Some are returned during the submit phase, and a few are not mapped at all,
806 * wherein anything that can't map specifically gets mapped to VIRTIOSCSI_S_FAILURE
807 */
808 if (pThis->fResetting)
809 respHdr.uResponse = VIRTIOSCSI_S_RESET;
810 else
811 {
812 switch (rcReq)
813 {
814 case SCSI_STATUS_OK:
815 {
816 if (pReq->uStatus != SCSI_STATUS_CHECK_CONDITION)
817 respHdr.uResponse = VIRTIOSCSI_S_OK;
818 else
819 virtioScsiR3SenseKeyToVirtioResp(&respHdr, pReq->pbSense[2]);
820 break;
821 }
822 case SCSI_STATUS_CHECK_CONDITION:
823 virtioScsiR3SenseKeyToVirtioResp(&respHdr, pReq->pbSense[2]);
824 break;
825
826 default:
827 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
828 break;
829 }
830 }
831
832 Log2Func(("status: (%d) %s, response: (%d) %s\n", pReq->uStatus, SCSIStatusText(pReq->uStatus),
833 respHdr.uResponse, virtioGetReqRespText(respHdr.uResponse)));
834
835 if (RT_FAILURE(rcReq))
836 Log2Func(("rcReq: %s\n", RTErrGetDefine(rcReq)));
837
838 if (LogIs3Enabled())
839 {
840 LogFunc(("cbDataIn = %u, cbDataOut = %u (cbIn = %u, cbOut = %u)\n",
841 pReq->cbDataIn, pReq->cbDataOut, pReq->pDescChain->cbPhysReturn, pReq->pDescChain->cbPhysSend));
842 LogFunc(("xfer = %lu, residual = %u\n", cbXfer, cbResidual));
843 LogFunc(("xfer direction: %s, sense written = %d, sense size = %d\n",
844 virtioGetTxDirText(pReq->enmTxDir), respHdr.cbSenseLen, pThis->virtioScsiConfig.uSenseSize));
845 }
846
847 if (respHdr.cbSenseLen && LogIs2Enabled())
848 {
849 LogFunc(("Sense: %s\n", SCSISenseText(pReq->pbSense[2])));
850 LogFunc(("Sense Ext3: %s\n", SCSISenseExtText(pReq->pbSense[12], pReq->pbSense[13])));
851 }
852
853 int cSegs = 0;
854
855 if ( (VIRTIO_IS_IN_DIRECTION(pReq->enmTxDir) && cbXfer32 > pReq->cbDataIn)
856 || (VIRTIO_IS_OUT_DIRECTION(pReq->enmTxDir) && cbXfer32 > pReq->cbDataOut))
857 {
858 Log2Func((" * * * * Data overrun, returning sense\n"));
859 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
860 0, SCSI_SENSE_ILLEGAL_REQUEST, 0, 0, 0, 0, 10, 0, 0, 0 };
861 respHdr.cbSenseLen = sizeof(abSense);
862 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
863 respHdr.uResponse = VIRTIOSCSI_S_OVERRUN;
864 respHdr.uResidual = pReq->cbDataIn;
865
866 virtioScsiR3ReqErr(pThis, pReq->qIdx, pReq->pDescChain, &respHdr, abSense);
867 }
868 else
869 {
870 Assert(pReq->pbSense != NULL);
871
872 /* req datain bytes already in guest phys mem. via virtioScsiIoReqCopyFromBuf() */
873
874 RTSGSEG aReqSegs[4];
875 aReqSegs[cSegs].pvSeg = &respHdr;
876 aReqSegs[cSegs++].cbSeg = sizeof(respHdr);
877
878 aReqSegs[cSegs].pvSeg = pReq->pbSense;
879 aReqSegs[cSegs++].cbSeg = pReq->cbSenseAlloc; /* VirtIO 1.0 spec 5.6.4/5.6.6.1 */
880
881 RTSGBUF reqSegBuf;
882 RTSgBufInit(&reqSegBuf, aReqSegs, cSegs);
883
884 size_t cbReqSgBuf = RTSgBufCalcTotalLength(&reqSegBuf);
885 AssertMsgReturn(cbReqSgBuf <= pReq->pDescChain->cbPhysReturn,
886 ("Guest expected less req data (space needed: %d, avail: %d)\n",
887 cbReqSgBuf, pReq->pDescChain->cbPhysReturn),
888 VERR_BUFFER_OVERFLOW);
889
890
891 virtioR3QueuePut(&pThis->Virtio, pReq->qIdx, &reqSegBuf, pReq->pDescChain, true /* fFence TBD */);
892 virtioQueueSync(&pThis->Virtio, pReq->qIdx);
893
894
895 Log2(("-----------------------------------------------------------------------------------------\n"));
896 }
897
898 virtioScsiR3FreeReq(pTarget, pReq);
899
900 if (!ASMAtomicDecU32(&pThis->cActiveReqs) && pThis->fQuiescing)
901 PDMDevHlpAsyncNotificationCompleted(pThis->CTX_SUFF(pDevIns));
902
903 return VINF_SUCCESS;
904}
905
906/**
907 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
908 *
909 * Copy virtual memory from VSCSI layer to guest physical memory
910 */
911static DECLCALLBACK(int) virtioScsiR3IoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
912 void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf, size_t cbCopy)
913{
914 RT_NOREF(hIoReq, cbCopy);
915
916 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
917
918 if (!pReq->cbDataIn)
919 return VINF_SUCCESS;
920
921 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
922 PVIRTIOSCSI pThis = pTarget->pVirtioScsi;
923
924 AssertReturn(pReq->pDescChain, VERR_INVALID_PARAMETER);
925
926 PRTSGBUF pSgPhysReturn = pReq->pDescChain->pSgPhysReturn;
927 RTSgBufAdvance(pSgPhysReturn, offDst);
928
929 size_t cbCopied = 0;
930 size_t cbRemain = pReq->cbDataIn;
931
932 if (!pSgPhysReturn->idxSeg && pSgPhysReturn->cbSegLeft == pSgPhysReturn->paSegs[0].cbSeg)
933 RTSgBufAdvance(pSgPhysReturn, pReq->uDataInOff);
934
935 while (cbRemain)
936 {
937 PCRTSGSEG paSeg = &pSgPhysReturn->paSegs[pSgPhysReturn->idxSeg];
938 uint64_t dstSgStart = (uint64_t)paSeg->pvSeg;
939 uint64_t dstSgLen = (uint64_t)paSeg->cbSeg;
940 uint64_t dstSgCur = (uint64_t)pSgPhysReturn->pvSegCur;
941 cbCopied = RT_MIN((uint64_t)pSgBuf->cbSegLeft, dstSgLen - (dstSgCur - dstSgStart));
942 PDMDevHlpPhysWrite(pThis->CTX_SUFF(pDevIns),
943 (RTGCPHYS)pSgPhysReturn->pvSegCur, pSgBuf->pvSegCur, cbCopied);
944 RTSgBufAdvance(pSgBuf, cbCopied);
945 RTSgBufAdvance(pSgPhysReturn, cbCopied);
946 cbRemain -= cbCopied;
947 }
948 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE(); /* needed? */
949
950 Log2Func((".... Copied %lu bytes from %lu byte guest buffer, residual=%lu\n",
951 cbCopy, pReq->pDescChain->cbPhysReturn, pReq->pDescChain->cbPhysReturn - cbCopy));
952
953 return VINF_SUCCESS;
954}
955
956/**
957 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
958 *
959 * Copy guest physical memory to VSCSI layer virtual memory
960 */
961static DECLCALLBACK(int) virtioScsiR3IoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
962 void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf, size_t cbCopy)
963{
964 RT_NOREF(hIoReq, cbCopy);
965
966 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
967
968 if (!pReq->cbDataOut)
969 return VINF_SUCCESS;
970
971 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
972 PVIRTIOSCSI pThis = pTarget->pVirtioScsi;
973
974 PRTSGBUF pSgPhysSend = pReq->pDescChain->pSgPhysSend;
975 RTSgBufAdvance(pSgPhysSend, offSrc);
976
977 size_t cbCopied = 0;
978 size_t cbRemain = pReq->cbDataOut;
979 while (cbRemain)
980 {
981 PCRTSGSEG paSeg = &pSgPhysSend->paSegs[pSgPhysSend->idxSeg];
982 uint64_t srcSgStart = (uint64_t)paSeg->pvSeg;
983 uint64_t srcSgLen = (uint64_t)paSeg->cbSeg;
984 uint64_t srcSgCur = (uint64_t)pSgPhysSend->pvSegCur;
985 cbCopied = RT_MIN((uint64_t)pSgBuf->cbSegLeft, srcSgLen - (srcSgCur - srcSgStart));
986 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns),
987 (RTGCPHYS)pSgPhysSend->pvSegCur, pSgBuf->pvSegCur, cbCopied);
988 RTSgBufAdvance(pSgBuf, cbCopied);
989 RTSgBufAdvance(pSgPhysSend, cbCopied);
990 cbRemain -= cbCopied;
991 }
992
993 Log2Func((".... Copied %lu bytes to %lu byte guest buffer, residual=%lu\n",
994 cbCopy, pReq->pDescChain->cbPhysReturn, pReq->pDescChain->cbPhysReturn - cbCopy));
995
996 return VINF_SUCCESS;
997}
998
999/**
1000 * @interface_method_impl{PDMIMEDIAEXPORT,pfnMediumEjected}
1001 */
1002static DECLCALLBACK(void) virtioScsiR3MediumEjected(PPDMIMEDIAEXPORT pInterface)
1003{
1004 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
1005 PVIRTIOSCSI pThis = pTarget->pVirtioScsi;
1006 LogFunc(("LUN %d Ejected!\n", pTarget->iTarget));
1007 if (pThis->pMediaNotify)
1008 {
1009 int rc = VMR3ReqCallNoWait(PDMDevHlpGetVM(pThis->CTX_SUFF(pDevIns)), VMCPUID_ANY,
1010 (PFNRT)pThis->pMediaNotify->pfnEjected, 2,
1011 pThis->pMediaNotify, pTarget->iTarget);
1012 AssertRC(rc);
1013 }
1014}
1015
1016/**
1017 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
1018 */
1019static DECLCALLBACK(void) virtioScsiR3IoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1020 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
1021{
1022 RT_NOREF4(pInterface, hIoReq, pvIoReqAlloc, enmState);
1023 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, IBase);
1024
1025 switch (enmState)
1026 {
1027 case PDMMEDIAEXIOREQSTATE_SUSPENDED:
1028 {
1029 /* Stop considering this request active */
1030 if (!ASMAtomicDecU32(&pThis->cActiveReqs) && pThis->fQuiescing)
1031 PDMDevHlpAsyncNotificationCompleted(pThis->CTX_SUFF(pDevIns));
1032 break;
1033 }
1034 case PDMMEDIAEXIOREQSTATE_ACTIVE:
1035 ASMAtomicIncU32(&pThis->cActiveReqs);
1036 break;
1037 default:
1038 AssertMsgFailed(("Invalid request state given %u\n", enmState));
1039 }
1040}
1041
1042
1043/*********************************************************************************************************************************
1044* Worker Thread *
1045*********************************************************************************************************************************/
1046
1047/**
1048 * Handles request queues for/on a worker thread.
1049 *
1050 * @returns VBox status code (logged by caller).
1051 */
1052static int virtioScsiR3ReqSubmit(PVIRTIOSCSI pThis, uint16_t qIdx, PVIRTIO_DESC_CHAIN_T pDescChain)
1053{
1054 AssertReturn(pDescChain->cbPhysSend, VERR_INVALID_PARAMETER);
1055
1056 ASMAtomicIncU32(&pThis->cActiveReqs);
1057
1058 /*
1059 * Extract command header and CDB from guest physical memory
1060 */
1061 size_t cbReqHdr = sizeof(REQ_CMD_HDR_T) + pThis->virtioScsiConfig.uCdbSize;
1062 PVIRTIOSCSI_REQ_CMD_T pVirtqReq = (PVIRTIOSCSI_REQ_CMD_T)RTMemAllocZ(cbReqHdr);
1063 AssertReturn(pVirtqReq, VERR_NO_MEMORY);
1064 uint8_t *pb = (uint8_t *)pVirtqReq;
1065 for (size_t cb = RT_MIN(pDescChain->cbPhysSend, cbReqHdr); cb; )
1066 {
1067 size_t cbSeg = cb;
1068 RTGCPHYS GCPhys = (RTGCPHYS)RTSgBufGetNextSegment(pDescChain->pSgPhysSend, &cbSeg);
1069 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCPhys, pb, cbSeg);
1070 pb += cbSeg;
1071 cb -= cbSeg;
1072 }
1073
1074 uint8_t uTarget = pVirtqReq->ReqHdr.abVirtioLun[1];
1075 uint32_t uScsiLun = (pVirtqReq->ReqHdr.abVirtioLun[2] << 8 | pVirtqReq->ReqHdr.abVirtioLun[3]) & 0x3fff;
1076 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[uTarget];
1077
1078 LogFunc(("[%s] (Target: %d LUN: %d) CDB: %.*Rhxs\n",
1079 SCSICmdText(pVirtqReq->uCdb[0]), uTarget, uScsiLun,
1080 virtioScsiEstimateCdbLen(pVirtqReq->uCdb[0], pThis->virtioScsiConfig.uCdbSize), pVirtqReq->uCdb));
1081
1082 Log3Func(("cmd id: %RX64, attr: %x, prio: %d, crn: %x\n",
1083 pVirtqReq->ReqHdr.uId, pVirtqReq->ReqHdr.uTaskAttr, pVirtqReq->ReqHdr.uPrio, pVirtqReq->ReqHdr.uCrn));
1084
1085 /*
1086 * Calculate request offsets
1087 */
1088 off_t uDataOutOff = sizeof(REQ_CMD_HDR_T) + pThis->virtioScsiConfig.uCdbSize;
1089 off_t uDataInOff = sizeof(REQ_RESP_HDR_T) + pThis->virtioScsiConfig.uSenseSize;
1090 uint32_t cbDataOut = pDescChain->cbPhysSend - uDataOutOff;
1091 uint32_t cbDataIn = pDescChain->cbPhysReturn - uDataInOff;
1092 /*
1093 * Handle submission errors
1094 */
1095
1096 if (RT_UNLIKELY(pThis->fResetting))
1097 {
1098 Log2Func(("Aborting req submission because reset is in progress\n"));
1099 REQ_RESP_HDR_T respHdr = { 0 };
1100 respHdr.cbSenseLen = 0;
1101 respHdr.uStatus = SCSI_STATUS_OK;
1102 respHdr.uResponse = VIRTIOSCSI_S_RESET;
1103 respHdr.uResidual = cbDataIn + cbDataOut;
1104 virtioScsiR3ReqErr(pThis, qIdx, pDescChain, &respHdr, NULL);
1105 RTMemFree(pVirtqReq);
1106 return VINF_SUCCESS;
1107 }
1108 if (RT_UNLIKELY(uScsiLun != 0))
1109 {
1110 Log2Func(("Error submitting request to bad target (%d) or bad LUN (%d)\n", uTarget, uScsiLun));
1111 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1112 0, SCSI_SENSE_ILLEGAL_REQUEST,
1113 0, 0, 0, 0, 10, SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED, 0, 0 };
1114 REQ_RESP_HDR_T respHdr = { 0 };
1115 respHdr.cbSenseLen = sizeof(abSense);
1116 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1117 respHdr.uResponse = VIRTIOSCSI_S_OK;
1118 respHdr.uResidual = cbDataOut + cbDataIn;
1119 virtioScsiR3ReqErr(pThis, qIdx, pDescChain, &respHdr, abSense);
1120 RTMemFree(pVirtqReq);
1121 return VINF_SUCCESS;
1122 }
1123 if (RT_UNLIKELY(uTarget >= pThis->cTargets || !pTarget->fPresent))
1124 {
1125 Log2Func(("Error submitting request, target not present!!\n"));
1126 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1127 0, SCSI_SENSE_NOT_READY, 0, 0, 0, 0, 10, 0, 0, 0 };
1128 REQ_RESP_HDR_T respHdr = { 0 };
1129 respHdr.cbSenseLen = sizeof(abSense);
1130 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1131 respHdr.uResponse = VIRTIOSCSI_S_BAD_TARGET;
1132 respHdr.uResidual = cbDataIn + cbDataOut;
1133 virtioScsiR3ReqErr(pThis, qIdx, pDescChain, &respHdr , abSense);
1134 RTMemFree(pVirtqReq);
1135 return VINF_SUCCESS;
1136 }
1137 if (RT_UNLIKELY(cbDataIn && cbDataOut && !pThis->fHasInOutBufs)) /* VirtIO 1.0, 5.6.6.1.1 */
1138 {
1139 Log2Func(("Error submitting request, got datain & dataout bufs w/o INOUT feature negotated\n"));
1140 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1141 0, SCSI_SENSE_ILLEGAL_REQUEST, 0, 0, 0, 0, 10, 0, 0, 0 };
1142 REQ_RESP_HDR_T respHdr = { 0 };
1143 respHdr.cbSenseLen = sizeof(abSense);
1144 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1145 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
1146 respHdr.uResidual = cbDataIn + cbDataOut;
1147 virtioScsiR3ReqErr(pThis, qIdx, pDescChain, &respHdr , abSense);
1148 RTMemFree(pVirtqReq);
1149 return VINF_SUCCESS;
1150 }
1151
1152 /*
1153 * Have underlying driver allocate a req of size set during initialization of this device.
1154 */
1155 PDMMEDIAEXIOREQ hIoReq = NULL;
1156 PVIRTIOSCSIREQ pReq;
1157 PPDMIMEDIAEX pIMediaEx = pTarget->pDrvMediaEx;
1158
1159 int rc = pIMediaEx->pfnIoReqAlloc(pIMediaEx, &hIoReq, (void **)&pReq, 0 /* uIoReqId */,
1160 PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR);
1161
1162 if (RT_FAILURE(rc))
1163 {
1164 RTMemFree(pVirtqReq);
1165 /** @todo r=bird: This *will* crash: virtioScsiR3FreeReq(pTarget, NULL); */
1166 AssertMsgRCReturn(rc, ("Failed to allocate I/O request, rc=%Rrc\n", rc), rc);
1167 }
1168
1169 pReq->hIoReq = hIoReq;
1170 pReq->pTarget = pTarget;
1171 pReq->qIdx = qIdx;
1172 pReq->cbDataIn = cbDataIn;
1173 pReq->cbDataOut = cbDataOut;
1174 pReq->pDescChain = pDescChain;
1175 pReq->uDataInOff = uDataInOff;
1176 pReq->uDataOutOff = uDataOutOff;
1177
1178 pReq->cbSenseAlloc = pThis->virtioScsiConfig.uSenseSize;
1179 pReq->pbSense = (uint8_t *)RTMemAllocZ(pReq->cbSenseAlloc);
1180 AssertMsgReturnStmt(pReq->pbSense, ("Out of memory allocating sense buffer"),
1181 virtioScsiR3FreeReq(pTarget, pReq); RTMemFree(pVirtqReq), VERR_NO_MEMORY);
1182
1183 /* Note: DrvSCSI allocates one virtual memory buffer for input and output phases of the request */
1184 rc = pIMediaEx->pfnIoReqSendScsiCmd(pIMediaEx, pReq->hIoReq, uScsiLun,
1185 pVirtqReq->uCdb, (size_t)pThis->virtioScsiConfig.uCdbSize,
1186 PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN, &pReq->enmTxDir,
1187 RT_MAX(cbDataIn, cbDataOut),
1188 pReq->pbSense, pReq->cbSenseAlloc, &pReq->cbSenseLen,
1189 &pReq->uStatus, RT_MS_30SEC);
1190
1191 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
1192 {
1193 /*
1194 * Getting here means the request failed in early in the submission to the lower level driver,
1195 * and there will be no callback to the finished/completion function for this request
1196 */
1197 Assert(RT_FAILURE_NP(rc));
1198 Log2Func(("Request submission error from lower-level driver\n"));
1199 uint8_t uASC, uASCQ = 0;
1200 switch (rc)
1201 {
1202 case VERR_NO_MEMORY:
1203 uASC = SCSI_ASC_SYSTEM_RESOURCE_FAILURE;
1204 break;
1205 default:
1206 uASC = SCSI_ASC_INTERNAL_TARGET_FAILURE;
1207 break;
1208 }
1209 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1210 0, SCSI_SENSE_VENDOR_SPECIFIC,
1211 0, 0, 0, 0, 10, uASC, uASCQ, 0 };
1212 REQ_RESP_HDR_T respHdr = { 0 };
1213 respHdr.cbSenseLen = sizeof(abSense);
1214 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1215 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
1216 respHdr.uResidual = cbDataIn + cbDataOut;
1217 virtioScsiR3ReqErr(pThis, qIdx, pDescChain, &respHdr, abSense);
1218 virtioScsiR3FreeReq(pTarget, pReq);
1219 }
1220
1221 RTMemFree(pVirtqReq);
1222 return VINF_SUCCESS;
1223}
1224
1225/**
1226 * Handles control transfers for/on a worker thread.
1227 *
1228 * @returns VBox status code (ignored by the caller).
1229 * @param pThis The device instance data.
1230 * @param qIdx CONTROLQ_IDX
1231 * @param pDescChain Descriptor chain to process.
1232 */
1233static int virtioScsiR3Ctrl(PVIRTIOSCSI pThis, uint16_t qIdx, PVIRTIO_DESC_CHAIN_T pDescChain)
1234{
1235 uint8_t bResponse = VIRTIOSCSI_S_OK;
1236
1237 /*
1238 * Allocate buffer and read in the control request/whatever.
1239 */
1240 /** @todo r=bird: The following may misbehave if the guest is not feeding you
1241 * sufficient data. There are no size checks below or with the caller
1242 * that I can see, and more importantly you're using RTMemAlloc rather
1243 * than RTMemAllocZ here, so you'll get random heap bytes.
1244 *
1245 * I've changed it to RTMemAllocZ so the memory is all zeroed, but you
1246 * need to consider how to deal with incorrectly sized input.
1247 *
1248 * Ditto in virtioScsiR3ReqSubmit.
1249 */
1250 PVIRTIO_SCSI_CTRL_UNION_T pScsiCtrlUnion = (PVIRTIO_SCSI_CTRL_UNION_T)RTMemAllocZ(sizeof(VIRTIO_SCSI_CTRL_UNION_T));
1251 AssertPtrReturn(pScsiCtrlUnion, VERR_NO_MEMORY /*ignored*/);
1252
1253 uint8_t *pb = pScsiCtrlUnion->ab;
1254 for (size_t cb = RT_MIN(pDescChain->cbPhysSend, sizeof(VIRTIO_SCSI_CTRL_UNION_T)); cb; )
1255 {
1256 size_t cbSeg = cb;
1257 /** @todo r=bird: This is ABUSING the RTSgBuf, interchanging host context
1258 * pointers with RTGCPHYS. If we hadn't just dropped 32-bit host
1259 * support, this would have been a serious problem. Now it is just UGLY! */
1260 AssertCompile(sizeof(RTGCPHYS) == sizeof(void *)); /* ASSUMING RTGCPHYS and host pointers are interchangable. (horrible!) */
1261 RTGCPHYS GCPhys = (RTGCPHYS)RTSgBufGetNextSegment(pDescChain->pSgPhysSend, &cbSeg);
1262 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCPhys, pb, cbSeg);
1263 pb += cbSeg;
1264 cb -= cbSeg;
1265 }
1266
1267 /*
1268 * Mask of events to tell guest driver this device supports
1269 * See VirtIO 1.0 specification section 5.6.6.2
1270 */
1271 uint32_t fSubscribedEvents = VIRTIOSCSI_EVT_ASYNC_POWER_MGMT
1272 | VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST
1273 | VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE
1274 | VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY;
1275
1276 RTSGBUF reqSegBuf;
1277
1278
1279 switch (pScsiCtrlUnion->scsiCtrl.uType)
1280 {
1281 case VIRTIOSCSI_T_TMF: /* Task Management Functions */
1282 {
1283 uint8_t uTarget = pScsiCtrlUnion->scsiCtrlTmf.abScsiLun[1];
1284 uint32_t uScsiLun = (pScsiCtrlUnion->scsiCtrlTmf.abScsiLun[2] << 8
1285 | pScsiCtrlUnion->scsiCtrlTmf.abScsiLun[3]) & 0x3fff;
1286 Log2Func(("[%s] (Target: %d LUN: %d) Task Mgt Function: %s\n",
1287 QUEUENAME(qIdx), uTarget, uScsiLun, virtioGetTMFTypeText(pScsiCtrlUnion->scsiCtrlTmf.uSubtype)));
1288
1289 PVIRTIOSCSITARGET pTarget = NULL;
1290 if (uTarget < pThis->cTargets)
1291 pTarget = &pThis->paTargetInstances[uTarget];
1292
1293 if (uTarget >= pThis->cTargets || !pTarget->fPresent)
1294 bResponse = VIRTIOSCSI_S_BAD_TARGET;
1295 else
1296 if (uScsiLun != 0)
1297 bResponse = VIRTIOSCSI_S_INCORRECT_LUN;
1298 else
1299 switch (pScsiCtrlUnion->scsiCtrlTmf.uSubtype)
1300 {
1301 case VIRTIOSCSI_T_TMF_ABORT_TASK:
1302 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1303 break;
1304 case VIRTIOSCSI_T_TMF_ABORT_TASK_SET:
1305 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1306 break;
1307 case VIRTIOSCSI_T_TMF_CLEAR_ACA:
1308 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1309 break;
1310 case VIRTIOSCSI_T_TMF_CLEAR_TASK_SET:
1311 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1312 break;
1313 case VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET:
1314 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1315 break;
1316 case VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET:
1317 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1318 break;
1319 case VIRTIOSCSI_T_TMF_QUERY_TASK:
1320 bResponse = VIRTIOSCSI_S_FUNCTION_REJECTED;
1321 break;
1322 case VIRTIOSCSI_T_TMF_QUERY_TASK_SET:
1323 bResponse = VIRTIOSCSI_S_FUNCTION_REJECTED;
1324 break;
1325 default:
1326 LogFunc(("Unknown TMF type\n"));
1327 bResponse = VIRTIOSCSI_S_FAILURE;
1328 }
1329
1330 RTSGSEG aReqSegs[] = { { &bResponse, sizeof(bResponse) } };
1331 RTSgBufInit(&reqSegBuf, aReqSegs, RT_ELEMENTS(aReqSegs));
1332
1333 break;
1334 }
1335 case VIRTIOSCSI_T_AN_QUERY: /* Guest SCSI driver is querying supported async event notifications */
1336 {
1337
1338 PVIRTIOSCSI_CTRL_AN_T pScsiCtrlAnQuery = &pScsiCtrlUnion->scsiCtrlAsyncNotify;
1339
1340 fSubscribedEvents &= pScsiCtrlAnQuery->fEventsRequested;
1341
1342 uint8_t uTarget = pScsiCtrlAnQuery->abScsiLun[1];
1343 uint32_t uScsiLun = (pScsiCtrlAnQuery->abScsiLun[2] << 8 | pScsiCtrlAnQuery->abScsiLun[3]) & 0x3fff;
1344
1345 PVIRTIOSCSITARGET pTarget = NULL;
1346 if (uTarget < pThis->cTargets)
1347 pTarget = &pThis->paTargetInstances[uTarget];
1348
1349 if (uTarget >= pThis->cTargets || !pTarget->fPresent)
1350 bResponse = VIRTIOSCSI_S_BAD_TARGET;
1351 else
1352 if (uScsiLun != 0)
1353 bResponse = VIRTIOSCSI_S_INCORRECT_LUN;
1354 else
1355 bResponse = VIRTIOSCSI_S_FUNCTION_COMPLETE;
1356
1357#ifdef LOG_ENABLED
1358 if (LogIs2Enabled())
1359 {
1360 char szTypeText[128];
1361 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), pScsiCtrlAnQuery->fEventsRequested);
1362 Log2Func(("[%s] (Target: %d LUN: %d) Async. Notification Query: %s\n",
1363 QUEUENAME(qIdx), uTarget, uScsiLun, szTypeText));
1364 }
1365#endif
1366 RTSGSEG aReqSegs[] = { { &fSubscribedEvents, sizeof(fSubscribedEvents) }, { &bResponse, sizeof(bResponse) } };
1367 RTSgBufInit(&reqSegBuf, aReqSegs, RT_ELEMENTS(aReqSegs));
1368
1369 break;
1370 }
1371 case VIRTIOSCSI_T_AN_SUBSCRIBE: /* Guest SCSI driver is subscribing to async event notification(s) */
1372 {
1373
1374 PVIRTIOSCSI_CTRL_AN_T pScsiCtrlAnSubscribe = &pScsiCtrlUnion->scsiCtrlAsyncNotify;
1375
1376 if (pScsiCtrlAnSubscribe->fEventsRequested & ~SUBSCRIBABLE_EVENTS)
1377 LogFunc(("Unsupported bits in event subscription event mask: %#x\n", pScsiCtrlAnSubscribe->fEventsRequested));
1378
1379 fSubscribedEvents &= pScsiCtrlAnSubscribe->fEventsRequested;
1380 pThis->fAsyncEvtsEnabled = fSubscribedEvents;
1381
1382 uint8_t uTarget = pScsiCtrlAnSubscribe->abScsiLun[1];
1383 uint32_t uScsiLun = (pScsiCtrlAnSubscribe->abScsiLun[2] << 8
1384 | pScsiCtrlAnSubscribe->abScsiLun[3]) & 0x3fff;
1385
1386#ifdef LOG_ENABLED
1387 if (LogIs2Enabled())
1388 {
1389 char szTypeText[128];
1390 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), pScsiCtrlAnSubscribe->fEventsRequested);
1391 Log2Func(("[%s] (Target: %d LUN: %d) Async. Notification Subscribe: %s\n",
1392 QUEUENAME(qIdx), uTarget, uScsiLun, szTypeText));
1393 }
1394#endif
1395
1396 PVIRTIOSCSITARGET pTarget = NULL;
1397 if (uTarget < pThis->cTargets)
1398 pTarget = &pThis->paTargetInstances[uTarget];
1399
1400 if (uTarget >= pThis->cTargets || !pTarget->fPresent)
1401 bResponse = VIRTIOSCSI_S_BAD_TARGET;
1402 else
1403 if (uScsiLun != 0)
1404 bResponse = VIRTIOSCSI_S_INCORRECT_LUN;
1405 else
1406 {
1407 /*
1408 * TBD: Verify correct status code if request mask is only partially fulfillable
1409 * and confirm when to use 'complete' vs. 'succeeded' See VirtIO 1.0 spec section 5.6.6.2
1410 * and read SAM docs*/
1411 if (fSubscribedEvents == pScsiCtrlAnSubscribe->fEventsRequested)
1412 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1413 else
1414 bResponse = VIRTIOSCSI_S_FUNCTION_COMPLETE;
1415 }
1416 RTSGSEG aReqSegs[] = { { &fSubscribedEvents, sizeof(fSubscribedEvents) }, { &bResponse, sizeof(bResponse) } };
1417 RTSgBufInit(&reqSegBuf, aReqSegs, RT_ELEMENTS(aReqSegs));
1418
1419 break;
1420 }
1421 default:
1422 {
1423 LogFunc(("Unknown control type extracted from %s: %u\n", QUEUENAME(qIdx), pScsiCtrlUnion->scsiCtrl.uType));
1424
1425 bResponse = VIRTIOSCSI_S_FAILURE;
1426
1427 RTSGSEG aReqSegs[] = { { &bResponse, sizeof(bResponse) } };
1428 RTSgBufInit(&reqSegBuf, aReqSegs, RT_ELEMENTS(aReqSegs));
1429 }
1430 }
1431
1432 LogFunc(("Response code: %s\n", virtioGetReqRespText(bResponse)));
1433 virtioR3QueuePut( &pThis->Virtio, qIdx, &reqSegBuf, pDescChain, true);
1434 virtioQueueSync(&pThis->Virtio, qIdx);
1435
1436 return VINF_SUCCESS;
1437}
1438
1439/**
1440 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
1441 */
1442static DECLCALLBACK(int) virtioScsiR3WorkerWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1443{
1444 RT_NOREF(pThread);
1445 uint16_t qIdx = ((uint64_t)pThread->pvUser) & 0xffff;
1446 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
1447 return SUPSemEventSignal(pThis->pSupDrvSession, pThis->aWorkers[qIdx].hEvtProcess);
1448}
1449
1450/**
1451 * @callback_method_impl{FNPDMTHREADDEV}
1452 */
1453static DECLCALLBACK(int) virtioScsiR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1454{
1455 uint16_t const qIdx = (uint16_t)(uintptr_t)pThread->pvUser;
1456 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
1457 PVIRTIOSCSIWORKER pWorker = &pThis->aWorkers[qIdx];
1458
1459 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
1460 return VINF_SUCCESS;
1461
1462 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
1463 {
1464 if (virtioQueueIsEmpty(&pThis->Virtio, qIdx))
1465 {
1466 /* Atomic interlocks avoid missing alarm while going to sleep & notifier waking the awoken */
1467 ASMAtomicWriteBool(&pWorker->fSleeping, true);
1468 bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
1469 if (!fNotificationSent)
1470 {
1471 Log6Func(("%s worker sleeping...\n", QUEUENAME(qIdx)));
1472 Assert(ASMAtomicReadBool(&pWorker->fSleeping));
1473 int rc = SUPSemEventWaitNoResume(pThis->pSupDrvSession, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
1474 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
1475 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
1476 return VINF_SUCCESS;
1477 Log6Func(("%s worker woken\n", QUEUENAME(qIdx)));
1478 ASMAtomicWriteBool(&pWorker->fNotified, false);
1479 }
1480 ASMAtomicWriteBool(&pWorker->fSleeping, false);
1481 }
1482
1483 if (!pThis->afQueueAttached[qIdx])
1484 {
1485 LogFunc(("%s queue not attached, worker aborting...\n", QUEUENAME(qIdx)));
1486 break;
1487 }
1488 if (!pThis->fQuiescing)
1489 {
1490 Log6Func(("fetching next descriptor chain from %s\n", QUEUENAME(qIdx)));
1491 PVIRTIO_DESC_CHAIN_T pDescChain;
1492 int rc = virtioR3QueueGet(&pThis->Virtio, qIdx, &pDescChain, true);
1493 if (rc == VERR_NOT_AVAILABLE)
1494 {
1495 Log6Func(("Nothing found in %s\n", QUEUENAME(qIdx)));
1496 continue;
1497 }
1498
1499 AssertRC(rc);
1500 if (qIdx == CONTROLQ_IDX)
1501 virtioScsiR3Ctrl(pThis, qIdx, pDescChain);
1502 else /* request queue index */
1503 {
1504 rc = virtioScsiR3ReqSubmit(pThis, qIdx, pDescChain);
1505 if (RT_FAILURE(rc))
1506 {
1507 LogRel(("Error submitting req packet, resetting %Rrc", rc));
1508 }
1509 }
1510 }
1511 }
1512 return VINF_SUCCESS;
1513}
1514
1515
1516/*********************************************************************************************************************************
1517* Sending evnets
1518*********************************************************************************************************************************/
1519
1520DECLINLINE(void) virtioScsiR3ReportEventsMissed(PVIRTIOSCSI pThis, uint16_t uTarget)
1521{
1522 virtioScsiR3SendEvent(pThis, uTarget, VIRTIOSCSI_T_NO_EVENT | VIRTIOSCSI_T_EVENTS_MISSED, 0);
1523}
1524
1525#if 0
1526
1527/** Only invoke this if VIRTIOSCSI_F_HOTPLUG is negotiated during intiailization
1528 * This effectively removes the SCSI Target/LUN on the guest side
1529 */
1530DECLINLINE(void) virtioScsiR3ReportTargetRemoved(PVIRTIOSCSI pThis, uint16_t uTarget)
1531{
1532 if (pThis->fHasHotplug)
1533 virtioScsiR3SendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET, VIRTIOSCSI_EVT_RESET_REMOVED);
1534}
1535
1536/** Only invoke this if VIRTIOSCSI_F_HOTPLUG is negotiated during intiailization
1537 * This effectively adds the SCSI Target/LUN on the guest side
1538 */
1539DECLINLINE(void) virtioScsiR3ReportTargetAdded(PVIRTIOSCSI pThis, uint16_t uTarget)
1540{
1541 if (pThis->fHasHotplug)
1542 virtioScsiR3SendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET, VIRTIOSCSI_EVT_RESET_RESCAN);
1543}
1544
1545DECLINLINE(void) virtioScsiR3ReportTargetReset(PVIRTIOSCSI pThis, uint16_t uTarget)
1546{
1547 virtioScsiR3SendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET, VIRTIOSCSI_EVT_RESET_HARD);
1548}
1549
1550DECLINLINE(void) virtioScsiR3ReportOperChange(PVIRTIOSCSI pThis, uint16_t uTarget)
1551{
1552 if (pThis->fSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE)
1553 virtioScsiR3SendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY, VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE);
1554}
1555
1556DECLINLINE(void) virtioScsiR3ReportPowerMsg(PVIRTIOSCSI pThis, uint16_t uTarget)
1557{
1558 if (pThis->fSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_POWER_MGMT)
1559 virtioScsiR3SendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY, VIRTIOSCSI_EVT_ASYNC_POWER_MGMT);
1560}
1561
1562DECLINLINE(void) virtioScsiR3ReportExtReq(PVIRTIOSCSI pThis, uint16_t uTarget)
1563{
1564 if (pThis->fSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST)
1565 virtioScsiR3SendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY, VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST);
1566}
1567
1568DECLINLINE(void) virtioScsiR3ReportMediaChange(PVIRTIOSCSI pThis, uint16_t uTarget)
1569{
1570 if (pThis->fSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE)
1571 virtioScsiR3SendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY, VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE);
1572}
1573
1574DECLINLINE(void) virtioScsiR3ReportMultiHost(PVIRTIOSCSI pThis, uint16_t uTarget)
1575{
1576 if (pThis->fSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_MULTI_HOST)
1577 virtioScsiR3SendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY, VIRTIOSCSI_EVT_ASYNC_MULTI_HOST);
1578}
1579
1580DECLINLINE(void) virtioScsiR3ReportDeviceBusy(PVIRTIOSCSI pThis, uint16_t uTarget)
1581{
1582 if (pThis->fSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY)
1583 virtioScsiR3SendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY, VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY);
1584}
1585
1586DECLINLINE(void) virtioScsiR3ReportParamChange(PVIRTIOSCSI pThis, uint16_t uTarget, uint32_t uSenseCode, uint32_t uSenseQualifier)
1587{
1588 uint32_t uReason = uSenseQualifier << 8 | uSenseCode;
1589 virtioScsiR3SendEvent(pThis, uTarget, VIRTIOSCSI_T_PARAM_CHANGE, uReason);
1590
1591}
1592
1593#endif
1594
1595/**
1596 * @callback_method_impl{FNVIRTIOQUEUENOTIFIED}
1597 */
1598static DECLCALLBACK(void) virtioScsiR3Notified(PVIRTIOSTATE pVirtio, uint16_t qIdx)
1599{
1600 AssertReturnVoid(qIdx < VIRTIOSCSI_QUEUE_CNT);
1601 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pVirtio, VIRTIOSCSI, Virtio);
1602 PVIRTIOSCSIWORKER pWorker = &pThis->aWorkers[qIdx];
1603
1604 RTLogFlush(RTLogDefaultInstanceEx(RT_MAKE_U32(0, UINT16_MAX)));
1605
1606 if (qIdx == CONTROLQ_IDX || IS_REQ_QUEUE(qIdx))
1607 {
1608 Log6Func(("%s has available data\n", QUEUENAME(qIdx)));
1609 /* Wake queue's worker thread up if sleeping */
1610 if (!ASMAtomicXchgBool(&pWorker->fNotified, true))
1611 {
1612 if (ASMAtomicReadBool(&pWorker->fSleeping))
1613 {
1614 Log6Func(("waking %s worker.\n", QUEUENAME(qIdx)));
1615 int rc = SUPSemEventSignal(pThis->pSupDrvSession, pWorker->hEvtProcess);
1616 AssertRC(rc);
1617 }
1618 }
1619 }
1620 else if (qIdx == EVENTQ_IDX)
1621 {
1622 Log3Func(("Driver queued buffer(s) to %s\n", QUEUENAME(qIdx)));
1623 if (ASMAtomicXchgBool(&pThis->fEventsMissed, false))
1624 virtioScsiR3ReportEventsMissed(pThis, 0);
1625 }
1626 else
1627 LogFunc(("Unexpected queue idx (ignoring): %d\n", qIdx));
1628}
1629
1630/**
1631 * @callback_method_impl{FNVIRTIOSTATUSCHANGED}
1632 */
1633static DECLCALLBACK(void) virtioScsiR3StatusChanged(PVIRTIOSTATE pVirtio, uint32_t fVirtioReady)
1634{
1635 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pVirtio, VIRTIOSCSI, Virtio);
1636
1637 pThis->fVirtioReady = fVirtioReady;
1638
1639 if (fVirtioReady)
1640 {
1641 LogFunc(("VirtIO ready\n-----------------------------------------------------------------------------------------\n"));
1642 uint64_t fFeatures = virtioGetNegotiatedFeatures(&pThis->Virtio);
1643 pThis->fHasT10pi = fFeatures & VIRTIO_SCSI_F_T10_PI;
1644 pThis->fHasHotplug = fFeatures & VIRTIO_SCSI_F_HOTPLUG;
1645 pThis->fHasInOutBufs = fFeatures & VIRTIO_SCSI_F_INOUT;
1646 pThis->fHasLunChange = fFeatures & VIRTIO_SCSI_F_CHANGE;
1647 pThis->fQuiescing = false;
1648 pThis->fResetting = false;
1649
1650 for (int i = 0; i < VIRTIOSCSI_QUEUE_CNT; i++)
1651 pThis->afQueueAttached[i] = true;
1652 }
1653 else
1654 {
1655 LogFunc(("VirtIO is resetting\n"));
1656 for (int i = 0; i < VIRTIOSCSI_QUEUE_CNT; i++)
1657 pThis->afQueueAttached[i] = false;
1658 }
1659}
1660
1661
1662/*********************************************************************************************************************************
1663* LEDs *
1664*********************************************************************************************************************************/
1665
1666#if 0 /** @todo r=bird: LEDs are not being set it seems. */
1667
1668/**
1669 * Turns on/off the write status LED.
1670 *
1671 * @param pTarget Pointer to the target device
1672 * @param fOn New LED state.
1673 */
1674static void virtioScsiR3SetWriteLed(PVIRTIOSCSITARGET pTarget, bool fOn)
1675{
1676 LogFlow(("%s virtioSetWriteLed: %s\n", pTarget->pszTargetName, fOn ? "on" : "off"));
1677 if (fOn)
1678 pTarget->led.Asserted.s.fWriting = pTarget->led.Actual.s.fWriting = 1;
1679 else
1680 pTarget->led.Actual.s.fWriting = 0;
1681}
1682
1683/**
1684 * Turns on/off the read status LED.
1685 *
1686 * @param pTarget Pointer to the device state structure.
1687 * @param fOn New LED state.
1688 */
1689static void virtioScsiR3SetReadLed(PVIRTIOSCSITARGET pTarget, bool fOn)
1690{
1691 LogFlow(("%s virtioSetReadLed: %s\n", pTarget->pszTargetName, fOn ? "on" : "off"));
1692 if (fOn)
1693 pTarget->led.Asserted.s.fReading = pTarget->led.Actual.s.fReading = 1;
1694 else
1695 pTarget->led.Actual.s.fReading = 0;
1696}
1697
1698#endif /* unused*/
1699
1700/**
1701 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed, Target level.}
1702 */
1703static DECLCALLBACK(int) virtioScsiR3TargetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iTarget, PPDMLED *ppLed)
1704{
1705 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, ILed);
1706 if (iTarget == 0)
1707 {
1708 *ppLed = &pTarget->led;
1709 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
1710 return VINF_SUCCESS;
1711 }
1712 return VERR_PDM_LUN_NOT_FOUND;
1713}
1714
1715/**
1716 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed, Device level.}
1717 */
1718static DECLCALLBACK(int) virtioScsiR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iTarget, PPDMLED *ppLed)
1719{
1720 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, ILeds);
1721 if (iTarget < pThis->cTargets)
1722 {
1723 *ppLed = &pThis->paTargetInstances[iTarget].led;
1724 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
1725 return VINF_SUCCESS;
1726 }
1727 return VERR_PDM_LUN_NOT_FOUND;
1728}
1729
1730
1731/*********************************************************************************************************************************
1732* PDMIMEDIAPORT (target) *
1733*********************************************************************************************************************************/
1734
1735/**
1736 * @interface_method_impl{PDMIMEDIAPORT,pfnQueryDeviceLocation, Target level.}
1737 */
1738static DECLCALLBACK(int) virtioScsiR3QueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
1739 uint32_t *piInstance, uint32_t *piTarget)
1740{
1741 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaPort);
1742 PPDMDEVINS pDevIns = pTarget->pVirtioScsi->CTX_SUFF(pDevIns);
1743
1744 AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
1745 AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
1746 AssertPtrReturn(piTarget, VERR_INVALID_POINTER);
1747
1748 *ppcszController = pDevIns->pReg->szName;
1749 *piInstance = pDevIns->iInstance;
1750 *piTarget = pTarget->iTarget;
1751
1752 return VINF_SUCCESS;
1753}
1754
1755
1756/*********************************************************************************************************************************
1757* Virtio config. *
1758*********************************************************************************************************************************/
1759
1760/**
1761 * Worker for virtioScsiR3DevCapWrite and virtioScsiR3DevCapRead.
1762 */
1763static int virtioScsiR3CfgAccessed(PVIRTIOSCSI pThis, uint32_t offConfig, void *pv, uint32_t cb, bool fWrite)
1764{
1765 AssertReturn(pv && cb <= sizeof(uint32_t), fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00);
1766
1767/**
1768 * Resolves to boolean true if uOffset matches a field offset and size exactly,
1769 * (or if 64-bit field, if it accesses either 32-bit part as a 32-bit access)
1770 * Assumption is this critereon is mandated by VirtIO 1.0, Section 4.1.3.1)
1771 * (Easily re-written to allow unaligned bounded access to a field).
1772 *
1773 * @param member - Member of VIRTIO_PCI_COMMON_CFG_T
1774 * @result - true or false
1775 */
1776#define MATCH_SCSI_CONFIG(member) \
1777 ( ( RT_SIZEOFMEMB(VIRTIOSCSI_CONFIG_T, member) == 8 \
1778 && ( offConfig == RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member) \
1779 || offConfig == RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member) + sizeof(uint32_t)) \
1780 && cb == sizeof(uint32_t)) \
1781 || ( offConfig == RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member) \
1782 && cb == RT_SIZEOFMEMB(VIRTIOSCSI_CONFIG_T, member)) )
1783
1784#define LOG_SCSI_CONFIG_ACCESSOR(member) \
1785 virtioLogMappedIoValue(__FUNCTION__, #member, RT_SIZEOFMEMB(VIRTIOSCSI_CONFIG_T, member), \
1786 pv, cb, offIntra, fWrite, false, 0);
1787
1788#define SCSI_CONFIG_ACCESSOR(member) \
1789 do \
1790 { \
1791 uint32_t offIntra = offConfig - RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member); \
1792 if (fWrite) \
1793 memcpy((char *)&pThis->virtioScsiConfig.member + offIntra, pv, cb); \
1794 else \
1795 memcpy(pv, (const char *)&pThis->virtioScsiConfig.member + offIntra, cb); \
1796 LOG_SCSI_CONFIG_ACCESSOR(member); \
1797 } while(0)
1798
1799#define SCSI_CONFIG_ACCESSOR_READONLY(member) \
1800 do \
1801 { \
1802 uint32_t offIntra = offConfig - RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member); \
1803 if (fWrite) \
1804 LogFunc(("Guest attempted to write readonly virtio_pci_common_cfg.%s\n", #member)); \
1805 else \
1806 { \
1807 memcpy(pv, (const char *)&pThis->virtioScsiConfig.member + offIntra, cb); \
1808 LOG_SCSI_CONFIG_ACCESSOR(member); \
1809 } \
1810 } while(0)
1811
1812 if (MATCH_SCSI_CONFIG( uNumQueues))
1813 SCSI_CONFIG_ACCESSOR_READONLY(uNumQueues);
1814 else if (MATCH_SCSI_CONFIG( uSegMax))
1815 SCSI_CONFIG_ACCESSOR_READONLY(uSegMax);
1816 else if (MATCH_SCSI_CONFIG( uMaxSectors))
1817 SCSI_CONFIG_ACCESSOR_READONLY(uMaxSectors);
1818 else if (MATCH_SCSI_CONFIG( uCmdPerLun))
1819 SCSI_CONFIG_ACCESSOR_READONLY(uCmdPerLun);
1820 else if (MATCH_SCSI_CONFIG( uEventInfoSize))
1821 SCSI_CONFIG_ACCESSOR_READONLY(uEventInfoSize);
1822 else if (MATCH_SCSI_CONFIG( uSenseSize))
1823 SCSI_CONFIG_ACCESSOR( uSenseSize);
1824 else if (MATCH_SCSI_CONFIG( uCdbSize))
1825 SCSI_CONFIG_ACCESSOR( uCdbSize);
1826 else if (MATCH_SCSI_CONFIG( uMaxChannel))
1827 SCSI_CONFIG_ACCESSOR_READONLY(uMaxChannel);
1828 else if (MATCH_SCSI_CONFIG( uMaxTarget))
1829 SCSI_CONFIG_ACCESSOR_READONLY(uMaxTarget);
1830 else if (MATCH_SCSI_CONFIG( uMaxLun))
1831 SCSI_CONFIG_ACCESSOR_READONLY(uMaxLun);
1832 else
1833 {
1834 LogFunc(("Bad access by guest to virtio_scsi_config: off=%u (%#x), cb=%u\n", offConfig, offConfig, cb));
1835 return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
1836 }
1837 return VINF_SUCCESS;
1838#undef SCSI_CONFIG_ACCESSOR_READONLY
1839#undef SCSI_CONFIG_ACCESSOR
1840#undef LOG_ACCESSOR
1841#undef MATCH_SCSI_CONFIG
1842}
1843
1844/**
1845 * @callback_method_impl{FNVIRTIODEVCAPREAD}
1846 */
1847static DECLCALLBACK(int) virtioScsiR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void *pv, uint32_t cb)
1848{
1849 return virtioScsiR3CfgAccessed(PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI), uOffset, pv, cb, false /*fRead*/);
1850}
1851
1852/**
1853 * @callback_method_impl{FNVIRTIODEVCAPWRITE}
1854 */
1855static DECLCALLBACK(int) virtioScsiR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
1856{
1857 return virtioScsiR3CfgAccessed(PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI), uOffset, (void *)pv, cb, true /*fWrite*/);
1858}
1859
1860
1861/*********************************************************************************************************************************
1862* IBase for device and targets *
1863*********************************************************************************************************************************/
1864
1865/**
1866 * @interface_method_impl{PDMIBASE,pfnQueryInterface, Target level.}
1867 */
1868static DECLCALLBACK(void *) virtioScsiR3TargetQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1869{
1870 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IBase);
1871 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pTarget->IBase);
1872 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pTarget->IMediaPort);
1873 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pTarget->IMediaExPort);
1874 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pTarget->ILed);
1875 return NULL;
1876}
1877
1878/**
1879 * @interface_method_impl{PDMIBASE,pfnQueryInterface, Device level.}
1880 */
1881static DECLCALLBACK(void *) virtioScsiR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1882{
1883 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, IBase);
1884
1885 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
1886 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
1887
1888 return NULL;
1889}
1890
1891
1892/*********************************************************************************************************************************
1893* Misc *
1894*********************************************************************************************************************************/
1895
1896/**
1897 * @callback_method_impl{FNDBGFHANDLERDEV, virtio-scsi debugger info callback.}
1898 */
1899static DECLCALLBACK(void) virtioScsiR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1900{
1901 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
1902
1903 /* Parse arguments. */
1904 RT_NOREF(pszArgs); //bool fVerbose = pszArgs && strstr(pszArgs, "verbose") != NULL;
1905
1906 /* Show basic information. */
1907 pHlp->pfnPrintf(pHlp, "%s#%d: virtio-scsci ",
1908 pDevIns->pReg->szName,
1909 pDevIns->iInstance);
1910 pHlp->pfnPrintf(pHlp, "numTargets=%lu", pThis->cTargets);
1911}
1912
1913
1914/*********************************************************************************************************************************
1915* Saved state *
1916*********************************************************************************************************************************/
1917
1918/**
1919 * @callback_method_impl{FNSSMDEVLOADEXEC}
1920 */
1921static DECLCALLBACK(int) virtioScsiR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1922{
1923 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
1924 LogFunc(("LOAD EXEC!!\n"));
1925
1926 AssertReturn(uPass == SSM_PASS_FINAL, VERR_SSM_UNEXPECTED_PASS);
1927 AssertLogRelMsgReturn(uVersion == VIRTIOSCSI_SAVED_STATE_VERSION,
1928 ("uVersion=%u\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
1929
1930 SSMR3GetU32(pSSM, &pThis->virtioScsiConfig.uNumQueues);
1931 SSMR3GetU32(pSSM, &pThis->virtioScsiConfig.uSegMax);
1932 SSMR3GetU32(pSSM, &pThis->virtioScsiConfig.uMaxSectors);
1933 SSMR3GetU32(pSSM, &pThis->virtioScsiConfig.uCmdPerLun);
1934 SSMR3GetU32(pSSM, &pThis->virtioScsiConfig.uEventInfoSize);
1935 SSMR3GetU32(pSSM, &pThis->virtioScsiConfig.uSenseSize);
1936 SSMR3GetU32(pSSM, &pThis->virtioScsiConfig.uCdbSize);
1937 SSMR3GetU16(pSSM, &pThis->virtioScsiConfig.uMaxChannel);
1938 SSMR3GetU16(pSSM, &pThis->virtioScsiConfig.uMaxTarget);
1939 SSMR3GetU32(pSSM, &pThis->virtioScsiConfig.uMaxLun);
1940 SSMR3GetU32(pSSM, &pThis->fAsyncEvtsEnabled);
1941 SSMR3GetU32(pSSM, (uint32_t *)&pThis->cActiveReqs);
1942 SSMR3GetBool(pSSM, &pThis->fEventsMissed);
1943 SSMR3GetU32(pSSM, &pThis->fVirtioReady);
1944 SSMR3GetU32(pSSM, &pThis->fHasT10pi);
1945 SSMR3GetU32(pSSM, &pThis->fHasHotplug);
1946 SSMR3GetU32(pSSM, &pThis->fHasInOutBufs);
1947 SSMR3GetU32(pSSM, &pThis->fHasLunChange);
1948 SSMR3GetU32(pSSM, &pThis->fResetting);
1949 int rc = SSMR3GetU32(pSSM, &pThis->fQuiescing);
1950 AssertRCReturn(rc, rc);
1951
1952 /*
1953 * Call the virtio core to let it load its state.
1954 */
1955 return virtioR3LoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM);
1956}
1957
1958/**
1959 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1960 */
1961static DECLCALLBACK(int) virtioScsiR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1962{
1963 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
1964 LogFunc(("SAVE EXEC!!\n"));
1965
1966 SSMR3PutU32(pSSM, pThis->virtioScsiConfig.uNumQueues);
1967 SSMR3PutU32(pSSM, pThis->virtioScsiConfig.uSegMax);
1968 SSMR3PutU32(pSSM, pThis->virtioScsiConfig.uMaxSectors);
1969 SSMR3PutU32(pSSM, pThis->virtioScsiConfig.uCmdPerLun);
1970 SSMR3PutU32(pSSM, pThis->virtioScsiConfig.uEventInfoSize);
1971 SSMR3PutU32(pSSM, pThis->virtioScsiConfig.uSenseSize);
1972 SSMR3PutU32(pSSM, pThis->virtioScsiConfig.uCdbSize);
1973 SSMR3PutU16(pSSM, pThis->virtioScsiConfig.uMaxChannel);
1974 SSMR3PutU16(pSSM, pThis->virtioScsiConfig.uMaxTarget);
1975 SSMR3PutU32(pSSM, pThis->virtioScsiConfig.uMaxLun);
1976 SSMR3PutU32(pSSM, pThis->fAsyncEvtsEnabled);
1977 SSMR3PutU32(pSSM, (uint32_t)pThis->cActiveReqs); /** @todo r=bird: Shouldn't this be zero? I don't think we can have
1978 * outstanding requests when the VM is suspended (which we are when this
1979 * function is called), and more importantely, I don't understand how they
1980 * would be restored by virtioScsiR3LoadExec. */
1981 SSMR3PutBool(pSSM, pThis->fEventsMissed);
1982 SSMR3PutU32(pSSM, pThis->fVirtioReady);
1983 SSMR3PutU32(pSSM, pThis->fHasT10pi);
1984 SSMR3PutU32(pSSM, pThis->fHasHotplug);
1985 SSMR3PutU32(pSSM, pThis->fHasInOutBufs);
1986 SSMR3PutU32(pSSM, pThis->fHasLunChange);
1987 SSMR3PutU32(pSSM, pThis->fResetting);
1988 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 */
1989
1990 /*
1991 * Call the virtio core to let it save its state.
1992 */
1993 return virtioR3SaveExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM);
1994}
1995
1996
1997/*********************************************************************************************************************************
1998* Device interface. *
1999*********************************************************************************************************************************/
2000
2001/**
2002 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
2003 *
2004 * One harddisk at one port has been unplugged.
2005 * The VM is suspended at this point.
2006 */
2007static DECLCALLBACK(void) virtioScsiR3Detach(PPDMDEVINS pDevIns, unsigned iTarget, uint32_t fFlags)
2008{
2009 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2010 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[iTarget];
2011
2012 LogFunc((""));
2013
2014 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2015 ("virtio-scsi: Device does not support hotplugging\n"));
2016 RT_NOREF(fFlags);
2017
2018 /*
2019 * Zero some important members.
2020 */
2021 pTarget->fPresent = false;
2022 pTarget->pDrvBase = NULL;
2023}
2024
2025/**
2026 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
2027 *
2028 * This is called when we change block driver.
2029 */
2030static DECLCALLBACK(int) virtioScsiR3Attach(PPDMDEVINS pDevIns, unsigned iTarget, uint32_t fFlags)
2031{
2032 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2033 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[iTarget];
2034 int rc;
2035
2036 pThis->pDevInsR3 = pDevIns;
2037 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
2038 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
2039
2040 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2041 ("virtio-scsi: Device does not support hotplugging\n"),
2042 VERR_INVALID_PARAMETER);
2043
2044 /* the usual paranoia */
2045 AssertRelease(!pTarget->pDrvBase);
2046 Assert(pTarget->iTarget == iTarget);
2047
2048 /*
2049 * Try attach the SCSI driver and get the interfaces,
2050 * required as well as optional.
2051 */
2052 rc = PDMDevHlpDriverAttach(pDevIns, pTarget->iTarget, &pDevIns->IBase,
2053 &pTarget->pDrvBase, (const char *)&pTarget->pszTargetName);
2054 if (RT_SUCCESS(rc))
2055 pTarget->fPresent = true;
2056 else
2057 AssertMsgFailed(("Failed to attach %s. rc=%Rrc\n", pTarget->pszTargetName, rc));
2058
2059 if (RT_FAILURE(rc))
2060 {
2061 pTarget->fPresent = false;
2062 pTarget->pDrvBase = NULL;
2063 }
2064 return rc;
2065}
2066
2067/**
2068 * @callback_method_impl{FNPDMDEVASYNCNOTIFY}
2069 */
2070static DECLCALLBACK(bool) virtioScsiR3DeviceQuiesced(PPDMDEVINS pDevIns)
2071{
2072 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2073 LogFunc(("Device I/O activity quiesced: enmQuiescingFor=%d\n", pThis->enmQuiescingFor));
2074
2075 if (pThis->enmQuiescingFor == kvirtIoScsiQuiescingForReset)
2076 virtioR3PropagateResetNotification(&pThis->Virtio);
2077 /** @todo r=bird: Do we need other notifications here for suspend and/or poweroff? */
2078
2079 pThis->enmQuiescingFor = kvirtIoScsiQuiescingForInvalid;
2080 pThis->fQuiescing = false;
2081 return true;
2082}
2083
2084/**
2085 * Worker for virtioScsiR3Reset() and virtioScsiR3SuspendOrPowerOff().
2086 */
2087static void virtioScsiR3QuiesceDevice(PPDMDEVINS pDevIns, VIRTIOSCSIQUIESCINGFOR enmQuiscingFor)
2088{
2089 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2090
2091 /* Prevent worker threads from removing/processing elements from virtq's */
2092 pThis->fQuiescing = true;
2093 pThis->enmQuiescingFor = enmQuiscingFor;
2094
2095 PDMDevHlpSetAsyncNotification(pDevIns, virtioScsiR3DeviceQuiesced);
2096
2097 /* If already quiesced invoke async callback. */
2098 if (!ASMAtomicReadU32(&pThis->cActiveReqs))
2099 PDMDevHlpAsyncNotificationCompleted(pDevIns);
2100}
2101
2102/**
2103 * Common worker for suspend and power off.
2104 */
2105static void virtioScsiR3SuspendOrPowerOff(PPDMDEVINS pDevIns, VIRTIOSCSIQUIESCINGFOR enmQuiscingFor)
2106{
2107 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2108
2109 virtioScsiR3QuiesceDevice(pDevIns, enmQuiscingFor);
2110
2111
2112 /* VM is halted, thus no new I/O being dumped into queues by the guest.
2113 * Workers have been flagged to stop pulling stuff already queued-up by the guest.
2114 * Now tell lower-level to to suspend reqs (for example, DrvVD suspends all reqs
2115 * on its wait queue, and we will get a callback as the state changes to
2116 * suspended (and later, resumed) for each).
2117 */
2118 for (uint32_t i = 0; i < pThis->cTargets; i++)
2119 {
2120 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[i];
2121 if (pTarget->pDrvBase)
2122 if (pTarget->pDrvMediaEx)
2123 pTarget->pDrvMediaEx->pfnNotifySuspend(pTarget->pDrvMediaEx);
2124 }
2125}
2126
2127/**
2128 * @interface_method_impl{PDMDEVREGR3,pfnPowerOff}
2129 */
2130static DECLCALLBACK(void) virtioScsiR3PowerOff(PPDMDEVINS pDevIns)
2131{
2132 LogFunc(("\n"));
2133 virtioScsiR3SuspendOrPowerOff(pDevIns, kvirtIoScsiQuiescingForPowerOff);
2134}
2135
2136/**
2137 * @interface_method_impl{PDMDEVREGR3,pfnSuspend}
2138 */
2139static DECLCALLBACK(void) virtioScsiR3Suspend(PPDMDEVINS pDevIns)
2140{
2141 LogFunc(("\n"));
2142 virtioScsiR3SuspendOrPowerOff(pDevIns, kvirtIoScsiQuiescingForSuspend);
2143}
2144
2145/**
2146 * @interface_method_impl{PDMDEVREGR3,pfnResume}
2147 */
2148static DECLCALLBACK(void) virtioScsiR3Resume(PPDMDEVINS pDevIns)
2149{
2150 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2151 LogFunc(("\n"));
2152
2153 pThis->fQuiescing = false;
2154
2155 /* Wake worker threads flagged to skip pulling queue entries during quiesce
2156 * to ensure they re-check their queues. Active request queues may already
2157 * be awake due to new reqs coming in.
2158 */
2159 for (uint16_t qIdx = 0; qIdx < VIRTIOSCSI_REQ_QUEUE_CNT; qIdx++)
2160 {
2161 PVIRTIOSCSIWORKER pWorker = &pThis->aWorkers[qIdx];
2162
2163 if (ASMAtomicReadBool(&pWorker->fSleeping))
2164 {
2165 Log6Func(("waking %s worker.\n", QUEUENAME(qIdx)));
2166 int rc = SUPSemEventSignal(pThis->pSupDrvSession, pWorker->hEvtProcess);
2167 AssertRC(rc);
2168 }
2169 }
2170
2171 /* Ensure guest is working the queues too. */
2172 virtioR3PropagateResumeNotification(&pThis->Virtio);
2173}
2174
2175/**
2176 * @interface_method_impl{PDMDEVREGR3,pfnReset}
2177 */
2178static DECLCALLBACK(void) virtioScsiR3Reset(PPDMDEVINS pDevIns)
2179{
2180 LogFunc(("\n"));
2181 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2182 pThis->fResetting = true;
2183 virtioScsiR3QuiesceDevice(pDevIns, kvirtIoScsiQuiescingForReset);
2184
2185 /** @todo r=bird: Shouldn't you reset the device here?!? */
2186}
2187
2188/**
2189 * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
2190 */
2191static DECLCALLBACK(int) virtioScsiR3Destruct(PPDMDEVINS pDevIns)
2192{
2193 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
2194 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2195
2196 RTMemFree(pThis->paTargetInstances);
2197 pThis->paTargetInstances = NULL;
2198 for (unsigned qIdx = 0; qIdx < VIRTIOSCSI_QUEUE_CNT; qIdx++)
2199 {
2200 PVIRTIOSCSIWORKER pWorker = &pThis->aWorkers[qIdx];
2201 if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
2202 {
2203 SUPSemEventClose(pThis->pSupDrvSession, pWorker->hEvtProcess);
2204 pWorker->hEvtProcess = NIL_SUPSEMEVENT;
2205 }
2206 }
2207
2208 virtioR3Term(&pThis->Virtio, pDevIns);
2209 return VINF_SUCCESS;
2210}
2211
2212/**
2213 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
2214 */
2215static DECLCALLBACK(int) virtioScsiR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2216{
2217 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2218
2219 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2220 int rc = VINF_SUCCESS;
2221
2222 pThis->pDevInsR3 = pDevIns;
2223 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
2224 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
2225 pThis->pSupDrvSession = PDMDevHlpGetSupDrvSession(pDevIns);
2226
2227 LogFunc(("PDM device instance: %d\n", iInstance));
2228 RTStrPrintf((char *)pThis->szInstance, sizeof(pThis->szInstance), "VIRTIOSCSI%d", iInstance);
2229
2230 /* Usable defaults */
2231 pThis->cTargets = 1;
2232
2233 /*
2234 * Validate and read configuration.
2235 */
2236 if (!CFGMR3AreValuesValid(pCfg,"NumTargets\0"
2237 "Bootable\0"
2238 /* "GCEnabled\0" TBD */
2239 /* "R0Enabled\0" TBD */
2240 ))
2241 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
2242 N_("virtio-scsi configuration error: unknown option specified"));
2243
2244 rc = CFGMR3QueryIntegerDef(pCfg, "NumTargets", &pThis->cTargets, true);
2245 if (RT_FAILURE(rc))
2246 return PDMDEV_SET_ERROR(pDevIns, rc,
2247 N_("virtio-scsi configuration error: failed to read NumTargets as integer"));
2248 LogFunc(("NumTargets=%d\n", pThis->cTargets));
2249
2250 rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &pThis->fBootable, true);
2251 if (RT_FAILURE(rc))
2252 return PDMDEV_SET_ERROR(pDevIns, rc,
2253 N_("virtio-scsi configuration error: failed to read Bootable as boolean"));
2254 LogFunc(("Bootable=%RTbool (unimplemented)\n", pThis->fBootable));
2255
2256 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, false);
2257 if (RT_FAILURE(rc))
2258 return PDMDEV_SET_ERROR(pDevIns, rc,
2259 N_("virtio-scsi configuration error: failed to read R0Enabled as boolean"));
2260
2261 rc = CFGMR3QueryBoolDef(pCfg, "RCEnabled", &pThis->fRCEnabled, false);
2262 if (RT_FAILURE(rc))
2263 return PDMDEV_SET_ERROR(pDevIns, rc,
2264 N_("virtio-scsi configuration error: failed to read RCEnabled as boolean"));
2265
2266 VIRTIOPCIPARAMS virtioPciParams, *pVirtioPciParams = &virtioPciParams;
2267 pVirtioPciParams->uDeviceId = PCI_DEVICE_ID_VIRTIOSCSI_HOST;
2268 pVirtioPciParams->uClassBase = PCI_CLASS_BASE_MASS_STORAGE;
2269 pVirtioPciParams->uClassSub = PCI_CLASS_SUB_SCSI_STORAGE_CONTROLLER;
2270 pVirtioPciParams->uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
2271 pVirtioPciParams->uSubsystemId = PCI_DEVICE_ID_VIRTIOSCSI_HOST; /* Virtio 1.0 spec allows PCI Device ID here */
2272 pVirtioPciParams->uInterruptLine = 0x00;
2273 pVirtioPciParams->uInterruptPin = 0x01;
2274
2275 pThis->IBase.pfnQueryInterface = virtioScsiR3DeviceQueryInterface;
2276
2277 /* Configure virtio_scsi_config that transacts via VirtIO implementation's Dev. Specific Cap callbacks */
2278 pThis->virtioScsiConfig.uNumQueues = VIRTIOSCSI_REQ_QUEUE_CNT;
2279 pThis->virtioScsiConfig.uSegMax = VIRTIOSCSI_MAX_SEG_COUNT;
2280 pThis->virtioScsiConfig.uMaxSectors = VIRTIOSCSI_MAX_SECTORS_HINT;
2281 pThis->virtioScsiConfig.uCmdPerLun = VIRTIOSCSI_MAX_COMMANDS_PER_LUN;
2282 pThis->virtioScsiConfig.uEventInfoSize = sizeof(VIRTIOSCSI_EVENT_T); /* Spec says at least this size! */
2283 pThis->virtioScsiConfig.uSenseSize = VIRTIOSCSI_SENSE_SIZE_DEFAULT;
2284 pThis->virtioScsiConfig.uCdbSize = VIRTIOSCSI_CDB_SIZE_DEFAULT;
2285 pThis->virtioScsiConfig.uMaxChannel = VIRTIOSCSI_MAX_CHANNEL_HINT;
2286 pThis->virtioScsiConfig.uMaxTarget = pThis->cTargets;
2287 pThis->virtioScsiConfig.uMaxLun = VIRTIOSCSI_MAX_LUN;
2288
2289 /* Initialize the generic Virtio core: */
2290 pThis->Virtio.Callbacks.pfnStatusChanged = virtioScsiR3StatusChanged;
2291 pThis->Virtio.Callbacks.pfnQueueNotified = virtioScsiR3Notified;
2292 pThis->Virtio.Callbacks.pfnDevCapRead = virtioScsiR3DevCapRead;
2293 pThis->Virtio.Callbacks.pfnDevCapWrite = virtioScsiR3DevCapWrite;
2294
2295 rc = virtioR3Init(&pThis->Virtio, pDevIns, pVirtioPciParams, pThis->szInstance, VIRTIOSCSI_HOST_SCSI_FEATURES_OFFERED,
2296 &pThis->virtioScsiConfig /*pvDevSpecificCap*/, sizeof(pThis->virtioScsiConfig));
2297
2298 if (RT_FAILURE(rc))
2299 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi: failed to initialize VirtIO"));
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 /* Initialize per device instance. */
2333
2334 Log2Func(("Found %d targets attached to controller\n", pThis->cTargets));
2335
2336 pThis->paTargetInstances = (PVIRTIOSCSITARGET)RTMemAllocZ(sizeof(VIRTIOSCSITARGET) * pThis->cTargets);
2337 if (!pThis->paTargetInstances)
2338 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to allocate memory for target states"));
2339
2340 for (RTUINT iTarget = 0; iTarget < pThis->cTargets; iTarget++)
2341 {
2342 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[iTarget];
2343
2344 if (RTStrAPrintf(&pTarget->pszTargetName, "VSCSI%u", iTarget) < 0)
2345 AssertLogRelFailedReturn(VERR_NO_MEMORY);
2346
2347 /* Initialize static parts of the device. */
2348 pTarget->iTarget = iTarget;
2349 pTarget->pVirtioScsi = pThis;
2350 pTarget->led.u32Magic = PDMLED_MAGIC;
2351
2352 pTarget->IBase.pfnQueryInterface = virtioScsiR3TargetQueryInterface;
2353
2354 /* IMediaPort and IMediaExPort interfaces provide callbacks for VD media and downstream driver access */
2355 pTarget->IMediaPort.pfnQueryDeviceLocation = virtioScsiR3QueryDeviceLocation;
2356 pTarget->IMediaPort.pfnQueryScsiInqStrings = NULL;
2357 pTarget->IMediaExPort.pfnIoReqCompleteNotify = virtioScsiR3IoReqFinish;
2358 pTarget->IMediaExPort.pfnIoReqCopyFromBuf = virtioScsiR3IoReqCopyFromBuf;
2359 pTarget->IMediaExPort.pfnIoReqCopyToBuf = virtioScsiR3IoReqCopyToBuf;
2360 pTarget->IMediaExPort.pfnIoReqStateChanged = virtioScsiR3IoReqStateChanged;
2361 pTarget->IMediaExPort.pfnMediumEjected = virtioScsiR3MediumEjected;
2362 pTarget->IMediaExPort.pfnIoReqQueryBuf = NULL; /* When used avoids copyFromBuf CopyToBuf*/
2363 pTarget->IMediaExPort.pfnIoReqQueryDiscardRanges = NULL;
2364
2365
2366 pTarget->IBase.pfnQueryInterface = virtioScsiR3TargetQueryInterface;
2367 pTarget->ILed.pfnQueryStatusLed = virtioScsiR3TargetQueryStatusLed;
2368 pThis->ILeds.pfnQueryStatusLed = virtioScsiR3DeviceQueryStatusLed;
2369 pTarget->led.u32Magic = PDMLED_MAGIC;
2370
2371 LogFunc(("Attaching LUN: %s\n", pTarget->pszTargetName));
2372
2373 AssertReturn(iTarget < pThis->cTargets, VERR_PDM_NO_SUCH_LUN);
2374 rc = PDMDevHlpDriverAttach(pDevIns, iTarget, &pTarget->IBase, &pTarget->pDrvBase, pTarget->pszTargetName);
2375 if (RT_SUCCESS(rc))
2376 {
2377 pTarget->fPresent = true;
2378
2379 pTarget->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIA);
2380 AssertMsgReturn(VALID_PTR(pTarget->pDrvMedia),
2381 ("virtio-scsi configuration error: LUN#%d missing basic media interface!\n", iTarget),
2382 VERR_PDM_MISSING_INTERFACE);
2383
2384 /* Get the extended media interface. */
2385 pTarget->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIAEX);
2386 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
2387 ("virtio-scsi configuration error: LUN#%d missing extended media interface!\n", iTarget),
2388 VERR_PDM_MISSING_INTERFACE);
2389
2390 rc = pTarget->pDrvMediaEx->pfnIoReqAllocSizeSet(pTarget->pDrvMediaEx, sizeof(VIRTIOSCSIREQ));
2391 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
2392 ("virtio-scsi configuration error: LUN#%u: Failed to set I/O request size!\n", iTarget),
2393 rc);
2394
2395 pTarget->pMediaNotify = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIANOTIFY);
2396 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
2397 ("virtio-scsi configuration error: LUN#%u: Failed to get set Media notify obj!\n", iTarget),
2398 VERR_PDM_MISSING_INTERFACE);
2399
2400 }
2401 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2402 {
2403 pTarget->fPresent = false;
2404 pTarget->pDrvBase = NULL;
2405 Log(("virtio-scsi: no driver attached to device %s\n", pTarget->pszTargetName));
2406 rc = VINF_SUCCESS;
2407 }
2408 else
2409 {
2410 AssertLogRelMsgFailed(("virtio-scsi: Failed to attach %s: %Rrc\n", pTarget->pszTargetName, rc));
2411 return rc;
2412 }
2413 }
2414
2415 /* Status driver */
2416 PPDMIBASE pUpBase;
2417 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pUpBase, "Status Port");
2418 if (RT_FAILURE(rc))
2419 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
2420
2421
2422 /*
2423 * Register saved state.
2424 */
2425 rc = PDMDevHlpSSMRegister(pDevIns, VIRTIOSCSI_SAVED_STATE_VERSION, sizeof(*pThis),
2426 virtioScsiR3SaveExec, virtioScsiR3LoadExec);
2427 AssertRCReturn(rc, rc);
2428
2429 /*
2430 * Register the debugger info callback (ignore errors).
2431 */
2432 char szTmp[128];
2433 RTStrPrintf(szTmp, sizeof(szTmp), "%s%d", pDevIns->pReg->szName, pDevIns->iInstance);
2434 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "virtio-scsi info", virtioScsiR3Info);
2435
2436 return rc;
2437}
2438
2439/**
2440 * The device registration structure.
2441 */
2442const PDMDEVREG g_DeviceVirtioSCSI =
2443{
2444 /* .u32Version = */ PDM_DEVREG_VERSION,
2445 /* .uReserved0 = */ 0,
2446 /* .szName = */ "virtio-scsi",
2447
2448#ifdef VIRTIOSCSI_GC_SUPPORT
2449 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ
2450 | PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION
2451 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
2452#else
2453 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS
2454 | PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION
2455 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
2456#endif
2457 /* .fClass = */ PDM_DEVREG_CLASS_MISC,
2458 /* .cMaxInstances = */ ~0U,
2459 /* .uSharedVersion = */ 42,
2460 /* .cbInstanceShared = */ sizeof(VIRTIOSCSI),
2461 /* .cbInstanceCC = */ 0,
2462 /* .cbInstanceRC = */ 0,
2463 /* .cMaxPciDevices = */ 1,
2464 /* .cMaxMsixVectors = */ VBOX_MSIX_MAX_ENTRIES,
2465 /* .pszDescription = */ "Virtio Host SCSI.\n",
2466#if defined(IN_RING3)
2467 /* .pszRCMod = */ "VBoxDDRC.rc",
2468 /* .pszR0Mod = */ "VBoxDDR0.r0",
2469 /* .pfnConstruct = */ virtioScsiR3Construct,
2470 /* .pfnDestruct = */ virtioScsiR3Destruct,
2471 /* .pfnRelocate = */ NULL,
2472 /* .pfnMemSetup = */ NULL,
2473 /* .pfnPowerOn = */ NULL,
2474 /* .pfnReset = */ virtioScsiR3Reset,
2475 /* .pfnSuspend = */ virtioScsiR3Suspend,
2476 /* .pfnResume = */ virtioScsiR3Resume,
2477 /* .pfnAttach = */ virtioScsiR3Attach,
2478 /* .pfnDetach = */ virtioScsiR3Detach,
2479 /* .pfnQueryInterface = */ NULL,
2480 /* .pfnInitComplete = */ NULL,
2481 /* .pfnPowerOff = */ virtioScsiR3PowerOff,
2482 /* .pfnSoftReset = */ NULL,
2483 /* .pfnReserved0 = */ NULL,
2484 /* .pfnReserved1 = */ NULL,
2485 /* .pfnReserved2 = */ NULL,
2486 /* .pfnReserved3 = */ NULL,
2487 /* .pfnReserved4 = */ NULL,
2488 /* .pfnReserved5 = */ NULL,
2489 /* .pfnReserved6 = */ NULL,
2490 /* .pfnReserved7 = */ NULL,
2491#elif defined(IN_RING0)
2492 /* .pfnEarlyConstruct = */ NULL,
2493 /* .pfnConstruct = */ NULL,
2494 /* .pfnDestruct = */ NULL,
2495 /* .pfnFinalDestruct = */ NULL,
2496 /* .pfnRequest = */ 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_RC)
2506 /* .pfnConstruct = */ NULL,
2507 /* .pfnReserved0 = */ NULL,
2508 /* .pfnReserved1 = */ NULL,
2509 /* .pfnReserved2 = */ NULL,
2510 /* .pfnReserved3 = */ NULL,
2511 /* .pfnReserved4 = */ NULL,
2512 /* .pfnReserved5 = */ NULL,
2513 /* .pfnReserved6 = */ NULL,
2514 /* .pfnReserved7 = */ NULL,
2515#else
2516# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2517#endif
2518 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2519};
2520
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