VirtualBox

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

Last change on this file since 80589 was 80589, checked in by vboxsync, 6 years ago

Devices/Storage: Add two new parameters to PDMIMEDIAEX::pfnIoReqSendScsiCmd to return the amount ofsense data written and the determined transfer direction from parsed CDB, bugref:9440

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 96.8 KB
Line 
1/* $Id: DevVirtioSCSI.cpp 80589 2019-09-04 18:20:28Z vboxsync $ $Revision: 80589 $ $Date: 2019-09-04 18:20:28 +0000 (Wed, 04 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 uSenseLen; /**< 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 PVIRTIOSCSI_REQ_CMD_T pVirtqReq; /**< Allocated to receive translated queued req */
483} VIRTIOSCSIREQ;
484
485#define PTARGET_FROM_LUN_BUF(lunBuf) &pThis->aTargetInstances[lunBuf[1]];
486
487#define SET_LUN_BUF(target, lun, out) \
488 out[0] = 0x01; out[1] = target; out[2] = (lun >> 8) & 0x40; out[3] = lun & 0xff; *((uint16_t *)out + 4) = 0;
489
490
491DECLINLINE(const char *) virtioGetTMFTypeText(uint32_t uSubType)
492{
493 switch (uSubType)
494 {
495 case VIRTIOSCSI_T_TMF_ABORT_TASK: return "ABORT TASK";
496 case VIRTIOSCSI_T_TMF_ABORT_TASK_SET: return "ABORT TASK SET";
497 case VIRTIOSCSI_T_TMF_CLEAR_ACA: return "CLEAR ACA";
498 case VIRTIOSCSI_T_TMF_CLEAR_TASK_SET: return "CLEAR TASK SET";
499 case VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET: return "I T NEXUS RESET";
500 case VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET: return "LOGICAL UNIT RESET";
501 case VIRTIOSCSI_T_TMF_QUERY_TASK: return "QUERY TASK";
502 case VIRTIOSCSI_T_TMF_QUERY_TASK_SET: return "QUERY TASK SET";
503 default: return "<unknown>";
504 }
505}
506
507DECLINLINE(const char *) virtioGetReqRespText(uint32_t vboxRc)
508{
509 switch (vboxRc)
510 {
511 case VIRTIOSCSI_S_OK: return "OK";
512 case VIRTIOSCSI_S_OVERRUN: return "OVERRRUN";
513 case VIRTIOSCSI_S_ABORTED: return "ABORTED";
514 case VIRTIOSCSI_S_BAD_TARGET: return "BAD TARGET";
515 case VIRTIOSCSI_S_RESET: return "RESET";
516 case VIRTIOSCSI_S_TRANSPORT_FAILURE: return "TRANSPORT FAILURE";
517 case VIRTIOSCSI_S_TARGET_FAILURE: return "TARGET FAILURE";
518 case VIRTIOSCSI_S_NEXUS_FAILURE: return "NEXUS FAILURE";
519 case VIRTIOSCSI_S_BUSY: return "BUSY";
520 case VIRTIOSCSI_S_FAILURE: return "FAILURE";
521 default: return "<unknown>";
522 }
523}
524
525DECLINLINE(const char *) virtioGetCtrlRespText(uint32_t vboxRc)
526{
527 switch (vboxRc)
528 {
529 case VIRTIOSCSI_S_OK: return "OK/COMPLETE";
530 case VIRTIOSCSI_S_BAD_TARGET: return "BAD TARGET";
531 case VIRTIOSCSI_S_BUSY: return "BUSY";
532 case VIRTIOSCSI_S_NEXUS_FAILURE: return "NEXUS FAILURE";
533 case VIRTIOSCSI_S_TRANSPORT_FAILURE: return "TRANSPORT FAILURE";
534 case VIRTIOSCSI_S_FAILURE: return "FAILURE";
535 case VIRTIOSCSI_S_INCORRECT_LUN: return "INCORRECT LUN";
536 case VIRTIOSCSI_S_FUNCTION_SUCCEEDED: return "FUNCTION SUCCEEDED";
537 case VIRTIOSCSI_S_FUNCTION_REJECTED: return "FUNCTION REJECTED";
538 default: return "<unknown>";
539 }
540}
541
542DECLINLINE(void) virtioGetControlAsyncMaskText(char *pszOutput, size_t cbOutput, uint32_t uAsyncTypesMask)
543{
544 RTStrPrintf(pszOutput, cbOutput, "%s%s%s%s%s%s",
545 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE) ? "CHANGE_OPERATION " : "",
546 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_POWER_MGMT) ? "POWER_MGMT " : "",
547 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST) ? "EXTERNAL_REQ " : "",
548 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE) ? "MEDIA_CHANGE " : "",
549 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_MULTI_HOST) ? "MULTI_HOST " : "",
550 (uAsyncTypesMask & VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY) ? "DEVICE_BUSY " : "");
551}
552
553uint8_t virtioScsiEstimateCdbLen(uint8_t uCmd, uint8_t cbMax)
554{
555 if (uCmd < 0x1f)
556 return 6;
557 else if (uCmd >= 0x20 && uCmd < 0x60)
558 return 10;
559 else if (uCmd >= 0x60 && uCmd < 0x80)
560 return cbMax;
561 else if (uCmd >= 0x80 && uCmd < 0xa0)
562 return 16;
563 else if (uCmd >= 0xa0 && uCmd < 0xC0)
564 return 12;
565 else
566 return cbMax;
567}
568
569/**
570 * This macro resolves to boolean true if uOffset matches a field offset and size exactly,
571 * (or if it is a 64-bit field, if it accesses either 32-bit part as a 32-bit access)
572 * ASSUMED this critereon is mandated by section 4.1.3.1 of the VirtIO 1.0 specification)
573 * This MACRO can be re-written to allow unaligned access to a field (within bounds).
574 *
575 * @param member - Member of VIRTIO_PCI_COMMON_CFG_T
576 * @result - true or false
577 */
578#define MATCH_SCSI_CONFIG(member) \
579 (RT_SIZEOFMEMB(VIRTIOSCSI_CONFIG_T, member) == 8 \
580 && ( uOffset == RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member) \
581 || uOffset == RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member) + sizeof(uint32_t)) \
582 && cb == sizeof(uint32_t)) \
583 || (uOffset == RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member) \
584 && cb == RT_SIZEOFMEMB(VIRTIOSCSI_CONFIG_T, member))
585
586#define LOG_ACCESSOR(member) \
587 virtioLogMappedIoValue(__FUNCTION__, #member, RT_SIZEOFMEMB(VIRTIOSCSI_CONFIG_T, member), \
588 pv, cb, uIntraOffset, fWrite, false, 0);
589
590#define SCSI_CONFIG_ACCESSOR(member) \
591 { \
592 uint32_t uIntraOffset = uOffset - RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member); \
593 if (fWrite) \
594 memcpy(((char *)&pThis->virtioScsiConfig.member) + uIntraOffset, (const char *)pv, cb); \
595 else \
596 memcpy((char *)pv, (const char *)(((char *)&pThis->virtioScsiConfig.member) + uIntraOffset), cb); \
597 LOG_ACCESSOR(member); \
598 }
599
600#define SCSI_CONFIG_ACCESSOR_READONLY(member) \
601 { \
602 uint32_t uIntraOffset = uOffset - RT_UOFFSETOF(VIRTIOSCSI_CONFIG_T, member); \
603 if (fWrite) \
604 LogFunc(("Guest attempted to write readonly virtio_pci_common_cfg.%s\n", #member)); \
605 else \
606 { \
607 memcpy((char *)pv, (const char *)(((char *)&pThis->virtioScsiConfig.member) + uIntraOffset), cb); \
608 LOG_ACCESSOR(member); \
609 } \
610 }
611
612
613
614typedef struct VIRTIOSCSIREQ *PVIRTIOSCSIREQ;
615
616#ifdef BOOTABLE_SUPPORT_TBD
617/** @callback_method_impl{FNIOMIOPORTIN} */
618static DECLCALLBACK(int) virtioScsiR3BiosIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint8_t *pbDst,
619 uint32_t *pcTransfers, unsigned cb);
620{
621}
622/** @callback_method_impl{FNIOMIOPORTOUT} */
623static DECLCALLBACK(int) virtioScsiR3BiosIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb);
624{
625}
626/** @callback_method_impl{FNIOMIOPORTOUTSTRING} */
627static DECLCALLBACK(int) virtioScsiR3BiosIoPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, const uint8_t *pbSrc,
628 uint32_t *pcTransfers, unsigned cb);
629{
630}
631/** @callback_method_impl{FNIOMIOPORTINSTRING} */
632static DECLCALLBACK(int) virtioScsiR3BiosIoPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint8_t *pbDst,
633 uint32_t *pcTransfers, unsigned cb);
634{
635}
636#endif
637
638/**
639 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
640 */
641static DECLCALLBACK(void) virtioScsiR3IoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
642 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
643{
644
645 RT_NOREF4(pInterface, hIoReq, pvIoReqAlloc, enmState);
646 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
647
648 switch (enmState)
649 {
650 case PDMMEDIAEXIOREQSTATE_SUSPENDED:
651 {
652 /* Make sure the request is not accounted for so the VM can suspend successfully. */
653 uint32_t cTasksActive = ASMAtomicDecU32(&pTarget->cReqsInProgress);
654 if (!cTasksActive && pTarget->CTX_SUFF(pVirtioScsi)->fSignalIdle)
655 PDMDevHlpAsyncNotificationCompleted(pTarget->CTX_SUFF(pVirtioScsi)->pDevInsR3);
656 break;
657 }
658 case PDMMEDIAEXIOREQSTATE_ACTIVE:
659 /* Make sure the request is accounted for so the VM suspends only when the request is complete. */
660 ASMAtomicIncU32(&pTarget->cReqsInProgress);
661 break;
662 default:
663 AssertMsgFailed(("Invalid request state given %u\n", enmState));
664 }
665}
666
667/**
668 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
669 */
670static DECLCALLBACK(int) virtioScsiR3IoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
671 void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf,
672 size_t cbCopy)
673{
674 RT_NOREF(hIoReq);
675 RT_NOREF(pInterface);
676
677 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
678
679 /** DrvSCSI.cpp, that issues this callback, just sticks one segment in the buffer */
680 memcpy(pReq->pbDataIn + offDst, pSgBuf->paSegs[0].pvSeg, cbCopy);
681 return VINF_SUCCESS;
682}
683
684
685/**
686 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
687 */
688static DECLCALLBACK(int) virtioScsiR3IoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
689 void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf,
690 size_t cbCopy)
691{
692 RT_NOREF(hIoReq);
693 RT_NOREF(pInterface);
694
695 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
696 /** DrvSCSI.cpp, that issues this callback, just sticks one segment in the buffer */
697
698 memcpy(pSgBuf->paSegs[0].pvSeg, pReq->pbDataOut + offSrc, cbCopy);
699
700 return VINF_SUCCESS;
701}
702
703static int virtioScsiSendEvent(PVIRTIOSCSI pThis, uint16_t uTarget, uint32_t uEventType, uint32_t uReason)
704{
705 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[uTarget];
706
707 VIRTIOSCSI_EVENT_T event = { uEventType, { 0 }, uReason };
708 SET_LUN_BUF(pTarget->iLUN, 0, event.uLUN);
709
710 switch(uEventType)
711 {
712 case VIRTIOSCSI_T_NO_EVENT:
713 if (uEventType & VIRTIOSCSI_T_EVENTS_MISSED)
714 LogFunc(("LUN: %s Warning driver that events were missed\n", event.uLUN));
715 else
716 LogFunc(("LUN: %s Warning driver event info it queued is shorter than configured\n", event.uLUN));
717 break;
718 case VIRTIOSCSI_T_TRANSPORT_RESET:
719 switch(uReason)
720 {
721 case VIRTIOSCSI_EVT_RESET_REMOVED:
722 LogFunc(("LUN: %s Target or LUN removed\n", event.uLUN));
723 break;
724 case VIRTIOSCSI_EVT_RESET_RESCAN:
725 LogFunc(("LUN: %s Target or LUN added\n", event.uLUN));
726 break;
727 case VIRTIOSCSI_EVT_RESET_HARD:
728 LogFunc(("LUN: %s Target was reset\n", event.uLUN));
729 break;
730 }
731 break;
732 case VIRTIOSCSI_T_ASYNC_NOTIFY:
733 char szTypeText[128];
734 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), uReason);
735 LogFunc(("LUN: %s Delivering subscribed async notification %s\n", event.uLUN, szTypeText));
736 break;
737 case VIRTIOSCSI_T_PARAM_CHANGE:
738 LogFunc(("LUN: %s PARAM_CHANGE sense code: 0x%x sense qualifier: 0x%x\n",
739 event.uLUN, uReason & 0xff, (uReason >> 8) & 0xff));
740 break;
741 default:
742 LogFunc(("LUN: %s Unknown event type: %d, ignoring\n", event.uLUN, uEventType));
743 return VINF_SUCCESS;
744 }
745
746 if (virtioQueueIsEmpty(pThis->hVirtio, EVENTQ_IDX))
747 {
748 LogFunc(("eventq is empty, events missed!\n"));
749 ASMAtomicWriteBool(&pThis->fEventsMissed, true);
750 return VINF_SUCCESS;
751 }
752
753 int rc = virtioQueueGet(pThis->hVirtio, EVENTQ_IDX, true, NULL, NULL);
754 AssertRC(rc);
755
756 RTSGBUF reqSegBuf;
757 RTSGSEG aReqSegs[] = { { &event, sizeof(event) } };
758 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
759
760 rc = virtioQueuePut (pThis->hVirtio, EVENTQ_IDX, &reqSegBuf, true);
761 AssertRC(rc);
762
763 rc = virtioQueueSync(pThis->hVirtio, EVENTQ_IDX);
764 AssertRC(rc);
765
766 return VINF_SUCCESS;
767
768}
769/** TBD: VirtIO 1.0 spec 5.6.6.1.1 requires some request actions on reset that are
770 * not implemented. Specifically either canceling outstanding I/O or
771 * returning VIRTIOSCSI_S_FAILURE for those requests. Since there's no
772 * way to cancel I/O on VSCSI at this time the only the only other
773 * possibility is to wait for the outstanding request count to drop
774 * and return the failure code for any-and-all until that's done before
775 * allowing a reset to continue.
776 *
777 * In the absence of active I/O farmed out to VSCSI
778 * the device handles a guest driver unload/reload gracefully and has
779 * been tested.
780 */
781static int virtioScsiReqFinish(PVIRTIOSCSI pThis, PVIRTIOSCSIREQ pReq, int rcReq)
782{
783 PVIRTIOSCSITARGET pTarget = pReq->pTarget;
784 PPDMIMEDIAEX pIMediaEx = pTarget->pDrvMediaEx;
785
786 ASMAtomicDecU32(&pTarget->cReqsInProgress);
787
788 size_t cbResidual = 0, cbXfer = 0;
789 int rc = pIMediaEx->pfnIoReqQueryResidual(pIMediaEx, pReq->hIoReq, &cbResidual);
790 AssertRC(rc);
791
792 rc = pIMediaEx->pfnIoReqQueryXferSize(pIMediaEx, pReq->hIoReq, &cbXfer);
793 AssertRC(rc);
794
795 #define SHOULD_FIX_VSCSI_TO_RETURN_SENSE_LEN 32
796 struct REQ_RESP_HDR respHdr = { 0 };
797 respHdr.uSenseLen = pReq->uStatus == SCSI_STATUS_CHECK_CONDITION ? SHOULD_FIX_VSCSI_TO_RETURN_SENSE_LEN : 0;
798 respHdr.uResidual = cbResidual;
799 respHdr.uStatus = pReq->uStatus;
800
801 /** VirtIO 1.0 spec 5.6.6.1.1 says device MUST return a VirtIO response byte value.
802 * Some are returned during the submit phase, and a few are not mapped at all,
803 * wherein anything that can't map specifically gets mapped to VIRTIOSCSI_S_FAILURE */
804 switch(rcReq)
805 {
806 case SCSI_STATUS_OK:
807 if (pReq->uStatus != SCSI_STATUS_CHECK_CONDITION)
808 {
809 respHdr.uResponse = VIRTIOSCSI_S_OK;
810 break;
811 }
812 /* fallthrough */
813 case SCSI_STATUS_BUSY:
814 respHdr.uResponse = VIRTIOSCSI_S_BUSY;
815 break;
816 case SCSI_STATUS_DATA_UNDEROVER_RUN:
817 respHdr.uResponse = VIRTIOSCSI_S_OVERRUN;
818 break;
819 case SCSI_STATUS_TASK_ABORTED:
820 respHdr.uResponse = VIRTIOSCSI_S_ABORTED;
821 break;
822 case SCSI_STATUS_CHECK_CONDITION:
823 {
824 uint8_t uSenseKey = pReq->pbSense[2];
825 switch (uSenseKey)
826 {
827 case SCSI_SENSE_ABORTED_COMMAND:
828 respHdr.uResponse = VIRTIOSCSI_S_ABORTED;
829 break;
830 case SCSI_SENSE_COPY_ABORTED:
831 respHdr.uResponse = VIRTIOSCSI_S_ABORTED;
832 break;
833 case SCSI_SENSE_UNIT_ATTENTION:
834 respHdr.uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
835 break;
836 case SCSI_SENSE_HARDWARE_ERROR:
837 respHdr.uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
838 break;
839 case SCSI_SENSE_NOT_READY:
840 respHdr.uResponse = VIRTIOSCSI_S_BUSY; /* e.g. try again */
841 break;
842 default:
843 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
844 break;
845 }
846 }
847 break;
848
849 default:
850 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
851 break;
852 }
853
854 Log2Func(("status: %s, response: (%x) %s\n",
855 SCSIStatusText(pReq->uStatus), respHdr.uResponse, virtioGetReqRespText(respHdr.uResponse)));
856
857 if (RT_FAILURE(rcReq))
858 Log2Func(("rcReq: %s\n", RTErrGetDefine(rcReq)));
859
860 Log3Func(("status:%02x/resp:%02x, xfer=%d, residual: %u, sense (len=%d, alloc=%d)\n",
861 pReq->uStatus, respHdr.uResponse, cbXfer, cbResidual,
862 respHdr.uSenseLen, pThis->virtioScsiConfig.uSenseSize));
863
864 if (respHdr.uSenseLen && LogIs2Enabled())
865 {
866 Log2Func(("Sense: %s\n", SCSISenseText(pReq->pbSense[2])));
867 Log2Func(("Sense Ext3: %s\n", SCSISenseExtText(pReq->pbSense[12], pReq->pbSense[13])));
868 }
869
870 int cSegs = 0;
871
872
873// Assert(pReq->cbDataIn >= cbXfer);
874 Assert(pReq->pbSense != NULL);
875
876 RTSGSEG aReqSegs[4];
877 aReqSegs[cSegs].pvSeg = &respHdr;
878 aReqSegs[cSegs++].cbSeg = sizeof(respHdr);
879
880 aReqSegs[cSegs].pvSeg = pReq->pbSense;
881 aReqSegs[cSegs++].cbSeg = pReq->cbSense; /* VirtIO 1.0 spec 5.6.4/5.6.6.1 */
882
883 if (pReq->cbPiIn)
884 {
885 aReqSegs[cSegs].pvSeg = pReq->pbPiIn;
886 aReqSegs[cSegs++].cbSeg = pReq->cbPiIn;
887 }
888 if (pReq->cbDataIn)
889 {
890 aReqSegs[cSegs].pvSeg = pReq->pbDataIn;
891 aReqSegs[cSegs++].cbSeg = cbXfer;
892 }
893 RTSGBUF reqSegBuf;
894 RTSgBufInit(&reqSegBuf, aReqSegs, cSegs);
895
896 /**
897 * Fill in the request queue current descriptor chain's IN queue entry/entries
898 * (phys. memory) with the Req response data in virtual memory.
899 */
900 size_t cbReqSgBuf = RTSgBufCalcTotalLength(&reqSegBuf);
901 size_t cbInSgBuf = RTSgBufCalcTotalLength(pReq->pInSgBuf);
902 AssertMsgReturn(cbReqSgBuf <= cbInSgBuf,
903 ("Guest expected less req data (space needed: %d, avail: %d)\n", cbReqSgBuf, cbInSgBuf),
904 VERR_BUFFER_OVERFLOW);
905
906 /**
907 * Following doesn't put up memory barrier (fence).
908 * VirtIO 1.0 Spec requires mem. barrier for ctrl cmds
909 * but doesn't mention fences in regard to requests. */
910 virtioQueuePut(pThis->hVirtio, pReq->qIdx, &reqSegBuf, true /* fFence TBD */);
911 virtioQueueSync(pThis->hVirtio, pReq->qIdx);
912
913 Log(("-----------------------------------------------------------------------------------------\n"));
914
915 RTMemFree(pReq->pbSense);
916 RTMemFree(pReq->pbDataIn);
917 RTMemFree(pReq->pbPiIn);
918 RTMemFree(pReq->pVirtqReq);
919
920 pIMediaEx->pfnIoReqFree(pIMediaEx, pReq->hIoReq);
921
922 if (pTarget->cReqsInProgress == 0 && pThis->fSignalIdle)
923 PDMDevHlpAsyncNotificationCompleted(pThis->pDevInsR3);
924
925 return VINF_SUCCESS;
926}
927
928/**
929 * This is called to complete a request immediately
930 *
931 * @param pThis - PDM driver instance state
932 * @param qIdx - Queue index
933 * @param pRespHdr - Response header
934 * @param pbSense - Pointer to sense buffer or NULL if none.
935 *
936 * @returns virtual box status code
937 */
938static int virtioScsiReqFinish(PVIRTIOSCSI pThis, uint16_t qIdx, struct REQ_RESP_HDR *pRespHdr, uint8_t *pbSense)
939{
940 uint8_t *abSenseBuf = (uint8_t *)RTMemAllocZ(pThis->virtioScsiConfig.uSenseSize);
941 AssertReturn(abSenseBuf, VERR_NO_MEMORY);
942
943 Log2Func(("status: %s response: %s\n",
944 SCSIStatusText(pRespHdr->uStatus), virtioGetReqRespText(pRespHdr->uResponse)));
945
946 RTSGSEG aReqSegs[2];
947 aReqSegs[0].cbSeg = sizeof(pRespHdr);
948 aReqSegs[0].pvSeg = pRespHdr;
949 aReqSegs[1].cbSeg = pThis->virtioScsiConfig.uSenseSize;
950 aReqSegs[1].pvSeg = abSenseBuf;
951
952 if (pbSense && pRespHdr->uSenseLen)
953 memcpy(abSenseBuf, pbSense, pRespHdr->uSenseLen);
954 else
955 pRespHdr->uSenseLen = 0;
956
957 RTSGBUF reqSegBuf;
958 RTSgBufInit(&reqSegBuf, aReqSegs, RT_ELEMENTS(aReqSegs));
959
960 virtioQueuePut(pThis->hVirtio, qIdx, &reqSegBuf, true /* fFence */);
961 virtioQueueSync(pThis->hVirtio, qIdx);
962
963 RTMemFree(abSenseBuf);
964
965 Log(("---------------------------------------------------------------------------------\n"));
966
967 return VINF_SUCCESS;
968}
969
970static int virtioScsiReqSubmit(PVIRTIOSCSI pThis, uint16_t qIdx, PRTSGBUF pInSgBuf, PRTSGBUF pOutSgBuf)
971{
972 AssertMsgReturn(pOutSgBuf->cSegs, ("Req. has no OUT data, unexpected/TBD\n"), VERR_INVALID_PARAMETER);
973
974 size_t cbOut = RTSgBufCalcTotalLength(pOutSgBuf);
975 size_t cbIn = RTSgBufCalcTotalLength(pInSgBuf);
976
977 AssertMsgReturn(cbOut >= sizeof(REQ_CMD_HDR) + pThis->virtioScsiConfig.uCdbSize,
978 ("Req too short"), VERR_BUFFER_UNDERFLOW);
979
980 PVIRTIOSCSI_REQ_CMD_T pVirtqReq = (PVIRTIOSCSI_REQ_CMD_T)RTMemAlloc(cbOut);
981 AssertReturn(pVirtqReq, VERR_NO_MEMORY);
982
983 off_t cbOff = 0;
984 size_t cbCopy = cbOut;
985 while (cbCopy)
986 {
987 size_t cbSeg = cbCopy;
988 RTGCPHYS GCPhys = (RTGCPHYS)RTSgBufGetNextSegment(pOutSgBuf, &cbSeg);
989 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCPhys, ((uint8_t *)pVirtqReq) + cbOff, cbSeg);
990 cbCopy -= cbSeg;
991 cbOff += cbSeg;
992 }
993
994 uint8_t uTarget = pVirtqReq->cmdHdr.uLUN[1];
995 uint32_t uLUN = (pVirtqReq->cmdHdr.uLUN[2] << 8 | pVirtqReq->cmdHdr.uLUN[3]) & 0x3fff;
996
997 LogFunc(("[%s] (Target: %d LUN: %d) CDB: %.*Rhxs\n",
998 SCSICmdText(pVirtqReq->uCdb[0]), uTarget, uLUN,
999 virtioScsiEstimateCdbLen(pVirtqReq->uCdb[0], pThis->virtioScsiConfig.uCdbSize), pVirtqReq->uCdb));
1000
1001 Log3Func((" id: %RX64, attr: %x, prio: %d, crn: %x\n",
1002 pVirtqReq->cmdHdr.uId, pVirtqReq->cmdHdr.uTaskAttr, pVirtqReq->cmdHdr.uPrio, pVirtqReq->cmdHdr.uCrn));
1003
1004 /**
1005 * Calculate request offsets
1006 */
1007 size_t cbPiIn = 0, cbPiOut = 0, cbPiHdr = 0;
1008 off_t piOutOff = 0;
1009
1010 if (pThis->fHasT10pi)
1011 {
1012 cbPiIn = pVirtqReq->piHdr.uPiBytesOut;
1013 cbPiOut = pVirtqReq->piHdr.uPiBytesIn;
1014 cbPiHdr = sizeof(REQ_CMD_PI) + cbPiOut;
1015 piOutOff = sizeof(REQ_CMD_HDR) + pThis->virtioScsiConfig.uCdbSize + sizeof(REQ_CMD_PI);
1016 }
1017
1018 off_t uDataOutOff = sizeof(REQ_CMD_HDR) + pThis->virtioScsiConfig.uCdbSize + cbPiHdr;
1019 off_t uDataInOff = sizeof(REQ_RESP_HDR) + pThis->virtioScsiConfig.uSenseSize + cbPiIn;
1020 uint8_t *pbDataOut = (uint8_t *)((uint64_t)pVirtqReq + uDataOutOff);
1021 size_t cbDataOut = cbOut - uDataOutOff;
1022 size_t cbDataIn = cbIn - uDataInOff;
1023
1024 if (uTarget >= pThis->cTargets || uLUN != 0)
1025 {
1026 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1027 0, SCSI_SENSE_ILLEGAL_REQUEST,
1028 0, 0, 0, 0, 10, SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED, 0, 0 };
1029 struct REQ_RESP_HDR respHdr = { 0 };
1030 respHdr.uSenseLen = sizeof(abSense);
1031 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1032 respHdr.uResponse = (uTarget > pThis->cTargets) ? VIRTIOSCSI_S_BAD_TARGET : VIRTIOSCSI_S_OK;
1033 respHdr.uResidual = cbDataOut + cbDataIn;
1034 virtioScsiReqFinish(pThis, qIdx, &respHdr, abSense);
1035 return VINF_SUCCESS;
1036 }
1037
1038 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[uTarget];
1039
1040 if (RT_UNLIKELY(!pTarget->fPresent))
1041 {
1042 Log2Func(("Error submitting request, target not present!!\n"));
1043 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1044 0, SCSI_SENSE_NOT_READY, 0, 0, 0, 0, 10, 0, 0, 0 };
1045 struct REQ_RESP_HDR respHdr = { 0 };
1046 respHdr.uSenseLen = sizeof(abSense);
1047 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1048 respHdr.uResponse = VIRTIOSCSI_S_TARGET_FAILURE;
1049 respHdr.uResidual = cbDataIn + cbDataOut;
1050 virtioScsiReqFinish(pThis, qIdx, &respHdr, abSense);
1051 RTMemFree(pVirtqReq);
1052 return VINF_SUCCESS;
1053 }
1054
1055 PDMMEDIAEXIOREQ hIoReq = NULL;
1056 PVIRTIOSCSIREQ pReq;
1057 PPDMIMEDIAEX pIMediaEx = pTarget->pDrvMediaEx;
1058
1059 int rc = pIMediaEx->pfnIoReqAlloc(pIMediaEx, &hIoReq, (void **)&pReq, 0 /* uIoReqId */,
1060 PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR);
1061
1062 AssertMsgRCReturn(rc, ("Failed to allocate I/O request, rc=%Rrc\n", rc), rc);
1063
1064 if (pThis->fHasT10pi)
1065 {
1066 pReq->cbPiOut = cbPiOut;
1067 pReq->pbPiOut = (uint8_t *)((uint64_t)pVirtqReq + piOutOff);
1068 pReq->cbPiIn = cbPiIn;
1069 pReq->pbPiIn = (uint8_t *)RTMemAllocZ(cbPiIn);
1070 AssertMsgReturn(pReq->pbPiIn, ("Out of memory allocating pi_in buffer"), VERR_NO_MEMORY);
1071 }
1072
1073 pReq->hIoReq = hIoReq;
1074 pReq->pTarget = pTarget;
1075 pReq->qIdx = qIdx;
1076 pReq->pbDataOut = pbDataOut;
1077 pReq->pVirtqReq = pVirtqReq;
1078 pReq->pInSgBuf = pInSgBuf;
1079 pReq->cbSense = pThis->virtioScsiConfig.uSenseSize;
1080 pReq->pbSense = (uint8_t *)RTMemAllocZ(pReq->cbSense);
1081 AssertMsgReturn(pReq->pbSense, ("Out of memory allocating sense buffer"), VERR_NO_MEMORY);
1082
1083 if (cbDataIn)
1084 {
1085 pReq->cbDataIn = cbDataIn;
1086 pReq->pbDataIn = (uint8_t *)RTMemAllocZ(cbDataIn);
1087 AssertMsgReturn(pReq->pbDataIn, ("Out of memory allocating datain buffer"), VERR_NO_MEMORY);
1088 }
1089
1090 ASMAtomicIncU32(&pTarget->cReqsInProgress);
1091
1092 rc = pIMediaEx->pfnIoReqSendScsiCmd(pIMediaEx, pReq->hIoReq, uLUN,
1093 pVirtqReq->uCdb, pThis->virtioScsiConfig.uCdbSize,
1094 PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN, NULL, cbDataIn,
1095 pReq->pbSense, pReq->cbSense, NULL,
1096 &pReq->uStatus, 30 * RT_MS_1SEC);
1097
1098 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
1099 {
1100 /**
1101 * This rc value from DrvSCSI/send SCSI cmd means the request failed early
1102 * (no mem, buf copy callback, request buffer creation, or req enqueue),
1103 * and not submitted to lower layers, error it out.
1104 */
1105 Log2Func(("Error submitting request!\n"));
1106 size_t cbResidual;
1107 pIMediaEx->pfnIoReqQueryResidual(pIMediaEx, pReq->hIoReq, &cbResidual);
1108 uint8_t uASC, uASCQ = 0;
1109 switch (rc)
1110 {
1111 case VERR_NO_MEMORY:
1112 uASC = SCSI_ASC_SYSTEM_RESOURCE_FAILURE;
1113 break;
1114 default:
1115 uASC = SCSI_ASC_INTERNAL_TARGET_FAILURE;
1116 break;
1117 }
1118 uint8_t abSense[] = { RT_BIT(7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED,
1119 0, SCSI_SENSE_VENDOR_SPECIFIC,
1120 0, 0, 0, 0, 10, uASC, uASCQ, 0 };
1121 struct REQ_RESP_HDR respHdr = { 0 };
1122 respHdr.uSenseLen = sizeof(abSense);
1123 respHdr.uStatus = SCSI_STATUS_CHECK_CONDITION;
1124 respHdr.uResponse = VIRTIOSCSI_S_FAILURE;
1125 respHdr.uResidual = cbDataIn + cbDataOut;
1126 virtioScsiReqFinish(pThis, qIdx, &respHdr, abSense);
1127 RTMemFree(pReq->pbSense);
1128 RTMemFree(pReq->pbDataIn);
1129 RTMemFree(pReq->pbPiIn);
1130 RTMemFree(pVirtqReq);
1131 pIMediaEx->pfnIoReqFree(pIMediaEx, pReq->hIoReq);
1132 return VINF_SUCCESS;
1133 }
1134
1135 return VINF_SUCCESS;
1136}
1137
1138/**
1139 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
1140 */
1141static DECLCALLBACK(int) virtioScsiR3IoReqCompleteNotify(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1142 void *pvIoReqAlloc, int rcReq)
1143{
1144 RT_NOREF(hIoReq);
1145 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
1146 virtioScsiReqFinish(pTarget->CTX_SUFF(pVirtioScsi), (PVIRTIOSCSIREQ)pvIoReqAlloc, rcReq);
1147 return VINF_SUCCESS;
1148}
1149
1150static int virtioScsiCtrl(PVIRTIOSCSI pThis, uint16_t qIdx, PRTSGBUF pInSgBuf, PRTSGBUF pOutSgBuf)
1151{
1152 RT_NOREF(pThis);
1153 RT_NOREF(qIdx);
1154 RT_NOREF(pInSgBuf);
1155
1156 /**
1157 * According to the VirtIO 1.0 SCSI Host device, spec, section 5.6.6.2, control packets are
1158 * extremely small, so more than one segment is highly unlikely but not a bug. Get the
1159 * the controlq sg buffer into virtual memory. */
1160
1161 size_t cbOut = RTSgBufCalcTotalLength(pOutSgBuf);
1162
1163 PVIRTIOSCSI_CTRL_T pScsiCtrl = (PVIRTIOSCSI_CTRL_T)RTMemAllocZ(cbOut);
1164 AssertMsgReturn(pScsiCtrl, ("Out of memory"), VERR_NO_MEMORY);
1165
1166 /**
1167 * Get control command into virtual memory
1168 */
1169 off_t cbOff = 0;
1170 size_t cbSeg = 0;
1171 while (cbOut)
1172 {
1173 RTGCPHYS pvSeg = (RTGCPHYS)RTSgBufGetNextSegment(pOutSgBuf, &cbSeg);
1174 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), pvSeg, pScsiCtrl + cbOff, cbSeg);
1175 cbOut -= cbSeg;
1176 cbOff += cbSeg;
1177 }
1178
1179 uint8_t uResponse = VIRTIOSCSI_S_OK;
1180
1181 /**
1182 * Mask of events to tell guest driver this device supports
1183 * See VirtIO 1.0 specification section 5.6.6.2
1184 */
1185 uint32_t uSubscribedEvents =
1186 VIRTIOSCSI_EVT_ASYNC_POWER_MGMT
1187 | VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST
1188 | VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE
1189 | VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY;
1190
1191 RTSGBUF reqSegBuf;
1192
1193 switch(pScsiCtrl->uType)
1194 {
1195 case VIRTIOSCSI_T_TMF: /* Task Management Functions */
1196 {
1197 PVIRTIOSCSI_CTRL_TMF_T pScsiCtrlTmf = (PVIRTIOSCSI_CTRL_TMF_T)pScsiCtrl;
1198 LogFunc(("%s, VirtIO LUN: %.8Rhxs\n%*sTask Mgt Function: %s (not yet implemented)\n",
1199 QUEUENAME(qIdx), pScsiCtrlTmf->uLUN,
1200 CBQUEUENAME(qIdx) + 18, "", virtioGetTMFTypeText(pScsiCtrlTmf->uSubtype)));
1201
1202 switch(pScsiCtrlTmf->uSubtype)
1203 {
1204 case VIRTIOSCSI_T_TMF_ABORT_TASK:
1205 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1206 break;
1207 case VIRTIOSCSI_T_TMF_ABORT_TASK_SET:
1208 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1209 break;
1210 case VIRTIOSCSI_T_TMF_CLEAR_ACA:
1211 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1212 break;
1213 case VIRTIOSCSI_T_TMF_CLEAR_TASK_SET:
1214 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1215 break;
1216 case VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET:
1217 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1218 break;
1219 case VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET:
1220 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1221 break;
1222 case VIRTIOSCSI_T_TMF_QUERY_TASK:
1223 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1224 break;
1225 case VIRTIOSCSI_T_TMF_QUERY_TASK_SET:
1226 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1227 break;
1228 default:
1229 LogFunc(("Unknown TMF type\n"));
1230 uResponse = VIRTIOSCSI_S_FAILURE;
1231 }
1232
1233 RTSGSEG aReqSegs[] = { { &uResponse, sizeof(uResponse) } };
1234 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1235
1236 break;
1237 }
1238 case VIRTIOSCSI_T_AN_QUERY: /** Guest SCSI driver is querying supported async event notifications */
1239 {
1240 PVIRTIOSCSI_CTRL_AN_T pScsiCtrlAnQuery = (PVIRTIOSCSI_CTRL_AN_T)pScsiCtrl;
1241
1242 char szTypeText[128];
1243 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), pScsiCtrlAnQuery->uEventsRequested);
1244
1245 Log3Func(("%s, VirtIO LUN: %.8Rhxs\n%*sAsync Query, types: %s\n",
1246 QUEUENAME(qIdx), pScsiCtrlAnQuery->uLUN, CBQUEUENAME(qIdx) + 30, "", szTypeText));
1247
1248 uSubscribedEvents &= pScsiCtrlAnQuery->uEventsRequested;
1249 uResponse = VIRTIOSCSI_S_FUNCTION_COMPLETE;
1250
1251 RTSGSEG aReqSegs[] = { { &uSubscribedEvents, sizeof(uSubscribedEvents) }, { &uResponse, sizeof(uResponse) } };
1252 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1253
1254 break;
1255 }
1256 case VIRTIOSCSI_T_AN_SUBSCRIBE: /** Guest SCSI driver is subscribing to async event notification(s) */
1257 {
1258 PVIRTIOSCSI_CTRL_AN_T pScsiCtrlAnSubscribe = (PVIRTIOSCSI_CTRL_AN_T)pScsiCtrl;
1259
1260 if (pScsiCtrlAnSubscribe->uEventsRequested & ~SUBSCRIBABLE_EVENTS)
1261 LogFunc(("Unsupported bits in event subscription event mask: 0x%x\n", pScsiCtrlAnSubscribe->uEventsRequested));
1262
1263 char szTypeText[128];
1264 virtioGetControlAsyncMaskText(szTypeText, sizeof(szTypeText), pScsiCtrlAnSubscribe->uEventsRequested);
1265
1266 Log3Func(("%s, VirtIO LUN: %.8Rhxs\n%*sAsync Subscribe, types: %s\n",
1267 QUEUENAME(qIdx), pScsiCtrlAnSubscribe->uLUN, CBQUEUENAME(qIdx) + 30, "", szTypeText));
1268
1269 uSubscribedEvents &= pScsiCtrlAnSubscribe->uEventsRequested;
1270 pThis->uAsyncEvtsEnabled = uSubscribedEvents;
1271
1272 /**
1273 * TBD: Verify correct status code if request mask is only partially fulfillable
1274 * and confirm when to use 'complete' vs. 'succeeded' See VirtIO 1.0 spec section 5.6.6.2 and read SAM docs*/
1275 if (uSubscribedEvents == pScsiCtrlAnSubscribe->uEventsRequested)
1276 uResponse = VIRTIOSCSI_S_FUNCTION_SUCCEEDED;
1277 else
1278 uResponse = VIRTIOSCSI_S_FUNCTION_COMPLETE;
1279
1280 RTSGSEG aReqSegs[] = { { &uSubscribedEvents, sizeof(uSubscribedEvents) }, { &uResponse, sizeof(uResponse) } };
1281 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1282
1283 break;
1284 }
1285 default:
1286 LogFunc(("Unknown control type extracted from %s: %d\n", QUEUENAME(qIdx), pScsiCtrl->uType));
1287
1288 uResponse = VIRTIOSCSI_S_FAILURE;
1289
1290 RTSGSEG aReqSegs[] = { { &uResponse, sizeof(uResponse) } };
1291 RTSgBufInit(&reqSegBuf, aReqSegs, sizeof(aReqSegs) / sizeof(RTSGSEG));
1292 }
1293
1294 LogFunc(("Response code: %s\n", virtioGetCtrlRespText(uResponse)));
1295 virtioQueuePut (pThis->hVirtio, qIdx, &reqSegBuf, true);
1296 virtioQueueSync(pThis->hVirtio, qIdx);
1297
1298 return VINF_SUCCESS;
1299}
1300
1301/*
1302 * Unblock the worker thread so it can respond to a state change.
1303 *
1304 * @returns VBox status code.
1305 * @param pDevIns The pcnet device instance.
1306 * @param pThread The send thread.
1307 */
1308static DECLCALLBACK(int) virtioScsiR3WorkerWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1309{
1310 RT_NOREF(pThread);
1311 uint16_t qIdx = ((uint64_t)pThread->pvUser) & 0xffff;
1312 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1313 return SUPSemEventSignal(pThis->pSupDrvSession, pThis->aWorker[qIdx].hEvtProcess);
1314}
1315
1316static int virtioScsiWorker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1317{
1318
1319 int rc;
1320 uint16_t qIdx = ((uint64_t)pThread->pvUser) & 0xffff;
1321 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1322 PWORKER pWorker = &pThis->aWorker[qIdx];
1323 PRTSGBUF pInSgBuf;
1324 PRTSGBUF pOutSgBuf;
1325
1326 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
1327 return VINF_SUCCESS;
1328
1329 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
1330 {
1331 if (virtioQueueIsEmpty(pThis->hVirtio, qIdx))
1332 {
1333 /** Interlocks avoid missing alarm while going to sleep & notifier doesn't wake the awoken */
1334 ASMAtomicWriteBool(&pWorker->fSleeping, true);
1335 bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
1336 if (!fNotificationSent)
1337 {
1338 Log3Func(("%s worker sleeping...\n", QUEUENAME(qIdx)));
1339 Assert(ASMAtomicReadBool(&pWorker->fSleeping));
1340 rc = SUPSemEventWaitNoResume(pThis->pSupDrvSession, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
1341 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
1342 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
1343 break;
1344 Log3Func(("%s worker woken\n", QUEUENAME(qIdx)));
1345 ASMAtomicWriteBool(&pWorker->fNotified, false);
1346 }
1347 ASMAtomicWriteBool(&pWorker->fSleeping, false);
1348 }
1349 Log3Func(("fetching next descriptor chain from %s\n", QUEUENAME(qIdx)));
1350 rc = virtioQueueGet(pThis->hVirtio, qIdx, true, &pInSgBuf, &pOutSgBuf);
1351 if (rc == VERR_NOT_AVAILABLE)
1352 {
1353 Log3Func(("Nothing found in %s\n", QUEUENAME(qIdx)));
1354 continue;
1355 }
1356
1357 AssertRC(rc);
1358 if (qIdx == CONTROLQ_IDX)
1359 virtioScsiCtrl(pThis, qIdx, pInSgBuf, pOutSgBuf);
1360 else
1361 {
1362 rc = virtioScsiReqSubmit(pThis, qIdx, pInSgBuf, pOutSgBuf);
1363 if (RT_FAILURE(rc))
1364 {
1365 LogRel(("Fatal error submitting req packet, resetting %Rrc", rc));
1366 /** TBD: MUST AT LEAD RETURN VIRTIO_SCSI_S_FAILURE for all pending I/O, Aborting is an option! */
1367 virtioResetAll(pThis->hVirtio);
1368 }
1369 }
1370 }
1371 return VINF_SUCCESS;
1372}
1373
1374
1375/*static void virtioScsiEventToClient(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1376{ } */
1377
1378
1379/**
1380 * Implementation invokes this to reset the VirtIO device
1381 */
1382static void virtioScsiDeviceReset(PVIRTIOSCSI pThis)
1383{
1384 pThis->virtioScsiConfig.uSenseSize = VIRTIOSCSI_SENSE_SIZE_DEFAULT;
1385 pThis->virtioScsiConfig.uCdbSize = VIRTIOSCSI_CDB_SIZE_DEFAULT;
1386 virtioResetAll(pThis->hVirtio);
1387}
1388
1389static int virtioScsiR3CfgAccessed(PVIRTIOSCSI pThis, uint32_t uOffset,
1390 const void *pv, size_t cb, uint8_t fWrite)
1391{
1392 int rc = VINF_SUCCESS;
1393 if (MATCH_SCSI_CONFIG(uNumQueues))
1394 {
1395 SCSI_CONFIG_ACCESSOR_READONLY(uNumQueues);
1396 }
1397 else
1398 if (MATCH_SCSI_CONFIG(uSegMax))
1399 {
1400 SCSI_CONFIG_ACCESSOR_READONLY(uSegMax);
1401 }
1402 else
1403 if (MATCH_SCSI_CONFIG(uMaxSectors))
1404 {
1405 SCSI_CONFIG_ACCESSOR_READONLY(uMaxSectors);
1406 }
1407 else
1408 if (MATCH_SCSI_CONFIG(uCmdPerLun))
1409 {
1410 SCSI_CONFIG_ACCESSOR_READONLY(uCmdPerLun);
1411 }
1412 else
1413 if (MATCH_SCSI_CONFIG(uEventInfoSize))
1414 {
1415 SCSI_CONFIG_ACCESSOR_READONLY(uEventInfoSize);
1416 }
1417 else
1418 if (MATCH_SCSI_CONFIG(uSenseSize))
1419 {
1420 SCSI_CONFIG_ACCESSOR(uSenseSize);
1421 }
1422 else
1423 if (MATCH_SCSI_CONFIG(uCdbSize))
1424 {
1425 SCSI_CONFIG_ACCESSOR(uCdbSize);
1426 }
1427 else
1428 if (MATCH_SCSI_CONFIG(uMaxChannel))
1429 {
1430 SCSI_CONFIG_ACCESSOR_READONLY(uMaxChannel);
1431 }
1432 else
1433 if (MATCH_SCSI_CONFIG(uMaxTarget))
1434 {
1435 SCSI_CONFIG_ACCESSOR_READONLY(uMaxTarget);
1436 }
1437 else
1438 if (MATCH_SCSI_CONFIG(uMaxLun))
1439 {
1440 SCSI_CONFIG_ACCESSOR_READONLY(uMaxLun);
1441 }
1442 else
1443 {
1444 LogFunc(("Bad access by guest to virtio_scsi_config: uoff=%d, cb=%d\n", uOffset, cb));
1445 rc = VERR_ACCESS_DENIED;
1446 }
1447 return rc;
1448}
1449
1450
1451
1452/**
1453 * virtio-scsi VirtIO Device-specific capabilities read callback
1454 * (other VirtIO capabilities and features are handled in VirtIO implementation)
1455 *
1456 * @param pDevIns The device instance.
1457 * @param uOffset Offset within device specific capabilities struct
1458 * @param pv Buffer in which to save read data
1459 * @param cb Number of bytes to read
1460 */
1461static DECLCALLBACK(int) virtioScsiR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, size_t cb)
1462{
1463 int rc = VINF_SUCCESS;
1464 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1465
1466 rc = virtioScsiR3CfgAccessed(pThis, uOffset, pv, cb, false);
1467
1468 return rc;
1469}
1470
1471/**
1472 * virtio-scsi VirtIO Device-specific capabilities read callback
1473 * (other VirtIO capabilities and features are handled in VirtIO implementation)
1474 *
1475 * @param pDevIns The device instance.
1476 * @param uOffset Offset within device specific capabilities struct
1477 * @param pv Buffer in which to save read data
1478 * @param cb Number of bytes to write
1479 */
1480static DECLCALLBACK(int) virtioScsiR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, size_t cb)
1481{
1482 int rc = VINF_SUCCESS;
1483 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1484
1485 rc = virtioScsiR3CfgAccessed(pThis, uOffset, pv, cb, true);
1486
1487 return rc;
1488}
1489
1490DECLINLINE(void) virtioScsiReportEventsMissed(PVIRTIOSCSI pThis, uint16_t uTarget)
1491{
1492 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_NO_EVENT | VIRTIOSCSI_T_EVENTS_MISSED, 0);
1493}
1494
1495DECLINLINE(void) virtioScsiReportTargetRemoved(PVIRTIOSCSI pThis, uint16_t uTarget)
1496{
1497 if (pThis->fHasHotplug)
1498 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET,
1499 VIRTIOSCSI_EVT_RESET_REMOVED);
1500}
1501
1502DECLINLINE(void) virtioScsiReportTargetAdded(PVIRTIOSCSI pThis, uint16_t uTarget)
1503{
1504 if (pThis->fHasHotplug)
1505 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET,
1506 VIRTIOSCSI_EVT_RESET_RESCAN);
1507}
1508
1509DECLINLINE(void) virtioScsiReportTargetReset(PVIRTIOSCSI pThis, uint16_t uTarget)
1510{
1511 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_TRANSPORT_RESET,
1512 VIRTIOSCSI_EVT_RESET_HARD);
1513}
1514
1515DECLINLINE(void) virtioScsiReportOperChange(PVIRTIOSCSI pThis, uint16_t uTarget)
1516{
1517 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE)
1518 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1519 VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE);
1520}
1521
1522DECLINLINE(void) virtioScsiReportPowerMsg(PVIRTIOSCSI pThis, uint16_t uTarget)
1523{
1524 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_POWER_MGMT)
1525 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1526 VIRTIOSCSI_EVT_ASYNC_POWER_MGMT);
1527}
1528
1529DECLINLINE(void) virtioScsiReportExtReq(PVIRTIOSCSI pThis, uint16_t uTarget)
1530{
1531 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST)
1532 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1533 VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST);
1534}
1535
1536DECLINLINE(void) virtioScsiReportMediaChange(PVIRTIOSCSI pThis, uint16_t uTarget)
1537{
1538 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE)
1539 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1540 VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE);
1541}
1542
1543DECLINLINE(void) virtioScsiReportMultiHost(PVIRTIOSCSI pThis, uint16_t uTarget)
1544{
1545 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_MULTI_HOST)
1546 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1547 VIRTIOSCSI_EVT_ASYNC_MULTI_HOST);
1548}
1549
1550DECLINLINE(void) virtioScsiReportDeviceBusy(PVIRTIOSCSI pThis, uint16_t uTarget)
1551{
1552 if (pThis->uSubscribedEvents & VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY)
1553 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_ASYNC_NOTIFY,
1554 VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY);
1555}
1556
1557
1558DECLINLINE(void) virtioScsiReportParamChange(PVIRTIOSCSI pThis, uint16_t uTarget, uint32_t uSenseCode, uint32_t uSenseQualifier)
1559{
1560 uint32_t uReason = uSenseQualifier << 8 | uSenseCode;
1561 virtioScsiSendEvent(pThis, uTarget, VIRTIOSCSI_T_PARAM_CHANGE, uReason);
1562
1563}
1564
1565static DECLCALLBACK(void) virtioScsiNotified(VIRTIOHANDLE hVirtio, void *pClient, uint16_t qIdx)
1566{
1567 RT_NOREF(hVirtio);
1568
1569 AssertReturnVoid(qIdx < VIRTIOSCSI_QUEUE_CNT);
1570 PVIRTIOSCSI pThis = (PVIRTIOSCSI)pClient;
1571 PWORKER pWorker = &pThis->aWorker[qIdx];
1572
1573
1574 RTLogFlush(RTLogDefaultInstanceEx(RT_MAKE_U32(0, UINT16_MAX)));
1575
1576 if (qIdx == CONTROLQ_IDX || IS_REQ_QUEUE(qIdx))
1577 {
1578 Log3Func(("%s has available data\n", QUEUENAME(qIdx)));
1579 /** Wake queue's worker thread up if sleeping */
1580 if (!ASMAtomicXchgBool(&pWorker->fNotified, true))
1581 {
1582 if (ASMAtomicReadBool(&pWorker->fSleeping))
1583 {
1584 Log3Func(("waking %s worker.\n", QUEUENAME(qIdx)));
1585 int rc = SUPSemEventSignal(pThis->pSupDrvSession, pWorker->hEvtProcess);
1586 AssertRC(rc);
1587 }
1588 }
1589 }
1590 else if (qIdx == EVENTQ_IDX)
1591 {
1592 Log3Func(("Driver queued buffer(s) to %s\n"));
1593 if (ASMAtomicXchgBool(&pThis->fEventsMissed, false))
1594 virtioScsiReportEventsMissed(pThis, 0);
1595 }
1596 else
1597 LogFunc(("Unexpected queue idx (ignoring): %d\n", qIdx));
1598}
1599
1600static DECLCALLBACK(void) virtioScsiStatusChanged(VIRTIOHANDLE hVirtio, void *pClient, bool fVirtioReady)
1601{
1602 RT_NOREF(hVirtio);
1603 PVIRTIOSCSI pThis = (PVIRTIOSCSI)pClient;
1604 pThis->fVirtioReady = fVirtioReady;
1605 if (fVirtioReady)
1606 {
1607 LogFunc(("VirtIO ready\n-----------------------------------------------------------------------------------------\n"));
1608 uint64_t features = virtioGetNegotiatedFeatures(hVirtio);
1609 pThis->fHasT10pi = features & VIRTIO_SCSI_F_T10_PI;
1610 pThis->fHasHotplug = features & VIRTIO_SCSI_F_HOTPLUG;
1611 pThis->fHasInOutBufs = features & VIRTIO_SCSI_F_INOUT;
1612 pThis->fHasLunChange = features & VIRTIO_SCSI_F_CHANGE;
1613 }
1614 else
1615 {
1616 LogFunc(("VirtIO is resetting\n"));
1617 for (int i = 0; i < VIRTIOSCSI_QUEUE_CNT; i++)
1618 pThis->fQueueAttached[i] = false;
1619 }
1620}
1621
1622/**
1623 * Turns on/off the write status LED.
1624 *
1625 * @param pTarget Pointer to the target device
1626 * @param fOn New LED state.
1627 */
1628void virtioScsiSetWriteLed(PVIRTIOSCSITARGET pTarget, bool fOn)
1629{
1630 LogFlow(("%s virtioSetWriteLed: %s\n", pTarget->pszLunName, fOn ? "on" : "off"));
1631 if (fOn)
1632 pTarget->led.Asserted.s.fWriting = pTarget->led.Actual.s.fWriting = 1;
1633 else
1634 pTarget->led.Actual.s.fWriting = fOn;
1635}
1636
1637/**
1638 * Turns on/off the read status LED.
1639 *
1640 * @param pTarget Pointer to the device state structure.
1641 * @param fOn New LED state.
1642 */
1643void virtioScsiSetReadLed(PVIRTIOSCSITARGET pTarget, bool fOn)
1644{
1645 LogFlow(("%s virtioSetReadLed: %s\n", pTarget->pszLunName, fOn ? "on" : "off"));
1646 if (fOn)
1647 pTarget->led.Asserted.s.fReading = pTarget->led.Actual.s.fReading = 1;
1648 else
1649 pTarget->led.Actual.s.fReading = fOn;
1650}
1651
1652/**
1653 * virtio-scsi debugger info callback.
1654 *
1655 * @param pDevIns The device instance.
1656 * @param pHlp The output helpers.
1657 * @param pszArgs The arguments.
1658 */
1659static DECLCALLBACK(void) virtioScsiR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1660{
1661 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1662 bool fVerbose = false;
1663
1664 /* Parse arguments. */
1665 if (pszArgs)
1666 fVerbose = strstr(pszArgs, "verbose") != NULL;
1667
1668 /* Show basic information. */
1669 pHlp->pfnPrintf(pHlp, "%s#%d: virtio-scsci ",
1670 pDevIns->pReg->szName,
1671 pDevIns->iInstance);
1672 pHlp->pfnPrintf(pHlp, "numTargets=%lu", pThis->cTargets);
1673}
1674
1675/**
1676 * @interface_method_impl{PDMIMEDIAEXPORT,pfnMediumEjected}
1677 */
1678static DECLCALLBACK(void) virtioScsiR3MediumEjected(PPDMIMEDIAEXPORT pInterface)
1679{
1680 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
1681 PVIRTIOSCSI pThis = pTarget->CTX_SUFF(pVirtioScsi);
1682 LogFunc(("LUN %d Ejected!\n", pTarget->iLUN));
1683 if (pThis->pMediaNotify)
1684 virtioScsiSetWriteLed(pTarget, false);
1685}
1686
1687/** @callback_method_impl{FNSSMDEVLIVEEXEC} */
1688static DECLCALLBACK(int) virtioScsiR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1689{
1690 LogFunc(("callback"));
1691 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1692 RT_NOREF(pThis);
1693 RT_NOREF(uPass);
1694 RT_NOREF(pSSM);
1695 return VINF_SSM_DONT_CALL_AGAIN;
1696}
1697
1698/** @callback_method_impl{FNSSMDEVLOADEXEC} */
1699static DECLCALLBACK(int) virtioScsiR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1700{
1701 LogFunc(("callback"));
1702 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1703 RT_NOREF(pThis);
1704 RT_NOREF(uPass);
1705 RT_NOREF(pSSM);
1706 RT_NOREF(uVersion);
1707 return VINF_SSM_DONT_CALL_AGAIN;
1708}
1709
1710/** @callback_method_impl{FNSSMDEVSAVEEXEC} */
1711static DECLCALLBACK(int) virtioScsiR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1712{
1713 LogFunc(("callback"));
1714 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1715
1716 RT_NOREF(pThis);
1717 RT_NOREF(pSSM);
1718 return VINF_SUCCESS;
1719}
1720
1721/** @callback_method_impl{FNSSMDEVLOADDONE} */
1722static DECLCALLBACK(int) virtioScsiR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1723{
1724 LogFunc(("callback"));
1725 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1726 RT_NOREF(pThis);
1727 RT_NOREF(pSSM);
1728 return VINF_SUCCESS;
1729}
1730
1731
1732/**
1733 * Callback employed by virtioScsiR3PDMReset.
1734 *
1735 * @returns true if we've quiesced, false if we're still working.
1736 * @param pDevIns The device instance.
1737 */
1738static DECLCALLBACK(bool) virtioScsiR3IsAsyncResetDone(PPDMDEVINS pDevIns)
1739{
1740 RT_NOREF(pDevIns);
1741 Log(("\n"));
1742
1743 return true;
1744}
1745
1746/**
1747 * @copydoc FNPDMDEVRESET
1748 */
1749static DECLCALLBACK(void) virtioScsiR3PDMReset(PPDMDEVINS pDevIns)
1750{
1751 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1752 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
1753
1754 bool fIoInProgress = false;
1755 for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aTargetInstances); i++)
1756 {
1757 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[i];
1758 if (pTarget->cReqsInProgress != 0)
1759 {
1760 fIoInProgress = true;
1761 break;
1762 }
1763 }
1764 if (!fIoInProgress)
1765 PDMDevHlpSetAsyncNotification(pDevIns, virtioScsiR3IsAsyncResetDone);
1766 else
1767 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
1768
1769 virtioScsiDeviceReset(pThis);
1770}
1771
1772/**
1773 * Device relocation callback.
1774 *
1775 * When this callback is called the device instance data, and if the
1776 * device have a GC component, is being relocated, or/and the selectors
1777 * have been changed. The device must use the chance to perform the
1778 * necessary pointer relocations and data updates.
1779 *
1780 * Before the GC code is executed the first time, this function will be
1781 * called with a 0 delta so GC pointer calculations can be one in one place.
1782 *
1783 * @param pDevIns Pointer to the device instance.
1784 * @param offDelta The relocation delta relative to the old location.
1785 *
1786 * @remark A relocation CANNOT fail.
1787 */
1788static DECLCALLBACK(void) virtioScsiR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1789{
1790 LogFunc(("Relocating virtio-scsi"));
1791 RT_NOREF(offDelta);
1792 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1793
1794 pThis->pDevInsR3 = pDevIns;
1795
1796 for (uint32_t i = 0; i < VIRTIOSCSI_MAX_TARGETS; i++)
1797 {
1798 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[i];
1799 pTarget->pVirtioScsiR3 = pThis;;
1800 }
1801
1802 /**
1803 * Important: Forward to virtio framework!
1804 */
1805 virtioRelocate(pDevIns, offDelta);
1806
1807}
1808
1809static DECLCALLBACK(int) virtioScsiR3QueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
1810 uint32_t *piInstance, uint32_t *piLUN)
1811{
1812 PVIRTIOSCSITARGET pThisTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaPort);
1813 PPDMDEVINS pDevIns = pThisTarget->CTX_SUFF(pVirtioScsi)->CTX_SUFF(pDevIns);
1814
1815 AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
1816 AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
1817 AssertPtrReturn(piLUN, VERR_INVALID_POINTER);
1818
1819 *ppcszController = pDevIns->pReg->szName;
1820 *piInstance = pDevIns->iInstance;
1821 *piLUN = pThisTarget->iLUN;
1822
1823 return VINF_SUCCESS;
1824}
1825
1826/**
1827 * Gets the pointer to the status LED of a unit.
1828 *
1829 * @returns VBox status code.
1830 * @param pInterface Pointer to the interface structure containing the called function pointer.
1831 * @param iLUN The unit which status LED we desire.
1832 * @param ppLed Where to store the LED pointer.
1833 */
1834static DECLCALLBACK(int) virtioScsiR3TargetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
1835{
1836 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, ILed);
1837 if (iLUN == 0)
1838 {
1839 *ppLed = &pTarget->led;
1840 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
1841 return VINF_SUCCESS;
1842 }
1843 return VERR_PDM_LUN_NOT_FOUND;
1844 }
1845
1846
1847/**
1848 * Gets the pointer to the status LED of a unit.
1849 *
1850 * @returns VBox status code.
1851 * @param pInterface Pointer to the interface structure containing the called function pointer.
1852 * @param iLUN The unit which status LED we desire.
1853 * @param ppLed Where to store the LED pointer.
1854 */
1855static DECLCALLBACK(int) virtioScsiR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
1856{
1857 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, ILeds);
1858 if (iLUN < pThis->cTargets)
1859 {
1860 *ppLed = &pThis->aTargetInstances[iLUN].led;
1861 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
1862 return VINF_SUCCESS;
1863 }
1864 return VERR_PDM_LUN_NOT_FOUND;
1865}
1866
1867
1868/**
1869 * Memory mapped I/O Handler for read operations.
1870 *
1871 * @returns VBox status code.
1872 *
1873 * @param pDevIns The device instance.
1874 * @param pvUser User argument.
1875 * @param GCPhysAddr Physical address (in GC) where the read starts.
1876 * @param pv Where to store the result.
1877 * @param cb Number of bytes read.
1878 */
1879PDMBOTHCBDECL(int) virtioScsiMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
1880{
1881 RT_NOREF_PV(pDevIns); RT_NOREF_PV(pvUser); RT_NOREF_PV(GCPhysAddr); RT_NOREF_PV(pv); RT_NOREF_PV(cb);
1882 LogFunc(("Read from MMIO area\n"));
1883 return VINF_SUCCESS;
1884}
1885
1886/**
1887 * Memory mapped I/O Handler for write operations.
1888 *
1889 * @returns VBox status code.
1890 *
1891 * @param pDevIns The device instance.
1892 * @param pvUser User argument.
1893 * @param GCPhysAddr Physical address (in GC) where the read starts.
1894 * @param pv Where to fetch the result.
1895 * @param cb Number of bytes to write.
1896 */
1897PDMBOTHCBDECL(int) virtioScsiMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
1898{
1899 RT_NOREF_PV(pDevIns); RT_NOREF_PV(pvUser); RT_NOREF_PV(GCPhysAddr); RT_NOREF_PV(pv); RT_NOREF_PV(cb);
1900 LogFunc(("Write to MMIO area\n"));
1901 return VINF_SUCCESS;
1902}
1903
1904/**
1905 * @callback_method_impl{FNPCIIOREGIONMAP}
1906 */
1907static DECLCALLBACK(int) virtioScsiR3Map(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
1908 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
1909{
1910 RT_NOREF(pPciDev, iRegion);
1911 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1912 int rc = VINF_SUCCESS;
1913
1914 Assert(cb >= 32);
1915
1916 switch (iRegion)
1917 {
1918 case 0:
1919 LogFunc(("virtio-scsi MMIO mapped at GCPhysAddr=%RGp cb=%RGp\n", GCPhysAddress, cb));
1920
1921 /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
1922 rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
1923 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
1924 virtioScsiMMIOWrite, virtioScsiMMIORead,
1925 "virtio-scsi MMIO");
1926 pThis->GCPhysMMIOBase = RT_SUCCESS(rc) ? GCPhysAddress : 0;
1927 return rc;
1928 case 1:
1929 /* VirtIO 1.0 doesn't uses Port I/O (Virtio 0.95 e.g. "legacy", does) */
1930 AssertMsgFailed(("virtio-scsi: Port I/O not supported by this Host SCSI device\n"));
1931 default:
1932 AssertMsgFailed(("Invalid enmType=%d\n", enmType));
1933 }
1934 return VERR_GENERAL_FAILURE; /* Should never get here */
1935}
1936
1937/**
1938 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1939 */
1940static DECLCALLBACK(void *) virtioScsiR3TargetQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1941{
1942 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IBase);
1943 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pTarget->IBase);
1944 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pTarget->IMediaPort);
1945 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pTarget->IMediaExPort);
1946 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pTarget->ILed);
1947 return NULL;
1948}
1949
1950/**
1951 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1952 */
1953static DECLCALLBACK(void *) virtioScsiR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1954{
1955 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, IBase);
1956
1957 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
1958 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
1959
1960 return NULL;
1961}
1962
1963/**
1964 * Detach notification.
1965 *
1966 * One harddisk at one port has been unplugged.
1967 * The VM is suspended at this point.
1968 *
1969 * @param pDevIns The device instance.
1970 * @param iLUN The logical unit which is being detached.
1971 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1972 */
1973static DECLCALLBACK(void) virtioScsiR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1974{
1975 RT_NOREF(fFlags);
1976 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1977 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[iLUN];
1978
1979 LogFunc((""));
1980
1981 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
1982 ("virtio-scsi: Device does not support hotplugging\n"));
1983
1984 /*
1985 * Zero some important members.
1986 */
1987 pTarget->fPresent = false;
1988 pTarget->pDrvBase = NULL;
1989}
1990
1991/**
1992 * Attach command.
1993 *
1994 * This is called when we change block driver.
1995 *
1996 * @returns VBox status code.
1997 * @param pDevIns The device instance.
1998 * @param iLUN The logical unit which is being detached.
1999 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2000 */
2001static DECLCALLBACK(int) virtioScsiR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2002{
2003 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2004 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[iLUN];
2005 int rc;
2006
2007 pThis->pDevInsR3 = pDevIns;
2008 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
2009 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
2010
2011 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2012 ("virtio-scsi: Device does not support hotplugging\n"),
2013 VERR_INVALID_PARAMETER);
2014
2015 /* the usual paranoia */
2016 AssertRelease(!pTarget->pDrvBase);
2017 Assert(pTarget->iLUN == iLUN);
2018
2019 /*
2020 * Try attach the SCSI driver and get the interfaces,
2021 * required as well as optional.
2022 */
2023 rc = PDMDevHlpDriverAttach(pDevIns, pTarget->iLUN, &pDevIns->IBase,
2024 &pTarget->pDrvBase, (const char *)&pTarget->pszLunName);
2025 if (RT_SUCCESS(rc))
2026 pTarget->fPresent = true;
2027 else
2028 AssertMsgFailed(("Failed to attach %s. rc=%Rrc\n", pTarget->pszLunName, rc));
2029
2030 if (RT_FAILURE(rc))
2031 {
2032 pTarget->fPresent = false;
2033 pTarget->pDrvBase = NULL;
2034 }
2035 return rc;
2036}
2037
2038static DECLCALLBACK(int) virtioScsiDestruct(PPDMDEVINS pDevIns)
2039{
2040 /*
2041 * Check the versions here as well since the destructor is *always* called.
2042 */
2043
2044 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
2045
2046 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2047
2048 for (int qIdx = 0; qIdx < VIRTQ_MAX_CNT; qIdx++)
2049 {
2050 PWORKER pWorker = &pThis->aWorker[qIdx];
2051 if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
2052 {
2053 SUPSemEventClose(pThis->pSupDrvSession, pWorker->hEvtProcess);
2054 pWorker->hEvtProcess = NIL_SUPSEMEVENT;
2055 }
2056 }
2057 return VINF_SUCCESS;
2058}
2059
2060static DECLCALLBACK(int) virtioScsiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg){
2061
2062 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2063
2064 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
2065 int rc = VINF_SUCCESS;
2066
2067 pThis->pDevInsR3 = pDevIns;
2068 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
2069 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
2070 pThis->pSupDrvSession = PDMDevHlpGetSupDrvSession(pDevIns);
2071
2072 LogFunc(("PDM device instance: %d\n", iInstance));
2073 RTStrPrintf((char *)pThis->szInstance, sizeof(pThis->szInstance), "VIRTIOSCSI%d", iInstance);
2074
2075 /*
2076 * Validate and read configuration.
2077 */
2078 if (!CFGMR3AreValuesValid(pCfg,"NumTargets\0"
2079 "Bootable\0"
2080 /* "GCEnabled\0" TBD */
2081 /* "R0Enabled\0" TBD */
2082 ))
2083 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
2084 N_("virtio-scsi configuration error: unknown option specified"));
2085
2086 rc = CFGMR3QueryIntegerDef(pCfg, "NumTargets", &pThis->cTargets, true);
2087 if (RT_FAILURE(rc))
2088 return PDMDEV_SET_ERROR(pDevIns, rc,
2089 N_("virtio-scsi configuration error: failed to read NumTargets as integer"));
2090 LogFunc(("NumTargets=%d\n", pThis->cTargets));
2091
2092 rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &pThis->fBootable, true);
2093 if (RT_FAILURE(rc))
2094 return PDMDEV_SET_ERROR(pDevIns, rc,
2095 N_("virtio-scsi configuration error: failed to read Bootable as boolean"));
2096 LogFunc(("Bootable=%RTbool (unimplemented)\n", pThis->fBootable));
2097
2098 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, false);
2099 if (RT_FAILURE(rc))
2100 return PDMDEV_SET_ERROR(pDevIns, rc,
2101 N_("virtio-scsi configuration error: failed to read R0Enabled as boolean"));
2102
2103 rc = CFGMR3QueryBoolDef(pCfg, "RCEnabled", &pThis->fRCEnabled, false);
2104 if (RT_FAILURE(rc))
2105 return PDMDEV_SET_ERROR(pDevIns, rc,
2106 N_("virtio-scsi configuration error: failed to read RCEnabled as boolean"));
2107
2108 VIRTIOPCIPARAMS virtioPciParams, *pVirtioPciParams = &virtioPciParams;
2109 pVirtioPciParams->uDeviceId = PCI_DEVICE_ID_VIRTIOSCSI_HOST;
2110 pVirtioPciParams->uClassBase = PCI_CLASS_BASE_MASS_STORAGE;
2111 pVirtioPciParams->uClassSub = PCI_CLASS_SUB_SCSI_STORAGE_CONTROLLER;
2112 pVirtioPciParams->uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
2113 pVirtioPciParams->uSubsystemId = PCI_DEVICE_ID_VIRTIOSCSI_HOST; /* Virtio 1.0 spec allows PCI Device ID here */
2114 pVirtioPciParams->uInterruptLine = 0x00;
2115 pVirtioPciParams->uInterruptPin = 0x01;
2116
2117 pThis->IBase.pfnQueryInterface = virtioScsiR3DeviceQueryInterface;
2118
2119 /* Configure virtio_scsi_config that transacts via VirtIO implementation's Dev. Specific Cap callbacks */
2120 pThis->virtioScsiConfig.uNumQueues = VIRTIOSCSI_REQ_QUEUE_CNT;
2121 pThis->virtioScsiConfig.uSegMax = VIRTIOSCSI_MAX_SEG_COUNT;
2122 pThis->virtioScsiConfig.uMaxSectors = VIRTIOSCSI_MAX_SECTORS_HINT;
2123 pThis->virtioScsiConfig.uCmdPerLun = VIRTIOSCSI_MAX_COMMANDS_PER_LUN;
2124 pThis->virtioScsiConfig.uEventInfoSize = sizeof(VIRTIOSCSI_EVENT_T); /* Spec says at least this size! */
2125 pThis->virtioScsiConfig.uSenseSize = VIRTIOSCSI_SENSE_SIZE_DEFAULT;
2126 pThis->virtioScsiConfig.uCdbSize = VIRTIOSCSI_CDB_SIZE_DEFAULT;
2127 pThis->virtioScsiConfig.uMaxChannel = VIRTIOSCSI_MAX_CHANNEL_HINT;
2128 pThis->virtioScsiConfig.uMaxTarget = pThis->cTargets;
2129 pThis->virtioScsiConfig.uMaxLun = VIRTIOSCSI_MAX_LUN;
2130
2131 rc = virtioConstruct(pDevIns, pThis, &pThis->hVirtio, pVirtioPciParams, pThis->szInstance,
2132 VIRTIOSCSI_HOST_SCSI_FEATURES_OFFERED,
2133 virtioScsiR3DevCapRead,
2134 virtioScsiR3DevCapWrite,
2135 virtioScsiStatusChanged,
2136 virtioScsiNotified,
2137 virtioScsiR3LiveExec,
2138 virtioScsiR3SaveExec,
2139 virtioScsiR3LoadExec,
2140 virtioScsiR3LoadDone,
2141 sizeof(VIRTIOSCSI_CONFIG_T) /* cbDevSpecificCap */,
2142 (void *)&pThis->virtioScsiConfig /* pDevSpecificCap */);
2143
2144 if (RT_FAILURE(rc))
2145 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi: failed to initialize VirtIO"));
2146
2147
2148 RTStrCopy((char *)pThis->szQueueNames[CONTROLQ_IDX], VIRTIO_MAX_QUEUE_NAME_SIZE, "controlq");
2149 RTStrCopy((char *)pThis->szQueueNames[EVENTQ_IDX], VIRTIO_MAX_QUEUE_NAME_SIZE, "eventq");
2150 for (uint16_t qIdx = VIRTQ_REQ_BASE; qIdx < VIRTQ_REQ_BASE + VIRTIOSCSI_REQ_QUEUE_CNT; qIdx++)
2151 RTStrPrintf((char *)pThis->szQueueNames[qIdx], VIRTIO_MAX_QUEUE_NAME_SIZE,
2152 "requestq<%d>", qIdx - VIRTQ_REQ_BASE);
2153
2154 /**
2155 * Create one worker per incoming-work-related queue (eventq is outgoing status to guest,
2156 * wherein guest is supposed to keep the queue loaded-up with buffer vectors the host
2157 * can quickly fill-in send back). Should be light-duty and fast enough to be handled on
2158 * requestq or controlq thread. The Linux virtio_scsi driver limits the number of request
2159 * queues to MIN(<# Guest CPUs>, <Device's req queue max>), so queue count is ultimately
2160 * constrained from host side at negotiation time and initialization and later through
2161 * bounds-checking.
2162 */
2163 for (uint16_t qIdx = 0; qIdx < VIRTIOSCSI_QUEUE_CNT; qIdx++)
2164 {
2165 rc = virtioQueueAttach(pThis->hVirtio, qIdx, QUEUENAME(qIdx));
2166 AssertMsgReturn(rc == VINF_SUCCESS, ("Failed to attach queue %s\n", QUEUENAME(qIdx)), rc);
2167 pThis->fQueueAttached[qIdx] = (rc == VINF_SUCCESS);
2168
2169 if (qIdx == CONTROLQ_IDX || IS_REQ_QUEUE(qIdx))
2170 {
2171 rc = PDMDevHlpThreadCreate(pDevIns, &pThis->aWorker[qIdx].pThread,
2172 (void *)(uint64_t)qIdx, virtioScsiWorker,
2173 virtioScsiR3WorkerWakeUp, 0, RTTHREADTYPE_IO, QUEUENAME(qIdx));
2174 if (rc != VINF_SUCCESS)
2175 {
2176 LogRel(("Error creating thread for Virtual Queue %s\n", QUEUENAME(qIdx)));
2177 return rc;
2178 }
2179
2180 rc = SUPSemEventCreate(pThis->pSupDrvSession, &pThis->aWorker[qIdx].hEvtProcess);
2181 if (RT_FAILURE(rc))
2182 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
2183 N_("LsiLogic: Failed to create SUP event semaphore"));
2184 }
2185 }
2186
2187 rc = PDMDevHlpPCIIORegionRegister(pDevIns, VIRTIOSCSI_REGION_MEM_IO, 32,
2188 PCI_ADDRESS_SPACE_MEM, virtioScsiR3Map);
2189 if (RT_FAILURE(rc))
2190 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi: cannot register PCI mmio address space"));
2191
2192#ifdef BOOTABLE_SUPPORT_TBD
2193 if (fBootable)
2194 {
2195 /* Register I/O port space for BIOS access. */
2196 rc = PDMDevHlpIOPortRegister(pDevIns, VIRTIOSCSI_BIOS_IO_PORT, 4, NULL,
2197 virtioScsiR3BiosIoPortWrite, virtioScsiR3BiosIoPortRead,
2198 virtioScsiR3BiosIoPortWriteStr, virtioScsiR3BiosIoPortReadStr,
2199 "virtio-scsi BIOS");
2200 if (RT_FAILURE(rc))
2201 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi cannot register BIOS I/O handlers"));
2202 }
2203#endif
2204
2205 /* Initialize per device instance. */
2206 for (RTUINT iLUN = 0; iLUN < VIRTIOSCSI_MAX_TARGETS; iLUN++)
2207 {
2208 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[iLUN];
2209
2210 if (RTStrAPrintf(&pTarget->pszLunName, "VSCSILUN%u", iLUN) < 0)
2211 AssertLogRelFailedReturn(VERR_NO_MEMORY);
2212
2213 /* Initialize static parts of the device. */
2214 pTarget->iLUN = iLUN;
2215 pTarget->pVirtioScsiR3 = pThis;
2216
2217 pTarget->IBase.pfnQueryInterface = virtioScsiR3TargetQueryInterface;
2218
2219 /* IMediaPort and IMediaExPort interfaces provide callbacks for VD media and downstream driver access */
2220 pTarget->IMediaPort.pfnQueryDeviceLocation = virtioScsiR3QueryDeviceLocation;
2221 pTarget->IMediaExPort.pfnIoReqCompleteNotify = virtioScsiR3IoReqCompleteNotify;
2222 pTarget->IMediaExPort.pfnIoReqCopyFromBuf = virtioScsiR3IoReqCopyFromBuf;
2223 pTarget->IMediaExPort.pfnIoReqCopyToBuf = virtioScsiR3IoReqCopyToBuf;
2224 pTarget->IMediaExPort.pfnIoReqStateChanged = virtioScsiR3IoReqStateChanged;
2225 pTarget->IMediaExPort.pfnMediumEjected = virtioScsiR3MediumEjected;
2226 pTarget->IMediaExPort.pfnIoReqQueryBuf = NULL; /* When used avoids copyFromBuf CopyToBuf*/
2227 pTarget->IMediaExPort.pfnIoReqQueryDiscardRanges = NULL;
2228
2229
2230 pTarget->IBase.pfnQueryInterface = virtioScsiR3TargetQueryInterface;
2231 pTarget->ILed.pfnQueryStatusLed = virtioScsiR3TargetQueryStatusLed;
2232 pThis->ILeds.pfnQueryStatusLed = virtioScsiR3DeviceQueryStatusLed;
2233 pTarget->led.u32Magic = PDMLED_MAGIC;
2234
2235 LogFunc(("Attaching LUN: %s\n", pTarget->pszLunName));
2236
2237 AssertReturn(iLUN < RT_ELEMENTS(pThis->aTargetInstances), VERR_PDM_NO_SUCH_LUN);
2238 rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pTarget->IBase, &pTarget->pDrvBase, (const char *)&pTarget->pszLunName);
2239 if (RT_SUCCESS(rc))
2240 {
2241 pTarget->fPresent = true;
2242
2243 /* DrvSCSI.cpp currently implements the IMedia and IMediaEx interfaces, so those
2244 * are the interfaces that will be used to pass SCSI requests down to the
2245 * DrvSCSI driver to eventually make it down to the VD layer */
2246 pTarget->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIA);
2247 AssertMsgReturn(VALID_PTR(pTarget->pDrvMedia),
2248 ("virtio-scsi configuration error: LUN#%d missing basic media interface!\n", pTarget->iLUN),
2249 VERR_PDM_MISSING_INTERFACE);
2250
2251 /* Get the extended media interface. */
2252 pTarget->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIAEX);
2253 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
2254 ("virtio-scsi configuration error: LUN#%d missing extended media interface!\n", pTarget->iLUN),
2255 VERR_PDM_MISSING_INTERFACE);
2256
2257 rc = pTarget->pDrvMediaEx->pfnIoReqAllocSizeSet(pTarget->pDrvMediaEx, sizeof(VIRTIOSCSIREQ));
2258 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
2259 ("virtio-scsi configuration error: LUN#%u: Failed to set I/O request size!\n", pTarget->iLUN),
2260 rc);
2261
2262 }
2263 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2264 {
2265 pTarget->fPresent = false;
2266 pTarget->pDrvBase = NULL;
2267 rc = VINF_SUCCESS;
2268 Log(("virtio-scsi: no driver attached to device %s\n", pTarget->pszLunName));
2269 }
2270 else
2271 {
2272 AssertLogRelMsgFailed(("virtio-scsi: Failed to attach %s\n", pTarget->pszLunName));
2273 return rc;
2274 }
2275 }
2276
2277 /* Status driver */
2278 PPDMIBASE pUpBase;
2279 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pUpBase, "Status Port");
2280 if (RT_FAILURE(rc))
2281 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
2282
2283 /*
2284 * Register the debugger info callback.
2285 */
2286 char szTmp[128];
2287 RTStrPrintf(szTmp, sizeof(szTmp), "%s%d", pDevIns->pReg->szName, pDevIns->iInstance);
2288 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "virtio-scsi info", virtioScsiR3Info);
2289
2290 return rc;
2291}
2292
2293/**
2294 * The device registration structure.
2295 */
2296const PDMDEVREG g_DeviceVirtioSCSI =
2297{
2298 /* .u32Version = */ PDM_DEVREG_VERSION,
2299 /* .uReserved0 = */ 0,
2300 /* .szName = */ "virtio-scsi",
2301#ifdef VIRTIOSCSI_GC_SUPPORT
2302 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
2303#else
2304 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS,
2305#endif
2306 /* .fClass = */ PDM_DEVREG_CLASS_MISC,
2307 /* .cMaxInstances = */ ~0U,
2308 /* .uSharedVersion = */ 42,
2309 /* .cbInstanceShared = */ sizeof(VIRTIOSCSI),
2310 /* .cbInstanceCC = */ 0,
2311 /* .cbInstanceRC = */ 0,
2312 /* .uReserved1 = */ 0,
2313 /* .pszDescription = */ "Virtio Host SCSI.\n",
2314#if defined(IN_RING3)
2315 /* .pszRCMod = */ "VBoxDDRC.rc",
2316 /* .pszR0Mod = */ "VBoxDDR0.r0",
2317 /* .pfnConstruct = */ virtioScsiConstruct,
2318 /* .pfnDestruct = */ virtioScsiDestruct,
2319 /* .pfnRelocate = */ virtioScsiR3Relocate,
2320 /* .pfnMemSetup = */ NULL,
2321 /* .pfnPowerOn = */ NULL,
2322 /* .pfnReset = */ virtioScsiR3PDMReset,
2323 /* .pfnSuspend = */ NULL,
2324 /* .pfnResume = */ NULL,
2325 /* .pfnAttach = */ virtioScsiR3Attach,
2326 /* .pfnDetach = */ virtioScsiR3Detach,
2327 /* .pfnQueryInterface = */ NULL,
2328 /* .pfnInitComplete = */ NULL,
2329 /* .pfnPowerOff = */ NULL,
2330 /* .pfnSoftReset = */ NULL,
2331 /* .pfnReserved0 = */ NULL,
2332 /* .pfnReserved1 = */ NULL,
2333 /* .pfnReserved2 = */ NULL,
2334 /* .pfnReserved3 = */ NULL,
2335 /* .pfnReserved4 = */ NULL,
2336 /* .pfnReserved5 = */ NULL,
2337 /* .pfnReserved6 = */ NULL,
2338 /* .pfnReserved7 = */ NULL,
2339#elif defined(IN_RING0)
2340 /* .pfnEarlyConstruct = */ NULL,
2341 /* .pfnConstruct = */ NULL,
2342 /* .pfnDestruct = */ NULL,
2343 /* .pfnFinalDestruct = */ NULL,
2344 /* .pfnRequest = */ NULL,
2345 /* .pfnReserved0 = */ NULL,
2346 /* .pfnReserved1 = */ NULL,
2347 /* .pfnReserved2 = */ NULL,
2348 /* .pfnReserved3 = */ NULL,
2349 /* .pfnReserved4 = */ NULL,
2350 /* .pfnReserved5 = */ NULL,
2351 /* .pfnReserved6 = */ NULL,
2352 /* .pfnReserved7 = */ NULL,
2353#elif defined(IN_RC)
2354 /* .pfnConstruct = */ NULL,
2355 /* .pfnReserved0 = */ NULL,
2356 /* .pfnReserved1 = */ NULL,
2357 /* .pfnReserved2 = */ NULL,
2358 /* .pfnReserved3 = */ NULL,
2359 /* .pfnReserved4 = */ NULL,
2360 /* .pfnReserved5 = */ NULL,
2361 /* .pfnReserved6 = */ NULL,
2362 /* .pfnReserved7 = */ NULL,
2363#else
2364# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2365#endif
2366 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2367};
2368
Note: See TracBrowser for help on using the repository browser.

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