VirtualBox

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

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

Storage/DevVirtioSCSI.cpp: Fix burns for unused variables

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

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