VirtualBox

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

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

Storage/DevVirtioSCSI.cpp: Attempt #2 to resolve burns

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 99.3 KB
Line 
1/* $Id: DevVirtioSCSI.cpp 81017 2019-09-26 12:26:45Z vboxsync $ $Revision: 81017 $ $Date: 2019-09-26 12:26:45 +0000 (Thu, 26 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",
750 uTarget, LUN0, szTypeText));
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",
758 uTarget, LUN0, uEventType));
759 return VINF_SUCCESS;
760 }
761
762 if (virtioQueueIsEmpty(pThis->hVirtio, EVENTQ_IDX))
763 {
764 LogFunc(("eventq is empty, events missed (driver didn't preload queue)!\n"));
765 ASMAtomicWriteBool(&pThis->fEventsMissed, true);
766 return VINF_SUCCESS;
767 }
768
769 PVIRTIO_DESC_CHAIN_T pDescChain;
770 virtioQueueGet(pThis->hVirtio, EVENTQ_IDX, &pDescChain, true);
771
772 RTSGBUF reqSegBuf;
773 RTSGSEG aReqSegs[] = { { &event, sizeof(event) } };
774 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
775
776 virtioQueuePut (pThis->hVirtio, EVENTQ_IDX, &reqSegBuf, pDescChain, true);
777 virtioQueueSync(pThis->hVirtio, EVENTQ_IDX);
778
779 return VINF_SUCCESS;
780}
781
782static void virtioScsiFreeReq(PVIRTIOSCSITARGET pTarget, PVIRTIOSCSIREQ pReq)
783{
784 RTMemFree(pReq->pbSense);
785 RTMemFree(pReq->pbPiIn);
786 RTMemFree(pReq->pbDataIn);
787 pTarget->pDrvMediaEx->pfnIoReqFree(pTarget->pDrvMediaEx, pReq->hIoReq);
788}
789
790/**
791 * This is called to complete a request immediately
792 *
793 * @param pThis - PDM driver instance state
794 * @param qIdx - Queue index
795 * @param pDescChain - Pointer to pre-processed descriptor chain pulled from virtq
796 * @param pRespHdr - Response header
797 * @param pbSense - Pointer to sense buffer or NULL if none.
798 *
799 * @returns virtual box status code
800 */
801static int virtioScsiReqErr(PVIRTIOSCSI pThis, uint16_t qIdx, PVIRTIO_DESC_CHAIN_T pDescChain,
802 struct REQ_RESP_HDR *pRespHdr, uint8_t *pbSense)
803{
804 uint8_t *abSenseBuf = (uint8_t *)RTMemAllocZ(pThis->virtioScsiConfig.uSenseSize);
805 AssertReturn(abSenseBuf, VERR_NO_MEMORY);
806
807 const char *pszCtrlRespText = virtioGetCtrlRespText(pRespHdr->uResponse);
808 Log2Func((" status: %s response: %s\n",
809 SCSIStatusText(pRespHdr->uStatus), pszCtrlRespText));
810
811 RTSGSEG aReqSegs[2];
812 aReqSegs[0].cbSeg = sizeof(pRespHdr);
813 aReqSegs[0].pvSeg = pRespHdr;
814 aReqSegs[1].cbSeg = pThis->virtioScsiConfig.uSenseSize;
815 aReqSegs[1].pvSeg = abSenseBuf;
816
817 if (pbSense && pRespHdr->uSenseLen)
818 memcpy(abSenseBuf, pbSense, pRespHdr->uSenseLen);
819 else
820 pRespHdr->uSenseLen = 0;
821
822 RTSGBUF reqSegBuf;
823 RTSgBufInit(&reqSegBuf, aReqSegs, RT_ELEMENTS(aReqSegs));
824
825 if (pThis->fResetting)
826 pRespHdr->uResponse = VIRTIOSCSI_S_RESET;
827
828 virtioQueuePut(pThis->hVirtio, qIdx, &reqSegBuf, pDescChain, true /* fFence */);
829 virtioQueueSync(pThis->hVirtio, qIdx);
830
831 RTMemFree(abSenseBuf);
832
833 if (!ASMAtomicDecU32(&pThis->cActiveReqs) && pThis->fQuiescing)
834 PDMDevHlpAsyncNotificationCompleted(pThis->CTX_SUFF(pDevIns));
835
836 Log2(("---------------------------------------------------------------------------------\n"));
837
838 return VINF_SUCCESS;
839}
840
841/**
842 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
843 */
844static DECLCALLBACK(int) virtioScsiIoReqFinish(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
845 void *pvIoReqAlloc, int rcReq)
846{
847 RT_NOREF(pInterface);
848
849 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
850 PVIRTIOSCSITARGET pTarget = pReq->pTarget;
851 PVIRTIOSCSI pThis = pTarget->pVirtioScsi;
852 PPDMIMEDIAEX pIMediaEx = pTarget->pDrvMediaEx;
853
854 size_t cbResidual = 0;
855 int rc = pIMediaEx->pfnIoReqQueryResidual(pIMediaEx, hIoReq, &cbResidual);
856 AssertRC(rc);
857
858 size_t cbXfer = 0;
859 rc = pIMediaEx->pfnIoReqQueryXferSize(pIMediaEx, hIoReq, &cbXfer);
860 AssertRC(rc);
861
862 Assert(!(cbXfer & 0xffffffff00000000));
863 uint32_t cbXfer32 = cbXfer & 0xffffffff;
864
865 struct REQ_RESP_HDR respHdr = { 0 };
866 respHdr.uSenseLen = pReq->pbSense[2] == SCSI_SENSE_NONE ? 0 : (uint32_t)pReq->uSenseLen;
867 AssertMsg(!(cbResidual & 0xffffffff00000000),
868 ("WARNING: Residual size larger than sizeof(uint32_t), truncating"));
869 respHdr.uResidual = (uint32_t)(cbResidual & 0xffffffff);
870 respHdr.uStatus = pReq->uStatus;
871
872 /* VirtIO 1.0 spec 5.6.6.1.1 says device MUST return a VirtIO response byte value.
873 * Some are returned during the submit phase, and a few are not mapped at all,
874 * wherein anything that can't map specifically gets mapped to VIRTIOSCSI_S_FAILURE
875 */
876 if (pThis->fResetting)
877 respHdr.uResponse = VIRTIOSCSI_S_RESET;
878 else
879 {
880 switch(rcReq)
881 {
882 case SCSI_STATUS_OK:
883 if (pReq->uStatus != SCSI_STATUS_CHECK_CONDITION)
884 {
885 respHdr.uResponse = VIRTIOSCSI_S_OK;
886 break;
887 }
888 case SCSI_STATUS_CHECK_CONDITION:
889 {
890 uint8_t uSenseKey = pReq->pbSense[2];
891 switch (uSenseKey)
892 {
893 case SCSI_SENSE_ABORTED_COMMAND:
894 respHdr.uResponse = VIRTIOSCSI_S_ABORTED;
895 break;
896 case SCSI_SENSE_COPY_ABORTED:
897 respHdr.uResponse = VIRTIOSCSI_S_ABORTED;
898 break;
899 case SCSI_SENSE_UNIT_ATTENTION:
900 respHdr.uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
901 break;
902 case SCSI_SENSE_HARDWARE_ERROR:
903 respHdr.uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
904 break;
905 case SCSI_SENSE_NOT_READY:
906 respHdr.uResponse = VIRTIOSCSI_S_BUSY; /* e.g. re-tryable */
907 break;
908 default:
909 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
910 break;
911 }
912 }
913 break;
914
915 default:
916 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
917 break;
918 }
919 }
920
921 Log2Func(("status: (%d) %s, response: (%d) %s\n",
922 pReq->uStatus, SCSIStatusText(pReq->uStatus),
923 respHdr.uResponse, virtioGetReqRespText(respHdr.uResponse)));
924
925 if (RT_FAILURE(rcReq))
926 Log2Func(("rcReq: %s\n", RTErrGetDefine(rcReq)));
927
928 if (LogIs3Enabled())
929 {
930 LogFunc(("cbDataIn = %u, cbDataOut = %u (cbIn = %u, cbOut = %u)\n",
931 pReq->cbDataIn, pReq->cbDataOut, pReq->pDescChain->cbPhysDst, pReq->pDescChain->cbVirtSrc));
932 LogFunc(("xfer = %lu, residual = %u\n", cbXfer, cbResidual));
933 const char *pszTxDirText = virtioGetTxDirText(pReq->enmTxDir);
934 LogFunc(("xfer direction: %s, sense written = %d, sense size = %d\n",
935 pszTxDirText, respHdr.uSenseLen, pThis->virtioScsiConfig.uSenseSize));
936 }
937
938 if (respHdr.uSenseLen && LogIs2Enabled())
939 {
940 LogFunc(("Sense: %s\n", SCSISenseText(pReq->pbSense[2])));
941 LogFunc(("Sense Ext3: %s\n", SCSISenseExtText(pReq->pbSense[12], pReq->pbSense[13])));
942 }
943
944 if (LogIs12Enabled())
945 {
946 uint32_t cb = RT_MIN(cbXfer32, 256);
947 if (VIRTIO_IN_DIRECTION(pReq->enmTxDir))
948 {
949 if (!isBufZero(pReq->pbDataIn, cb))
950 {
951 Log(("datain[showing the first %d of %d total bytes xferred]:\n", cb, cbXfer32));
952 VIRTIO_HEX_DUMP(RTLOGGRPFLAGS_LEVEL_12, pReq->pbDataIn, cb, 0, 0);
953 }
954 else
955 Log12Func(("First %d bytes transfered in this req are 0 --\n", cb));
956 }
957 else
958 if (VIRTIO_OUT_DIRECTION(pReq->enmTxDir))
959 {
960 if (!isBufZero(pReq->pbDataOut, cb))
961 {
962 Log(("dataout[showing the first %d of %d total bytes xferred]:\n", cb, cbXfer32));
963 VIRTIO_HEX_DUMP(RTLOGGRPFLAGS_LEVEL_12, pReq->pbDataOut, cb, 0, 0);
964 }
965 else
966 Log12Func(("First %d bytes transfered in this req are 0 --\n", cb));
967 }
968 }
969
970 int cSegs = 0;
971
972 if ( (VIRTIO_IN_DIRECTION(pReq->enmTxDir) && cbXfer32 > pReq->cbDataIn)
973 || (VIRTIO_OUT_DIRECTION(pReq->enmTxDir) && cbXfer32 > pReq->cbDataOut))
974 {
975 /* TBD try to figure out optimal sense info to send back besides response of VIRTIOSCSI_S_OVERRUN */
976 Log2Func((" * * * * Data overrun, returning sense\n"));
977 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
978 0, SCSI_SENSE_ILLEGAL_REQUEST, 0, 0, 0, 0, 10, 0, 0, 0 };
979 respHdr.uSenseLen = sizeof(abSense);
980 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
981 respHdr.uResponse = VIRTIOSCSI_S_OVERRUN;
982 respHdr.uResidual = pReq->cbDataIn;
983
984 virtioScsiReqErr(pThis, pReq->qIdx, pReq->pDescChain, &respHdr, abSense);
985 return VINF_SUCCESS;
986 }
987 else
988 {
989 Assert(pReq->pbSense != NULL);
990
991 RTSGSEG aReqSegs[4];
992 aReqSegs[cSegs].pvSeg = &respHdr;
993 aReqSegs[cSegs++].cbSeg = sizeof(respHdr);
994
995 aReqSegs[cSegs].pvSeg = pReq->pbSense;
996 aReqSegs[cSegs++].cbSeg = pReq->cbSense; /* VirtIO 1.0 spec 5.6.4/5.6.6.1 */
997
998 if (pReq->cbPiIn)
999 {
1000 aReqSegs[cSegs].pvSeg = pReq->pbPiIn;
1001 aReqSegs[cSegs++].cbSeg = pReq->cbPiIn;
1002 }
1003 if (pReq->cbDataIn)
1004 {
1005 aReqSegs[cSegs].pvSeg = pReq->pbDataIn;
1006 aReqSegs[cSegs++].cbSeg = cbXfer;
1007 }
1008 RTSGBUF reqSegBuf;
1009 RTSgBufInit(&reqSegBuf, aReqSegs, cSegs);
1010
1011 /*
1012 * Fill in the request queue current descriptor chain's IN queue entry/entries
1013 * (phys. memory) with the Req response data in virtual memory.
1014 */
1015 size_t cbReqSgBuf = RTSgBufCalcTotalLength(&reqSegBuf);
1016 AssertMsgReturn(cbReqSgBuf <= pReq->pDescChain->cbPhysDst,
1017 ("Guest expected less req data (space needed: %d, avail: %d)\n",
1018 cbReqSgBuf, pReq->pDescChain->cbPhysDst),
1019 VERR_BUFFER_OVERFLOW);
1020
1021 virtioQueuePut(pThis->hVirtio, pReq->qIdx, &reqSegBuf, pReq->pDescChain, true /* fFence TBD */);
1022 virtioQueueSync(pThis->hVirtio, pReq->qIdx);
1023
1024
1025 Log2(("-----------------------------------------------------------------------------------------\n"));
1026 }
1027
1028 virtioScsiFreeReq(pTarget, pReq);
1029
1030 if (!ASMAtomicDecU32(&pThis->cActiveReqs) && pThis->fQuiescing)
1031 PDMDevHlpAsyncNotificationCompleted(pThis->CTX_SUFF(pDevIns));
1032
1033 return VINF_SUCCESS;
1034}
1035
1036static int virtioScsiReqSubmit(PVIRTIOSCSI pThis, uint16_t qIdx, PVIRTIO_DESC_CHAIN_T pDescChain)
1037{
1038 ASMAtomicIncU32(&pThis->cActiveReqs);
1039
1040 PVIRTIOSCSI_REQ_CMD_T pVirtqReq = (PVIRTIOSCSI_REQ_CMD_T)pDescChain->pVirtSrc;
1041
1042 uint8_t uTarget = pVirtqReq->cmdHdr.uVirtioLun[1];
1043 uint32_t uScsiLun = (pVirtqReq->cmdHdr.uVirtioLun[2] << 8 | pVirtqReq->cmdHdr.uVirtioLun[3]) & 0x3fff;
1044 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[uTarget];
1045
1046 LogFunc(("[%s] (Target: %d LUN: %d) CDB: %.*Rhxs\n",
1047 SCSICmdText(pVirtqReq->uCdb[0]), uTarget, uScsiLun,
1048 virtioScsiEstimateCdbLen(pVirtqReq->uCdb[0],
1049 pThis->virtioScsiConfig.uCdbSize), pVirtqReq->uCdb));
1050
1051 Log3Func(("id: %RX64, attr: %x, prio: %d, crn: %x\n",
1052 pVirtqReq->cmdHdr.uId, pVirtqReq->cmdHdr.uTaskAttr, pVirtqReq->cmdHdr.uPrio, pVirtqReq->cmdHdr.uCrn));
1053
1054 /*
1055 * Calculate request offsets
1056 */
1057 uint32_t cbPiIn = 0, cbPiOut = 0, cbPiHdr = 0;
1058 off_t piOutOff = 0;
1059
1060 if (pThis->fHasT10pi)
1061 {
1062 cbPiIn = pVirtqReq->piHdr.uPiBytesOut;
1063 cbPiOut = pVirtqReq->piHdr.uPiBytesIn;
1064 cbPiHdr = sizeof(REQ_CMD_PI) + cbPiOut;
1065 piOutOff = sizeof(REQ_CMD_HDR) + pThis->virtioScsiConfig.uCdbSize + sizeof(REQ_CMD_PI);
1066 }
1067
1068 off_t uDataOutOff = sizeof(REQ_CMD_HDR) + pThis->virtioScsiConfig.uCdbSize + cbPiHdr;
1069 off_t uDataInOff = sizeof(REQ_RESP_HDR) + pThis->virtioScsiConfig.uSenseSize + cbPiIn;
1070 uint8_t *pbDataOut = (uint8_t *)((uint64_t)pVirtqReq + uDataOutOff);
1071 uint32_t cbDataOut = pDescChain->cbVirtSrc - uDataOutOff;
1072 uint32_t cbDataIn = pDescChain->cbPhysDst - uDataInOff;
1073
1074 /**
1075 * Handle submission errors
1076 */
1077 if (pThis->fResetting)
1078 {
1079 Log2Func(("Aborting req submission because reset is in progress\n"));
1080 struct REQ_RESP_HDR respHdr = { 0 };
1081 respHdr.uSenseLen = 0;
1082 respHdr.uStatus = SCSI_STATUS_OK;
1083 respHdr.uResponse = VIRTIOSCSI_S_RESET;
1084 respHdr.uResidual = cbDataIn + cbDataOut;
1085 virtioScsiReqErr(pThis, qIdx, pDescChain, &respHdr, NULL);
1086 return VINF_SUCCESS;
1087 }
1088 else
1089 if (uTarget >= pThis->cTargets || uScsiLun != 0)
1090 {
1091 Log2Func(("Error submitting request to bad target (%d) or bad LUN (%d)\n", uTarget, uScsiLun));
1092 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1093 0, SCSI_SENSE_ILLEGAL_REQUEST,
1094 0, 0, 0, 0, 10, SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED, 0, 0 };
1095 struct REQ_RESP_HDR respHdr = { 0 };
1096 respHdr.uSenseLen = sizeof(abSense);
1097 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1098 respHdr.uResponse = (uTarget > pThis->cTargets) ? VIRTIOSCSI_S_BAD_TARGET : VIRTIOSCSI_S_OK;
1099 respHdr.uResidual = cbDataOut + cbDataIn;
1100 virtioScsiReqErr(pThis, qIdx, pDescChain, &respHdr, abSense);
1101 return VINF_SUCCESS;
1102 }
1103 else
1104 if (RT_UNLIKELY(!pTarget->fPresent))
1105 {
1106 Log2Func(("Error submitting request, target not present!!\n"));
1107 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1108 0, SCSI_SENSE_NOT_READY, 0, 0, 0, 0, 10, 0, 0, 0 };
1109 struct REQ_RESP_HDR respHdr = { 0 };
1110 respHdr.uSenseLen = sizeof(abSense);
1111 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1112 respHdr.uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
1113 respHdr.uResidual = cbDataIn + cbDataOut;
1114 virtioScsiReqErr(pThis, qIdx, pDescChain, &respHdr , abSense);
1115 return VINF_SUCCESS;
1116 }
1117
1118 /*
1119 * Have underlying driver allocate a req of size set during initialization of this device.
1120 */
1121 PDMMEDIAEXIOREQ hIoReq = NULL;
1122 PVIRTIOSCSIREQ pReq;
1123 PPDMIMEDIAEX pIMediaEx = pTarget->pDrvMediaEx;
1124
1125 int rc = pIMediaEx->pfnIoReqAlloc(pIMediaEx, &hIoReq, (void **)&pReq, 0 /* uIoReqId */,
1126 PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR);
1127
1128 AssertMsgRCReturn(rc, ("Failed to allocate I/O request, rc=%Rrc\n", rc), rc);
1129
1130 /*
1131 * Prepare req's parameters for submission
1132 */
1133 if (pThis->fHasT10pi)
1134 {
1135 pReq->cbPiOut = cbPiOut;
1136 pReq->pbPiOut = (uint8_t *)((uint64_t)pVirtqReq + piOutOff);
1137 pReq->cbPiIn = cbPiIn;
1138 pReq->pbPiIn = (uint8_t *)RTMemAlloc(cbPiIn);
1139 AssertMsgReturn(pReq->pbPiIn, ("Out of memory allocating pi_in buffer"), VERR_NO_MEMORY);
1140 }
1141
1142 pReq->hIoReq = hIoReq;
1143 pReq->pTarget = pTarget;
1144 pReq->qIdx = qIdx;
1145 pReq->cbDataOut = cbDataOut;
1146 pReq->pbDataOut = cbDataOut ? pbDataOut : 0;
1147 pReq->pDescChain = pDescChain;
1148 pReq->cbSense = pThis->virtioScsiConfig.uSenseSize;
1149 pReq->pbSense = (uint8_t *)RTMemAlloc(pReq->cbSense);
1150 AssertMsgReturn(pReq->pbSense, ("Out of memory allocating sense buffer"), VERR_NO_MEMORY);
1151
1152 if (cbDataIn)
1153 {
1154 pReq->cbDataIn = cbDataIn;
1155 pReq->pbDataIn = (uint8_t *)RTMemAlloc(cbDataIn);
1156 AssertMsgReturn(pReq->pbDataIn, ("Out of memory allocating datain buffer"), VERR_NO_MEMORY);
1157 }
1158
1159 rc = pIMediaEx->pfnIoReqSendScsiCmd(pIMediaEx, pReq->hIoReq, uScsiLun,
1160 pVirtqReq->uCdb, (size_t)pThis->virtioScsiConfig.uCdbSize,
1161 PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN, &pReq->enmTxDir,
1162 (size_t)RT_MAX(cbDataIn, cbDataOut),
1163 pReq->pbSense, (size_t)pReq->cbSense, &pReq->uSenseLen,
1164 &pReq->uStatus, 30 * RT_MS_1SEC);
1165
1166 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
1167 {
1168 /*
1169 * Getting here means the request failed in early in the submission to the lower level driver,
1170 * and there will be no callback to the finished/completion function for this request
1171 */
1172 Log2Func(("Request submission error from lower-level driver\n"));
1173 uint8_t uASC, uASCQ = 0;
1174 switch (rc)
1175 {
1176 case VERR_NO_MEMORY:
1177 uASC = SCSI_ASC_SYSTEM_RESOURCE_FAILURE;
1178 break;
1179 default:
1180 uASC = SCSI_ASC_INTERNAL_TARGET_FAILURE;
1181 break;
1182 }
1183 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1184 0, SCSI_SENSE_VENDOR_SPECIFIC,
1185 0, 0, 0, 0, 10, uASC, uASCQ, 0 };
1186 struct REQ_RESP_HDR respHdr = { 0 };
1187 respHdr.uSenseLen = sizeof(abSense);
1188 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1189 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
1190 respHdr.uResidual = cbDataIn + cbDataOut;
1191 virtioScsiReqErr(pThis, qIdx, pDescChain, &respHdr, abSense);
1192 virtioScsiFreeReq(pTarget, pReq);
1193 return VINF_SUCCESS;
1194 }
1195
1196 return VINF_SUCCESS;
1197}
1198
1199static int virtioScsiCtrl(PVIRTIOSCSI pThis, uint16_t qIdx, PVIRTIO_DESC_CHAIN_T pDescChain)
1200{
1201 RT_NOREF2(pThis, qIdx);
1202
1203 uint8_t uResponse = VIRTIOSCSI_S_OK;
1204
1205 PVIRTIOSCSI_CTRL_T pScsiCtrl = (PVIRTIOSCSI_CTRL_T)pDescChain->pVirtSrc;
1206
1207 /*
1208 * Mask of events to tell guest driver this device supports
1209 * See VirtIO 1.0 specification section 5.6.6.2
1210 */
1211 uint32_t uSubscribedEvents =
1212 VIRTIOSCSI_EVT_ASYNC_POWER_MGMT
1213 | VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST
1214 | VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE
1215 | VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY;
1216
1217 RTSGBUF reqSegBuf;
1218
1219 switch(pScsiCtrl->uType)
1220 {
1221 case VIRTIOSCSI_T_TMF: /* Task Management Functions */
1222 {
1223 PVIRTIOSCSI_CTRL_TMF_T pScsiCtrlTmf = (PVIRTIOSCSI_CTRL_TMF_T)pScsiCtrl;
1224 const char *pszTmfTypeText = virtioGetTMFTypeText(pScsiCtrlTmf->uSubtype);
1225 LogFunc(("%s, VirtIO LUN: %.8Rhxs\n%*sTask Mgt Function: %s (not yet implemented)\n",
1226 QUEUENAME(qIdx), pScsiCtrlTmf->uScsiLun,
1227 CBQUEUENAME(qIdx) + 18, "", pszTmfTypeText));
1228
1229 switch(pScsiCtrlTmf->uSubtype)
1230 {
1231 case VIRTIOSCSI_T_TMF_ABORT_TASK:
1232 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1233 break;
1234 case VIRTIOSCSI_T_TMF_ABORT_TASK_SET:
1235 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1236 break;
1237 case VIRTIOSCSI_T_TMF_CLEAR_ACA:
1238 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1239 break;
1240 case VIRTIOSCSI_T_TMF_CLEAR_TASK_SET:
1241 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1242 break;
1243 case VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET:
1244 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1245 break;
1246 case VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET:
1247 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1248 break;
1249 case VIRTIOSCSI_T_TMF_QUERY_TASK:
1250 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1251 break;
1252 case VIRTIOSCSI_T_TMF_QUERY_TASK_SET:
1253 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1254 break;
1255 default:
1256 LogFunc(("Unknown TMF type\n"));
1257 uResponse = VIRTIOSCSI_S_FAILURE;
1258 }
1259
1260 RTSGSEG aReqSegs[] = { { &uResponse, sizeof(uResponse) } };
1261 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1262
1263 break;
1264 }
1265 case VIRTIOSCSI_T_AN_QUERY: /* Guest SCSI driver is querying supported async event notifications */
1266 {
1267 PVIRTIOSCSI_CTRL_AN_T pScsiCtrlAnQuery = (PVIRTIOSCSI_CTRL_AN_T)pScsiCtrl;
1268
1269 uSubscribedEvents &= pScsiCtrlAnQuery->uEventsRequested;
1270 uResponse = VIRTIOSCSI_S_FUNCTION_COMPLETE;
1271
1272 if (LogIs3Enabled())
1273 {
1274 char szTypeText[128];
1275 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), pScsiCtrlAnQuery->uEventsRequested);
1276
1277 LogFunc(("%s, VirtIO LUN: %.8Rhxs\n%*sAsync Query, types: %s\n",
1278 QUEUENAME(qIdx), pScsiCtrlAnQuery->uScsiLun, CBQUEUENAME(qIdx) + 30, "", szTypeText));
1279 }
1280
1281 RTSGSEG aReqSegs[] = { { &uSubscribedEvents, sizeof(uSubscribedEvents) }, { &uResponse, sizeof(uResponse) } };
1282 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1283
1284 break;
1285 }
1286 case VIRTIOSCSI_T_AN_SUBSCRIBE: /* Guest SCSI driver is subscribing to async event notification(s) */
1287 {
1288 PVIRTIOSCSI_CTRL_AN_T pScsiCtrlAnSubscribe = (PVIRTIOSCSI_CTRL_AN_T)pScsiCtrl;
1289
1290 if (pScsiCtrlAnSubscribe->uEventsRequested & ~SUBSCRIBABLE_EVENTS)
1291 LogFunc(("Unsupported bits in event subscription event mask: 0x%x\n", pScsiCtrlAnSubscribe->uEventsRequested));
1292
1293 uSubscribedEvents &= pScsiCtrlAnSubscribe->uEventsRequested;
1294 pThis->uAsyncEvtsEnabled = uSubscribedEvents;
1295
1296 if (LogIs3Enabled())
1297 {
1298 char szTypeText[128];
1299 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), pScsiCtrlAnSubscribe->uEventsRequested);
1300
1301 LogFunc(("%s, VirtIO LUN: %.8Rhxs\n%*sAsync Subscribe, types: %s\n",
1302 QUEUENAME(qIdx), pScsiCtrlAnSubscribe->uScsiLun, CBQUEUENAME(qIdx) + 30, "", szTypeText));
1303 }
1304
1305 /*
1306 * TBD: Verify correct status code if request mask is only partially fulfillable
1307 * and confirm when to use 'complete' vs. 'succeeded' See VirtIO 1.0 spec section 5.6.6.2
1308 * and read SAM docs*/
1309 if (uSubscribedEvents == pScsiCtrlAnSubscribe->uEventsRequested)
1310 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1311 else
1312 uResponse = VIRTIOSCSI_S_FUNCTION_COMPLETE;
1313
1314 RTSGSEG aReqSegs[] = { { &uSubscribedEvents, sizeof(uSubscribedEvents) }, { &uResponse, sizeof(uResponse) } };
1315 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1316
1317 break;
1318 }
1319 default:
1320 LogFunc(("Unknown control type extracted from %s: %d\n", QUEUENAME(qIdx), pScsiCtrl->uType));
1321
1322 uResponse = VIRTIOSCSI_S_FAILURE;
1323
1324 RTSGSEG aReqSegs[] = { { &uResponse, sizeof(uResponse) } };
1325 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1326 }
1327
1328 const char *pszCtrlRespText = virtioGetCtrlRespText(uResponse);
1329 LogFunc(("Response code: %s\n", pszCtrlRespText));
1330 virtioQueuePut (pThis->hVirtio, qIdx, &reqSegBuf, pDescChain, true);
1331 virtioQueueSync(pThis->hVirtio, qIdx);
1332
1333 return VINF_SUCCESS;
1334}
1335
1336/*
1337 * Unblock the worker thread so it can respond to a state change.
1338 *
1339 * @returns VBox status code.
1340 * @param pDevIns The pcnet device instance.
1341 * @param pThread The send thread.
1342 */
1343static DECLCALLBACK(int) virtioScsiWorkerWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1344{
1345 RT_NOREF(pThread);
1346 uint16_t qIdx = ((uint64_t)pThread->pvUser) & 0xffff;
1347 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1348 return SUPSemEventSignal(pThis->pSupDrvSession, pThis->aWorker[qIdx].hEvtProcess);
1349}
1350
1351static int virtioScsiWorker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1352{
1353 int rc;
1354 uint16_t qIdx = ((uint64_t)pThread->pvUser) & 0xffff;
1355 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1356 PWORKER pWorker = &pThis->aWorker[qIdx];
1357
1358 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
1359 return VINF_SUCCESS;
1360
1361 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
1362 {
1363 if (virtioQueueIsEmpty(pThis->hVirtio, qIdx))
1364 {
1365 /* Atomic interlocks avoid missing alarm while going to sleep & notifier waking the awoken */
1366 ASMAtomicWriteBool(&pWorker->fSleeping, true);
1367 bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
1368 if (!fNotificationSent)
1369 {
1370 Log6Func(("%s worker sleeping...\n", QUEUENAME(qIdx)));
1371 Assert(ASMAtomicReadBool(&pWorker->fSleeping));
1372 rc = SUPSemEventWaitNoResume(pThis->pSupDrvSession, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
1373 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
1374 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
1375 break;
1376 Log6Func(("%s worker woken\n", QUEUENAME(qIdx)));
1377 ASMAtomicWriteBool(&pWorker->fNotified, false);
1378 }
1379 ASMAtomicWriteBool(&pWorker->fSleeping, false);
1380 }
1381
1382 if (!pThis->fQueueAttached[qIdx])
1383 {
1384 LogFunc(("%s queue not attached, worker aborting...\n", QUEUENAME(qIdx)));
1385 break;
1386 }
1387 if (!pThis->fQuiescing)
1388 {
1389 Log6Func(("fetching next descriptor chain from %s\n", QUEUENAME(qIdx)));
1390 PVIRTIO_DESC_CHAIN_T pDescChain;
1391 rc = virtioQueueGet(pThis->hVirtio, qIdx, &pDescChain, true);
1392 if (rc == VERR_NOT_AVAILABLE)
1393 {
1394 Log6Func(("Nothing found in %s\n", QUEUENAME(qIdx)));
1395 continue;
1396 }
1397
1398 AssertRC(rc);
1399 if (qIdx == CONTROLQ_IDX)
1400 virtioScsiCtrl(pThis, qIdx, pDescChain);
1401 else /* request queue index */
1402 {
1403 rc = virtioScsiReqSubmit(pThis, qIdx, pDescChain);
1404 if (RT_FAILURE(rc))
1405 {
1406 LogRel(("Error submitting req packet, resetting %Rrc", rc));
1407 }
1408 }
1409 }
1410 }
1411 return VINF_SUCCESS;
1412}
1413
1414
1415DECLINLINE(void) virtioScsiReportEventsMissed(PVIRTIOSCSI pThis, uint16_t uTarget)
1416{
1417 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_NO_EVENT | VIRTIOSCSI_T_EVENTS_MISSED, 0);
1418}
1419
1420#if 0
1421/* Only invoke this if VIRTIOSCSI_F_HOTPLUG is negotiated during intiailization
1422 * This effectively removes the SCSI Target/LUN on the guest side
1423 */
1424DECLINLINE(void) virtioScsiReportTargetRemoved(PVIRTIOSCSI pThis, uint16_t uTarget)
1425{
1426 if (pThis->fHasHotplug)
1427 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET,
1428 VIRTIOSCSI_EVT_RESET_REMOVED);
1429}
1430
1431/* Only invoke thi if VIRTIOSCSI_F_HOTPLUG is negotiated during intiailization
1432 * This effectively adds the SCSI Target/LUN on the guest side
1433 */
1434DECLINLINE(void) virtioScsiReportTargetAdded(PVIRTIOSCSI pThis, uint16_t uTarget)
1435{
1436 if (pThis->fHasHotplug)
1437 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET,
1438 VIRTIOSCSI_EVT_RESET_RESCAN);
1439}
1440
1441DECLINLINE(void) virtioScsiReportTargetReset(PVIRTIOSCSI pThis, uint16_t uTarget)
1442{
1443 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET,
1444 VIRTIOSCSI_EVT_RESET_HARD);
1445}
1446
1447DECLINLINE(void) virtioScsiReportOperChange(PVIRTIOSCSI pThis, uint16_t uTarget)
1448{
1449 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE)
1450 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1451 VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE);
1452}
1453
1454DECLINLINE(void) virtioScsiReportPowerMsg(PVIRTIOSCSI pThis, uint16_t uTarget)
1455{
1456 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_POWER_MGMT)
1457 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1458 VIRTIOSCSI_EVT_ASYNC_POWER_MGMT);
1459}
1460
1461DECLINLINE(void) virtioScsiReportExtReq(PVIRTIOSCSI pThis, uint16_t uTarget)
1462{
1463 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST)
1464 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1465 VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST);
1466}
1467
1468DECLINLINE(void) virtioScsiReportMediaChange(PVIRTIOSCSI pThis, uint16_t uTarget)
1469{
1470 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE)
1471 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1472 VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE);
1473}
1474
1475DECLINLINE(void) virtioScsiReportMultiHost(PVIRTIOSCSI pThis, uint16_t uTarget)
1476{
1477 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_MULTI_HOST)
1478 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1479 VIRTIOSCSI_EVT_ASYNC_MULTI_HOST);
1480}
1481
1482DECLINLINE(void) virtioScsiReportDeviceBusy(PVIRTIOSCSI pThis, uint16_t uTarget)
1483{
1484 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY)
1485 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1486 VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY);
1487}
1488#endif
1489
1490DECLINLINE(void) virtioScsiReportParamChange(PVIRTIOSCSI pThis, uint16_t uTarget, uint32_t uSenseCode, uint32_t uSenseQualifier)
1491{
1492 uint32_t uReason = uSenseQualifier << 8 | uSenseCode;
1493 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_PARAM_CHANGE, uReason);
1494
1495}
1496
1497static DECLCALLBACK(void) virtioScsiNotified(VIRTIOHANDLE hVirtio, void *pClient, uint16_t qIdx)
1498{
1499 RT_NOREF(hVirtio);
1500
1501 AssertReturnVoid(qIdx < VIRTIOSCSI_QUEUE_CNT);
1502 PVIRTIOSCSI pThis = (PVIRTIOSCSI)pClient;
1503 PWORKER pWorker = &pThis->aWorker[qIdx];
1504
1505 RTLogFlush(RTLogDefaultInstanceEx(RT_MAKE_U32(0, UINT16_MAX)));
1506
1507 if (qIdx == CONTROLQ_IDX || IS_REQ_QUEUE(qIdx))
1508 {
1509 Log6Func(("%s has available data\n", QUEUENAME(qIdx)));
1510 /* Wake queue's worker thread up if sleeping */
1511 if (!ASMAtomicXchgBool(&pWorker->fNotified, true))
1512 {
1513 if (ASMAtomicReadBool(&pWorker->fSleeping))
1514 {
1515 Log6Func(("waking %s worker.\n", QUEUENAME(qIdx)));
1516 int rc = SUPSemEventSignal(pThis->pSupDrvSession, pWorker->hEvtProcess);
1517 AssertRC(rc);
1518 }
1519 }
1520 }
1521 else if (qIdx == EVENTQ_IDX)
1522 {
1523 Log3Func(("Driver queued buffer(s) to %s\n", QUEUENAME(qIdx)));
1524 if (ASMAtomicXchgBool(&pThis->fEventsMissed, false))
1525 virtioScsiReportEventsMissed(pThis, 0);
1526 }
1527 else
1528 LogFunc(("Unexpected queue idx (ignoring): %d\n", qIdx));
1529}
1530
1531static DECLCALLBACK(void) virtioScsiStatusChanged(VIRTIOHANDLE hVirtio, void *pClient, uint32_t fVirtioReady)
1532{
1533 RT_NOREF(hVirtio);
1534 PVIRTIOSCSI pThis = (PVIRTIOSCSI)pClient;
1535
1536 pThis->fVirtioReady = fVirtioReady;
1537
1538 if (fVirtioReady)
1539 {
1540 LogFunc(("VirtIO ready\n-----------------------------------------------------------------------------------------\n"));
1541 uint64_t features = virtioGetNegotiatedFeatures(hVirtio);
1542 pThis->fHasT10pi = features & VIRTIO_SCSI_F_T10_PI;
1543 pThis->fHasHotplug = features & VIRTIO_SCSI_F_HOTPLUG;
1544 pThis->fHasInOutBufs = features & VIRTIO_SCSI_F_INOUT;
1545 pThis->fHasLunChange = features & VIRTIO_SCSI_F_CHANGE;
1546 pThis->fQuiescing = false;
1547 pThis->fResetting = false;
1548
1549 for (int i = 0; i < VIRTIOSCSI_QUEUE_CNT; i++)
1550 pThis->fQueueAttached[i] = true;
1551 }
1552 else
1553 {
1554 LogFunc(("VirtIO is resetting\n"));
1555 for (int i = 0; i < VIRTIOSCSI_QUEUE_CNT; i++)
1556 pThis->fQueueAttached[i] = false;
1557 }
1558}
1559
1560/**
1561 * virtio-scsi debugger info callback.
1562 *
1563 * @param pDevIns The device instance.
1564 * @param pHlp The output helpers.
1565 * @param pszArgs The arguments.
1566 */
1567static DECLCALLBACK(void) virtioScsiInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1568{
1569 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1570 bool fVerbose = false;
1571
1572 /* Parse arguments. */
1573 if (pszArgs)
1574 fVerbose = strstr(pszArgs, "verbose") != NULL;
1575
1576 /* Show basic information. */
1577 pHlp->pfnPrintf(pHlp, "%s#%d: virtio-scsci ",
1578 pDevIns->pReg->szName,
1579 pDevIns->iInstance);
1580 pHlp->pfnPrintf(pHlp, "numTargets=%lu", pThis->cTargets);
1581}
1582
1583/**
1584 * @interface_method_impl{PDMIMEDIAEXPORT,pfnMediumEjected}
1585 */
1586static DECLCALLBACK(void) virtioScsiMediumEjected(PPDMIMEDIAEXPORT pInterface)
1587{
1588 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
1589 PVIRTIOSCSI pThis = pTarget->pVirtioScsi;
1590 LogFunc(("LUN %d Ejected!\n", pTarget->iTarget));
1591 if (pThis->pMediaNotify)
1592 {
1593 int rc = VMR3ReqCallNoWait(PDMDevHlpGetVM(pThis->CTX_SUFF(pDevIns)), VMCPUID_ANY,
1594 (PFNRT)pThis->pMediaNotify->pfnEjected, 2,
1595 pThis->pMediaNotify, pTarget->iTarget);
1596 AssertRC(rc);
1597 }
1598}
1599
1600/** @callback_method_impl{FNSSMDEVLIVEEXEC} */
1601static DECLCALLBACK(int) virtioScsiLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1602{
1603 LogFunc(("callback"));
1604 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1605 RT_NOREF(pThis);
1606 RT_NOREF(uPass);
1607 RT_NOREF(pSSM);
1608 return VINF_SSM_DONT_CALL_AGAIN;
1609}
1610
1611/** @callback_method_impl{FNSSMDEVLOADEXEC} */
1612static DECLCALLBACK(int) virtioScsiLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1613{
1614 LogFunc(("callback"));
1615 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1616 RT_NOREF(pThis);
1617 RT_NOREF(uPass);
1618 RT_NOREF(pSSM);
1619 RT_NOREF(uVersion);
1620 return VINF_SSM_DONT_CALL_AGAIN;
1621}
1622
1623/** @callback_method_impl{FNSSMDEVSAVEEXEC} */
1624static DECLCALLBACK(int) virtioScsiSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1625{
1626 LogFunc(("callback"));
1627 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1628
1629 RT_NOREF(pThis);
1630 RT_NOREF(pSSM);
1631 return VINF_SUCCESS;
1632}
1633
1634/** @callback_method_impl{FNSSMDEVLOADDONE} */
1635static DECLCALLBACK(int) virtioScsiLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1636{
1637 LogFunc(("callback"));
1638 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1639 RT_NOREF(pThis);
1640 RT_NOREF(pSSM);
1641 return VINF_SUCCESS;
1642}
1643
1644/**
1645 * Is asynchronous handling of suspend or power off notification completed?
1646 *
1647 * This is called to check whether the device has quiesced. Don't deadlock.
1648 * Avoid blocking. Do NOT wait for anything.
1649 *
1650 * @returns true if done, false if more work to be done.
1651 *
1652 * @param pDevIns The device instance.
1653 * @remarks The caller will enter the device critical section.
1654 * @thread EMT(0)
1655 */
1656static DECLCALLBACK(bool) virtioScsiDeviceQuiesced(PPDMDEVINS pDevIns)
1657{
1658 LogFunc(("Device I/O activity quiesced.\n"));
1659 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1660
1661 pThis->fQuiescing = false;
1662
1663 return true;
1664}
1665
1666static void virtioScsiQuiesceDevice(PPDMDEVINS pDevIns)
1667{
1668 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1669
1670 /* Prevent worker threads from removing/processing elements from virtq's */
1671 pThis->fQuiescing = true;
1672
1673 PDMDevHlpSetAsyncNotification(pDevIns, virtioScsiDeviceQuiesced);
1674
1675 /* If already quiesced invoke async callback. */
1676 if (!ASMAtomicReadU32(&pThis->cActiveReqs))
1677 PDMDevHlpAsyncNotificationCompleted(pDevIns);
1678}
1679
1680/**
1681 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
1682 */
1683static DECLCALLBACK(void) virtioScsiIoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1684 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
1685{
1686 RT_NOREF4(pInterface, hIoReq, pvIoReqAlloc, enmState);
1687 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, IBase);
1688
1689 switch (enmState)
1690 {
1691 case PDMMEDIAEXIOREQSTATE_SUSPENDED:
1692 {
1693 /* Stop considering this request active */
1694 if (!ASMAtomicDecU32(&pThis->cActiveReqs) && pThis->fQuiescing)
1695 PDMDevHlpAsyncNotificationCompleted(pThis->CTX_SUFF(pDevIns));
1696 break;
1697 }
1698 case PDMMEDIAEXIOREQSTATE_ACTIVE:
1699 ASMAtomicIncU32(&pThis->cActiveReqs);
1700 break;
1701 default:
1702 AssertMsgFailed(("Invalid request state given %u\n", enmState));
1703 }
1704}
1705
1706/**
1707 * @copydoc FNPDMDEVRESET
1708 */
1709static DECLCALLBACK(void) virtioScsiReset(PPDMDEVINS pDevIns)
1710{
1711 LogFunc(("\n"));
1712 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1713 pThis->fResetting = true;
1714 virtioScsiQuiesceDevice(pDevIns);
1715}
1716
1717/**
1718 * @interface_method_impl{PDMDEVREG,pfnResume}
1719 */
1720static DECLCALLBACK(void) virtioScsiResume(PPDMDEVINS pDevIns)
1721{
1722 LogFunc(("\n"));
1723
1724 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1725
1726 pThis->fQuiescing = false;
1727
1728 /* Wake worker threads flagged to skip pulling queue entries during quiesce
1729 * to ensure they re-check their queues. Active request queues may already
1730 * be awake due to new reqs coming in.
1731 */
1732 for (uint16_t qIdx = 0; qIdx < VIRTIOSCSI_REQ_QUEUE_CNT; qIdx++)
1733 {
1734 PWORKER pWorker = &pThis->aWorker[qIdx];
1735
1736 if (ASMAtomicReadBool(&pWorker->fSleeping))
1737 {
1738 Log6Func(("waking %s worker.\n", QUEUENAME(qIdx)));
1739 int rc = SUPSemEventSignal(pThis->pSupDrvSession, pWorker->hEvtProcess);
1740 AssertRC(rc);
1741 }
1742 }
1743
1744 /* Ensure guest is working the queues too. */
1745 virtioPropagateResumeNotification(pThis->hVirtio);
1746}
1747
1748/**
1749 * @interface_method_impl{PDMDEVREG,pfnSuspend}
1750 */
1751static DECLCALLBACK(void) virtioScsiSuspendOrPoweroff(PPDMDEVINS pDevIns)
1752{
1753 LogFunc(("\n"));
1754
1755 virtioScsiQuiesceDevice(pDevIns);
1756
1757 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1758
1759 /* VM is halted, thus no new I/O being dumped into queues by the guest.
1760 * Workers have been flagged to stop pulling stuff already queued-up by the guest.
1761 * Now tell lower-level to to suspend reqs (for example, DrvVD suspends all reqs
1762 * on its wait queue, and we will get a callback as the state changes to
1763 * suspended (and later, resumed) for each).
1764 */
1765 for (uint32_t i = 0; i < pThis->cTargets; i++)
1766 {
1767 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[i];
1768 if (pTarget->pDrvBase)
1769 if (pTarget->pDrvMediaEx)
1770 pTarget->pDrvMediaEx->pfnNotifySuspend(pTarget->pDrvMediaEx);
1771 }
1772}
1773
1774/**
1775 * Turns on/off the write status LED.
1776 *
1777 * @param pTarget Pointer to the target device
1778 * @param fOn New LED state.
1779 */
1780void virtioScsiSetWriteLed(PVIRTIOSCSITARGET pTarget, bool fOn)
1781{
1782 LogFlow(("%s virtioSetWriteLed: %s\n", pTarget->pszTargetName, fOn ? "on" : "off"));
1783 if (fOn)
1784 pTarget->led.Asserted.s.fWriting = pTarget->led.Actual.s.fWriting = 1;
1785 else
1786 pTarget->led.Actual.s.fWriting = fOn;
1787}
1788
1789/**
1790 * Turns on/off the read status LED.
1791 *
1792 * @param pTarget Pointer to the device state structure.
1793 * @param fOn New LED state.
1794 */
1795void virtioScsiSetReadLed(PVIRTIOSCSITARGET pTarget, bool fOn)
1796{
1797 LogFlow(("%s virtioSetReadLed: %s\n", pTarget->pszTargetName, fOn ? "on" : "off"));
1798 if (fOn)
1799 pTarget->led.Asserted.s.fReading = pTarget->led.Actual.s.fReading = 1;
1800 else
1801 pTarget->led.Actual.s.fReading = fOn;
1802}
1803
1804/**
1805 * Gets the pointer to the status LED of a unit.
1806 *
1807 * @returns VBox status code.
1808 * @param pInterface Pointer to the interface structure containing the called function pointer.
1809 * @param iTarget The unit which status LED we desire.
1810 * @param ppLed Where to store the LED pointer.
1811 */
1812static DECLCALLBACK(int) virtioScsiTargetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iTarget, PPDMLED *ppLed)
1813{
1814 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, ILed);
1815 if (iTarget == 0)
1816 {
1817 *ppLed = &pTarget->led;
1818 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
1819 return VINF_SUCCESS;
1820 }
1821 return VERR_PDM_LUN_NOT_FOUND;
1822}
1823
1824/**
1825 * Gets the pointer to the status LED of a unit.
1826 *
1827 * @returns VBox status code.
1828 * @param pInterface Pointer to the interface structure containing the called function pointer.
1829 * @param iTarget The unit which status LED we desire.
1830 * @param ppLed Where to store the LED pointer.
1831 */
1832static DECLCALLBACK(int) virtioScsiDeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iTarget, PPDMLED *ppLed)
1833{
1834 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, ILeds);
1835 if (iTarget < pThis->cTargets)
1836 {
1837 *ppLed = &pThis->paTargetInstances[iTarget].led;
1838 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
1839 return VINF_SUCCESS;
1840 }
1841 return VERR_PDM_LUN_NOT_FOUND;
1842 }
1843
1844static int virtioScsiCfgAccessed(PVIRTIOSCSI pThis, uint32_t uOffset,
1845 const void *pv, uint32_t cb, bool fWrite)
1846{
1847 int rc = VINF_SUCCESS;
1848 if (MATCH_SCSI_CONFIG(uNumQueues))
1849 SCSI_CONFIG_ACCESSOR_READONLY(uNumQueues);
1850 else
1851 if (MATCH_SCSI_CONFIG(uSegMax))
1852 SCSI_CONFIG_ACCESSOR_READONLY(uSegMax);
1853 else
1854 if (MATCH_SCSI_CONFIG(uMaxSectors))
1855 SCSI_CONFIG_ACCESSOR_READONLY(uMaxSectors);
1856 else
1857 if (MATCH_SCSI_CONFIG(uCmdPerLun))
1858 SCSI_CONFIG_ACCESSOR_READONLY(uCmdPerLun);
1859 else
1860 if (MATCH_SCSI_CONFIG(uEventInfoSize))
1861 SCSI_CONFIG_ACCESSOR_READONLY(uEventInfoSize);
1862 else
1863 if (MATCH_SCSI_CONFIG(uSenseSize))
1864 SCSI_CONFIG_ACCESSOR(uSenseSize);
1865 else
1866 if (MATCH_SCSI_CONFIG(uCdbSize))
1867 SCSI_CONFIG_ACCESSOR(uCdbSize);
1868 else
1869 if (MATCH_SCSI_CONFIG(uMaxChannel))
1870 SCSI_CONFIG_ACCESSOR_READONLY(uMaxChannel);
1871 else
1872 if (MATCH_SCSI_CONFIG(uMaxTarget))
1873 SCSI_CONFIG_ACCESSOR_READONLY(uMaxTarget);
1874 else
1875 if (MATCH_SCSI_CONFIG(uMaxLun))
1876 SCSI_CONFIG_ACCESSOR_READONLY(uMaxLun);
1877 else
1878 {
1879 LogFunc(("Bad access by guest to virtio_scsi_config: uoff=%d, cb=%d\n", uOffset, cb));
1880 rc = VERR_ACCESS_DENIED;
1881 }
1882 return rc;
1883}
1884
1885/**
1886 * virtio-scsi VirtIO Device-specific capabilities read callback
1887 * (other VirtIO capabilities and features are handled in VirtIO implementation)
1888 *
1889 * @param pDevIns The device instance.
1890 * @param uOffset Offset within device specific capabilities struct
1891 * @param pv Buffer in which to save read data
1892 * @param cb Number of bytes to read
1893 */
1894static DECLCALLBACK(int) virtioScsiDevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
1895{
1896 int rc = VINF_SUCCESS;
1897 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1898
1899 rc = virtioScsiCfgAccessed(pThis, uOffset, pv, cb, false);
1900
1901 return rc;
1902}
1903
1904/**
1905 * virtio-scsi VirtIO Device-specific capabilities read callback
1906 * (other VirtIO capabilities and features are handled in VirtIO implementation)
1907 *
1908 * @param pDevIns The device instance.
1909 * @param uOffset Offset within device specific capabilities struct
1910 * @param pv Buffer in which to save read data
1911 * @param cb Number of bytes to write
1912 */
1913static DECLCALLBACK(int) virtioScsiDevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
1914{
1915 int rc = VINF_SUCCESS;
1916 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1917
1918 rc = virtioScsiCfgAccessed(pThis, uOffset, pv, cb, true);
1919
1920 return rc;
1921}
1922
1923/**
1924 * Device relocation callback.
1925 *
1926 * When this callback is called the device instance data, and if the
1927 * device have a GC component, is being relocated, or/and the selectors
1928 * have been changed. The device must use the chance to perform the
1929 * necessary pointer relocations and data updates.
1930 *
1931 * Before the GC code is executed the first time, this function will be
1932 * called with a 0 delta so GC pointer calculations can be one in one place.
1933 *
1934 * @param pDevIns Pointer to the device instance.
1935 * @param offDelta The relocation delta relative to the old location.
1936 *
1937 * @remark A relocation CANNOT fail.
1938 */
1939static DECLCALLBACK(void) virtioScsiRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1940{
1941 LogFunc(("Relocating virtio-scsi"));
1942 RT_NOREF(offDelta);
1943 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1944
1945 pThis->pDevInsR3 = pDevIns;
1946
1947 for (uint32_t i = 0; i < pThis->cTargets; i++)
1948 {
1949 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[i];
1950 pTarget->pVirtioScsi = pThis;
1951 }
1952
1953 /*
1954 * Important: Forward to virtio framework!
1955 */
1956 virtioRelocate(pDevIns, offDelta);
1957
1958}
1959
1960static DECLCALLBACK(int) virtioScsiQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
1961 uint32_t *piInstance, uint32_t *piTarget)
1962{
1963 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaPort);
1964 PPDMDEVINS pDevIns = pTarget->pVirtioScsi->CTX_SUFF(pDevIns);
1965
1966 AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
1967 AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
1968 AssertPtrReturn(piTarget, VERR_INVALID_POINTER);
1969
1970 *ppcszController = pDevIns->pReg->szName;
1971 *piInstance = pDevIns->iInstance;
1972 *piTarget = pTarget->iTarget;
1973
1974 return VINF_SUCCESS;
1975}
1976
1977/**
1978 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1979 */
1980static DECLCALLBACK(void *) virtioScsiTargetQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1981{
1982 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IBase);
1983 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pTarget->IBase);
1984 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pTarget->IMediaPort);
1985 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pTarget->IMediaExPort);
1986 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pTarget->ILed);
1987 return NULL;
1988}
1989
1990/**
1991 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1992 */
1993static DECLCALLBACK(void *) virtioScsiDeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1994{
1995 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, IBase);
1996
1997 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
1998 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
1999
2000 return NULL;
2001}
2002
2003/**
2004 * Detach notification.
2005 *
2006 * One harddisk at one port has been unplugged.
2007 * The VM is suspended at this point.
2008 *
2009 * @param pDevIns The device instance.
2010 * @param iTarget The logical unit which is being detached.
2011 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2012 */
2013static DECLCALLBACK(void) virtioScsiDetach(PPDMDEVINS pDevIns, unsigned iTarget, uint32_t fFlags)
2014{
2015 RT_NOREF(fFlags);
2016 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2017 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[iTarget];
2018
2019 LogFunc((""));
2020
2021 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2022 ("virtio-scsi: Device does not support hotplugging\n"));
2023
2024 /*
2025 * Zero some important members.
2026 */
2027 pTarget->fPresent = false;
2028 pTarget->pDrvBase = NULL;
2029}
2030
2031/**
2032 * Attach command.
2033 *
2034 * This is called when we change block driver.
2035 *
2036 * @returns VBox status code.
2037 * @param pDevIns The device instance.
2038 * @param iTarget The logical unit which is being detached.
2039 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2040 */
2041static DECLCALLBACK(int) virtioScsiAttach(PPDMDEVINS pDevIns, unsigned iTarget, uint32_t fFlags)
2042{
2043 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2044 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[iTarget];
2045 int rc;
2046
2047 pThis->pDevInsR3 = pDevIns;
2048 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
2049 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
2050
2051 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2052 ("virtio-scsi: Device does not support hotplugging\n"),
2053 VERR_INVALID_PARAMETER);
2054
2055 /* the usual paranoia */
2056 AssertRelease(!pTarget->pDrvBase);
2057 Assert(pTarget->iTarget == iTarget);
2058
2059 /*
2060 * Try attach the SCSI driver and get the interfaces,
2061 * required as well as optional.
2062 */
2063 rc = PDMDevHlpDriverAttach(pDevIns, pTarget->iTarget, &pDevIns->IBase,
2064 &pTarget->pDrvBase, (const char *)&pTarget->pszTargetName);
2065 if (RT_SUCCESS(rc))
2066 pTarget->fPresent = true;
2067 else
2068 AssertMsgFailed(("Failed to attach %s. rc=%Rrc\n", pTarget->pszTargetName, rc));
2069
2070 if (RT_FAILURE(rc))
2071 {
2072 pTarget->fPresent = false;
2073 pTarget->pDrvBase = NULL;
2074 }
2075 return rc;
2076}
2077
2078static DECLCALLBACK(int) virtioScsiDestruct(PPDMDEVINS pDevIns)
2079{
2080 /*
2081 * Check the versions here as well since the destructor is *always* called.
2082 */
2083
2084 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
2085
2086 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2087
2088 RTMemFree(pThis->paTargetInstances);
2089 pThis->paTargetInstances = NULL;
2090 for (int qIdx = 0; qIdx < VIRTIOSCSI_QUEUE_CNT; qIdx++)
2091 {
2092 PWORKER pWorker = &pThis->aWorker[qIdx];
2093 if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
2094 {
2095 SUPSemEventClose(pThis->pSupDrvSession, pWorker->hEvtProcess);
2096 pWorker->hEvtProcess = NIL_SUPSEMEVENT;
2097 }
2098 }
2099 return VINF_SUCCESS;
2100}
2101
2102static DECLCALLBACK(int) virtioScsiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg){
2103
2104 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2105
2106 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2107 int rc = VINF_SUCCESS;
2108
2109 pThis->pDevInsR3 = pDevIns;
2110 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
2111 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
2112 pThis->pSupDrvSession = PDMDevHlpGetSupDrvSession(pDevIns);
2113
2114 LogFunc(("PDM device instance: %d\n", iInstance));
2115 RTStrPrintf((char *)pThis->szInstance, sizeof(pThis->szInstance), "VIRTIOSCSI%d", iInstance);
2116
2117 /* Usable defaults */
2118 pThis->cTargets = 1;
2119
2120 /*
2121 * Validate and read configuration.
2122 */
2123 if (!CFGMR3AreValuesValid(pCfg,"NumTargets\0"
2124 "Bootable\0"
2125 /* "GCEnabled\0" TBD */
2126 /* "R0Enabled\0" TBD */
2127 ))
2128 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
2129 N_("virtio-scsi configuration error: unknown option specified"));
2130
2131 rc = CFGMR3QueryIntegerDef(pCfg, "NumTargets", &pThis->cTargets, true);
2132 if (RT_FAILURE(rc))
2133 return PDMDEV_SET_ERROR(pDevIns, rc,
2134 N_("virtio-scsi configuration error: failed to read NumTargets as integer"));
2135 LogFunc(("NumTargets=%d\n", pThis->cTargets));
2136
2137 rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &pThis->fBootable, true);
2138 if (RT_FAILURE(rc))
2139 return PDMDEV_SET_ERROR(pDevIns, rc,
2140 N_("virtio-scsi configuration error: failed to read Bootable as boolean"));
2141 LogFunc(("Bootable=%RTbool (unimplemented)\n", pThis->fBootable));
2142
2143 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, false);
2144 if (RT_FAILURE(rc))
2145 return PDMDEV_SET_ERROR(pDevIns, rc,
2146 N_("virtio-scsi configuration error: failed to read R0Enabled as boolean"));
2147
2148 rc = CFGMR3QueryBoolDef(pCfg, "RCEnabled", &pThis->fRCEnabled, false);
2149 if (RT_FAILURE(rc))
2150 return PDMDEV_SET_ERROR(pDevIns, rc,
2151 N_("virtio-scsi configuration error: failed to read RCEnabled as boolean"));
2152
2153 VIRTIOPCIPARAMS virtioPciParams, *pVirtioPciParams = &virtioPciParams;
2154 pVirtioPciParams->uDeviceId = PCI_DEVICE_ID_VIRTIOSCSI_HOST;
2155 pVirtioPciParams->uClassBase = PCI_CLASS_BASE_MASS_STORAGE;
2156 pVirtioPciParams->uClassSub = PCI_CLASS_SUB_SCSI_STORAGE_CONTROLLER;
2157 pVirtioPciParams->uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
2158 pVirtioPciParams->uSubsystemId = PCI_DEVICE_ID_VIRTIOSCSI_HOST; /* Virtio 1.0 spec allows PCI Device ID here */
2159 pVirtioPciParams->uInterruptLine = 0x00;
2160 pVirtioPciParams->uInterruptPin = 0x01;
2161
2162 pThis->IBase.pfnQueryInterface = virtioScsiDeviceQueryInterface;
2163
2164 /* Configure virtio_scsi_config that transacts via VirtIO implementation's Dev. Specific Cap callbacks */
2165 pThis->virtioScsiConfig.uNumQueues = VIRTIOSCSI_REQ_QUEUE_CNT;
2166 pThis->virtioScsiConfig.uSegMax = VIRTIOSCSI_MAX_SEG_COUNT;
2167 pThis->virtioScsiConfig.uMaxSectors = VIRTIOSCSI_MAX_SECTORS_HINT;
2168 pThis->virtioScsiConfig.uCmdPerLun = VIRTIOSCSI_MAX_COMMANDS_PER_LUN;
2169 pThis->virtioScsiConfig.uEventInfoSize = sizeof(VIRTIOSCSI_EVENT_T); /* Spec says at least this size! */
2170 pThis->virtioScsiConfig.uSenseSize = VIRTIOSCSI_SENSE_SIZE_DEFAULT;
2171 pThis->virtioScsiConfig.uCdbSize = VIRTIOSCSI_CDB_SIZE_DEFAULT;
2172 pThis->virtioScsiConfig.uMaxChannel = VIRTIOSCSI_MAX_CHANNEL_HINT;
2173 pThis->virtioScsiConfig.uMaxTarget = pThis->cTargets;
2174 pThis->virtioScsiConfig.uMaxLun = VIRTIOSCSI_MAX_LUN;
2175
2176 rc = virtioConstruct(pDevIns, pThis, &pThis->hVirtio, pVirtioPciParams, pThis->szInstance,
2177 VIRTIOSCSI_HOST_SCSI_FEATURES_OFFERED,
2178 virtioScsiDevCapRead,
2179 virtioScsiDevCapWrite,
2180 virtioScsiStatusChanged,
2181 virtioScsiNotified,
2182 virtioScsiLiveExec,
2183 virtioScsiSaveExec,
2184 virtioScsiLoadExec,
2185 virtioScsiLoadDone,
2186 sizeof(VIRTIOSCSI_CONFIG_T) /* cbDevSpecificCap */,
2187 (void *)&pThis->virtioScsiConfig /* pDevSpecificCap */);
2188
2189 if (RT_FAILURE(rc))
2190 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi: failed to initialize VirtIO"));
2191
2192 RTStrCopy((char *)pThis->szQueueNames[CONTROLQ_IDX], VIRTIO_MAX_QUEUE_NAME_SIZE, "controlq");
2193 RTStrCopy((char *)pThis->szQueueNames[EVENTQ_IDX], VIRTIO_MAX_QUEUE_NAME_SIZE, "eventq");
2194 for (uint16_t qIdx = VIRTQ_REQ_BASE; qIdx < VIRTQ_REQ_BASE + VIRTIOSCSI_REQ_QUEUE_CNT; qIdx++)
2195 RTStrPrintf((char *)pThis->szQueueNames[qIdx], VIRTIO_MAX_QUEUE_NAME_SIZE,
2196 "requestq<%d>", qIdx - VIRTQ_REQ_BASE);
2197
2198 for (uint16_t qIdx = 0; qIdx < VIRTIOSCSI_QUEUE_CNT; qIdx++)
2199 {
2200 rc = virtioQueueAttach(pThis->hVirtio, qIdx, QUEUENAME(qIdx));
2201 AssertMsgReturn(rc == VINF_SUCCESS, ("Failed to attach queue %s\n", QUEUENAME(qIdx)), rc);
2202 pThis->fQueueAttached[qIdx] = (rc == VINF_SUCCESS);
2203
2204 if (qIdx == CONTROLQ_IDX || IS_REQ_QUEUE(qIdx))
2205 {
2206 rc = PDMDevHlpThreadCreate(pDevIns, &pThis->aWorker[qIdx].pThread,
2207 (void *)(uint64_t)qIdx, virtioScsiWorker,
2208 virtioScsiWorkerWakeUp, 0, RTTHREADTYPE_IO, QUEUENAME(qIdx));
2209 if (rc != VINF_SUCCESS)
2210 {
2211 LogRel(("Error creating thread for Virtual Queue %s\n", QUEUENAME(qIdx)));
2212 return rc;
2213 }
2214
2215 rc = SUPSemEventCreate(pThis->pSupDrvSession, &pThis->aWorker[qIdx].hEvtProcess);
2216 if (RT_FAILURE(rc))
2217 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2218 N_("DevVirtioSCSI: Failed to create SUP event semaphore"));
2219 }
2220 }
2221
2222#ifdef BOOTABLE_SUPPORT_TBD
2223 if (fBootable)
2224 {
2225 /* Register I/O port space for BIOS access. */
2226 rc = PDMDevHlpIOPortRegister(pDevIns, VIRTIOSCSI_BIOS_IO_PORT, 4, NULL,
2227 virtioScsiBiosIoPortWrite, virtioScsiBiosIoPortRead,
2228 virtioScsiBiosIoPortWriteStr, virtioScsiBiosIoPortReadStr,
2229 "virtio-scsi BIOS");
2230 if (RT_FAILURE(rc))
2231 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi cannot register BIOS I/O handlers"));
2232 }
2233#endif
2234
2235 /* Initialize per device instance. */
2236
2237 Log2Func(("Found %d targets attached to controller\n", pThis->cTargets));
2238
2239 pThis->paTargetInstances = (PVIRTIOSCSITARGET)RTMemAllocZ(sizeof(VIRTIOSCSITARGET) * pThis->cTargets);
2240 if (!pThis->paTargetInstances)
2241 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to allocate memory for target states"));
2242
2243 for (RTUINT iTarget = 0; iTarget < pThis->cTargets; iTarget++)
2244 {
2245 PVIRTIOSCSITARGET pTarget = &pThis->paTargetInstances[iTarget];
2246
2247 if (RTStrAPrintf(&pTarget->pszTargetName, "VSCSI%u", iTarget) < 0)
2248 AssertLogRelFailedReturn(VERR_NO_MEMORY);
2249
2250 /* Initialize static parts of the device. */
2251 pTarget->iTarget = iTarget;
2252 pTarget->pVirtioScsi = pThis;
2253 pTarget->led.u32Magic = PDMLED_MAGIC;
2254
2255 pTarget->IBase.pfnQueryInterface = virtioScsiTargetQueryInterface;
2256
2257 /* IMediaPort and IMediaExPort interfaces provide callbacks for VD media and downstream driver access */
2258 pTarget->IMediaPort.pfnQueryDeviceLocation = virtioScsiQueryDeviceLocation;
2259 pTarget->IMediaExPort.pfnIoReqCompleteNotify = virtioScsiIoReqFinish;
2260 pTarget->IMediaExPort.pfnIoReqCopyFromBuf = virtioScsiIoReqCopyFromBuf;
2261 pTarget->IMediaExPort.pfnIoReqCopyToBuf = virtioScsiIoReqCopyToBuf;
2262 pTarget->IMediaExPort.pfnIoReqStateChanged = virtioScsiIoReqStateChanged;
2263 pTarget->IMediaExPort.pfnMediumEjected = virtioScsiMediumEjected;
2264 pTarget->IMediaExPort.pfnIoReqQueryBuf = NULL; /* When used avoids copyFromBuf CopyToBuf*/
2265 pTarget->IMediaExPort.pfnIoReqQueryDiscardRanges = NULL;
2266
2267
2268 pTarget->IBase.pfnQueryInterface = virtioScsiTargetQueryInterface;
2269 pTarget->ILed.pfnQueryStatusLed = virtioScsiTargetQueryStatusLed;
2270 pThis->ILeds.pfnQueryStatusLed = virtioScsiDeviceQueryStatusLed;
2271 pTarget->led.u32Magic = PDMLED_MAGIC;
2272
2273 LogFunc(("Attaching LUN: %s\n", pTarget->pszTargetName));
2274
2275 AssertReturn(iTarget < pThis->cTargets, VERR_PDM_NO_SUCH_LUN);
2276 rc = PDMDevHlpDriverAttach(pDevIns, iTarget, &pTarget->IBase, &pTarget->pDrvBase, pTarget->pszTargetName);
2277 if (RT_SUCCESS(rc))
2278 {
2279 pTarget->fPresent = true;
2280
2281 pTarget->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIA);
2282 AssertMsgReturn(VALID_PTR(pTarget->pDrvMedia),
2283 ("virtio-scsi configuration error: LUN#%d missing basic media interface!\n", iTarget),
2284 VERR_PDM_MISSING_INTERFACE);
2285
2286 /* Get the extended media interface. */
2287 pTarget->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIAEX);
2288 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
2289 ("virtio-scsi configuration error: LUN#%d missing extended media interface!\n", iTarget),
2290 VERR_PDM_MISSING_INTERFACE);
2291
2292 rc = pTarget->pDrvMediaEx->pfnIoReqAllocSizeSet(pTarget->pDrvMediaEx, sizeof(VIRTIOSCSIREQ));
2293 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
2294 ("virtio-scsi configuration error: LUN#%u: Failed to set I/O request size!\n", iTarget),
2295 rc);
2296
2297 pTarget->pMediaNotify = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIANOTIFY);
2298 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
2299 ("virtio-scsi configuration error: LUN#%u: Failed to get set Media notify obj!\n",
2300 iTarget), rc);
2301
2302 }
2303 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2304 {
2305 pTarget->fPresent = false;
2306 pTarget->pDrvBase = NULL;
2307 rc = VINF_SUCCESS;
2308 Log(("virtio-scsi: no driver attached to device %s\n", pTarget->pszTargetName));
2309 }
2310 else
2311 {
2312 AssertLogRelMsgFailed(("virtio-scsi: Failed to attach %s: %Rrc\n", pTarget->pszTargetName, rc));
2313 return rc;
2314 }
2315 }
2316
2317 /* Status driver */
2318 PPDMIBASE pUpBase;
2319 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pUpBase, "Status Port");
2320 if (RT_FAILURE(rc))
2321 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
2322
2323 /*
2324 * Register the debugger info callback.
2325 */
2326 char szTmp[128];
2327 RTStrPrintf(szTmp, sizeof(szTmp), "%s%d", pDevIns->pReg->szName, pDevIns->iInstance);
2328 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "virtio-scsi info", virtioScsiInfo);
2329
2330 return rc;
2331}
2332
2333/**
2334 * The device registration structure.
2335 */
2336const PDMDEVREG g_DeviceVirtioSCSI =
2337{
2338 /* .u32Version = */ PDM_DEVREG_VERSION,
2339 /* .uReserved0 = */ 0,
2340 /* .szName = */ "virtio-scsi",
2341#ifdef VIRTIOSCSI_GC_SUPPORT
2342 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0
2343 | PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION
2344 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
2345#else
2346 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS
2347 | PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION
2348 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
2349#endif
2350 /* .fClass = */ PDM_DEVREG_CLASS_MISC,
2351 /* .cMaxInstances = */ ~0U,
2352 /* .uSharedVersion = */ 42,
2353 /* .cbInstanceShared = */ sizeof(VIRTIOSCSI),
2354 /* .cbInstanceCC = */ 0,
2355 /* .cbInstanceRC = */ 0,
2356 /* .cMaxPciDevices = */ 1,
2357 /* .cMaxMsixVectors = */ 0,
2358 /* .pszDescription = */ "Virtio Host SCSI.\n",
2359#if defined(IN_RING3)
2360 /* .pszRCMod = */ "VBoxDDRC.rc",
2361 /* .pszR0Mod = */ "VBoxDDR0.r0",
2362 /* .pfnConstruct = */ virtioScsiConstruct,
2363 /* .pfnDestruct = */ virtioScsiDestruct,
2364 /* .pfnRelocate = */ virtioScsiRelocate,
2365 /* .pfnMemSetup = */ NULL,
2366 /* .pfnPowerOn = */ NULL,
2367 /* .pfnReset = */ virtioScsiReset,
2368 /* .pfnSuspend = */ virtioScsiSuspendOrPoweroff,
2369 /* .pfnResume = */ virtioScsiResume,
2370 /* .pfnAttach = */ virtioScsiAttach,
2371 /* .pfnDetach = */ virtioScsiDetach,
2372 /* .pfnQueryInterface = */ NULL,
2373 /* .pfnInitComplete = */ NULL,
2374 /* .pfnPowerOff = */ virtioScsiSuspendOrPoweroff,
2375 /* .pfnSoftReset = */ NULL,
2376 /* .pfnReserved0 = */ NULL,
2377 /* .pfnReserved1 = */ NULL,
2378 /* .pfnReserved2 = */ NULL,
2379 /* .pfnReserved3 = */ NULL,
2380 /* .pfnReserved4 = */ NULL,
2381 /* .pfnReserved5 = */ NULL,
2382 /* .pfnReserved6 = */ NULL,
2383 /* .pfnReserved7 = */ NULL,
2384#elif defined(IN_RING0)
2385 /* .pfnEarlyConstruct = */ NULL,
2386 /* .pfnConstruct = */ NULL,
2387 /* .pfnDestruct = */ NULL,
2388 /* .pfnFinalDestruct = */ NULL,
2389 /* .pfnRequest = */ NULL,
2390 /* .pfnReserved0 = */ NULL,
2391 /* .pfnReserved1 = */ NULL,
2392 /* .pfnReserved2 = */ NULL,
2393 /* .pfnReserved3 = */ NULL,
2394 /* .pfnReserved4 = */ NULL,
2395 /* .pfnReserved5 = */ NULL,
2396 /* .pfnReserved6 = */ NULL,
2397 /* .pfnReserved7 = */ NULL,
2398#elif defined(IN_RC)
2399 /* .pfnConstruct = */ NULL,
2400 /* .pfnReserved0 = */ NULL,
2401 /* .pfnReserved1 = */ NULL,
2402 /* .pfnReserved2 = */ NULL,
2403 /* .pfnReserved3 = */ NULL,
2404 /* .pfnReserved4 = */ NULL,
2405 /* .pfnReserved5 = */ NULL,
2406 /* .pfnReserved6 = */ NULL,
2407 /* .pfnReserved7 = */ NULL,
2408#else
2409# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2410#endif
2411 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2412};
2413
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