VirtualBox

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

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

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

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