VirtualBox

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

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

storage/DevVirtioSCSI.cpp, major changes that flesh out most of the functionality of the VirtIO 1.0 implementation (not the Host SCSI dev specific implmentation). See #9440, Comment #45 for more information

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.0 KB
Line 
1/* $Id: DevVirtioSCSI.cpp 80219 2019-08-12 07:23:05Z vboxsync $ $Revision: 80219 $ $Date: 2019-08-12 07:23:05 +0000 (Mon, 12 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
25#include <VBox/vmm/pdmdev.h>
26#include <VBox/vmm/pdmstorageifs.h>
27#include <VBox/vmm/pdmcritsect.h>
28#include <VBox/version.h>
29#include <iprt/errcore.h>
30#include <VBox/log.h>
31#include <iprt/assert.h>
32#include <iprt/string.h>
33#include "../build/VBoxDD.h"
34#include <VBox/scsi.h>
35#ifdef IN_RING3
36# include <iprt/alloc.h>
37# include <iprt/memcache.h>
38# include <iprt/param.h>
39# include <iprt/uuid.h>
40#endif
41#include "../VirtIO/Virtio_1_0.h"
42
43#include "VBoxSCSI.h"
44#include "VBoxDD.h"
45
46#define VIRTIOSCSI_MAX_TARGETS 1
47#define VIRTIOSCSI_SAVED_STATE_MINOR_VERSION 0x01
48#define REQ_ALLOC_SIZE 1024 /* TBD */
49
50#define PCI_VENDOR_ID_VIRTIO 0x1AF4
51#define PCI_DEVICE_ID_VIRTIOSCSI_HOST 0x1048
52#define PCI_CLASS_BASE_MASS_STORAGE 0x01
53#define PCI_CLASS_SUB_SCSI_STORAGE_CONTROLLER 0x00
54#define PCI_CLASS_PROG_UNSPECIFIED 0x00
55#define VIRTIOSCSI_PCI_CLASS 0x01 /* Base class Mass Storage? */
56#define VIRTIOSCSI_N_QUEUES 3 /* Control, Event, Request */
57#define VIRTIOSCSI_REGION_MEM_IO 0
58#define VIRTIOSCSI_REGION_PORT_IO 1
59#define VIRTIOSCSI_REGION_PCI_CAP 2
60
61/**
62 * Device-specific (SCSI Host Device) queue indicies
63 */
64#define VIRTIOSCSI_VIRTQ_CONTROLQ 0 /* Index of control queue */
65#define VIRTIOSCSI_VIRTQ_EVENTQ 1 /* Index of event queue */
66#define VIRTIOSCSI_VIRTQ_REQ_BASE 2 /* Base index of req queues */
67
68/**
69 * Definitions that follow are based on the VirtIO 1.0 specification.
70 * Struct names are the same. The field names have been adapted to VirtualBox
71 * data type + camel case annotation, with the original field name from the
72 * VirtIO specification in the field's comment.
73 */
74
75/** @name VirtIO 1.0 SCSI Host feature bits
76 * @{ */
77#define VIRTIOSCSI_F_INOUT RT_BIT_64(0) /** Request is device readable AND writeable */
78#define VIRTIOSCSI_F_HOTPLUG RT_BIT_64(1) /** Host SHOULD allow hotplugging SCSI LUNs & targets */
79#define VIRTIOSCSI_F_CHANGE RT_BIT_64(2) /** Host reports LUN changes via VIRTIOSCSI_T_PARAM_CHANGE event */
80#define VIRTIOSCSI_F_T10_PI RT_BIT_64(3) /** Ext. flds for T10 prot info (DIF/DIX) in SCSI req hdr */
81/** @} */
82
83/**
84 * Features VirtIO 1.0 Host SCSI controller offers guest driver
85 */
86#define VIRTIOSCSI_HOST_SCSI_FEATURES_OFFERED \
87 (VIRTIOSCSI_F_INOUT | VIRTIOSCSI_F_HOTPLUG | VIRTIOSCSI_F_CHANGE | VIRTIOSCSI_F_T10_PI)
88
89typedef struct virtio_scsi_config
90{
91 uint32_t uNumQueues; /** num_queues */
92 uint32_t uSegMax; /** seg_max */
93 uint32_t uMaxSectors; /** max_sectors */
94 uint32_t uCmdPerLun; /** cmd_per_lun */
95 uint32_t uEventInfoSize; /** event_info_size */
96 uint32_t uSenseSize; /** sense_size */
97 uint32_t uCdbSize; /** cdb_size */
98 uint16_t uMaxChannel; /** max_channel */
99 uint16_t uMaxTarget; /** max_target */
100 uint32_t uMaxLun; /** max_lun */
101} VIRTIO_SCSI_CONFIG_T, PVIRTIO_SCSI_CONFIG_T;
102
103#define CDB_SIZE 1 /* logic tbd */
104#define SENSE_SIZE 1 /* logic tbd */
105#define PI_BYTES_OUT 1 /* logic tbd */
106#define PI_BYTES_IN 1 /* logic tbd */
107#define DATA_OUT 1 /* logic tbd */
108typedef struct virtio_scsi_req_cmd
109{
110 /* Device-readable part */
111 uint8_t uLUN[8]; /** lun */
112 uint64_t uId; /** id */
113 uint8_t uTaskAttr; /** task_attr */
114 uint8_t uPrio; /** prio */
115 uint8_t uCrn; /** crn */
116 uint8_t uCdb[CDB_SIZE]; /** cdb */
117
118 /** Following three fields only present if VIRTIOSCSI_F_T10_PI negotiated */
119
120 uint32_t uPiBytesOut; /** pi_bytesout */
121 uint32_t uPiBytesIn; /** pi_bytesin */
122 uint8_t uPiOut[PI_BYTES_OUT]; /** pi_out[] */
123
124 uint8_t uDataOut[DATA_OUT]; /** dataout */
125
126 /* Device-writable part */
127 uint32_t uSenseLen; /** sense_len */
128 uint32_t uResidual; /** residual */
129 uint16_t uStatusQualifier; /** status_qualifier */
130 uint8_t uStatus; /** status */
131 uint8_t uResponse; /** response */
132 uint8_t uSense[SENSE_SIZE]; /** sense */
133
134 /** Following two fields only present if VIRTIOSCSI_F_T10_PI negotiated */
135 uint8_t uPiIn[PI_BYTES_IN]; /** pi_in[] */
136 uint8_t uDataIn[]; /** detain; */
137} VIRTIO_SCSI_REQ_CMD_T, *PVIRTIO_SCSI_REQ_CMD_T;
138
139/** @name VirtIO 1.0 SCSI req command-specific response values
140 * @{ */
141#define VIRTIOSCSI_S_OK 0 /* control, command */
142#define VIRTIOSCSI_S_OVERRUN 1 /* control */
143#define VIRTIOSCSI_S_ABORTED 2 /* control */
144#define VIRTIOSCSI_S_BAD_TARGET 3 /* control, command */
145#define VIRTIOSCSI_S_RESET 4 /* control */
146#define VIRTIOSCSI_S_BUSY 5 /* control, command */
147#define VIRTIOSCSI_S_TRANSPORT_FAILURE 6 /* control, command */
148#define VIRTIOSCSI_S_TARGET_FAILURE 7 /* control, command */
149#define VIRTIOSCSI_S_NEXUS_FAILURE 8 /* control, command */
150#define VIRTIOSCSI_S_FAILURE 9 /* control, command */
151#define VIRTIOSCSI_S_INCORRECT_LUN 12 /* command */
152/** @} */
153
154
155/** @name VirtIO 1.0 SCSI Command-specific task_attr values
156 * @{ */
157#define VIRTIOSCSI_S_SIMPLE 0
158#define VIRTIOSCSI_S_ORDERED 1
159#define VIRTIOSCSI_S_HEAD 2
160#define VIRTIOSCSI_S_ACA 3
161/** @} */
162
163#define VIRTIOSCSI_T_TMF 0
164#define VIRTIOSCSI_T_TMF_ABORT_TASK 0
165#define VIRTIOSCSI_T_TMF_ABORT_TASK_SET 1
166#define VIRTIOSCSI_T_TMF_CLEAR_ACA 2
167#define VIRTIOSCSI_T_TMF_CLEAR_TASK_SET 3
168#define VIRTIOSCSI_T_TMF_I_T_NEXUS_RESET 4
169#define VIRTIOSCSI_T_TMF_LOGICAL_UNIT_RESET 5
170#define VIRTIOSCSI_T_TMF_QUERY_TASK 6
171#define VIRTIOSCSI_T_TMF_QUERY_TASK_SET 7
172
173typedef struct virtio_scsi_ctrl_tmf
174{
175 // Device-readable part
176 uint32_t uType; /** type */
177 uint32_t uSubtype; /** subtype */
178 uint8_t uLUN[8]; /** lun */
179 uint64_t uId; /** id */
180 // Device-writable part
181 uint8_t uResponse; /** response */
182} VIRTIO_SCSI_CTRL_BUF_T, *PVIRTIO_SCSI_CTRL_BUF_T;
183
184/** @name VirtIO 1.0 SCSI tmf control command-specific response values
185 * @{ */
186#define VIRTIOSCSI_S_FUNCTION_COMPLETE 0
187#define VIRTIOSCSI_S_FUNCTION_SUCCEEDED 10
188#define VIRTIOSCSI_S_FUNCTION_REJECTED 11
189/** @} */
190
191#define VIRTIOSCSI_T_AN_QUERY 1 /** Asynchronous notification query */
192#define VIRTIOSCSI_T_AN_SUBSCRIBE 2 /** Asynchronous notification subscription */
193
194typedef struct virtio_scsi_ctrl_an
195{
196 // Device-readable part
197 uint32_t uType; /** type */
198 uint8_t uLUN[8]; /** lun */
199 uint32_t uEventRequested; /** event_requested */
200 // Device-writable part
201 uint32_t uEventActual; /** event_actual */
202 uint8_t uResponse; /** response */
203} VIRTIO_SCSI_CTRL_AN, *PVIRTIO_SCSI_CTRL_AN_T;
204
205#define VIRTIOSCSI_EVT_ASYNC_OPERATIONAL_CHANGE 2
206#define VIRTIOSCSI_EVT_ASYNC_POWER_MGMT 4
207#define VIRTIOSCSI_EVT_ASYNC_EXTERNAL_REQUEST 8
208#define VIRTIOSCSI_EVT_ASYNC_MEDIA_CHANGE 16
209#define VIRTIOSCSI_EVT_ASYNC_MULTI_HOST 32
210#define VIRTIOSCSI_EVT_ASYNC_DEVICE_BUSY 64
211
212/** Device operation: controlq */
213
214typedef struct virtio_scsi_ctrl
215{
216 uint32_t type; /** type */
217 uint8_t response; /** response */
218} VIRTIO_SCSI_CTRL_T, *PVIRTIO_SCSI_CTRL_T;
219
220
221#define VIRTIOSCSI_T_NO_EVENT 0
222
223#define VIRTIOSCSI_T_TRANSPORT_RESET 1
224#define VIRTIOSCSI_T_ASYNC_NOTIFY 2 /** Asynchronous notification */
225#define VIRTIOSCSI_T_PARAM_CHANGE 3
226
227#define VIRTIOSCSI_EVT_RESET_HARD 0
228#define VIRTIOSCSI_EVT_RESET_RESCAN 1
229#define VIRTIOSCSI_EVT_RESET_REMOVED 2
230
231/** Device operation: eventq */
232
233#define VIRTIOSCSI_T_EVENTS_MISSED 0x80000000
234typedef struct virtio_scsi_event {
235 // Device-writable part
236 uint32_t uEvent; /** event: */
237 uint8_t uLUN[8]; /** lun */
238 uint32_t uReason; /** reason */
239} VIRTIO_SCSI_EVENT_T, *PVIRTIO_SCSI_EVENT_T;
240
241/**
242 * State of a target attached to the VirtIO SCSI Host
243 */
244typedef struct VIRTIOSCSITARGET
245{
246 /** Pointer to PCI device that owns this target instance. - R3 pointer */
247 R3PTRTYPE(struct VIRTIOSCSI *) pVirtioScsiR3;
248
249 /** Pointer to attached driver's base interface. */
250 R3PTRTYPE(PPDMIBASE) pDrvBase;
251
252 /** Target LUN */
253 RTUINT iLUN;
254
255 /** Target LUN Description */
256 char * pszLunName;
257
258 /** Target base interface. */
259 PDMIBASE IBase;
260
261 /** Flag whether device is present. */
262 bool fPresent;
263
264 /** Media port interface. */
265 PDMIMEDIAPORT IMediaPort;
266
267 /** Pointer to the attached driver's media interface. */
268 R3PTRTYPE(PPDMIMEDIA) pDrvMedia;
269
270 /** Extended media port interface. */
271 PDMIMEDIAEXPORT IMediaExPort;
272
273 /** Pointer to the attached driver's extended media interface. */
274 R3PTRTYPE(PPDMIMEDIAEX) pDrvMediaEx;
275
276 /** Status LED interface */
277 PDMILEDPORTS ILed;
278 /** The status LED state for this device. */
279 PDMLED led;
280
281 /** Number of outstanding tasks on the port. */
282 volatile uint32_t cOutstandingRequests;
283
284} VIRTIOSCSITARGET, *PVIRTIOSCSITARGET;
285
286
287/**
288 * PDM instance data (state) for VirtIO Host SCSI device
289 *
290 * @extends PDMPCIDEV
291 */
292typedef struct VIRTIOSCSI
293{
294 /* Opaque handle to VirtIO common framework (must be first item
295 * in this struct so PDMINS_2_DATA macro's casting works) */
296 VIRTIOHANDLE hVirtio;
297
298 /* SCSI target instances data */
299 VIRTIOSCSITARGET aTargetInstances[VIRTIOSCSI_MAX_TARGETS];
300
301 const char szInstance[16];
302
303 /** Device base interface. */
304 PDMIBASE IBase;
305
306
307 /** Pointer to the device instance. - R3 ptr. */
308 PPDMDEVINSR3 pDevInsR3;
309 /** Pointer to the device instance. - R0 ptr. */
310 PPDMDEVINSR0 pDevInsR0;
311 /** Pointer to the device instance. - RC ptr. */
312 PPDMDEVINSRC pDevInsRC;
313
314 /** Status LUN: LEDs port interface. */
315 PDMILEDPORTS ILeds;
316
317 /** Status LUN: Partner of ILeds. */
318 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
319
320 /** Base address of the memory mapping. */
321 RTGCPHYS GCPhysMMIOBase;
322
323 /** IMediaExPort: Media ejection notification */
324 R3PTRTYPE(PPDMIMEDIANOTIFY) pMediaNotify;
325
326 /** Queue to send tasks to R3. - HC ptr */
327 R3PTRTYPE(PPDMQUEUE) pNotifierQueueR3;
328
329 /** The support driver session handle. */
330 R3R0PTRTYPE(PSUPDRVSESSION) pSupDrvSession;
331
332 /** Worker thread. */
333 R3PTRTYPE(PPDMTHREAD) pThreadWrk;
334
335 /** The event semaphore the processing thread waits on. */
336 SUPSEMEVENT hEvtProcess;
337
338 /** Number of ports detected */
339 uint64_t cTargets;
340
341 /** True if PDMDevHlpAsyncNotificationCompleted should be called when port goes idle */
342 bool volatile fSignalIdle;
343
344 VIRTIO_SCSI_CONFIG_T virtioScsiConfig;
345
346
347} VIRTIOSCSI, *PVIRTIOSCSI;
348
349/**
350 * This macro resolves to boolean true if uOffset matches a field offset and size exactly,
351 * (or if it is a 64-bit field, if it accesses either 32-bit part as a 32-bit access)
352 * ASSUMED this critereon is mandated by section 4.1.3.1 of the VirtIO 1.0 specification)
353 * This MACRO can be re-written to allow unaligned access to a field (within bounds).
354 *
355 * @param member - Member of VIRTIO_PCI_COMMON_CFG_T
356 * @result - true or false
357 */
358#define MATCH_SCSI_CONFIG(member) \
359 (RT_SIZEOFMEMB(VIRTIO_SCSI_CONFIG_T, member) == 8 \
360 && ( uOffset == RT_UOFFSETOF(VIRTIO_SCSI_CONFIG_T, member) \
361 || uOffset == RT_UOFFSETOF(VIRTIO_SCSI_CONFIG_T, member) + sizeof(uint32_t)) \
362 && cb == sizeof(uint32_t)) \
363 || (uOffset == RT_UOFFSETOF(VIRTIO_SCSI_CONFIG_T, member) \
364 && cb == RT_SIZEOFMEMB(VIRTIO_SCSI_CONFIG_T, member))
365
366#define LOG_ACCESSOR(member) \
367 virtioLogMappedIoValue(__FUNCTION__, #member, RT_SIZEOFMEMB(VIRTIO_SCSI_CONFIG_T, member), \
368 pv, cb, uMemberOffset, fWrite, false, 0);
369
370#define SCSI_CONFIG_ACCESSOR(member) \
371 { \
372 uint32_t uMemberOffset = uOffset - RT_UOFFSETOF(VIRTIO_SCSI_CONFIG_T, member); \
373 if (fWrite) \
374 memcpy(((char *)&pVirtioScsi->virtioScsiConfig.member) + uMemberOffset, (const char *)pv, cb); \
375 else \
376 memcpy((char *)pv, (const char *)(((char *)&pVirtioScsi->virtioScsiConfig.member) + uMemberOffset), cb); \
377 LOG_ACCESSOR(member); \
378 }
379
380#define SCSI_CONFIG_ACCESSOR_READONLY(member) \
381 { \
382 uint32_t uMemberOffset = uOffset - RT_UOFFSETOF(VIRTIO_SCSI_CONFIG_T, member); \
383 if (fWrite) \
384 LogFunc(("Guest attempted to write readonly virtio_pci_common_cfg.%s\n", #member)); \
385 else \
386 { \
387 memcpy((char *)pv, (const char *)(((char *)&pVirtioScsi->virtioScsiConfig.member) + uMemberOffset), cb); \
388 LOG_ACCESSOR(member); \
389 } \
390 }
391
392
393//pk: Needed for virtioIO (e.g. to talk to devSCSI? TBD ??
394/**
395 * Task state for a CCB request.
396 */
397typedef struct VIRTIOSCSIREQ
398{
399 /** Device this task is assigned to. */
400 PVIRTIOSCSITARGET pTargetDevice;
401 /** The command control block from the guest. */
402// CCBU CCBGuest;
403 /** Guest physical address of th CCB. */
404 RTGCPHYS GCPhysAddrCCB;
405 /** Pointer to the R3 sense buffer. */
406 uint8_t *pbSenseBuffer;
407 /** Flag whether this is a request from the BIOS. */
408 bool fBIOS;
409 /** 24-bit request flag (default is 32-bit). */
410 bool fIs24Bit;
411 /** SCSI status code. */
412 uint8_t u8ScsiSts;
413} VIRTIOSCSIREQ;
414typedef struct VIRTIOSCSIREQ *PVIRTIOSCSIREQ;
415/*********************************************************************************************************************************/
416
417#ifdef BOOTABLE_SUPPORT_TBD
418/** @callback_method_impl{FNIOMIOPORTIN} */
419static DECLCALLBACK(int) virtioScsiR3BiosIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint8_t *pbDst,
420 uint32_t *pcTransfers, unsigned cb);
421{
422}
423/** @callback_method_impl{FNIOMIOPORTOUT} */
424static DECLCALLBACK(int) virtioScsiR3BiosIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb);
425{
426}
427/** @callback_method_impl{FNIOMIOPORTOUTSTRING} */
428static DECLCALLBACK(int) virtioScsiR3BiosIoPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, const uint8_t *pbSrc,
429 uint32_t *pcTransfers, unsigned cb);
430{
431}
432/** @callback_method_impl{FNIOMIOPORTINSTRING} */
433static DECLCALLBACK(int) virtioScsiR3BiosIoPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint8_t *pbDst,
434 uint32_t *pcTransfers, unsigned cb);
435{
436}
437#endif
438
439/**
440 * Turns on/off the write status LED.
441 *
442 * @param pTarget Pointer to the target device
443 * @param fOn New LED state.
444 */
445void virtioScsiSetWriteLed(PVIRTIOSCSITARGET pTarget, bool fOn)
446{
447 LogFlow(("%s virtioSetWriteLed: %s\n", pTarget->pszLunName, fOn ? "on" : "off"));
448 if (fOn)
449 pTarget->led.Asserted.s.fWriting = pTarget->led.Actual.s.fWriting = 1;
450 else
451 pTarget->led.Actual.s.fWriting = fOn;
452}
453
454/**
455 * Turns on/off the read status LED.
456 *
457 * @param pTarget Pointer to the device state structure.
458 * @param fOn New LED state.
459 */
460void virtioScsiSetReadLed(PVIRTIOSCSITARGET pTarget, bool fOn)
461{
462 LogFlow(("%s virtioSetReadLed: %s\n", pTarget->pszLunName, fOn ? "on" : "off"));
463 if (fOn)
464 pTarget->led.Asserted.s.fReading = pTarget->led.Actual.s.fReading = 1;
465 else
466 pTarget->led.Actual.s.fReading = fOn;
467}
468
469/**
470 * virtio-scsi debugger info callback.
471 *
472 * @param pDevIns The device instance.
473 * @param pHlp The output helpers.
474 * @param pszArgs The arguments.
475 */
476static DECLCALLBACK(void) virtioScsiR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
477{
478 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
479 bool fVerbose = false;
480
481 /* Parse arguments. */
482 if (pszArgs)
483 fVerbose = strstr(pszArgs, "verbose") != NULL;
484
485 /* Show basic information. */
486 pHlp->pfnPrintf(pHlp, "%s#%d: virtio-scsci ",
487 pDevIns->pReg->szName,
488 pDevIns->iInstance);
489 pHlp->pfnPrintf(pHlp, "numTargets=%lu", pThis->cTargets);
490}
491
492/** @callback_method_impl{FNSSMDEVLIVEEXEC} */
493static DECLCALLBACK(int) virtioScsiR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
494{
495 LogFunc(("callback"));
496 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
497 RT_NOREF(pThis);
498 RT_NOREF(uPass);
499 RT_NOREF(pSSM);
500 return VINF_SSM_DONT_CALL_AGAIN;
501}
502
503/** @callback_method_impl{FNSSMDEVLOADEXEC} */
504static DECLCALLBACK(int) virtioScsiR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
505{
506 LogFunc(("callback"));
507 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
508 RT_NOREF(pThis);
509 RT_NOREF(uPass);
510 RT_NOREF(pSSM);
511 RT_NOREF(uVersion);
512 return VINF_SSM_DONT_CALL_AGAIN;
513}
514
515/** @callback_method_impl{FNSSMDEVSAVEEXEC} */
516static DECLCALLBACK(int) virtioScsiR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
517{
518 LogFunc(("callback"));
519 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
520 RT_NOREF(pThis);
521 RT_NOREF(pSSM);
522 return VINF_SUCCESS;
523}
524
525/** @callback_method_impl{FNSSMDEVLOADDONE} */
526static DECLCALLBACK(int) virtioScsiR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
527{
528 LogFunc(("callback"));
529 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
530 RT_NOREF(pThis);
531 RT_NOREF(pSSM);
532 return VINF_SUCCESS;
533}
534
535/**
536 * @copydoc FNPDMDEVRESET
537 */
538static DECLCALLBACK(void) virtioScsiR3Reset(PPDMDEVINS pDevIns)
539{
540 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
541 NOREF(pThis);
542
543// ASMAtomicWriteBool(&pThis->fSignalIdle, true);
544// if (!virtioScsiR3AllAsyncIOIsFinished(pDevIns))
545// PDMDevHlpSetAsyncNotification(pDevIns, virtioScsiR3IsAsyncResetDone);
546// else
547// {
548// ASMAtomicWriteBool(&pThis->fSignalIdle, false);
549// }
550}
551
552/**
553 * Device relocation callback.
554 *
555 * When this callback is called the device instance data, and if the
556 * device have a GC component, is being relocated, or/and the selectors
557 * have been changed. The device must use the chance to perform the
558 * necessary pointer relocations and data updates.
559 *
560 * Before the GC code is executed the first time, this function will be
561 * called with a 0 delta so GC pointer calculations can be one in one place.
562 *
563 * @param pDevIns Pointer to the device instance.
564 * @param offDelta The relocation delta relative to the old location.
565 *
566 * @remark A relocation CANNOT fail.
567 */
568static DECLCALLBACK(void) virtioScsiR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
569{
570 LogFunc(("Relocating virtio-scsi"));
571 RT_NOREF(offDelta);
572 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
573
574 pThis->pDevInsR3 = pDevIns;
575
576 for (uint32_t i = 0; i < VIRTIOSCSI_MAX_TARGETS; i++)
577 {
578 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[i];
579 pTarget->pVirtioScsiR3 = pThis;;
580 }
581
582 /**
583 * Important: Forward to virtio framework!
584 */
585 virtioRelocate(pDevIns, offDelta);
586
587}
588
589static DECLCALLBACK(int) virtioScsiR3QueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
590 uint32_t *piInstance, uint32_t *piLUN)
591{
592 PVIRTIOSCSITARGET pVirtioScsiTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaPort);
593 PPDMDEVINS pDevIns = pVirtioScsiTarget->CTX_SUFF(pVirtioScsi)->CTX_SUFF(pDevIns);
594
595 AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
596 AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
597 AssertPtrReturn(piLUN, VERR_INVALID_POINTER);
598
599 *ppcszController = pDevIns->pReg->szName;
600 *piInstance = pDevIns->iInstance;
601 *piLUN = pVirtioScsiTarget->iLUN;
602
603 return VINF_SUCCESS;
604}
605
606/**
607 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
608 */
609static DECLCALLBACK(int) virtioScsiR3IoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
610 void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf,
611 size_t cbCopy)
612{
613 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
614 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
615 size_t cbCopied = 0;
616 RT_NOREF(pTarget);
617 RT_NOREF(pReq);
618 RT_NOREF(pInterface);
619 RT_NOREF(pvIoReqAlloc);
620 RT_NOREF(offDst);
621 RT_NOREF(pSgBuf);
622 RT_NOREF(hIoReq);
623 RT_NOREF(cbCopy);
624 RT_NOREF(cbCopied);
625
626/*
627 if (RT_UNLIKELY(pReq->fBIOS))
628 cbCopied = vboxscsiCopyToBuf(&pTarget->CTX_SUFF(pVirtioScsi)->VBoxSCSI, pSgBuf, offDst, cbCopy);
629 else
630 cbCopied = virtioScsiR3CopySgBufToGuest(pTarget->CTX_SUFF(pVirtioScsi), pReq, pSgBuf, offDst, cbCopy);
631 return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_OVERFLOW;
632*/
633 return 0; /* placeholder */
634}
635
636/**
637 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
638 */
639static DECLCALLBACK(int) virtioScsiR3IoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
640 void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf,
641 size_t cbCopy)
642{
643 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
644 PVIRTIOSCSIREQ pReq = (PVIRTIOSCSIREQ)pvIoReqAlloc;
645 size_t cbCopied = 0;
646 RT_NOREF(pTarget);
647 RT_NOREF(pReq);
648 RT_NOREF(pInterface);
649 RT_NOREF(pvIoReqAlloc);
650 RT_NOREF(offSrc);
651 RT_NOREF(pSgBuf);
652 RT_NOREF(hIoReq);
653 RT_NOREF(cbCopy);
654 RT_NOREF(cbCopied);
655
656/*
657 if (RT_UNLIKELY(pReq->fBIOS))
658 cbCopied = vboxscsiCopyFromBuf(&pTarget->CTX_SUFF(pVirtioScsi)->VBoxSCSI, pSgBuf, offSrc, cbCopy);
659 else
660 cbCopied = vboxscsiR3CopySgBufFromGuest(pTarget->CTX_SUFF(pVirtioScsi), pReq, pSgBuf, offSrc, cbCopy);
661 return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_UNDERRUN;
662*/
663 return 0; /* placeholder */
664
665}
666
667/**
668 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
669 */
670static DECLCALLBACK(int) virtioScsiR3IoReqCompleteNotify(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
671 void *pvIoReqAlloc, int rcReq)
672{
673 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
674 RT_NOREF(pTarget);
675 RT_NOREF(pInterface);
676 RT_NOREF(pvIoReqAlloc);
677 RT_NOREF(rcReq);
678 RT_NOREF(hIoReq);
679// virtioScsiR3ReqComplete(pTarget->CTX_SUFF(pVirtioScsi), (VIRTIOSCSIREQ)pvIoReqAlloc, rcReq);
680 return VINF_SUCCESS;
681}
682
683/**
684 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
685 */
686static DECLCALLBACK(void) virtioScsiR3IoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
687 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
688{
689
690 RT_NOREF4(pInterface, hIoReq, pvIoReqAlloc, enmState);
691 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
692
693 switch (enmState)
694 {
695 case PDMMEDIAEXIOREQSTATE_SUSPENDED:
696 {
697 /* Make sure the request is not accounted for so the VM can suspend successfully. */
698 uint32_t cTasksActive = ASMAtomicDecU32(&pTarget->cOutstandingRequests);
699 if (!cTasksActive && pTarget->CTX_SUFF(pVirtioScsi)->fSignalIdle)
700 PDMDevHlpAsyncNotificationCompleted(pTarget->CTX_SUFF(pVirtioScsi)->pDevInsR3);
701 break;
702 }
703 case PDMMEDIAEXIOREQSTATE_ACTIVE:
704 /* Make sure the request is accounted for so the VM suspends only when the request is complete. */
705 ASMAtomicIncU32(&pTarget->cOutstandingRequests);
706 break;
707 default:
708 AssertMsgFailed(("Invalid request state given %u\n", enmState));
709 }
710}
711
712/**
713 * @interface_method_impl{PDMIMEDIAEXPORT,pfnMediumEjected}
714 */
715static DECLCALLBACK(void) virtioScsiR3MediumEjected(PPDMIMEDIAEXPORT pInterface)
716{
717 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IMediaExPort);
718 PVIRTIOSCSI pThis = pTarget->CTX_SUFF(pVirtioScsi);
719
720 if (pThis->pMediaNotify)
721 virtioScsiSetWriteLed(pTarget, false);
722}
723
724/**
725 * Transmit queue consumer
726 * Queue a new async task.
727 *
728 * @returns Success indicator.
729 * If false the item will not be removed and the flushing will stop.
730 * @param pDevIns The device instance.
731 * @param pItem The item to consume. Upon return this item will be freed.
732 */
733static DECLCALLBACK(bool) virtioScsiR3NotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
734{
735 RT_NOREF(pItem);
736 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
737
738 int rc = SUPSemEventSignal(pThis->pSupDrvSession, pThis->hEvtProcess);
739 AssertRC(rc);
740
741 return true;
742}
743
744
745/**
746 * Gets the pointer to the status LED of a unit.
747 *
748 * @returns VBox status code.
749 * @param pInterface Pointer to the interface structure containing the called function pointer.
750 * @param iLUN The unit which status LED we desire.
751 * @param ppLed Where to store the LED pointer.
752 */
753static DECLCALLBACK(int) virtioScsiR3TargetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
754{
755 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, ILed);
756 if (iLUN == 0)
757 {
758 *ppLed = &pTarget->led;
759 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
760 return VINF_SUCCESS;
761 }
762 return VERR_PDM_LUN_NOT_FOUND;
763 }
764
765
766/**
767 * Gets the pointer to the status LED of a unit.
768 *
769 * @returns VBox status code.
770 * @param pInterface Pointer to the interface structure containing the called function pointer.
771 * @param iLUN The unit which status LED we desire.
772 * @param ppLed Where to store the LED pointer.
773 */
774static DECLCALLBACK(int) virtioScsiR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
775{
776 PVIRTIOSCSI pThis = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, ILeds);
777 if (iLUN < pThis->cTargets)
778 {
779 *ppLed = &pThis->aTargetInstances[iLUN].led;
780 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
781 return VINF_SUCCESS;
782 }
783 return VERR_PDM_LUN_NOT_FOUND;
784}
785
786/**
787 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
788 */
789static DECLCALLBACK(void *) virtioScsiR3TargetQueryInterface(PPDMIBASE pInterface, const char *pszIID)
790{
791 PVIRTIOSCSITARGET pTarget = RT_FROM_MEMBER(pInterface, VIRTIOSCSITARGET, IBase);
792 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pTarget->IBase);
793 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pTarget->IMediaPort);
794 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pTarget->IMediaExPort);
795 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pTarget->ILed);
796 return NULL;
797}
798
799static int virtioScsiR3CfgAccessed(PVIRTIOSCSI pVirtioScsi, uint32_t uOffset,
800 const void *pv, size_t cb, uint8_t fWrite)
801{
802 int rc = VINF_SUCCESS;
803 if (MATCH_SCSI_CONFIG(uNumQueues))
804 {
805 SCSI_CONFIG_ACCESSOR_READONLY(uNumQueues);
806 }
807 else
808 if (MATCH_SCSI_CONFIG(uSegMax))
809 {
810 SCSI_CONFIG_ACCESSOR_READONLY(uSegMax);
811 }
812 else
813 if (MATCH_SCSI_CONFIG(uMaxSectors))
814 {
815 SCSI_CONFIG_ACCESSOR_READONLY(uMaxSectors);
816 }
817 else
818 if (MATCH_SCSI_CONFIG(uCmdPerLun))
819 {
820 SCSI_CONFIG_ACCESSOR_READONLY(uCmdPerLun);
821 }
822 else
823 if (MATCH_SCSI_CONFIG(uEventInfoSize))
824 {
825 SCSI_CONFIG_ACCESSOR_READONLY(uEventInfoSize);
826 }
827 else
828 if (MATCH_SCSI_CONFIG(uSenseSize))
829 {
830 SCSI_CONFIG_ACCESSOR(uSenseSize);
831 }
832 else
833 if (MATCH_SCSI_CONFIG(uCdbSize))
834 {
835 SCSI_CONFIG_ACCESSOR(uCdbSize);
836 }
837 else
838 if (MATCH_SCSI_CONFIG(uMaxChannel))
839 {
840 SCSI_CONFIG_ACCESSOR_READONLY(uMaxChannel);
841 }
842 else
843 if (MATCH_SCSI_CONFIG(uMaxTarget))
844 {
845 SCSI_CONFIG_ACCESSOR_READONLY(uMaxTarget);
846 }
847 else
848 if (MATCH_SCSI_CONFIG(uMaxLun))
849 {
850 SCSI_CONFIG_ACCESSOR_READONLY(uMaxLun);
851 }
852 else
853 {
854 LogFunc(("Bad access by guest to virtio_scsi_config: uoff=%d, cb=%d\n", uOffset, cb));
855 rc = VERR_ACCESS_DENIED;
856 }
857 return rc;
858}
859
860
861/**
862 * Get this callback from the virtio framework when the driver is ready so we know
863 * the request for the number of queues is valid (for now presuming the driver(s) aren't
864 * dynamically adding req queues but create them all while initializing
865 * based on config information on host).
866 */
867static DECLCALLBACK(void) virtioScsiStatusChanged(VIRTIOHANDLE hVirtio, bool fDriverOK)
868{
869#define MAX_QUEUENAME_SIZE 20
870 Log2Func(("\n"));
871 char pszQueueName[MAX_QUEUENAME_SIZE];
872 if (fDriverOK)
873 {
874 Log2Func(("VirtIO reports ready... Initializing queues\n"));
875 virtioQueueInit(hVirtio, VIRTIOSCSI_VIRTQ_CONTROLQ, "controlq");
876 virtioQueueInit(hVirtio, VIRTIOSCSI_VIRTQ_EVENTQ, "eventq");
877 for (uint16_t qIdx = VIRTIOSCSI_VIRTQ_REQ_BASE; qIdx < virtioGetNumQueues(hVirtio); qIdx++)
878 {
879 RTStrPrintf(pszQueueName, sizeof(pszQueueName), "requestq_%d", qIdx - 2);
880 bool fEnabled = virtioQueueInit(hVirtio, qIdx, (const char *)pszQueueName);
881 if (!fEnabled)
882 break;
883 }
884 } else {
885 Log2Func(("VirtIO reports not ready\n"));
886 }
887}
888
889static DECLCALLBACK(void) virtioScsiQueueNotified(VIRTIOHANDLE hVirtio, uint16_t qIdx)
890{
891 Log2Func(("virtio callback: %s has avail data[s]\n", virtioQueueGetName(hVirtio, qIdx)));
892}
893
894/**
895 * virtio-scsi VirtIO Device-specific capabilities read callback
896 * (other VirtIO capabilities and features are handled in VirtIO implementation)
897 *
898 * @param pDevIns The device instance.
899 * @param uOffset Offset within device specific capabilities struct
900 * @param pv Buffer in which to save read data
901 * @param cb Number of bytes to read
902 */
903static DECLCALLBACK(int) virtioScsiR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, size_t cb)
904{
905 int rc = VINF_SUCCESS;
906 PVIRTIOSCSI pVirtioScsi = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
907
908// LogFunc(("Read from Device-Specific capabilities: uOffset: 0x%x, cb: 0x%x\n",
909// uOffset, cb));
910
911 rc = virtioScsiR3CfgAccessed(pVirtioScsi, uOffset, pv, cb, false);
912
913 return rc;
914}
915
916/**
917 * virtio-scsi VirtIO Device-specific capabilities read callback
918 * (other VirtIO capabilities and features are handled in VirtIO implementation)
919 *
920 * @param pDevIns The device instance.
921 * @param uOffset Offset within device specific capabilities struct
922 * @param pv Buffer in which to save read data
923 * @param cb Number of bytes to write
924 */
925static DECLCALLBACK(int) virtioScsiR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, size_t cb)
926{
927 int rc = VINF_SUCCESS;
928 PVIRTIOSCSI pVirtioScsi = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
929
930// LogFunc(("Write to Device-Specific capabilities: uOffset: 0x%x, cb: 0x%x\n",
931// uOffset, cb));
932
933 rc = virtioScsiR3CfgAccessed(pVirtioScsi, uOffset, pv, cb, true);
934
935 return rc;
936}
937
938
939/**
940 * Memory mapped I/O Handler for read operations.
941 *
942 * @returns VBox status code.
943 *
944 * @param pDevIns The device instance.
945 * @param pvUser User argument.
946 * @param GCPhysAddr Physical address (in GC) where the read starts.
947 * @param pv Where to store the result.
948 * @param cb Number of bytes read.
949 */
950PDMBOTHCBDECL(int) virtioScsiMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
951{
952 RT_NOREF_PV(pDevIns); RT_NOREF_PV(pvUser); RT_NOREF_PV(GCPhysAddr); RT_NOREF_PV(pv); RT_NOREF_PV(cb);
953 LogFunc(("Read from MMIO area\n"));
954 return VINF_SUCCESS;
955}
956
957/**
958 * Memory mapped I/O Handler for write operations.
959 *
960 * @returns VBox status code.
961 *
962 * @param pDevIns The device instance.
963 * @param pvUser User argument.
964 * @param GCPhysAddr Physical address (in GC) where the read starts.
965 * @param pv Where to fetch the result.
966 * @param cb Number of bytes to write.
967 */
968PDMBOTHCBDECL(int) virtioScsiMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
969{
970 RT_NOREF_PV(pDevIns); RT_NOREF_PV(pvUser); RT_NOREF_PV(GCPhysAddr); RT_NOREF_PV(pv); RT_NOREF_PV(cb);
971 LogFunc(("Write to MMIO area\n"));
972 return VINF_SUCCESS;
973}
974
975/**
976 * @callback_method_impl{FNPCIIOREGIONMAP}
977 */
978static DECLCALLBACK(int) virtioScsiR3Map(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
979 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
980{
981 RT_NOREF(pPciDev, iRegion);
982 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
983 int rc = VINF_SUCCESS;
984
985 Assert(cb >= 32);
986
987 switch (iRegion)
988 {
989 case 0:
990 LogFunc(("virtio-scsi MMIO mapped at GCPhysAddr=%RGp cb=%RGp\n", GCPhysAddress, cb));
991
992 /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
993 rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
994 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
995 virtioScsiMMIOWrite, virtioScsiMMIORead,
996 "virtio-scsi MMIO");
997 pThis->GCPhysMMIOBase = RT_SUCCESS(rc) ? GCPhysAddress : 0;
998 return rc;
999 case 1:
1000 /* VirtIO 1.0 doesn't uses Port I/O (Virtio 0.95 e.g. "legacy", does) */
1001 AssertMsgFailed(("virtio-scsi: Port I/O not supported by this Host SCSI device\n"));
1002 default:
1003 AssertMsgFailed(("Invalid enmType=%d\n", enmType));
1004 }
1005 return VERR_GENERAL_FAILURE; /* Should never get here */
1006}
1007
1008/**
1009 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1010 */
1011static DECLCALLBACK(void *) virtioScsiR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1012{
1013 PVIRTIOSCSI pVirtioScsi = RT_FROM_MEMBER(pInterface, VIRTIOSCSI, IBase);
1014
1015 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pVirtioScsi->IBase);
1016 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pVirtioScsi->ILeds);
1017
1018 return NULL;
1019}
1020
1021/**
1022 * Detach notification.
1023 *
1024 * One harddisk at one port has been unplugged.
1025 * The VM is suspended at this point.
1026 *
1027 * @param pDevIns The device instance.
1028 * @param iLUN The logical unit which is being detached.
1029 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1030 */
1031static DECLCALLBACK(void) virtioScsiR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1032{
1033 RT_NOREF(fFlags);
1034 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1035 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[iLUN];
1036
1037 LogFunc((""));
1038
1039 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
1040 ("virtio-scsi: Device does not support hotplugging\n"));
1041
1042 /*
1043 * Zero some important members.
1044 */
1045 pTarget->fPresent = false;
1046 pTarget->pDrvBase = NULL;
1047}
1048
1049/**
1050 * Attach command.
1051 *
1052 * This is called when we change block driver.
1053 *
1054 * @returns VBox status code.
1055 * @param pDevIns The device instance.
1056 * @param iLUN The logical unit which is being detached.
1057 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1058 */
1059static DECLCALLBACK(int) virtioScsiR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1060{
1061 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1062 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[iLUN];
1063 int rc;
1064
1065 pThis->pDevInsR3 = pDevIns;
1066 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1067 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1068
1069 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
1070 ("virtio-scsi: Device does not support hotplugging\n"),
1071 VERR_INVALID_PARAMETER);
1072
1073 /* the usual paranoia */
1074 AssertRelease(!pTarget->pDrvBase);
1075 Assert(pTarget->iLUN == iLUN);
1076
1077 /*
1078 * Try attach the SCSI driver and get the interfaces,
1079 * required as well as optional.
1080 */
1081 rc = PDMDevHlpDriverAttach(pDevIns, pTarget->iLUN, &pDevIns->IBase,
1082 &pTarget->pDrvBase, (const char *)&pTarget->pszLunName);
1083 if (RT_SUCCESS(rc))
1084 pTarget->fPresent = true;
1085 else
1086 AssertMsgFailed(("Failed to attach %s. rc=%Rrc\n", pTarget->pszLunName, rc));
1087
1088 if (RT_FAILURE(rc))
1089 {
1090 pTarget->fPresent = false;
1091 pTarget->pDrvBase = NULL;
1092 }
1093 return rc;
1094}
1095
1096static DECLCALLBACK(int) virtioScsiDestruct(PPDMDEVINS pDevIns)
1097{
1098 /*
1099 * Check the versions here as well since the destructor is *always* called.
1100 */
1101 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1102 return VINF_SUCCESS;
1103}
1104
1105static DECLCALLBACK(int) virtioScsiConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg){
1106
1107 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1108
1109 PVIRTIOSCSI pThis = PDMINS_2_DATA(pDevIns, PVIRTIOSCSI);
1110 int rc = VINF_SUCCESS;
1111 bool fBootable = false;
1112
1113 pThis->pDevInsR3 = pDevIns;
1114 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1115 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1116
1117 LogFunc(("PDM device instance: %d\n", iInstance));
1118 RTStrPrintf((char *)pThis->szInstance, sizeof(pThis->szInstance), "VIRTIOSCSI%d", iInstance);
1119
1120 /*
1121 * Validate and read configuration.
1122 */
1123 if (!CFGMR3AreValuesValid(pCfg,"NumTargets\0"
1124 "Bootable\0"
1125 /* "GCEnabled\0" TBD */
1126 /* "R0Enabled\0" TBD */
1127 ))
1128 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1129 N_("virtio-scsi configuration error: unknown option specified"));
1130
1131 rc = CFGMR3QueryIntegerDef(pCfg, "NumTargets", &pThis->cTargets, true);
1132 if (RT_FAILURE(rc))
1133 return PDMDEV_SET_ERROR(pDevIns, rc,
1134 N_("virtio-scsi configuration error: failed to read NumTargets as integer"));
1135 LogFunc(("NumTargets=%d\n", pThis->cTargets));
1136
1137 rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &fBootable, true);
1138 if (RT_FAILURE(rc))
1139 return PDMDEV_SET_ERROR(pDevIns, rc,
1140 N_("virtio-scsi configuration error: failed to read Bootable as boolean"));
1141 LogFunc(("Bootable=%RTbool (unimplemented)\n", fBootable));
1142
1143 VIRTIOPCIPARAMS virtioPciParams, *pVirtioPciParams = &virtioPciParams;
1144 pVirtioPciParams->uDeviceId = PCI_DEVICE_ID_VIRTIOSCSI_HOST;
1145 pVirtioPciParams->uClassBase = PCI_CLASS_BASE_MASS_STORAGE;
1146 pVirtioPciParams->uClassSub = PCI_CLASS_SUB_SCSI_STORAGE_CONTROLLER;
1147 pVirtioPciParams->uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
1148 pVirtioPciParams->uSubsystemId = PCI_DEVICE_ID_VIRTIOSCSI_HOST; /* Virtio 1.0 spec allows PCI Device ID here */
1149 pVirtioPciParams->uInterruptLine = 0x00;
1150 pVirtioPciParams->uInterruptPin = 0x01;
1151
1152 pThis->IBase.pfnQueryInterface = virtioScsiR3DeviceQueryInterface;
1153
1154 /* Configure virtio_scsi_config that transacts via VirtIO implementation's Dev. Specific Cap callbacks */
1155 pThis->virtioScsiConfig.uNumQueues = 3; /* controlq, eventq, + n request queues*/
1156 pThis->virtioScsiConfig.uSegMax = 1024; /* initial guess */
1157 pThis->virtioScsiConfig.uMaxSectors = 0x10000;
1158 pThis->virtioScsiConfig.uCmdPerLun = 64; /* initial guess */
1159 pThis->virtioScsiConfig.uEventInfoSize = 100; /* VirtIO 1.0 spec says this should based on # of negotiated features */
1160 pThis->virtioScsiConfig.uSenseSize = 96; /* from VirtIO 1.0 spec, must restore this on reset */
1161 pThis->virtioScsiConfig.uCdbSize = 32; /* from VirtIO 1.0 spec, must restore this on reset */
1162 pThis->virtioScsiConfig.uMaxChannel = 0; /* from VirtIO 1.0 spec */
1163 pThis->virtioScsiConfig.uMaxTarget = pThis->cTargets;
1164 pThis->virtioScsiConfig.uMaxLun = 16383; /* from VirtIO 1.0 spec */
1165
1166 rc = virtioConstruct(pDevIns, &pThis->hVirtio, pVirtioPciParams, pThis->szInstance,
1167 VIRTIOSCSI_N_QUEUES, VIRTIOSCSI_REGION_PCI_CAP,
1168 VIRTIOSCSI_HOST_SCSI_FEATURES_OFFERED,
1169 virtioScsiR3DevCapRead,
1170 virtioScsiR3DevCapWrite,
1171 virtioScsiStatusChanged,
1172 virtioScsiQueueNotified,
1173 virtioScsiR3LiveExec,
1174 virtioScsiR3SaveExec,
1175 virtioScsiR3LoadExec,
1176 virtioScsiR3LoadDone,
1177 sizeof(VIRTIO_SCSI_CONFIG_T) /* cbDevSpecificCap */,
1178 (void *)&pThis->virtioScsiConfig /* pDevSpecificCap */);
1179
1180 if (RT_FAILURE(rc))
1181 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi: failed to initialize VirtIO"));
1182
1183
1184 rc = PDMDevHlpPCIIORegionRegister(pDevIns, VIRTIOSCSI_REGION_MEM_IO, 32,
1185 PCI_ADDRESS_SPACE_MEM, virtioScsiR3Map);
1186 if (RT_FAILURE(rc))
1187 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi: cannot register PCI mmio address space"));
1188
1189#ifdef BOOTABLE_SUPPORT_TBD
1190 if (fBootable)
1191 {
1192 /* Register I/O port space for BIOS access. */
1193 rc = PDMDevHlpIOPortRegister(pDevIns, VIRTIOSCSI_BIOS_IO_PORT, 4, NULL,
1194 virtioScsiR3BiosIoPortWrite, virtioScsiR3BiosIoPortRead,
1195 virtioScsiR3BiosIoPortWriteStr, virtioScsiR3BiosIoPortReadStr,
1196 "virtio-scsi BIOS");
1197 if (RT_FAILURE(rc))
1198 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi cannot register BIOS I/O handlers"));
1199 }
1200#endif
1201
1202 /* Initialize task queue. */
1203 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 5, 0,
1204 virtioScsiR3NotifyQueueConsumer, true, "VirtioTask", &pThis->pNotifierQueueR3);
1205 if (RT_FAILURE(rc))
1206 return rc;
1207
1208 /* Initialize per device instance. */
1209 for (RTUINT iLUN = 0; iLUN < VIRTIOSCSI_MAX_TARGETS; iLUN++)
1210 {
1211 PVIRTIOSCSITARGET pTarget = &pThis->aTargetInstances[iLUN];
1212
1213 if (RTStrAPrintf(&pTarget->pszLunName, "VSCSILUN%u", iLUN) < 0)
1214 AssertLogRelFailedReturn(VERR_NO_MEMORY);
1215
1216 /* Initialize static parts of the device. */
1217 pTarget->iLUN = iLUN;
1218 pTarget->pVirtioScsiR3 = pThis;
1219
1220 pTarget->IBase.pfnQueryInterface = virtioScsiR3TargetQueryInterface;
1221
1222 /* IMediaPort and IMediaExPort interfaces provide callbacks for VD media and downstream driver access */
1223 pTarget->IMediaPort.pfnQueryDeviceLocation = virtioScsiR3QueryDeviceLocation;
1224 pTarget->IMediaExPort.pfnIoReqCompleteNotify = virtioScsiR3IoReqCompleteNotify;
1225 pTarget->IMediaExPort.pfnIoReqCopyFromBuf = virtioScsiR3IoReqCopyFromBuf;
1226 pTarget->IMediaExPort.pfnIoReqCopyToBuf = virtioScsiR3IoReqCopyToBuf;
1227 pTarget->IMediaExPort.pfnIoReqStateChanged = virtioScsiR3IoReqStateChanged;
1228 pTarget->IMediaExPort.pfnMediumEjected = virtioScsiR3MediumEjected;
1229 pTarget->IMediaExPort.pfnIoReqQueryBuf = NULL;
1230 pTarget->IMediaExPort.pfnIoReqQueryDiscardRanges = NULL;
1231 pTarget->IBase.pfnQueryInterface = virtioScsiR3TargetQueryInterface;
1232 pTarget->ILed.pfnQueryStatusLed = virtioScsiR3TargetQueryStatusLed;
1233 pThis->ILeds.pfnQueryStatusLed = virtioScsiR3DeviceQueryStatusLed;
1234 pTarget->led.u32Magic = PDMLED_MAGIC;
1235
1236 LogFunc(("Attaching LUN: %s\n", pTarget->pszLunName));
1237
1238 /* Attach this SCSI driver (upstream driver pre-determined statically outside this module) */
1239 AssertReturn(iLUN < RT_ELEMENTS(pThis->aTargetInstances), VERR_PDM_NO_SUCH_LUN);
1240 rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pTarget->IBase, &pTarget->pDrvBase, (const char *)&pTarget->pszLunName);
1241 if (RT_SUCCESS(rc))
1242 {
1243 pTarget->fPresent = true;
1244
1245 pTarget->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIA);
1246 AssertMsgReturn(VALID_PTR(pTarget->pDrvMedia),
1247 ("virtio-scsi configuration error: LUN#%d missing basic media interface!\n", pTarget->iLUN),
1248 VERR_PDM_MISSING_INTERFACE);
1249
1250 /* Get the extended media interface. */
1251 pTarget->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pTarget->pDrvBase, PDMIMEDIAEX);
1252 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
1253 ("virtio-scsi configuration error: LUN#%d missing extended media interface!\n", pTarget->iLUN),
1254 VERR_PDM_MISSING_INTERFACE);
1255
1256 rc = pTarget->pDrvMediaEx->pfnIoReqAllocSizeSet(pTarget->pDrvMediaEx, REQ_ALLOC_SIZE /*TBD*/);
1257 AssertMsgReturn(VALID_PTR(pTarget->pDrvMediaEx),
1258 ("virtio-scsi configuration error: LUN#%u: Failed to set I/O request size!\n", pTarget->iLUN),
1259 rc);
1260
1261 }
1262 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1263 {
1264 pTarget->fPresent = false;
1265 pTarget->pDrvBase = NULL;
1266 rc = VINF_SUCCESS;
1267 Log(("virtio-scsi: no driver attached to device %s\n", pTarget->pszLunName));
1268 }
1269 else
1270 {
1271 AssertLogRelMsgFailed(("virtio-scsi: Failed to attach %s\n", pTarget->pszLunName));
1272 return rc;
1273 }
1274 }
1275
1276/* rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIOSCSI_SAVED_STATE_MINOR_VERSION, sizeof(*pThis), NULL,
1277 NULL, virtioScsiR3LiveExec, NULL,
1278 NULL, virtioScsiR3SaveExec, NULL,
1279 NULL, virtioScsiR3LoadExec, virtioScsiR3LoadDone);
1280 if (RT_FAILURE(rc))
1281 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-scsi cannot register save state handlers"));
1282*/
1283
1284 /* Status driver */
1285 PPDMIBASE pUpBase;
1286 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pUpBase, "Status Port");
1287 if (RT_FAILURE(rc))
1288 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
1289
1290 /*
1291 * Register the debugger info callback.
1292 */
1293 char szTmp[128];
1294 RTStrPrintf(szTmp, sizeof(szTmp), "%s%d", pDevIns->pReg->szName, pDevIns->iInstance);
1295 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "virtio-scsi info", virtioScsiR3Info);
1296
1297 return rc;
1298}
1299
1300/**
1301 * The device registration structure.
1302 */
1303const PDMDEVREG g_DeviceVirtioSCSI =
1304{
1305 /* u32Version */
1306 PDM_DEVREG_VERSION,
1307 /* szName */
1308 "virtio-scsi",
1309 /* szRCMod */
1310 "VBoxDDRC.rc",
1311 /* szR0Mod */
1312 "VBoxDDR0.r0",
1313 /* pszDescription */
1314 "Virtio Host SCSI.\n",
1315 /* fFlags */
1316#ifdef VIRTIOSCSI_GC_SUPPORT
1317 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1318#else
1319 PDM_DEVREG_FLAGS_DEFAULT_BITS,
1320#endif
1321 /* fClass */
1322 PDM_DEVREG_CLASS_MISC,
1323 /* cMaxInstances */
1324 ~0U,
1325 /* cbInstance */
1326 sizeof(VIRTIOSCSI),
1327 /* pfnConstruct */
1328 virtioScsiConstruct,
1329 /* pfnDestruct */
1330 virtioScsiDestruct,
1331 /* pfnRelocate */
1332 virtioScsiR3Relocate,
1333 /* pfnMemSetup */
1334 NULL,
1335 /* pfnPowerOn */
1336 NULL,
1337 /* pfnReset */
1338 virtioScsiR3Reset,
1339 /* pfnSuspend */
1340 NULL,
1341 /* pfnResume */
1342 NULL,
1343 /* pfnAttach */
1344 virtioScsiR3Attach,
1345 /* pfnDetach */
1346 virtioScsiR3Detach,
1347 /* pfnQueryInterface */
1348 NULL,
1349 /* pfnInitComplete */
1350 NULL,
1351 /* pfnPowerOff */
1352 NULL,
1353 /* pfnSoftReset */
1354 NULL,
1355 /* u32VersionEnd */
1356 PDM_DEVREG_VERSION
1357};
1358
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