VirtualBox

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

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

Fix doxygen burn and fixed MMIO related macros, and notification area configuration

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