VirtualBox

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

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

Storage/DevVirtioSCSI.cpp: While trying to diagnose issue seen with EFI boot, fixed problem it had working on Windows. At least Windows was able to find and format a disk

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