VirtualBox

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

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

Storage/DevVirtioSCSI.cpp: DrvSCSI (VSCSI) seems to return status information regardless of whether there was a problem or not. Linux considers that an error. See bugref:9440, Comment #60

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