VirtualBox

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

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

Storage/DevVirtioSCSI.cpp: Implemented interrupt handling and check/improved nuanced spec'd handling of EVENT_IDX mode, notification, device reset, added client/framework interface and other cleanup (see bugref:9440 Comment 51)

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