VirtualBox

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

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

Storage/DevVirtioSCSI.cpp: This is being integrated to stop burns. I pasted in and modified some hexdump code I wrote at Sun many years ago, and apparently my editor isn't smart enough to apply the tab and CRLF filters it does when things are entered via keystrokes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 95.1 KB
Line 
1/* $Id: DevVirtioSCSI.cpp 80528 2019-09-01 22:28:39Z vboxsync $ $Revision: 80528 $ $Date: 2019-09-01 22:28:39 +0000 (Sun, 01 Sep 2019) $ $Author: vboxsync $ */
2/** @file
3 * VBox storage devices - Virtio SCSI Driver
4 *
5 */
6
7/*
8 * Copyright (C) 2006-2019 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*********************************************************************************************************************************
21* Header Files *
22*********************************************************************************************************************************/
23//#define LOG_GROUP LOG_GROUP_DRV_SCSI
24#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
25
26#include <VBox/vmm/pdmdev.h>
27#include <VBox/vmm/pdmstorageifs.h>
28#include <VBox/vmm/pdmcritsect.h>
29#include <VBox/version.h>
30#include <VBox/log.h>
31#include <iprt/errcore.h>
32#include <iprt/assert.h>
33#include <iprt/string.h>
34#include "../build/VBoxDD.h"
35#include <VBox/scsi.h>
36#ifdef IN_RING3
37# include <iprt/alloc.h>
38# include <iprt/memcache.h>
39# include <iprt/semaphore.h>
40# include <iprt/sg.h>
41# include <iprt/param.h>
42# include <iprt/uuid.h>
43#endif
44#include "../VirtIO/Virtio_1_0.h"
45
46#include "VBoxSCSI.h"
47#include "VBoxDD.h"
48
49
50/**
51 * @name VirtIO 1.0 SCSI Host feature bits (See VirtIO 1.0 specification, Section 5.6.3)
52 * @{ */
53#define VIRTIO_SCSI_F_INOUT RT_BIT_64(0) /** Request is device readable AND writeable */
54#define VIRTIO_SCSI_F_HOTPLUG RT_BIT_64(1) /** Host allows hotplugging SCSI LUNs & targets */
55#define VIRTIO_SCSI_F_CHANGE RT_BIT_64(2) /** Host LUNs chgs via VIRTIOSCSI_T_PARAM_CHANGE evt */
56#define VIRTIO_SCSI_F_T10_PI RT_BIT_64(3) /** Add T10 port info (DIF/DIX) in SCSI req hdr */
57/** @} */
58
59
60#define VIRTIOSCSI_HOST_SCSI_FEATURES_ALL \
61 (VIRTIO_SCSI_F_INOUT | VIRTIO_SCSI_F_HOTPLUG | VIRTIO_SCSI_F_CHANGE | VIRTIO_SCSI_F_T10_PI)
62
63#define VIRTIOSCSI_HOST_SCSI_FEATURES_NONE 0
64
65#define VIRTIOSCSI_HOST_SCSI_FEATURES_OFFERED \
66 VIRTIOSCSI_HOST_SCSI_FEATURES_NONE
67
68/**
69 * TEMPORARY NOTE: following parameter is set to 1 for early development. Will be increased later
70 */
71#define VIRTIOSCSI_REQ_QUEUE_CNT 1 /**< Number of req queues exposed by dev. */
72#define VIRTIOSCSI_QUEUE_CNT VIRTIOSCSI_REQ_QUEUE_CNT + 2
73#define VIRTIOSCSI_MAX_TARGETS 1 /**< Can probably determined from higher layers */
74#define VIRTIOSCSI_MAX_LUN 1 /* < VirtIO specification, section 5.6.4 */
75#define VIRTIOSCSI_MAX_COMMANDS_PER_LUN 1 /* < T.B.D. What is a good value for this? */
76#define VIRTIOSCSI_MAX_SEG_COUNT 1024 /* < T.B.D. What is a good value for this? */
77#define VIRTIOSCSI_MAX_SECTORS_HINT 0x10000 /* < VirtIO specification, section 5.6.4 */
78#define VIRTIOSCSI_MAX_CHANNEL_HINT 0 /* < VirtIO specification, section 5.6.4 should be 0 */
79#define VIRTIOSCSI_SAVED_STATE_MINOR_VERSION 0x01 /**< SSM version # */
80
81#define PCI_DEVICE_ID_VIRTIOSCSI_HOST 0x1048 /**< Informs guest driver of type of VirtIO device */
82#define PCI_CLASS_BASE_MASS_STORAGE 0x01 /**< PCI Mass Storage device class */
83#define PCI_CLASS_SUB_SCSI_STORAGE_CONTROLLER 0x00 /**< PCI SCSI Controller subclass */
84#define PCI_CLASS_PROG_UNSPECIFIED 0x00 /**< Programming interface. N/A. */
85#define VIRTIOSCSI_PCI_CLASS 0x01 /**< Base class Mass Storage? */
86
87
88#define VIRTIOSCSI_SENSE_SIZE_DEFAULT 96 /**< VirtIO 1.0: 96 on reset, guest can change */
89#define VIRTIOSCSI_CDB_SIZE_DEFAULT 32 /**< VirtIO 1.0: 32 on reset, guest can change */
90#define VIRTIOSCSI_PI_BYTES_IN 1 /**< Value TBD (see section 5.6.6.1) */
91#define VIRTIOSCSI_PI_BYTES_OUT 1 /**< Value TBD (see section 5.6.6.1) */
92#define VIRTIOSCSI_DATA_OUT 512 /**< Value TBD (see section 5.6.6.1) */
93
94/**
95 * VirtIO SCSI Host Device device-specific queue indicies
96 *
97 * Virtqs (and their indices) are specified for a SCSI Host Device as described in the VirtIO 1.0 specification
98 * section 5.6. Thus there is no need to explicitly indicate the number of queues needed by this device. The number
99 * of req queues is variable and determined by virtio_scsi_config.num_queues. See VirtIO 1.0 spec section 5.6.4
100 */
101#define CONTROLQ_IDX 0 /**< Spec-defined Index of control queue */
102#define EVENTQ_IDX 1 /**< Spec-defined Index of event queue */
103#define VIRTQ_REQ_BASE 2 /**< Spec-defined base index of request queues */
104
105#define QUEUENAME(qIdx) (pThis->szQueueNames[qIdx]) /**< Macro to get queue name from its index */
106#define CBQUEUENAME(qIdx) RTStrNLen(QUEUENAME(qIdx), sizeof(QUEUENAME(qIdx)))
107
108#define IS_REQ_QUEUE(qIdx) (qIdx >= VIRTQ_REQ_BASE && qIdx < VIRTIOSCSI_QUEUE_CNT)
109/**
110 * The following struct is the VirtIO SCSI Host Device device-specific configuration described in section 5.6.4
111 * of the VirtIO 1.0 specification. This layout maps an MMIO area shared VirtIO guest driver. The VBox VirtIO
112 * this virtual controller device implementation is a client of. The frame work calls back whenever the guest driver
113 * accesses any part of field in this struct
114 */
115typedef struct virtio_scsi_config
116{
117 uint32_t uNumQueues; /**< num_queues # of req q's exposed by dev */
118 uint32_t uSegMax; /**< seg_max Max # of segs allowed in cmd */
119 uint32_t uMaxSectors; /**< max_sectors Hint to guest max xfer to use */
120 uint32_t uCmdPerLun; /**< cmd_per_lun Max # of link cmd sent per lun */
121 uint32_t uEventInfoSize; /**< event_info_size Fill max, evtq bufs */
122 uint32_t uSenseSize; /**< sense_size Max sense data size dev writes */
123 uint32_t uCdbSize; /**< cdb_size Max CDB size driver writes */
124 uint16_t uMaxChannel; /**< max_channel Hint to guest driver */
125 uint16_t uMaxTarget; /**< max_target Hint to guest driver */
126 uint32_t uMaxLun; /**< max_lun Hint to guest driver */
127} VIRTIOSCSI_CONFIG_T, PVIRTIOSCSI_CONFIG_T;
128
129
130/**
131 * @name VirtIO 1.0 SCSI Host Device device specific control types
132 * @{ */
133#define VIRTIOSCSI_T_NO_EVENT 0
134#define VIRTIOSCSI_T_TRANSPORT_RESET 1
135#define VIRTIOSCSI_T_ASYNC_NOTIFY 2 /**< Asynchronous notification */
136#define VIRTIOSCSI_T_PARAM_CHANGE 3
137/** @} */
138
139/**
140 * Device operation: eventq
141 */
142#define VIRTIOSCSI_T_EVENTS_MISSED 0x80000000
143typedef struct virtio_scsi_event {
144 // Device-writable part
145 uint32_t uEvent; /**< event: */
146 uint8_t uLUN[8]; /**< lun */
147 uint32_t uReason; /**< reason */
148} VIRTIOSCSI_EVENT_T, *PVIRTIOSCSI_EVENT_T;
149
150/**
151 * @name VirtIO 1.0 SCSI Host Device device specific event types
152 * @{ */
153#define VIRTIOSCSI_EVT_RESET_HARD 0 /**< */
154#define VIRTIOSCSI_EVT_RESET_RESCAN 1 /**< */
155#define VIRTIOSCSI_EVT_RESET_REMOVED 2 /**< */
156/** @} */
157
158
159#pragma pack(1)
160
161/**
162 * Device operation: reqestq
163 */
164struct REQ_CMD_HDR
165{
166 uint8_t uLUN[8]; /**< lun */
167 uint64_t uId; /**< id */
168 uint8_t uTaskAttr; /**< task_attr */
169 uint8_t uPrio; /**< prio */
170 uint8_t uCrn; /**< crn */
171};
172
173struct REQ_CMD_PI
174{
175 uint32_t uPiBytesOut; /**< pi_bytesout */
176 uint32_t uPiBytesIn; /**< pi_bytesin */
177};
178
179struct REQ_RESP_HDR
180{
181 uint32_t cbSense; /**< sense_len */
182 uint32_t uResidual; /**< residual */
183 uint16_t uStatusQualifier; /**< status_qualifier */
184 uint8_t uStatus; /**< status SCSI status code */
185 uint8_t uResponse; /**< response */
186};
187
188typedef struct virtio_scsi_req_cmd
189{
190 /* Device-readable section */
191
192 struct REQ_CMD_HDR cmdHdr;
193 uint8_t uCdb[1]; /**< cdb */
194
195 struct REQ_CMD_PI piHdr; /** T10 Pi block integrity (optional feature) */
196 uint8_t uPiOut[1]; /**< pi_out[] T10 pi block integrity */
197 uint8_t uDataOut[1]; /**< dataout */
198
199 /** Device writable section */
200
201 struct REQ_RESP_HDR respHdr;
202 uint8_t uSense[1]; /**< sense */
203 uint8_t uPiIn[1]; /**< pi_in[] T10 Pi block integrity */
204 uint8_t uDataIn[1]; /**< detain; */
205
206} VIRTIOSCSI_REQ_CMD_T, *PVIRTIOSCSI_REQ_CMD_T;
207#pragma pack()
208
209/**
210 * @name VirtIO 1.0 SCSI Host Device Req command-specific response values
211 * @{ */
212#define VIRTIOSCSI_S_OK 0 /**< control, command */
213#define VIRTIOSCSI_S_OVERRUN 1 /**< control */
214#define VIRTIOSCSI_S_ABORTED 2 /**< control */
215#define VIRTIOSCSI_S_BAD_TARGET 3 /**< control, command */
216#define VIRTIOSCSI_S_RESET 4 /**< control */
217#define VIRTIOSCSI_S_BUSY 5 /**< control, command */
218#define VIRTIOSCSI_S_TRANSPORT_FAILURE 6 /**< control, command */
219#define VIRTIOSCSI_S_TARGET_FAILURE 7 /**< control, command */
220#define VIRTIOSCSI_S_NEXUS_FAILURE 8 /**< control, command */
221#define VIRTIOSCSI_S_FAILURE 9 /**< control, command */
222#define VIRTIOSCSI_S_INCORRECT_LUN 12 /**< command */
223/** @} */
224
225/**
226 * @name VirtIO 1.0 SCSI Host Device command-specific task_attr values
227 * @{ */
228#define VIRTIOSCSI_S_SIMPLE 0 /**< */
229#define VIRTIOSCSI_S_ORDERED 1 /**< */
230#define VIRTIOSCSI_S_HEAD 2 /**< */
231#define VIRTIOSCSI_S_ACA 3 /**< */
232/** @} */
233
234/**
235 * @name VirtIO 1.0 SCSI Host Device Control command before we know type (5.6.6.2)
236 * @{ */
237typedef struct virtio_scsi_ctrl
238{
239 uint32_t uType;
240} VIRTIOSCSI_CTRL, *PVIRTIOSCSI_CTRL_T;
241
242/**
243 * @name VirtIO 1.0 SCSI Host Device command-specific TMF values
244 * @{ */
245#define VIRTIOSCSI_T_TMF 0 /**< */
246#define VIRTIOSCSI_T_TMF_ABORT_TASK 0 /**< */
247#define VIRTIOSCSI_T_TMF_ABORT_TASK_SET 1 /**< */
248#define VIRTIOSCSI_T_TMF_CLEAR_ACA 2 /**< */
249#define VIRTIOSCSI_T_TMF_CLEAR_TASK_SET 3 /**< */
250#define VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET 4 /**< */
251#define VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET 5 /**< */
252#define VIRTIOSCSI_T_TMF_QUERY_TASK 6 /**< */
253#define VIRTIOSCSI_T_TMF_QUERY_TASK_SET 7 /**< */
254/*** @} */
255
256#pragma pack(1)
257typedef struct virtio_scsi_ctrl_tmf
258{
259 // Device-readable part
260 uint32_t uType; /** type */
261 uint32_t uSubtype; /** subtype */
262 uint8_t uLUN[8]; /** lun */
263 uint64_t uId; /** id */
264 // Device-writable part
265 uint8_t uResponse; /** response */
266} VIRTIOSCSI_CTRL_TMF_T, *PVIRTIOSCSI_CTRL_TMF_T;
267#pragma pack(0)
268
269/**
270 * @name VirtIO 1.0 SCSI Host Device device specific tmf control response values
271 * @{ */
272#define VIRTIOSCSI_S_FUNCTION_COMPLETE 0 /**< */
273#define VIRTIOSCSI_S_FUNCTION_SUCCEEDED 10 /**< */
274#define VIRTIOSCSI_S_FUNCTION_REJECTED 11 /**< */
275/** @} */
276
277#define VIRTIOSCSI_T_AN_QUERY 1 /** Asynchronous notification query */
278#define VIRTIOSCSI_T_AN_SUBSCRIBE 2 /** Asynchronous notification subscription */
279
280#pragma pack(1)
281typedef struct virtio_scsi_ctrl_an
282{
283 // Device-readable part
284 uint32_t uType; /** type */
285 uint8_t uLUN[8]; /** lun */
286 uint32_t uEventsRequested; /** event_requested */
287 // Device-writable part
288 uint32_t uEventActual; /** event_actual */
289 uint8_t uResponse; /** response */
290} VIRTIOSCSI_CTRL_AN, *PVIRTIOSCSI_CTRL_AN_T;
291#pragma pack()
292
293/**
294 * @name VirtIO 1.0 SCSI Host Device device specific tmf control response values
295 * @{ */
296#define VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE 2 /**< */
297#define VIRTIOSCSI_EVT_ASYNC_POWER_MGMT 4 /**< */
298#define VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST 8 /**< */
299#define VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE 16 /**< */
300#define VIRTIOSCSI_EVT_ASYNC_MULTI_HOST 32 /**< */
301#define VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY 64 /**< */
302/** @} */
303
304#define SUBSCRIBABLE_EVENTS \
305 VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE \
306 & VIRTIOSCSI_EVT_ASYNC_POWER_MGMT \
307 & VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST \
308 & VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE \
309 & VIRTIOSCSI_EVT_ASYNC_MULTI_HOST \
310 & VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY
311
312/**
313 * Worker thread context
314 */
315typedef struct WORKER
316{
317 R3PTRTYPE(PPDMTHREAD) pThread; /**< pointer to worker thread's handle */
318 SUPSEMEVENT hEvtProcess; /**< handle of associated sleep/wake-up semaphore */
319 bool fSleeping; /**< Flags whether worker thread is sleeping or not */
320 bool fNotified; /**< Flags whether worker thread notified */
321} WORKER, *PWORKER;
322
323/**
324 * State of a target attached to the VirtIO SCSI Host
325 */
326typedef struct VIRTIOSCSITARGET
327{
328 /** Pointer to PCI device that owns this target instance. - R3 pointer */
329 R3PTRTYPE(struct VIRTIOSCSI *) pVirtioScsiR3;
330
331 /** Pointer to attached driver's base interface. */
332 R3PTRTYPE(PPDMIBASE) pDrvBase;
333
334 /** Target LUN */
335 RTUINT iLUN;
336
337 /** Target LUN Description */
338 char * pszLunName;
339
340 /** Target base interface. */
341 PDMIBASE IBase;
342
343 /** Flag whether device is present. */
344 bool fPresent;
345
346 /** Media port interface. */
347 PDMIMEDIAPORT IMediaPort;
348
349 /** Pointer to the attached driver's media interface. */
350 R3PTRTYPE(PPDMIMEDIA) pDrvMedia;
351
352 /** Extended media port interface. */
353 PDMIMEDIAEXPORT IMediaExPort;
354
355 /** Pointer to the attached driver's extended media interface. */
356 R3PTRTYPE(PPDMIMEDIAEX) pDrvMediaEx;
357
358 /** Status LED interface */
359 PDMILEDPORTS ILed;
360
361 /** The status LED state for this device. */
362 PDMLED led;
363
364 /** Number of requests active */
365 volatile uint32_t cReqsInProgress;
366
367} VIRTIOSCSITARGET, *PVIRTIOSCSITARGET;
368
369/**
370 * PDM instance data (state) for VirtIO Host SCSI device
371 *
372 * @extends PDMPCIDEV
373 */
374typedef struct VIRTIOSCSI
375{
376 /** Opaque handle to VirtIO common framework (must be first item
377 * in this struct so PDMINS_2_DATA macro's casting works) */
378 VIRTIOHANDLE hVirtio;
379
380 /** SCSI target instances data */
381 VIRTIOSCSITARGET aTargetInstances[VIRTIOSCSI_MAX_TARGETS];
382
383 /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
384 WORKER aWorker[VIRTIOSCSI_QUEUE_CNT];
385
386 bool fBootable;
387 bool fRCEnabled;
388 bool fR0Enabled;
389 /** Instance name */
390 const char szInstance[16];
391
392 /** Device-specific spec-based VirtIO queuenames */
393 const char szQueueNames[VIRTIOSCSI_QUEUE_CNT][VIRTIO_MAX_QUEUE_NAME_SIZE];
394
395 /** Track which VirtIO queues we've attached to */
396 bool fQueueAttached[VIRTIOSCSI_QUEUE_CNT];
397
398 /** Device base interface. */
399 PDMIBASE IBase;
400
401 /** Pointer to the device instance. - R3 ptr. */
402 PPDMDEVINSR3 pDevInsR3;
403 /** Pointer to the device instance. - R0 ptr. */
404 PPDMDEVINSR0 pDevInsR0;
405 /** Pointer to the device instance. - RC ptr. */
406 PPDMDEVINSRC pDevInsRC;
407
408 /** Status LUN: LEDs port interface. */
409 PDMILEDPORTS ILeds;
410
411 /** Status LUN: Partner of ILeds. */
412 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
413
414 /** Base address of the memory mapping. */
415 RTGCPHYS GCPhysMMIOBase;
416
417 /** IMediaExPort: Media ejection notification */
418 R3PTRTYPE(PPDMIMEDIANOTIFY) pMediaNotify;
419
420 /** Queue to send tasks to R3. - HC ptr */
421 R3PTRTYPE(PPDMQUEUE) pNotifierQueueR3;
422
423 /** The support driver session handle. */
424 R3R0PTRTYPE(PSUPDRVSESSION) pSupDrvSession;
425
426 /** Mask of VirtIO Async Event types this device will deliver */
427 uint32_t uAsyncEvtsEnabled;
428
429 /** The event semaphore the processing thread waits on. */
430
431 /** Number of ports detected */
432 uint64_t cTargets;
433
434 /** True if PDMDevHlpAsyncNotificationCompleted should be called when port goes idle */
435 bool volatile fSignalIdle;
436
437 /** Events the guest has subscribed to get notifications of */
438 uint32_t uSubscribedEvents;
439
440 /** Set if events missed due to lack of bufs avail on eventq */
441 bool fEventsMissed;
442
443 /** VirtIO Host SCSI device runtime configuration parameters */
444 VIRTIOSCSI_CONFIG_T virtioScsiConfig;
445
446 /** True if the guest/driver and VirtIO framework are in the ready state */
447 bool fVirtioReady;
448
449 /** True if VIRTIO_SCSI_F_T10_PI was negotiated */
450 bool fHasT10pi;
451
452 /** True if VIRTIO_SCSI_F_T10_PI was negotiated */
453 bool fHasHotplug;
454
455 /** True if VIRTIO_SCSI_F_T10_PI was negotiated */
456 bool fHasInOutBufs;
457
458 /** True if VIRTIO_SCSI_F_T10_PI was negotiated */
459 bool fHasLunChange;
460
461} VIRTIOSCSI, *PVIRTIOSCSI;
462
463/**
464 * Request structure for IMediaEx (Associated Interfaces implemented by DrvSCSI)
465 */
466typedef struct VIRTIOSCSIREQ
467{
468 PDMMEDIAEXIOREQ hIoReq; /**< Handle of I/O request */
469 PVIRTIOSCSITARGET pTarget; /**< Target */
470 uint16_t qIdx; /**< Index of queue this request arrived on */
471 size_t cbPiOut; /**< Size of T10 pi in buffer */
472 uint8_t *pbPiOut; /**< Address of pi out buffer */
473 uint8_t *pbDataOut; /**< dataout */
474 size_t cbPiIn; /**< Size of T10 pi buffer */
475 uint8_t *pbPiIn; /**< Address of pi in buffer */
476 size_t cbDataIn; /**< Size of datain buffer */
477 uint8_t *pbDataIn; /**< datain */
478 size_t cbSense; /**< Size of sense buffer */
479 uint8_t *pbSense; /**< Pointer to R3 sense buffer */
480 uint8_t uStatus; /**< SCSI status code */
481 PRTSGBUF pInSgBuf; /**< Buf vector to return PDM result to VirtIO Guest */
482} VIRTIOSCSIREQ;
483
484#define PTARGET_FROM_LUN_BUF(lunBuf) &pThis->aTargetInstances[lunBuf[1]];
485
486#define SET_LUN_BUF(target, lun, out) \
487 out[0] = 0x01; out[1] = target; out[2] = (lun >> 8) & 0x40; out[3] = lun & 0xff; *((uint16_t *)out + 4) = 0;
488
489/**
490 * Do a hex dump of a buffer
491 * @param pv Pointer to array to dump
492 * @param cb Number of characters to dump
493 * @param uBase Base address of offset addresses displayed
494 * @param pszTitle Header line/title for the dump
495 *
496 */
497void virtioScsiHexDump(uint8_t *pv, size_t cb, uint32_t uBase, const char *pszTitle) {
498 if (pszTitle)
499 Log2(("%s [%d bytes]:\n", pszTitle, cb));
500 for (uint32_t i = 0; i < RT_MAX(1, (cb / 16)); i++)
501 {
502 uint32_t uAddr = i * 16 + uBase;
503 Log2(("%x%x%x%x: ", (uAddr >> 12) & 0xf, (uAddr >> 8) & 0xf, (uAddr >> 4) & 0xf, uAddr & 0xf));
504 for (int j = 0; j < 16; j++)
505 {
506 if (i * 16 + j >= cb)
507 Log2(("-- %s", (j + 1) % 8 ? "" : " "));
508 else
509 {
510 uint8_t u8 = pv[i * 16 + j];
511 Log2(("%x%x %s", u8 >> 4 & 0xf, u8 & 0xf, (j + 1) % 8 ? "" : " "));
512 }
513 }
514 for (int j = 0; j < 16; j++ ) {
515 if (i * 16 + j >= cb)
516 Log2((" "));
517 else
518 {
519 uint8_t u8 = pv[i * 16 + j];
520 Log2(("%c", u8 >= 0x20 && u8 <= 0x7e ? u8 : '.'));
521 }
522 }
523 Log2(("\n"));
524 }
525 Log2(("\n"));
526}
527DECLINLINE(const char *) virtioGetTMFTypeText(uint32_t uSubType)
528{
529 switch (uSubType)
530 {
531 case VIRTIOSCSI_T_TMF_ABORT_TASK: return "ABORT TASK";
532 case VIRTIOSCSI_T_TMF_ABORT_TASK_SET: return "ABORT TASK SET";
533 case VIRTIOSCSI_T_TMF_CLEAR_ACA: return "CLEAR ACA";
534 case VIRTIOSCSI_T_TMF_CLEAR_TASK_SET: return "CLEAR TASK SET";
535 case VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET: return "I T NEXUS RESET";
536 case VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET: return "LOGICAL UNIT RESET";
537 case VIRTIOSCSI_T_TMF_QUERY_TASK: return "QUERY TASK";
538 case VIRTIOSCSI_T_TMF_QUERY_TASK_SET: return "QUERY TASK SET";
539 default: return "<unknown>";
540 }
541}
542
543DECLINLINE(void) virtioGetControlAsyncMaskText(char *pszOutput, size_t cbOutput, uint32_t uAsyncTypesMask)
544{
545 RTStrPrintf(pszOutput, cbOutput, "%s%s%s%s%s%s",
546 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE) ? "CHANGE_OPERATION " : "",
547 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_POWER_MGMT) ? "POWER_MGMT " : "",
548 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST) ? "EXTERNAL_REQ " : "",
549 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE) ? "MEDIA_CHANGE " : "",
550 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_MULTI_HOST) ? "MULTI_HOST " : "",
551 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY) ? "DEVICE_BUSY " : "");
552}
553
554DECLINLINE(const char *) virtioGetReqRespText(uint32_t vboxRc)
555{
556 switch (vboxRc)
557 {
558 case VIRTIOSCSI_S_OK: return "OK";
559 case VIRTIOSCSI_S_OVERRUN: return "OVERRRUN";
560 case VIRTIOSCSI_S_BAD_TARGET: return "BAD TARGET";
561 case VIRTIOSCSI_S_BUSY: return "BUSY";
562 case VIRTIOSCSI_S_RESET: return "RESET";
563 case VIRTIOSCSI_S_ABORTED: return "ABORTED";
564 case VIRTIOSCSI_S_NEXUS_FAILURE: return "NEXUS FAILURE";
565 case VIRTIOSCSI_S_TRANSPORT_FAILURE: return "TRANSPORT FAILURE";
566 case VIRTIOSCSI_S_FAILURE: return "FAILURE";
567 default: return "<unknown>";
568 }
569}
570
571DECLINLINE(const char *) virtioGetCtrlRespText(uint32_t vboxRc)
572{
573 switch (vboxRc)
574 {
575 case VIRTIOSCSI_S_OK: return "OK/COMPLETE";
576 case VIRTIOSCSI_S_BAD_TARGET: return "BAD TARGET";
577 case VIRTIOSCSI_S_BUSY: return "BUSY";
578 case VIRTIOSCSI_S_NEXUS_FAILURE: return "NEXUS FAILURE";
579 case VIRTIOSCSI_S_TRANSPORT_FAILURE: return "TRANSPORT FAILURE";
580 case VIRTIOSCSI_S_FAILURE: return "FAILURE";
581 case VIRTIOSCSI_S_INCORRECT_LUN: return "INCORRECT LUN";
582 case VIRTIOSCSI_S_FUNCTION_SUCCEEDED: return "FUNCTION SUCCEEDED";
583 case VIRTIOSCSI_S_FUNCTION_REJECTED: return "FUNCTION REJECTED";
584 default: return "<unknown>";
585 }
586}
587
588DECLINLINE(const char *) virtioGetScsiStatusText(uint8_t uScsiStatusCode)
589{
590 switch (uScsiStatusCode)
591 {
592 case 0x00: return "GOOD";
593 case 0x02: return "CHECK CONDITION";
594 case 0x04: return "CONDITION MET";
595 case 0x08: return "BUSY";
596 case 0x10: return "INTERMEDIATE";
597 case 0x14: return "CONDITION MET";
598 case 0x18: return "RESERVATION CONFLICT";
599 case 0x22: return "COMMAND TERMINATED";
600 case 0x28: return "TASK SET FULL";
601 case 0x30: return "ACA ACTIVE";
602 case 0x40: return "TASK ABORTED";
603 default: return "<UNKNOWN CODE>";
604 }
605}
606
607uint8_t virtioScsiEstimateCdbLen(uint8_t uCmd, uint8_t cbMax)
608{
609 if (uCmd < 0x1f)
610 return 6;
611 else if (uCmd >= 0x20 && uCmd < 0x60)
612 return 10;
613 else if (uCmd >= 0x60 && uCmd < 0x80)
614 return cbMax;
615 else if (uCmd >= 0x80 && uCmd < 0xa0)
616 return 16;
617 else if (uCmd >= 0xa0 && uCmd < 0xC0)
618 return 12;
619 else
620 return cbMax;
621}
622
623/**
624 * This macro resolves to boolean true if uOffset matches a field offset and size exactly,
625 * (or if it is a 64-bit field, if it accesses either 32-bit part as a 32-bit access)
626 * ASSUMED this critereon is mandated by section 4.1.3.1 of the VirtIO 1.0 specification)
627 * This MACRO can be re-written to allow unaligned access to a field (within bounds).
628 *
629 * @param member - Member of VIRTIO_PCI_COMMON_CFG_T
630 * @result - true or false
631 */
632#define MATCH_SCSI_CONFIG(member) \
633 (RT_SIZEOFMEMB(VIRTIOSCSI_CONFIG_T, member) == 8 \
634 && ( uOffset == RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member) \
635 || uOffset == RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member) + sizeof(uint32_t)) \
636 && cb == sizeof(uint32_t)) \
637 || (uOffset == RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member) \
638 && cb == RT_SIZEOFMEMB(VIRTIOSCSI_CONFIG_T, member))
639
640#define LOG_ACCESSOR(member) \
641 virtioLogMappedIoValue(__FUNCTION__, #member, RT_SIZEOFMEMB(VIRTIOSCSI_CONFIG_T, member), \
642 pv, cb, uIntraOffset, fWrite, false, 0);
643
644#define SCSI_CONFIG_ACCESSOR(member) \
645 { \
646 uint32_t uIntraOffset = uOffset - RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member); \
647 if (fWrite) \
648 memcpy(((char *)&pThis->virtioScsiConfig.member) + uIntraOffset, (const char *)pv, cb); \
649 else \
650 memcpy((char *)pv, (const char *)(((char *)&pThis->virtioScsiConfig.member) + uIntraOffset), cb); \
651 LOG_ACCESSOR(member); \
652 }
653
654#define SCSI_CONFIG_ACCESSOR_READONLY(member) \
655 { \
656 uint32_t uIntraOffset = uOffset - RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member); \
657 if (fWrite) \
658 LogFunc(("Guest attempted to write readonly virtio_pci_common_cfg.%s\n", #member)); \
659 else \
660 { \
661 memcpy((char *)pv, (const char *)(((char *)&pThis->virtioScsiConfig.member) + uIntraOffset), cb); \
662 LOG_ACCESSOR(member); \
663 } \
664 }
665
666
667
668typedef struct VIRTIOSCSIREQ *PVIRTIOSCSIREQ;
669
670#ifdef BOOTABLE_SUPPORT_TBD
671/** @callback_method_impl{FNIOMIOPORTIN} */
672static DECLCALLBACK(int) virtioScsiR3BiosIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint8_t *pbDst,
673 uint32_t *pcTransfers, unsigned cb);
674{
675}
676/** @callback_method_impl{FNIOMIOPORTOUT} */
677static DECLCALLBACK(int) virtioScsiR3BiosIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb);
678{
679}
680/** @callback_method_impl{FNIOMIOPORTOUTSTRING} */
681static DECLCALLBACK(int) virtioScsiR3BiosIoPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, const uint8_t *pbSrc,
682 uint32_t *pcTransfers, unsigned cb);
683{
684}
685/** @callback_method_impl{FNIOMIOPORTINSTRING} */
686static DECLCALLBACK(int) virtioScsiR3BiosIoPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint8_t *pbDst,
687 uint32_t *pcTransfers, unsigned cb);
688{
689}
690#endif
691
692/**
693 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
694 */
695static DECLCALLBACK(void) virtioScsiR3IoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
696 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
697{
698
699 RT_NOREF4(pInterface, hIoReq, pvIoReqAlloc, enmState);
700 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
701
702 switch (enmState)
703 {
704 case PDMMEDIAEXIOREQSTATE_SUSPENDED:
705 {
706 /* Make sure the request is not accounted for so the VM can suspend successfully. */
707 uint32_t cTasksActive = ASMAtomicDecU32(&pTarget->cReqsInProgress);
708 if (!cTasksActive && pTarget->CTX_SUFF(pVirtioScsi)->fSignalIdle)
709 PDMDevHlpAsyncNotificationCompleted(pTarget->CTX_SUFF(pVirtioScsi)->pDevInsR3);
710 break;
711 }
712 case PDMMEDIAEXIOREQSTATE_ACTIVE:
713 /* Make sure the request is accounted for so the VM suspends only when the request is complete. */
714 ASMAtomicIncU32(&pTarget->cReqsInProgress);
715 break;
716 default:
717 AssertMsgFailed(("Invalid request state given %u\n", enmState));
718 }
719}
720
721/**
722 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
723 */
724static DECLCALLBACK(int) virtioScsiR3IoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
725 void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf,
726 size_t cbCopy)
727{
728 RT_NOREF(hIoReq);
729 RT_NOREF(pInterface);
730
731 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
732
733 /** DrvSCSI.cpp, that issues this callback, just sticks one segment in the buffer */
734// memset(pReq->pbDataIn + offDst, 0, cbCopy);
735LogFunc(("*** pSgBuf->cbSegLeft=%d\n", pSgBuf->cbSegLeft));
736 memcpy(pReq->pbDataIn + offDst, pSgBuf->paSegs[0].pvSeg, cbCopy);
737 return VINF_SUCCESS;
738}
739
740
741/**
742 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
743 */
744static DECLCALLBACK(int) virtioScsiR3IoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
745 void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf,
746 size_t cbCopy)
747{
748 RT_NOREF(hIoReq);
749 RT_NOREF(pInterface);
750
751 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
752 /** DrvSCSI.cpp, that issues this callback, just sticks one segment in the buffer */
753
754 memcpy(pSgBuf->paSegs[0].pvSeg, pReq->pbDataOut + offSrc, cbCopy);
755
756 return VINF_SUCCESS;
757}
758
759static int virtioScsiSendEvent(PVIRTIOSCSI pThis, uint16_t uTarget, uint32_t uEventType, uint32_t uReason)
760{
761 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[uTarget];
762
763 VIRTIOSCSI_EVENT_T event = { uEventType, { 0 }, uReason };
764 SET_LUN_BUF(pTarget->iLUN, 0, event.uLUN);
765
766 switch(uEventType)
767 {
768 case VIRTIOSCSI_T_NO_EVENT:
769 if (uEventType & VIRTIOSCSI_T_EVENTS_MISSED)
770 LogFunc(("LUN: %s Warning driver that events were missed\n", event.uLUN));
771 else
772 LogFunc(("LUN: %s Warning driver event info it queued is shorter than configured\n", event.uLUN));
773 break;
774 case VIRTIOSCSI_T_TRANSPORT_RESET:
775 switch(uReason)
776 {
777 case VIRTIOSCSI_EVT_RESET_REMOVED:
778 LogFunc(("LUN: %s Target or LUN removed\n", event.uLUN));
779 break;
780 case VIRTIOSCSI_EVT_RESET_RESCAN:
781 LogFunc(("LUN: %s Target or LUN added\n", event.uLUN));
782 break;
783 case VIRTIOSCSI_EVT_RESET_HARD:
784 LogFunc(("LUN: %s Target was reset\n", event.uLUN));
785 break;
786 }
787 break;
788 case VIRTIOSCSI_T_ASYNC_NOTIFY:
789 char szTypeText[128];
790 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), uReason);
791 LogFunc(("LUN: %s Delivering subscribed async notification %s\n", event.uLUN, szTypeText));
792 break;
793 case VIRTIOSCSI_T_PARAM_CHANGE:
794 LogFunc(("LUN: %s PARAM_CHANGE sense code: 0x%x sense qualifier: 0x%x\n",
795 event.uLUN, uReason & 0xff, (uReason >> 8) & 0xff));
796 break;
797 default:
798 LogFunc(("LUN: %s Unknown event type: %d, ignoring\n", event.uLUN, uEventType));
799 return VINF_SUCCESS;
800 }
801
802 if (virtioQueueIsEmpty(pThis->hVirtio, EVENTQ_IDX))
803 {
804 LogFunc(("eventq is empty, events missed!\n"));
805 ASMAtomicWriteBool(&pThis->fEventsMissed, true);
806 return VINF_SUCCESS;
807 }
808
809 int rc = virtioQueueGet(pThis->hVirtio, EVENTQ_IDX, true, NULL, NULL);
810 AssertRC(rc);
811
812 RTSGBUF reqSegBuf;
813 RTSGSEG aReqSegs[] = { { &event, sizeof(event) } };
814 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
815
816 rc = virtioQueuePut (pThis->hVirtio, EVENTQ_IDX, &reqSegBuf, true);
817 AssertRC(rc);
818
819 rc = virtioQueueSync(pThis->hVirtio, EVENTQ_IDX);
820 AssertRC(rc);
821
822 return VINF_SUCCESS;
823
824}
825
826static int virtioScsiReqFinish(PVIRTIOSCSI pThis, PVIRTIOSCSIREQ pReq, int rcReq)
827{
828 PVIRTIOSCSITARGET pTarget = pReq->pTarget;
829 PPDMIMEDIAEX pIMediaEx = pTarget->pDrvMediaEx;
830
831 ASMAtomicDecU32(&pTarget->cReqsInProgress);
832
833 size_t cbResidual = 0, cbXfer = 0;
834 int rc = pIMediaEx->pfnIoReqQueryResidual(pIMediaEx, pReq->hIoReq, &cbResidual);
835 AssertRC(rc);
836
837 rc = pIMediaEx->pfnIoReqQueryXferSize(pIMediaEx, pReq->hIoReq, &cbXfer);
838 AssertRC(rc);
839
840 /**
841 * Linux does not want any sense code if there wasn't problem!
842 */
843 if (pReq->pbSense[2] == SCSI_SENSE_NONE)
844 pReq->cbSense = 0;
845
846 struct REQ_RESP_HDR respHdr;
847 respHdr.cbSense = pReq->cbSense;
848 respHdr.uResidual = cbResidual;
849 respHdr.uStatus = pReq->uStatus;
850 respHdr.uResponse = rcReq;
851 respHdr.uStatusQualifier = 0;
852
853 LogFunc(("status: %s response: %s 0x%x%x/0x%x%x cbXfer=%d, cbResidual: %u\n",
854 virtioGetScsiStatusText(pReq->uStatus), virtioGetReqRespText(respHdr.uResponse),
855 pReq->uStatus >> 4 & 0xf, pReq->uStatus & 0xf,
856 respHdr.uResponse >> 4 & 0xf, respHdr.uResponse & 0xf,
857 cbXfer, cbResidual));
858
859 if (pReq->cbSense)
860 {
861 Log2Func(("Sense: %s\n", SCSISenseText(pReq->pbSense[2])));
862 Log2Func(("Sense Ext3: %s\n", SCSISenseExtText(pReq->pbSense[12], pReq->pbSense[13])));
863 virtioScsiHexDump(pReq->pbSense, pReq->cbSense, 0, "\nSense");
864 }
865
866 if (pReq->cbDataIn)
867 virtioScsiHexDump(pReq->pbDataIn, pReq->cbDataIn, 0, "\ndatain");
868
869 if (pReq->cbPiIn)
870 virtioScsiHexDump(pReq->pbPiIn, pReq->cbPiIn, 0, "\nPi in");
871
872 if (cbResidual)
873 Log(("Residual: %d\n", cbResidual));
874
875 Log(("---------------------------------------------------------------------------------\n"));
876 int cSegs = 0;
877 RTSGSEG aReqSegs[4];
878 aReqSegs[cSegs].pvSeg = &respHdr;
879 aReqSegs[cSegs++].cbSeg = sizeof(respHdr);
880 if (pReq->cbSense)
881 {
882 aReqSegs[cSegs].pvSeg = pReq->pbSense;
883 aReqSegs[cSegs++].cbSeg = pReq->cbSense;
884 }
885 if (pReq->cbPiIn)
886 {
887 aReqSegs[cSegs].pvSeg = pReq->pbPiIn;
888 aReqSegs[cSegs++].cbSeg = pReq->cbPiIn;
889 }
890 if (pReq->cbDataIn)
891 {
892 aReqSegs[cSegs].pvSeg = pReq->pbDataIn;
893 aReqSegs[cSegs++].cbSeg = pReq->cbDataIn;
894 }
895 RTSGBUF reqSegBuf;
896 RTSgBufInit(&reqSegBuf, aReqSegs, cSegs);
897
898 /**
899 * Fill in the request queue current descriptor chain's IN queue entry/entries
900 * (phys. memory) with the Req response data in virtual memory.
901 */
902 size_t cbReqSgBuf = RTSgBufCalcTotalLength(&reqSegBuf);
903 size_t cbInSgBuf = RTSgBufCalcTotalLength(pReq->pInSgBuf);
904 AssertMsgReturn(cbReqSgBuf <= cbInSgBuf,
905 ("Guest expected less req data (space needed: %d, avail: %d)\n", cbReqSgBuf, cbInSgBuf),
906 VERR_BUFFER_OVERFLOW);
907
908 /**
909 * Following doesn't put up memory barrier (fence).
910 * VirtIO 1.0 Spec requires mem. barrier for ctrl cmds
911 * but doesn't mention fences in regard to requests. */
912 virtioQueuePut(pThis->hVirtio, pReq->qIdx, &reqSegBuf, true /* fFence TBD */);
913 virtioQueueSync(pThis->hVirtio, pReq->qIdx);
914
915 RTMemFree(pReq->pbSense);
916 RTMemFree(pReq->pbDataIn);
917 RTMemFree(pReq->pbPiIn);
918
919 pIMediaEx->pfnIoReqFree(pIMediaEx, pReq->hIoReq);
920
921 if (pTarget->cReqsInProgress == 0 && pThis->fSignalIdle)
922 PDMDevHlpAsyncNotificationCompleted(pThis->pDevInsR3);
923
924 return VINF_SUCCESS;
925}
926
927/**
928 * This is called to complete a request immediately
929 *
930 * @param pThis - PDM driver instance state
931 * @param qIdx - Queue index
932 * @param respHdr - Response header
933 * @param pbSense - Pointer to sense buffer or NULL if none.
934 *
935 * @returns virtual box status code
936 */
937static int virtioScsiReqFinish(PVIRTIOSCSI pThis, uint16_t qIdx, struct REQ_RESP_HDR *respHdr, uint8_t *pbSense)
938{
939 int cSegs = 0;
940 RTSGSEG aReqSegs[2];
941 aReqSegs[cSegs].pvSeg = &respHdr;
942 aReqSegs[cSegs++].cbSeg = sizeof(respHdr);
943 if (respHdr->cbSense)
944 {
945 aReqSegs[cSegs].pvSeg = pbSense;
946 aReqSegs[cSegs++].cbSeg = respHdr->cbSense;
947 }
948 RTSGBUF reqSegBuf;
949 RTSgBufInit(&reqSegBuf, aReqSegs, cSegs);
950 virtioQueuePut(pThis->hVirtio, qIdx, &reqSegBuf, true /* fFence */);
951 virtioQueueSync(pThis->hVirtio, qIdx);
952 LogFunc(("Response code: %s\n", virtioGetReqRespText(respHdr->uResponse)));
953 Log(("---------------------------------------------------------------------------------\n"));
954 return VINF_SUCCESS;
955}
956
957static int virtioScsiReqSubmit(PVIRTIOSCSI pThis, uint16_t qIdx, PRTSGBUF pInSgBuf, PRTSGBUF pOutSgBuf)
958{
959 RT_NOREF(pInSgBuf);
960 RT_NOREF(qIdx);
961
962 AssertMsgReturn(pOutSgBuf->cSegs, ("Req. has no OUT data, unexpected/TBD\n"), VERR_NOT_IMPLEMENTED);
963
964 size_t cbOut = RTSgBufCalcTotalLength(pOutSgBuf);
965 size_t cbIn = RTSgBufCalcTotalLength(pInSgBuf);
966 size_t cbCdb = pThis->virtioScsiConfig.uCdbSize;
967 size_t cbSense = pThis->virtioScsiConfig.uSenseSize;
968 size_t cbCmdHdr = sizeof(REQ_CMD_HDR);
969 size_t cbRespHdr = sizeof(REQ_RESP_HDR);
970
971
972 AssertMsgReturn(cbOut >= RT_SIZEOFMEMB(VIRTIOSCSI_REQ_CMD_T, cmdHdr), ("Req to short"), VERR_BUFFER_UNDERFLOW);
973
974 /** Actual CDB bytes didn't fill negotiated space allocated for it, adjust size */
975 if (cbOut <= cbCmdHdr)
976 cbCdb -= (cbCmdHdr - cbOut);
977
978 PVIRTIOSCSI_REQ_CMD_T pVirtqReq = (PVIRTIOSCSI_REQ_CMD_T)RTMemAllocZ(cbOut);
979 off_t cbOff = 0;
980 size_t cbSeg = 0, cbLeft = cbOut;
981 while (cbLeft)
982 {
983 RTGCPHYS pvSeg = (RTGCPHYS)RTSgBufGetNextSegment(pOutSgBuf, &cbSeg);
984 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), pvSeg, pVirtqReq + cbOff, cbSeg);
985 cbLeft -= cbSeg;
986 cbOff += cbSeg;
987 }
988
989 uint8_t *pbCdb = pVirtqReq->uCdb;
990
991 uint8_t uTarget = pVirtqReq->cmdHdr.uLUN[1];
992 uint32_t uLUN = (pVirtqReq->cmdHdr.uLUN[2] << 8 | pVirtqReq->cmdHdr.uLUN[3]) & 0x3fff;
993
994 LogFunc(("[%s] (Target: %d LUN: %d) CDB: %.*Rhxs\n",
995 SCSICmdText(pbCdb[0]), uTarget, uLUN,
996 virtioScsiEstimateCdbLen(pbCdb[0], pThis->virtioScsiConfig.uCdbSize), pbCdb));
997
998 Log3Func((" id: %RX64, attr: %x, prio: %d, crn: %x\n",
999 pVirtqReq->cmdHdr.uId, pVirtqReq->cmdHdr.uTaskAttr, pVirtqReq->cmdHdr.uPrio, pVirtqReq->cmdHdr.uCrn));
1000
1001 off_t uPiOutOff = 0;
1002 size_t cbPiHdr = 0;
1003 size_t cbPiIn = 0;
1004 size_t cbPiOut = 0;
1005 uint8_t *pbPiOut = NULL;
1006
1007 if (pThis->fHasT10pi)
1008 {
1009 cbPiOut = pVirtqReq->piHdr.uPiBytesOut;
1010 cbPiIn = pVirtqReq->piHdr.uPiBytesIn;
1011 uPiOutOff = cbCmdHdr + cbCdb + cbPiHdr;
1012 pbPiOut = (uint8_t *)((uint64_t)pVirtqReq + uPiOutOff);
1013 cbPiHdr = sizeof(REQ_CMD_PI);
1014 }
1015
1016 off_t uDataOutOff = cbCmdHdr + cbCdb + cbPiHdr + cbPiOut;
1017
1018 /** Next line works because the OUT s/g buffer doesn't hold writable (e.g. IN) part(s) of req */
1019 size_t cbDataOut = cbOut - uDataOutOff;
1020
1021 uint8_t *pbDataOut = (uint8_t *)((uint64_t)pVirtqReq + uDataOutOff);
1022
1023 if (cbDataOut)
1024 virtioScsiHexDump(pVirtqReq->uDataOut, cbDataOut, 0, "\ndataout");
1025
1026
1027 if (cbPiOut)
1028 virtioScsiHexDump(pVirtqReq->uPiOut, cbPiOut, 0, "\nPi out");
1029
1030 PDMMEDIAEXIOREQ hIoReq = NULL;
1031 PVIRTIOSCSIREQ pReq;
1032 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[uTarget];
1033 PPDMIMEDIAEX pIMediaEx = pTarget->pDrvMediaEx;
1034
1035 size_t cbDataIn = cbIn - (cbRespHdr + cbSense + cbPiIn);
1036
1037 if (uTarget >= pThis->cTargets)
1038 {
1039 struct REQ_RESP_HDR respHdr;
1040 respHdr.cbSense = 0;
1041 respHdr.uResidual = cbDataOut + cbDataIn;
1042 respHdr.uStatus = SCSI_STATUS_OK;
1043 respHdr.uResponse = VIRTIOSCSI_S_BAD_TARGET;
1044 respHdr.uStatusQualifier = 0;
1045 virtioScsiReqFinish(pThis, qIdx, &respHdr, NULL /* pbSense */);
1046 return VINF_SUCCESS;
1047 }
1048
1049 struct REQ_RESP_HDR respHdr = { 0 };
1050 if (uLUN != 0)
1051 {
1052 /**
1053 * This is needed for the time being to respond with the right error condition and sense data
1054 * at the right time. It doesn't work to send requests to bad luns down to DrvSCSI. It returs
1055 * success for the INQUIRY commands and then starts failing future requests with BAD PARAM errors.
1056 * Unfortunately that's too late for Linux. If it doesn't get the bad lun error at inquiry it
1057 * seems to mishandle the error on future requests and will create 8 devices for the target and
1058 * it gets ugly. */
1059
1060 uint8_t pbSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED, 0, SCSI_SENSE_ILLEGAL_REQUEST,
1061 0, 0, 0, 0, 10, SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED, 0, 0 };
1062 respHdr.cbSense = sizeof(pbSense);
1063 respHdr.uResidual = cbDataIn + cbDataOut;
1064 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1065 respHdr.uResponse = VIRTIOSCSI_S_OK;
1066 respHdr.uStatusQualifier = 0;
1067 virtioScsiReqFinish(pThis, qIdx, &respHdr, pbSense);
1068 return VINF_SUCCESS;
1069 }
1070
1071 if (RT_LIKELY(pTarget->fPresent))
1072 {
1073 int rc = pIMediaEx->pfnIoReqAlloc(pIMediaEx, &hIoReq, (void **)&pReq, 0 /* uIoReqId */,
1074 PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR);
1075
1076 AssertMsgRCReturn(rc, ("Failed to allocate I/O request, rc=%Rrc\n", rc), rc);
1077
1078 ASMAtomicIncU32(&pTarget->cReqsInProgress);
1079
1080 pReq->hIoReq = hIoReq;
1081 pReq->pTarget = pTarget;
1082 pReq->qIdx = qIdx;
1083 pReq->cbPiOut = cbPiOut;
1084 pReq->pbPiOut = pbPiOut;
1085 pReq->pbDataOut = pbDataOut;
1086 pReq->cbPiIn = cbPiIn;
1087
1088 if (cbPiIn)
1089 {
1090 pReq->pbPiIn = (uint8_t *)RTMemAllocZ(cbPiIn);
1091 AssertMsgReturn(pReq->pbPiIn, ("Out of memory allocating pi_in buffer"), VERR_NO_MEMORY);
1092 }
1093
1094 if (cbDataIn)
1095 {
1096 pReq->cbDataIn = cbDataIn;
1097 pReq->pbDataIn = (uint8_t *)RTMemAllocZ(cbDataIn);
1098 AssertMsgReturn(pReq->pbDataIn, ("Out of memory allocating datain buffer"), VERR_NO_MEMORY);
1099 }
1100
1101 if (cbSense)
1102 {
1103 pReq->cbSense = cbSense;
1104 pReq->pbSense = (uint8_t *)RTMemAllocZ(cbSense);
1105 pReq->pInSgBuf = pInSgBuf;
1106 AssertMsgReturn(pReq->pbSense, ("Out of memory allocating sense buffer"), VERR_NO_MEMORY);
1107 }
1108
1109 Log3Func(("Submitting req on %s\n", uTarget, uLUN, QUEUENAME(qIdx)));
1110
1111 rc = pIMediaEx->pfnIoReqSendScsiCmd(pIMediaEx, pReq->hIoReq, uLUN, pbCdb, cbCdb,
1112 PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN, cbDataIn, pReq->pbSense, cbSense,
1113 &pReq->uStatus, 30 * RT_MS_1SEC);
1114
1115 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
1116 {
1117 /**
1118 * This rc value from DrvSCSI/send SCSI cmd means the request failed early
1119 * (no mem, buf copy callback, request buffer creation, or req enqueue),
1120 * and not submitted to lower layers, error it out.
1121 */
1122 LogRel(("Error submitting request!\n"));
1123 size_t cbResidual;
1124 pIMediaEx->pfnIoReqQueryResidual(pIMediaEx, pReq->hIoReq, &cbResidual);
1125 uint8_t uASC, uASCQ = 0;
1126 switch (rc)
1127 {
1128 case VERR_NO_MEMORY:
1129 uASC = SCSI_ASC_SYSTEM_RESOURCE_FAILURE;
1130 break;
1131 default:
1132 uASC = SCSI_ASC_INTERNAL_TARGET_FAILURE;
1133 break;
1134 }
1135 uint8_t pbSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED, 0, SCSI_SENSE_VENDOR_SPECIFIC,
1136 0, 0, 0, 0, 10, uASC, uASCQ, 0 };
1137 respHdr.cbSense = sizeof(pbSense);
1138 respHdr.uResidual = cbDataIn + cbDataOut;
1139 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1140 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
1141 respHdr.uStatusQualifier = 0;
1142 virtioScsiReqFinish(pThis, qIdx, &respHdr, NULL /* pbSense */);
1143 return VINF_SUCCESS;
1144 }
1145 } else {
1146 LogRel(("Error submitting request, target not present!!\n"));
1147 uint8_t pbSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED, 0, SCSI_SENSE_NOT_READY,
1148 0, 0, 0, 0, 10, 0, 0, 0 };
1149 respHdr.cbSense = sizeof(pbSense);
1150 respHdr.uResidual = cbDataIn + cbDataOut;
1151 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1152 respHdr.uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
1153 respHdr.uStatusQualifier = 0;
1154 virtioScsiReqFinish(pThis, qIdx, &respHdr, NULL /* pbSense */);
1155 return VINF_SUCCESS;
1156 }
1157
1158 return VINF_SUCCESS;
1159}
1160
1161/**
1162 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
1163 */
1164static DECLCALLBACK(int) virtioScsiR3IoReqCompleteNotify(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1165 void *pvIoReqAlloc, int rcReq)
1166{
1167 RT_NOREF(hIoReq);
1168 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
1169 virtioScsiReqFinish(pTarget->CTX_SUFF(pVirtioScsi), (PVIRTIOSCSIREQ)pvIoReqAlloc, rcReq);
1170 return VINF_SUCCESS;
1171}
1172
1173static int virtioScsiCtrl(PVIRTIOSCSI pThis, uint16_t qIdx, PRTSGBUF pInSgBuf, PRTSGBUF pOutSgBuf)
1174{
1175 RT_NOREF(pThis);
1176 RT_NOREF(qIdx);
1177 RT_NOREF(pInSgBuf);
1178
1179 /**
1180 * According to the VirtIO 1.0 SCSI Host device, spec, section 5.6.6.2, control packets are
1181 * extremely small, so more than one segment is highly unlikely but not a bug. Get the
1182 * the controlq sg buffer into virtual memory. */
1183
1184 size_t cbOut = RTSgBufCalcTotalLength(pOutSgBuf);
1185
1186 PVIRTIOSCSI_CTRL_T pScsiCtrl = (PVIRTIOSCSI_CTRL_T)RTMemAllocZ(cbOut);
1187 AssertMsgReturn(pScsiCtrl, ("Out of memory"), VERR_NO_MEMORY);
1188
1189 /**
1190 * Get control command into virtual memory
1191 */
1192 off_t cbOff = 0;
1193 size_t cbSeg = 0;
1194 while (cbOut)
1195 {
1196 RTGCPHYS pvSeg = (RTGCPHYS)RTSgBufGetNextSegment(pOutSgBuf, &cbSeg);
1197 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), pvSeg, pScsiCtrl + cbOff, cbSeg);
1198 cbOut -= cbSeg;
1199 cbOff += cbSeg;
1200 }
1201
1202 uint8_t uResponse = VIRTIOSCSI_S_OK;
1203
1204 /**
1205 * Mask of events to tell guest driver this device supports
1206 * See VirtIO 1.0 specification section 5.6.6.2
1207 */
1208 uint32_t uSubscribedEvents =
1209 VIRTIOSCSI_EVT_ASYNC_POWER_MGMT
1210 | VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST
1211 | VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE
1212 | VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY;
1213
1214 RTSGBUF reqSegBuf;
1215
1216 switch(pScsiCtrl->uType)
1217 {
1218 case VIRTIOSCSI_T_TMF: /* Task Management Functions */
1219 {
1220 PVIRTIOSCSI_CTRL_TMF_T pScsiCtrlTmf = (PVIRTIOSCSI_CTRL_TMF_T)pScsiCtrl;
1221 LogFunc(("%s, VirtIO LUN: %.8Rhxs\n%*sTask Mgt Function: %s (not yet implemented)\n",
1222 QUEUENAME(qIdx), pScsiCtrlTmf->uLUN,
1223 CBQUEUENAME(qIdx) + 18, "", virtioGetTMFTypeText(pScsiCtrlTmf->uSubtype)));
1224
1225 switch(pScsiCtrlTmf->uSubtype)
1226 {
1227 case VIRTIOSCSI_T_TMF_ABORT_TASK:
1228 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1229 break;
1230 case VIRTIOSCSI_T_TMF_ABORT_TASK_SET:
1231 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1232 break;
1233 case VIRTIOSCSI_T_TMF_CLEAR_ACA:
1234 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1235 break;
1236 case VIRTIOSCSI_T_TMF_CLEAR_TASK_SET:
1237 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1238 break;
1239 case VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET:
1240 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1241 break;
1242 case VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET:
1243 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1244 break;
1245 case VIRTIOSCSI_T_TMF_QUERY_TASK:
1246 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1247 break;
1248 case VIRTIOSCSI_T_TMF_QUERY_TASK_SET:
1249 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1250 break;
1251 default:
1252 LogFunc(("Unknown TMF type\n"));
1253 uResponse = VIRTIOSCSI_S_FAILURE;
1254 }
1255
1256 RTSGSEG aReqSegs[] = { { &uResponse, sizeof(uResponse) } };
1257 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1258
1259 break;
1260 }
1261 case VIRTIOSCSI_T_AN_QUERY: /** Guest SCSI driver is querying supported async event notifications */
1262 {
1263 PVIRTIOSCSI_CTRL_AN_T pScsiCtrlAnQuery = (PVIRTIOSCSI_CTRL_AN_T)pScsiCtrl;
1264
1265 char szTypeText[128];
1266 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), pScsiCtrlAnQuery->uEventsRequested);
1267
1268 Log3Func(("%s, VirtIO LUN: %.8Rhxs\n%*sAsync Query, types: %s\n",
1269 QUEUENAME(qIdx), pScsiCtrlAnQuery->uLUN, CBQUEUENAME(qIdx) + 30, "", szTypeText));
1270
1271 uSubscribedEvents &= pScsiCtrlAnQuery->uEventsRequested;
1272 uResponse = VIRTIOSCSI_S_FUNCTION_COMPLETE;
1273
1274 RTSGSEG aReqSegs[] = { { &uSubscribedEvents, sizeof(uSubscribedEvents) }, { &uResponse, sizeof(uResponse) } };
1275 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1276
1277 break;
1278 }
1279 case VIRTIOSCSI_T_AN_SUBSCRIBE: /** Guest SCSI driver is subscribing to async event notification(s) */
1280 {
1281 PVIRTIOSCSI_CTRL_AN_T pScsiCtrlAnSubscribe = (PVIRTIOSCSI_CTRL_AN_T)pScsiCtrl;
1282
1283 if (pScsiCtrlAnSubscribe->uEventsRequested & ~SUBSCRIBABLE_EVENTS)
1284 LogFunc(("Unsupported bits in event subscription event mask: 0x%x\n", pScsiCtrlAnSubscribe->uEventsRequested));
1285
1286 char szTypeText[128];
1287 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), pScsiCtrlAnSubscribe->uEventsRequested);
1288
1289 Log3Func(("%s, VirtIO LUN: %.8Rhxs\n%*sAsync Subscribe, types: %s\n",
1290 QUEUENAME(qIdx), pScsiCtrlAnSubscribe->uLUN, CBQUEUENAME(qIdx) + 30, "", szTypeText));
1291
1292 uSubscribedEvents &= pScsiCtrlAnSubscribe->uEventsRequested;
1293 pThis->uAsyncEvtsEnabled = uSubscribedEvents;
1294
1295 /**
1296 * TBD: Verify correct status code if request mask is only partially fulfillable
1297 * and confirm when to use 'complete' vs. 'succeeded' See VirtIO 1.0 spec section 5.6.6.2 and read SAM docs*/
1298 if (uSubscribedEvents == pScsiCtrlAnSubscribe->uEventsRequested)
1299 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1300 else
1301 uResponse = VIRTIOSCSI_S_FUNCTION_COMPLETE;
1302
1303 RTSGSEG aReqSegs[] = { { &uSubscribedEvents, sizeof(uSubscribedEvents) }, { &uResponse, sizeof(uResponse) } };
1304 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1305
1306 break;
1307 }
1308 default:
1309 LogFunc(("Unknown control type extracted from %s: %d\n", QUEUENAME(qIdx), pScsiCtrl->uType));
1310
1311 uResponse = VIRTIOSCSI_S_FAILURE;
1312
1313 RTSGSEG aReqSegs[] = { { &uResponse, sizeof(uResponse) } };
1314 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1315 }
1316
1317 LogFunc(("Response code: %s\n", virtioGetCtrlRespText(uResponse)));
1318 virtioQueuePut (pThis->hVirtio, qIdx, &reqSegBuf, true);
1319 virtioQueueSync(pThis->hVirtio, qIdx);
1320
1321 return VINF_SUCCESS;
1322}
1323
1324/*
1325 * Unblock the worker thread so it can respond to a state change.
1326 *
1327 * @returns VBox status code.
1328 * @param pDevIns The pcnet device instance.
1329 * @param pThread The send thread.
1330 */
1331static DECLCALLBACK(int) virtioScsiR3WorkerWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1332{
1333 RT_NOREF(pThread);
1334 uint16_t qIdx = ((uint64_t)pThread->pvUser) & 0xffff;
1335 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1336 return SUPSemEventSignal(pThis->pSupDrvSession, pThis->aWorker[qIdx].hEvtProcess);
1337}
1338
1339static int virtioScsiWorker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1340{
1341
1342 int rc;
1343 uint16_t qIdx = ((uint64_t)pThread->pvUser) & 0xffff;
1344 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1345 PWORKER pWorker = &pThis->aWorker[qIdx];
1346 PRTSGBUF pInSgBuf;
1347 PRTSGBUF pOutSgBuf;
1348
1349 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
1350 return VINF_SUCCESS;
1351
1352 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
1353 {
1354 if (virtioQueueIsEmpty(pThis->hVirtio, qIdx))
1355 {
1356 /** Interlocks avoid missing alarm while going to sleep & notifier doesn't wake the awoken */
1357 ASMAtomicWriteBool(&pWorker->fSleeping, true);
1358 bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
1359 if (!fNotificationSent)
1360 {
1361 Log3Func(("%s worker sleeping...\n", QUEUENAME(qIdx)));
1362 Assert(ASMAtomicReadBool(&pWorker->fSleeping));
1363 rc = SUPSemEventWaitNoResume(pThis->pSupDrvSession, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
1364 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
1365 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
1366 break;
1367 Log3Func(("%s worker woken\n", QUEUENAME(qIdx)));
1368 ASMAtomicWriteBool(&pWorker->fNotified, false);
1369 }
1370 ASMAtomicWriteBool(&pWorker->fSleeping, false);
1371 }
1372 Log3Func(("fetching next descriptor chain from %s\n", QUEUENAME(qIdx)));
1373 rc = virtioQueueGet(pThis->hVirtio, qIdx, true, &pInSgBuf, &pOutSgBuf);
1374 if (rc == VERR_NOT_AVAILABLE)
1375 {
1376 Log3Func(("Nothing found in %s\n", QUEUENAME(qIdx)));
1377 continue;
1378 }
1379
1380 AssertRC(rc);
1381 if (qIdx == CONTROLQ_IDX)
1382 virtioScsiCtrl(pThis, qIdx, pInSgBuf, pOutSgBuf);
1383 else
1384 {
1385 rc = virtioScsiReqSubmit(pThis, qIdx, pInSgBuf, pOutSgBuf);
1386 if (RT_FAILURE(rc))
1387 {
1388 LogRel(("Fatal error submitting req packet, resetting %Rrc", rc));
1389 /** TBD: MUST AT LEAD RETURN VIRTIO_SCSI_S_FAILURE for all pending I/O, Aborting is an option! */
1390 virtioResetAll(pThis->hVirtio);
1391 }
1392 }
1393 }
1394 return VINF_SUCCESS;
1395}
1396
1397
1398/*static void virtioScsiEventToClient(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1399{ } */
1400
1401
1402/**
1403 * Implementation invokes this to reset the VirtIO device
1404 */
1405static void virtioScsiDeviceReset(PVIRTIOSCSI pThis)
1406{
1407 pThis->virtioScsiConfig.uSenseSize = VIRTIOSCSI_SENSE_SIZE_DEFAULT;
1408 pThis->virtioScsiConfig.uCdbSize = VIRTIOSCSI_CDB_SIZE_DEFAULT;
1409 virtioResetAll(pThis->hVirtio);
1410}
1411
1412static int virtioScsiR3CfgAccessed(PVIRTIOSCSI pThis, uint32_t uOffset,
1413 const void *pv, size_t cb, uint8_t fWrite)
1414{
1415 int rc = VINF_SUCCESS;
1416 if (MATCH_SCSI_CONFIG(uNumQueues))
1417 {
1418 SCSI_CONFIG_ACCESSOR_READONLY(uNumQueues);
1419 }
1420 else
1421 if (MATCH_SCSI_CONFIG(uSegMax))
1422 {
1423 SCSI_CONFIG_ACCESSOR_READONLY(uSegMax);
1424 }
1425 else
1426 if (MATCH_SCSI_CONFIG(uMaxSectors))
1427 {
1428 SCSI_CONFIG_ACCESSOR_READONLY(uMaxSectors);
1429 }
1430 else
1431 if (MATCH_SCSI_CONFIG(uCmdPerLun))
1432 {
1433 SCSI_CONFIG_ACCESSOR_READONLY(uCmdPerLun);
1434 }
1435 else
1436 if (MATCH_SCSI_CONFIG(uEventInfoSize))
1437 {
1438 SCSI_CONFIG_ACCESSOR_READONLY(uEventInfoSize);
1439 }
1440 else
1441 if (MATCH_SCSI_CONFIG(uSenseSize))
1442 {
1443 SCSI_CONFIG_ACCESSOR(uSenseSize);
1444 }
1445 else
1446 if (MATCH_SCSI_CONFIG(uCdbSize))
1447 {
1448 SCSI_CONFIG_ACCESSOR(uCdbSize);
1449 }
1450 else
1451 if (MATCH_SCSI_CONFIG(uMaxChannel))
1452 {
1453 SCSI_CONFIG_ACCESSOR_READONLY(uMaxChannel);
1454 }
1455 else
1456 if (MATCH_SCSI_CONFIG(uMaxTarget))
1457 {
1458 SCSI_CONFIG_ACCESSOR_READONLY(uMaxTarget);
1459 }
1460 else
1461 if (MATCH_SCSI_CONFIG(uMaxLun))
1462 {
1463 SCSI_CONFIG_ACCESSOR_READONLY(uMaxLun);
1464 }
1465 else
1466 {
1467 LogFunc(("Bad access by guest to virtio_scsi_config: uoff=%d, cb=%d\n", uOffset, cb));
1468 rc = VERR_ACCESS_DENIED;
1469 }
1470 return rc;
1471}
1472
1473
1474
1475/**
1476 * virtio-scsi VirtIO Device-specific capabilities read callback
1477 * (other VirtIO capabilities and features are handled in VirtIO implementation)
1478 *
1479 * @param pDevIns The device instance.
1480 * @param uOffset Offset within device specific capabilities struct
1481 * @param pv Buffer in which to save read data
1482 * @param cb Number of bytes to read
1483 */
1484static DECLCALLBACK(int) virtioScsiR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, size_t cb)
1485{
1486 int rc = VINF_SUCCESS;
1487 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1488
1489 rc = virtioScsiR3CfgAccessed(pThis, uOffset, pv, cb, false);
1490
1491 return rc;
1492}
1493
1494/**
1495 * virtio-scsi VirtIO Device-specific capabilities read callback
1496 * (other VirtIO capabilities and features are handled in VirtIO implementation)
1497 *
1498 * @param pDevIns The device instance.
1499 * @param uOffset Offset within device specific capabilities struct
1500 * @param pv Buffer in which to save read data
1501 * @param cb Number of bytes to write
1502 */
1503static DECLCALLBACK(int) virtioScsiR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, size_t cb)
1504{
1505 int rc = VINF_SUCCESS;
1506 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1507
1508 rc = virtioScsiR3CfgAccessed(pThis, uOffset, pv, cb, true);
1509
1510 return rc;
1511}
1512
1513DECLINLINE(void) virtioScsiReportEventsMissed(PVIRTIOSCSI pThis, uint16_t uTarget)
1514{
1515 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_NO_EVENT | VIRTIOSCSI_T_EVENTS_MISSED, 0);
1516}
1517
1518DECLINLINE(void) virtioScsiReportTargetRemoved(PVIRTIOSCSI pThis, uint16_t uTarget)
1519{
1520 if (pThis->fHasHotplug)
1521 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET,
1522 VIRTIOSCSI_EVT_RESET_REMOVED);
1523}
1524
1525DECLINLINE(void) virtioScsiReportTargetAdded(PVIRTIOSCSI pThis, uint16_t uTarget)
1526{
1527 if (pThis->fHasHotplug)
1528 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET,
1529 VIRTIOSCSI_EVT_RESET_RESCAN);
1530}
1531
1532DECLINLINE(void) virtioScsiReportTargetReset(PVIRTIOSCSI pThis, uint16_t uTarget)
1533{
1534 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET,
1535 VIRTIOSCSI_EVT_RESET_HARD);
1536}
1537
1538DECLINLINE(void) virtioScsiReportOperChange(PVIRTIOSCSI pThis, uint16_t uTarget)
1539{
1540 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE)
1541 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1542 VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE);
1543}
1544
1545DECLINLINE(void) virtioScsiReportPowerMsg(PVIRTIOSCSI pThis, uint16_t uTarget)
1546{
1547 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_POWER_MGMT)
1548 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1549 VIRTIOSCSI_EVT_ASYNC_POWER_MGMT);
1550}
1551
1552DECLINLINE(void) virtioScsiReportExtReq(PVIRTIOSCSI pThis, uint16_t uTarget)
1553{
1554 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST)
1555 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1556 VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST);
1557}
1558
1559DECLINLINE(void) virtioScsiReportMediaChange(PVIRTIOSCSI pThis, uint16_t uTarget)
1560{
1561 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE)
1562 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1563 VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE);
1564}
1565
1566DECLINLINE(void) virtioScsiReportMultiHost(PVIRTIOSCSI pThis, uint16_t uTarget)
1567{
1568 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_MULTI_HOST)
1569 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1570 VIRTIOSCSI_EVT_ASYNC_MULTI_HOST);
1571}
1572
1573DECLINLINE(void) virtioScsiReportDeviceBusy(PVIRTIOSCSI pThis, uint16_t uTarget)
1574{
1575 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY)
1576 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1577 VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY);
1578}
1579
1580
1581DECLINLINE(void) virtioScsiReportParamChange(PVIRTIOSCSI pThis, uint16_t uTarget, uint32_t uSenseCode, uint32_t uSenseQualifier)
1582{
1583 uint32_t uReason = uSenseQualifier << 8 | uSenseCode;
1584 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_PARAM_CHANGE, uReason);
1585
1586}
1587
1588static DECLCALLBACK(void) virtioScsiNotified(VIRTIOHANDLE hVirtio, void *pClient, uint16_t qIdx)
1589{
1590 RT_NOREF(hVirtio);
1591
1592 AssertReturnVoid(qIdx < VIRTIOSCSI_QUEUE_CNT);
1593 PVIRTIOSCSI pThis = (PVIRTIOSCSI)pClient;
1594 PWORKER pWorker = &pThis->aWorker[qIdx];
1595
1596
1597 RTLogFlush(RTLogDefaultInstanceEx(RT_MAKE_U32(0, UINT16_MAX)));
1598
1599 if (qIdx == CONTROLQ_IDX || IS_REQ_QUEUE(qIdx))
1600 {
1601 Log3Func(("%s has available data\n", QUEUENAME(qIdx)));
1602 /** Wake queue's worker thread up if sleeping */
1603 if (!ASMAtomicXchgBool(&pWorker->fNotified, true))
1604 {
1605 if (ASMAtomicReadBool(&pWorker->fSleeping))
1606 {
1607 Log3Func(("waking %s worker.\n", QUEUENAME(qIdx)));
1608 int rc = SUPSemEventSignal(pThis->pSupDrvSession, pWorker->hEvtProcess);
1609 AssertRC(rc);
1610 }
1611 }
1612 }
1613 else if (qIdx == EVENTQ_IDX)
1614 {
1615 Log3Func(("Driver queued buffer(s) to %s\n"));
1616 if (ASMAtomicXchgBool(&pThis->fEventsMissed, false))
1617 virtioScsiReportEventsMissed(pThis, 0);
1618 }
1619 else
1620 LogFunc(("Unexpected queue idx (ignoring): %d\n", qIdx));
1621}
1622
1623static DECLCALLBACK(void) virtioScsiStatusChanged(VIRTIOHANDLE hVirtio, void *pClient, bool fVirtioReady)
1624{
1625 RT_NOREF(hVirtio);
1626 PVIRTIOSCSI pThis = (PVIRTIOSCSI)pClient;
1627 pThis->fVirtioReady = fVirtioReady;
1628 if (fVirtioReady)
1629 {
1630 LogFunc(("VirtIO ready\n---------------------------------------------------------------------------------\n"));
1631 uint64_t features = virtioGetNegotiatedFeatures(hVirtio);
1632 pThis->fHasT10pi = features & VIRTIO_SCSI_F_T10_PI;
1633 pThis->fHasHotplug = features & VIRTIO_SCSI_F_HOTPLUG;
1634 pThis->fHasInOutBufs = features & VIRTIO_SCSI_F_INOUT;
1635 pThis->fHasLunChange = features & VIRTIO_SCSI_F_CHANGE;
1636 }
1637 else
1638 {
1639 LogFunc(("VirtIO is resetting\n"));
1640 for (int i = 0; i < VIRTIOSCSI_QUEUE_CNT; i++)
1641 pThis->fQueueAttached[i] = false;
1642 }
1643}
1644
1645/**
1646 * Turns on/off the write status LED.
1647 *
1648 * @param pTarget Pointer to the target device
1649 * @param fOn New LED state.
1650 */
1651void virtioScsiSetWriteLed(PVIRTIOSCSITARGET pTarget, bool fOn)
1652{
1653 LogFlow(("%s virtioSetWriteLed: %s\n", pTarget->pszLunName, fOn ? "on" : "off"));
1654 if (fOn)
1655 pTarget->led.Asserted.s.fWriting = pTarget->led.Actual.s.fWriting = 1;
1656 else
1657 pTarget->led.Actual.s.fWriting = fOn;
1658}
1659
1660/**
1661 * Turns on/off the read status LED.
1662 *
1663 * @param pTarget Pointer to the device state structure.
1664 * @param fOn New LED state.
1665 */
1666void virtioScsiSetReadLed(PVIRTIOSCSITARGET pTarget, bool fOn)
1667{
1668 LogFlow(("%s virtioSetReadLed: %s\n", pTarget->pszLunName, fOn ? "on" : "off"));
1669 if (fOn)
1670 pTarget->led.Asserted.s.fReading = pTarget->led.Actual.s.fReading = 1;
1671 else
1672 pTarget->led.Actual.s.fReading = fOn;
1673}
1674
1675/**
1676 * virtio-scsi debugger info callback.
1677 *
1678 * @param pDevIns The device instance.
1679 * @param pHlp The output helpers.
1680 * @param pszArgs The arguments.
1681 */
1682static DECLCALLBACK(void) virtioScsiR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1683{
1684 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1685 bool fVerbose = false;
1686
1687 /* Parse arguments. */
1688 if (pszArgs)
1689 fVerbose = strstr(pszArgs, "verbose") != NULL;
1690
1691 /* Show basic information. */
1692 pHlp->pfnPrintf(pHlp, "%s#%d: virtio-scsci ",
1693 pDevIns->pReg->szName,
1694 pDevIns->iInstance);
1695 pHlp->pfnPrintf(pHlp, "numTargets=%lu", pThis->cTargets);
1696}
1697
1698/**
1699 * @interface_method_impl{PDMIMEDIAEXPORT,pfnMediumEjected}
1700 */
1701static DECLCALLBACK(void) virtioScsiR3MediumEjected(PPDMIMEDIAEXPORT pInterface)
1702{
1703 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
1704 PVIRTIOSCSI pThis = pTarget->CTX_SUFF(pVirtioScsi);
1705 LogFunc(("LUN %d Ejected!\n", pTarget->iLUN));
1706 if (pThis->pMediaNotify)
1707 virtioScsiSetWriteLed(pTarget, false);
1708}
1709
1710/** @callback_method_impl{FNSSMDEVLIVEEXEC} */
1711static DECLCALLBACK(int) virtioScsiR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1712{
1713 LogFunc(("callback"));
1714 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1715 RT_NOREF(pThis);
1716 RT_NOREF(uPass);
1717 RT_NOREF(pSSM);
1718 return VINF_SSM_DONT_CALL_AGAIN;
1719}
1720
1721/** @callback_method_impl{FNSSMDEVLOADEXEC} */
1722static DECLCALLBACK(int) virtioScsiR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1723{
1724 LogFunc(("callback"));
1725 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1726 RT_NOREF(pThis);
1727 RT_NOREF(uPass);
1728 RT_NOREF(pSSM);
1729 RT_NOREF(uVersion);
1730 return VINF_SSM_DONT_CALL_AGAIN;
1731}
1732
1733/** @callback_method_impl{FNSSMDEVSAVEEXEC} */
1734static DECLCALLBACK(int) virtioScsiR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1735{
1736 LogFunc(("callback"));
1737 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1738
1739 RT_NOREF(pThis);
1740 RT_NOREF(pSSM);
1741 return VINF_SUCCESS;
1742}
1743
1744/** @callback_method_impl{FNSSMDEVLOADDONE} */
1745static DECLCALLBACK(int) virtioScsiR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1746{
1747 LogFunc(("callback"));
1748 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1749 RT_NOREF(pThis);
1750 RT_NOREF(pSSM);
1751 return VINF_SUCCESS;
1752}
1753
1754
1755/**
1756 * Callback employed by virtioScsiR3PDMReset.
1757 *
1758 * @returns true if we've quiesced, false if we're still working.
1759 * @param pDevIns The device instance.
1760 */
1761static DECLCALLBACK(bool) virtioScsiR3IsAsyncResetDone(PPDMDEVINS pDevIns)
1762{
1763 RT_NOREF(pDevIns);
1764 Log(("\n"));
1765
1766 return true;
1767}
1768
1769/**
1770 * @copydoc FNPDMDEVRESET
1771 */
1772static DECLCALLBACK(void) virtioScsiR3PDMReset(PPDMDEVINS pDevIns)
1773{
1774 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1775 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
1776
1777 bool fIoInProgress = false;
1778 for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aTargetInstances); i++)
1779 {
1780 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[i];
1781 if (pTarget->cReqsInProgress != 0)
1782 {
1783 fIoInProgress = true;
1784 break;
1785 }
1786 }
1787 if (!fIoInProgress)
1788 PDMDevHlpSetAsyncNotification(pDevIns, virtioScsiR3IsAsyncResetDone);
1789 else
1790 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
1791
1792 virtioScsiDeviceReset(pThis);
1793}
1794
1795/**
1796 * Device relocation callback.
1797 *
1798 * When this callback is called the device instance data, and if the
1799 * device have a GC component, is being relocated, or/and the selectors
1800 * have been changed. The device must use the chance to perform the
1801 * necessary pointer relocations and data updates.
1802 *
1803 * Before the GC code is executed the first time, this function will be
1804 * called with a 0 delta so GC pointer calculations can be one in one place.
1805 *
1806 * @param pDevIns Pointer to the device instance.
1807 * @param offDelta The relocation delta relative to the old location.
1808 *
1809 * @remark A relocation CANNOT fail.
1810 */
1811static DECLCALLBACK(void) virtioScsiR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1812{
1813 LogFunc(("Relocating virtio-scsi"));
1814 RT_NOREF(offDelta);
1815 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1816
1817 pThis->pDevInsR3 = pDevIns;
1818
1819 for (uint32_t i = 0; i < VIRTIOSCSI_MAX_TARGETS; i++)
1820 {
1821 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[i];
1822 pTarget->pVirtioScsiR3 = pThis;;
1823 }
1824
1825 /**
1826 * Important: Forward to virtio framework!
1827 */
1828 virtioRelocate(pDevIns, offDelta);
1829
1830}
1831
1832static DECLCALLBACK(int) virtioScsiR3QueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
1833 uint32_t *piInstance, uint32_t *piLUN)
1834{
1835 PVIRTIOSCSITARGET pThisTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaPort);
1836 PPDMDEVINS pDevIns = pThisTarget->CTX_SUFF(pVirtioScsi)->CTX_SUFF(pDevIns);
1837
1838 AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
1839 AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
1840 AssertPtrReturn(piLUN, VERR_INVALID_POINTER);
1841
1842 *ppcszController = pDevIns->pReg->szName;
1843 *piInstance = pDevIns->iInstance;
1844 *piLUN = pThisTarget->iLUN;
1845
1846 return VINF_SUCCESS;
1847}
1848
1849/**
1850 * Gets the pointer to the status LED of a unit.
1851 *
1852 * @returns VBox status code.
1853 * @param pInterface Pointer to the interface structure containing the called function pointer.
1854 * @param iLUN The unit which status LED we desire.
1855 * @param ppLed Where to store the LED pointer.
1856 */
1857static DECLCALLBACK(int) virtioScsiR3TargetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
1858{
1859 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, ILed);
1860 if (iLUN == 0)
1861 {
1862 *ppLed = &pTarget->led;
1863 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
1864 return VINF_SUCCESS;
1865 }
1866 return VERR_PDM_LUN_NOT_FOUND;
1867 }
1868
1869
1870/**
1871 * Gets the pointer to the status LED of a unit.
1872 *
1873 * @returns VBox status code.
1874 * @param pInterface Pointer to the interface structure containing the called function pointer.
1875 * @param iLUN The unit which status LED we desire.
1876 * @param ppLed Where to store the LED pointer.
1877 */
1878static DECLCALLBACK(int) virtioScsiR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
1879{
1880 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, ILeds);
1881 if (iLUN < pThis->cTargets)
1882 {
1883 *ppLed = &pThis->aTargetInstances[iLUN].led;
1884 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
1885 return VINF_SUCCESS;
1886 }
1887 return VERR_PDM_LUN_NOT_FOUND;
1888}
1889
1890
1891/**
1892 * Memory mapped I/O Handler for read operations.
1893 *
1894 * @returns VBox status code.
1895 *
1896 * @param pDevIns The device instance.
1897 * @param pvUser User argument.
1898 * @param GCPhysAddr Physical address (in GC) where the read starts.
1899 * @param pv Where to store the result.
1900 * @param cb Number of bytes read.
1901 */
1902PDMBOTHCBDECL(int) virtioScsiMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
1903{
1904 RT_NOREF_PV(pDevIns); RT_NOREF_PV(pvUser); RT_NOREF_PV(GCPhysAddr); RT_NOREF_PV(pv); RT_NOREF_PV(cb);
1905 LogFunc(("Read from MMIO area\n"));
1906 return VINF_SUCCESS;
1907}
1908
1909/**
1910 * Memory mapped I/O Handler for write operations.
1911 *
1912 * @returns VBox status code.
1913 *
1914 * @param pDevIns The device instance.
1915 * @param pvUser User argument.
1916 * @param GCPhysAddr Physical address (in GC) where the read starts.
1917 * @param pv Where to fetch the result.
1918 * @param cb Number of bytes to write.
1919 */
1920PDMBOTHCBDECL(int) virtioScsiMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
1921{
1922 RT_NOREF_PV(pDevIns); RT_NOREF_PV(pvUser); RT_NOREF_PV(GCPhysAddr); RT_NOREF_PV(pv); RT_NOREF_PV(cb);
1923 LogFunc(("Write to MMIO area\n"));
1924 return VINF_SUCCESS;
1925}
1926
1927/**
1928 * @callback_method_impl{FNPCIIOREGIONMAP}
1929 */
1930static DECLCALLBACK(int) virtioScsiR3Map(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
1931 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
1932{
1933 RT_NOREF(pPciDev, iRegion);
1934 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1935 int rc = VINF_SUCCESS;
1936
1937 Assert(cb >= 32);
1938
1939 switch (iRegion)
1940 {
1941 case 0:
1942 LogFunc(("virtio-scsi MMIO mapped at GCPhysAddr=%RGp cb=%RGp\n", GCPhysAddress, cb));
1943
1944 /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
1945 rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
1946 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
1947 virtioScsiMMIOWrite, virtioScsiMMIORead,
1948 "virtio-scsi MMIO");
1949 pThis->GCPhysMMIOBase = RT_SUCCESS(rc) ? GCPhysAddress : 0;
1950 return rc;
1951 case 1:
1952 /* VirtIO 1.0 doesn't uses Port I/O (Virtio 0.95 e.g. "legacy", does) */
1953 AssertMsgFailed(("virtio-scsi: Port I/O not supported by this Host SCSI device\n"));
1954 default:
1955 AssertMsgFailed(("Invalid enmType=%d\n", enmType));
1956 }
1957 return VERR_GENERAL_FAILURE; /* Should never get here */
1958}
1959
1960/**
1961 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1962 */
1963static DECLCALLBACK(void *) virtioScsiR3TargetQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1964{
1965 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IBase);
1966 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pTarget->IBase);
1967 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pTarget->IMediaPort);
1968 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pTarget->IMediaExPort);
1969 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pTarget->ILed);
1970 return NULL;
1971}
1972
1973/**
1974 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1975 */
1976static DECLCALLBACK(void *) virtioScsiR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1977{
1978 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, IBase);
1979
1980 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
1981 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
1982
1983 return NULL;
1984}
1985
1986/**
1987 * Detach notification.
1988 *
1989 * One harddisk at one port has been unplugged.
1990 * The VM is suspended at this point.
1991 *
1992 * @param pDevIns The device instance.
1993 * @param iLUN The logical unit which is being detached.
1994 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1995 */
1996static DECLCALLBACK(void) virtioScsiR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1997{
1998 RT_NOREF(fFlags);
1999 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2000 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[iLUN];
2001
2002 LogFunc((""));
2003
2004 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2005 ("virtio-scsi: Device does not support hotplugging\n"));
2006
2007 /*
2008 * Zero some important members.
2009 */
2010 pTarget->fPresent = false;
2011 pTarget->pDrvBase = NULL;
2012}
2013
2014/**
2015 * Attach command.
2016 *
2017 * This is called when we change block driver.
2018 *
2019 * @returns VBox status code.
2020 * @param pDevIns The device instance.
2021 * @param iLUN The logical unit which is being detached.
2022 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2023 */
2024static DECLCALLBACK(int) virtioScsiR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2025{
2026 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2027 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[iLUN];
2028 int rc;
2029
2030 pThis->pDevInsR3 = pDevIns;
2031 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
2032 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
2033
2034 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2035 ("virtio-scsi: Device does not support hotplugging\n"),
2036 VERR_INVALID_PARAMETER);
2037
2038 /* the usual paranoia */
2039 AssertRelease(!pTarget->pDrvBase);
2040 Assert(pTarget->iLUN == iLUN);
2041
2042 /*
2043 * Try attach the SCSI driver and get the interfaces,
2044 * required as well as optional.
2045 */
2046 rc = PDMDevHlpDriverAttach(pDevIns, pTarget->iLUN, &pDevIns->IBase,
2047 &pTarget->pDrvBase, (const char *)&pTarget->pszLunName);
2048 if (RT_SUCCESS(rc))
2049 pTarget->fPresent = true;
2050 else
2051 AssertMsgFailed(("Failed to attach %s. rc=%Rrc\n", pTarget->pszLunName, rc));
2052
2053 if (RT_FAILURE(rc))
2054 {
2055 pTarget->fPresent = false;
2056 pTarget->pDrvBase = NULL;
2057 }
2058 return rc;
2059}
2060
2061static DECLCALLBACK(int) virtioScsiDestruct(PPDMDEVINS pDevIns)
2062{
2063 /*
2064 * Check the versions here as well since the destructor is *always* called.
2065 */
2066
2067 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
2068
2069 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2070
2071 for (int qIdx = 0; qIdx < VIRTQ_MAX_CNT; qIdx++)
2072 {
2073 PWORKER pWorker = &pThis->aWorker[qIdx];
2074 if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
2075 {
2076 SUPSemEventClose(pThis->pSupDrvSession, pWorker->hEvtProcess);
2077 pWorker->hEvtProcess = NIL_SUPSEMEVENT;
2078 }
2079 }
2080 return VINF_SUCCESS;
2081}
2082
2083static DECLCALLBACK(int) virtioScsiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg){
2084
2085 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2086
2087 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2088 int rc = VINF_SUCCESS;
2089
2090 pThis->pDevInsR3 = pDevIns;
2091 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
2092 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
2093 pThis->pSupDrvSession = PDMDevHlpGetSupDrvSession(pDevIns);
2094
2095 LogFunc(("PDM device instance: %d\n", iInstance));
2096 RTStrPrintf((char *)pThis->szInstance, sizeof(pThis->szInstance), "VIRTIOSCSI%d", iInstance);
2097
2098 /*
2099 * Validate and read configuration.
2100 */
2101 if (!CFGMR3AreValuesValid(pCfg,"NumTargets\0"
2102 "Bootable\0"
2103 /* "GCEnabled\0" TBD */
2104 /* "R0Enabled\0" TBD */
2105 ))
2106 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
2107 N_("virtio-scsi configuration error: unknown option specified"));
2108
2109 rc = CFGMR3QueryIntegerDef(pCfg, "NumTargets", &pThis->cTargets, true);
2110 if (RT_FAILURE(rc))
2111 return PDMDEV_SET_ERROR(pDevIns, rc,
2112 N_("virtio-scsi configuration error: failed to read NumTargets as integer"));
2113 LogFunc(("NumTargets=%d\n", pThis->cTargets));
2114
2115 rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &pThis->fBootable, true);
2116 if (RT_FAILURE(rc))
2117 return PDMDEV_SET_ERROR(pDevIns, rc,
2118 N_("virtio-scsi configuration error: failed to read Bootable as boolean"));
2119 LogFunc(("Bootable=%RTbool (unimplemented)\n", pThis->fBootable));
2120
2121 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, false);
2122 if (RT_FAILURE(rc))
2123 return PDMDEV_SET_ERROR(pDevIns, rc,
2124 N_("virtio-scsi configuration error: failed to read R0Enabled as boolean"));
2125
2126 rc = CFGMR3QueryBoolDef(pCfg, "RCEnabled", &pThis->fRCEnabled, false);
2127 if (RT_FAILURE(rc))
2128 return PDMDEV_SET_ERROR(pDevIns, rc,
2129 N_("virtio-scsi configuration error: failed to read RCEnabled as boolean"));
2130
2131 VIRTIOPCIPARAMS virtioPciParams, *pVirtioPciParams = &virtioPciParams;
2132 pVirtioPciParams->uDeviceId = PCI_DEVICE_ID_VIRTIOSCSI_HOST;
2133 pVirtioPciParams->uClassBase = PCI_CLASS_BASE_MASS_STORAGE;
2134 pVirtioPciParams->uClassSub = PCI_CLASS_SUB_SCSI_STORAGE_CONTROLLER;
2135 pVirtioPciParams->uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
2136 pVirtioPciParams->uSubsystemId = PCI_DEVICE_ID_VIRTIOSCSI_HOST; /* Virtio 1.0 spec allows PCI Device ID here */
2137 pVirtioPciParams->uInterruptLine = 0x00;
2138 pVirtioPciParams->uInterruptPin = 0x01;
2139
2140 pThis->IBase.pfnQueryInterface = virtioScsiR3DeviceQueryInterface;
2141
2142 /* Configure virtio_scsi_config that transacts via VirtIO implementation's Dev. Specific Cap callbacks */
2143 pThis->virtioScsiConfig.uNumQueues = VIRTIOSCSI_REQ_QUEUE_CNT;
2144 pThis->virtioScsiConfig.uSegMax = VIRTIOSCSI_MAX_SEG_COUNT;
2145 pThis->virtioScsiConfig.uMaxSectors = VIRTIOSCSI_MAX_SECTORS_HINT;
2146 pThis->virtioScsiConfig.uCmdPerLun = VIRTIOSCSI_MAX_COMMANDS_PER_LUN;
2147 pThis->virtioScsiConfig.uEventInfoSize = sizeof(VIRTIOSCSI_EVENT_T); /* Spec says at least this size! */
2148 pThis->virtioScsiConfig.uSenseSize = VIRTIOSCSI_SENSE_SIZE_DEFAULT;
2149 pThis->virtioScsiConfig.uCdbSize = VIRTIOSCSI_CDB_SIZE_DEFAULT;
2150 pThis->virtioScsiConfig.uMaxChannel = VIRTIOSCSI_MAX_CHANNEL_HINT;
2151 pThis->virtioScsiConfig.uMaxTarget = pThis->cTargets;
2152 pThis->virtioScsiConfig.uMaxLun = VIRTIOSCSI_MAX_LUN;
2153
2154 rc = virtioConstruct(pDevIns, pThis, &pThis->hVirtio, pVirtioPciParams, pThis->szInstance,
2155 VIRTIOSCSI_HOST_SCSI_FEATURES_OFFERED,
2156 virtioScsiR3DevCapRead,
2157 virtioScsiR3DevCapWrite,
2158 virtioScsiStatusChanged,
2159 virtioScsiNotified,
2160 virtioScsiR3LiveExec,
2161 virtioScsiR3SaveExec,
2162 virtioScsiR3LoadExec,
2163 virtioScsiR3LoadDone,
2164 sizeof(VIRTIOSCSI_CONFIG_T) /* cbDevSpecificCap */,
2165 (void *)&pThis->virtioScsiConfig /* pDevSpecificCap */);
2166
2167 if (RT_FAILURE(rc))
2168 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi: failed to initialize VirtIO"));
2169
2170
2171 RTStrCopy((char *)pThis->szQueueNames[CONTROLQ_IDX], VIRTIO_MAX_QUEUE_NAME_SIZE, "controlq");
2172 RTStrCopy((char *)pThis->szQueueNames[EVENTQ_IDX], VIRTIO_MAX_QUEUE_NAME_SIZE, "eventq");
2173 for (uint16_t qIdx = VIRTQ_REQ_BASE; qIdx < VIRTQ_REQ_BASE + VIRTIOSCSI_REQ_QUEUE_CNT; qIdx++)
2174 RTStrPrintf((char *)pThis->szQueueNames[qIdx], VIRTIO_MAX_QUEUE_NAME_SIZE,
2175 "requestq<%d>", qIdx - VIRTQ_REQ_BASE);
2176
2177 /**
2178 * Create one worker per incoming-work-related queue (eventq is outgoing status to guest,
2179 * wherein guest is supposed to keep the queue loaded-up with buffer vectors the host
2180 * can quickly fill-in send back). Should be light-duty and fast enough to be handled on
2181 * requestq or controlq thread. The Linux virtio_scsi driver limits the number of request
2182 * queues to MIN(<# Guest CPUs>, <Device's req queue max>), so queue count is ultimately
2183 * constrained from host side at negotiation time and initialization and later through
2184 * bounds-checking.
2185 */
2186 for (uint16_t qIdx = 0; qIdx < VIRTIOSCSI_QUEUE_CNT; qIdx++)
2187 {
2188 rc = virtioQueueAttach(pThis->hVirtio, qIdx, QUEUENAME(qIdx));
2189 AssertMsgReturn(rc == VINF_SUCCESS, ("Failed to attach queue %s\n", QUEUENAME(qIdx)), rc);
2190 pThis->fQueueAttached[qIdx] = (rc == VINF_SUCCESS);
2191
2192 if (qIdx == CONTROLQ_IDX || IS_REQ_QUEUE(qIdx))
2193 {
2194 rc = PDMDevHlpThreadCreate(pDevIns, &pThis->aWorker[qIdx].pThread,
2195 (void *)(uint64_t)qIdx, virtioScsiWorker,
2196 virtioScsiR3WorkerWakeUp, 0, RTTHREADTYPE_IO, QUEUENAME(qIdx));
2197 if (rc != VINF_SUCCESS)
2198 {
2199 LogRel(("Error creating thread for Virtual Queue %s\n", QUEUENAME(qIdx)));
2200 return rc;
2201 }
2202
2203 rc = SUPSemEventCreate(pThis->pSupDrvSession, &pThis->aWorker[qIdx].hEvtProcess);
2204 if (RT_FAILURE(rc))
2205 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2206 N_("LsiLogic: Failed to create SUP event semaphore"));
2207 }
2208 }
2209
2210 rc = PDMDevHlpPCIIORegionRegister(pDevIns, VIRTIOSCSI_REGION_MEM_IO, 32,
2211 PCI_ADDRESS_SPACE_MEM, virtioScsiR3Map);
2212 if (RT_FAILURE(rc))
2213 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi: cannot register PCI mmio address space"));
2214
2215#ifdef BOOTABLE_SUPPORT_TBD
2216 if (fBootable)
2217 {
2218 /* Register I/O port space for BIOS access. */
2219 rc = PDMDevHlpIOPortRegister(pDevIns, VIRTIOSCSI_BIOS_IO_PORT, 4, NULL,
2220 virtioScsiR3BiosIoPortWrite, virtioScsiR3BiosIoPortRead,
2221 virtioScsiR3BiosIoPortWriteStr, virtioScsiR3BiosIoPortReadStr,
2222 "virtio-scsi BIOS");
2223 if (RT_FAILURE(rc))
2224 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi cannot register BIOS I/O handlers"));
2225 }
2226#endif
2227
2228 /* Initialize per device instance. */
2229 for (RTUINT iLUN = 0; iLUN < VIRTIOSCSI_MAX_TARGETS; iLUN++)
2230 {
2231 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[iLUN];
2232
2233 if (RTStrAPrintf(&pTarget->pszLunName, "VSCSILUN%u", iLUN) < 0)
2234 AssertLogRelFailedReturn(VERR_NO_MEMORY);
2235
2236 /* Initialize static parts of the device. */
2237 pTarget->iLUN = iLUN;
2238 pTarget->pVirtioScsiR3 = pThis;
2239
2240 pTarget->IBase.pfnQueryInterface = virtioScsiR3TargetQueryInterface;
2241
2242 /* IMediaPort and IMediaExPort interfaces provide callbacks for VD media and downstream driver access */
2243 pTarget->IMediaPort.pfnQueryDeviceLocation = virtioScsiR3QueryDeviceLocation;
2244 pTarget->IMediaExPort.pfnIoReqCompleteNotify = virtioScsiR3IoReqCompleteNotify;
2245 pTarget->IMediaExPort.pfnIoReqCopyFromBuf = virtioScsiR3IoReqCopyFromBuf;
2246 pTarget->IMediaExPort.pfnIoReqCopyToBuf = virtioScsiR3IoReqCopyToBuf;
2247 pTarget->IMediaExPort.pfnIoReqStateChanged = virtioScsiR3IoReqStateChanged;
2248 pTarget->IMediaExPort.pfnMediumEjected = virtioScsiR3MediumEjected;
2249 pTarget->IMediaExPort.pfnIoReqQueryBuf = NULL; /* When used avoids copyFromBuf CopyToBuf*/
2250 pTarget->IMediaExPort.pfnIoReqQueryDiscardRanges = NULL;
2251
2252
2253 pTarget->IBase.pfnQueryInterface = virtioScsiR3TargetQueryInterface;
2254 pTarget->ILed.pfnQueryStatusLed = virtioScsiR3TargetQueryStatusLed;
2255 pThis->ILeds.pfnQueryStatusLed = virtioScsiR3DeviceQueryStatusLed;
2256 pTarget->led.u32Magic = PDMLED_MAGIC;
2257
2258 LogFunc(("Attaching LUN: %s\n", pTarget->pszLunName));
2259
2260 AssertReturn(iLUN < RT_ELEMENTS(pThis->aTargetInstances), VERR_PDM_NO_SUCH_LUN);
2261 rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pTarget->IBase, &pTarget->pDrvBase, (const char *)&pTarget->pszLunName);
2262 if (RT_SUCCESS(rc))
2263 {
2264 pTarget->fPresent = true;
2265
2266 /* DrvSCSI.cpp currently implements the IMedia and IMediaEx interfaces, so those
2267 * are the interfaces that will be used to pass SCSI requests down to the
2268 * DrvSCSI driver to eventually make it down to the VD layer */
2269 pTarget->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIA);
2270 AssertMsgReturn(VALID_PTR(pTarget->pDrvMedia),
2271 ("virtio-scsi configuration error: LUN#%d missing basic media interface!\n", pTarget->iLUN),
2272 VERR_PDM_MISSING_INTERFACE);
2273
2274 /* Get the extended media interface. */
2275 pTarget->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIAEX);
2276 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
2277 ("virtio-scsi configuration error: LUN#%d missing extended media interface!\n", pTarget->iLUN),
2278 VERR_PDM_MISSING_INTERFACE);
2279
2280 rc = pTarget->pDrvMediaEx->pfnIoReqAllocSizeSet(pTarget->pDrvMediaEx, sizeof(VIRTIOSCSIREQ));
2281 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
2282 ("virtio-scsi configuration error: LUN#%u: Failed to set I/O request size!\n", pTarget->iLUN),
2283 rc);
2284
2285 }
2286 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2287 {
2288 pTarget->fPresent = false;
2289 pTarget->pDrvBase = NULL;
2290 rc = VINF_SUCCESS;
2291 Log(("virtio-scsi: no driver attached to device %s\n", pTarget->pszLunName));
2292 }
2293 else
2294 {
2295 AssertLogRelMsgFailed(("virtio-scsi: Failed to attach %s\n", pTarget->pszLunName));
2296 return rc;
2297 }
2298 }
2299
2300 /* Status driver */
2301 PPDMIBASE pUpBase;
2302 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pUpBase, "Status Port");
2303 if (RT_FAILURE(rc))
2304 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
2305
2306 /*
2307 * Register the debugger info callback.
2308 */
2309 char szTmp[128];
2310 RTStrPrintf(szTmp, sizeof(szTmp), "%s%d", pDevIns->pReg->szName, pDevIns->iInstance);
2311 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "virtio-scsi info", virtioScsiR3Info);
2312
2313 return rc;
2314}
2315
2316/**
2317 * The device registration structure.
2318 */
2319const PDMDEVREG g_DeviceVirtioSCSI =
2320{
2321 /* u32Version */
2322 PDM_DEVREG_VERSION,
2323 /* szName */
2324 "virtio-scsi",
2325 /* szRCMod */
2326 "VBoxDDRC.rc",
2327 /* szR0Mod */
2328 "VBoxDDR0.r0",
2329 /* pszDescription */
2330 "Virtio Host SCSI.\n",
2331 /* fFlags */
2332#ifdef VIRTIOSCSI_GC_SUPPORT
2333 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
2334#else
2335 PDM_DEVREG_FLAGS_DEFAULT_BITS,
2336#endif
2337 /* fClass */
2338 PDM_DEVREG_CLASS_MISC,
2339 /* cMaxInstances */
2340 ~0U,
2341 /* cbInstance */
2342 sizeof(VIRTIOSCSI),
2343 /* pfnConstruct */
2344 virtioScsiConstruct,
2345 /* pfnDestruct */
2346 virtioScsiDestruct,
2347 /* pfnRelocate */
2348 virtioScsiR3Relocate,
2349 /* pfnMemSetup */
2350 NULL,
2351 /* pfnPowerOn */
2352 NULL,
2353 /* pfnReset */
2354 virtioScsiR3PDMReset,
2355 /* pfnSuspend */
2356 NULL,
2357 /* pfnResume */
2358 NULL,
2359 /* pfnAttach */
2360 virtioScsiR3Attach,
2361 /* pfnDetach */
2362 virtioScsiR3Detach,
2363 /* pfnQueryInterface */
2364 NULL,
2365 /* pfnInitComplete */
2366 NULL,
2367 /* pfnPowerOff */
2368 NULL,
2369 /* pfnSoftReset */
2370 NULL,
2371 /* u32VersionEnd */
2372 PDM_DEVREG_VERSION
2373};
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