VirtualBox

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

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

Adjust some parameters and interface

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

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