VirtualBox

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

Last change on this file since 80802 was 80762, checked in by vboxsync, 6 years ago

Storage/DevVirtioSCSI.cpp: Added multi-target support. See bugref:9440, Comment #90

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