VirtualBox

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

Last change on this file was 100400, checked in by vboxsync, 12 months ago

Devices/VirtIO: Add support for the VirtIO over MMIO transport mode useful for ARM, bugref:10459

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

© 2023 Oracle
ContactPrivacy policyTerms of Use