VirtualBox

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

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

Storage/DevVirtioSCSI.cpp: More fixing burns on build box for stuff that builds clean on my Debian machine

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

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