VirtualBox

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

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

Storage/DevVirtioSCSI.cpp: Fix burn caused by unused function.

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