VirtualBox

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

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

Storage/DevVirtioSCSI.cpp: Fixed/improved attach target count/allocation and processing, converted size_t to uint32_t where possible and added mapping on uint32_t <-> size_t conversions between VirtIO and the VSCSI layer. Other clean-up

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

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