VirtualBox

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

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

Storage/DevVirtioSCSI.cpp: minor cleanup from last integration

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