VirtualBox

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

Last change on this file since 99027 was 98661, checked in by vboxsync, 2 years ago

Devices/DevVirtioSCSI: Possible fix for bugref:9440#c164, the queue head can get out of sync if two threads complete I/O requests concurrently

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 121.5 KB
Line 
1/* $Id: DevVirtioSCSI.cpp 98661 2023-02-20 16:31:14Z vboxsync $ */
2/** @file
3 * VBox storage devices - Virtio SCSI Driver
4 *
5 * Log-levels used:
6 * - Level 1: The most important (but usually rare) things to note
7 * - Level 2: SCSI command logging
8 * - Level 3: Vector and I/O transfer summary (shows what client sent an expects and fulfillment)
9 * - Level 6: Device <-> Guest Driver negotation, traffic, notifications and state handling
10 * - Level 12: Brief formatted hex dumps of I/O data
11 */
12
13/*
14 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
15 *
16 * This file is part of VirtualBox base platform packages, as
17 * available from https://www.virtualbox.org.
18 *
19 * This program is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU General Public License
21 * as published by the Free Software Foundation, in version 3 of the
22 * License.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, see <https://www.gnu.org/licenses>.
31 *
32 * SPDX-License-Identifier: GPL-3.0-only
33 */
34
35
36/*********************************************************************************************************************************
37* Header Files *
38*********************************************************************************************************************************/
39//#define LOG_GROUP LOG_GROUP_DRV_SCSI
40#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
41
42#include <VBox/vmm/pdmdev.h>
43#include <VBox/vmm/pdmstorageifs.h>
44#include <VBox/vmm/pdmcritsect.h>
45#include <VBox/AssertGuest.h>
46#include <VBox/msi.h>
47#include <VBox/version.h>
48#include <VBox/log.h>
49#include <iprt/errcore.h>
50#include <iprt/assert.h>
51#include <iprt/string.h>
52#include <VBox/sup.h>
53#include "../build/VBoxDD.h"
54#include <VBox/scsi.h>
55#ifdef IN_RING3
56# include <iprt/alloc.h>
57# include <iprt/memcache.h>
58# include <iprt/semaphore.h>
59# include <iprt/sg.h>
60# include <iprt/param.h>
61# include <iprt/uuid.h>
62#endif
63#include "../VirtIO/VirtioCore.h"
64
65#include "VBoxSCSI.h"
66#include "VBoxDD.h"
67
68
69/*********************************************************************************************************************************
70* Defined Constants And Macros *
71*********************************************************************************************************************************/
72/** The current saved state version. */
73#define VIRTIOSCSI_SAVED_STATE_VERSION UINT32_C(1)
74
75
76#define LUN0 0
77/** @name VirtIO 1.0 SCSI Host feature bits (See VirtIO 1.0 specification, Section 5.6.3)
78 * @{ */
79#define VIRTIO_SCSI_F_INOUT RT_BIT_64(0) /** Request is device readable AND writeable */
80#define VIRTIO_SCSI_F_HOTPLUG RT_BIT_64(1) /** Host allows hotplugging SCSI LUNs & targets */
81#define VIRTIO_SCSI_F_CHANGE RT_BIT_64(2) /** Host LUNs chgs via VIRTIOSCSI_T_PARAM_CHANGE evt */
82#define VIRTIO_SCSI_F_T10_PI RT_BIT_64(3) /** Add T10 port info (DIF/DIX) in SCSI req hdr */
83/** @} */
84
85
86#define VIRTIOSCSI_HOST_SCSI_FEATURES_ALL \
87 (VIRTIO_SCSI_F_INOUT | VIRTIO_SCSI_F_HOTPLUG | VIRTIO_SCSI_F_CHANGE | VIRTIO_SCSI_F_T10_PI)
88
89#define VIRTIOSCSI_HOST_SCSI_FEATURES_NONE 0
90
91#define VIRTIOSCSI_HOST_SCSI_FEATURES_OFFERED VIRTIOSCSI_HOST_SCSI_FEATURES_NONE
92
93#define VIRTIOSCSI_REQ_VIRTQ_CNT 4 /**< T.B.D. Consider increasing */
94#define VIRTIOSCSI_VIRTQ_CNT (VIRTIOSCSI_REQ_VIRTQ_CNT + 2)
95#define VIRTIOSCSI_MAX_TARGETS 256 /**< T.B.D. Figure out a a good value for this. */
96#define VIRTIOSCSI_MAX_LUN 1 /**< VirtIO specification, section 5.6.4 */
97#define VIRTIOSCSI_MAX_COMMANDS_PER_LUN 128 /**< T.B.D. What is a good value for this? */
98#define VIRTIOSCSI_MAX_SEG_COUNT 126 /**< T.B.D. What is a good value for this? */
99#define VIRTIOSCSI_MAX_SECTORS_HINT 0x10000 /**< VirtIO specification, section 5.6.4 */
100#define VIRTIOSCSI_MAX_CHANNEL_HINT 0 /**< VirtIO specification, section 5.6.4 should be 0 */
101
102#define PCI_DEVICE_ID_VIRTIOSCSI_HOST 0x1048 /**< Informs guest driver of type of VirtIO device */
103#define PCI_CLASS_BASE_MASS_STORAGE 0x01 /**< PCI Mass Storage device class */
104#define PCI_CLASS_SUB_SCSI_STORAGE_CONTROLLER 0x00 /**< PCI SCSI Controller subclass */
105#define PCI_CLASS_PROG_UNSPECIFIED 0x00 /**< Programming interface. N/A. */
106#define VIRTIOSCSI_PCI_CLASS 0x01 /**< Base class Mass Storage? */
107
108#define VIRTIOSCSI_SENSE_SIZE_DEFAULT 96 /**< VirtIO 1.0: 96 on reset, guest can change */
109#define VIRTIOSCSI_SENSE_SIZE_MAX 4096 /**< Picked out of thin air by bird. */
110#define VIRTIOSCSI_CDB_SIZE_DEFAULT 32 /**< VirtIO 1.0: 32 on reset, guest can change */
111#define VIRTIOSCSI_CDB_SIZE_MAX 255 /**< Picked out of thin air by bird. */
112#define VIRTIOSCSI_PI_BYTES_IN 1 /**< Value TBD (see section 5.6.6.1) */
113#define VIRTIOSCSI_PI_BYTES_OUT 1 /**< Value TBD (see section 5.6.6.1) */
114#define VIRTIOSCSI_DATA_OUT 512 /**< Value TBD (see section 5.6.6.1) */
115
116/**
117 * VirtIO SCSI Host Device device-specific queue indicies.
118 * (Note: # of request queues is determined by virtio_scsi_config.num_queues. VirtIO 1.0, 5.6.4)
119 */
120#define CONTROLQ_IDX 0 /**< VirtIO Spec-defined Index of control queue */
121#define EVENTQ_IDX 1 /**< VirtIO Spec-defined Index of event queue */
122#define VIRTQ_REQ_BASE 2 /**< VirtIO Spec-defined base index of req. queues */
123
124#define VIRTQNAME(uVirtqNbr) (pThis->aszVirtqNames[uVirtqNbr]) /**< Macro to get queue name from its index */
125#define CBVIRTQNAME(uVirtqNbr) RTStrNLen(VIRTQNAME(uVirtqNbr), sizeof(VIRTQNAME(uVirtqNbr)))
126
127#define IS_REQ_VIRTQ(uVirtqNbr) (uVirtqNbr >= VIRTQ_REQ_BASE && uVirtqNbr < VIRTIOSCSI_VIRTQ_CNT)
128
129#define VIRTIO_IS_IN_DIRECTION(pMediaExTxDirEnumValue) \
130 ((pMediaExTxDirEnumValue) == PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE)
131
132#define VIRTIO_IS_OUT_DIRECTION(pMediaExTxDirEnumValue) \
133 ((pMediaExTxDirEnumValue) == PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE)
134
135#define IS_VIRTQ_EMPTY(pDevIns, pVirtio, uVirtqNbr) \
136 (virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr) == 0)
137
138
139/*********************************************************************************************************************************
140* Structures and Typedefs *
141*********************************************************************************************************************************/
142/**
143 * VirtIO SCSI Host Device device-specific configuration (see VirtIO 1.0, section 5.6.4)
144 * VBox VirtIO core issues callback to this VirtIO device-specific implementation to handle
145 * MMIO accesses to device-specific configuration parameters.
146 */
147typedef struct virtio_scsi_config
148{
149 uint32_t uNumVirtqs; /**< num_queues \# of req q's exposed by dev */
150 uint32_t uSegMax; /**< seg_max Max \# of segs allowed in cmd */
151 uint32_t uMaxSectors; /**< max_sectors Hint to guest max xfer to use */
152 uint32_t uCmdPerLun; /**< cmd_per_lun Max \# of link cmd sent per lun */
153 uint32_t uEventInfoSize; /**< event_info_size Fill max, evtq bufs */
154 uint32_t uSenseSize; /**< sense_size Max sense data size dev writes */
155 uint32_t uCdbSize; /**< cdb_size Max CDB size driver writes */
156 uint16_t uMaxChannel; /**< max_channel Hint to guest driver */
157 uint16_t uMaxTarget; /**< max_target Hint to guest driver */
158 uint32_t uMaxLun; /**< max_lun Hint to guest driver */
159} VIRTIOSCSI_CONFIG_T, PVIRTIOSCSI_CONFIG_T;
160
161/** @name VirtIO 1.0 SCSI Host Device device specific control types
162 * @{ */
163#define VIRTIOSCSI_T_NO_EVENT 0
164#define VIRTIOSCSI_T_TRANSPORT_RESET 1
165#define VIRTIOSCSI_T_ASYNC_NOTIFY 2 /**< Asynchronous notification */
166#define VIRTIOSCSI_T_PARAM_CHANGE 3
167/** @} */
168
169/**
170 * Device operation: eventq
171 */
172#define VIRTIOSCSI_T_EVENTS_MISSED UINT32_C(0x80000000)
173typedef struct virtio_scsi_event
174{
175 // Device-writable part
176 uint32_t uEvent; /**< event */
177 uint8_t abVirtioLun[8]; /**< lun */
178 uint32_t uReason; /**< reason */
179} VIRTIOSCSI_EVENT_T, *PVIRTIOSCSI_EVENT_T;
180
181/** @name VirtIO 1.0 SCSI Host Device device specific event types
182 * @{ */
183#define VIRTIOSCSI_EVT_RESET_HARD 0 /**< */
184#define VIRTIOSCSI_EVT_RESET_RESCAN 1 /**< */
185#define VIRTIOSCSI_EVT_RESET_REMOVED 2 /**< */
186/** @} */
187
188/**
189 * Device operation: reqestq
190 */
191#pragma pack(1)
192typedef struct REQ_CMD_HDR_T
193{
194 uint8_t abVirtioLun[8]; /**< lun */
195 uint64_t uId; /**< id */
196 uint8_t uTaskAttr; /**< task_attr */
197 uint8_t uPrio; /**< prio */
198 uint8_t uCrn; /**< crn */
199} REQ_CMD_HDR_T;
200#pragma pack()
201AssertCompileSize(REQ_CMD_HDR_T, 19);
202
203typedef struct REQ_CMD_PI_T
204{
205 uint32_t uPiBytesOut; /**< pi_bytesout */
206 uint32_t uPiBytesIn; /**< pi_bytesin */
207} REQ_CMD_PI_T;
208AssertCompileSize(REQ_CMD_PI_T, 8);
209
210typedef struct REQ_RESP_HDR_T
211{
212 uint32_t cbSenseLen; /**< sense_len */
213 uint32_t uResidual; /**< residual */
214 uint16_t uStatusQualifier; /**< status_qualifier */
215 uint8_t uStatus; /**< status SCSI status code */
216 uint8_t uResponse; /**< response */
217} REQ_RESP_HDR_T;
218AssertCompileSize(REQ_RESP_HDR_T, 12);
219
220#pragma pack(1)
221typedef struct VIRTIOSCSI_REQ_CMD_T
222{
223 /** Device-readable section
224 * @{ */
225 REQ_CMD_HDR_T ReqHdr;
226 uint8_t uCdb[1]; /**< cdb */
227
228 REQ_CMD_PI_T piHdr; /**< T10 Pi block integrity (optional feature) */
229 uint8_t uPiOut[1]; /**< pi_out[] T10 pi block integrity */
230 uint8_t uDataOut[1]; /**< dataout */
231 /** @} */
232
233 /** @name Device writable section
234 * @{ */
235 REQ_RESP_HDR_T respHdr;
236 uint8_t uSense[1]; /**< sense */
237 uint8_t uPiIn[1]; /**< pi_in[] T10 Pi block integrity */
238 uint8_t uDataIn[1]; /**< detain; */
239 /** @} */
240} VIRTIOSCSI_REQ_CMD_T, *PVIRTIOSCSI_REQ_CMD_T;
241#pragma pack()
242AssertCompileSize(VIRTIOSCSI_REQ_CMD_T, 19+8+12+6);
243
244/** @name VirtIO 1.0 SCSI Host Device Req command-specific response values
245 * @{ */
246#define VIRTIOSCSI_S_OK 0 /**< control, command */
247#define VIRTIOSCSI_S_OVERRUN 1 /**< control */
248#define VIRTIOSCSI_S_ABORTED 2 /**< control */
249#define VIRTIOSCSI_S_BAD_TARGET 3 /**< control, command */
250#define VIRTIOSCSI_S_RESET 4 /**< control */
251#define VIRTIOSCSI_S_BUSY 5 /**< control, command */
252#define VIRTIOSCSI_S_TRANSPORT_FAILURE 6 /**< control, command */
253#define VIRTIOSCSI_S_TARGET_FAILURE 7 /**< control, command */
254#define VIRTIOSCSI_S_NEXUS_FAILURE 8 /**< control, command */
255#define VIRTIOSCSI_S_FAILURE 9 /**< control, command */
256#define VIRTIOSCSI_S_INCORRECT_LUN 12 /**< command */
257/** @} */
258
259/** @name VirtIO 1.0 SCSI Host Device command-specific task_attr values
260 * @{ */
261#define VIRTIOSCSI_S_SIMPLE 0 /**< */
262#define VIRTIOSCSI_S_ORDERED 1 /**< */
263#define VIRTIOSCSI_S_HEAD 2 /**< */
264#define VIRTIOSCSI_S_ACA 3 /**< */
265/** @} */
266
267/**
268 * VirtIO 1.0 SCSI Host Device Control command before we know type (5.6.6.2)
269 */
270typedef struct VIRTIOSCSI_CTRL_T
271{
272 uint32_t uType;
273} VIRTIOSCSI_CTRL_T, *PVIRTIOSCSI_CTRL_T;
274
275/** @name VirtIO 1.0 SCSI Host Device command-specific TMF values
276 * @{ */
277#define VIRTIOSCSI_T_TMF 0 /**< */
278#define VIRTIOSCSI_T_TMF_ABORT_TASK 0 /**< */
279#define VIRTIOSCSI_T_TMF_ABORT_TASK_SET 1 /**< */
280#define VIRTIOSCSI_T_TMF_CLEAR_ACA 2 /**< */
281#define VIRTIOSCSI_T_TMF_CLEAR_TASK_SET 3 /**< */
282#define VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET 4 /**< */
283#define VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET 5 /**< */
284#define VIRTIOSCSI_T_TMF_QUERY_TASK 6 /**< */
285#define VIRTIOSCSI_T_TMF_QUERY_TASK_SET 7 /**< */
286/** @} */
287
288#pragma pack(1)
289typedef struct VIRTIOSCSI_CTRL_TMF_T
290{
291 uint32_t uType; /**< type */
292 uint32_t uSubtype; /**< subtype */
293 uint8_t abScsiLun[8]; /**< lun */
294 uint64_t uId; /**< id */
295} VIRTIOSCSI_CTRL_TMF_T, *PVIRTIOSCSI_CTRL_TMF_T;
296#pragma pack()
297AssertCompileSize(VIRTIOSCSI_CTRL_TMF_T, 24);
298
299/** VirtIO 1.0 section 5.6.6.2, CTRL TMF response is an 8-bit status */
300
301/** @name VirtIO 1.0 SCSI Host Device device specific tmf control response values
302 * @{ */
303#define VIRTIOSCSI_S_FUNCTION_COMPLETE 0 /**< */
304#define VIRTIOSCSI_S_FUNCTION_SUCCEEDED 10 /**< */
305#define VIRTIOSCSI_S_FUNCTION_REJECTED 11 /**< */
306/** @} */
307
308#define VIRTIOSCSI_T_AN_QUERY 1 /**< Asynchronous notification query */
309#define VIRTIOSCSI_T_AN_SUBSCRIBE 2 /**< Asynchronous notification subscription */
310
311#pragma pack(1)
312typedef struct VIRTIOSCSI_CTRL_AN_T
313{
314 uint32_t uType; /**< type */
315 uint8_t abScsiLun[8]; /**< lun */
316 uint32_t fEventsRequested; /**< event_requested */
317} VIRTIOSCSI_CTRL_AN_T, *PVIRTIOSCSI_CTRL_AN_T;
318#pragma pack()
319AssertCompileSize(VIRTIOSCSI_CTRL_AN_T, 16);
320
321/** VirtIO 1.0, Section 5.6.6.2, CTRL AN response is 4-byte evt mask + 8-bit status */
322
323typedef union VIRTIO_SCSI_CTRL_UNION_T
324{
325 VIRTIOSCSI_CTRL_T Type;
326 VIRTIOSCSI_CTRL_TMF_T Tmf;
327 VIRTIOSCSI_CTRL_AN_T AsyncNotify;
328 uint8_t ab[24];
329} VIRTIO_SCSI_CTRL_UNION_T, *PVIRTIO_SCSI_CTRL_UNION_T;
330AssertCompile(sizeof(VIRTIO_SCSI_CTRL_UNION_T) == 24); /* VIRTIOSCSI_CTRL_T forces 4 byte alignment, the other two are byte packed. */
331
332/** @name VirtIO 1.0 SCSI Host Device device specific tmf control response values
333 * @{ */
334#define VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE 2 /**< */
335#define VIRTIOSCSI_EVT_ASYNC_POWER_MGMT 4 /**< */
336#define VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST 8 /**< */
337#define VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE 16 /**< */
338#define VIRTIOSCSI_EVT_ASYNC_MULTI_HOST 32 /**< */
339#define VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY 64 /**< */
340/** @} */
341
342#define SUBSCRIBABLE_EVENTS \
343 ( VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE \
344 | VIRTIOSCSI_EVT_ASYNC_POWER_MGMT \
345 | VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST \
346 | VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE \
347 | VIRTIOSCSI_EVT_ASYNC_MULTI_HOST \
348 | VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY )
349
350#define SUPPORTED_EVENTS 0 /* TBD */
351
352/**
353 * Worker thread context, shared state.
354 */
355typedef struct VIRTIOSCSIWORKER
356{
357 SUPSEMEVENT hEvtProcess; /**< handle of associated sleep/wake-up semaphore */
358 bool volatile fSleeping; /**< Flags whether worker thread is sleeping or not */
359 bool volatile fNotified; /**< Flags whether worker thread notified */
360} VIRTIOSCSIWORKER;
361/** Pointer to a VirtIO SCSI worker. */
362typedef VIRTIOSCSIWORKER *PVIRTIOSCSIWORKER;
363
364/**
365 * Worker thread context, ring-3 state.
366 */
367typedef struct VIRTIOSCSIWORKERR3
368{
369 R3PTRTYPE(PPDMTHREAD) pThread; /**< pointer to worker thread's handle */
370 RTCRITSECT CritSectVirtq; /**< Protecting the virtq against concurrent thread access. */
371 uint16_t auRedoDescs[VIRTQ_SIZE]; /**< List of previously suspended reqs to re-submit */
372 uint16_t cRedoDescs; /**< Number of redo desc chain head desc idxes in list */
373} VIRTIOSCSIWORKERR3;
374/** Pointer to a VirtIO SCSI worker. */
375typedef VIRTIOSCSIWORKERR3 *PVIRTIOSCSIWORKERR3;
376
377/**
378 * State of a target attached to the VirtIO SCSI Host
379 */
380typedef struct VIRTIOSCSITARGET
381{
382 /** The ring-3 device instance so we can easily get our bearings. */
383 PPDMDEVINSR3 pDevIns;
384
385 /** Pointer to attached driver's base interface. */
386 R3PTRTYPE(PPDMIBASE) pDrvBase;
387
388 /** Target number (PDM LUN) */
389 uint32_t uTarget;
390
391 /** Target Description */
392 R3PTRTYPE(char *) pszTargetName;
393
394 /** Target base interface. */
395 PDMIBASE IBase;
396
397 /** Flag whether device is present. */
398 bool fPresent;
399
400 /** Media port interface. */
401 PDMIMEDIAPORT IMediaPort;
402
403 /** Pointer to the attached driver's media interface. */
404 R3PTRTYPE(PPDMIMEDIA) pDrvMedia;
405
406 /** Extended media port interface. */
407 PDMIMEDIAEXPORT IMediaExPort;
408
409 /** Pointer to the attached driver's extended media interface. */
410 R3PTRTYPE(PPDMIMEDIAEX) pDrvMediaEx;
411
412 /** Status LED interface */
413 PDMILEDPORTS ILed;
414
415 /** The status LED state for this device. */
416 PDMLED led;
417
418} VIRTIOSCSITARGET, *PVIRTIOSCSITARGET;
419
420/**
421 * VirtIO Host SCSI device state, shared edition.
422 *
423 * @extends VIRTIOCORE
424 */
425typedef struct VIRTIOSCSI
426{
427 /** The core virtio state. */
428 VIRTIOCORE Virtio;
429
430 /** VirtIO Host SCSI device runtime configuration parameters */
431 VIRTIOSCSI_CONFIG_T virtioScsiConfig;
432
433 bool fBootable;
434 bool afPadding0[3];
435
436 /** Number of targets in paTargetInstances. */
437 uint32_t cTargets;
438
439 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
440 VIRTIOSCSIWORKER aWorkers[VIRTIOSCSI_VIRTQ_CNT];
441
442 /** Instance name */
443 char szInstance[16];
444
445 /** Device-specific spec-based VirtIO VIRTQNAMEs */
446 char aszVirtqNames[VIRTIOSCSI_VIRTQ_CNT][VIRTIO_MAX_VIRTQ_NAME_SIZE];
447
448 /** Track which VirtIO queues we've attached to */
449 bool afVirtqAttached[VIRTIOSCSI_VIRTQ_CNT];
450
451 /** Set if events missed due to lack of bufs avail on eventq */
452 bool fEventsMissed;
453
454 /** Explicit alignment padding. */
455 bool afPadding1[2];
456
457 /** Mask of VirtIO Async Event types this device will deliver */
458 uint32_t fAsyncEvtsEnabled;
459
460 /** Total number of requests active across all targets */
461 volatile uint32_t cActiveReqs;
462
463
464 /** True if the guest/driver and VirtIO framework are in the ready state */
465 uint32_t fVirtioReady;
466
467 /** True if VIRTIO_SCSI_F_T10_PI was negotiated */
468 uint32_t fHasT10pi;
469
470 /** True if VIRTIO_SCSI_F_HOTPLUG was negotiated */
471 uint32_t fHasHotplug;
472
473 /** True if VIRTIO_SCSI_F_INOUT was negotiated */
474 uint32_t fHasInOutBufs;
475
476 /** True if VIRTIO_SCSI_F_CHANGE was negotiated */
477 uint32_t fHasLunChange;
478
479 /** True if in the process of resetting */
480 uint32_t fResetting;
481
482} VIRTIOSCSI;
483/** Pointer to the shared state of the VirtIO Host SCSI device. */
484typedef VIRTIOSCSI *PVIRTIOSCSI;
485
486
487/**
488 * VirtIO Host SCSI device state, ring-3 edition.
489 *
490 * @extends VIRTIOCORER3
491 */
492typedef struct VIRTIOSCSIR3
493{
494 /** The core virtio ring-3 state. */
495 VIRTIOCORER3 Virtio;
496
497 /** Array of per-target data. */
498 R3PTRTYPE(PVIRTIOSCSITARGET) paTargetInstances;
499
500 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
501 VIRTIOSCSIWORKERR3 aWorkers[VIRTIOSCSI_VIRTQ_CNT];
502
503 /** Device base interface. */
504 PDMIBASE IBase;
505
506 /** Pointer to the device instance.
507 * @note Only used in interface callbacks. */
508 PPDMDEVINSR3 pDevIns;
509
510 /** Status Target: LEDs port interface. */
511 PDMILEDPORTS ILeds;
512
513 /** IMediaExPort: Media ejection notification */
514 R3PTRTYPE(PPDMIMEDIANOTIFY) pMediaNotify;
515
516 /** Virtq to send tasks to R3. - HC ptr */
517 R3PTRTYPE(PPDMQUEUE) pNotifierVirtqR3;
518
519 /** True if in the process of quiescing I/O */
520 uint32_t fQuiescing;
521
522 /** For which purpose we're quiescing. */
523 VIRTIOVMSTATECHANGED enmQuiescingFor;
524
525} VIRTIOSCSIR3;
526/** Pointer to the ring-3 state of the VirtIO Host SCSI device. */
527typedef VIRTIOSCSIR3 *PVIRTIOSCSIR3;
528
529
530/**
531 * VirtIO Host SCSI device state, ring-0 edition.
532 */
533typedef struct VIRTIOSCSIR0
534{
535 /** The core virtio ring-0 state. */
536 VIRTIOCORER0 Virtio;
537} VIRTIOSCSIR0;
538/** Pointer to the ring-0 state of the VirtIO Host SCSI device. */
539typedef VIRTIOSCSIR0 *PVIRTIOSCSIR0;
540
541
542/**
543 * VirtIO Host SCSI device state, raw-mode edition.
544 */
545typedef struct VIRTIOSCSIRC
546{
547 /** The core virtio raw-mode state. */
548 VIRTIOCORERC Virtio;
549} VIRTIOSCSIRC;
550/** Pointer to the ring-0 state of the VirtIO Host SCSI device. */
551typedef VIRTIOSCSIRC *PVIRTIOSCSIRC;
552
553
554/** @typedef VIRTIOSCSICC
555 * The instance data for the current context. */
556typedef CTX_SUFF(VIRTIOSCSI) VIRTIOSCSICC;
557/** @typedef PVIRTIOSCSICC
558 * Pointer to the instance data for the current context. */
559typedef CTX_SUFF(PVIRTIOSCSI) PVIRTIOSCSICC;
560
561
562/**
563 * Request structure for IMediaEx (Associated Interfaces implemented by DrvSCSI)
564 * @note cbIn, cbOUt, cbDataOut mostly for debugging
565 */
566typedef struct VIRTIOSCSIREQ
567{
568 PDMMEDIAEXIOREQ hIoReq; /**< Handle of I/O request */
569 PVIRTIOSCSITARGET pTarget; /**< Target */
570 uint16_t uVirtqNbr; /**< Index of queue this request arrived on */
571 PVIRTQBUF pVirtqBuf; /**< Prepared desc chain pulled from virtq avail ring */
572 size_t cbDataIn; /**< size of datain buffer */
573 size_t cbDataOut; /**< size of dataout buffer */
574 uint16_t uDataInOff; /**< Fixed size of respHdr + sense (precede datain) */
575 uint16_t uDataOutOff; /**< Fixed size of reqhdr + cdb (precede dataout) */
576 uint32_t cbSenseAlloc; /**< Size of sense buffer */
577 size_t cbSenseLen; /**< Receives \# bytes written into sense buffer */
578 uint8_t *pbSense; /**< Pointer to R3 sense buffer */
579 PDMMEDIAEXIOREQSCSITXDIR enmTxDir; /**< Receives transfer direction of I/O req */
580 uint8_t uStatus; /**< SCSI status code */
581} VIRTIOSCSIREQ;
582typedef VIRTIOSCSIREQ *PVIRTIOSCSIREQ;
583
584
585/**
586 * callback_method_impl{VIRTIOCORER0,pfnVirtqNotified}
587 * @todo this causes burn if I prefix with at-sign. This callback is in VIRTIOCORER0 and VIRTIOCORER3
588 */
589static DECLCALLBACK(void) virtioScsiNotified(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
590{
591
592 RT_NOREF(pVirtio);
593 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
594
595 AssertReturnVoid(uVirtqNbr < VIRTIOSCSI_VIRTQ_CNT);
596 PVIRTIOSCSIWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
597
598#if defined (IN_RING3) && defined (LOG_ENABLED)
599 RTLogFlush(NULL);
600#endif
601
602 if (uVirtqNbr == CONTROLQ_IDX || IS_REQ_VIRTQ(uVirtqNbr))
603 {
604 Log6Func(("%s has available data\n", VIRTQNAME(uVirtqNbr)));
605 /* Wake queue's worker thread up if sleeping */
606 if (!ASMAtomicXchgBool(&pWorker->fNotified, true))
607 {
608 if (ASMAtomicReadBool(&pWorker->fSleeping))
609 {
610 Log6Func(("waking %s worker.\n", VIRTQNAME(uVirtqNbr)));
611 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
612 AssertRC(rc);
613 }
614 }
615 }
616 else if (uVirtqNbr == EVENTQ_IDX)
617 {
618 Log3Func(("Driver queued buffer(s) to %s\n", VIRTQNAME(uVirtqNbr)));
619// if (ASMAtomicXchgBool(&pThis->fEventsMissed, false))
620// virtioScsiR3ReportEventsMissed(pDevIns, pThis, 0);
621 }
622 else
623 LogFunc(("Unexpected queue idx (ignoring): %d\n", uVirtqNbr));
624}
625
626
627#ifdef IN_RING3 /* spans most of the file, at the moment. */
628
629
630DECLINLINE(void) virtioScsiSetVirtqNames(PVIRTIOSCSI pThis)
631{
632 RTStrCopy(pThis->aszVirtqNames[CONTROLQ_IDX], VIRTIO_MAX_VIRTQ_NAME_SIZE, "controlq");
633 RTStrCopy(pThis->aszVirtqNames[EVENTQ_IDX], VIRTIO_MAX_VIRTQ_NAME_SIZE, "eventq");
634 for (uint16_t uVirtqNbr = VIRTQ_REQ_BASE; uVirtqNbr < VIRTQ_REQ_BASE + VIRTIOSCSI_REQ_VIRTQ_CNT; uVirtqNbr++)
635 RTStrPrintf(pThis->aszVirtqNames[uVirtqNbr], VIRTIO_MAX_VIRTQ_NAME_SIZE,
636 "requestq<%d>", uVirtqNbr - VIRTQ_REQ_BASE);
637}
638
639#ifdef LOG_ENABLED
640
641
642DECLINLINE(const char *) virtioGetTxDirText(uint32_t enmTxDir)
643{
644 switch (enmTxDir)
645 {
646 case PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN: return "<UNKNOWN>";
647 case PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE: return "<DEV-TO-GUEST>";
648 case PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE: return "<GUEST-TO-DEV>";
649 case PDMMEDIAEXIOREQSCSITXDIR_NONE: return "<NONE>";
650 default: return "<BAD ENUM>";
651 }
652}
653
654DECLINLINE(const char *) virtioGetTMFTypeText(uint32_t uSubType)
655{
656 switch (uSubType)
657 {
658 case VIRTIOSCSI_T_TMF_ABORT_TASK: return "ABORT TASK";
659 case VIRTIOSCSI_T_TMF_ABORT_TASK_SET: return "ABORT TASK SET";
660 case VIRTIOSCSI_T_TMF_CLEAR_ACA: return "CLEAR ACA";
661 case VIRTIOSCSI_T_TMF_CLEAR_TASK_SET: return "CLEAR TASK SET";
662 case VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET: return "I T NEXUS RESET";
663 case VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET: return "LOGICAL UNIT RESET";
664 case VIRTIOSCSI_T_TMF_QUERY_TASK: return "QUERY TASK";
665 case VIRTIOSCSI_T_TMF_QUERY_TASK_SET: return "QUERY TASK SET";
666 default: return "<unknown>";
667 }
668}
669
670DECLINLINE(const char *) virtioGetReqRespText(uint32_t vboxRc)
671{
672 switch (vboxRc)
673 {
674 case VIRTIOSCSI_S_OK: return "OK/COMPLETE";
675 case VIRTIOSCSI_S_OVERRUN: return "OVERRRUN";
676 case VIRTIOSCSI_S_ABORTED: return "ABORTED";
677 case VIRTIOSCSI_S_BAD_TARGET: return "BAD TARGET";
678 case VIRTIOSCSI_S_RESET: return "RESET";
679 case VIRTIOSCSI_S_TRANSPORT_FAILURE: return "TRANSPORT FAILURE";
680 case VIRTIOSCSI_S_TARGET_FAILURE: return "TARGET FAILURE";
681 case VIRTIOSCSI_S_NEXUS_FAILURE: return "NEXUS FAILURE";
682 case VIRTIOSCSI_S_BUSY: return "BUSY";
683 case VIRTIOSCSI_S_FAILURE: return "FAILURE";
684 case VIRTIOSCSI_S_INCORRECT_LUN: return "INCORRECT LUN";
685 case VIRTIOSCSI_S_FUNCTION_SUCCEEDED: return "FUNCTION SUCCEEDED";
686 case VIRTIOSCSI_S_FUNCTION_REJECTED: return "FUNCTION REJECTED";
687 default: return "<unknown>";
688 }
689}
690
691DECLINLINE(void) virtioGetControlAsyncMaskText(char *pszOutput, uint32_t cbOutput, uint32_t fAsyncTypesMask)
692{
693 RTStrPrintf(pszOutput, cbOutput, "%s%s%s%s%s%s",
694 fAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE ? "CHANGE_OPERATION " : "",
695 fAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_POWER_MGMT ? "POWER_MGMT " : "",
696 fAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST ? "EXTERNAL_REQ " : "",
697 fAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE ? "MEDIA_CHANGE " : "",
698 fAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_MULTI_HOST ? "MULTI_HOST " : "",
699 fAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY ? "DEVICE_BUSY " : "");
700}
701
702static uint8_t virtioScsiEstimateCdbLen(uint8_t uCmd, uint8_t cbMax)
703{
704 if (uCmd < 0x1f)
705 return RT_MIN(6, cbMax);
706 if (uCmd >= 0x20 && uCmd < 0x60)
707 return RT_MIN(10, cbMax);
708 if (uCmd >= 0x60 && uCmd < 0x80)
709 return cbMax;
710 if (uCmd >= 0x80 && uCmd < 0xa0)
711 return RT_MIN(16, cbMax);
712 if (uCmd >= 0xa0 && uCmd < 0xc0)
713 return RT_MIN(12, cbMax);
714 return cbMax;
715}
716
717#endif /* LOG_ENABLED */
718
719
720/**
721 * Wrapper around virtioCoreR3VirtqUsedBufPut() and virtioCoreVirtqUsedRingSync() doing some device locking.
722 *
723 * @returns nothing.
724 * @param pDevIns The PDM device instance.
725 * @param pVirtio Pointer to the shared virtio core structure.
726 * @param uVirtqNbr The virtq number.
727 * @param pSgVirtReturn The S/G buffer to return data from.
728 * @param pVirtqBuf The virtq buffer.
729 *
730 * @note This is a temporary fix, see @bugref{9440#c164} on why it is necessary. Should be moved to VirtioCore or
731 * VirtioCore should be changed to not require locking.
732 */
733DECLINLINE(void) virtioScsiR3VirtqUsedBufPutAndSync(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr, PRTSGBUF pSgVirtReturn,
734 PVIRTQBUF pVirtqBuf)
735{
736 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
737
738 RTCritSectEnter(&pThisCC->aWorkers[uVirtqNbr].CritSectVirtq);
739 virtioCoreR3VirtqUsedBufPut(pThisCC->pDevIns, pVirtio, uVirtqNbr, pSgVirtReturn, pVirtqBuf, true /* fFence */);
740 virtioCoreVirtqUsedRingSync(pThisCC->pDevIns, pVirtio, uVirtqNbr);
741 RTCritSectLeave(&pThisCC->aWorkers[uVirtqNbr].CritSectVirtq);
742}
743
744
745/*
746 * @todo Figure out how to implement this with R0 changes. Not used by current linux driver
747 */
748
749#if 0
750static int virtioScsiR3SendEvent(PPDMDEVINS pDevIns, PVIRTIOSCSI pThis, uint16_t uTarget, uint32_t uEventType, uint32_t uReason)
751{
752 switch (uEventType)
753 {
754 case VIRTIOSCSI_T_NO_EVENT:
755 Log6Func(("(target=%d, LUN=%d): Warning event info guest queued is shorter than configured\n", uTarget, LUN0));
756 break;
757 case VIRTIOSCSI_T_NO_EVENT | VIRTIOSCSI_T_EVENTS_MISSED:
758 Log6Func(("(target=%d, LUN=%d): Warning driver that events were missed\n", uTarget, LUN0));
759 break;
760 case VIRTIOSCSI_T_TRANSPORT_RESET:
761 switch (uReason)
762 {
763 case VIRTIOSCSI_EVT_RESET_REMOVED:
764 Log6Func(("(target=%d, LUN=%d): Target or LUN removed\n", uTarget, LUN0));
765 break;
766 case VIRTIOSCSI_EVT_RESET_RESCAN:
767 Log6Func(("(target=%d, LUN=%d): Target or LUN added\n", uTarget, LUN0));
768 break;
769 case VIRTIOSCSI_EVT_RESET_HARD:
770 Log6Func(("(target=%d, LUN=%d): Target was reset\n", uTarget, LUN0));
771 break;
772 }
773 break;
774 case VIRTIOSCSI_T_ASYNC_NOTIFY:
775 {
776#ifdef LOG_ENABLED
777 char szTypeText[128];
778 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), uReason);
779 Log6Func(("(target=%d, LUN=%d): Delivering subscribed async notification %s\n", uTarget, LUN0, szTypeText));
780#endif
781 break;
782 }
783 case VIRTIOSCSI_T_PARAM_CHANGE:
784 LogFunc(("(target=%d, LUN=%d): PARAM_CHANGE sense code: 0x%x sense qualifier: 0x%x\n",
785 uTarget, LUN0, uReason & 0xff, (uReason >> 8) & 0xff));
786 break;
787 default:
788 Log6Func(("(target=%d, LUN=%d): Unknown event type: %d, ignoring\n", uTarget, LUN0, uEventType));
789 return VINF_SUCCESS;
790 }
791
792 PVIRTQBUF pVirtqBuf = NULL;
793 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, EVENTQ_IDX, &pVirtqBuf, true);
794 if (rc == VERR_NOT_AVAILABLE)
795 {
796 LogFunc(("eventq is empty, events missed (driver didn't preload queue)!\n"));
797 ASMAtomicWriteBool(&pThis->fEventsMissed, true);
798 return VINF_SUCCESS;
799 }
800 AssertRCReturn(rc, rc);
801
802 VIRTIOSCSI_EVENT_T Event;
803 Event.uEvent = uEventType;
804 Event.uReason = uReason;
805 Event.abVirtioLun[0] = 1;
806 Event.abVirtioLun[1] = uTarget;
807 Event.abVirtioLun[2] = (LUN0 >> 8) & 0x40;
808 Event.abVirtioLun[3] = LUN0 & 0xff;
809 Event.abVirtioLun[4] = 0;
810 Event.abVirtioLun[5] = 0;
811 Event.abVirtioLun[6] = 0;
812 Event.abVirtioLun[7] = 0;
813
814 RTSGSEG aReqSegs[1];
815 aReqSegs[0].pvSeg = &Event;
816 aReqSegs[0].cbSeg = sizeof(Event);
817
818 RTSGBUF ReqSgBuf;
819 RTSgBufInit(&ReqSgBuf, aReqSegs, RT_ELEMENTS(aReqSegs));
820
821 virtioScsiR3VirtqUsedBufPutAndSync(pDevIns, &pThis->Virtio, EVENTQ_IDX, &ReqSgBuf, pVirtqBuf);
822 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
823
824 return rc;
825}
826#endif
827
828/**
829 * Releases one reference from the given controller instances active request counter.
830 *
831 * @returns nothing.
832 * @param pDevIns The device instance.
833 * @param pThis VirtIO SCSI shared instance data.
834 * @param pThisCC VirtIO SCSI ring-3 instance data.
835 */
836DECLINLINE(void) virtioScsiR3Release(PPDMDEVINS pDevIns, PVIRTIOSCSI pThis, PVIRTIOSCSICC pThisCC)
837{
838 Assert(pThis->cActiveReqs);
839
840 if (!ASMAtomicDecU32(&pThis->cActiveReqs) && pThisCC->fQuiescing)
841 PDMDevHlpAsyncNotificationCompleted(pDevIns);
842}
843
844/**
845 * Retains one reference for the given controller instances active request counter.
846 *
847 * @returns nothing.
848 * @param pThis VirtIO SCSI shared instance data.
849 */
850DECLINLINE(void) virtioScsiR3Retain(PVIRTIOSCSI pThis)
851{
852 ASMAtomicIncU32(&pThis->cActiveReqs);
853}
854
855/** Internal worker. */
856static void virtioScsiR3FreeReq(PVIRTIOSCSITARGET pTarget, PVIRTIOSCSIREQ pReq)
857{
858 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pTarget->pDevIns, PVIRTIOSCSI);
859 RTMemFree(pReq->pbSense);
860 pReq->pbSense = NULL;
861 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pReq->pVirtqBuf);
862 pReq->pVirtqBuf = NULL;
863 pTarget->pDrvMediaEx->pfnIoReqFree(pTarget->pDrvMediaEx, pReq->hIoReq);
864}
865
866/**
867 * This is called to complete a request immediately
868 *
869 * @param pDevIns The device instance.
870 * @param pThis VirtIO SCSI shared instance data.
871 * @param uVirtqNbr Virtq index
872 * @param pVirtqBuf Pointer to pre-processed descriptor chain pulled from virtq
873 * @param pRespHdr Response header
874 * @param pbSense Pointer to sense buffer or NULL if none.
875 * @param cbSenseCfg The configured sense buffer size.
876 *
877 * @returns VINF_SUCCESS
878 */
879static int virtioScsiR3ReqErr(PPDMDEVINS pDevIns, PVIRTIOSCSI pThis, uint16_t uVirtqNbr,
880 PVIRTQBUF pVirtqBuf, REQ_RESP_HDR_T *pRespHdr, uint8_t *pbSense,
881 size_t cbSenseCfg)
882{
883 Log2Func((" status: %s response: %s\n",
884 SCSIStatusText(pRespHdr->uStatus), virtioGetReqRespText(pRespHdr->uResponse)));
885
886 RTSGSEG aReqSegs[2];
887
888 /* Segment #1: Response header*/
889 aReqSegs[0].pvSeg = pRespHdr;
890 aReqSegs[0].cbSeg = sizeof(*pRespHdr);
891
892 /* Segment #2: Sense data. */
893 uint8_t abSenseBuf[VIRTIOSCSI_SENSE_SIZE_MAX];
894 AssertCompile(VIRTIOSCSI_SENSE_SIZE_MAX <= 4096);
895 Assert(cbSenseCfg <= sizeof(abSenseBuf));
896
897 RT_ZERO(abSenseBuf);
898 if (pbSense && pRespHdr->cbSenseLen)
899 memcpy(abSenseBuf, pbSense, RT_MIN(pRespHdr->cbSenseLen, sizeof(abSenseBuf)));
900 else
901 pRespHdr->cbSenseLen = 0;
902
903 aReqSegs[1].pvSeg = abSenseBuf;
904 aReqSegs[1].cbSeg = cbSenseCfg;
905
906 /* Init S/G buffer. */
907 RTSGBUF ReqSgBuf;
908 RTSgBufInit(&ReqSgBuf, aReqSegs, RT_ELEMENTS(aReqSegs));
909
910 if (pThis->fResetting)
911 pRespHdr->uResponse = VIRTIOSCSI_S_RESET;
912
913 virtioScsiR3VirtqUsedBufPutAndSync(pDevIns, &pThis->Virtio, uVirtqNbr, &ReqSgBuf, pVirtqBuf);
914 Log2(("---------------------------------------------------------------------------------\n"));
915
916 return VINF_SUCCESS;
917}
918
919
920/**
921 * Variant of virtioScsiR3ReqErr that takes four (4) REQ_RESP_HDR_T member
922 * fields rather than a pointer to an initialized structure.
923 *
924 * @param pDevIns The device instance.
925 * @param pThis VirtIO SCSI shared instance data.
926 * @param uVirtqNbr Virtq index
927 * @param pVirtqBuf Pointer to pre-processed descriptor chain pulled from virtq
928 * @param cbResidual The number of residual bytes or something like that.
929 * @param bStatus The SCSI status code.
930 * @param bResponse The virtio SCSI response code.
931 * @param pbSense Pointer to sense buffer or NULL if none.
932 * @param cbSense The number of bytes of sense data. Zero if none.
933 * @param cbSenseCfg The configured sense buffer size.
934 *
935 * @returns VINF_SUCCESS
936 */
937static int virtioScsiR3ReqErr4(PPDMDEVINS pDevIns, PVIRTIOSCSI pThis, uint16_t uVirtqNbr,
938 PVIRTQBUF pVirtqBuf, size_t cbResidual, uint8_t bStatus, uint8_t bResponse,
939 uint8_t *pbSense, size_t cbSense, size_t cbSenseCfg)
940{
941 REQ_RESP_HDR_T RespHdr;
942 RespHdr.cbSenseLen = cbSense & UINT32_MAX;
943 RespHdr.uResidual = cbResidual & UINT32_MAX;
944 RespHdr.uStatusQualifier = 0;
945 RespHdr.uStatus = bStatus;
946 RespHdr.uResponse = bResponse;
947
948 return virtioScsiR3ReqErr(pDevIns, pThis, uVirtqNbr, pVirtqBuf, &RespHdr, pbSense, cbSenseCfg);
949}
950
951static void virtioScsiR3SenseKeyToVirtioResp(REQ_RESP_HDR_T *respHdr, uint8_t uSenseKey)
952{
953 switch (uSenseKey)
954 {
955 case SCSI_SENSE_ABORTED_COMMAND:
956 respHdr->uResponse = VIRTIOSCSI_S_ABORTED;
957 break;
958 case SCSI_SENSE_COPY_ABORTED:
959 respHdr->uResponse = VIRTIOSCSI_S_ABORTED;
960 break;
961 case SCSI_SENSE_UNIT_ATTENTION:
962 respHdr->uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
963 break;
964 case SCSI_SENSE_HARDWARE_ERROR:
965 respHdr->uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
966 break;
967 case SCSI_SENSE_NOT_READY:
968 /* Not sure what to return for this. See choices at VirtIO 1.0, 5.6.6.1.1 */
969 respHdr->uResponse = VIRTIOSCSI_S_FAILURE;
970 /* respHdr->uResponse = VIRTIOSCSI_S_BUSY; */ /* BUSY is VirtIO's 'retryable' response */
971 break;
972 default:
973 respHdr->uResponse = VIRTIOSCSI_S_FAILURE;
974 break;
975 }
976}
977
978/**
979 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
980 */
981static DECLCALLBACK(int) virtioScsiR3IoReqFinish(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
982 void *pvIoReqAlloc, int rcReq)
983{
984 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
985 PPDMDEVINS pDevIns = pTarget->pDevIns;
986 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
987 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
988 PPDMIMEDIAEX pIMediaEx = pTarget->pDrvMediaEx;
989 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
990
991 size_t cbResidual = 0;
992 int rc = pIMediaEx->pfnIoReqQueryResidual(pIMediaEx, hIoReq, &cbResidual);
993 AssertRC(rc);
994
995 size_t cbXfer = 0;
996 rc = pIMediaEx->pfnIoReqQueryXferSize(pIMediaEx, hIoReq, &cbXfer);
997 AssertRC(rc);
998
999 /* Masking deals with data type size discrepancies between
1000 * The APIs (virtio and VBox). Windows C-compiler complains otherwise */
1001 Assert(!(cbXfer & 0xffffffff00000000));
1002 uint32_t cbXfer32 = cbXfer & 0xffffffff;
1003 REQ_RESP_HDR_T respHdr = { 0 };
1004 respHdr.cbSenseLen = pReq->pbSense[2] == SCSI_SENSE_NONE ? 0 : (uint32_t)pReq->cbSenseLen;
1005 AssertMsg(!(cbResidual & 0xffffffff00000000),
1006 ("WARNING: Residual size larger than sizeof(uint32_t), truncating"));
1007 respHdr.uResidual = (uint32_t)(cbResidual & 0xffffffff);
1008 respHdr.uStatus = pReq->uStatus;
1009
1010 /* VirtIO 1.0 spec 5.6.6.1.1 says device MUST return a VirtIO response byte value.
1011 * Some are returned during the submit phase, and a few are not mapped at all,
1012 * wherein anything that can't map specifically gets mapped to VIRTIOSCSI_S_FAILURE
1013 */
1014 if (pThis->fResetting)
1015 respHdr.uResponse = VIRTIOSCSI_S_RESET;
1016 else
1017 {
1018 switch (rcReq)
1019 {
1020 case SCSI_STATUS_OK:
1021 {
1022 if (pReq->uStatus != SCSI_STATUS_CHECK_CONDITION)
1023 respHdr.uResponse = VIRTIOSCSI_S_OK;
1024 else
1025 virtioScsiR3SenseKeyToVirtioResp(&respHdr, pReq->pbSense[2]);
1026 break;
1027 }
1028 case SCSI_STATUS_CHECK_CONDITION:
1029 virtioScsiR3SenseKeyToVirtioResp(&respHdr, pReq->pbSense[2]);
1030 break;
1031
1032 default:
1033 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
1034 break;
1035 }
1036 }
1037
1038 Log2Func(("status: (%d) %s, response: (%d) %s\n", pReq->uStatus, SCSIStatusText(pReq->uStatus),
1039 respHdr.uResponse, virtioGetReqRespText(respHdr.uResponse)));
1040
1041 if (RT_FAILURE(rcReq))
1042 Log2Func(("rcReq: %Rrc\n", rcReq));
1043
1044 if (LogIs3Enabled())
1045 {
1046 LogFunc(("cbDataIn = %u, cbDataOut = %u (cbIn = %u, cbOut = %u)\n",
1047 pReq->cbDataIn, pReq->cbDataOut, pReq->pVirtqBuf->cbPhysReturn, pReq->pVirtqBuf->cbPhysSend));
1048 LogFunc(("xfer = %lu, residual = %u\n", cbXfer, cbResidual));
1049 LogFunc(("xfer direction: %s, sense written = %d, sense size = %d\n",
1050 virtioGetTxDirText(pReq->enmTxDir), respHdr.cbSenseLen, pThis->virtioScsiConfig.uSenseSize));
1051 }
1052
1053 if (respHdr.cbSenseLen && LogIs2Enabled())
1054 {
1055 LogFunc(("Sense: %s\n", SCSISenseText(pReq->pbSense[2])));
1056 LogFunc(("Sense Ext3: %s\n", SCSISenseExtText(pReq->pbSense[12], pReq->pbSense[13])));
1057 }
1058
1059 if ( (VIRTIO_IS_IN_DIRECTION(pReq->enmTxDir) && cbXfer32 > pReq->cbDataIn)
1060 || (VIRTIO_IS_OUT_DIRECTION(pReq->enmTxDir) && cbXfer32 > pReq->cbDataOut))
1061 {
1062 Log2Func((" * * * * Data overrun, returning sense\n"));
1063 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1064 0, SCSI_SENSE_ILLEGAL_REQUEST, 0, 0, 0, 0, 10, 0, 0, 0 };
1065 respHdr.cbSenseLen = sizeof(abSense);
1066 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1067 respHdr.uResponse = VIRTIOSCSI_S_OVERRUN;
1068 respHdr.uResidual = pReq->cbDataIn & UINT32_MAX;
1069
1070 virtioScsiR3ReqErr(pDevIns, pThis, pReq->uVirtqNbr, pReq->pVirtqBuf, &respHdr, abSense,
1071 RT_MIN(pThis->virtioScsiConfig.uSenseSize, VIRTIOSCSI_SENSE_SIZE_MAX));
1072 }
1073 else
1074 {
1075 Assert(pReq->pbSense != NULL);
1076
1077 /* req datain bytes already in guest phys mem. via virtioScsiIoReqCopyFromBuf() */
1078 RTSGSEG aReqSegs[2];
1079
1080 aReqSegs[0].pvSeg = &respHdr;
1081 aReqSegs[0].cbSeg = sizeof(respHdr);
1082
1083 aReqSegs[1].pvSeg = pReq->pbSense;
1084 aReqSegs[1].cbSeg = pReq->cbSenseAlloc; /* VirtIO 1.0 spec 5.6.4/5.6.6.1 */
1085
1086 RTSGBUF ReqSgBuf;
1087 RTSgBufInit(&ReqSgBuf, aReqSegs, RT_ELEMENTS(aReqSegs));
1088
1089 size_t cbReqSgBuf = RTSgBufCalcTotalLength(&ReqSgBuf);
1090 /** @todo r=bird: Returning here looks a little bogus... */
1091 AssertMsgReturn(cbReqSgBuf <= pReq->pVirtqBuf->cbPhysReturn,
1092 ("Guest expected less req data (space needed: %zu, avail: %u)\n",
1093 cbReqSgBuf, pReq->pVirtqBuf->cbPhysReturn),
1094 VERR_BUFFER_OVERFLOW);
1095
1096 virtioScsiR3VirtqUsedBufPutAndSync(pDevIns, &pThis->Virtio, pReq->uVirtqNbr, &ReqSgBuf, pReq->pVirtqBuf);
1097 Log2(("-----------------------------------------------------------------------------------------\n"));
1098 }
1099
1100 virtioScsiR3FreeReq(pTarget, pReq);
1101 virtioScsiR3Release(pDevIns, pThis, pThisCC);
1102 return rc;
1103}
1104
1105/**
1106 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
1107
1108 * Copy virtual memory from VSCSI layer to guest physical memory
1109 */
1110static DECLCALLBACK(int) virtioScsiR3IoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1111 void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf, size_t cbCopy)
1112{
1113 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
1114 PPDMDEVINS pDevIns = pTarget->pDevIns;
1115 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
1116 RT_NOREF(hIoReq, cbCopy);
1117
1118 if (!pReq->cbDataIn)
1119 return VINF_SUCCESS;
1120
1121 AssertReturn(pReq->pVirtqBuf, VERR_INVALID_PARAMETER);
1122
1123 PVIRTIOSGBUF pSgPhysReturn = pReq->pVirtqBuf->pSgPhysReturn;
1124 virtioCoreGCPhysChainAdvance(pSgPhysReturn, offDst);
1125
1126 size_t cbCopied = 0;
1127 size_t cbRemain = pReq->cbDataIn;
1128
1129 /* Skip past the REQ_RESP_HDR_T and sense code if we're at the start of the buffer. */
1130 if (!pSgPhysReturn->idxSeg && pSgPhysReturn->cbSegLeft == pSgPhysReturn->paSegs[0].cbSeg)
1131 virtioCoreGCPhysChainAdvance(pSgPhysReturn, pReq->uDataInOff);
1132
1133 while (cbRemain)
1134 {
1135 cbCopied = RT_MIN(pSgBuf->cbSegLeft, pSgPhysReturn->cbSegLeft);
1136 Assert(cbCopied > 0);
1137 PDMDevHlpPCIPhysWriteUser(pDevIns, pSgPhysReturn->GCPhysCur, pSgBuf->pvSegCur, cbCopied);
1138 RTSgBufAdvance(pSgBuf, cbCopied);
1139 virtioCoreGCPhysChainAdvance(pSgPhysReturn, cbCopied);
1140 cbRemain -= cbCopied;
1141 }
1142 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE(); /* needed? */
1143
1144 Log3Func((".... Copied %lu bytes from %lu byte guest buffer, residual=%lu\n",
1145 cbCopy, pReq->pVirtqBuf->cbPhysReturn, pReq->pVirtqBuf->cbPhysReturn - cbCopy));
1146
1147 return VINF_SUCCESS;
1148}
1149
1150/**
1151 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
1152 *
1153 * Copy guest physical memory to VSCSI layer virtual memory
1154 */
1155static DECLCALLBACK(int) virtioScsiR3IoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1156 void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf, size_t cbCopy)
1157{
1158 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
1159 PPDMDEVINS pDevIns = pTarget->pDevIns;
1160 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
1161 RT_NOREF(hIoReq, cbCopy);
1162
1163 if (!pReq->cbDataOut)
1164 return VINF_SUCCESS;
1165
1166 PVIRTIOSGBUF pSgPhysSend = pReq->pVirtqBuf->pSgPhysSend;
1167 virtioCoreGCPhysChainAdvance(pSgPhysSend, offSrc);
1168
1169 size_t cbCopied = 0;
1170 size_t cbRemain = pReq->cbDataOut;
1171 while (cbRemain)
1172 {
1173 cbCopied = RT_MIN(pSgBuf->cbSegLeft, pSgPhysSend->cbSegLeft);
1174 Assert(cbCopied > 0);
1175 PDMDevHlpPCIPhysReadUser(pDevIns, pSgPhysSend->GCPhysCur, pSgBuf->pvSegCur, cbCopied);
1176 RTSgBufAdvance(pSgBuf, cbCopied);
1177 virtioCoreGCPhysChainAdvance(pSgPhysSend, cbCopied);
1178 cbRemain -= cbCopied;
1179 }
1180
1181 Log2Func((".... Copied %lu bytes to %lu byte guest buffer, residual=%lu\n",
1182 cbCopy, pReq->pVirtqBuf->cbPhysReturn, pReq->pVirtqBuf->cbPhysReturn - cbCopy));
1183
1184 return VINF_SUCCESS;
1185}
1186
1187/**
1188 * Handles request queues for/on a worker thread.
1189 *
1190 * @returns VBox status code (logged by caller).
1191 */
1192static int virtioScsiR3ReqSubmit(PPDMDEVINS pDevIns, PVIRTIOSCSI pThis, PVIRTIOSCSICC pThisCC,
1193 uint16_t uVirtqNbr, PVIRTQBUF pVirtqBuf)
1194{
1195 /*
1196 * Validate configuration values we use here before we start.
1197 */
1198 uint32_t const cbCdb = pThis->virtioScsiConfig.uCdbSize;
1199 uint32_t const cbSenseCfg = pThis->virtioScsiConfig.uSenseSize;
1200 /** @todo Report these as errors to the guest or does the caller do that? */
1201 ASSERT_GUEST_LOGREL_MSG_RETURN(cbCdb <= VIRTIOSCSI_CDB_SIZE_MAX, ("cbCdb=%#x\n", cbCdb), VERR_OUT_OF_RANGE);
1202 ASSERT_GUEST_LOGREL_MSG_RETURN(cbSenseCfg <= VIRTIOSCSI_SENSE_SIZE_MAX, ("cbSenseCfg=%#x\n", cbSenseCfg), VERR_OUT_OF_RANGE);
1203
1204 /*
1205 * Extract command header and CDB from guest physical memory
1206 * The max size is rather small here (19 + 255 = 274), so put
1207 * it on the stack.
1208 */
1209 size_t const cbReqHdr = sizeof(REQ_CMD_HDR_T) + cbCdb;
1210 AssertReturn(pVirtqBuf && pVirtqBuf->cbPhysSend >= cbReqHdr, VERR_INVALID_PARAMETER);
1211
1212 AssertCompile(VIRTIOSCSI_CDB_SIZE_MAX < 4096);
1213 union
1214 {
1215 RT_GCC_EXTENSION struct
1216 {
1217 REQ_CMD_HDR_T ReqHdr;
1218 uint8_t abCdb[VIRTIOSCSI_CDB_SIZE_MAX];
1219 } ;
1220 uint8_t ab[sizeof(REQ_CMD_HDR_T) + VIRTIOSCSI_CDB_SIZE_MAX];
1221 uint64_t au64Align[(sizeof(REQ_CMD_HDR_T) + VIRTIOSCSI_CDB_SIZE_MAX) / sizeof(uint64_t)];
1222 } VirtqReq;
1223 RT_ZERO(VirtqReq);
1224
1225 for (size_t offReq = 0; offReq < cbReqHdr; )
1226 {
1227 size_t cbSeg = cbReqHdr - offReq;
1228 RTGCPHYS GCPhys = virtioCoreGCPhysChainGetNextSeg(pVirtqBuf->pSgPhysSend, &cbSeg);
1229 PDMDevHlpPCIPhysReadMeta(pDevIns, GCPhys, &VirtqReq.ab[offReq], cbSeg);
1230 offReq += cbSeg;
1231 }
1232
1233 uint8_t const uType = VirtqReq.ReqHdr.abVirtioLun[0];
1234 uint8_t const uTarget = VirtqReq.ReqHdr.abVirtioLun[1];
1235 uint32_t uScsiLun = RT_MAKE_U16(VirtqReq.ReqHdr.abVirtioLun[3], VirtqReq.ReqHdr.abVirtioLun[2]) & 0x3fff;
1236
1237 bool fBadLUNFormat = false;
1238 if (uType == 0xc1 && uTarget == 0x01)
1239 {
1240 LogRel(("* * * WARNING: REPORT LUNS LU ACCESSED. FEATURE NOT IMPLEMENTED SEE DevVirtioScsi.cpp * * * "));
1241 /* Force rejection. */ /** @todo figure out right way to handle. Note this is a very
1242 * vague and confusing part of the VirtIO spec (which deviates from the SCSI standard).
1243 * I have not been able to determine how to implement this properly. I've checked the
1244 * source code of Guest drivers, and so far none seem to use it. If this message is logged,
1245 * meaning a guest expects this feature, implementing it can be re-visited */
1246 uScsiLun = 0xff;
1247 }
1248 else
1249 if (uType != 1)
1250 fBadLUNFormat = true;
1251
1252 LogFunc(("[%s] (Target: %d LUN: %d) CDB: %.*Rhxs\n",
1253 SCSICmdText(VirtqReq.abCdb[0]), uTarget, uScsiLun,
1254 virtioScsiEstimateCdbLen(VirtqReq.abCdb[0], cbCdb), &VirtqReq.abCdb[0]));
1255
1256 Log3Func(("cmd id: %RX64, attr: %x, prio: %d, crn: %x\n",
1257 VirtqReq.ReqHdr.uId, VirtqReq.ReqHdr.uTaskAttr, VirtqReq.ReqHdr.uPrio, VirtqReq.ReqHdr.uCrn));
1258
1259 /*
1260 * Calculate request offsets and data sizes.
1261 */
1262 uint32_t const offDataOut = sizeof(REQ_CMD_HDR_T) + cbCdb;
1263 uint32_t const offDataIn = sizeof(REQ_RESP_HDR_T) + cbSenseCfg;
1264 size_t const cbDataOut = pVirtqBuf->cbPhysSend - offDataOut;
1265 /** @todo r=bird: Validate cbPhysReturn properly? I've just RT_MAX'ed it for now. */
1266 size_t const cbDataIn = RT_MAX(pVirtqBuf->cbPhysReturn, offDataIn) - offDataIn;
1267 Assert(offDataOut <= UINT16_MAX);
1268 Assert(offDataIn <= UINT16_MAX);
1269
1270 /*
1271 * Handle submission errors
1272 */
1273 if (RT_LIKELY(!fBadLUNFormat))
1274 { /* likely */ }
1275 else
1276 {
1277 Log2Func(("Error submitting request, bad LUN format\n"));
1278 return virtioScsiR3ReqErr4(pDevIns, pThis, uVirtqNbr, pVirtqBuf, cbDataIn + cbDataOut, 0 /*bStatus*/,
1279 VIRTIOSCSI_S_FAILURE, NULL /*pbSense*/, 0 /*cbSense*/, cbSenseCfg);
1280 }
1281
1282 PVIRTIOSCSITARGET const pTarget = &pThisCC->paTargetInstances[uTarget];
1283 if (RT_LIKELY( uTarget < pThis->cTargets
1284 && pTarget->fPresent
1285 && pTarget->pDrvMediaEx))
1286 { /* likely */ }
1287 else
1288 {
1289 Log2Func(("Error submitting request to bad target (%d) or bad LUN (%d)\n", uTarget, uScsiLun));
1290 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1291 0, SCSI_SENSE_ILLEGAL_REQUEST,
1292 0, 0, 0, 0, 10, SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED, 0, 0 };
1293 return virtioScsiR3ReqErr4(pDevIns, pThis, uVirtqNbr, pVirtqBuf, cbDataIn + cbDataOut, SCSI_STATUS_CHECK_CONDITION,
1294 VIRTIOSCSI_S_BAD_TARGET, abSense, sizeof(abSense), cbSenseCfg);
1295 }
1296 if (RT_LIKELY(uScsiLun == 0))
1297 { /* likely */ }
1298 else
1299 {
1300 Log2Func(("Error submitting request to bad target (%d) or bad LUN (%d)\n", uTarget, uScsiLun));
1301 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1302 0, SCSI_SENSE_ILLEGAL_REQUEST,
1303 0, 0, 0, 0, 10, SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED, 0, 0 };
1304 return virtioScsiR3ReqErr4(pDevIns, pThis, uVirtqNbr, pVirtqBuf, cbDataIn + cbDataOut, SCSI_STATUS_CHECK_CONDITION,
1305 VIRTIOSCSI_S_OK, abSense, sizeof(abSense), cbSenseCfg);
1306 }
1307 if (RT_LIKELY(!pThis->fResetting))
1308 { /* likely */ }
1309 else
1310 {
1311 Log2Func(("Aborting req submission because reset is in progress\n"));
1312 return virtioScsiR3ReqErr4(pDevIns, pThis, uVirtqNbr, pVirtqBuf, cbDataIn + cbDataOut, SCSI_STATUS_OK,
1313 VIRTIOSCSI_S_RESET, NULL /*pbSense*/, 0 /*cbSense*/, cbSenseCfg);
1314 }
1315
1316#if 0
1317 if (RT_LIKELY(!cbDataIn || !cbDataOut || pThis->fHasInOutBufs)) /* VirtIO 1.0, 5.6.6.1.1 */
1318 { /* likely */ }
1319 else
1320 {
1321 Log2Func(("Error submitting request, got datain & dataout bufs w/o INOUT feature negotated\n"));
1322 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1323 0, SCSI_SENSE_ILLEGAL_REQUEST, 0, 0, 0, 0, 10, 0, 0, 0 };
1324 return virtioScsiR3ReqErr4(pDevIns, pThis, uVirtqNbr, pVirtqBuf, cbDataIn + cbDataOut, SCSI_STATUS_CHECK_CONDITION,
1325 VIRTIOSCSI_S_FAILURE, abSense, sizeof(abSense), cbSenseCfg);
1326 }
1327#endif
1328 /*
1329 * Have underlying driver allocate a req of size set during initialization of this device.
1330 */
1331 virtioScsiR3Retain(pThis);
1332
1333 PDMMEDIAEXIOREQ hIoReq = NULL;
1334 PVIRTIOSCSIREQ pReq = NULL;
1335 PPDMIMEDIAEX pIMediaEx = pTarget->pDrvMediaEx;
1336
1337 int rc = pIMediaEx->pfnIoReqAlloc(pIMediaEx, &hIoReq, (void **)&pReq, 0 /* uIoReqId */,
1338 PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR);
1339
1340 if (RT_FAILURE(rc))
1341 {
1342 virtioScsiR3Release(pDevIns, pThis, pThisCC);
1343 return rc;
1344 }
1345
1346 pReq->hIoReq = hIoReq;
1347 pReq->pTarget = pTarget;
1348 pReq->uVirtqNbr = uVirtqNbr;
1349 pReq->cbDataIn = cbDataIn;
1350 pReq->cbDataOut = cbDataOut;
1351 pReq->pVirtqBuf = pVirtqBuf;
1352 virtioCoreR3VirtqBufRetain(pVirtqBuf); /* (For pReq->pVirtqBuf. Released by virtioScsiR3FreeReq.) */
1353 pReq->uDataInOff = offDataIn;
1354 pReq->uDataOutOff = offDataOut;
1355
1356 pReq->cbSenseAlloc = cbSenseCfg;
1357 pReq->pbSense = (uint8_t *)RTMemAllocZ(pReq->cbSenseAlloc);
1358 AssertMsgReturnStmt(pReq->pbSense, ("Out of memory allocating sense buffer"),
1359 virtioScsiR3FreeReq(pTarget, pReq);, VERR_NO_MEMORY);
1360
1361 /* Note: DrvSCSI allocates one virtual memory buffer for input and output phases of the request */
1362 rc = pIMediaEx->pfnIoReqSendScsiCmd(pIMediaEx, pReq->hIoReq, uScsiLun,
1363 &VirtqReq.abCdb[0], cbCdb,
1364 PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN, &pReq->enmTxDir,
1365 RT_MAX(cbDataIn, cbDataOut),
1366 pReq->pbSense, pReq->cbSenseAlloc, &pReq->cbSenseLen,
1367 &pReq->uStatus, RT_MS_30SEC);
1368
1369 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
1370 {
1371 /*
1372 * Getting here means the request failed in early in the submission to the lower level driver,
1373 * and there will be no callback to the finished/completion function for this request
1374 */
1375 Assert(RT_FAILURE_NP(rc));
1376 Log2Func(("Request-submission error from lower-level driver\n"));
1377 uint8_t uASC, uASCQ = 0;
1378 switch (rc)
1379 {
1380 case VERR_NO_MEMORY:
1381 uASC = SCSI_ASC_SYSTEM_RESOURCE_FAILURE;
1382 break;
1383 default:
1384 uASC = SCSI_ASC_INTERNAL_TARGET_FAILURE;
1385 break;
1386 }
1387 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1388 0, SCSI_SENSE_VENDOR_SPECIFIC,
1389 0, 0, 0, 0, 10, uASC, uASCQ, 0 };
1390 REQ_RESP_HDR_T respHdr = { 0 };
1391 respHdr.cbSenseLen = sizeof(abSense);
1392 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1393 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
1394 respHdr.uResidual = (cbDataIn + cbDataOut) & UINT32_MAX;
1395 virtioScsiR3ReqErr(pDevIns, pThis, uVirtqNbr, pVirtqBuf, &respHdr, abSense, cbSenseCfg);
1396 virtioScsiR3FreeReq(pTarget, pReq);
1397 virtioScsiR3Release(pDevIns, pThis, pThisCC);
1398 }
1399 return VINF_SUCCESS;
1400}
1401
1402/**
1403 * Handles control transfers for/on a worker thread.
1404 *
1405 * @returns VBox status code (ignored by the caller).
1406 * @param pDevIns The device instance.
1407 * @param pThis VirtIO SCSI shared instance data.
1408 * @param pThisCC VirtIO SCSI ring-3 instance data.
1409 * @param uVirtqNbr CONTROLQ_IDX
1410 * @param pVirtqBuf Descriptor chain to process.
1411 */
1412static int virtioScsiR3Ctrl(PPDMDEVINS pDevIns, PVIRTIOSCSI pThis, PVIRTIOSCSICC pThisCC,
1413 uint16_t uVirtqNbr, PVIRTQBUF pVirtqBuf)
1414{
1415 AssertReturn(pVirtqBuf->cbPhysSend >= RT_MIN(sizeof(VIRTIOSCSI_CTRL_AN_T),
1416 sizeof(VIRTIOSCSI_CTRL_TMF_T)), 0);
1417
1418 /*
1419 * Allocate buffer and read in the control command
1420 */
1421 VIRTIO_SCSI_CTRL_UNION_T ScsiCtrlUnion;
1422 RT_ZERO(ScsiCtrlUnion);
1423
1424 size_t const cb = RT_MIN(pVirtqBuf->cbPhysSend, sizeof(VIRTIO_SCSI_CTRL_UNION_T));
1425 for (size_t uOffset = 0; uOffset < cb; )
1426 {
1427 size_t cbSeg = cb - uOffset;
1428 RTGCPHYS GCPhys = virtioCoreGCPhysChainGetNextSeg(pVirtqBuf->pSgPhysSend, &cbSeg);
1429 PDMDevHlpPCIPhysReadMeta(pDevIns, GCPhys, &ScsiCtrlUnion.ab[uOffset], cbSeg);
1430 uOffset += cbSeg;
1431 }
1432
1433 AssertReturn( (ScsiCtrlUnion.Type.uType == VIRTIOSCSI_T_TMF
1434 && pVirtqBuf->cbPhysSend >= sizeof(VIRTIOSCSI_CTRL_TMF_T))
1435 || ( ( ScsiCtrlUnion.Type.uType == VIRTIOSCSI_T_AN_QUERY
1436 || ScsiCtrlUnion.Type.uType == VIRTIOSCSI_T_AN_SUBSCRIBE)
1437 && pVirtqBuf->cbPhysSend >= sizeof(VIRTIOSCSI_CTRL_AN_T)),
1438 0 /** @todo r=bird: what kind of status is '0' here? */);
1439
1440 union
1441 {
1442 uint32_t fSupportedEvents;
1443 } uData;
1444 uint8_t bResponse = VIRTIOSCSI_S_OK;
1445 uint8_t cSegs;
1446 RTSGSEG aReqSegs[2];
1447 switch (ScsiCtrlUnion.Type.uType)
1448 {
1449 case VIRTIOSCSI_T_TMF: /* Task Management Functions */
1450 {
1451 uint8_t uTarget = ScsiCtrlUnion.Tmf.abScsiLun[1];
1452 uint32_t uScsiLun = RT_MAKE_U16(ScsiCtrlUnion.Tmf.abScsiLun[3], ScsiCtrlUnion.Tmf.abScsiLun[2]) & 0x3fff;
1453 Log2Func(("[%s] (Target: %d LUN: %d) Task Mgt Function: %s\n",
1454 VIRTQNAME(uVirtqNbr), uTarget, uScsiLun, virtioGetTMFTypeText(ScsiCtrlUnion.Tmf.uSubtype)));
1455
1456 if (uTarget >= pThis->cTargets || !pThisCC->paTargetInstances[uTarget].fPresent)
1457 bResponse = VIRTIOSCSI_S_BAD_TARGET;
1458 else
1459 if (uScsiLun != 0)
1460 bResponse = VIRTIOSCSI_S_INCORRECT_LUN;
1461 else
1462 switch (ScsiCtrlUnion.Tmf.uSubtype)
1463 {
1464 case VIRTIOSCSI_T_TMF_ABORT_TASK:
1465 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1466 break;
1467 case VIRTIOSCSI_T_TMF_ABORT_TASK_SET:
1468 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1469 break;
1470 case VIRTIOSCSI_T_TMF_CLEAR_ACA:
1471 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1472 break;
1473 case VIRTIOSCSI_T_TMF_CLEAR_TASK_SET:
1474 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1475 break;
1476 case VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET:
1477 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1478 break;
1479 case VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET:
1480 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1481 break;
1482 case VIRTIOSCSI_T_TMF_QUERY_TASK:
1483 bResponse = VIRTIOSCSI_S_FUNCTION_REJECTED;
1484 break;
1485 case VIRTIOSCSI_T_TMF_QUERY_TASK_SET:
1486 bResponse = VIRTIOSCSI_S_FUNCTION_REJECTED;
1487 break;
1488 default:
1489 LogFunc(("Unknown TMF type\n"));
1490 bResponse = VIRTIOSCSI_S_FAILURE;
1491 }
1492 cSegs = 0; /* only bResponse */
1493 break;
1494 }
1495 case VIRTIOSCSI_T_AN_QUERY: /* Guest SCSI driver is querying supported async event notifications */
1496 {
1497 uint8_t uTarget = ScsiCtrlUnion.AsyncNotify.abScsiLun[1];
1498 uint32_t uScsiLun = RT_MAKE_U16(ScsiCtrlUnion.AsyncNotify.abScsiLun[3],
1499 ScsiCtrlUnion.AsyncNotify.abScsiLun[2]) & 0x3fff;
1500
1501 if (uTarget >= pThis->cTargets || !pThisCC->paTargetInstances[uTarget].fPresent)
1502 bResponse = VIRTIOSCSI_S_BAD_TARGET;
1503 else
1504 if (uScsiLun != 0)
1505 bResponse = VIRTIOSCSI_S_INCORRECT_LUN;
1506 else
1507 bResponse = VIRTIOSCSI_S_FUNCTION_COMPLETE;
1508
1509#ifdef LOG_ENABLED
1510 if (LogIs2Enabled())
1511 {
1512 char szTypeText[128];
1513 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), ScsiCtrlUnion.AsyncNotify.fEventsRequested);
1514 Log2Func(("[%s] (Target: %d LUN: %d) Async. Notification Query: %s\n",
1515 VIRTQNAME(uVirtqNbr), uTarget, uScsiLun, szTypeText));
1516 }
1517#endif
1518 uData.fSupportedEvents = SUPPORTED_EVENTS;
1519 aReqSegs[0].pvSeg = &uData.fSupportedEvents;
1520 aReqSegs[0].cbSeg = sizeof(uData.fSupportedEvents);
1521 cSegs = 1;
1522 break;
1523 }
1524 case VIRTIOSCSI_T_AN_SUBSCRIBE: /* Guest SCSI driver is subscribing to async event notification(s) */
1525 {
1526 if (ScsiCtrlUnion.AsyncNotify.fEventsRequested & ~SUBSCRIBABLE_EVENTS)
1527 LogFunc(("Unsupported bits in event subscription event mask: %#x\n",
1528 ScsiCtrlUnion.AsyncNotify.fEventsRequested));
1529
1530 uint8_t uTarget = ScsiCtrlUnion.AsyncNotify.abScsiLun[1];
1531 uint32_t uScsiLun = RT_MAKE_U16(ScsiCtrlUnion.AsyncNotify.abScsiLun[3],
1532 ScsiCtrlUnion.AsyncNotify.abScsiLun[2]) & 0x3fff;
1533
1534#ifdef LOG_ENABLED
1535 if (LogIs2Enabled())
1536 {
1537 char szTypeText[128];
1538 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), ScsiCtrlUnion.AsyncNotify.fEventsRequested);
1539 Log2Func(("[%s] (Target: %d LUN: %d) Async. Notification Subscribe: %s\n",
1540 VIRTQNAME(uVirtqNbr), uTarget, uScsiLun, szTypeText));
1541 }
1542#endif
1543 if (uTarget >= pThis->cTargets || !pThisCC->paTargetInstances[uTarget].fPresent)
1544 bResponse = VIRTIOSCSI_S_BAD_TARGET;
1545 else
1546 if (uScsiLun != 0)
1547 bResponse = VIRTIOSCSI_S_INCORRECT_LUN;
1548 else
1549 {
1550 bResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED; /* or VIRTIOSCSI_S_FUNCTION_COMPLETE? */
1551 pThis->fAsyncEvtsEnabled = SUPPORTED_EVENTS & ScsiCtrlUnion.AsyncNotify.fEventsRequested;
1552 }
1553
1554 aReqSegs[0].pvSeg = &pThis->fAsyncEvtsEnabled;
1555 aReqSegs[0].cbSeg = sizeof(pThis->fAsyncEvtsEnabled);
1556 cSegs = 1;
1557 break;
1558 }
1559 default:
1560 {
1561 LogFunc(("Unknown control type extracted from %s: %u\n", VIRTQNAME(uVirtqNbr), ScsiCtrlUnion.Type.uType));
1562
1563 bResponse = VIRTIOSCSI_S_FAILURE;
1564 cSegs = 0; /* only bResponse */
1565 break;
1566 }
1567 }
1568
1569 /* Add the response code: */
1570 aReqSegs[cSegs].pvSeg = &bResponse;
1571 aReqSegs[cSegs].cbSeg = sizeof(bResponse);
1572 cSegs++;
1573 Assert(cSegs <= RT_ELEMENTS(aReqSegs));
1574
1575 LogFunc(("Response code: %s\n", virtioGetReqRespText(bResponse)));
1576
1577 RTSGBUF ReqSgBuf;
1578 RTSgBufInit(&ReqSgBuf, aReqSegs, cSegs);
1579
1580 virtioScsiR3VirtqUsedBufPutAndSync(pDevIns, &pThis->Virtio, uVirtqNbr, &ReqSgBuf, pVirtqBuf);
1581 return VINF_SUCCESS;
1582}
1583
1584/**
1585 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
1586 */
1587static DECLCALLBACK(int) virtioScsiR3WorkerWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1588{
1589 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
1590 return PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[(uintptr_t)pThread->pvUser].hEvtProcess);
1591}
1592
1593/**
1594 * @callback_method_impl{FNPDMTHREADDEV}
1595 */
1596static DECLCALLBACK(int) virtioScsiR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1597{
1598 uint16_t const uVirtqNbr = (uint16_t)(uintptr_t)pThread->pvUser;
1599 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
1600 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
1601 PVIRTIOSCSIWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
1602 PVIRTIOSCSIWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
1603
1604 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
1605 return VINF_SUCCESS;
1606
1607 Log6Func(("[Re]starting %s worker\n", VIRTQNAME(uVirtqNbr)));
1608 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
1609 {
1610 if ( !pWorkerR3->cRedoDescs
1611 && IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, uVirtqNbr))
1612 {
1613 /* Atomic interlocks avoid missing alarm while going to sleep & notifier waking the awoken */
1614 ASMAtomicWriteBool(&pWorker->fSleeping, true);
1615 bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
1616 if (!fNotificationSent)
1617 {
1618 Log6Func(("%s worker sleeping...\n", VIRTQNAME(uVirtqNbr)));
1619 Assert(ASMAtomicReadBool(&pWorker->fSleeping));
1620 int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
1621 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
1622 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
1623 {
1624 Log6Func(("%s worker thread not running, exiting\n", VIRTQNAME(uVirtqNbr)));
1625 return VINF_SUCCESS;
1626 }
1627 if (rc == VERR_INTERRUPTED)
1628 {
1629 Log6Func(("%s worker interrupted ... continuing\n", VIRTQNAME(uVirtqNbr)));
1630 continue;
1631 }
1632 Log6Func(("%s worker woken\n", VIRTQNAME(uVirtqNbr)));
1633 ASMAtomicWriteBool(&pWorker->fNotified, false);
1634 }
1635 ASMAtomicWriteBool(&pWorker->fSleeping, false);
1636 }
1637 if (!virtioCoreIsVirtqEnabled(&pThis->Virtio, uVirtqNbr))
1638 {
1639 LogFunc(("%s queue not enabled, worker aborting...\n", VIRTQNAME(uVirtqNbr)));
1640 break;
1641 }
1642
1643 if (!pThis->afVirtqAttached[uVirtqNbr])
1644 {
1645 LogFunc(("%s queue not attached, worker aborting...\n", VIRTQNAME(uVirtqNbr)));
1646 break;
1647 }
1648 if (!pThisCC->fQuiescing)
1649 {
1650 /* Process any reqs that were suspended saved to the redo queue in save exec. */
1651 for (int i = 0; i < pWorkerR3->cRedoDescs; i++)
1652 {
1653#ifdef VIRTIO_VBUF_ON_STACK
1654 PVIRTQBUF pVirtqBuf = virtioCoreR3VirtqBufAlloc();
1655 if (!pVirtqBuf)
1656 {
1657 LogRel(("Failed to allocate memory for VIRTQBUF\n"));
1658 break; /* No point in trying to allocate memory for other descriptor chains */
1659 }
1660 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, uVirtqNbr,
1661 pWorkerR3->auRedoDescs[i], pVirtqBuf);
1662#else /* !VIRTIO_VBUF_ON_STACK */
1663 PVIRTQBUF pVirtqBuf;
1664 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, uVirtqNbr,
1665 pWorkerR3->auRedoDescs[i], &pVirtqBuf);
1666#endif /* !VIRTIO_VBUF_ON_STACK */
1667 if (RT_FAILURE(rc))
1668 LogRel(("Error fetching desc chain to redo, %Rrc", rc));
1669
1670 rc = virtioScsiR3ReqSubmit(pDevIns, pThis, pThisCC, uVirtqNbr, pVirtqBuf);
1671 if (RT_FAILURE(rc))
1672 LogRel(("Error submitting req packet, resetting %Rrc", rc));
1673
1674 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
1675 }
1676 pWorkerR3->cRedoDescs = 0;
1677
1678 Log6Func(("fetching next descriptor chain from %s\n", VIRTQNAME(uVirtqNbr)));
1679#ifdef VIRTIO_VBUF_ON_STACK
1680 PVIRTQBUF pVirtqBuf = virtioCoreR3VirtqBufAlloc();
1681 if (!pVirtqBuf)
1682 LogRel(("Failed to allocate memory for VIRTQBUF\n"));
1683 else
1684 {
1685 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, uVirtqNbr, pVirtqBuf, true);
1686#else /* !VIRTIO_VBUF_ON_STACK */
1687 PVIRTQBUF pVirtqBuf = NULL;
1688 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, uVirtqNbr, &pVirtqBuf, true);
1689#endif /* !VIRTIO_VBUF_ON_STACK */
1690 if (rc == VERR_NOT_AVAILABLE)
1691 {
1692 Log6Func(("Nothing found in %s\n", VIRTQNAME(uVirtqNbr)));
1693 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
1694 continue;
1695 }
1696
1697 AssertRC(rc);
1698 if (uVirtqNbr == CONTROLQ_IDX)
1699 virtioScsiR3Ctrl(pDevIns, pThis, pThisCC, uVirtqNbr, pVirtqBuf);
1700 else /* request queue index */
1701 {
1702 rc = virtioScsiR3ReqSubmit(pDevIns, pThis, pThisCC, uVirtqNbr, pVirtqBuf);
1703 if (RT_FAILURE(rc))
1704 LogRel(("Error submitting req packet, resetting %Rrc", rc));
1705 }
1706
1707 virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
1708#ifdef VIRTIO_VBUF_ON_STACK
1709 }
1710#endif /* VIRTIO_VBUF_ON_STACK */
1711 }
1712 }
1713 return VINF_SUCCESS;
1714}
1715
1716
1717/*********************************************************************************************************************************
1718* Sending evnets
1719*********************************************************************************************************************************/
1720
1721/*
1722 * @todo Figure out how to implement this with R0 changes. Not used by current linux driver
1723 */
1724
1725#if 0
1726DECLINLINE(void) virtioScsiR3ReportEventsMissed(PPDMDEVINS pDevIns, PVIRTIOSCSI pThis, uint16_t uTarget)
1727{
1728 virtioScsiR3SendEvent(pDevIns, pThis, uTarget, VIRTIOSCSI_T_NO_EVENT | VIRTIOSCSI_T_EVENTS_MISSED, 0);
1729}
1730#endif
1731
1732#if 0
1733/* SUBSCRIBABLE EVENT - not sure when to call this or how to detect when media is added or removed
1734 * via the VBox GUI */
1735DECLINLINE(void) virtioScsiR3ReportMediaChange(PPDMDEVINS pDevIns, PVIRTIOSCSI pThis, uint16_t uTarget)
1736{
1737 if (pThis->fAsyncEvtsEnabled & VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE)
1738 virtioScsiR3SendEvent(pDevIns, pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY, VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE);
1739}
1740
1741/* ESSENTIAL (NON-SUBSCRIBABLE) EVENT TYPES (most guest virtio-scsi drivers ignore?) */
1742
1743DECLINLINE(void) virtioScsiR3ReportTransportReset(PDMDEVINS pDevIns, PVIRTIOSCSI pThis, uint16_t uTarget)
1744{
1745 virtioScsiR3SendEvent(pDevIns, pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET, VIRTIOSCSI_EVT_RESET_HARD);
1746}
1747
1748DECLINLINE(void) virtioScsiR3ReportParamChange(PDMDEVINS pDevIns, PVIRTIOSCSI pThis, uint16_t uTarget,
1749 uint32_t uSenseCode, uint32_t uSenseQualifier)
1750{
1751 uint32_t uReason = uSenseQualifier << 8 | uSenseCode;
1752 virtioScsiR3SendEvent(pDevIns, pThis, uTarget, VIRTIOSCSI_T_PARAM_CHANGE, uReason);
1753
1754}
1755
1756DECLINLINE(void) virtioScsiR3ReportTargetRemoved(PDMDEVINS pDevIns, PVIRTIOSCSI pThis, uint16_t uTarget)
1757{
1758 if (pThis->fHasHotplug)
1759 virtioScsiR3SendEvent(pDevIns, pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET, VIRTIOSCSI_EVT_RESET_REMOVED);
1760}
1761
1762DECLINLINE(void) virtioScsiR3ReportTargetAdded(PDMDEVINS pDevInsPVIRTIOSCSI pThis, uint16_t uTarget)
1763{
1764 if (pThis->fHasHotplug)
1765 virtioScsiR3SendEvent(pDevIns, pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET, VIRTIOSCSI_EVT_RESET_RESCAN);
1766}
1767
1768#endif
1769
1770/**
1771 * @callback_method_impl{VIRTIOCORER3,pfnStatusChanged}
1772 */
1773static DECLCALLBACK(void) virtioScsiR3StatusChanged(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint32_t fVirtioReady)
1774{
1775 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pVirtio, VIRTIOSCSI, Virtio);
1776 PVIRTIOSCSICC pThisCC = RT_FROM_MEMBER(pVirtioCC, VIRTIOSCSICC, Virtio);
1777
1778 pThis->fVirtioReady = fVirtioReady;
1779
1780 if (fVirtioReady)
1781 {
1782 LogFunc(("VirtIO ready\n-----------------------------------------------------------------------------------------\n"));
1783 uint64_t fFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
1784 pThis->fHasT10pi = fFeatures & VIRTIO_SCSI_F_T10_PI;
1785 pThis->fHasHotplug = fFeatures & VIRTIO_SCSI_F_HOTPLUG;
1786 pThis->fHasInOutBufs = fFeatures & VIRTIO_SCSI_F_INOUT;
1787 pThis->fHasLunChange = fFeatures & VIRTIO_SCSI_F_CHANGE;
1788 pThis->fResetting = false;
1789 pThisCC->fQuiescing = false;
1790
1791 for (unsigned i = 0; i < VIRTIOSCSI_VIRTQ_CNT; i++)
1792 pThis->afVirtqAttached[i] = true;
1793 }
1794 else
1795 {
1796 LogFunc(("VirtIO is resetting\n"));
1797 for (unsigned i = 0; i < VIRTIOSCSI_VIRTQ_CNT; i++)
1798 pThis->afVirtqAttached[i] = false;
1799
1800 /*
1801 * BIOS may change these values. When the OS comes up, and KVM driver accessed
1802 * through Windows, it assumes they are the default size. So as per the VirtIO 1.0 spec,
1803 * 5.6.4, these device configuration values must be set to default upon device reset.
1804 */
1805 pThis->virtioScsiConfig.uSenseSize = VIRTIOSCSI_SENSE_SIZE_DEFAULT;
1806 pThis->virtioScsiConfig.uCdbSize = VIRTIOSCSI_CDB_SIZE_DEFAULT;
1807 }
1808
1809
1810}
1811
1812
1813/*********************************************************************************************************************************
1814* LEDs *
1815*********************************************************************************************************************************/
1816
1817/**
1818 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed, Target level.}
1819 */
1820static DECLCALLBACK(int) virtioScsiR3TargetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
1821{
1822 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, ILed);
1823 if (iLUN == 0)
1824 {
1825 *ppLed = &pTarget->led;
1826 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
1827 return VINF_SUCCESS;
1828 }
1829 return VERR_PDM_LUN_NOT_FOUND;
1830}
1831/**
1832 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed, Device level.}
1833 */
1834static DECLCALLBACK(int) virtioScsiR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
1835{
1836 PVIRTIOSCSICC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIOSCSICC, ILeds);
1837 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIOSCSI);
1838 if (iLUN < pThis->cTargets)
1839 {
1840 *ppLed = &pThisCC->paTargetInstances[iLUN].led;
1841 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
1842 return VINF_SUCCESS;
1843 }
1844 return VERR_PDM_LUN_NOT_FOUND;
1845}
1846
1847
1848/*********************************************************************************************************************************
1849* PDMIMEDIAPORT (target) *
1850*********************************************************************************************************************************/
1851
1852/**
1853 * @interface_method_impl{PDMIMEDIAPORT,pfnQueryDeviceLocation, Target level.}
1854 */
1855static DECLCALLBACK(int) virtioScsiR3QueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
1856 uint32_t *piInstance, uint32_t *piLUN)
1857{
1858 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaPort);
1859 PPDMDEVINS pDevIns = pTarget->pDevIns;
1860
1861 AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
1862 AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
1863 AssertPtrReturn(piLUN, VERR_INVALID_POINTER);
1864
1865 *ppcszController = pDevIns->pReg->szName;
1866 *piInstance = pDevIns->iInstance;
1867 *piLUN = pTarget->uTarget;
1868
1869 return VINF_SUCCESS;
1870}
1871
1872
1873/*********************************************************************************************************************************
1874* Virtio config. *
1875*********************************************************************************************************************************/
1876
1877/**
1878 * Worker for virtioScsiR3DevCapWrite and virtioScsiR3DevCapRead.
1879 */
1880static int virtioScsiR3CfgAccessed(PVIRTIOSCSI pThis, uint32_t uOffsetOfAccess, void *pv, uint32_t cb, bool fWrite)
1881{
1882 AssertReturn(pv && cb <= sizeof(uint32_t), fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00);
1883
1884 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uNumVirtqs, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess))
1885 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uNumVirtqs, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess, &pThis->virtioScsiConfig);
1886 else
1887 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uSegMax, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess))
1888 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uSegMax, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess, &pThis->virtioScsiConfig);
1889 else
1890 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uMaxSectors, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess))
1891 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMaxSectors, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess, &pThis->virtioScsiConfig);
1892 else
1893 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uCmdPerLun, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess))
1894 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uCmdPerLun, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess, &pThis->virtioScsiConfig);
1895 else
1896 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uEventInfoSize, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess))
1897 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uEventInfoSize, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess, &pThis->virtioScsiConfig);
1898 else
1899 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uSenseSize, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess))
1900 VIRTIO_DEV_CONFIG_ACCESS( uSenseSize, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess, &pThis->virtioScsiConfig);
1901 else
1902 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uCdbSize, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess))
1903 VIRTIO_DEV_CONFIG_ACCESS( uCdbSize, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess, &pThis->virtioScsiConfig);
1904 else
1905 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uMaxChannel, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess))
1906 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMaxChannel, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess, &pThis->virtioScsiConfig);
1907 else
1908 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uMaxTarget, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess))
1909 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMaxTarget, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess, &pThis->virtioScsiConfig);
1910 else
1911 if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uMaxLun, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess))
1912 VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMaxLun, VIRTIOSCSI_CONFIG_T, uOffsetOfAccess, &pThis->virtioScsiConfig);
1913 else
1914 {
1915 LogFunc(("Bad access by guest to virtio_scsi_config: off=%u (%#x), cb=%u\n", uOffsetOfAccess, uOffsetOfAccess, cb));
1916 return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
1917 }
1918 return VINF_SUCCESS;
1919}
1920
1921/**
1922 * @callback_method_impl{VIRTIOCORER3,pfnDevCapRead}
1923 */
1924static DECLCALLBACK(int) virtioScsiR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void *pv, uint32_t cb)
1925{
1926 return virtioScsiR3CfgAccessed(PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI), uOffset, pv, cb, false /*fRead*/);
1927}
1928
1929/**
1930 * @callback_method_impl{VIRTIOCORER3,pfnDevCapWrite}
1931 */
1932static DECLCALLBACK(int) virtioScsiR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
1933{
1934 return virtioScsiR3CfgAccessed(PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI), uOffset, (void *)pv, cb, true /*fWrite*/);
1935}
1936
1937
1938/*********************************************************************************************************************************
1939* IBase for device and targets *
1940*********************************************************************************************************************************/
1941
1942/**
1943 * @interface_method_impl{PDMIBASE,pfnQueryInterface, Target level.}
1944 */
1945static DECLCALLBACK(void *) virtioScsiR3TargetQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1946{
1947 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IBase);
1948 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pTarget->IBase);
1949 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pTarget->IMediaPort);
1950 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pTarget->IMediaExPort);
1951 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pTarget->ILed);
1952 return NULL;
1953}
1954
1955/**
1956 * @interface_method_impl{PDMIBASE,pfnQueryInterface, Device level.}
1957 */
1958static DECLCALLBACK(void *) virtioScsiR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1959{
1960 PVIRTIOSCSICC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIOSCSICC, IBase);
1961
1962 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
1963 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
1964
1965 return NULL;
1966}
1967
1968
1969/*********************************************************************************************************************************
1970* Misc *
1971*********************************************************************************************************************************/
1972
1973/**
1974 * @callback_method_impl{FNDBGFHANDLERDEV, virtio-scsi debugger info callback.}
1975 */
1976static DECLCALLBACK(void) virtioScsiR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1977{
1978 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
1979
1980 /* Parse arguments. */
1981 RT_NOREF(pszArgs); //bool fVerbose = pszArgs && strstr(pszArgs, "verbose") != NULL;
1982
1983 /* Show basic information. */
1984 pHlp->pfnPrintf(pHlp, "%s#%d: virtio-scsci ",
1985 pDevIns->pReg->szName,
1986 pDevIns->iInstance);
1987 pHlp->pfnPrintf(pHlp, "numTargets=%lu", pThis->cTargets);
1988}
1989
1990
1991/*********************************************************************************************************************************
1992* Saved state *
1993*********************************************************************************************************************************/
1994
1995/**
1996 * @callback_method_impl{FNSSMDEVLOADEXEC}
1997 */
1998static DECLCALLBACK(int) virtioScsiR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1999{
2000 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2001 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
2002 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2003
2004 LogFunc(("LOAD EXEC!!\n"));
2005
2006 AssertReturn(uPass == SSM_PASS_FINAL, VERR_SSM_UNEXPECTED_PASS);
2007 AssertLogRelMsgReturn(uVersion == VIRTIOSCSI_SAVED_STATE_VERSION,
2008 ("uVersion=%u\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
2009
2010 virtioScsiSetVirtqNames(pThis);
2011 for (int uVirtqNbr = 0; uVirtqNbr < VIRTIOSCSI_VIRTQ_CNT; uVirtqNbr++)
2012 pHlp->pfnSSMGetBool(pSSM, &pThis->afVirtqAttached[uVirtqNbr]);
2013
2014 pHlp->pfnSSMGetU32(pSSM, &pThis->virtioScsiConfig.uNumVirtqs);
2015 pHlp->pfnSSMGetU32(pSSM, &pThis->virtioScsiConfig.uSegMax);
2016 pHlp->pfnSSMGetU32(pSSM, &pThis->virtioScsiConfig.uMaxSectors);
2017 pHlp->pfnSSMGetU32(pSSM, &pThis->virtioScsiConfig.uCmdPerLun);
2018 pHlp->pfnSSMGetU32(pSSM, &pThis->virtioScsiConfig.uEventInfoSize);
2019 pHlp->pfnSSMGetU32(pSSM, &pThis->virtioScsiConfig.uSenseSize);
2020 pHlp->pfnSSMGetU32(pSSM, &pThis->virtioScsiConfig.uCdbSize);
2021 pHlp->pfnSSMGetU16(pSSM, &pThis->virtioScsiConfig.uMaxChannel);
2022 pHlp->pfnSSMGetU16(pSSM, &pThis->virtioScsiConfig.uMaxTarget);
2023 pHlp->pfnSSMGetU32(pSSM, &pThis->virtioScsiConfig.uMaxLun);
2024 pHlp->pfnSSMGetU32(pSSM, &pThis->fAsyncEvtsEnabled);
2025 pHlp->pfnSSMGetBool(pSSM, &pThis->fEventsMissed);
2026 pHlp->pfnSSMGetU32(pSSM, &pThis->fVirtioReady);
2027 pHlp->pfnSSMGetU32(pSSM, &pThis->fHasT10pi);
2028 pHlp->pfnSSMGetU32(pSSM, &pThis->fHasHotplug);
2029 pHlp->pfnSSMGetU32(pSSM, &pThis->fHasInOutBufs);
2030 pHlp->pfnSSMGetU32(pSSM, &pThis->fHasLunChange);
2031 pHlp->pfnSSMGetU32(pSSM, &pThis->fResetting);
2032
2033 uint32_t cTargets;
2034 int rc = pHlp->pfnSSMGetU32(pSSM, &cTargets);
2035 AssertRCReturn(rc, rc);
2036 AssertReturn(cTargets == pThis->cTargets,
2037 pHlp->pfnSSMSetLoadError(pSSM, VERR_SSM_LOAD_CONFIG_MISMATCH, RT_SRC_POS,
2038 N_("target count has changed: %u saved, %u configured now"),
2039 cTargets, pThis->cTargets));
2040
2041 for (uint16_t uTarget = 0; uTarget < pThis->cTargets; uTarget++)
2042 {
2043 uint16_t cReqsRedo;
2044 rc = pHlp->pfnSSMGetU16(pSSM, &cReqsRedo);
2045 AssertRCReturn(rc, rc);
2046 AssertReturn(cReqsRedo < VIRTQ_SIZE,
2047 pHlp->pfnSSMSetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
2048 N_("Bad count of I/O transactions to re-do in saved state (%#x, max %#x - 1)"),
2049 cReqsRedo, VIRTQ_SIZE));
2050
2051 for (uint16_t uVirtqNbr = VIRTQ_REQ_BASE; uVirtqNbr < VIRTIOSCSI_VIRTQ_CNT; uVirtqNbr++)
2052 {
2053 PVIRTIOSCSIWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
2054 pWorkerR3->cRedoDescs = 0;
2055 }
2056
2057 for (int i = 0; i < cReqsRedo; i++)
2058 {
2059 uint16_t uVirtqNbr;
2060 rc = pHlp->pfnSSMGetU16(pSSM, &uVirtqNbr);
2061 AssertRCReturn(rc, rc);
2062 AssertReturn(uVirtqNbr < VIRTIOSCSI_VIRTQ_CNT,
2063 pHlp->pfnSSMSetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
2064 N_("Bad queue index for re-do in saved state (%#x, max %#x)"),
2065 uVirtqNbr, VIRTIOSCSI_VIRTQ_CNT - 1));
2066
2067 uint16_t idxHead;
2068 rc = pHlp->pfnSSMGetU16(pSSM, &idxHead);
2069 AssertRCReturn(rc, rc);
2070 AssertReturn(idxHead < VIRTQ_SIZE,
2071 pHlp->pfnSSMSetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
2072 N_("Bad queue element index for re-do in saved state (%#x, max %#x)"),
2073 idxHead, VIRTQ_SIZE - 1));
2074
2075 PVIRTIOSCSIWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
2076 pWorkerR3->auRedoDescs[pWorkerR3->cRedoDescs++] = idxHead;
2077 pWorkerR3->cRedoDescs %= VIRTQ_SIZE;
2078 }
2079 }
2080
2081 /*
2082 * Call the virtio core to let it load its state.
2083 */
2084 rc = virtioCoreR3ModernDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM,
2085 uVersion, VIRTIOSCSI_SAVED_STATE_VERSION, pThis->virtioScsiConfig.uNumVirtqs);
2086
2087 /*
2088 * Nudge request queue workers
2089 */
2090 for (int uVirtqNbr = VIRTQ_REQ_BASE; uVirtqNbr < VIRTIOSCSI_VIRTQ_CNT; uVirtqNbr++)
2091 {
2092 if (pThis->afVirtqAttached[uVirtqNbr])
2093 {
2094 LogFunc(("Waking %s worker.\n", VIRTQNAME(uVirtqNbr)));
2095 int rc2 = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[uVirtqNbr].hEvtProcess);
2096 AssertRCReturn(rc, rc2);
2097 }
2098 }
2099
2100 return rc;
2101}
2102
2103/**
2104 * @callback_method_impl{FNSSMDEVSAVEEXEC}
2105 */
2106static DECLCALLBACK(int) virtioScsiR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
2107{
2108 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2109 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
2110 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2111
2112 LogFunc(("SAVE EXEC!!\n"));
2113
2114 for (int uVirtqNbr = 0; uVirtqNbr < VIRTIOSCSI_VIRTQ_CNT; uVirtqNbr++)
2115 pHlp->pfnSSMPutBool(pSSM, pThis->afVirtqAttached[uVirtqNbr]);
2116
2117 pHlp->pfnSSMPutU32(pSSM, pThis->virtioScsiConfig.uNumVirtqs);
2118 pHlp->pfnSSMPutU32(pSSM, pThis->virtioScsiConfig.uSegMax);
2119 pHlp->pfnSSMPutU32(pSSM, pThis->virtioScsiConfig.uMaxSectors);
2120 pHlp->pfnSSMPutU32(pSSM, pThis->virtioScsiConfig.uCmdPerLun);
2121 pHlp->pfnSSMPutU32(pSSM, pThis->virtioScsiConfig.uEventInfoSize);
2122 pHlp->pfnSSMPutU32(pSSM, pThis->virtioScsiConfig.uSenseSize);
2123 pHlp->pfnSSMPutU32(pSSM, pThis->virtioScsiConfig.uCdbSize);
2124 pHlp->pfnSSMPutU16(pSSM, pThis->virtioScsiConfig.uMaxChannel);
2125 pHlp->pfnSSMPutU16(pSSM, pThis->virtioScsiConfig.uMaxTarget);
2126 pHlp->pfnSSMPutU32(pSSM, pThis->virtioScsiConfig.uMaxLun);
2127 pHlp->pfnSSMPutU32(pSSM, pThis->fAsyncEvtsEnabled);
2128 pHlp->pfnSSMPutBool(pSSM, pThis->fEventsMissed);
2129 pHlp->pfnSSMPutU32(pSSM, pThis->fVirtioReady);
2130 pHlp->pfnSSMPutU32(pSSM, pThis->fHasT10pi);
2131 pHlp->pfnSSMPutU32(pSSM, pThis->fHasHotplug);
2132 pHlp->pfnSSMPutU32(pSSM, pThis->fHasInOutBufs);
2133 pHlp->pfnSSMPutU32(pSSM, pThis->fHasLunChange);
2134 pHlp->pfnSSMPutU32(pSSM, pThis->fResetting);
2135
2136 AssertMsg(!pThis->cActiveReqs, ("There are still outstanding requests on this device\n"));
2137
2138 pHlp->pfnSSMPutU32(pSSM, pThis->cTargets);
2139
2140 for (uint16_t uTarget = 0; uTarget < pThis->cTargets; uTarget++)
2141 {
2142 PVIRTIOSCSITARGET pTarget = &pThisCC->paTargetInstances[uTarget];
2143
2144 /* Query all suspended requests and store them in the request queue. */
2145 if (pTarget->pDrvMediaEx)
2146 {
2147 uint32_t cReqsRedo = pTarget->pDrvMediaEx->pfnIoReqGetSuspendedCount(pTarget->pDrvMediaEx);
2148
2149 pHlp->pfnSSMPutU16(pSSM, cReqsRedo);
2150
2151 if (cReqsRedo)
2152 {
2153 PDMMEDIAEXIOREQ hIoReq;
2154 PVIRTIOSCSIREQ pReq;
2155
2156 int rc = pTarget->pDrvMediaEx->pfnIoReqQuerySuspendedStart(pTarget->pDrvMediaEx, &hIoReq,
2157 (void **)&pReq);
2158 AssertRCBreak(rc);
2159
2160 while(--cReqsRedo)
2161 {
2162 pHlp->pfnSSMPutU16(pSSM, pReq->uVirtqNbr);
2163 pHlp->pfnSSMPutU16(pSSM, pReq->pVirtqBuf->uHeadIdx);
2164
2165 rc = pTarget->pDrvMediaEx->pfnIoReqQuerySuspendedNext(pTarget->pDrvMediaEx, hIoReq,
2166 &hIoReq, (void **)&pReq);
2167 AssertRCBreak(rc);
2168 }
2169 }
2170 }
2171 }
2172
2173 /*
2174 * Call the virtio core to let it save its state.
2175 */
2176 return virtioCoreR3SaveExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, VIRTIOSCSI_SAVED_STATE_VERSION, VIRTIOSCSI_VIRTQ_CNT);
2177}
2178
2179
2180/*********************************************************************************************************************************
2181* Device interface. *
2182*********************************************************************************************************************************/
2183
2184/**
2185 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
2186 *
2187 * One harddisk at one port has been unplugged.
2188 * The VM is suspended at this point.
2189 */
2190static DECLCALLBACK(void) virtioScsiR3Detach(PPDMDEVINS pDevIns, unsigned uTarget, uint32_t fFlags)
2191{
2192 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2193 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
2194 AssertReturnVoid(uTarget < pThis->cTargets);
2195 PVIRTIOSCSITARGET pTarget = &pThisCC->paTargetInstances[uTarget];
2196
2197 LogFunc((""));
2198
2199 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2200 ("virtio-scsi: Device does not support hotplugging\n"));
2201 RT_NOREF(fFlags);
2202
2203 /*
2204 * Zero all important members.
2205 */
2206 pTarget->fPresent = false;
2207 pTarget->pDrvBase = NULL;
2208 pTarget->pDrvMedia = NULL;
2209 pTarget->pDrvMediaEx = NULL;
2210}
2211
2212/**
2213 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
2214 *
2215 * This is called when we change block driver.
2216 */
2217static DECLCALLBACK(int) virtioScsiR3Attach(PPDMDEVINS pDevIns, unsigned uTarget, uint32_t fFlags)
2218{
2219 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2220 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
2221 AssertReturn(uTarget < pThis->cTargets, VERR_PDM_LUN_NOT_FOUND);
2222 PVIRTIOSCSITARGET pTarget = &pThisCC->paTargetInstances[uTarget];
2223
2224 Assert(pTarget->pDevIns == pDevIns);
2225 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2226 ("virtio-scsi: Device does not support hotplugging\n"),
2227 VERR_INVALID_PARAMETER);
2228
2229 AssertRelease(!pTarget->pDrvBase);
2230 Assert(pTarget->uTarget == uTarget);
2231
2232 /*
2233 * Try attach the SCSI driver and get the interfaces, required as well as optional.
2234 */
2235 int rc = PDMDevHlpDriverAttach(pDevIns, pTarget->uTarget, &pDevIns->IBase, &pTarget->pDrvBase, pTarget->pszTargetName);
2236 if (RT_SUCCESS(rc))
2237 {
2238 pTarget->fPresent = true;
2239 pTarget->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIA);
2240 AssertMsgReturn(RT_VALID_PTR(pTarget->pDrvMedia),
2241 ("virtio-scsi configuration error: LUN#%d missing basic media interface!\n", uTarget),
2242 VERR_PDM_MISSING_INTERFACE);
2243
2244 /* Get the extended media interface. */
2245 pTarget->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIAEX);
2246 AssertMsgReturn(RT_VALID_PTR(pTarget->pDrvMediaEx),
2247 ("virtio-scsi configuration error: LUN#%d missing extended media interface!\n", uTarget),
2248 VERR_PDM_MISSING_INTERFACE);
2249
2250 rc = pTarget->pDrvMediaEx->pfnIoReqAllocSizeSet(pTarget->pDrvMediaEx, sizeof(VIRTIOSCSIREQ));
2251 AssertMsgReturn(RT_VALID_PTR(pTarget->pDrvMediaEx),
2252 ("virtio-scsi configuration error: LUN#%u: Failed to set I/O request size!\n", uTarget),
2253 rc);
2254 }
2255 else
2256 AssertMsgFailed(("Failed to attach %s. rc=%Rrc\n", pTarget->pszTargetName, rc));
2257
2258 if (RT_FAILURE(rc))
2259 {
2260 pTarget->fPresent = false;
2261 pTarget->pDrvBase = NULL;
2262 pTarget->pDrvMedia = NULL;
2263 pTarget->pDrvMediaEx = NULL;
2264 pThisCC->pMediaNotify = NULL;
2265 }
2266 return rc;
2267}
2268
2269/**
2270 * @callback_method_impl{FNPDMDEVASYNCNOTIFY}
2271 */
2272static DECLCALLBACK(bool) virtioScsiR3DeviceQuiesced(PPDMDEVINS pDevIns)
2273{
2274 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2275 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
2276
2277 if (ASMAtomicReadU32(&pThis->cActiveReqs))
2278 return false;
2279
2280 LogFunc(("Device I/O activity quiesced: %s\n",
2281 virtioCoreGetStateChangeText(pThisCC->enmQuiescingFor)));
2282
2283 virtioCoreR3VmStateChanged(&pThis->Virtio, pThisCC->enmQuiescingFor);
2284
2285 pThis->fResetting = false;
2286 pThisCC->fQuiescing = false;
2287
2288 return true;
2289}
2290
2291/**
2292 * Worker for virtioScsiR3Reset() and virtioScsiR3SuspendOrPowerOff().
2293 */
2294static void virtioScsiR3QuiesceDevice(PPDMDEVINS pDevIns, VIRTIOVMSTATECHANGED enmQuiscingFor)
2295{
2296 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2297 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
2298
2299 /* Prevent worker threads from removing/processing elements from virtq's */
2300 pThisCC->fQuiescing = true;
2301 pThisCC->enmQuiescingFor = enmQuiscingFor;
2302
2303 PDMDevHlpSetAsyncNotification(pDevIns, virtioScsiR3DeviceQuiesced);
2304
2305 /* If already quiesced invoke async callback. */
2306 if (!ASMAtomicReadU32(&pThis->cActiveReqs))
2307 PDMDevHlpAsyncNotificationCompleted(pDevIns);
2308}
2309
2310/**
2311 * @interface_method_impl{PDMDEVREGR3,pfnReset}
2312 */
2313static DECLCALLBACK(void) virtioScsiR3Reset(PPDMDEVINS pDevIns)
2314{
2315 LogFunc(("\n"));
2316 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2317 pThis->fResetting = true;
2318 virtioScsiR3QuiesceDevice(pDevIns, kvirtIoVmStateChangedReset);
2319}
2320
2321/**
2322 * @interface_method_impl{PDMDEVREGR3,pfnPowerOff}
2323 */
2324static DECLCALLBACK(void) virtioScsiR3SuspendOrPowerOff(PPDMDEVINS pDevIns, VIRTIOVMSTATECHANGED enmType)
2325{
2326 LogFunc(("\n"));
2327
2328 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2329 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
2330
2331 /* VM is halted, thus no new I/O being dumped into queues by the guest.
2332 * Workers have been flagged to stop pulling stuff already queued-up by the guest.
2333 * Now tell lower-level to to suspend reqs (for example, DrvVD suspends all reqs
2334 * on its wait queue, and we will get a callback as the state changes to
2335 * suspended (and later, resumed) for each).
2336 */
2337 for (uint32_t i = 0; i < pThis->cTargets; i++)
2338 {
2339 PVIRTIOSCSITARGET pTarget = &pThisCC->paTargetInstances[i];
2340 if (pTarget->pDrvMediaEx)
2341 pTarget->pDrvMediaEx->pfnNotifySuspend(pTarget->pDrvMediaEx);
2342 }
2343
2344 virtioScsiR3QuiesceDevice(pDevIns, enmType);
2345}
2346
2347/**
2348 * @interface_method_impl{PDMDEVREGR3,pfnSuspend}
2349 */
2350static DECLCALLBACK(void) virtioScsiR3PowerOff(PPDMDEVINS pDevIns)
2351{
2352 LogFunc(("\n"));
2353 virtioScsiR3SuspendOrPowerOff(pDevIns, kvirtIoVmStateChangedPowerOff);
2354}
2355
2356/**
2357 * @interface_method_impl{PDMDEVREGR3,pfnSuspend}
2358 */
2359static DECLCALLBACK(void) virtioScsiR3Suspend(PPDMDEVINS pDevIns)
2360{
2361 LogFunc(("\n"));
2362 virtioScsiR3SuspendOrPowerOff(pDevIns, kvirtIoVmStateChangedSuspend);
2363}
2364
2365/**
2366 * @interface_method_impl{PDMDEVREGR3,pfnResume}
2367 */
2368static DECLCALLBACK(void) virtioScsiR3Resume(PPDMDEVINS pDevIns)
2369{
2370 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2371 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
2372 LogFunc(("\n"));
2373
2374 pThisCC->fQuiescing = false;
2375
2376 /* Wake worker threads flagged to skip pulling queue entries during quiesce
2377 * to ensure they re-check their queues. Active request queues may already
2378 * be awake due to new reqs coming in.
2379 */
2380 for (uint16_t uVirtqNbr = 0; uVirtqNbr < VIRTIOSCSI_REQ_VIRTQ_CNT; uVirtqNbr++)
2381 {
2382 if ( virtioCoreIsVirtqEnabled(&pThis->Virtio, uVirtqNbr)
2383 && ASMAtomicReadBool(&pThis->aWorkers[uVirtqNbr].fSleeping))
2384 {
2385 Log6Func(("waking %s worker.\n", VIRTQNAME(uVirtqNbr)));
2386 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[uVirtqNbr].hEvtProcess);
2387 AssertRC(rc);
2388 }
2389 }
2390 /* Ensure guest is working the queues too. */
2391 virtioCoreR3VmStateChanged(&pThis->Virtio, kvirtIoVmStateChangedResume);
2392}
2393
2394/**
2395 * @interface_method_impl{PDMIMEDIAEXPORT,pfnMediumEjected}
2396 */
2397static DECLCALLBACK(void) virtioScsiR3MediumEjected(PPDMIMEDIAEXPORT pInterface)
2398{
2399 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
2400 PPDMDEVINS pDevIns = pTarget->pDevIns;
2401 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
2402
2403#if 0 /* need more info about how to use this event. The VirtIO 1.0 specification
2404 * lists several SCSI related event types but presumes the reader knows
2405 * how to use them without providing references. */
2406 virtioScsiR3ReportMediaChange(pDevIns, pThis, pTarget->uTarget);
2407#endif
2408
2409 if (pThisCC->pMediaNotify)
2410 {
2411 int rc = PDMDevHlpVMReqCallNoWait(pDevIns, VMCPUID_ANY,
2412 (PFNRT)pThisCC->pMediaNotify->pfnEjected, 2,
2413 pThisCC->pMediaNotify, pTarget->uTarget);
2414 AssertRC(rc);
2415 }
2416}
2417
2418/**
2419 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
2420 */
2421static DECLCALLBACK(void) virtioScsiR3IoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
2422 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
2423{
2424 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
2425 PPDMDEVINS pDevIns = pTarget->pDevIns;
2426 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2427 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
2428 RT_NOREF(hIoReq, pvIoReqAlloc);
2429
2430 switch (enmState)
2431 {
2432 case PDMMEDIAEXIOREQSTATE_SUSPENDED:
2433 {
2434 /* Stop considering this request active */
2435 virtioScsiR3Release(pDevIns, pThis, pThisCC);
2436 break;
2437 }
2438 case PDMMEDIAEXIOREQSTATE_ACTIVE:
2439 virtioScsiR3Retain(pThis);
2440 break;
2441 default:
2442 AssertMsgFailed(("Invalid request state given %u\n", enmState));
2443 }
2444}
2445
2446/**
2447 * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
2448 */
2449static DECLCALLBACK(int) virtioScsiR3Destruct(PPDMDEVINS pDevIns)
2450{
2451 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
2452 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2453 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
2454
2455 RTMemFree(pThisCC->paTargetInstances);
2456 pThisCC->paTargetInstances = NULL;
2457 pThisCC->pMediaNotify = NULL;
2458
2459 for (unsigned uVirtqNbr = 0; uVirtqNbr < VIRTIOSCSI_VIRTQ_CNT; uVirtqNbr++)
2460 {
2461 PVIRTIOSCSIWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
2462 if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
2463 {
2464 PDMDevHlpSUPSemEventClose(pDevIns, pWorker->hEvtProcess);
2465 pWorker->hEvtProcess = NIL_SUPSEMEVENT;
2466 }
2467
2468 if (pThisCC->aWorkers[uVirtqNbr].pThread)
2469 {
2470 /* Destroy the thread. */
2471 int rcThread;
2472 int rc = PDMDevHlpThreadDestroy(pDevIns, pThisCC->aWorkers[uVirtqNbr].pThread, &rcThread);
2473 if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
2474 AssertMsgFailed(("%s Failed to destroythread rc=%Rrc rcThread=%Rrc\n",
2475 __FUNCTION__, rc, rcThread));
2476 pThisCC->aWorkers[uVirtqNbr].pThread = NULL;
2477 }
2478
2479 if (RTCritSectIsInitialized(&pThisCC->aWorkers[uVirtqNbr].CritSectVirtq))
2480 RTCritSectDelete(&pThisCC->aWorkers[uVirtqNbr].CritSectVirtq);
2481 }
2482
2483 virtioCoreR3Term(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
2484 return VINF_SUCCESS;
2485}
2486
2487/**
2488 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
2489 */
2490static DECLCALLBACK(int) virtioScsiR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2491{
2492 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2493 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2494 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
2495 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2496
2497 /*
2498 * Quick initialization of the state data, making sure that the destructor always works.
2499 */
2500 pThisCC->pDevIns = pDevIns;
2501
2502 LogFunc(("PDM device instance: %d\n", iInstance));
2503 RTStrPrintf(pThis->szInstance, sizeof(pThis->szInstance), "VIRTIOSCSI%d", iInstance);
2504
2505 pThisCC->IBase.pfnQueryInterface = virtioScsiR3DeviceQueryInterface;
2506 pThisCC->ILeds.pfnQueryStatusLed = virtioScsiR3DeviceQueryStatusLed;
2507
2508 /*
2509 * Validate and read configuration.
2510 */
2511 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "NumTargets|Bootable", "");
2512
2513 int rc = pHlp->pfnCFGMQueryU32Def(pCfg, "NumTargets", &pThis->cTargets, 1);
2514 if (RT_FAILURE(rc))
2515 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi configuration error: failed to read NumTargets as integer"));
2516 if (pThis->cTargets < 1 || pThis->cTargets > VIRTIOSCSI_MAX_TARGETS)
2517 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2518 N_("virtio-scsi configuration error: NumTargets=%u is out of range (1..%u)"),
2519 pThis->cTargets, VIRTIOSCSI_MAX_TARGETS);
2520
2521 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "Bootable", &pThis->fBootable, true);
2522 if (RT_FAILURE(rc))
2523 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi configuration error: failed to read Bootable as boolean"));
2524
2525 LogRel(("%s: Targets=%u Bootable=%RTbool (unimplemented) R0Enabled=%RTbool RCEnabled=%RTbool\n",
2526 pThis->szInstance, pThis->cTargets, pThis->fBootable, pDevIns->fR0Enabled, pDevIns->fRCEnabled));
2527
2528
2529 /*
2530 * Do core virtio initialization.
2531 */
2532
2533 /* Configure virtio_scsi_config that transacts via VirtIO implementation's Dev. Specific Cap callbacks */
2534 pThis->virtioScsiConfig.uNumVirtqs = VIRTIOSCSI_REQ_VIRTQ_CNT;
2535 pThis->virtioScsiConfig.uSegMax = VIRTIOSCSI_MAX_SEG_COUNT;
2536 pThis->virtioScsiConfig.uMaxSectors = VIRTIOSCSI_MAX_SECTORS_HINT;
2537 pThis->virtioScsiConfig.uCmdPerLun = VIRTIOSCSI_MAX_COMMANDS_PER_LUN;
2538 pThis->virtioScsiConfig.uEventInfoSize = sizeof(VIRTIOSCSI_EVENT_T); /*VirtIO 1.0 Spec says at least this size! */
2539 pThis->virtioScsiConfig.uSenseSize = VIRTIOSCSI_SENSE_SIZE_DEFAULT;
2540 pThis->virtioScsiConfig.uCdbSize = VIRTIOSCSI_CDB_SIZE_DEFAULT;
2541 pThis->virtioScsiConfig.uMaxChannel = VIRTIOSCSI_MAX_CHANNEL_HINT;
2542 pThis->virtioScsiConfig.uMaxTarget = pThis->cTargets;
2543 pThis->virtioScsiConfig.uMaxLun = VIRTIOSCSI_MAX_LUN;
2544
2545 /* Initialize the generic Virtio core: */
2546 pThisCC->Virtio.pfnVirtqNotified = virtioScsiNotified;
2547 pThisCC->Virtio.pfnStatusChanged = virtioScsiR3StatusChanged;
2548 pThisCC->Virtio.pfnDevCapRead = virtioScsiR3DevCapRead;
2549 pThisCC->Virtio.pfnDevCapWrite = virtioScsiR3DevCapWrite;
2550
2551 VIRTIOPCIPARAMS VirtioPciParams;
2552 VirtioPciParams.uDeviceId = PCI_DEVICE_ID_VIRTIOSCSI_HOST;
2553 VirtioPciParams.uClassBase = PCI_CLASS_BASE_MASS_STORAGE;
2554 VirtioPciParams.uClassSub = PCI_CLASS_SUB_SCSI_STORAGE_CONTROLLER;
2555 VirtioPciParams.uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
2556 VirtioPciParams.uSubsystemId = PCI_DEVICE_ID_VIRTIOSCSI_HOST; /* VirtIO 1.0 spec allows PCI Device ID here */
2557 VirtioPciParams.uInterruptLine = 0x00;
2558 VirtioPciParams.uInterruptPin = 0x01;
2559
2560 rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, pThis->szInstance,
2561 VIRTIOSCSI_HOST_SCSI_FEATURES_OFFERED, 0 /*fOfferLegacy*/,
2562 &pThis->virtioScsiConfig /*pvDevSpecificCap*/, sizeof(pThis->virtioScsiConfig));
2563 if (RT_FAILURE(rc))
2564 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi: failed to initialize VirtIO"));
2565
2566 /*
2567 * Initialize queues.
2568 */
2569
2570 virtioScsiSetVirtqNames(pThis);
2571
2572 /* Attach the queues and create worker threads for them: */
2573 for (uint16_t uVirtqNbr = 0; uVirtqNbr < VIRTIOSCSI_VIRTQ_CNT; uVirtqNbr++)
2574 {
2575 rc = virtioCoreR3VirtqAttach(&pThis->Virtio, uVirtqNbr, VIRTQNAME(uVirtqNbr));
2576 if (RT_FAILURE(rc))
2577 continue;
2578 if (uVirtqNbr == CONTROLQ_IDX || IS_REQ_VIRTQ(uVirtqNbr))
2579 {
2580 rc = PDMDevHlpThreadCreate(pDevIns, &pThisCC->aWorkers[uVirtqNbr].pThread,
2581 (void *)(uintptr_t)uVirtqNbr, virtioScsiR3WorkerThread,
2582 virtioScsiR3WorkerWakeUp, 0, RTTHREADTYPE_IO, VIRTQNAME(uVirtqNbr));
2583 if (rc != VINF_SUCCESS)
2584 {
2585 LogRel(("Error creating thread for Virtual Virtq %s: %Rrc\n", VIRTQNAME(uVirtqNbr), rc));
2586 return rc;
2587 }
2588
2589 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->aWorkers[uVirtqNbr].hEvtProcess);
2590 if (RT_FAILURE(rc))
2591 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2592 N_("DevVirtioSCSI: Failed to create SUP event semaphore"));
2593 }
2594 pThis->afVirtqAttached[uVirtqNbr] = true;
2595 rc = RTCritSectInit(&pThisCC->aWorkers[uVirtqNbr].CritSectVirtq);
2596 if (RT_FAILURE(rc))
2597 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2598 N_("DevVirtioSCSI: Failed to create worker critical section"));
2599 }
2600
2601 /*
2602 * Initialize per device instances (targets).
2603 */
2604 Log2Func(("Probing %d targets ...\n", pThis->cTargets));
2605
2606 pThisCC->paTargetInstances = (PVIRTIOSCSITARGET)RTMemAllocZ(sizeof(VIRTIOSCSITARGET) * pThis->cTargets);
2607 if (!pThisCC->paTargetInstances)
2608 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to allocate memory for target states"));
2609
2610 for (uint32_t uTarget = 0; uTarget < pThis->cTargets; uTarget++)
2611 {
2612 PVIRTIOSCSITARGET pTarget = &pThisCC->paTargetInstances[uTarget];
2613
2614 if (RTStrAPrintf(&pTarget->pszTargetName, "VSCSI%u", uTarget) < 0)
2615 AssertLogRelFailedReturn(VERR_NO_MEMORY);
2616
2617 /* Initialize static parts of the device. */
2618 pTarget->pDevIns = pDevIns;
2619 pTarget->uTarget = uTarget;
2620
2621 pTarget->IBase.pfnQueryInterface = virtioScsiR3TargetQueryInterface;
2622
2623 /* IMediaPort and IMediaExPort interfaces provide callbacks for VD media and downstream driver access */
2624 pTarget->IMediaPort.pfnQueryDeviceLocation = virtioScsiR3QueryDeviceLocation;
2625 pTarget->IMediaPort.pfnQueryScsiInqStrings = NULL;
2626 pTarget->IMediaExPort.pfnIoReqCompleteNotify = virtioScsiR3IoReqFinish;
2627 pTarget->IMediaExPort.pfnIoReqCopyFromBuf = virtioScsiR3IoReqCopyFromBuf;
2628 pTarget->IMediaExPort.pfnIoReqCopyToBuf = virtioScsiR3IoReqCopyToBuf;
2629 pTarget->IMediaExPort.pfnIoReqStateChanged = virtioScsiR3IoReqStateChanged;
2630 pTarget->IMediaExPort.pfnMediumEjected = virtioScsiR3MediumEjected;
2631 pTarget->IMediaExPort.pfnIoReqQueryBuf = NULL; /* When used avoids copyFromBuf CopyToBuf*/
2632 pTarget->IMediaExPort.pfnIoReqQueryDiscardRanges = NULL;
2633
2634 pTarget->IBase.pfnQueryInterface = virtioScsiR3TargetQueryInterface;
2635 pTarget->ILed.pfnQueryStatusLed = virtioScsiR3TargetQueryStatusLed;
2636 pTarget->led.u32Magic = PDMLED_MAGIC;
2637
2638 LogFunc(("Attaching LUN: %s\n", pTarget->pszTargetName));
2639
2640 AssertReturn(uTarget < pThis->cTargets, VERR_PDM_NO_SUCH_LUN);
2641 rc = PDMDevHlpDriverAttach(pDevIns, uTarget, &pTarget->IBase, &pTarget->pDrvBase, pTarget->pszTargetName);
2642 if (RT_SUCCESS(rc))
2643 {
2644 pTarget->fPresent = true;
2645
2646 pTarget->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIA);
2647 AssertMsgReturn(RT_VALID_PTR(pTarget->pDrvMedia),
2648 ("virtio-scsi configuration error: LUN#%d missing basic media interface!\n", uTarget),
2649 VERR_PDM_MISSING_INTERFACE);
2650 /* Get the extended media interface. */
2651 pTarget->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIAEX);
2652 AssertMsgReturn(RT_VALID_PTR(pTarget->pDrvMediaEx),
2653 ("virtio-scsi configuration error: LUN#%d missing extended media interface!\n", uTarget),
2654 VERR_PDM_MISSING_INTERFACE);
2655
2656 rc = pTarget->pDrvMediaEx->pfnIoReqAllocSizeSet(pTarget->pDrvMediaEx, sizeof(VIRTIOSCSIREQ));
2657 AssertMsgReturn(RT_VALID_PTR(pTarget->pDrvMediaEx),
2658 ("virtio-scsi configuration error: LUN#%u: Failed to set I/O request size!\n", uTarget),
2659 rc);
2660 }
2661 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2662 {
2663 pTarget->fPresent = false;
2664 pTarget->pDrvBase = NULL;
2665 Log(("virtio-scsi: no driver attached to device %s\n", pTarget->pszTargetName));
2666 rc = VINF_SUCCESS;
2667 }
2668 else
2669 {
2670 AssertLogRelMsgFailed(("virtio-scsi: Failed to attach %s: %Rrc\n", pTarget->pszTargetName, rc));
2671 return rc;
2672 }
2673 }
2674
2675 /*
2676 * Status driver (optional).
2677 */
2678 PPDMIBASE pUpBase = NULL;
2679 AssertCompile(PDM_STATUS_LUN >= VIRTIOSCSI_MAX_TARGETS);
2680 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pUpBase, "Status Port");
2681 if (RT_FAILURE(rc) && rc != VERR_PDM_NO_ATTACHED_DRIVER)
2682 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
2683 if (RT_SUCCESS(rc) && pUpBase)
2684 pThisCC->pMediaNotify = PDMIBASE_QUERY_INTERFACE(pUpBase, PDMIMEDIANOTIFY);
2685
2686
2687 /*
2688 * Register saved state.
2689 */
2690 rc = PDMDevHlpSSMRegister(pDevIns, VIRTIOSCSI_SAVED_STATE_VERSION, sizeof(*pThis),
2691 virtioScsiR3SaveExec, virtioScsiR3LoadExec);
2692 AssertRCReturn(rc, rc);
2693
2694 /*
2695 * Register the debugger info callback (ignore errors).
2696 */
2697 char szTmp[128];
2698 RTStrPrintf(szTmp, sizeof(szTmp), "%s%u", pDevIns->pReg->szName, pDevIns->iInstance);
2699 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "virtio-scsi info", virtioScsiR3Info);
2700
2701 return rc;
2702}
2703
2704#else /* !IN_RING3 */
2705
2706/**
2707 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
2708 */
2709static DECLCALLBACK(int) virtioScsiRZConstruct(PPDMDEVINS pDevIns)
2710{
2711 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2712
2713 PVIRTIOSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIOSCSI);
2714 PVIRTIOSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOSCSICC);
2715
2716
2717 pThisCC->Virtio.pfnVirtqNotified = virtioScsiNotified;
2718 return virtioCoreRZInit(pDevIns, &pThis->Virtio);
2719}
2720
2721#endif /* !IN_RING3 */
2722
2723
2724/**
2725 * The device registration structure.
2726 */
2727const PDMDEVREG g_DeviceVirtioSCSI =
2728{
2729 /* .u32Version = */ PDM_DEVREG_VERSION,
2730 /* .uReserved0 = */ 0,
2731 /* .szName = */ "virtio-scsi",
2732 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE
2733 | PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION
2734 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
2735 /* .fClass = */ PDM_DEVREG_CLASS_STORAGE,
2736 /* .cMaxInstances = */ ~0U,
2737 /* .uSharedVersion = */ 42,
2738 /* .cbInstanceShared = */ sizeof(VIRTIOSCSI),
2739 /* .cbInstanceCC = */ sizeof(VIRTIOSCSICC),
2740 /* .cbInstanceRC = */ sizeof(VIRTIOSCSIRC),
2741 /* .cMaxPciDevices = */ 1,
2742 /* .cMaxMsixVectors = */ VBOX_MSIX_MAX_ENTRIES,
2743 /* .pszDescription = */ "Virtio Host SCSI.\n",
2744#if defined(IN_RING3)
2745 /* .pszRCMod = */ "VBoxDDRC.rc",
2746 /* .pszR0Mod = */ "VBoxDDR0.r0",
2747 /* .pfnConstruct = */ virtioScsiR3Construct,
2748 /* .pfnDestruct = */ virtioScsiR3Destruct,
2749 /* .pfnRelocate = */ NULL,
2750 /* .pfnMemSetup = */ NULL,
2751 /* .pfnPowerOn = */ NULL,
2752 /* .pfnReset = */ virtioScsiR3Reset,
2753 /* .pfnSuspend = */ virtioScsiR3Suspend,
2754 /* .pfnResume = */ virtioScsiR3Resume,
2755 /* .pfnAttach = */ virtioScsiR3Attach,
2756 /* .pfnDetach = */ virtioScsiR3Detach,
2757 /* .pfnQueryInterface = */ NULL,
2758 /* .pfnInitComplete = */ NULL,
2759 /* .pfnPowerOff = */ virtioScsiR3PowerOff,
2760 /* .pfnSoftReset = */ NULL,
2761 /* .pfnReserved0 = */ NULL,
2762 /* .pfnReserved1 = */ NULL,
2763 /* .pfnReserved2 = */ NULL,
2764 /* .pfnReserved3 = */ NULL,
2765 /* .pfnReserved4 = */ NULL,
2766 /* .pfnReserved5 = */ NULL,
2767 /* .pfnReserved6 = */ NULL,
2768 /* .pfnReserved7 = */ NULL,
2769#elif defined(IN_RING0)
2770 /* .pfnEarlyConstruct = */ NULL,
2771 /* .pfnConstruct = */ virtioScsiRZConstruct,
2772 /* .pfnDestruct = */ NULL,
2773 /* .pfnFinalDestruct = */ NULL,
2774 /* .pfnRequest = */ NULL,
2775 /* .pfnReserved0 = */ NULL,
2776 /* .pfnReserved1 = */ NULL,
2777 /* .pfnReserved2 = */ NULL,
2778 /* .pfnReserved3 = */ NULL,
2779 /* .pfnReserved4 = */ NULL,
2780 /* .pfnReserved5 = */ NULL,
2781 /* .pfnReserved6 = */ NULL,
2782 /* .pfnReserved7 = */ NULL,
2783#elif defined(IN_RC)
2784 /* .pfnConstruct = */ virtioScsiRZConstruct,
2785 /* .pfnReserved0 = */ NULL,
2786 /* .pfnReserved1 = */ NULL,
2787 /* .pfnReserved2 = */ NULL,
2788 /* .pfnReserved3 = */ NULL,
2789 /* .pfnReserved4 = */ NULL,
2790 /* .pfnReserved5 = */ NULL,
2791 /* .pfnReserved6 = */ NULL,
2792 /* .pfnReserved7 = */ NULL,
2793#else
2794# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2795#endif
2796 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2797};
2798
Note: See TracBrowser for help on using the repository browser.

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