VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp@ 98110

Last change on this file since 98110 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 242.2 KB
Line 
1/* $Id: DevLsiLogicSCSI.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * DevLsiLogicSCSI - LsiLogic LSI53c1030 SCSI controller.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DEV_LSILOGICSCSI
33#include <VBox/vmm/pdmdev.h>
34#include <VBox/vmm/pdmstorageifs.h>
35#include <VBox/vmm/pdmqueue.h>
36#include <VBox/vmm/pdmthread.h>
37#include <VBox/vmm/pdmcritsect.h>
38#include <VBox/AssertGuest.h>
39#include <VBox/scsi.h>
40#include <VBox/sup.h>
41#include <iprt/assert.h>
42#include <iprt/asm.h>
43#include <iprt/string.h>
44#include <iprt/list.h>
45#ifdef IN_RING3
46# include <iprt/memcache.h>
47# include <iprt/mem.h>
48# include <iprt/param.h>
49# include <iprt/uuid.h>
50# include <iprt/time.h>
51#endif
52
53#include "DevLsiLogicSCSI.h"
54#include "VBoxSCSI.h"
55
56#include "VBoxDD.h"
57
58
59/*********************************************************************************************************************************
60* Defined Constants And Macros *
61*********************************************************************************************************************************/
62/** The current saved state version. */
63#define LSILOGIC_SAVED_STATE_VERSION 6
64/** The saved state version used by VirtualBox before removal of the
65 * VBoxSCSI BIOS interface. */
66#define LSILOGIC_SAVED_STATE_VERSION_PRE_VBOXSCSI_REMOVAL 5
67/** The saved state version used by VirtualBox before the diagnostic
68 * memory access was implemented. */
69#define LSILOGIC_SAVED_STATE_VERSION_PRE_DIAG_MEM 4
70/** The saved state version used by VirtualBox before the doorbell status flag
71 * was changed from bool to a 32bit enum. */
72#define LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL 3
73/** The saved state version used by VirtualBox before SAS support was added. */
74#define LSILOGIC_SAVED_STATE_VERSION_PRE_SAS 2
75/** The saved state version used by VirtualBox 3.0 and earlier. It does not
76 * include the device config part. */
77#define LSILOGIC_SAVED_STATE_VERSION_VBOX_30 1
78
79/** Maximum number of entries in the release log. */
80#define MAX_REL_LOG_ERRORS 1024
81
82#define LSILOGIC_RTGCPHYS_FROM_U32(Hi, Lo) ( (RTGCPHYS)RT_MAKE_U64(Lo, Hi) )
83
84/** Upper number a buffer is freed if it was too big before. */
85#define LSILOGIC_MAX_ALLOC_TOO_MUCH 20
86
87/** Maximum size of the memory regions (prevents teh guest from DOSing the host by
88 * allocating loadds of memory). */
89#define LSILOGIC_MEMORY_REGIONS_MAX _1M
90
91
92/*********************************************************************************************************************************
93* Structures and Typedefs *
94*********************************************************************************************************************************/
95
96/** Pointer to the shared instance data for the LsiLogic emulation. */
97typedef struct LSILOGICSCSI *PLSILOGICSCSI;
98
99#ifdef IN_RING3
100/**
101 * Memory buffer callback.
102 *
103 * @returns nothing.
104 * @param pDevIns The device instance.
105 * @param GCPhys The guest physical address of the memory buffer.
106 * @param pSgBuf The pointer to the host R3 S/G buffer.
107 * @param cbCopy How many bytes to copy between the two buffers.
108 * @param pcbSkip Initially contains the amount of bytes to skip
109 * starting from the guest physical address before
110 * accessing the S/G buffer and start copying data.
111 * On return this contains the remaining amount if
112 * cbCopy < *pcbSkip or 0 otherwise.
113 */
114typedef DECLCALLBACKTYPE(void, FNLSILOGICR3MEMCOPYCALLBACK,(PPDMDEVINS pDevIns, RTGCPHYS GCPhys,
115 PRTSGBUF pSgBuf, size_t cbCopy, size_t *pcbSkip));
116/** Pointer to a memory copy buffer callback. */
117typedef FNLSILOGICR3MEMCOPYCALLBACK *PFNLSILOGICR3MEMCOPYCALLBACK;
118#endif
119
120/**
121 * Reply data.
122 */
123typedef struct LSILOGICSCSIREPLY
124{
125 /** Lower 32 bits of the reply address in memory. */
126 uint32_t u32HostMFALowAddress;
127 /** Full address of the reply in guest memory. */
128 RTGCPHYS GCPhysReplyAddress;
129 /** Size of the reply. */
130 uint32_t cbReply;
131 /** Different views to the reply depending on the request type. */
132 MptReplyUnion Reply;
133} LSILOGICSCSIREPLY;
134/** Pointer to reply data. */
135typedef LSILOGICSCSIREPLY *PLSILOGICSCSIREPLY;
136
137/**
138 * Memory region of the IOC.
139 */
140typedef struct LSILOGICMEMREGN
141{
142 /** List node. */
143 RTLISTNODE NodeList;
144 /** 32bit address the region starts to describe. */
145 uint32_t u32AddrStart;
146 /** 32bit address the region ends (inclusive). */
147 uint32_t u32AddrEnd;
148 /** Data for this region - variable. */
149 uint32_t au32Data[1];
150} LSILOGICMEMREGN;
151/** Pointer to a memory region. */
152typedef LSILOGICMEMREGN *PLSILOGICMEMREGN;
153
154/**
155 * State of a device attached to the buslogic host adapter.
156 *
157 * @implements PDMIBASE
158 * @implements PDMISCSIPORT
159 * @implements PDMILEDPORTS
160 */
161typedef struct LSILOGICDEVICE
162{
163 /** Pointer to the owning lsilogic device instance - R3 pointer */
164 PPDMDEVINSR3 pDevIns;
165
166 /** LUN of the device. */
167 uint32_t iLUN;
168 /** Number of outstanding tasks on the port. */
169 volatile uint32_t cOutstandingRequests;
170
171 /** Our base interface. */
172 PDMIBASE IBase;
173 /** Media port interface. */
174 PDMIMEDIAPORT IMediaPort;
175 /** Extended media port interface. */
176 PDMIMEDIAEXPORT IMediaExPort;
177 /** Led interface. */
178 PDMILEDPORTS ILed;
179 /** Pointer to the attached driver's base interface. */
180 R3PTRTYPE(PPDMIBASE) pDrvBase;
181 /** Pointer to the attached driver's media interface. */
182 R3PTRTYPE(PPDMIMEDIA) pDrvMedia;
183 /** Pointer to the attached driver's extended media interface. */
184 R3PTRTYPE(PPDMIMEDIAEX) pDrvMediaEx;
185 /** The status LED state for this device. */
186 PDMLED Led;
187 /** Device name. */
188 char szName[16];
189} LSILOGICDEVICE;
190/** Pointer to a device state. */
191typedef LSILOGICDEVICE *PLSILOGICDEVICE;
192
193/** Pointer to a task state. */
194typedef struct LSILOGICREQ *PLSILOGICREQ;
195
196
197/**
198 * Shared instance data for the LsiLogic emulation.
199 */
200typedef struct LSILOGICSCSI
201{
202 /** The state the controller is currently in. */
203 LSILOGICSTATE enmState;
204 /** Who needs to init the driver to get into operational state. */
205 LSILOGICWHOINIT enmWhoInit;
206 /** Flag whether we are in doorbell function. */
207 LSILOGICDOORBELLSTATE enmDoorbellState;
208 /** Flag whether diagnostic access is enabled. */
209 bool fDiagnosticEnabled;
210 /** Flag whether a notification was send to R3. */
211 bool fNotificationSent;
212 /** Flag whether the guest enabled event notification from the IOC. */
213 bool fEventNotificationEnabled;
214 /** Flag whether the diagnostic address and RW registers are enabled. */
215 bool fDiagRegsEnabled;
216
217 /** Number of device states allocated. */
218 uint32_t cDeviceStates;
219 uint32_t u32Padding1;
220
221 /** Interrupt mask. */
222 volatile uint32_t uInterruptMask;
223 /** Interrupt status register. */
224 volatile uint32_t uInterruptStatus;
225
226 /** Buffer for messages which are passed through the doorbell using the
227 * handshake method. */
228 uint32_t aMessage[sizeof(MptConfigurationRequest)]; /** @todo r=bird: Looks like 4 times the required size? Please explain in comment if this correct... */
229 /** Actual position in the buffer. */
230 uint32_t iMessage;
231 /** Size of the message which is given in the doorbell message in dwords. */
232 uint32_t cMessage;
233
234 /** Reply buffer.
235 * @note 60 bytes */
236 MptReplyUnion ReplyBuffer;
237 /** Next entry to read. */
238 uint32_t uNextReplyEntryRead;
239 /** Size of the reply in the buffer in 16bit words. */
240 uint32_t cReplySize;
241
242 /** The fault code of the I/O controller if we are in the fault state. */
243 uint16_t u16IOCFaultCode;
244 uint16_t u16Padding2;
245
246 /** Upper 32 bits of the message frame address to locate requests in guest memory. */
247 uint32_t u32HostMFAHighAddr;
248 /** Upper 32 bits of the sense buffer address. */
249 uint32_t u32SenseBufferHighAddr;
250 /** Maximum number of devices the driver reported he can handle. */
251 uint8_t cMaxDevices;
252 /** Maximum number of buses the driver reported he can handle. */
253 uint8_t cMaxBuses;
254 /** Current size of reply message frames in the guest. */
255 uint16_t cbReplyFrame;
256
257 /** Next key to write in the sequence to get access
258 * to diagnostic memory. */
259 uint32_t iDiagnosticAccess;
260
261 /** Number entries configured for the reply queue. */
262 uint32_t cReplyQueueEntries;
263 /** Number entries configured for the outstanding request queue. */
264 uint32_t cRequestQueueEntries;
265
266 /** Critical section protecting the reply post queue. */
267 PDMCRITSECT ReplyPostQueueCritSect;
268 /** Critical section protecting the reply free queue. */
269 PDMCRITSECT ReplyFreeQueueCritSect;
270 /** Critical section protecting the request queue against
271 * concurrent access from the guest. */
272 PDMCRITSECT RequestQueueCritSect;
273 /** Critical section protecting the reply free queue against
274 * concurrent write access from the guest. */
275 PDMCRITSECT ReplyFreeQueueWriteCritSect;
276
277 /** The reply free qeueue (only the first cReplyQueueEntries are used). */
278 uint32_t volatile aReplyFreeQueue[LSILOGICSCSI_REPLY_QUEUE_DEPTH_MAX];
279 /** The reply post qeueue (only the first cReplyQueueEntries are used). */
280 uint32_t volatile aReplyPostQueue[LSILOGICSCSI_REPLY_QUEUE_DEPTH_MAX];
281 /** The request qeueue (only the first cRequestQueueEntries are used). */
282 uint32_t volatile aRequestQueue[LSILOGICSCSI_REQUEST_QUEUE_DEPTH_MAX];
283
284 /** Next free entry in the reply queue the guest can write a address to. */
285 volatile uint32_t uReplyFreeQueueNextEntryFreeWrite;
286 /** Next valid entry the controller can read a valid address for reply frames from. */
287 volatile uint32_t uReplyFreeQueueNextAddressRead;
288
289 /** Next free entry in the reply queue the guest can write a address to. */
290 volatile uint32_t uReplyPostQueueNextEntryFreeWrite;
291 /** Next valid entry the controller can read a valid address for reply frames from. */
292 volatile uint32_t uReplyPostQueueNextAddressRead;
293
294 /** Next free entry the guest can write a address to a request frame to. */
295 volatile uint32_t uRequestQueueNextEntryFreeWrite;
296 /** Next valid entry the controller can read a valid address for request frames from. */
297 volatile uint32_t uRequestQueueNextAddressRead;
298
299 /** Indicates that PDMDevHlpAsyncNotificationCompleted should be called when
300 * a port is entering the idle state. */
301 bool volatile fSignalIdle;
302 /** Flag whether the worker thread is sleeping. */
303 volatile bool fWrkThreadSleeping;
304 bool afPadding3[2];
305
306 /** Current address to read from or write to in the diagnostic memory region. */
307 uint32_t u32DiagMemAddr;
308
309 /** Emulated controller type */
310 LSILOGICCTRLTYPE enmCtrlType;
311 /** Handle counter */
312 uint16_t u16NextHandle;
313
314 /** Number of ports this controller has. */
315 uint8_t cPorts;
316 uint8_t afPadding4;
317
318 /** The event semaphore the processing thread waits on. */
319 SUPSEMEVENT hEvtProcess;
320
321 /** PCI Region \#0: I/O ports register access. */
322 IOMIOPORTHANDLE hIoPortsReg;
323 /** PCI Region \#1: MMIO register access. */
324 IOMMMIOHANDLE hMmioReg;
325 /** PCI Region \#2: MMIO diag. */
326 IOMMMIOHANDLE hMmioDiag;
327 /** ISA Ports for the BIOS (when booting is configured). */
328 IOMIOPORTHANDLE hIoPortsBios;
329} LSILOGICSCSI;
330AssertCompileMemberAlignment(LSILOGICSCSI, ReplyPostQueueCritSect, 8);
331
332/**
333 * Ring-3 instance data for the LsiLogic emulation.
334 */
335typedef struct LSILOGICSCSIR3
336{
337 /** States for attached devices. */
338 R3PTRTYPE(PLSILOGICDEVICE) paDeviceStates;
339 /** Status LUN: The base interface. */
340 PDMIBASE IBase;
341 /** Status LUN: Leds interface. */
342 PDMILEDPORTS ILeds;
343 /** Status LUN: Partner of ILeds. */
344 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
345 /** Status LUN: Media Notifys. */
346 R3PTRTYPE(PPDMIMEDIANOTIFY) pMediaNotify;
347 /** Pointer to the configuration page area. */
348 R3PTRTYPE(PMptConfigurationPagesSupported) pConfigurationPages;
349
350 /** Current size of the memory regions. */
351 uint32_t cbMemRegns;
352 uint32_t u32Padding3;
353
354 /** Critical section protecting the memory regions. */
355 RTCRITSECT CritSectMemRegns;
356 /** List of memory regions - PLSILOGICMEMREGN. */
357 RTLISTANCHORR3 ListMemRegns;
358
359 /** Worker thread. */
360 R3PTRTYPE(PPDMTHREAD) pThreadWrk;
361
362 /** The device instace - only for getting bearings in interface methods. */
363 PPDMDEVINSR3 pDevIns;
364} LSILOGICSCSIR3;
365/** Pointer to the ring-3 instance data for the LsiLogic emulation. */
366typedef LSILOGICSCSIR3 *PLSILOGICSCSIR3;
367
368
369/**
370 * Ring-0 instance data for the LsiLogic emulation.
371 */
372typedef struct LSILOGICSCSIR0
373{
374 uint64_t u64Unused;
375} LSILOGICSCSIR0;
376/** Pointer to the ring-0 instance data for the LsiLogic emulation. */
377typedef LSILOGICSCSIR0 *PLSILOGICSCSIR0;
378
379
380/**
381 * Raw-mode instance data for the LsiLogic emulation.
382 */
383typedef struct LSILOGICSCSIRC
384{
385 uint64_t u64Unused;
386} LSILOGICSCSIRC;
387/** Pointer to the raw-mode instance data for the LsiLogic emulation. */
388typedef LSILOGICSCSIRC *PLSILOGICSCSIRC;
389
390
391/** The current context instance data for the LsiLogic emulation. */
392typedef CTX_SUFF(LSILOGICSCSI) LSILOGICSCSICC;
393/** Pointer to the current context instance data for the LsiLogic emulation. */
394typedef CTX_SUFF(PLSILOGICSCSI) PLSILOGICSCSICC;
395
396
397/**
398 * Task state object which holds all necessary data while
399 * processing the request from the guest.
400 */
401typedef struct LSILOGICREQ
402{
403 /** I/O request handle. */
404 PDMMEDIAEXIOREQ hIoReq;
405 /** Next in the redo list. */
406 PLSILOGICREQ pRedoNext;
407 /** Target device. */
408 PLSILOGICDEVICE pTargetDevice;
409 /** The message request from the guest. */
410 MptRequestUnion GuestRequest;
411 /** Address of the message request frame in guests memory.
412 * Used to read the S/G entries in the second step. */
413 RTGCPHYS GCPhysMessageFrameAddr;
414 /** Physical start address of the S/G list. */
415 RTGCPHYS GCPhysSgStart;
416 /** Chain offset */
417 uint32_t cChainOffset;
418 /** Pointer to the sense buffer. */
419 uint8_t abSenseBuffer[18];
420 /** SCSI status code. */
421 uint8_t u8ScsiSts;
422} LSILOGICREQ;
423
424
425#ifndef VBOX_DEVICE_STRUCT_TESTCASE
426
427
428/*********************************************************************************************************************************
429* Internal Functions *
430*********************************************************************************************************************************/
431RT_C_DECLS_BEGIN
432#ifdef IN_RING3
433static void lsilogicR3InitializeConfigurationPages(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC);
434static void lsilogicR3ConfigurationPagesFree(PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC);
435static int lsilogicR3ProcessConfigurationRequest(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC,
436 PMptConfigurationRequest pConfigurationReq, PMptConfigurationReply pReply);
437#endif
438RT_C_DECLS_END
439
440
441/*********************************************************************************************************************************
442* Global Variables *
443*********************************************************************************************************************************/
444/** Key sequence the guest has to write to enable access
445 * to diagnostic memory. */
446static const uint8_t g_lsilogicDiagnosticAccess[] = {0x04, 0x0b, 0x02, 0x07, 0x0d};
447
448/**
449 * Updates the status of the interrupt pin of the device.
450 *
451 * @returns nothing.
452 * @param pDevIns The device instance.
453 * @param pThis Pointer to the shared LsiLogic device state.
454 */
455static void lsilogicUpdateInterrupt(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis)
456{
457 uint32_t uIntSts;
458
459 LogFlowFunc(("Updating interrupts\n"));
460
461 /* Mask out doorbell status so that it does not affect interrupt updating. */
462 uIntSts = (ASMAtomicReadU32(&pThis->uInterruptStatus) & ~LSILOGIC_REG_HOST_INTR_STATUS_DOORBELL_STS);
463 /* Check maskable interrupts. */
464 uIntSts &= ~(ASMAtomicReadU32(&pThis->uInterruptMask) & ~LSILOGIC_REG_HOST_INTR_MASK_IRQ_ROUTING);
465
466 if (uIntSts)
467 {
468 LogFlowFunc(("Setting interrupt\n"));
469 PDMDevHlpPCISetIrq(pDevIns, 0, 1);
470 }
471 else
472 {
473 LogFlowFunc(("Clearing interrupt\n"));
474 PDMDevHlpPCISetIrq(pDevIns, 0, 0);
475 }
476}
477
478/**
479 * Sets a given interrupt status bit in the status register and
480 * updates the interrupt status.
481 *
482 * @returns nothing.
483 * @param pDevIns The device instance.
484 * @param pThis Pointer to the shared LsiLogic device state.
485 * @param uStatus The status bit to set.
486 */
487DECLINLINE(void) lsilogicSetInterrupt(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, uint32_t uStatus)
488{
489 ASMAtomicOrU32(&pThis->uInterruptStatus, uStatus);
490 lsilogicUpdateInterrupt(pDevIns, pThis);
491}
492
493/**
494 * Clears a given interrupt status bit in the status register and
495 * updates the interrupt status.
496 *
497 * @returns nothing.
498 * @param pDevIns The device instance.
499 * @param pThis Pointer to the shared LsiLogic device state.
500 * @param uStatus The status bit to set.
501 */
502DECLINLINE(void) lsilogicClearInterrupt(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, uint32_t uStatus)
503{
504 ASMAtomicAndU32(&pThis->uInterruptStatus, ~uStatus);
505 lsilogicUpdateInterrupt(pDevIns, pThis);
506}
507
508
509#ifdef IN_RING3
510/**
511 * Sets the I/O controller into fault state and sets the fault code.
512 *
513 * @returns nothing
514 * @param pThis Pointer to the shared LsiLogic device state.
515 * @param uIOCFaultCode Fault code to set.
516 */
517DECLINLINE(void) lsilogicSetIOCFaultCode(PLSILOGICSCSI pThis, uint16_t uIOCFaultCode)
518{
519 if (pThis->enmState != LSILOGICSTATE_FAULT)
520 {
521 LogFunc(("Setting I/O controller into FAULT state: uIOCFaultCode=%u\n", uIOCFaultCode));
522 pThis->enmState = LSILOGICSTATE_FAULT;
523 pThis->u16IOCFaultCode = uIOCFaultCode;
524 }
525 else
526 LogFunc(("We are already in FAULT state\n"));
527}
528#endif /* IN_RING3 */
529
530
531/**
532 * Returns the number of frames in the reply free queue.
533 *
534 * @returns Number of frames in the reply free queue.
535 * @param pThis Pointer to the shared LsiLogic device state.
536 */
537DECLINLINE(uint32_t) lsilogicReplyFreeQueueGetFrameCount(PLSILOGICSCSI pThis)
538{
539 uint32_t cReplyFrames = 0;
540
541 if (pThis->uReplyFreeQueueNextAddressRead <= pThis->uReplyFreeQueueNextEntryFreeWrite)
542 cReplyFrames = pThis->uReplyFreeQueueNextEntryFreeWrite - pThis->uReplyFreeQueueNextAddressRead;
543 else
544 cReplyFrames = pThis->cReplyQueueEntries - pThis->uReplyFreeQueueNextAddressRead + pThis->uReplyFreeQueueNextEntryFreeWrite;
545
546 return cReplyFrames;
547}
548
549#ifdef IN_RING3
550
551/**
552 * Returns the number of free entries in the reply post queue.
553 *
554 * @returns Number of frames in the reply free queue.
555 * @param pThis Pointer to the shared LsiLogic device state.
556 */
557DECLINLINE(uint32_t) lsilogicReplyPostQueueGetFrameCount(PLSILOGICSCSI pThis)
558{
559 uint32_t cReplyFrames = 0;
560
561 if (pThis->uReplyPostQueueNextAddressRead <= pThis->uReplyPostQueueNextEntryFreeWrite)
562 cReplyFrames = pThis->cReplyQueueEntries - pThis->uReplyPostQueueNextEntryFreeWrite + pThis->uReplyPostQueueNextAddressRead;
563 else
564 cReplyFrames = pThis->uReplyPostQueueNextEntryFreeWrite - pThis->uReplyPostQueueNextAddressRead;
565
566 return cReplyFrames;
567}
568
569
570/**
571 * Performs a hard reset on the controller.
572 *
573 * @returns VBox status code.
574 * @param pDevIns The device instance.
575 * @param pThis Pointer to the shared LsiLogic device state.
576 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
577 */
578static int lsilogicR3HardReset(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC)
579{
580 pThis->enmState = LSILOGICSTATE_RESET;
581 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
582
583 /* The interrupts are masked out. */
584 pThis->uInterruptMask |= LSILOGIC_REG_HOST_INTR_MASK_DOORBELL
585 | LSILOGIC_REG_HOST_INTR_MASK_REPLY;
586 /* Reset interrupt states. */
587 pThis->uInterruptStatus = 0;
588 lsilogicUpdateInterrupt(pDevIns, pThis);
589
590 /* Reset the queues. */
591 pThis->uReplyFreeQueueNextEntryFreeWrite = 0;
592 pThis->uReplyFreeQueueNextAddressRead = 0;
593 pThis->uReplyPostQueueNextEntryFreeWrite = 0;
594 pThis->uReplyPostQueueNextAddressRead = 0;
595 pThis->uRequestQueueNextEntryFreeWrite = 0;
596 pThis->uRequestQueueNextAddressRead = 0;
597
598 /* Disable diagnostic access. */
599 pThis->iDiagnosticAccess = 0;
600 pThis->fDiagnosticEnabled = false;
601 pThis->fDiagRegsEnabled = false;
602
603 /* Set default values. */
604 pThis->cMaxDevices = pThis->cDeviceStates;
605 pThis->cMaxBuses = 1;
606 pThis->cbReplyFrame = 128; /** @todo Figure out where it is needed. */
607 pThis->u16NextHandle = 1;
608 pThis->u32DiagMemAddr = 0;
609
610 lsilogicR3InitializeConfigurationPages(pDevIns, pThis, pThisCC);
611
612 /* Mark that we finished performing the reset. */
613 pThis->enmState = LSILOGICSTATE_READY;
614 return VINF_SUCCESS;
615}
616
617/**
618 * Allocates the configuration pages based on the device.
619 *
620 * @returns nothing.
621 * @param pThis Pointer to the shared LsiLogic device state.
622 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
623 */
624static int lsilogicR3ConfigurationPagesAlloc(PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC)
625{
626 pThisCC->pConfigurationPages = (PMptConfigurationPagesSupported)RTMemAllocZ(sizeof(MptConfigurationPagesSupported));
627 if (!pThisCC->pConfigurationPages)
628 return VERR_NO_MEMORY;
629
630 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
631 {
632 PMptConfigurationPagesSas pPages = &pThisCC->pConfigurationPages->u.SasPages;
633
634 pPages->cbManufacturingPage7 = LSILOGICSCSI_MANUFACTURING7_GET_SIZE(pThis->cPorts);
635 PMptConfigurationPageManufacturing7 pManufacturingPage7 = (PMptConfigurationPageManufacturing7)RTMemAllocZ(pPages->cbManufacturingPage7);
636 AssertPtrReturn(pManufacturingPage7, VERR_NO_MEMORY);
637 pPages->pManufacturingPage7 = pManufacturingPage7;
638
639 /* SAS I/O unit page 0 - Port specific information. */
640 pPages->cbSASIOUnitPage0 = LSILOGICSCSI_SASIOUNIT0_GET_SIZE(pThis->cPorts);
641 PMptConfigurationPageSASIOUnit0 pSASPage0 = (PMptConfigurationPageSASIOUnit0)RTMemAllocZ(pPages->cbSASIOUnitPage0);
642 AssertPtrReturn(pSASPage0, VERR_NO_MEMORY);
643 pPages->pSASIOUnitPage0 = pSASPage0;
644
645 /* SAS I/O unit page 1 - Port specific settings. */
646 pPages->cbSASIOUnitPage1 = LSILOGICSCSI_SASIOUNIT1_GET_SIZE(pThis->cPorts);
647 PMptConfigurationPageSASIOUnit1 pSASPage1 = (PMptConfigurationPageSASIOUnit1)RTMemAllocZ(pPages->cbSASIOUnitPage1);
648 AssertPtrReturn(pSASPage1, VERR_NO_MEMORY);
649 pPages->pSASIOUnitPage1 = pSASPage1;
650
651 pPages->cPHYs = pThis->cPorts;
652 pPages->paPHYs = (PMptPHY)RTMemAllocZ(pPages->cPHYs * sizeof(MptPHY));
653 AssertPtrReturn(pPages->paPHYs, VERR_NO_MEMORY);
654
655 /* Initialize the PHY configuration */
656 for (unsigned i = 0; i < pThis->cPorts; i++)
657 {
658 /* Settings for present devices. */
659 if (pThisCC->paDeviceStates[i].pDrvBase)
660 {
661 PMptSASDevice pSASDevice = (PMptSASDevice)RTMemAllocZ(sizeof(MptSASDevice));
662 AssertPtrReturn(pSASDevice, VERR_NO_MEMORY);
663
664 /* Link into device list. */
665 if (!pPages->cDevices)
666 {
667 pPages->pSASDeviceHead = pSASDevice;
668 pPages->pSASDeviceTail = pSASDevice;
669 pPages->cDevices = 1;
670 }
671 else
672 {
673 pSASDevice->pPrev = pPages->pSASDeviceTail;
674 pPages->pSASDeviceTail->pNext = pSASDevice;
675 pPages->pSASDeviceTail = pSASDevice;
676 pPages->cDevices++;
677 }
678 }
679 }
680 }
681
682 return VINF_SUCCESS;
683}
684
685/**
686 * Frees the configuration pages if allocated.
687 *
688 * @returns nothing.
689 * @param pThis Pointer to the shared LsiLogic device state.
690 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
691 */
692static void lsilogicR3ConfigurationPagesFree(PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC)
693{
694
695 if (pThisCC->pConfigurationPages)
696 {
697 /* Destroy device list if we emulate a SAS controller. */
698 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
699 {
700 PMptConfigurationPagesSas pSasPages = &pThisCC->pConfigurationPages->u.SasPages;
701 PMptSASDevice pSASDeviceCurr = pSasPages->pSASDeviceHead;
702
703 while (pSASDeviceCurr)
704 {
705 PMptSASDevice pFree = pSASDeviceCurr;
706
707 pSASDeviceCurr = pSASDeviceCurr->pNext;
708 RTMemFree(pFree);
709 }
710 if (pSasPages->paPHYs)
711 RTMemFree(pSasPages->paPHYs);
712 if (pSasPages->pManufacturingPage7)
713 RTMemFree(pSasPages->pManufacturingPage7);
714 if (pSasPages->pSASIOUnitPage0)
715 RTMemFree(pSasPages->pSASIOUnitPage0);
716 if (pSasPages->pSASIOUnitPage1)
717 RTMemFree(pSasPages->pSASIOUnitPage1);
718
719 pSasPages->pSASDeviceHead = NULL;
720 pSasPages->paPHYs = NULL;
721 pSasPages->pManufacturingPage7 = NULL;
722 pSasPages->pSASIOUnitPage0 = NULL;
723 pSasPages->pSASIOUnitPage1 = NULL;
724 }
725
726 RTMemFree(pThisCC->pConfigurationPages);
727 pThisCC->pConfigurationPages = NULL;
728 }
729}
730
731/**
732 * Finishes a context reply.
733 *
734 * @returns nothing
735 * @param pDevIns The device instance.
736 * @param pThis Pointer to the shared LsiLogic device state.
737 * @param u32MessageContext The message context ID to post.
738 */
739static void lsilogicR3FinishContextReply(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, uint32_t u32MessageContext)
740{
741 LogFlowFunc(("pThis=%#p u32MessageContext=%#x\n", pThis, u32MessageContext));
742
743 AssertMsg(pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_NOT_IN_USE, ("We are in a doorbell function\n"));
744
745 /* Write message context ID into reply post queue. */
746 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->ReplyPostQueueCritSect, VINF_SUCCESS);
747 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->ReplyPostQueueCritSect, rc);
748
749 /* Check for a entry in the queue. */
750 if (!lsilogicReplyPostQueueGetFrameCount(pThis))
751 {
752 /* Set error code. */
753 lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
754 PDMDevHlpCritSectLeave(pDevIns, &pThis->ReplyPostQueueCritSect);
755 return;
756 }
757
758 /* We have a context reply. */
759 ASMAtomicWriteU32(&pThis->aReplyPostQueue[pThis->uReplyPostQueueNextEntryFreeWrite], u32MessageContext);
760 ASMAtomicIncU32(&pThis->uReplyPostQueueNextEntryFreeWrite);
761 pThis->uReplyPostQueueNextEntryFreeWrite %= pThis->cReplyQueueEntries;
762
763 /* Set interrupt. */
764 lsilogicSetInterrupt(pDevIns, pThis, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR);
765
766 PDMDevHlpCritSectLeave(pDevIns, &pThis->ReplyPostQueueCritSect);
767}
768
769
770/**
771 * Takes necessary steps to finish a reply frame.
772 *
773 * @returns nothing
774 * @param pDevIns The device instance.
775 * @param pThis Pointer to the shared LsiLogic device state.
776 * @param pReply Pointer to the reply message.
777 * @param fForceReplyFifo Flag whether the use of the reply post fifo is forced.
778 */
779static void lsilogicFinishAddressReply(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, PMptReplyUnion pReply, bool fForceReplyFifo)
780{
781 /*
782 * If we are in a doorbell function we set the reply size now and
783 * set the system doorbell status interrupt to notify the guest that
784 * we are ready to send the reply.
785 */
786 if (pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_NOT_IN_USE && !fForceReplyFifo)
787 {
788 /* Set size of the reply in 16bit words. The size in the reply is in 32bit dwords. */
789 pThis->cReplySize = pReply->Header.u8MessageLength * 2;
790 Log(("%s: cReplySize=%u\n", __FUNCTION__, pThis->cReplySize));
791 pThis->uNextReplyEntryRead = 0;
792 lsilogicSetInterrupt(pDevIns, pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
793 }
794 else
795 {
796 /*
797 * The reply queues are only used if the request was fetched from the request queue.
798 * Requests from the request queue are always transferred to R3. So it is not possible
799 * that this case happens in R0 or GC.
800 */
801# ifdef IN_RING3
802 /* Grab a free reply message from the queue. */
803 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->ReplyFreeQueueCritSect, VINF_SUCCESS);
804 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->ReplyFreeQueueCritSect, rc);
805
806 /* Check for a free reply frame. */
807 if (!lsilogicReplyFreeQueueGetFrameCount(pThis))
808 {
809 /* Set error code. */
810 lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
811 PDMDevHlpCritSectLeave(pDevIns, &pThis->ReplyFreeQueueCritSect);
812 return;
813 }
814
815 uint32_t u32ReplyFrameAddressLow = pThis->aReplyFreeQueue[pThis->uReplyFreeQueueNextAddressRead];
816
817 pThis->uReplyFreeQueueNextAddressRead++;
818 pThis->uReplyFreeQueueNextAddressRead %= pThis->cReplyQueueEntries;
819
820 PDMDevHlpCritSectLeave(pDevIns, &pThis->ReplyFreeQueueCritSect);
821
822 /* Build 64bit physical address. */
823 RTGCPHYS GCPhysReplyMessage = LSILOGIC_RTGCPHYS_FROM_U32(pThis->u32HostMFAHighAddr, u32ReplyFrameAddressLow);
824 size_t cbReplyCopied = (pThis->cbReplyFrame < sizeof(MptReplyUnion)) ? pThis->cbReplyFrame : sizeof(MptReplyUnion);
825
826 /* Write reply to guest memory. */
827 PDMDevHlpPCIPhysWriteMeta(pDevIns, GCPhysReplyMessage, pReply, cbReplyCopied);
828
829 /* Write low 32bits of reply frame into post reply queue. */
830 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->ReplyPostQueueCritSect, VINF_SUCCESS);
831 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->ReplyPostQueueCritSect, rc);
832
833 /* Check for a entry in the queue. */
834 if (!lsilogicReplyPostQueueGetFrameCount(pThis))
835 {
836 /* Set error code. */
837 lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
838 PDMDevHlpCritSectLeave(pDevIns, &pThis->ReplyPostQueueCritSect);
839 return;
840 }
841
842 /* We have a address reply. Set the 31th bit to indicate that. */
843 ASMAtomicWriteU32(&pThis->aReplyPostQueue[pThis->uReplyPostQueueNextEntryFreeWrite],
844 RT_BIT(31) | (u32ReplyFrameAddressLow >> 1));
845 ASMAtomicIncU32(&pThis->uReplyPostQueueNextEntryFreeWrite);
846 pThis->uReplyPostQueueNextEntryFreeWrite %= pThis->cReplyQueueEntries;
847
848 if (fForceReplyFifo)
849 {
850 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
851 lsilogicSetInterrupt(pDevIns, pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
852 }
853
854 /* Set interrupt. */
855 lsilogicSetInterrupt(pDevIns, pThis, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR);
856
857 PDMDevHlpCritSectLeave(pDevIns, &pThis->ReplyPostQueueCritSect);
858# else
859 AssertMsgFailed(("This is not allowed to happen.\n"));
860# endif
861 }
862}
863
864
865/**
866 * Tries to find a memory region which covers the given address.
867 *
868 * @returns Pointer to memory region or NULL if not found.
869 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
870 * @param u32Addr The 32bit address to search for.
871 */
872static PLSILOGICMEMREGN lsilogicR3MemRegionFindByAddr(PLSILOGICSCSICC pThisCC, uint32_t u32Addr)
873{
874 PLSILOGICMEMREGN pRegion = NULL;
875
876 PLSILOGICMEMREGN pIt;
877 RTListForEach(&pThisCC->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
878 {
879 if ( u32Addr >= pIt->u32AddrStart
880 && u32Addr <= pIt->u32AddrEnd)
881 {
882 pRegion = pIt;
883 break;
884 }
885 }
886
887 return pRegion;
888}
889
890/**
891 * Frees all allocated memory regions.
892 *
893 * @returns nothing.
894 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
895 */
896static void lsilogicR3MemRegionsFree(PLSILOGICSCSICC pThisCC)
897{
898 PLSILOGICMEMREGN pItNext;
899
900 PLSILOGICMEMREGN pIt;
901 RTListForEachSafe(&pThisCC->ListMemRegns, pIt, pItNext, LSILOGICMEMREGN, NodeList)
902 {
903 RTListNodeRemove(&pIt->NodeList);
904 RTMemFree(pIt);
905 }
906 pThisCC->cbMemRegns = 0;
907}
908
909/**
910 * Inserts a given memory region into the list.
911 *
912 * @returns nothing.
913 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
914 * @param pRegion The region to insert.
915 */
916static void lsilogicR3MemRegionInsert(PLSILOGICSCSICC pThisCC, PLSILOGICMEMREGN pRegion)
917{
918 bool fInserted = false;
919
920 /* Insert at the right position. */
921 PLSILOGICMEMREGN pIt;
922 RTListForEach(&pThisCC->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
923 {
924 if (pRegion->u32AddrEnd < pIt->u32AddrStart)
925 {
926 RTListNodeInsertBefore(&pIt->NodeList, &pRegion->NodeList);
927 fInserted = true;
928 break;
929 }
930 }
931 if (!fInserted)
932 RTListAppend(&pThisCC->ListMemRegns, &pRegion->NodeList);
933}
934
935/**
936 * Count number of memory regions.
937 *
938 * @returns Number of memory regions.
939 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
940 */
941static uint32_t lsilogicR3MemRegionsCount(PLSILOGICSCSICC pThisCC)
942{
943 uint32_t cRegions = 0;
944
945 PLSILOGICMEMREGN pIt;
946 RTListForEach(&pThisCC->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
947 {
948 cRegions++;
949 }
950
951 return cRegions;
952}
953
954/**
955 * Handles a write to the diagnostic data register.
956 *
957 * @returns nothing.
958 * @param pThis Pointer to the shared LsiLogic device state.
959 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
960 * @param u32Data Data to write.
961 */
962static void lsilogicR3DiagRegDataWrite(PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC, uint32_t u32Data)
963{
964 RTCritSectEnter(&pThisCC->CritSectMemRegns);
965
966 PLSILOGICMEMREGN pRegion = lsilogicR3MemRegionFindByAddr(pThisCC, pThis->u32DiagMemAddr);
967 if (pRegion)
968 {
969 uint32_t offRegion = pThis->u32DiagMemAddr - pRegion->u32AddrStart;
970
971 AssertMsg( offRegion % 4 == 0
972 && pThis->u32DiagMemAddr <= pRegion->u32AddrEnd,
973 ("Region offset not on a word boundary or crosses memory region\n"));
974
975 offRegion /= 4;
976 pRegion->au32Data[offRegion] = u32Data;
977 }
978 else
979 {
980 pRegion = NULL;
981
982 /* Create new region, first check whether we can extend another region. */
983 PLSILOGICMEMREGN pIt;
984 RTListForEach(&pThisCC->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
985 {
986 if (pThis->u32DiagMemAddr == pIt->u32AddrEnd + sizeof(uint32_t))
987 {
988 pRegion = pIt;
989 break;
990 }
991 }
992
993 if (pRegion)
994 {
995 /* Reallocate. */
996 RTListNodeRemove(&pRegion->NodeList);
997
998 uint32_t cRegionSizeOld = (pRegion->u32AddrEnd - pRegion->u32AddrStart) / 4 + 1;
999 uint32_t cRegionSizeNew = cRegionSizeOld + 512;
1000
1001 if (pThisCC->cbMemRegns + 512 * sizeof(uint32_t) < LSILOGIC_MEMORY_REGIONS_MAX)
1002 {
1003 PLSILOGICMEMREGN pRegionNew;
1004 pRegionNew = (PLSILOGICMEMREGN)RTMemRealloc(pRegion, RT_UOFFSETOF_DYN(LSILOGICMEMREGN, au32Data[cRegionSizeNew]));
1005 if (pRegionNew)
1006 {
1007 pRegion = pRegionNew;
1008 memset(&pRegion->au32Data[cRegionSizeOld], 0, 512 * sizeof(uint32_t));
1009 pRegion->au32Data[cRegionSizeOld] = u32Data;
1010 pRegion->u32AddrEnd = pRegion->u32AddrStart + (cRegionSizeNew - 1) * sizeof(uint32_t);
1011 pThisCC->cbMemRegns += 512 * sizeof(uint32_t);
1012 }
1013 /* else: Silently fail, there is nothing we can do here and the guest might work nevertheless. */
1014
1015 lsilogicR3MemRegionInsert(pThisCC, pRegion);
1016 }
1017 }
1018 else
1019 {
1020 if (pThisCC->cbMemRegns + 512 * sizeof(uint32_t) < LSILOGIC_MEMORY_REGIONS_MAX)
1021 {
1022 /* Create completely new. */
1023 pRegion = (PLSILOGICMEMREGN)RTMemAllocZ(RT_OFFSETOF(LSILOGICMEMREGN, au32Data[512]));
1024 if (pRegion)
1025 {
1026 pRegion->u32AddrStart = pThis->u32DiagMemAddr;
1027 pRegion->u32AddrEnd = pRegion->u32AddrStart + (512 - 1) * sizeof(uint32_t);
1028 pRegion->au32Data[0] = u32Data;
1029 pThisCC->cbMemRegns += 512 * sizeof(uint32_t);
1030
1031 lsilogicR3MemRegionInsert(pThisCC, pRegion);
1032 }
1033 /* else: Silently fail, there is nothing we can do here and the guest might work nevertheless. */
1034 }
1035 }
1036
1037 }
1038
1039 /* Memory access is always 32bit big. */
1040 pThis->u32DiagMemAddr += sizeof(uint32_t);
1041 RTCritSectLeave(&pThisCC->CritSectMemRegns);
1042}
1043
1044/**
1045 * Handles a read from the diagnostic data register.
1046 *
1047 * @returns nothing.
1048 * @param pThis Pointer to the shared LsiLogic device state.
1049 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
1050 * @param pu32Data Where to store the data.
1051 */
1052static void lsilogicR3DiagRegDataRead(PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC, uint32_t *pu32Data)
1053{
1054 RTCritSectEnter(&pThisCC->CritSectMemRegns);
1055
1056 PLSILOGICMEMREGN pRegion = lsilogicR3MemRegionFindByAddr(pThisCC, pThis->u32DiagMemAddr);
1057 if (pRegion)
1058 {
1059 uint32_t offRegion = pThis->u32DiagMemAddr - pRegion->u32AddrStart;
1060
1061 AssertMsg( offRegion % 4 == 0
1062 && pThis->u32DiagMemAddr <= pRegion->u32AddrEnd,
1063 ("Region offset not on a word boundary or crosses memory region\n"));
1064
1065 offRegion /= 4;
1066 *pu32Data = pRegion->au32Data[offRegion];
1067 }
1068 else /* No region, default value 0. */
1069 *pu32Data = 0;
1070
1071 /* Memory access is always 32bit big. */
1072 pThis->u32DiagMemAddr += sizeof(uint32_t);
1073 RTCritSectLeave(&pThisCC->CritSectMemRegns);
1074}
1075
1076/**
1077 * Handles a write to the diagnostic memory address register.
1078 *
1079 * @returns nothing.
1080 * @param pThis Pointer to the shared LsiLogic device state.
1081 * @param u32Addr Address to write.
1082 */
1083static void lsilogicR3DiagRegAddressWrite(PLSILOGICSCSI pThis, uint32_t u32Addr)
1084{
1085 pThis->u32DiagMemAddr = u32Addr & ~UINT32_C(0x3); /* 32bit alignment. */
1086}
1087
1088/**
1089 * Handles a read from the diagnostic memory address register.
1090 *
1091 * @returns nothing.
1092 * @param pThis Pointer to the shared LsiLogic device state.
1093 * @param pu32Addr Where to store the current address.
1094 */
1095static void lsilogicR3DiagRegAddressRead(PLSILOGICSCSI pThis, uint32_t *pu32Addr)
1096{
1097 *pu32Addr = pThis->u32DiagMemAddr;
1098}
1099
1100/**
1101 * Processes a given Request from the guest
1102 *
1103 * @returns VBox status code.
1104 * @param pDevIns The device instance.
1105 * @param pThis Pointer to the shared LsiLogic device state.
1106 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
1107 * @param pMessageHdr Pointer to the message header of the request.
1108 * @param pReply Pointer to the reply.
1109 */
1110static int lsilogicR3ProcessMessageRequest(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC,
1111 PMptMessageHdr pMessageHdr, PMptReplyUnion pReply)
1112{
1113 int rc = VINF_SUCCESS;
1114 bool fForceReplyPostFifo = false;
1115
1116# ifdef LOG_ENABLED
1117 if (pMessageHdr->u8Function < RT_ELEMENTS(g_apszMPTFunctionNames))
1118 Log(("Message request function: %s\n", g_apszMPTFunctionNames[pMessageHdr->u8Function]));
1119 else
1120 Log(("Message request function: <unknown>\n"));
1121# endif
1122
1123 memset(pReply, 0, sizeof(MptReplyUnion));
1124
1125 switch (pMessageHdr->u8Function)
1126 {
1127 case MPT_MESSAGE_HDR_FUNCTION_SCSI_TASK_MGMT:
1128 {
1129 PMptSCSITaskManagementRequest pTaskMgmtReq = (PMptSCSITaskManagementRequest)pMessageHdr;
1130
1131 LogFlow(("u8TaskType=%u\n", pTaskMgmtReq->u8TaskType));
1132 LogFlow(("u32TaskMessageContext=%#x\n", pTaskMgmtReq->u32TaskMessageContext));
1133
1134 pReply->SCSITaskManagement.u8MessageLength = 6; /* 6 32bit dwords. */
1135 pReply->SCSITaskManagement.u8TaskType = pTaskMgmtReq->u8TaskType;
1136 pReply->SCSITaskManagement.u32TerminationCount = 0;
1137 fForceReplyPostFifo = true;
1138 break;
1139 }
1140 case MPT_MESSAGE_HDR_FUNCTION_IOC_INIT:
1141 {
1142 /*
1143 * This request sets the I/O controller to the
1144 * operational state.
1145 */
1146 PMptIOCInitRequest pIOCInitReq = (PMptIOCInitRequest)pMessageHdr;
1147
1148 /* Update configuration values. */
1149 pThis->enmWhoInit = (LSILOGICWHOINIT)pIOCInitReq->u8WhoInit;
1150 pThis->cbReplyFrame = pIOCInitReq->u16ReplyFrameSize;
1151 pThis->cMaxBuses = pIOCInitReq->u8MaxBuses;
1152 pThis->cMaxDevices = pIOCInitReq->u8MaxDevices;
1153 pThis->u32HostMFAHighAddr = pIOCInitReq->u32HostMfaHighAddr;
1154 pThis->u32SenseBufferHighAddr = pIOCInitReq->u32SenseBufferHighAddr;
1155
1156 if (pThis->enmState == LSILOGICSTATE_READY)
1157 {
1158 pThis->enmState = LSILOGICSTATE_OPERATIONAL;
1159 }
1160
1161 /* Return reply. */
1162 pReply->IOCInit.u8MessageLength = 5;
1163 pReply->IOCInit.u8WhoInit = pThis->enmWhoInit;
1164 pReply->IOCInit.u8MaxDevices = pThis->cMaxDevices;
1165 pReply->IOCInit.u8MaxBuses = pThis->cMaxBuses;
1166 break;
1167 }
1168 case MPT_MESSAGE_HDR_FUNCTION_IOC_FACTS:
1169 {
1170 pReply->IOCFacts.u8MessageLength = 15; /* 15 32bit dwords. */
1171
1172 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
1173 {
1174 pReply->IOCFacts.u16MessageVersion = 0x0102; /* Version from the specification. */
1175 pReply->IOCFacts.u8NumberOfPorts = pThis->cPorts;
1176 }
1177 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
1178 {
1179 pReply->IOCFacts.u16MessageVersion = 0x0105; /* Version from the specification. */
1180 pReply->IOCFacts.u8NumberOfPorts = pThis->cPorts;
1181 }
1182 else
1183 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
1184
1185 pReply->IOCFacts.u8IOCNumber = 0; /* PCI function number. */
1186 pReply->IOCFacts.u16IOCExceptions = 0;
1187 pReply->IOCFacts.u8MaxChainDepth = LSILOGICSCSI_MAXIMUM_CHAIN_DEPTH;
1188 pReply->IOCFacts.u8WhoInit = pThis->enmWhoInit;
1189 pReply->IOCFacts.u8BlockSize = 12; /* Block size in 32bit dwords. This is the largest request we can get (SCSI I/O). */
1190 pReply->IOCFacts.u8Flags = 0; /* Bit 0 is set if the guest must upload the FW prior to using the controller. Obviously not needed here. */
1191 pReply->IOCFacts.u16ReplyQueueDepth = pThis->cReplyQueueEntries - 1; /* One entry is always free. */
1192 pReply->IOCFacts.u16RequestFrameSize = 128; /** @todo Figure out where it is needed. */
1193 pReply->IOCFacts.u32CurrentHostMFAHighAddr = pThis->u32HostMFAHighAddr;
1194 pReply->IOCFacts.u16GlobalCredits = pThis->cRequestQueueEntries - 1; /* One entry is always free. */
1195
1196 pReply->IOCFacts.u8EventState = 0; /* Event notifications not enabled. */
1197 pReply->IOCFacts.u32CurrentSenseBufferHighAddr = pThis->u32SenseBufferHighAddr;
1198 pReply->IOCFacts.u16CurReplyFrameSize = pThis->cbReplyFrame;
1199 pReply->IOCFacts.u8MaxDevices = pThis->cMaxDevices;
1200 pReply->IOCFacts.u8MaxBuses = pThis->cMaxBuses;
1201
1202 pReply->IOCFacts.u16ProductID = 0xcafe; /* Our own product ID :) */
1203 pReply->IOCFacts.u32FwImageSize = 0; /* No image needed. */
1204 pReply->IOCFacts.u32FWVersion = 0;
1205
1206 /* Check for a valid firmware image in the IOC memory which was downloaded by the guest earlier and use that. */
1207 RTCritSectEnter(&pThisCC->CritSectMemRegns);
1208 PLSILOGICMEMREGN pRegion = lsilogicR3MemRegionFindByAddr(pThisCC, LSILOGIC_FWIMGHDR_LOAD_ADDRESS);
1209 if (pRegion)
1210 {
1211 uint32_t offImgHdr = (LSILOGIC_FWIMGHDR_LOAD_ADDRESS - pRegion->u32AddrStart);
1212 if (pRegion->u32AddrEnd - offImgHdr + 1 >= sizeof(FwImageHdr)) /* End address is inclusive. */
1213 {
1214 PFwImageHdr pFwImgHdr = (PFwImageHdr)&pRegion->au32Data[offImgHdr / 4];
1215
1216 /* Check for the signature. */
1217 /** @todo Checksum validation. */
1218 if ( pFwImgHdr->u32Signature1 == LSILOGIC_FWIMGHDR_SIGNATURE1
1219 && pFwImgHdr->u32Signature2 == LSILOGIC_FWIMGHDR_SIGNATURE2
1220 && pFwImgHdr->u32Signature3 == LSILOGIC_FWIMGHDR_SIGNATURE3)
1221 {
1222 LogFlowFunc(("IOC Facts: Found valid firmware image header in memory, using version (%#x), size (%d) and product ID (%#x) from there\n",
1223 pFwImgHdr->u32FwVersion, pFwImgHdr->u32ImageSize, pFwImgHdr->u16ProductId));
1224
1225 pReply->IOCFacts.u16ProductID = pFwImgHdr->u16ProductId;
1226 pReply->IOCFacts.u32FwImageSize = pFwImgHdr->u32ImageSize;
1227 pReply->IOCFacts.u32FWVersion = pFwImgHdr->u32FwVersion;
1228 }
1229 }
1230 }
1231 RTCritSectLeave(&pThisCC->CritSectMemRegns);
1232 break;
1233 }
1234 case MPT_MESSAGE_HDR_FUNCTION_PORT_FACTS:
1235 {
1236 PMptPortFactsRequest pPortFactsReq = (PMptPortFactsRequest)pMessageHdr;
1237
1238 pReply->PortFacts.u8MessageLength = 10;
1239 pReply->PortFacts.u8PortNumber = pPortFactsReq->u8PortNumber;
1240
1241 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
1242 {
1243 /* This controller only supports one bus with bus number 0. */
1244 if (pPortFactsReq->u8PortNumber >= pThis->cPorts)
1245 {
1246 pReply->PortFacts.u8PortType = 0; /* Not existant. */
1247 }
1248 else
1249 {
1250 pReply->PortFacts.u8PortType = 0x01; /* SCSI Port. */
1251 pReply->PortFacts.u16MaxDevices = LSILOGICSCSI_PCI_SPI_DEVICES_PER_BUS_MAX;
1252 pReply->PortFacts.u16ProtocolFlags = RT_BIT(3) | RT_BIT(0); /* SCSI initiator and LUN supported. */
1253 pReply->PortFacts.u16PortSCSIID = 7; /* Default */
1254 pReply->PortFacts.u16MaxPersistentIDs = 0;
1255 pReply->PortFacts.u16MaxPostedCmdBuffers = 0; /* Only applies for target mode which we dont support. */
1256 pReply->PortFacts.u16MaxLANBuckets = 0; /* Only for the LAN controller. */
1257 }
1258 }
1259 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
1260 {
1261 if (pPortFactsReq->u8PortNumber >= pThis->cPorts)
1262 {
1263 pReply->PortFacts.u8PortType = 0; /* Not existant. */
1264 }
1265 else
1266 {
1267 pReply->PortFacts.u8PortType = 0x30; /* SAS Port. */
1268 pReply->PortFacts.u16MaxDevices = pThis->cPorts;
1269 pReply->PortFacts.u16ProtocolFlags = RT_BIT(3) | RT_BIT(0); /* SCSI initiator and LUN supported. */
1270 pReply->PortFacts.u16PortSCSIID = pThis->cPorts;
1271 pReply->PortFacts.u16MaxPersistentIDs = 0;
1272 pReply->PortFacts.u16MaxPostedCmdBuffers = 0; /* Only applies for target mode which we dont support. */
1273 pReply->PortFacts.u16MaxLANBuckets = 0; /* Only for the LAN controller. */
1274 }
1275 }
1276 else
1277 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
1278 break;
1279 }
1280 case MPT_MESSAGE_HDR_FUNCTION_PORT_ENABLE:
1281 {
1282 /*
1283 * The port enable request notifies the IOC to make the port available and perform
1284 * appropriate discovery on the associated link.
1285 */
1286 PMptPortEnableRequest pPortEnableReq = (PMptPortEnableRequest)pMessageHdr;
1287
1288 pReply->PortEnable.u8MessageLength = 5;
1289 pReply->PortEnable.u8PortNumber = pPortEnableReq->u8PortNumber;
1290 break;
1291 }
1292 case MPT_MESSAGE_HDR_FUNCTION_EVENT_NOTIFICATION:
1293 {
1294 PMptEventNotificationRequest pEventNotificationReq = (PMptEventNotificationRequest)pMessageHdr;
1295
1296 if (pEventNotificationReq->u8Switch)
1297 pThis->fEventNotificationEnabled = true;
1298 else
1299 pThis->fEventNotificationEnabled = false;
1300
1301 pReply->EventNotification.u16EventDataLength = 1; /* 1 32bit D-Word. */
1302 pReply->EventNotification.u8MessageLength = 8;
1303 pReply->EventNotification.u8MessageFlags = (1 << 7);
1304 pReply->EventNotification.u8AckRequired = 0;
1305 pReply->EventNotification.u32Event = MPT_EVENT_EVENT_CHANGE;
1306 pReply->EventNotification.u32EventContext = 0;
1307 pReply->EventNotification.u32EventData = pThis->fEventNotificationEnabled ? 1 : 0;
1308
1309 break;
1310 }
1311 case MPT_MESSAGE_HDR_FUNCTION_EVENT_ACK:
1312 {
1313 AssertMsgFailed(("todo"));
1314 break;
1315 }
1316 case MPT_MESSAGE_HDR_FUNCTION_CONFIG:
1317 {
1318 PMptConfigurationRequest pConfigurationReq = (PMptConfigurationRequest)pMessageHdr;
1319
1320 rc = lsilogicR3ProcessConfigurationRequest(pDevIns, pThis, pThisCC, pConfigurationReq, &pReply->Configuration);
1321 AssertRC(rc);
1322 break;
1323 }
1324 case MPT_MESSAGE_HDR_FUNCTION_FW_UPLOAD:
1325 {
1326 PMptFWUploadRequest pFWUploadReq = (PMptFWUploadRequest)pMessageHdr;
1327
1328 pReply->FWUpload.u8ImageType = pFWUploadReq->u8ImageType;
1329 pReply->FWUpload.u8MessageLength = 6;
1330 pReply->FWUpload.u32ActualImageSize = 0;
1331 break;
1332 }
1333 case MPT_MESSAGE_HDR_FUNCTION_FW_DOWNLOAD:
1334 {
1335 //PMptFWDownloadRequest pFWDownloadReq = (PMptFWDownloadRequest)pMessageHdr;
1336
1337 pReply->FWDownload.u8MessageLength = 5;
1338 LogFlowFunc(("FW Download request issued\n"));
1339 break;
1340 }
1341 case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST: /* Should be handled already. */
1342 default:
1343 AssertMsgFailed(("Invalid request function %#x\n", pMessageHdr->u8Function));
1344 }
1345
1346 /* Copy common bits from request message frame to reply. */
1347 pReply->Header.u8Function = pMessageHdr->u8Function;
1348 pReply->Header.u32MessageContext = pMessageHdr->u32MessageContext;
1349
1350 lsilogicFinishAddressReply(pDevIns, pThis, pReply, fForceReplyPostFifo);
1351 return rc;
1352}
1353
1354#endif /* IN_RING3 */
1355
1356/**
1357 * Writes a value to a register at a given offset.
1358 *
1359 * @returns Strict VBox status code.
1360 * @param pDevIns The devie instance.
1361 * @param pThis Pointer to the shared LsiLogic device state.
1362 * @param offReg Offset of the register to write.
1363 * @param u32 The value being written.
1364 */
1365static VBOXSTRICTRC lsilogicRegisterWrite(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, uint32_t offReg, uint32_t u32)
1366{
1367 LogFlowFunc(("pThis=%#p offReg=%#x u32=%#x\n", pThis, offReg, u32));
1368 switch (offReg)
1369 {
1370 case LSILOGIC_REG_REPLY_QUEUE:
1371 {
1372 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->ReplyFreeQueueWriteCritSect, VINF_IOM_R3_MMIO_WRITE);
1373 if (rc != VINF_SUCCESS)
1374 return rc;
1375 /* Add the entry to the reply free queue. */
1376 ASMAtomicWriteU32(&pThis->aReplyFreeQueue[pThis->uReplyFreeQueueNextEntryFreeWrite], u32);
1377 pThis->uReplyFreeQueueNextEntryFreeWrite++;
1378 pThis->uReplyFreeQueueNextEntryFreeWrite %= pThis->cReplyQueueEntries;
1379 PDMDevHlpCritSectLeave(pDevIns, &pThis->ReplyFreeQueueWriteCritSect);
1380 break;
1381 }
1382 case LSILOGIC_REG_REQUEST_QUEUE:
1383 {
1384 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->RequestQueueCritSect, VINF_IOM_R3_MMIO_WRITE);
1385 if (rc != VINF_SUCCESS)
1386 return rc;
1387
1388 uint32_t uNextWrite = ASMAtomicReadU32(&pThis->uRequestQueueNextEntryFreeWrite);
1389
1390 ASMAtomicWriteU32(&pThis->aRequestQueue[uNextWrite], u32);
1391
1392 /*
1393 * Don't update the value in place. It can happen that we get preempted
1394 * after the increment but before the modulo.
1395 * Another EMT will read the wrong value when processing the queues
1396 * and hang in an endless loop creating thousands of requests.
1397 */
1398 uNextWrite++;
1399 uNextWrite %= pThis->cRequestQueueEntries;
1400 ASMAtomicWriteU32(&pThis->uRequestQueueNextEntryFreeWrite, uNextWrite);
1401 PDMDevHlpCritSectLeave(pDevIns, &pThis->RequestQueueCritSect);
1402
1403 /* Send notification to R3 if there is not one sent already. Do this
1404 * only if the worker thread is not sleeping or might go sleeping. */
1405 if (!ASMAtomicXchgBool(&pThis->fNotificationSent, true))
1406 {
1407 if (ASMAtomicReadBool(&pThis->fWrkThreadSleeping))
1408 {
1409 LogFlowFunc(("Signal event semaphore\n"));
1410 rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEvtProcess);
1411 AssertRC(rc);
1412 }
1413 }
1414 break;
1415 }
1416 case LSILOGIC_REG_DOORBELL:
1417 {
1418 /*
1419 * When the guest writes to this register a real device would set the
1420 * doorbell status bit in the interrupt status register to indicate that the IOP
1421 * has still to process the message.
1422 * The guest needs to wait with posting new messages here until the bit is cleared.
1423 * Because the guest is not continuing execution while we are here we can skip this.
1424 */
1425 if (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_NOT_IN_USE)
1426 {
1427 uint32_t uFunction = LSILOGIC_REG_DOORBELL_GET_FUNCTION(u32);
1428
1429 switch (uFunction)
1430 {
1431 case LSILOGIC_DOORBELL_FUNCTION_IO_UNIT_RESET:
1432 case LSILOGIC_DOORBELL_FUNCTION_IOC_MSG_UNIT_RESET:
1433 {
1434 /*
1435 * The I/O unit reset does much more on real hardware like
1436 * reloading the firmware, nothing we need to do here,
1437 * so this is like the IOC message unit reset.
1438 */
1439 pThis->enmState = LSILOGICSTATE_RESET;
1440
1441 /* Reset interrupt status. */
1442 pThis->uInterruptStatus = 0;
1443 lsilogicUpdateInterrupt(pDevIns, pThis);
1444
1445 /* Reset the queues. */
1446 pThis->uReplyFreeQueueNextEntryFreeWrite = 0;
1447 pThis->uReplyFreeQueueNextAddressRead = 0;
1448 pThis->uReplyPostQueueNextEntryFreeWrite = 0;
1449 pThis->uReplyPostQueueNextAddressRead = 0;
1450 pThis->uRequestQueueNextEntryFreeWrite = 0;
1451 pThis->uRequestQueueNextAddressRead = 0;
1452
1453 /* Only the IOC message unit reset transisionts to the ready state. */
1454 if (uFunction == LSILOGIC_DOORBELL_FUNCTION_IOC_MSG_UNIT_RESET)
1455 pThis->enmState = LSILOGICSTATE_READY;
1456 break;
1457 }
1458 case LSILOGIC_DOORBELL_FUNCTION_HANDSHAKE:
1459 {
1460 pThis->cMessage = LSILOGIC_REG_DOORBELL_GET_SIZE(u32);
1461 pThis->iMessage = 0;
1462
1463 /* This is not supposed to happen and the result is undefined, just stay in the current state. */
1464 AssertMsgReturn(pThis->cMessage <= RT_ELEMENTS(pThis->aMessage),
1465 ("Message doesn't fit into the buffer, cMessage=%u", pThis->cMessage),
1466 VINF_SUCCESS);
1467
1468 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_FN_HANDSHAKE;
1469 /* Update the interrupt status to notify the guest that a doorbell function was started. */
1470 lsilogicSetInterrupt(pDevIns, pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1471 break;
1472 }
1473 case LSILOGIC_DOORBELL_FUNCTION_REPLY_FRAME_REMOVAL:
1474 {
1475 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_LOW;
1476 /* Update the interrupt status to notify the guest that a doorbell function was started. */
1477 lsilogicSetInterrupt(pDevIns, pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1478 break;
1479 }
1480 default:
1481 AssertMsgFailed(("Unknown function %u to perform\n", uFunction));
1482 }
1483 }
1484 else if (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_FN_HANDSHAKE)
1485 {
1486 /*
1487 * We are already performing a doorbell function.
1488 * Get the remaining parameters, ignore any excess writes.
1489 */
1490 AssertMsgReturn(pThis->iMessage < pThis->cMessage,
1491 ("Guest is trying to write more than was indicated in the handshake\n"),
1492 VINF_SUCCESS);
1493
1494 /*
1495 * If the last byte of the message is written, force a switch to R3 because some requests might force
1496 * a reply through the FIFO which cannot be handled in GC or R0.
1497 */
1498#ifndef IN_RING3
1499 if (pThis->iMessage == pThis->cMessage - 1)
1500 return VINF_IOM_R3_MMIO_WRITE;
1501#endif
1502 pThis->aMessage[pThis->iMessage++] = u32;
1503#ifdef IN_RING3
1504 if (pThis->iMessage == pThis->cMessage)
1505 {
1506 int rc = lsilogicR3ProcessMessageRequest(pDevIns, pThis, PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC),
1507 (PMptMessageHdr)pThis->aMessage, &pThis->ReplyBuffer);
1508 AssertRC(rc);
1509 }
1510#endif
1511 }
1512 break;
1513 }
1514 case LSILOGIC_REG_HOST_INTR_STATUS:
1515 {
1516 /*
1517 * Clear the bits the guest wants except the system doorbell interrupt and the IO controller
1518 * status bit.
1519 * The former bit is always cleared no matter what the guest writes to the register and
1520 * the latter one is read only.
1521 */
1522 ASMAtomicAndU32(&pThis->uInterruptStatus, ~LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1523
1524 /*
1525 * Check if there is still a doorbell function in progress. Set the
1526 * system doorbell interrupt bit again if it is.
1527 * We do not use lsilogicSetInterrupt here because the interrupt status
1528 * is updated afterwards anyway.
1529 */
1530 if ( (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_FN_HANDSHAKE)
1531 && (pThis->cMessage == pThis->iMessage))
1532 {
1533 if (pThis->uNextReplyEntryRead == pThis->cReplySize)
1534 {
1535 /* Reply finished. Reset doorbell in progress status. */
1536 Log(("%s: Doorbell function finished\n", __FUNCTION__));
1537 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
1538 }
1539 ASMAtomicOrU32(&pThis->uInterruptStatus, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1540 }
1541 else if ( pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_NOT_IN_USE
1542 && pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_FN_HANDSHAKE)
1543 {
1544 /* Reply frame removal, check whether the reply free queue is empty. */
1545 if ( pThis->uReplyFreeQueueNextAddressRead == pThis->uReplyFreeQueueNextEntryFreeWrite
1546 && pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW)
1547 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
1548 ASMAtomicOrU32(&pThis->uInterruptStatus, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1549 }
1550
1551 lsilogicUpdateInterrupt(pDevIns, pThis);
1552 break;
1553 }
1554 case LSILOGIC_REG_HOST_INTR_MASK:
1555 {
1556 ASMAtomicWriteU32(&pThis->uInterruptMask, u32 & LSILOGIC_REG_HOST_INTR_MASK_W_MASK);
1557 lsilogicUpdateInterrupt(pDevIns, pThis);
1558 break;
1559 }
1560 case LSILOGIC_REG_WRITE_SEQUENCE:
1561 {
1562 if (pThis->fDiagnosticEnabled)
1563 {
1564 /* Any value will cause a reset and disabling access. */
1565 pThis->fDiagnosticEnabled = false;
1566 pThis->iDiagnosticAccess = 0;
1567 pThis->fDiagRegsEnabled = false;
1568 }
1569 else if ((u32 & 0xf) == g_lsilogicDiagnosticAccess[pThis->iDiagnosticAccess])
1570 {
1571 pThis->iDiagnosticAccess++;
1572 if (pThis->iDiagnosticAccess == RT_ELEMENTS(g_lsilogicDiagnosticAccess))
1573 {
1574 /*
1575 * Key sequence successfully written. Enable access to diagnostic
1576 * memory and register.
1577 */
1578 pThis->fDiagnosticEnabled = true;
1579 }
1580 }
1581 else
1582 {
1583 /* Wrong value written - reset to beginning. */
1584 pThis->iDiagnosticAccess = 0;
1585 }
1586 break;
1587 }
1588 case LSILOGIC_REG_HOST_DIAGNOSTIC:
1589 {
1590 if (pThis->fDiagnosticEnabled)
1591 {
1592#ifndef IN_RING3
1593 return VINF_IOM_R3_MMIO_WRITE;
1594#else
1595 if (u32 & LSILOGIC_REG_HOST_DIAGNOSTIC_RESET_ADAPTER)
1596 lsilogicR3HardReset(pDevIns, pThis, PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC));
1597 else if (u32 & LSILOGIC_REG_HOST_DIAGNOSTIC_DIAG_RW_ENABLE)
1598 pThis->fDiagRegsEnabled = true;
1599#endif
1600 }
1601 break;
1602 }
1603 case LSILOGIC_REG_DIAG_RW_DATA:
1604 {
1605 if (pThis->fDiagRegsEnabled)
1606 {
1607#ifndef IN_RING3
1608 return VINF_IOM_R3_MMIO_WRITE;
1609#else
1610 lsilogicR3DiagRegDataWrite(pThis, PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC), u32);
1611#endif
1612 }
1613 break;
1614 }
1615 case LSILOGIC_REG_DIAG_RW_ADDRESS:
1616 {
1617 if (pThis->fDiagRegsEnabled)
1618 {
1619#ifndef IN_RING3
1620 return VINF_IOM_R3_MMIO_WRITE;
1621#else
1622 lsilogicR3DiagRegAddressWrite(pThis, u32);
1623#endif
1624 }
1625 break;
1626 }
1627 default: /* Ignore. */
1628 {
1629 break;
1630 }
1631 }
1632 return VINF_SUCCESS;
1633}
1634
1635/**
1636 * Reads the content of a register at a given offset.
1637 *
1638 * @returns VBox status code.
1639 * @param pDevIns The device instance.
1640 * @param pThis Pointer to the shared LsiLogic device state.
1641 * @param offReg Offset of the register to read.
1642 * @param pu32 Where to store the content of the register.
1643 */
1644static VBOXSTRICTRC lsilogicRegisterRead(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, uint32_t offReg, uint32_t *pu32)
1645{
1646 int rc = VINF_SUCCESS;
1647 uint32_t u32 = 0;
1648 Assert(!(offReg & 3));
1649
1650 /* Align to a 4 byte offset. */
1651 switch (offReg)
1652 {
1653 case LSILOGIC_REG_REPLY_QUEUE:
1654 {
1655 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->ReplyPostQueueCritSect, VINF_IOM_R3_MMIO_READ);
1656 if (rc != VINF_SUCCESS)
1657 break;
1658
1659 uint32_t idxReplyPostQueueWrite = ASMAtomicUoReadU32(&pThis->uReplyPostQueueNextEntryFreeWrite);
1660 uint32_t idxReplyPostQueueRead = ASMAtomicUoReadU32(&pThis->uReplyPostQueueNextAddressRead);
1661
1662 if (idxReplyPostQueueWrite != idxReplyPostQueueRead)
1663 {
1664 u32 = pThis->aReplyPostQueue[idxReplyPostQueueRead];
1665 idxReplyPostQueueRead++;
1666 idxReplyPostQueueRead %= pThis->cReplyQueueEntries;
1667 ASMAtomicWriteU32(&pThis->uReplyPostQueueNextAddressRead, idxReplyPostQueueRead);
1668 }
1669 else
1670 {
1671 /* The reply post queue is empty. Reset interrupt. */
1672 u32 = UINT32_C(0xffffffff);
1673 lsilogicClearInterrupt(pDevIns, pThis, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR);
1674 }
1675 PDMDevHlpCritSectLeave(pDevIns, &pThis->ReplyPostQueueCritSect);
1676
1677 Log(("%s: Returning address %#x\n", __FUNCTION__, u32));
1678 break;
1679 }
1680 case LSILOGIC_REG_DOORBELL:
1681 {
1682 u32 = LSILOGIC_REG_DOORBELL_SET_STATE(pThis->enmState);
1683 u32 |= LSILOGIC_REG_DOORBELL_SET_USED(pThis->enmDoorbellState);
1684 u32 |= LSILOGIC_REG_DOORBELL_SET_WHOINIT(pThis->enmWhoInit);
1685 /*
1686 * If there is a doorbell function in progress we pass the return value
1687 * instead of the status code. We transfer 16bit of the reply
1688 * during one read.
1689 */
1690 switch (pThis->enmDoorbellState)
1691 {
1692 case LSILOGICDOORBELLSTATE_NOT_IN_USE:
1693 /* We return the status code of the I/O controller. */
1694 u32 |= pThis->u16IOCFaultCode;
1695 break;
1696 case LSILOGICDOORBELLSTATE_FN_HANDSHAKE:
1697 /* Return next 16bit value. */
1698 if (pThis->uNextReplyEntryRead < pThis->cReplySize)
1699 u32 |= pThis->ReplyBuffer.au16Reply[pThis->uNextReplyEntryRead++];
1700 lsilogicSetInterrupt(pDevIns, pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1701 break;
1702 case LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_LOW:
1703 {
1704 uint32_t cReplyFrames = lsilogicReplyFreeQueueGetFrameCount(pThis);
1705
1706 u32 |= cReplyFrames & UINT32_C(0xffff);
1707 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_HIGH;
1708 lsilogicSetInterrupt(pDevIns, pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1709 break;
1710 }
1711 case LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_HIGH:
1712 {
1713 uint32_t cReplyFrames = lsilogicReplyFreeQueueGetFrameCount(pThis);
1714
1715 u32 |= cReplyFrames >> 16;
1716 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW;
1717 lsilogicSetInterrupt(pDevIns, pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1718 break;
1719 }
1720 case LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW:
1721 if (pThis->uReplyFreeQueueNextEntryFreeWrite != pThis->uReplyFreeQueueNextAddressRead)
1722 {
1723 u32 |= pThis->aReplyFreeQueue[pThis->uReplyFreeQueueNextAddressRead] & UINT32_C(0xffff);
1724 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_HIGH;
1725 lsilogicSetInterrupt(pDevIns, pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1726 }
1727 break;
1728 case LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_HIGH:
1729 u32 |= pThis->aReplyFreeQueue[pThis->uReplyFreeQueueNextAddressRead] >> 16;
1730 pThis->uReplyFreeQueueNextAddressRead++;
1731 pThis->uReplyFreeQueueNextAddressRead %= pThis->cReplyQueueEntries;
1732 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW;
1733 lsilogicSetInterrupt(pDevIns, pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1734 break;
1735 default:
1736 AssertMsgFailed(("Invalid doorbell state %d\n", pThis->enmDoorbellState));
1737 }
1738
1739 break;
1740 }
1741 case LSILOGIC_REG_HOST_INTR_STATUS:
1742 {
1743 u32 = ASMAtomicReadU32(&pThis->uInterruptStatus);
1744 break;
1745 }
1746 case LSILOGIC_REG_HOST_INTR_MASK:
1747 {
1748 u32 = ASMAtomicReadU32(&pThis->uInterruptMask);
1749 break;
1750 }
1751 case LSILOGIC_REG_HOST_DIAGNOSTIC:
1752 {
1753 if (pThis->fDiagnosticEnabled)
1754 u32 |= LSILOGIC_REG_HOST_DIAGNOSTIC_DRWE;
1755 if (pThis->fDiagRegsEnabled)
1756 u32 |= LSILOGIC_REG_HOST_DIAGNOSTIC_DIAG_RW_ENABLE;
1757 break;
1758 }
1759 case LSILOGIC_REG_DIAG_RW_DATA:
1760 {
1761 if (pThis->fDiagRegsEnabled)
1762 {
1763#ifndef IN_RING3
1764 return VINF_IOM_R3_MMIO_READ;
1765#else
1766 lsilogicR3DiagRegDataRead(pThis, PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC), &u32);
1767#endif
1768 }
1769 }
1770 RT_FALL_THRU();
1771 case LSILOGIC_REG_DIAG_RW_ADDRESS:
1772 {
1773 if (pThis->fDiagRegsEnabled)
1774 {
1775#ifndef IN_RING3
1776 return VINF_IOM_R3_MMIO_READ;
1777#else
1778 lsilogicR3DiagRegAddressRead(pThis, &u32);
1779#endif
1780 }
1781 }
1782 RT_FALL_THRU();
1783 case LSILOGIC_REG_TEST_BASE_ADDRESS: /* The spec doesn't say anything about these registers, so we just ignore them */
1784 default: /* Ignore. */
1785 {
1786 /** @todo LSILOGIC_REG_DIAG_* should return all F's when accessed by MMIO. We
1787 * return 0. Likely to apply to undefined offsets as well. */
1788 break;
1789 }
1790 }
1791
1792 *pu32 = u32;
1793 LogFlowFunc(("pThis=%#p offReg=%#x u32=%#x\n", pThis, offReg, u32));
1794 return rc;
1795}
1796
1797/**
1798 * @callback_method_impl{FNIOMIOPORTNEWOUT}
1799 */
1800static DECLCALLBACK(VBOXSTRICTRC)
1801lsilogicIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
1802{
1803 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
1804 VBOXSTRICTRC rcStrict;
1805 RT_NOREF2(pvUser, cb);
1806
1807 if (!(offPort & 3))
1808 {
1809 rcStrict = lsilogicRegisterWrite(pDevIns, pThis, offPort, u32);
1810 if (rcStrict == VINF_IOM_R3_MMIO_WRITE)
1811 rcStrict = VINF_IOM_R3_IOPORT_WRITE;
1812 }
1813 else
1814 {
1815 Log(("lsilogicIOPortWrite: Ignoring misaligned write - offPort=%#x u32=%#x cb=%#x\n", offPort, u32, cb));
1816 rcStrict = VINF_SUCCESS;
1817 }
1818
1819 return rcStrict;
1820}
1821
1822/**
1823 * @callback_method_impl{FNIOMIOPORTNEWIN}
1824 */
1825static DECLCALLBACK(VBOXSTRICTRC)
1826lsilogicIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
1827{
1828 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
1829 RT_NOREF_PV(pvUser);
1830 RT_NOREF_PV(cb);
1831
1832 VBOXSTRICTRC rcStrict = lsilogicRegisterRead(pDevIns, pThis, offPort & ~(uint32_t)3, pu32);
1833 if (rcStrict == VINF_IOM_R3_MMIO_READ)
1834 rcStrict = VINF_IOM_R3_IOPORT_READ;
1835
1836 return rcStrict;
1837}
1838
1839/**
1840 * @callback_method_impl{FNIOMMMIONEWWRITE}
1841 */
1842static DECLCALLBACK(VBOXSTRICTRC) lsilogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
1843{
1844 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
1845 uint32_t u32;
1846 RT_NOREF_PV(pvUser);
1847
1848 /* See comments in lsilogicR3Construct regarding size and alignment. */
1849 if (cb == 4)
1850 u32 = *(uint32_t const *)pv;
1851 else
1852 {
1853 if (cb > 4)
1854 u32 = *(uint32_t const *)pv;
1855 else if (cb >= 2)
1856 u32 = *(uint16_t const *)pv;
1857 else
1858 u32 = *(uint8_t const *)pv;
1859 Log(("lsilogicMMIOWrite: Non-DWORD write access - off=%#RGp u32=%#x cb=%#x\n", off, u32, cb));
1860 }
1861
1862 VBOXSTRICTRC rcStrict;
1863 if (!(off & 3))
1864 rcStrict = lsilogicRegisterWrite(pDevIns, pThis, (uint32_t)off, u32);
1865 else
1866 {
1867 Log(("lsilogicMMIOWrite: Ignoring misaligned write - off=%#RGp u32=%#x cb=%#x\n", off, u32, cb));
1868 rcStrict = VINF_SUCCESS;
1869 }
1870 return rcStrict;
1871}
1872
1873/**
1874 * @callback_method_impl{FNIOMMMIONEWREAD}
1875 */
1876static DECLCALLBACK(VBOXSTRICTRC) lsilogicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
1877{
1878 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
1879 Assert(!(off & 3)); Assert(cb == 4); /* If any of these trigger you've changed the registration flags or IOM is busted. */
1880 RT_NOREF2(pvUser, cb);
1881
1882 return lsilogicRegisterRead(pDevIns, pThis, off, (uint32_t *)pv);
1883}
1884
1885/**
1886 * @callback_method_impl{FNIOMMMIONEWWRITE}
1887 */
1888static DECLCALLBACK(VBOXSTRICTRC)
1889lsilogicDiagnosticWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
1890{
1891 RT_NOREF(pDevIns, pvUser, off, pv, cb);
1892 LogFlowFunc(("pThis=%#p GCPhysAddr=%RGp pv=%#p{%.*Rhxs} cb=%u\n", PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI), off, pv, cb, pv, cb));
1893 return VINF_SUCCESS;
1894}
1895
1896/**
1897 * @callback_method_impl{FNIOMMMIONEWREAD}
1898 */
1899static DECLCALLBACK(VBOXSTRICTRC) lsilogicDiagnosticRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
1900{
1901 RT_NOREF(pDevIns, pvUser, off, pv, cb);
1902 LogFlowFunc(("pThis=%#p off=%RGp pv=%#p{%.*Rhxs} cb=%u\n", PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI), off, pv, cb, pv, cb));
1903 return VINF_SUCCESS;
1904}
1905
1906#ifdef IN_RING3
1907
1908# ifdef LOG_ENABLED
1909/**
1910 * Dump an SG entry.
1911 *
1912 * @returns nothing.
1913 * @param pSGEntry Pointer to the SG entry to dump
1914 */
1915static void lsilogicDumpSGEntry(PMptSGEntryUnion pSGEntry)
1916{
1917 if (LogIsEnabled())
1918 {
1919 switch (pSGEntry->Simple32.u2ElementType)
1920 {
1921 case MPTSGENTRYTYPE_SIMPLE:
1922 {
1923 Log(("%s: Dumping info for SIMPLE SG entry:\n", __FUNCTION__));
1924 Log(("%s: u24Length=%u\n", __FUNCTION__, pSGEntry->Simple32.u24Length));
1925 Log(("%s: fEndOfList=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfList));
1926 Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.f64BitAddress));
1927 Log(("%s: fBufferContainsData=%d\n", __FUNCTION__, pSGEntry->Simple32.fBufferContainsData));
1928 Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.fLocalAddress));
1929 Log(("%s: fEndOfBuffer=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfBuffer));
1930 Log(("%s: fLastElement=%d\n", __FUNCTION__, pSGEntry->Simple32.fLastElement));
1931 Log(("%s: u32DataBufferAddressLow=%u\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow));
1932 if (pSGEntry->Simple32.f64BitAddress)
1933 {
1934 Log(("%s: u32DataBufferAddressHigh=%u\n", __FUNCTION__, pSGEntry->Simple64.u32DataBufferAddressHigh));
1935 Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__,
1936 ((uint64_t)pSGEntry->Simple64.u32DataBufferAddressHigh << 32)
1937 | pSGEntry->Simple64.u32DataBufferAddressLow));
1938 }
1939 else
1940 Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow));
1941
1942 break;
1943 }
1944 case MPTSGENTRYTYPE_CHAIN:
1945 {
1946 Log(("%s: Dumping info for CHAIN SG entry:\n", __FUNCTION__));
1947 Log(("%s: u16Length=%u\n", __FUNCTION__, pSGEntry->Chain.u16Length));
1948 Log(("%s: u8NExtChainOffset=%d\n", __FUNCTION__, pSGEntry->Chain.u8NextChainOffset));
1949 Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Chain.f64BitAddress));
1950 Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Chain.fLocalAddress));
1951 Log(("%s: u32SegmentAddressLow=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow));
1952 Log(("%s: u32SegmentAddressHigh=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressHigh));
1953 if (pSGEntry->Chain.f64BitAddress)
1954 Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__,
1955 ((uint64_t)pSGEntry->Chain.u32SegmentAddressHigh << 32) | pSGEntry->Chain.u32SegmentAddressLow));
1956 else
1957 Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow));
1958 break;
1959 }
1960 }
1961 }
1962}
1963# endif /* LOG_ENABLED */
1964
1965/**
1966 * Copy from guest to host memory worker.
1967 *
1968 * @copydoc FNLSILOGICR3MEMCOPYCALLBACK
1969 */
1970static DECLCALLBACK(void) lsilogicR3CopyBufferFromGuestWorker(PPDMDEVINS pDevIns, RTGCPHYS GCPhys,
1971 PRTSGBUF pSgBuf, size_t cbCopy, size_t *pcbSkip)
1972{
1973 size_t cbSkipped = RT_MIN(cbCopy, *pcbSkip);
1974 cbCopy -= cbSkipped;
1975 GCPhys += cbSkipped;
1976 *pcbSkip -= cbSkipped;
1977
1978 while (cbCopy)
1979 {
1980 size_t cbSeg = cbCopy;
1981 void *pvSeg = RTSgBufGetNextSegment(pSgBuf, &cbSeg);
1982
1983 AssertPtr(pvSeg);
1984 PDMDevHlpPCIPhysReadUser(pDevIns, GCPhys, pvSeg, cbSeg);
1985 GCPhys += cbSeg;
1986 cbCopy -= cbSeg;
1987 }
1988}
1989
1990/**
1991 * Copy from host to guest memory worker.
1992 *
1993 * @copydoc FNLSILOGICR3MEMCOPYCALLBACK
1994 */
1995static DECLCALLBACK(void) lsilogicR3CopyBufferToGuestWorker(PPDMDEVINS pDevIns, RTGCPHYS GCPhys,
1996 PRTSGBUF pSgBuf, size_t cbCopy, size_t *pcbSkip)
1997{
1998 size_t cbSkipped = RT_MIN(cbCopy, *pcbSkip);
1999 cbCopy -= cbSkipped;
2000 GCPhys += cbSkipped;
2001 *pcbSkip -= cbSkipped;
2002
2003 while (cbCopy)
2004 {
2005 size_t cbSeg = cbCopy;
2006 void *pvSeg = RTSgBufGetNextSegment(pSgBuf, &cbSeg);
2007
2008 AssertPtr(pvSeg);
2009 PDMDevHlpPCIPhysWriteUser(pDevIns, GCPhys, pvSeg, cbSeg);
2010 GCPhys += cbSeg;
2011 cbCopy -= cbSeg;
2012 }
2013}
2014
2015/**
2016 * Walks the guest S/G buffer calling the given copy worker for every buffer.
2017 *
2018 * @returns The amout of bytes actually copied.
2019 * @param pDevIns The device instance.
2020 * @param pLsiReq LSI request state.
2021 * @param pfnCopyWorker The copy method to apply for each guest buffer.
2022 * @param pSgBuf The host S/G buffer.
2023 * @param cbSkip How many bytes to skip in advance before starting to
2024 * copy.
2025 * @param cbCopy How many bytes to copy.
2026 */
2027static size_t lsilogicSgBufWalker(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq,
2028 PFNLSILOGICR3MEMCOPYCALLBACK pfnCopyWorker,
2029 PRTSGBUF pSgBuf, size_t cbSkip, size_t cbCopy)
2030{
2031 bool fEndOfList = false;
2032 RTGCPHYS GCPhysSgEntryNext = pLsiReq->GCPhysSgStart;
2033 RTGCPHYS GCPhysSegmentStart = pLsiReq->GCPhysSgStart;
2034 uint32_t cChainOffsetNext = pLsiReq->cChainOffset;
2035 size_t cbCopied = 0;
2036
2037 /*
2038 * Add the amount to skip to the host buffer size to avoid a
2039 * few conditionals later on.
2040 */
2041 cbCopy += cbSkip;
2042
2043 /* Go through the list until we reach the end. */
2044 while ( !fEndOfList
2045 && cbCopy)
2046 {
2047 bool fEndOfSegment = false;
2048
2049 while ( !fEndOfSegment
2050 && cbCopy)
2051 {
2052 MptSGEntryUnion SGEntry;
2053
2054 Log(("%s: Reading SG entry from %RGp\n", __FUNCTION__, GCPhysSgEntryNext));
2055
2056 /* Read the entry. */
2057 PDMDevHlpPCIPhysReadMeta(pDevIns, GCPhysSgEntryNext, &SGEntry, sizeof(MptSGEntryUnion));
2058
2059# ifdef LOG_ENABLED
2060 lsilogicDumpSGEntry(&SGEntry);
2061# endif
2062
2063 AssertMsg(SGEntry.Simple32.u2ElementType == MPTSGENTRYTYPE_SIMPLE, ("Invalid SG entry type\n"));
2064
2065 /* Check if this is a zero element and abort. */
2066 if ( !SGEntry.Simple32.u24Length
2067 && SGEntry.Simple32.fEndOfList
2068 && SGEntry.Simple32.fEndOfBuffer)
2069 return cbCopied - RT_MIN(cbSkip, cbCopied);
2070
2071 size_t cbCopyThis = RT_MIN(SGEntry.Simple32.u24Length, cbCopy);
2072 RTGCPHYS GCPhysAddrDataBuffer = SGEntry.Simple32.u32DataBufferAddressLow;
2073
2074 if (SGEntry.Simple32.f64BitAddress)
2075 {
2076 GCPhysAddrDataBuffer |= ((uint64_t)SGEntry.Simple64.u32DataBufferAddressHigh) << 32;
2077 GCPhysSgEntryNext += sizeof(MptSGEntrySimple64);
2078 }
2079 else
2080 GCPhysSgEntryNext += sizeof(MptSGEntrySimple32);
2081
2082 pfnCopyWorker(pDevIns, GCPhysAddrDataBuffer, pSgBuf, cbCopyThis, &cbSkip);
2083 cbCopy -= cbCopyThis;
2084 cbCopied += cbCopyThis;
2085
2086 /* Check if we reached the end of the list. */
2087 if (SGEntry.Simple32.fEndOfList)
2088 {
2089 /* We finished. */
2090 fEndOfSegment = true;
2091 fEndOfList = true;
2092 }
2093 else if (SGEntry.Simple32.fLastElement)
2094 fEndOfSegment = true;
2095 } /* while (!fEndOfSegment) */
2096
2097 /* Get next chain element. */
2098 if (cChainOffsetNext)
2099 {
2100 MptSGEntryChain SGEntryChain;
2101
2102 PDMDevHlpPCIPhysReadMeta(pDevIns, GCPhysSegmentStart + cChainOffsetNext, &SGEntryChain, sizeof(MptSGEntryChain));
2103
2104 AssertMsg(SGEntryChain.u2ElementType == MPTSGENTRYTYPE_CHAIN, ("Invalid SG entry type\n"));
2105
2106 /* Set the next address now. */
2107 GCPhysSgEntryNext = SGEntryChain.u32SegmentAddressLow;
2108 if (SGEntryChain.f64BitAddress)
2109 GCPhysSgEntryNext |= ((uint64_t)SGEntryChain.u32SegmentAddressHigh) << 32;
2110
2111 GCPhysSegmentStart = GCPhysSgEntryNext;
2112 cChainOffsetNext = SGEntryChain.u8NextChainOffset * sizeof(uint32_t);
2113 }
2114 } /* while (!fEndOfList) */
2115
2116 return cbCopied - RT_MIN(cbSkip, cbCopied);
2117}
2118
2119/**
2120 * Copies a data buffer into the S/G buffer set up by the guest.
2121 *
2122 * @returns Amount of bytes copied to the guest.
2123 * @param pDevIns The device instance.
2124 * @param pReq Request structure.
2125 * @param pSgBuf The S/G buffer to copy from.
2126 * @param cbSkip How many bytes to skip in advance before starting to copy.
2127 * @param cbCopy How many bytes to copy.
2128 */
2129static size_t lsilogicR3CopySgBufToGuest(PPDMDEVINS pDevIns, PLSILOGICREQ pReq, PRTSGBUF pSgBuf,
2130 size_t cbSkip, size_t cbCopy)
2131{
2132 return lsilogicSgBufWalker(pDevIns, pReq, lsilogicR3CopyBufferToGuestWorker, pSgBuf, cbSkip, cbCopy);
2133}
2134
2135/**
2136 * Copies the guest S/G buffer into a host data buffer.
2137 *
2138 * @returns Amount of bytes copied from the guest.
2139 * @param pDevIns The device instance.
2140 * @param pReq Request structure.
2141 * @param pSgBuf The S/G buffer to copy into.
2142 * @param cbSkip How many bytes to skip in advance before starting to copy.
2143 * @param cbCopy How many bytes to copy.
2144 */
2145static size_t lsilogicR3CopySgBufFromGuest(PPDMDEVINS pDevIns, PLSILOGICREQ pReq, PRTSGBUF pSgBuf,
2146 size_t cbSkip, size_t cbCopy)
2147{
2148 return lsilogicSgBufWalker(pDevIns, pReq, lsilogicR3CopyBufferFromGuestWorker, pSgBuf, cbSkip, cbCopy);
2149}
2150
2151#if 0 /* unused */
2152/**
2153 * Copy a simple memory buffer to the guest memory buffer.
2154 *
2155 * @returns Amount of bytes copied to the guest.
2156 * @param pThis The LsiLogic controller device instance.
2157 * @param pReq Request structure.
2158 * @param pvSrc The buffer to copy from.
2159 * @param cbSrc How many bytes to copy.
2160 * @param cbSkip How many bytes to skip initially.
2161 */
2162static size_t lsilogicR3CopyBufferToGuest(PLSILOGICSCSI pThis, PLSILOGICREQ pReq, const void *pvSrc,
2163 size_t cbSrc, size_t cbSkip)
2164{
2165 RTSGSEG Seg;
2166 RTSGBUF SgBuf;
2167 Seg.pvSeg = (void *)pvSrc;
2168 Seg.cbSeg = cbSrc;
2169 RTSgBufInit(&SgBuf, &Seg, 1);
2170 return lsilogicR3CopySgBufToGuest(pThis, pReq, &SgBuf, cbSkip, cbSrc);
2171}
2172
2173/**
2174 * Copy a guest memry buffe into simple host memory buffer.
2175 *
2176 * @returns Amount of bytes copied to the guest.
2177 * @param pThis The LsiLogic controller device instance.
2178 * @param pReq Request structure.
2179 * @param pvSrc The buffer to copy from.
2180 * @param cbSrc How many bytes to copy.
2181 * @param cbSkip How many bytes to skip initially.
2182 */
2183static size_t lsilogicR3CopyBufferFromGuest(PLSILOGICSCSI pThis, PLSILOGICREQ pReq, void *pvDst,
2184 size_t cbDst, size_t cbSkip)
2185{
2186 RTSGSEG Seg;
2187 RTSGBUF SgBuf;
2188 Seg.pvSeg = (void *)pvDst;
2189 Seg.cbSeg = cbDst;
2190 RTSgBufInit(&SgBuf, &Seg, 1);
2191 return lsilogicR3CopySgBufFromGuest(pThis, pReq, &SgBuf, cbSkip, cbDst);
2192}
2193#endif
2194
2195# ifdef LOG_ENABLED
2196static void lsilogicR3DumpSCSIIORequest(PMptSCSIIORequest pSCSIIORequest)
2197{
2198 if (LogIsEnabled())
2199 {
2200 Log(("%s: u8TargetID=%d\n", __FUNCTION__, pSCSIIORequest->u8TargetID));
2201 Log(("%s: u8Bus=%d\n", __FUNCTION__, pSCSIIORequest->u8Bus));
2202 Log(("%s: u8ChainOffset=%d\n", __FUNCTION__, pSCSIIORequest->u8ChainOffset));
2203 Log(("%s: u8Function=%d\n", __FUNCTION__, pSCSIIORequest->u8Function));
2204 Log(("%s: u8CDBLength=%d\n", __FUNCTION__, pSCSIIORequest->u8CDBLength));
2205 Log(("%s: u8SenseBufferLength=%d\n", __FUNCTION__, pSCSIIORequest->u8SenseBufferLength));
2206 Log(("%s: u8MessageFlags=%d\n", __FUNCTION__, pSCSIIORequest->u8MessageFlags));
2207 Log(("%s: u32MessageContext=%#x\n", __FUNCTION__, pSCSIIORequest->u32MessageContext));
2208 for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8LUN); i++)
2209 Log(("%s: u8LUN[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8LUN[i]));
2210 Log(("%s: u32Control=%#x\n", __FUNCTION__, pSCSIIORequest->u32Control));
2211 for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8CDB); i++)
2212 Log(("%s: u8CDB[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8CDB[i]));
2213 Log(("%s: u32DataLength=%#x\n", __FUNCTION__, pSCSIIORequest->u32DataLength));
2214 Log(("%s: u32SenseBufferLowAddress=%#x\n", __FUNCTION__, pSCSIIORequest->u32SenseBufferLowAddress));
2215 }
2216}
2217# endif
2218
2219/**
2220 * Handles the completion of th given request.
2221 *
2222 * @returns nothing.
2223 * @param pDevIns The device instance.
2224 * @param pThis Pointer to the shared LsiLogic device state.
2225 * @param pReq The request to complete.
2226 * @param rcReq Status code of the request.
2227 */
2228static void lsilogicR3ReqComplete(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, PLSILOGICREQ pReq, int rcReq)
2229{
2230 PLSILOGICDEVICE pTgtDev = pReq->pTargetDevice;
2231 RTGCPHYS GCPhysAddrSenseBuffer;
2232
2233 GCPhysAddrSenseBuffer = pReq->GuestRequest.SCSIIO.u32SenseBufferLowAddress;
2234 GCPhysAddrSenseBuffer |= ((uint64_t)pThis->u32SenseBufferHighAddr << 32);
2235
2236 /* Copy the sense buffer over. */
2237 if (pReq->GuestRequest.SCSIIO.u8SenseBufferLength > 0)
2238 PDMDevHlpPCIPhysWriteMeta(pDevIns, GCPhysAddrSenseBuffer, pReq->abSenseBuffer,
2239 RT_UNLIKELY( pReq->GuestRequest.SCSIIO.u8SenseBufferLength
2240 < sizeof(pReq->abSenseBuffer))
2241 ? pReq->GuestRequest.SCSIIO.u8SenseBufferLength
2242 : sizeof(pReq->abSenseBuffer));
2243
2244 if (RT_SUCCESS(rcReq) && RT_LIKELY(pReq->u8ScsiSts == SCSI_STATUS_OK))
2245 {
2246 uint32_t u32MsgCtx = pReq->GuestRequest.SCSIIO.u32MessageContext;
2247
2248 /* Free the request before posting completion. */
2249 pTgtDev->pDrvMediaEx->pfnIoReqFree(pTgtDev->pDrvMediaEx, pReq->hIoReq);
2250 lsilogicR3FinishContextReply(pDevIns, pThis, u32MsgCtx);
2251 }
2252 else
2253 {
2254 MptReplyUnion IOCReply;
2255 RT_ZERO(IOCReply);
2256
2257 /* The SCSI target encountered an error during processing post a reply. */
2258 IOCReply.SCSIIOError.u8TargetID = pReq->GuestRequest.SCSIIO.u8TargetID;
2259 IOCReply.SCSIIOError.u8Bus = pReq->GuestRequest.SCSIIO.u8Bus;
2260 IOCReply.SCSIIOError.u8MessageLength = 8;
2261 IOCReply.SCSIIOError.u8Function = pReq->GuestRequest.SCSIIO.u8Function;
2262 IOCReply.SCSIIOError.u8CDBLength = pReq->GuestRequest.SCSIIO.u8CDBLength;
2263 IOCReply.SCSIIOError.u8SenseBufferLength = pReq->GuestRequest.SCSIIO.u8SenseBufferLength;
2264 IOCReply.SCSIIOError.u8MessageFlags = pReq->GuestRequest.SCSIIO.u8MessageFlags;
2265 IOCReply.SCSIIOError.u32MessageContext = pReq->GuestRequest.SCSIIO.u32MessageContext;
2266 IOCReply.SCSIIOError.u8SCSIStatus = pReq->u8ScsiSts;
2267 IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_AUTOSENSE_VALID;
2268 IOCReply.SCSIIOError.u16IOCStatus = 0;
2269 IOCReply.SCSIIOError.u32IOCLogInfo = 0;
2270 IOCReply.SCSIIOError.u32TransferCount = 0;
2271 IOCReply.SCSIIOError.u32SenseCount = sizeof(pReq->abSenseBuffer);
2272 IOCReply.SCSIIOError.u32ResponseInfo = 0;
2273
2274 /* Free the request before posting completion. */
2275 pTgtDev->pDrvMediaEx->pfnIoReqFree(pTgtDev->pDrvMediaEx, pReq->hIoReq);
2276 lsilogicFinishAddressReply(pDevIns, pThis, &IOCReply, false);
2277 }
2278
2279 ASMAtomicDecU32(&pTgtDev->cOutstandingRequests);
2280
2281 if (pTgtDev->cOutstandingRequests == 0 && pThis->fSignalIdle)
2282 PDMDevHlpAsyncNotificationCompleted(pDevIns);
2283}
2284
2285/**
2286 * Processes a SCSI I/O request by setting up the request
2287 * and sending it to the underlying SCSI driver.
2288 * Steps needed to complete request are done in the
2289 * callback called by the driver below upon completion of
2290 * the request.
2291 *
2292 * @returns VBox status code.
2293 * @param pDevIns The device instance.
2294 * @param pThis Pointer to the shared LsiLogic device state.
2295 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
2296 * @param GCPhysMessageFrameAddr Guest physical address where the request is located.
2297 * @param pGuestReq The request read fro th guest memory.
2298 */
2299static int lsilogicR3ProcessSCSIIORequest(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC,
2300 RTGCPHYS GCPhysMessageFrameAddr, PMptRequestUnion pGuestReq)
2301{
2302 MptReplyUnion IOCReply;
2303 int rc = VINF_SUCCESS;
2304
2305# ifdef LOG_ENABLED
2306 lsilogicR3DumpSCSIIORequest(&pGuestReq->SCSIIO);
2307# endif
2308
2309 if (RT_LIKELY( (pGuestReq->SCSIIO.u8TargetID < pThis->cDeviceStates)
2310 && (pGuestReq->SCSIIO.u8Bus == 0)))
2311 {
2312 PLSILOGICDEVICE pTgtDev = &pThisCC->paDeviceStates[pGuestReq->SCSIIO.u8TargetID];
2313
2314 if (pTgtDev->pDrvBase)
2315 {
2316 /* Allocate and prepare a new request. */
2317 PDMMEDIAEXIOREQ hIoReq;
2318 PLSILOGICREQ pLsiReq = NULL;
2319 rc = pTgtDev->pDrvMediaEx->pfnIoReqAlloc(pTgtDev->pDrvMediaEx, &hIoReq, (void **)&pLsiReq,
2320 pGuestReq->SCSIIO.u32MessageContext,
2321 PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR);
2322 if (RT_SUCCESS(rc))
2323 {
2324 pLsiReq->hIoReq = hIoReq;
2325 pLsiReq->pTargetDevice = pTgtDev;
2326 pLsiReq->GCPhysMessageFrameAddr = GCPhysMessageFrameAddr;
2327 pLsiReq->GCPhysSgStart = GCPhysMessageFrameAddr + sizeof(MptSCSIIORequest);
2328 pLsiReq->cChainOffset = pGuestReq->SCSIIO.u8ChainOffset;
2329 if (pLsiReq->cChainOffset)
2330 pLsiReq->cChainOffset = pLsiReq->cChainOffset * sizeof(uint32_t) - sizeof(MptSCSIIORequest);
2331 memcpy(&pLsiReq->GuestRequest, pGuestReq, sizeof(MptRequestUnion));
2332 RT_BZERO(&pLsiReq->abSenseBuffer[0], sizeof(pLsiReq->abSenseBuffer));
2333
2334 PDMMEDIAEXIOREQSCSITXDIR enmXferDir = PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN;
2335 uint8_t uDataDirection = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pLsiReq->GuestRequest.SCSIIO.u32Control);
2336
2337 /*
2338 * Keep the direction to unknown if there is a mismatch between the data length
2339 * and the transfer direction bit.
2340 * The Solaris 9 driver is buggy and sets it to none for INQUIRY requests.
2341 */
2342 if ( uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE
2343 && pLsiReq->GuestRequest.SCSIIO.u32DataLength == 0)
2344 enmXferDir = PDMMEDIAEXIOREQSCSITXDIR_NONE;
2345 else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE)
2346 enmXferDir = PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE;
2347 else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ)
2348 enmXferDir = PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE;
2349
2350 ASMAtomicIncU32(&pTgtDev->cOutstandingRequests);
2351 rc = pTgtDev->pDrvMediaEx->pfnIoReqSendScsiCmd(pTgtDev->pDrvMediaEx, pLsiReq->hIoReq, pLsiReq->GuestRequest.SCSIIO.au8LUN[1],
2352 &pLsiReq->GuestRequest.SCSIIO.au8CDB[0], pLsiReq->GuestRequest.SCSIIO.u8CDBLength,
2353 enmXferDir, NULL, pLsiReq->GuestRequest.SCSIIO.u32DataLength,
2354 &pLsiReq->abSenseBuffer[0], sizeof(pLsiReq->abSenseBuffer), NULL,
2355 &pLsiReq->u8ScsiSts, 30 * RT_MS_1SEC);
2356 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
2357 lsilogicR3ReqComplete(pDevIns, pThis, pLsiReq, rc);
2358
2359 return VINF_SUCCESS;
2360 }
2361 else
2362 IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_DEVICE_NOT_THERE;
2363 }
2364 else
2365 {
2366 /* Device is not present report SCSI selection timeout. */
2367 IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_DEVICE_NOT_THERE;
2368 }
2369 }
2370 else
2371 {
2372 /* Report out of bounds target ID or bus. */
2373 if (pGuestReq->SCSIIO.u8Bus != 0)
2374 IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_BUS;
2375 else
2376 IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_TARGETID;
2377 }
2378
2379 static int g_cLogged = 0;
2380
2381 if (g_cLogged++ < MAX_REL_LOG_ERRORS)
2382 {
2383 LogRel(("LsiLogic#%d: %d/%d (Bus/Target) doesn't exist\n", pDevIns->iInstance,
2384 pGuestReq->SCSIIO.u8TargetID, pGuestReq->SCSIIO.u8Bus));
2385 /* Log the CDB too */
2386 LogRel(("LsiLogic#%d: Guest issued CDB {%#x",
2387 pDevIns->iInstance, pGuestReq->SCSIIO.au8CDB[0]));
2388 for (unsigned i = 1; i < pGuestReq->SCSIIO.u8CDBLength; i++)
2389 LogRel((", %#x", pGuestReq->SCSIIO.au8CDB[i]));
2390 LogRel(("}\n"));
2391 }
2392
2393 /* The rest is equal to both errors. */
2394 IOCReply.SCSIIOError.u8TargetID = pGuestReq->SCSIIO.u8TargetID;
2395 IOCReply.SCSIIOError.u8Bus = pGuestReq->SCSIIO.u8Bus;
2396 IOCReply.SCSIIOError.u8MessageLength = sizeof(MptSCSIIOErrorReply) / 4;
2397 IOCReply.SCSIIOError.u8Function = pGuestReq->SCSIIO.u8Function;
2398 IOCReply.SCSIIOError.u8CDBLength = pGuestReq->SCSIIO.u8CDBLength;
2399 IOCReply.SCSIIOError.u8SenseBufferLength = pGuestReq->SCSIIO.u8SenseBufferLength;
2400 IOCReply.SCSIIOError.u8Reserved = 0;
2401 IOCReply.SCSIIOError.u8MessageFlags = 0;
2402 IOCReply.SCSIIOError.u32MessageContext = pGuestReq->SCSIIO.u32MessageContext;
2403 IOCReply.SCSIIOError.u8SCSIStatus = SCSI_STATUS_OK;
2404 IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_TERMINATED;
2405 IOCReply.SCSIIOError.u32IOCLogInfo = 0;
2406 IOCReply.SCSIIOError.u32TransferCount = 0;
2407 IOCReply.SCSIIOError.u32SenseCount = 0;
2408 IOCReply.SCSIIOError.u32ResponseInfo = 0;
2409
2410 lsilogicFinishAddressReply(pDevIns, pThis, &IOCReply, false);
2411
2412 return rc;
2413}
2414
2415
2416/**
2417 * @interface_method_impl{PDMIMEDIAPORT,pfnQueryDeviceLocation}
2418 */
2419static DECLCALLBACK(int) lsilogicR3QueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
2420 uint32_t *piInstance, uint32_t *piLUN)
2421{
2422 PLSILOGICDEVICE pTgtDev = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IMediaPort);
2423 PPDMDEVINS pDevIns = pTgtDev->pDevIns;
2424
2425 AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
2426 AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
2427 AssertPtrReturn(piLUN, VERR_INVALID_POINTER);
2428
2429 *ppcszController = pDevIns->pReg->szName;
2430 *piInstance = pDevIns->iInstance;
2431 *piLUN = pTgtDev->iLUN;
2432
2433 return VINF_SUCCESS;
2434}
2435
2436
2437/**
2438 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
2439 */
2440static DECLCALLBACK(int) lsilogicR3IoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
2441 void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf,
2442 size_t cbCopy)
2443{
2444 RT_NOREF1(hIoReq);
2445 PLSILOGICDEVICE pTgtDev = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IMediaExPort);
2446 PPDMDEVINS pDevIns = pTgtDev->pDevIns;
2447 PLSILOGICREQ pReq = (PLSILOGICREQ)pvIoReqAlloc;
2448
2449 size_t cbCopied = lsilogicR3CopySgBufToGuest(pDevIns, pReq, pSgBuf, offDst, cbCopy);
2450 return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_OVERFLOW;
2451}
2452
2453/**
2454 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
2455 */
2456static DECLCALLBACK(int) lsilogicR3IoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
2457 void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf,
2458 size_t cbCopy)
2459{
2460 RT_NOREF1(hIoReq);
2461 PLSILOGICDEVICE pTgtDev = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IMediaExPort);
2462 PPDMDEVINS pDevIns = pTgtDev->pDevIns;
2463 PLSILOGICREQ pReq = (PLSILOGICREQ)pvIoReqAlloc;
2464
2465 size_t cbCopied = lsilogicR3CopySgBufFromGuest(pDevIns, pReq, pSgBuf, offSrc, cbCopy);
2466 return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_UNDERRUN;
2467}
2468
2469/**
2470 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
2471 */
2472static DECLCALLBACK(int) lsilogicR3IoReqCompleteNotify(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
2473 void *pvIoReqAlloc, int rcReq)
2474{
2475 RT_NOREF(hIoReq);
2476 PLSILOGICDEVICE pTgtDev = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IMediaExPort);
2477 PPDMDEVINS pDevIns = pTgtDev->pDevIns;
2478 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
2479 lsilogicR3ReqComplete(pDevIns, pThis, (PLSILOGICREQ)pvIoReqAlloc, rcReq);
2480 return VINF_SUCCESS;
2481}
2482
2483/**
2484 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
2485 */
2486static DECLCALLBACK(void) lsilogicR3IoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
2487 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
2488{
2489 RT_NOREF3(hIoReq, pvIoReqAlloc, enmState);
2490 PLSILOGICDEVICE pTgtDev = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IMediaExPort);
2491
2492 switch (enmState)
2493 {
2494 case PDMMEDIAEXIOREQSTATE_SUSPENDED:
2495 {
2496 /* Make sure the request is not accounted for so the VM can suspend successfully. */
2497 PPDMDEVINS pDevIns = pTgtDev->pDevIns;
2498 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
2499 uint32_t cTasksActive = ASMAtomicDecU32(&pTgtDev->cOutstandingRequests);
2500 if (!cTasksActive && pThis->fSignalIdle)
2501 PDMDevHlpAsyncNotificationCompleted(pDevIns);
2502 break;
2503 }
2504 case PDMMEDIAEXIOREQSTATE_ACTIVE:
2505 /* Make sure the request is accounted for so the VM suspends only when the request is complete. */
2506 ASMAtomicIncU32(&pTgtDev->cOutstandingRequests);
2507 break;
2508 default:
2509 AssertMsgFailed(("Invalid request state given %u\n", enmState));
2510 }
2511}
2512
2513/**
2514 * @interface_method_impl{PDMIMEDIAEXPORT,pfnMediumEjected}
2515 */
2516static DECLCALLBACK(void) lsilogicR3MediumEjected(PPDMIMEDIAEXPORT pInterface)
2517{
2518 PLSILOGICDEVICE pTgtDev = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IMediaExPort);
2519 PPDMDEVINS pDevIns = pTgtDev->pDevIns;
2520 PLSILOGICSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC);
2521
2522 if (pThisCC->pMediaNotify)
2523 {
2524 int rc = PDMDevHlpVMReqCallNoWait(pDevIns, VMCPUID_ANY,
2525 (PFNRT)pThisCC->pMediaNotify->pfnEjected, 2,
2526 pThisCC->pMediaNotify, pTgtDev->iLUN);
2527 AssertRC(rc);
2528 }
2529}
2530
2531
2532/**
2533 * Return the configuration page header and data
2534 * which matches the given page type and number.
2535 *
2536 * @returns VINF_SUCCESS if successful
2537 * VERR_NOT_FOUND if the requested page could be found.
2538 * @param pThis Pointer to the shared LsiLogic device state. data.
2539 * @param pPages The pages supported by the controller.
2540 * @param u8PageNumber Number of the page to get.
2541 * @param ppPageHeader Where to store the pointer to the page header.
2542 * @param ppbPageData Where to store the pointer to the page data.
2543 * @param pcbPage Where to store the size of the page data in bytes on success.
2544 */
2545static int lsilogicR3ConfigurationIOUnitPageGetFromNumber(PLSILOGICSCSI pThis,
2546 PMptConfigurationPagesSupported pPages,
2547 uint8_t u8PageNumber,
2548 PMptConfigurationPageHeader *ppPageHeader,
2549 uint8_t **ppbPageData, size_t *pcbPage)
2550{
2551 RT_NOREF(pThis);
2552 int rc = VINF_SUCCESS;
2553
2554 AssertPtr(ppPageHeader); Assert(ppbPageData);
2555
2556 switch (u8PageNumber)
2557 {
2558 case 0:
2559 *ppPageHeader = &pPages->IOUnitPage0.u.fields.Header;
2560 *ppbPageData = pPages->IOUnitPage0.u.abPageData;
2561 *pcbPage = sizeof(pPages->IOUnitPage0);
2562 break;
2563 case 1:
2564 *ppPageHeader = &pPages->IOUnitPage1.u.fields.Header;
2565 *ppbPageData = pPages->IOUnitPage1.u.abPageData;
2566 *pcbPage = sizeof(pPages->IOUnitPage1);
2567 break;
2568 case 2:
2569 *ppPageHeader = &pPages->IOUnitPage2.u.fields.Header;
2570 *ppbPageData = pPages->IOUnitPage2.u.abPageData;
2571 *pcbPage = sizeof(pPages->IOUnitPage2);
2572 break;
2573 case 3:
2574 *ppPageHeader = &pPages->IOUnitPage3.u.fields.Header;
2575 *ppbPageData = pPages->IOUnitPage3.u.abPageData;
2576 *pcbPage = sizeof(pPages->IOUnitPage3);
2577 break;
2578 case 4:
2579 *ppPageHeader = &pPages->IOUnitPage4.u.fields.Header;
2580 *ppbPageData = pPages->IOUnitPage4.u.abPageData;
2581 *pcbPage = sizeof(pPages->IOUnitPage4);
2582 break;
2583 default:
2584 rc = VERR_NOT_FOUND;
2585 }
2586
2587 return rc;
2588}
2589
2590/**
2591 * Return the configuration page header and data
2592 * which matches the given page type and number.
2593 *
2594 * @returns VINF_SUCCESS if successful
2595 * VERR_NOT_FOUND if the requested page could be found.
2596 * @param pThis Pointer to the shared LsiLogic device state. data.
2597 * @param pPages The pages supported by the controller.
2598 * @param u8PageNumber Number of the page to get.
2599 * @param ppPageHeader Where to store the pointer to the page header.
2600 * @param ppbPageData Where to store the pointer to the page data.
2601 * @param pcbPage Where to store the size of the page data in bytes on success.
2602 */
2603static int lsilogicR3ConfigurationIOCPageGetFromNumber(PLSILOGICSCSI pThis,
2604 PMptConfigurationPagesSupported pPages,
2605 uint8_t u8PageNumber,
2606 PMptConfigurationPageHeader *ppPageHeader,
2607 uint8_t **ppbPageData, size_t *pcbPage)
2608{
2609 RT_NOREF(pThis);
2610 int rc = VINF_SUCCESS;
2611
2612 AssertPtr(ppPageHeader); Assert(ppbPageData);
2613
2614 switch (u8PageNumber)
2615 {
2616 case 0:
2617 *ppPageHeader = &pPages->IOCPage0.u.fields.Header;
2618 *ppbPageData = pPages->IOCPage0.u.abPageData;
2619 *pcbPage = sizeof(pPages->IOCPage0);
2620 break;
2621 case 1:
2622 *ppPageHeader = &pPages->IOCPage1.u.fields.Header;
2623 *ppbPageData = pPages->IOCPage1.u.abPageData;
2624 *pcbPage = sizeof(pPages->IOCPage1);
2625 break;
2626 case 2:
2627 *ppPageHeader = &pPages->IOCPage2.u.fields.Header;
2628 *ppbPageData = pPages->IOCPage2.u.abPageData;
2629 *pcbPage = sizeof(pPages->IOCPage2);
2630 break;
2631 case 3:
2632 *ppPageHeader = &pPages->IOCPage3.u.fields.Header;
2633 *ppbPageData = pPages->IOCPage3.u.abPageData;
2634 *pcbPage = sizeof(pPages->IOCPage3);
2635 break;
2636 case 4:
2637 *ppPageHeader = &pPages->IOCPage4.u.fields.Header;
2638 *ppbPageData = pPages->IOCPage4.u.abPageData;
2639 *pcbPage = sizeof(pPages->IOCPage4);
2640 break;
2641 case 6:
2642 *ppPageHeader = &pPages->IOCPage6.u.fields.Header;
2643 *ppbPageData = pPages->IOCPage6.u.abPageData;
2644 *pcbPage = sizeof(pPages->IOCPage6);
2645 break;
2646 default:
2647 rc = VERR_NOT_FOUND;
2648 }
2649
2650 return rc;
2651}
2652
2653/**
2654 * Return the configuration page header and data
2655 * which matches the given page type and number.
2656 *
2657 * @returns VINF_SUCCESS if successful
2658 * VERR_NOT_FOUND if the requested page could be found.
2659 * @param pThis Pointer to the shared LsiLogic device state. data.
2660 * @param pPages The pages supported by the controller.
2661 * @param u8PageNumber Number of the page to get.
2662 * @param ppPageHeader Where to store the pointer to the page header.
2663 * @param ppbPageData Where to store the pointer to the page data.
2664 * @param pcbPage Where to store the size of the page data in bytes on success.
2665 */
2666static int lsilogicR3ConfigurationManufacturingPageGetFromNumber(PLSILOGICSCSI pThis,
2667 PMptConfigurationPagesSupported pPages,
2668 uint8_t u8PageNumber,
2669 PMptConfigurationPageHeader *ppPageHeader,
2670 uint8_t **ppbPageData, size_t *pcbPage)
2671{
2672 int rc = VINF_SUCCESS;
2673
2674 AssertPtr(ppPageHeader); Assert(ppbPageData);
2675
2676 switch (u8PageNumber)
2677 {
2678 case 0:
2679 *ppPageHeader = &pPages->ManufacturingPage0.u.fields.Header;
2680 *ppbPageData = pPages->ManufacturingPage0.u.abPageData;
2681 *pcbPage = sizeof(pPages->ManufacturingPage0);
2682 break;
2683 case 1:
2684 *ppPageHeader = &pPages->ManufacturingPage1.u.fields.Header;
2685 *ppbPageData = pPages->ManufacturingPage1.u.abPageData;
2686 *pcbPage = sizeof(pPages->ManufacturingPage1);
2687 break;
2688 case 2:
2689 *ppPageHeader = &pPages->ManufacturingPage2.u.fields.Header;
2690 *ppbPageData = pPages->ManufacturingPage2.u.abPageData;
2691 *pcbPage = sizeof(pPages->ManufacturingPage2);
2692 break;
2693 case 3:
2694 *ppPageHeader = &pPages->ManufacturingPage3.u.fields.Header;
2695 *ppbPageData = pPages->ManufacturingPage3.u.abPageData;
2696 *pcbPage = sizeof(pPages->ManufacturingPage3);
2697 break;
2698 case 4:
2699 *ppPageHeader = &pPages->ManufacturingPage4.u.fields.Header;
2700 *ppbPageData = pPages->ManufacturingPage4.u.abPageData;
2701 *pcbPage = sizeof(pPages->ManufacturingPage4);
2702 break;
2703 case 5:
2704 *ppPageHeader = &pPages->ManufacturingPage5.u.fields.Header;
2705 *ppbPageData = pPages->ManufacturingPage5.u.abPageData;
2706 *pcbPage = sizeof(pPages->ManufacturingPage5);
2707 break;
2708 case 6:
2709 *ppPageHeader = &pPages->ManufacturingPage6.u.fields.Header;
2710 *ppbPageData = pPages->ManufacturingPage6.u.abPageData;
2711 *pcbPage = sizeof(pPages->ManufacturingPage6);
2712 break;
2713 case 7:
2714 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
2715 {
2716 *ppPageHeader = &pPages->u.SasPages.pManufacturingPage7->u.fields.Header;
2717 *ppbPageData = pPages->u.SasPages.pManufacturingPage7->u.abPageData;
2718 *pcbPage = pPages->u.SasPages.cbManufacturingPage7;
2719 }
2720 else
2721 rc = VERR_NOT_FOUND;
2722 break;
2723 case 8:
2724 *ppPageHeader = &pPages->ManufacturingPage8.u.fields.Header;
2725 *ppbPageData = pPages->ManufacturingPage8.u.abPageData;
2726 *pcbPage = sizeof(pPages->ManufacturingPage8);
2727 break;
2728 case 9:
2729 *ppPageHeader = &pPages->ManufacturingPage9.u.fields.Header;
2730 *ppbPageData = pPages->ManufacturingPage9.u.abPageData;
2731 *pcbPage = sizeof(pPages->ManufacturingPage9);
2732 break;
2733 case 10:
2734 *ppPageHeader = &pPages->ManufacturingPage10.u.fields.Header;
2735 *ppbPageData = pPages->ManufacturingPage10.u.abPageData;
2736 *pcbPage = sizeof(pPages->ManufacturingPage10);
2737 break;
2738 default:
2739 rc = VERR_NOT_FOUND;
2740 }
2741
2742 return rc;
2743}
2744
2745/**
2746 * Return the configuration page header and data
2747 * which matches the given page type and number.
2748 *
2749 * @returns VINF_SUCCESS if successful
2750 * VERR_NOT_FOUND if the requested page could be found.
2751 * @param pThis Pointer to the shared LsiLogic device state. data.
2752 * @param pPages The pages supported by the controller.
2753 * @param u8PageNumber Number of the page to get.
2754 * @param ppPageHeader Where to store the pointer to the page header.
2755 * @param ppbPageData Where to store the pointer to the page data.
2756 * @param pcbPage Where to store the size of the page data in bytes on success.
2757 */
2758static int lsilogicR3ConfigurationBiosPageGetFromNumber(PLSILOGICSCSI pThis,
2759 PMptConfigurationPagesSupported pPages,
2760 uint8_t u8PageNumber,
2761 PMptConfigurationPageHeader *ppPageHeader,
2762 uint8_t **ppbPageData, size_t *pcbPage)
2763{
2764 RT_NOREF(pThis);
2765 int rc = VINF_SUCCESS;
2766
2767 AssertPtr(ppPageHeader); Assert(ppbPageData);
2768
2769 switch (u8PageNumber)
2770 {
2771 case 1:
2772 *ppPageHeader = &pPages->BIOSPage1.u.fields.Header;
2773 *ppbPageData = pPages->BIOSPage1.u.abPageData;
2774 *pcbPage = sizeof(pPages->BIOSPage1);
2775 break;
2776 case 2:
2777 *ppPageHeader = &pPages->BIOSPage2.u.fields.Header;
2778 *ppbPageData = pPages->BIOSPage2.u.abPageData;
2779 *pcbPage = sizeof(pPages->BIOSPage2);
2780 break;
2781 case 4:
2782 *ppPageHeader = &pPages->BIOSPage4.u.fields.Header;
2783 *ppbPageData = pPages->BIOSPage4.u.abPageData;
2784 *pcbPage = sizeof(pPages->BIOSPage4);
2785 break;
2786 default:
2787 rc = VERR_NOT_FOUND;
2788 }
2789
2790 return rc;
2791}
2792
2793/**
2794 * Return the configuration page header and data
2795 * which matches the given page type and number.
2796 *
2797 * @returns VINF_SUCCESS if successful
2798 * VERR_NOT_FOUND if the requested page could be found.
2799 * @param pThis Pointer to the shared LsiLogic device state. data.
2800 * @param pPages The pages supported by the controller.
2801 * @param u8Port The port to retrieve the page for.
2802 * @param u8PageNumber Number of the page to get.
2803 * @param ppPageHeader Where to store the pointer to the page header.
2804 * @param ppbPageData Where to store the pointer to the page data.
2805 * @param pcbPage Where to store the size of the page data in bytes on success.
2806 */
2807static int lsilogicR3ConfigurationSCSISPIPortPageGetFromNumber(PLSILOGICSCSI pThis,
2808 PMptConfigurationPagesSupported pPages,
2809 uint8_t u8Port,
2810 uint8_t u8PageNumber,
2811 PMptConfigurationPageHeader *ppPageHeader,
2812 uint8_t **ppbPageData, size_t *pcbPage)
2813{
2814 RT_NOREF(pThis);
2815 int rc = VINF_SUCCESS;
2816 AssertPtr(ppPageHeader); Assert(ppbPageData);
2817
2818
2819 if (u8Port >= RT_ELEMENTS(pPages->u.SpiPages.aPortPages))
2820 return VERR_NOT_FOUND;
2821
2822 switch (u8PageNumber)
2823 {
2824 case 0:
2825 *ppPageHeader = &pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage0.u.fields.Header;
2826 *ppbPageData = pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage0.u.abPageData;
2827 *pcbPage = sizeof(pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage0);
2828 break;
2829 case 1:
2830 *ppPageHeader = &pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage1.u.fields.Header;
2831 *ppbPageData = pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage1.u.abPageData;
2832 *pcbPage = sizeof(pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage1);
2833 break;
2834 case 2:
2835 *ppPageHeader = &pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage2.u.fields.Header;
2836 *ppbPageData = pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage2.u.abPageData;
2837 *pcbPage = sizeof(pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage2);
2838 break;
2839 default:
2840 rc = VERR_NOT_FOUND;
2841 }
2842
2843 return rc;
2844}
2845
2846/**
2847 * Return the configuration page header and data
2848 * which matches the given page type and number.
2849 *
2850 * @returns VINF_SUCCESS if successful
2851 * VERR_NOT_FOUND if the requested page could be found.
2852 * @param pThis Pointer to the shared LsiLogic device state. data.
2853 * @param pPages The pages supported by the controller.
2854 * @param u8Bus The bus the device is on the page should be returned.
2855 * @param u8TargetID The target ID of the device to return the page for.
2856 * @param u8PageNumber Number of the page to get.
2857 * @param ppPageHeader Where to store the pointer to the page header.
2858 * @param ppbPageData Where to store the pointer to the page data.
2859 * @param pcbPage Where to store the size of the page data in bytes on success.
2860 */
2861static int lsilogicR3ConfigurationSCSISPIDevicePageGetFromNumber(PLSILOGICSCSI pThis,
2862 PMptConfigurationPagesSupported pPages,
2863 uint8_t u8Bus,
2864 uint8_t u8TargetID, uint8_t u8PageNumber,
2865 PMptConfigurationPageHeader *ppPageHeader,
2866 uint8_t **ppbPageData, size_t *pcbPage)
2867{
2868 RT_NOREF(pThis);
2869 int rc = VINF_SUCCESS;
2870 AssertPtr(ppPageHeader); Assert(ppbPageData);
2871
2872 if (u8Bus >= RT_ELEMENTS(pPages->u.SpiPages.aBuses))
2873 return VERR_NOT_FOUND;
2874
2875 if (u8TargetID >= RT_ELEMENTS(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages))
2876 return VERR_NOT_FOUND;
2877
2878 switch (u8PageNumber)
2879 {
2880 case 0:
2881 *ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage0.u.fields.Header;
2882 *ppbPageData = pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage0.u.abPageData;
2883 *pcbPage = sizeof(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage0);
2884 break;
2885 case 1:
2886 *ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage1.u.fields.Header;
2887 *ppbPageData = pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage1.u.abPageData;
2888 *pcbPage = sizeof(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage1);
2889 break;
2890 case 2:
2891 *ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage2.u.fields.Header;
2892 *ppbPageData = pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage2.u.abPageData;
2893 *pcbPage = sizeof(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage2);
2894 break;
2895 case 3:
2896 *ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage3.u.fields.Header;
2897 *ppbPageData = pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage3.u.abPageData;
2898 *pcbPage = sizeof(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage3);
2899 break;
2900 default:
2901 rc = VERR_NOT_FOUND;
2902 }
2903
2904 return rc;
2905}
2906
2907static int lsilogicR3ConfigurationSASIOUnitPageGetFromNumber(PMptConfigurationPagesSupported pPages,
2908 uint8_t u8PageNumber,
2909 PMptExtendedConfigurationPageHeader *ppPageHeader,
2910 uint8_t **ppbPageData, size_t *pcbPage)
2911{
2912 int rc = VINF_SUCCESS;
2913
2914 switch (u8PageNumber)
2915 {
2916 case 0:
2917 *ppPageHeader = &pPages->u.SasPages.pSASIOUnitPage0->u.fields.ExtHeader;
2918 *ppbPageData = pPages->u.SasPages.pSASIOUnitPage0->u.abPageData;
2919 *pcbPage = pPages->u.SasPages.cbSASIOUnitPage0;
2920 break;
2921 case 1:
2922 *ppPageHeader = &pPages->u.SasPages.pSASIOUnitPage1->u.fields.ExtHeader;
2923 *ppbPageData = pPages->u.SasPages.pSASIOUnitPage1->u.abPageData;
2924 *pcbPage = pPages->u.SasPages.cbSASIOUnitPage1;
2925 break;
2926 case 2:
2927 *ppPageHeader = &pPages->u.SasPages.SASIOUnitPage2.u.fields.ExtHeader;
2928 *ppbPageData = pPages->u.SasPages.SASIOUnitPage2.u.abPageData;
2929 *pcbPage = sizeof(pPages->u.SasPages.SASIOUnitPage2);
2930 break;
2931 case 3:
2932 *ppPageHeader = &pPages->u.SasPages.SASIOUnitPage3.u.fields.ExtHeader;
2933 *ppbPageData = pPages->u.SasPages.SASIOUnitPage3.u.abPageData;
2934 *pcbPage = sizeof(pPages->u.SasPages.SASIOUnitPage3);
2935 break;
2936 default:
2937 rc = VERR_NOT_FOUND;
2938 }
2939
2940 return rc;
2941}
2942
2943static int lsilogicR3ConfigurationSASPHYPageGetFromNumber(PMptConfigurationPagesSupported pPages,
2944 uint8_t u8PageNumber,
2945 MptConfigurationPageAddress PageAddress,
2946 PMptExtendedConfigurationPageHeader *ppPageHeader,
2947 uint8_t **ppbPageData, size_t *pcbPage)
2948{
2949 int rc = VINF_SUCCESS;
2950 uint8_t uAddressForm = MPT_CONFIGURATION_PAGE_ADDRESS_GET_SAS_FORM(PageAddress);
2951 PMptConfigurationPagesSas pPagesSas = &pPages->u.SasPages;
2952 PMptPHY pPHYPages = NULL;
2953
2954 Log(("Address form %d\n", uAddressForm));
2955
2956 if (uAddressForm == 0) /* PHY number */
2957 {
2958 uint8_t u8PhyNumber = PageAddress.SASPHY.Form0.u8PhyNumber;
2959
2960 Log(("PHY number %d\n", u8PhyNumber));
2961
2962 if (u8PhyNumber >= pPagesSas->cPHYs)
2963 return VERR_NOT_FOUND;
2964
2965 pPHYPages = &pPagesSas->paPHYs[u8PhyNumber];
2966 }
2967 else if (uAddressForm == 1) /* Index form */
2968 {
2969 uint16_t u16Index = PageAddress.SASPHY.Form1.u16Index;
2970
2971 Log(("PHY index %d\n", u16Index));
2972
2973 if (u16Index >= pPagesSas->cPHYs)
2974 return VERR_NOT_FOUND;
2975
2976 pPHYPages = &pPagesSas->paPHYs[u16Index];
2977 }
2978 else
2979 rc = VERR_NOT_FOUND; /* Correct? */
2980
2981 if (pPHYPages)
2982 {
2983 switch (u8PageNumber)
2984 {
2985 case 0:
2986 *ppPageHeader = &pPHYPages->SASPHYPage0.u.fields.ExtHeader;
2987 *ppbPageData = pPHYPages->SASPHYPage0.u.abPageData;
2988 *pcbPage = sizeof(pPHYPages->SASPHYPage0);
2989 break;
2990 case 1:
2991 *ppPageHeader = &pPHYPages->SASPHYPage1.u.fields.ExtHeader;
2992 *ppbPageData = pPHYPages->SASPHYPage1.u.abPageData;
2993 *pcbPage = sizeof(pPHYPages->SASPHYPage1);
2994 break;
2995 default:
2996 rc = VERR_NOT_FOUND;
2997 }
2998 }
2999 else
3000 rc = VERR_NOT_FOUND;
3001
3002 return rc;
3003}
3004
3005static int lsilogicR3ConfigurationSASDevicePageGetFromNumber(PMptConfigurationPagesSupported pPages,
3006 uint8_t u8PageNumber,
3007 MptConfigurationPageAddress PageAddress,
3008 PMptExtendedConfigurationPageHeader *ppPageHeader,
3009 uint8_t **ppbPageData, size_t *pcbPage)
3010{
3011 int rc = VINF_SUCCESS;
3012 uint8_t uAddressForm = MPT_CONFIGURATION_PAGE_ADDRESS_GET_SAS_FORM(PageAddress);
3013 PMptConfigurationPagesSas pPagesSas = &pPages->u.SasPages;
3014 PMptSASDevice pSASDevice = NULL;
3015
3016 Log(("Address form %d\n", uAddressForm));
3017
3018 if (uAddressForm == 0)
3019 {
3020 uint16_t u16Handle = PageAddress.SASDevice.Form0And2.u16Handle;
3021
3022 Log(("Get next handle %#x\n", u16Handle));
3023
3024 pSASDevice = pPagesSas->pSASDeviceHead;
3025
3026 /* Get the first device? */
3027 if (u16Handle != 0xffff)
3028 {
3029 /* No, search for the right one. */
3030
3031 while ( pSASDevice
3032 && pSASDevice->SASDevicePage0.u.fields.u16DevHandle != u16Handle)
3033 pSASDevice = pSASDevice->pNext;
3034
3035 if (pSASDevice)
3036 pSASDevice = pSASDevice->pNext;
3037 }
3038 }
3039 else if (uAddressForm == 1)
3040 {
3041 uint8_t u8TargetID = PageAddress.SASDevice.Form1.u8TargetID;
3042 uint8_t u8Bus = PageAddress.SASDevice.Form1.u8Bus;
3043
3044 Log(("u8TargetID=%d u8Bus=%d\n", u8TargetID, u8Bus));
3045
3046 pSASDevice = pPagesSas->pSASDeviceHead;
3047
3048 while ( pSASDevice
3049 && ( pSASDevice->SASDevicePage0.u.fields.u8TargetID != u8TargetID
3050 || pSASDevice->SASDevicePage0.u.fields.u8Bus != u8Bus))
3051 pSASDevice = pSASDevice->pNext;
3052 }
3053 else if (uAddressForm == 2)
3054 {
3055 uint16_t u16Handle = PageAddress.SASDevice.Form0And2.u16Handle;
3056
3057 Log(("Handle %#x\n", u16Handle));
3058
3059 pSASDevice = pPagesSas->pSASDeviceHead;
3060
3061 while ( pSASDevice
3062 && pSASDevice->SASDevicePage0.u.fields.u16DevHandle != u16Handle)
3063 pSASDevice = pSASDevice->pNext;
3064 }
3065
3066 if (pSASDevice)
3067 {
3068 switch (u8PageNumber)
3069 {
3070 case 0:
3071 *ppPageHeader = &pSASDevice->SASDevicePage0.u.fields.ExtHeader;
3072 *ppbPageData = pSASDevice->SASDevicePage0.u.abPageData;
3073 *pcbPage = sizeof(pSASDevice->SASDevicePage0);
3074 break;
3075 case 1:
3076 *ppPageHeader = &pSASDevice->SASDevicePage1.u.fields.ExtHeader;
3077 *ppbPageData = pSASDevice->SASDevicePage1.u.abPageData;
3078 *pcbPage = sizeof(pSASDevice->SASDevicePage1);
3079 break;
3080 case 2:
3081 *ppPageHeader = &pSASDevice->SASDevicePage2.u.fields.ExtHeader;
3082 *ppbPageData = pSASDevice->SASDevicePage2.u.abPageData;
3083 *pcbPage = sizeof(pSASDevice->SASDevicePage2);
3084 break;
3085 default:
3086 rc = VERR_NOT_FOUND;
3087 }
3088 }
3089 else
3090 rc = VERR_NOT_FOUND;
3091
3092 return rc;
3093}
3094
3095/**
3096 * Returns the extended configuration page header and data.
3097 * @returns VINF_SUCCESS if successful
3098 * VERR_NOT_FOUND if the requested page could be found.
3099 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
3100 * @param pConfigurationReq The configuration request.
3101 * @param ppPageHeader Where to return the pointer to the page header on success.
3102 * @param ppbPageData Where to store the pointer to the page data.
3103 * @param pcbPage Where to store the size of the page in bytes.
3104 */
3105static int lsilogicR3ConfigurationPageGetExtended(PLSILOGICSCSICC pThisCC, PMptConfigurationRequest pConfigurationReq,
3106 PMptExtendedConfigurationPageHeader *ppPageHeader,
3107 uint8_t **ppbPageData, size_t *pcbPage)
3108{
3109 int rc = VINF_SUCCESS;
3110
3111 Log(("Extended page requested:\n"));
3112 Log(("u8ExtPageType=%#x\n", pConfigurationReq->u8ExtPageType));
3113 Log(("u8ExtPageLength=%d\n", pConfigurationReq->u16ExtPageLength));
3114
3115 switch (pConfigurationReq->u8ExtPageType)
3116 {
3117 case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT:
3118 {
3119 rc = lsilogicR3ConfigurationSASIOUnitPageGetFromNumber(pThisCC->pConfigurationPages,
3120 pConfigurationReq->u8PageNumber,
3121 ppPageHeader, ppbPageData, pcbPage);
3122 break;
3123 }
3124 case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASPHYS:
3125 {
3126 rc = lsilogicR3ConfigurationSASPHYPageGetFromNumber(pThisCC->pConfigurationPages,
3127 pConfigurationReq->u8PageNumber,
3128 pConfigurationReq->PageAddress,
3129 ppPageHeader, ppbPageData, pcbPage);
3130 break;
3131 }
3132 case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE:
3133 {
3134 rc = lsilogicR3ConfigurationSASDevicePageGetFromNumber(pThisCC->pConfigurationPages,
3135 pConfigurationReq->u8PageNumber,
3136 pConfigurationReq->PageAddress,
3137 ppPageHeader, ppbPageData, pcbPage);
3138 break;
3139 }
3140 case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASEXPANDER: /* No expanders supported */
3141 case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_ENCLOSURE: /* No enclosures supported */
3142 default:
3143 rc = VERR_NOT_FOUND;
3144 }
3145
3146 return rc;
3147}
3148
3149/**
3150 * Processes a Configuration request.
3151 *
3152 * @returns VBox status code.
3153 * @param pDevIns The device instance.
3154 * @param pThis Pointer to the shared LsiLogic device state.
3155 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
3156 * @param pConfigurationReq Pointer to the request structure.
3157 * @param pReply Pointer to the reply message frame
3158 */
3159static int lsilogicR3ProcessConfigurationRequest(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC,
3160 PMptConfigurationRequest pConfigurationReq, PMptConfigurationReply pReply)
3161{
3162 int rc = VINF_SUCCESS;
3163 uint8_t *pbPageData = NULL;
3164 PMptConfigurationPageHeader pPageHeader = NULL;
3165 PMptExtendedConfigurationPageHeader pExtPageHeader = NULL;
3166 uint8_t u8PageType;
3167 uint8_t u8PageAttribute;
3168 size_t cbPage = 0;
3169
3170 LogFlowFunc(("pThis=%#p\n", pThis));
3171
3172 u8PageType = MPT_CONFIGURATION_PAGE_TYPE_GET(pConfigurationReq->u8PageType);
3173 u8PageAttribute = MPT_CONFIGURATION_PAGE_ATTRIBUTE_GET(pConfigurationReq->u8PageType);
3174
3175 Log(("GuestRequest:\n"));
3176 Log(("u8Action=%#x\n", pConfigurationReq->u8Action));
3177 Log(("u8PageType=%#x\n", u8PageType));
3178 Log(("u8PageNumber=%d\n", pConfigurationReq->u8PageNumber));
3179 Log(("u8PageLength=%d\n", pConfigurationReq->u8PageLength));
3180 Log(("u8PageVersion=%d\n", pConfigurationReq->u8PageVersion));
3181
3182 /* Copy common bits from the request into the reply. */
3183 pReply->u8MessageLength = 6; /* 6 32bit D-Words. */
3184 pReply->u8Action = pConfigurationReq->u8Action;
3185 pReply->u8Function = pConfigurationReq->u8Function;
3186 pReply->u32MessageContext = pConfigurationReq->u32MessageContext;
3187
3188 switch (u8PageType)
3189 {
3190 case MPT_CONFIGURATION_PAGE_TYPE_IO_UNIT:
3191 {
3192 /* Get the page data. */
3193 rc = lsilogicR3ConfigurationIOUnitPageGetFromNumber(pThis,
3194 pThisCC->pConfigurationPages,
3195 pConfigurationReq->u8PageNumber,
3196 &pPageHeader, &pbPageData, &cbPage);
3197 break;
3198 }
3199 case MPT_CONFIGURATION_PAGE_TYPE_IOC:
3200 {
3201 /* Get the page data. */
3202 rc = lsilogicR3ConfigurationIOCPageGetFromNumber(pThis,
3203 pThisCC->pConfigurationPages,
3204 pConfigurationReq->u8PageNumber,
3205 &pPageHeader, &pbPageData, &cbPage);
3206 break;
3207 }
3208 case MPT_CONFIGURATION_PAGE_TYPE_MANUFACTURING:
3209 {
3210 /* Get the page data. */
3211 rc = lsilogicR3ConfigurationManufacturingPageGetFromNumber(pThis,
3212 pThisCC->pConfigurationPages,
3213 pConfigurationReq->u8PageNumber,
3214 &pPageHeader, &pbPageData, &cbPage);
3215 break;
3216 }
3217 case MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT:
3218 {
3219 /* Get the page data. */
3220 rc = lsilogicR3ConfigurationSCSISPIPortPageGetFromNumber(pThis,
3221 pThisCC->pConfigurationPages,
3222 pConfigurationReq->PageAddress.MPIPortNumber.u8PortNumber,
3223 pConfigurationReq->u8PageNumber,
3224 &pPageHeader, &pbPageData, &cbPage);
3225 break;
3226 }
3227 case MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE:
3228 {
3229 /* Get the page data. */
3230 rc = lsilogicR3ConfigurationSCSISPIDevicePageGetFromNumber(pThis,
3231 pThisCC->pConfigurationPages,
3232 pConfigurationReq->PageAddress.BusAndTargetId.u8Bus,
3233 pConfigurationReq->PageAddress.BusAndTargetId.u8TargetID,
3234 pConfigurationReq->u8PageNumber,
3235 &pPageHeader, &pbPageData, &cbPage);
3236 break;
3237 }
3238 case MPT_CONFIGURATION_PAGE_TYPE_BIOS:
3239 {
3240 rc = lsilogicR3ConfigurationBiosPageGetFromNumber(pThis,
3241 pThisCC->pConfigurationPages,
3242 pConfigurationReq->u8PageNumber,
3243 &pPageHeader, &pbPageData, &cbPage);
3244 break;
3245 }
3246 case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED:
3247 {
3248 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
3249 {
3250 rc = lsilogicR3ConfigurationPageGetExtended(pThisCC,
3251 pConfigurationReq,
3252 &pExtPageHeader, &pbPageData, &cbPage);
3253 }
3254 else
3255 rc = VERR_NOT_FOUND;
3256 break;
3257 }
3258 default:
3259 rc = VERR_NOT_FOUND;
3260 }
3261
3262 if (rc == VERR_NOT_FOUND)
3263 {
3264 Log(("Page not found\n"));
3265 pReply->u8PageType = pConfigurationReq->u8PageType;
3266 pReply->u8PageNumber = pConfigurationReq->u8PageNumber;
3267 pReply->u8PageLength = pConfigurationReq->u8PageLength;
3268 pReply->u8PageVersion = pConfigurationReq->u8PageVersion;
3269 pReply->u16IOCStatus = MPT_IOCSTATUS_CONFIG_INVALID_PAGE;
3270 return VINF_SUCCESS;
3271 }
3272
3273 if (u8PageType == MPT_CONFIGURATION_PAGE_TYPE_EXTENDED)
3274 {
3275 pReply->u8PageType = pExtPageHeader->u8PageType;
3276 pReply->u8PageNumber = pExtPageHeader->u8PageNumber;
3277 pReply->u8PageVersion = pExtPageHeader->u8PageVersion;
3278 pReply->u8ExtPageType = pExtPageHeader->u8ExtPageType;
3279 pReply->u16ExtPageLength = pExtPageHeader->u16ExtPageLength;
3280
3281 for (int i = 0; i < pExtPageHeader->u16ExtPageLength; i++)
3282 LogFlowFunc(("PageData[%d]=%#x\n", i, ((uint32_t *)pbPageData)[i]));
3283 }
3284 else
3285 {
3286 pReply->u8PageType = pPageHeader->u8PageType;
3287 pReply->u8PageNumber = pPageHeader->u8PageNumber;
3288 pReply->u8PageLength = pPageHeader->u8PageLength;
3289 pReply->u8PageVersion = pPageHeader->u8PageVersion;
3290
3291 for (int i = 0; i < pReply->u8PageLength; i++)
3292 LogFlowFunc(("PageData[%d]=%#x\n", i, ((uint32_t *)pbPageData)[i]));
3293 }
3294
3295 /*
3296 * Don't use the scatter gather handling code as the configuration request always have only one
3297 * simple element.
3298 */
3299 switch (pConfigurationReq->u8Action)
3300 {
3301 case MPT_CONFIGURATION_REQUEST_ACTION_DEFAULT: /* Nothing to do. We are always using the defaults. */
3302 case MPT_CONFIGURATION_REQUEST_ACTION_HEADER:
3303 {
3304 /* Already copied above nothing to do. */
3305 break;
3306 }
3307 case MPT_CONFIGURATION_REQUEST_ACTION_READ_NVRAM:
3308 case MPT_CONFIGURATION_REQUEST_ACTION_READ_CURRENT:
3309 case MPT_CONFIGURATION_REQUEST_ACTION_READ_DEFAULT:
3310 {
3311 uint32_t cbBuffer = pConfigurationReq->SimpleSGElement.u24Length;
3312 if (cbBuffer != 0)
3313 {
3314 RTGCPHYS GCPhysAddrPageBuffer = pConfigurationReq->SimpleSGElement.u32DataBufferAddressLow;
3315 if (pConfigurationReq->SimpleSGElement.f64BitAddress)
3316 GCPhysAddrPageBuffer |= (uint64_t)pConfigurationReq->SimpleSGElement.u32DataBufferAddressHigh << 32;
3317
3318 PDMDevHlpPCIPhysWriteMeta(pDevIns, GCPhysAddrPageBuffer, pbPageData, RT_MIN(cbBuffer, cbPage));
3319 }
3320 break;
3321 }
3322 case MPT_CONFIGURATION_REQUEST_ACTION_WRITE_CURRENT:
3323 case MPT_CONFIGURATION_REQUEST_ACTION_WRITE_NVRAM:
3324 {
3325 uint32_t cbBuffer = pConfigurationReq->SimpleSGElement.u24Length;
3326 if (cbBuffer != 0)
3327 {
3328 RTGCPHYS GCPhysAddrPageBuffer = pConfigurationReq->SimpleSGElement.u32DataBufferAddressLow;
3329 if (pConfigurationReq->SimpleSGElement.f64BitAddress)
3330 GCPhysAddrPageBuffer |= (uint64_t)pConfigurationReq->SimpleSGElement.u32DataBufferAddressHigh << 32;
3331
3332 LogFlow(("cbBuffer=%u cbPage=%u\n", cbBuffer, cbPage));
3333
3334 PDMDevHlpPCIPhysReadMeta(pDevIns, GCPhysAddrPageBuffer, pbPageData, RT_MIN(cbBuffer, cbPage));
3335 }
3336 break;
3337 }
3338 default:
3339 AssertMsgFailed(("todo\n"));
3340 }
3341
3342 return VINF_SUCCESS;
3343}
3344
3345/**
3346 * Initializes the configuration pages for the SPI SCSI controller.
3347 *
3348 * @returns nothing
3349 * @param pThis Pointer to the shared LsiLogic device state.
3350 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
3351 */
3352static void lsilogicR3InitializeConfigurationPagesSpi(PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC)
3353{
3354 PMptConfigurationPagesSpi pPages = &pThisCC->pConfigurationPages->u.SpiPages;
3355
3356 AssertMsg(pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI, ("Controller is not the SPI SCSI one\n"));
3357 LogFlowFunc(("pThis=%#p\n", pThis));
3358 RT_NOREF(pThis);
3359
3360 /* Clear everything first. */
3361 memset(pPages, 0, sizeof(MptConfigurationPagesSpi));
3362
3363 for (unsigned i = 0; i < RT_ELEMENTS(pPages->aPortPages); i++)
3364 {
3365 /* SCSI-SPI port page 0. */
3366 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3367 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT;
3368 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.Header.u8PageNumber = 0;
3369 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIPort0) / 4;
3370 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fInformationUnitTransfersCapable = true;
3371 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fDTCapable = true;
3372 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fQASCapable = true;
3373 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.u8MinimumSynchronousTransferPeriod = 0;
3374 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.u8MaximumSynchronousOffset = 0xff;
3375 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fWide = true;
3376 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fAIPCapable = true;
3377 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.u2SignalingType = 0x3; /* Single Ended. */
3378
3379 /* SCSI-SPI port page 1. */
3380 pPages->aPortPages[i].SCSISPIPortPage1.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
3381 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT;
3382 pPages->aPortPages[i].SCSISPIPortPage1.u.fields.Header.u8PageNumber = 1;
3383 pPages->aPortPages[i].SCSISPIPortPage1.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIPort1) / 4;
3384 pPages->aPortPages[i].SCSISPIPortPage1.u.fields.u8SCSIID = 7;
3385 pPages->aPortPages[i].SCSISPIPortPage1.u.fields.u16PortResponseIDsBitmask = (1 << 7);
3386 pPages->aPortPages[i].SCSISPIPortPage1.u.fields.u32OnBusTimerValue = 0;
3387
3388 /* SCSI-SPI port page 2. */
3389 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
3390 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT;
3391 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.Header.u8PageNumber = 2;
3392 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIPort2) / 4;
3393 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.u4HostSCSIID = 7;
3394 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.u2InitializeHBA = 0x3;
3395 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.fTerminationDisabled = true;
3396 for (unsigned iDevice = 0; iDevice < RT_ELEMENTS(pPages->aPortPages[i].SCSISPIPortPage2.u.fields.aDeviceSettings); iDevice++)
3397 {
3398 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.aDeviceSettings[iDevice].fBootChoice = true;
3399 }
3400 /* Everything else 0 for now. */
3401 }
3402
3403 for (unsigned uBusCurr = 0; uBusCurr < RT_ELEMENTS(pPages->aBuses); uBusCurr++)
3404 {
3405 for (unsigned uDeviceCurr = 0; uDeviceCurr < RT_ELEMENTS(pPages->aBuses[uBusCurr].aDevicePages); uDeviceCurr++)
3406 {
3407 /* SCSI-SPI device page 0. */
3408 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage0.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3409 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE;
3410 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage0.u.fields.Header.u8PageNumber = 0;
3411 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage0.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIDevice0) / 4;
3412 /* Everything else 0 for now. */
3413
3414 /* SCSI-SPI device page 1. */
3415 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage1.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
3416 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE;
3417 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage1.u.fields.Header.u8PageNumber = 1;
3418 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage1.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIDevice1) / 4;
3419 /* Everything else 0 for now. */
3420
3421 /* SCSI-SPI device page 2. */
3422 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage2.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
3423 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE;
3424 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage2.u.fields.Header.u8PageNumber = 2;
3425 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage2.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIDevice2) / 4;
3426 /* Everything else 0 for now. */
3427
3428 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage3.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3429 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE;
3430 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage3.u.fields.Header.u8PageNumber = 3;
3431 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage3.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIDevice3) / 4;
3432 /* Everything else 0 for now. */
3433 }
3434 }
3435}
3436
3437/**
3438 * Generates a handle.
3439 *
3440 * @returns the handle.
3441 * @param pThis Pointer to the shared LsiLogic device state.
3442 */
3443DECLINLINE(uint16_t) lsilogicGetHandle(PLSILOGICSCSI pThis)
3444{
3445 uint16_t u16Handle = pThis->u16NextHandle++;
3446 return u16Handle;
3447}
3448
3449/**
3450 * Generates a SAS address (WWID)
3451 *
3452 * @returns nothing.
3453 * @param pSASAddress Pointer to an unitialised SAS address.
3454 * @param iId iId which will go into the address.
3455 *
3456 * @todo Generate better SAS addresses. (Request a block from SUN probably)
3457 */
3458void lsilogicSASAddressGenerate(PSASADDRESS pSASAddress, unsigned iId)
3459{
3460 pSASAddress->u8Address[0] = (0x5 << 5);
3461 pSASAddress->u8Address[1] = 0x01;
3462 pSASAddress->u8Address[2] = 0x02;
3463 pSASAddress->u8Address[3] = 0x03;
3464 pSASAddress->u8Address[4] = 0x04;
3465 pSASAddress->u8Address[5] = 0x05;
3466 pSASAddress->u8Address[6] = 0x06;
3467 pSASAddress->u8Address[7] = iId;
3468}
3469
3470/**
3471 * Initializes the configuration pages for the SAS SCSI controller.
3472 *
3473 * @returns nothing
3474 * @param pThis Pointer to the shared LsiLogic device state.
3475 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
3476 */
3477static void lsilogicR3InitializeConfigurationPagesSas(PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC)
3478{
3479 PMptConfigurationPagesSas pPages = &pThisCC->pConfigurationPages->u.SasPages;
3480
3481 AssertMsg(pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS, ("Controller is not the SAS SCSI one\n"));
3482
3483 LogFlowFunc(("pThis=%#p\n", pThis));
3484
3485 /* Manufacturing Page 7 - Connector settings. */
3486 PMptConfigurationPageManufacturing7 pManufacturingPage7 = pPages->pManufacturingPage7;
3487 AssertPtr(pManufacturingPage7);
3488
3489 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(pManufacturingPage7,
3490 0, 7,
3491 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
3492 /* Set size manually. */
3493 if (pPages->cbManufacturingPage7 / 4 > 255)
3494 pManufacturingPage7->u.fields.Header.u8PageLength = 255;
3495 else
3496 pManufacturingPage7->u.fields.Header.u8PageLength = pPages->cbManufacturingPage7 / 4;
3497 pManufacturingPage7->u.fields.u8NumPhys = pThis->cPorts;
3498
3499 /* SAS I/O unit page 0 - Port specific information. */
3500 PMptConfigurationPageSASIOUnit0 pSASPage0 = pPages->pSASIOUnitPage0;
3501 AssertPtr(pSASPage0);
3502
3503 MPT_CONFIG_EXTENDED_PAGE_HEADER_INIT(pSASPage0, pPages->cbSASIOUnitPage0,
3504 0, MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY,
3505 MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT);
3506 pSASPage0->u.fields.u8NumPhys = pThis->cPorts;
3507 pPages->pSASIOUnitPage0 = pSASPage0;
3508
3509 /* SAS I/O unit page 1 - Port specific settings. */
3510 PMptConfigurationPageSASIOUnit1 pSASPage1 = pPages->pSASIOUnitPage1;
3511 AssertPtr(pSASPage1);
3512
3513 MPT_CONFIG_EXTENDED_PAGE_HEADER_INIT(pSASPage1, pPages->cbSASIOUnitPage1,
3514 1, MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE,
3515 MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT);
3516 pSASPage1->u.fields.u8NumPhys = pSASPage0->u.fields.u8NumPhys;
3517 pSASPage1->u.fields.u16ControlFlags = 0;
3518 pSASPage1->u.fields.u16AdditionalControlFlags = 0;
3519
3520 /* SAS I/O unit page 2 - Port specific information. */
3521 pPages->SASIOUnitPage2.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3522 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
3523 pPages->SASIOUnitPage2.u.fields.ExtHeader.u8PageNumber = 2;
3524 pPages->SASIOUnitPage2.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT;
3525 pPages->SASIOUnitPage2.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASIOUnit2) / 4;
3526
3527 /* SAS I/O unit page 3 - Port specific information. */
3528 pPages->SASIOUnitPage3.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3529 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
3530 pPages->SASIOUnitPage3.u.fields.ExtHeader.u8PageNumber = 3;
3531 pPages->SASIOUnitPage3.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT;
3532 pPages->SASIOUnitPage3.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASIOUnit3) / 4;
3533
3534 Assert(pPages->cPHYs == pThis->cPorts);
3535 AssertPtr(pPages->paPHYs);
3536
3537 /* Initialize the PHY configuration */
3538 PMptSASDevice pSASDevice = pPages->pSASDeviceHead;
3539 for (unsigned i = 0; i < pThis->cPorts; i++)
3540 {
3541 PMptPHY pPHYPages = &pPages->paPHYs[i];
3542 uint16_t u16ControllerHandle = lsilogicGetHandle(pThis);
3543
3544 pManufacturingPage7->u.fields.aPHY[i].u8Location = LSILOGICSCSI_MANUFACTURING7_LOCATION_AUTO;
3545
3546 pSASPage0->u.fields.aPHY[i].u8Port = i;
3547 pSASPage0->u.fields.aPHY[i].u8PortFlags = 0;
3548 pSASPage0->u.fields.aPHY[i].u8PhyFlags = 0;
3549 pSASPage0->u.fields.aPHY[i].u8NegotiatedLinkRate = LSILOGICSCSI_SASIOUNIT0_NEGOTIATED_RATE_FAILED;
3550 pSASPage0->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_NO);
3551 pSASPage0->u.fields.aPHY[i].u16ControllerDevHandle = u16ControllerHandle;
3552 pSASPage0->u.fields.aPHY[i].u16AttachedDevHandle = 0; /* No device attached. */
3553 pSASPage0->u.fields.aPHY[i].u32DiscoveryStatus = 0; /* No errors */
3554
3555 pSASPage1->u.fields.aPHY[i].u8Port = i;
3556 pSASPage1->u.fields.aPHY[i].u8PortFlags = 0;
3557 pSASPage1->u.fields.aPHY[i].u8PhyFlags = 0;
3558 pSASPage1->u.fields.aPHY[i].u8MaxMinLinkRate = LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MIN_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_15GB)
3559 | LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MAX_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_30GB);
3560 pSASPage1->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_NO);
3561
3562 /* SAS PHY page 0. */
3563 pPHYPages->SASPHYPage0.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3564 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
3565 pPHYPages->SASPHYPage0.u.fields.ExtHeader.u8PageNumber = 0;
3566 pPHYPages->SASPHYPage0.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASPHYS;
3567 pPHYPages->SASPHYPage0.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASPHY0) / 4;
3568 pPHYPages->SASPHYPage0.u.fields.u8AttachedPhyIdentifier = i;
3569 pPHYPages->SASPHYPage0.u.fields.u32AttachedDeviceInfo = LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_SET(LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_NO);
3570 pPHYPages->SASPHYPage0.u.fields.u8ProgrammedLinkRate = LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MIN_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_15GB)
3571 | LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MAX_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_30GB);
3572 pPHYPages->SASPHYPage0.u.fields.u8HwLinkRate = LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MIN_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_15GB)
3573 | LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MAX_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_30GB);
3574
3575 /* SAS PHY page 1. */
3576 pPHYPages->SASPHYPage1.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3577 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
3578 pPHYPages->SASPHYPage1.u.fields.ExtHeader.u8PageNumber = 1;
3579 pPHYPages->SASPHYPage1.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASPHYS;
3580 pPHYPages->SASPHYPage1.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASPHY1) / 4;
3581
3582 /* Settings for present devices. */
3583 if (pThisCC->paDeviceStates[i].pDrvBase)
3584 {
3585 uint16_t u16DeviceHandle = lsilogicGetHandle(pThis);
3586 SASADDRESS SASAddress;
3587 AssertPtr(pSASDevice);
3588
3589 memset(&SASAddress, 0, sizeof(SASADDRESS));
3590 lsilogicSASAddressGenerate(&SASAddress, i);
3591
3592 pSASPage0->u.fields.aPHY[i].u8NegotiatedLinkRate = LSILOGICSCSI_SASIOUNIT0_NEGOTIATED_RATE_SET(LSILOGICSCSI_SASIOUNIT0_NEGOTIATED_RATE_30GB);
3593 pSASPage0->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_END)
3594 | LSILOGICSCSI_SASIOUNIT0_DEVICE_SSP_TARGET;
3595 pSASPage0->u.fields.aPHY[i].u16AttachedDevHandle = u16DeviceHandle;
3596 pSASPage1->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_END)
3597 | LSILOGICSCSI_SASIOUNIT0_DEVICE_SSP_TARGET;
3598 pSASPage0->u.fields.aPHY[i].u16ControllerDevHandle = u16DeviceHandle;
3599
3600 pPHYPages->SASPHYPage0.u.fields.u32AttachedDeviceInfo = LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_SET(LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_END);
3601 pPHYPages->SASPHYPage0.u.fields.SASAddress = SASAddress;
3602 pPHYPages->SASPHYPage0.u.fields.u16OwnerDevHandle = u16DeviceHandle;
3603 pPHYPages->SASPHYPage0.u.fields.u16AttachedDevHandle = u16DeviceHandle;
3604
3605 /* SAS device page 0. */
3606 pSASDevice->SASDevicePage0.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3607 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
3608 pSASDevice->SASDevicePage0.u.fields.ExtHeader.u8PageNumber = 0;
3609 pSASDevice->SASDevicePage0.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE;
3610 pSASDevice->SASDevicePage0.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASDevice0) / 4;
3611 pSASDevice->SASDevicePage0.u.fields.SASAddress = SASAddress;
3612 pSASDevice->SASDevicePage0.u.fields.u16ParentDevHandle = u16ControllerHandle;
3613 pSASDevice->SASDevicePage0.u.fields.u8PhyNum = i;
3614 pSASDevice->SASDevicePage0.u.fields.u8AccessStatus = LSILOGICSCSI_SASDEVICE0_STATUS_NO_ERRORS;
3615 pSASDevice->SASDevicePage0.u.fields.u16DevHandle = u16DeviceHandle;
3616 pSASDevice->SASDevicePage0.u.fields.u8TargetID = i;
3617 pSASDevice->SASDevicePage0.u.fields.u8Bus = 0;
3618 pSASDevice->SASDevicePage0.u.fields.u32DeviceInfo = LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_SET(LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_END)
3619 | LSILOGICSCSI_SASIOUNIT0_DEVICE_SSP_TARGET;
3620 pSASDevice->SASDevicePage0.u.fields.u16Flags = LSILOGICSCSI_SASDEVICE0_FLAGS_DEVICE_PRESENT
3621 | LSILOGICSCSI_SASDEVICE0_FLAGS_DEVICE_MAPPED_TO_BUS_AND_TARGET_ID
3622 | LSILOGICSCSI_SASDEVICE0_FLAGS_DEVICE_MAPPING_PERSISTENT;
3623 pSASDevice->SASDevicePage0.u.fields.u8PhysicalPort = i;
3624
3625 /* SAS device page 1. */
3626 pSASDevice->SASDevicePage1.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3627 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
3628 pSASDevice->SASDevicePage1.u.fields.ExtHeader.u8PageNumber = 1;
3629 pSASDevice->SASDevicePage1.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE;
3630 pSASDevice->SASDevicePage1.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASDevice1) / 4;
3631 pSASDevice->SASDevicePage1.u.fields.SASAddress = SASAddress;
3632 pSASDevice->SASDevicePage1.u.fields.u16DevHandle = u16DeviceHandle;
3633 pSASDevice->SASDevicePage1.u.fields.u8TargetID = i;
3634 pSASDevice->SASDevicePage1.u.fields.u8Bus = 0;
3635
3636 /* SAS device page 2. */
3637 pSASDevice->SASDevicePage2.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3638 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
3639 pSASDevice->SASDevicePage2.u.fields.ExtHeader.u8PageNumber = 2;
3640 pSASDevice->SASDevicePage2.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE;
3641 pSASDevice->SASDevicePage2.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASDevice2) / 4;
3642 pSASDevice->SASDevicePage2.u.fields.SASAddress = SASAddress;
3643
3644 pSASDevice = pSASDevice->pNext;
3645 }
3646 }
3647}
3648
3649/**
3650 * Initializes the configuration pages.
3651 *
3652 * @returns nothing
3653 * @param pDevIns The device instance.
3654 * @param pThis Pointer to the shared LsiLogic device state.
3655 * @param pThisCC Pointer to the ring-3 LsiLogic device state.
3656 */
3657static void lsilogicR3InitializeConfigurationPages(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis, PLSILOGICSCSICC pThisCC)
3658{
3659 /* Initialize the common pages. */
3660
3661 LogFlowFunc(("pThis=%#p\n", pThis));
3662
3663 AssertPtrReturnVoid(pThisCC->pConfigurationPages);
3664 PMptConfigurationPagesSupported pPages = pThisCC->pConfigurationPages;
3665
3666 /* Manufacturing Page 0. */
3667 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage0,
3668 MptConfigurationPageManufacturing0, 0,
3669 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
3670 strncpy((char *)pPages->ManufacturingPage0.u.fields.abChipName, "VBox MPT Fusion", 16);
3671 strncpy((char *)pPages->ManufacturingPage0.u.fields.abChipRevision, "1.0", 8);
3672 strncpy((char *)pPages->ManufacturingPage0.u.fields.abBoardName, "VBox MPT Fusion", 16);
3673 strncpy((char *)pPages->ManufacturingPage0.u.fields.abBoardAssembly, "SUN", 8);
3674 memcpy(pPages->ManufacturingPage0.u.fields.abBoardTracerNumber, "CAFECAFECAFECAFE", 16);
3675
3676 /* Manufacturing Page 1 - I don't know what this contains so we leave it 0 for now. */
3677 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage1,
3678 MptConfigurationPageManufacturing1, 1,
3679 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
3680
3681 /* Manufacturing Page 2. */
3682 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage2,
3683 MptConfigurationPageManufacturing2, 2,
3684 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
3685
3686 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
3687 {
3688 pPages->ManufacturingPage2.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SPI_DEVICE_ID;
3689 pPages->ManufacturingPage2.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SPI_REVISION_ID;
3690 }
3691 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
3692 {
3693 pPages->ManufacturingPage2.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SAS_DEVICE_ID;
3694 pPages->ManufacturingPage2.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SAS_REVISION_ID;
3695 }
3696
3697 /* Manufacturing Page 3. */
3698 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage3,
3699 MptConfigurationPageManufacturing3, 3,
3700 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
3701
3702 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
3703 {
3704 pPages->ManufacturingPage3.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SPI_DEVICE_ID;
3705 pPages->ManufacturingPage3.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SPI_REVISION_ID;
3706 }
3707 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
3708 {
3709 pPages->ManufacturingPage3.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SAS_DEVICE_ID;
3710 pPages->ManufacturingPage3.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SAS_REVISION_ID;
3711 }
3712
3713 /* Manufacturing Page 4 - I don't know what this contains so we leave it 0 for now. */
3714 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage4,
3715 MptConfigurationPageManufacturing4, 4,
3716 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
3717
3718 /* Manufacturing Page 5 - WWID settings. */
3719 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage5,
3720 MptConfigurationPageManufacturing5, 5,
3721 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
3722
3723 /* Manufacturing Page 6 - Product specific settings. */
3724 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage6,
3725 MptConfigurationPageManufacturing6, 6,
3726 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3727
3728 /* Manufacturing Page 8 - Product specific settings. */
3729 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage8,
3730 MptConfigurationPageManufacturing8, 8,
3731 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3732
3733 /* Manufacturing Page 9 - Product specific settings. */
3734 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage9,
3735 MptConfigurationPageManufacturing9, 9,
3736 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3737
3738 /* Manufacturing Page 10 - Product specific settings. */
3739 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage10,
3740 MptConfigurationPageManufacturing10, 10,
3741 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3742
3743 /* I/O Unit page 0. */
3744 MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage0,
3745 MptConfigurationPageIOUnit0, 0,
3746 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3747 pPages->IOUnitPage0.u.fields.u64UniqueIdentifier = 0xcafe;
3748
3749 /* I/O Unit page 1. */
3750 MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage1,
3751 MptConfigurationPageIOUnit1, 1,
3752 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3753 pPages->IOUnitPage1.u.fields.fSingleFunction = true;
3754 pPages->IOUnitPage1.u.fields.fAllPathsMapped = false;
3755 pPages->IOUnitPage1.u.fields.fIntegratedRAIDDisabled = true;
3756 pPages->IOUnitPage1.u.fields.f32BitAccessForced = false;
3757
3758 /* I/O Unit page 2. */
3759 MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage2,
3760 MptConfigurationPageIOUnit2, 2,
3761 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT);
3762 pPages->IOUnitPage2.u.fields.fPauseOnError = false;
3763 pPages->IOUnitPage2.u.fields.fVerboseModeEnabled = false;
3764 pPages->IOUnitPage2.u.fields.fDisableColorVideo = false;
3765 pPages->IOUnitPage2.u.fields.fNotHookInt40h = false;
3766 pPages->IOUnitPage2.u.fields.u32BIOSVersion = 0xcafecafe;
3767 pPages->IOUnitPage2.u.fields.aAdapterOrder[0].fAdapterEnabled = true;
3768 pPages->IOUnitPage2.u.fields.aAdapterOrder[0].fAdapterEmbedded = true;
3769 pPages->IOUnitPage2.u.fields.aAdapterOrder[0].u8PCIBusNumber = 0;
3770 pPages->IOUnitPage2.u.fields.aAdapterOrder[0].u8PCIDevFn = pDevIns->apPciDevs[0]->uDevFn;
3771
3772 /* I/O Unit page 3. */
3773 MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage3,
3774 MptConfigurationPageIOUnit3, 3,
3775 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3776 pPages->IOUnitPage3.u.fields.u8GPIOCount = 0;
3777
3778 /* I/O Unit page 4. */
3779 MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage4,
3780 MptConfigurationPageIOUnit4, 4,
3781 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3782
3783 /* IOC page 0. */
3784 MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage0,
3785 MptConfigurationPageIOC0, 0,
3786 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3787 pPages->IOCPage0.u.fields.u32TotalNVStore = 0;
3788 pPages->IOCPage0.u.fields.u32FreeNVStore = 0;
3789
3790 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
3791 {
3792 pPages->IOCPage0.u.fields.u16VendorId = LSILOGICSCSI_PCI_VENDOR_ID;
3793 pPages->IOCPage0.u.fields.u16DeviceId = LSILOGICSCSI_PCI_SPI_DEVICE_ID;
3794 pPages->IOCPage0.u.fields.u8RevisionId = LSILOGICSCSI_PCI_SPI_REVISION_ID;
3795 pPages->IOCPage0.u.fields.u32ClassCode = LSILOGICSCSI_PCI_SPI_CLASS_CODE;
3796 pPages->IOCPage0.u.fields.u16SubsystemVendorId = LSILOGICSCSI_PCI_SPI_SUBSYSTEM_VENDOR_ID;
3797 pPages->IOCPage0.u.fields.u16SubsystemId = LSILOGICSCSI_PCI_SPI_SUBSYSTEM_ID;
3798 }
3799 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
3800 {
3801 pPages->IOCPage0.u.fields.u16VendorId = LSILOGICSCSI_PCI_VENDOR_ID;
3802 pPages->IOCPage0.u.fields.u16DeviceId = LSILOGICSCSI_PCI_SAS_DEVICE_ID;
3803 pPages->IOCPage0.u.fields.u8RevisionId = LSILOGICSCSI_PCI_SAS_REVISION_ID;
3804 pPages->IOCPage0.u.fields.u32ClassCode = LSILOGICSCSI_PCI_SAS_CLASS_CODE;
3805 pPages->IOCPage0.u.fields.u16SubsystemVendorId = LSILOGICSCSI_PCI_SAS_SUBSYSTEM_VENDOR_ID;
3806 pPages->IOCPage0.u.fields.u16SubsystemId = LSILOGICSCSI_PCI_SAS_SUBSYSTEM_ID;
3807 }
3808
3809 /* IOC page 1. */
3810 MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage1,
3811 MptConfigurationPageIOC1, 1,
3812 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3813 pPages->IOCPage1.u.fields.fReplyCoalescingEnabled = false;
3814 pPages->IOCPage1.u.fields.u32CoalescingTimeout = 0;
3815 pPages->IOCPage1.u.fields.u8CoalescingDepth = 0;
3816
3817 /* IOC page 2. */
3818 MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage2,
3819 MptConfigurationPageIOC2, 2,
3820 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3821 /* Everything else here is 0. */
3822
3823 /* IOC page 3. */
3824 MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage3,
3825 MptConfigurationPageIOC3, 3,
3826 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3827 /* Everything else here is 0. */
3828
3829 /* IOC page 4. */
3830 MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage4,
3831 MptConfigurationPageIOC4, 4,
3832 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3833 /* Everything else here is 0. */
3834
3835 /* IOC page 6. */
3836 MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage6,
3837 MptConfigurationPageIOC6, 6,
3838 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3839 /* Everything else here is 0. */
3840
3841 /* BIOS page 1. */
3842 MPT_CONFIG_PAGE_HEADER_INIT_BIOS(&pPages->BIOSPage1,
3843 MptConfigurationPageBIOS1, 1,
3844 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3845
3846 /* BIOS page 2. */
3847 MPT_CONFIG_PAGE_HEADER_INIT_BIOS(&pPages->BIOSPage2,
3848 MptConfigurationPageBIOS2, 2,
3849 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3850
3851 /* BIOS page 4. */
3852 MPT_CONFIG_PAGE_HEADER_INIT_BIOS(&pPages->BIOSPage4,
3853 MptConfigurationPageBIOS4, 4,
3854 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3855
3856 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
3857 lsilogicR3InitializeConfigurationPagesSpi(pThis, pThisCC);
3858 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
3859 lsilogicR3InitializeConfigurationPagesSas(pThis, pThisCC);
3860 else
3861 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
3862}
3863
3864/**
3865 * Sets the emulated controller type from a given string.
3866 *
3867 * @returns VBox status code.
3868 *
3869 * @param pThis Pointer to the shared LsiLogic device state.
3870 * @param pcszCtrlType The string to use.
3871 */
3872static int lsilogicR3GetCtrlTypeFromString(PLSILOGICSCSI pThis, const char *pcszCtrlType)
3873{
3874 int rc = VERR_INVALID_PARAMETER;
3875
3876 if (!RTStrCmp(pcszCtrlType, LSILOGICSCSI_PCI_SPI_CTRLNAME))
3877 {
3878 pThis->enmCtrlType = LSILOGICCTRLTYPE_SCSI_SPI;
3879 rc = VINF_SUCCESS;
3880 }
3881 else if (!RTStrCmp(pcszCtrlType, LSILOGICSCSI_PCI_SAS_CTRLNAME))
3882 {
3883 pThis->enmCtrlType = LSILOGICCTRLTYPE_SCSI_SAS;
3884 rc = VINF_SUCCESS;
3885 }
3886
3887 return rc;
3888}
3889
3890/**
3891 * @callback_method_impl{PFNDBGFHANDLERDEV}
3892 */
3893static DECLCALLBACK(void) lsilogicR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
3894{
3895 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
3896 PLSILOGICSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC);
3897
3898 /*
3899 * Parse args.
3900 */
3901 bool const fVerbose = pszArgs && strstr(pszArgs, "verbose") != NULL;
3902
3903 /*
3904 * Show info.
3905 */
3906 pHlp->pfnPrintf(pHlp,
3907 "%s#%d: port=%04x mmio=%RGp max-devices=%u GC=%RTbool R0=%RTbool\n",
3908 pDevIns->pReg->szName, pDevIns->iInstance,
3909 PDMDevHlpIoPortGetMappingAddress(pDevIns, pThis->hIoPortsReg),
3910 PDMDevHlpMmioGetMappingAddress(pDevIns, pThis->hMmioReg),
3911 pThis->cDeviceStates, pDevIns->fRCEnabled, pDevIns->fR0Enabled);
3912
3913 /*
3914 * Show general state.
3915 */
3916 pHlp->pfnPrintf(pHlp, "enmState=%u\n", pThis->enmState);
3917 pHlp->pfnPrintf(pHlp, "enmWhoInit=%u\n", pThis->enmWhoInit);
3918 pHlp->pfnPrintf(pHlp, "enmDoorbellState=%d\n", pThis->enmDoorbellState);
3919 pHlp->pfnPrintf(pHlp, "fDiagnosticEnabled=%RTbool\n", pThis->fDiagnosticEnabled);
3920 pHlp->pfnPrintf(pHlp, "fNotificationSent=%RTbool\n", pThis->fNotificationSent);
3921 pHlp->pfnPrintf(pHlp, "fEventNotificationEnabled=%RTbool\n", pThis->fEventNotificationEnabled);
3922 pHlp->pfnPrintf(pHlp, "uInterruptMask=%#x\n", pThis->uInterruptMask);
3923 pHlp->pfnPrintf(pHlp, "uInterruptStatus=%#x\n", pThis->uInterruptStatus);
3924 pHlp->pfnPrintf(pHlp, "u16IOCFaultCode=%#06x\n", pThis->u16IOCFaultCode);
3925 pHlp->pfnPrintf(pHlp, "u32HostMFAHighAddr=%#x\n", pThis->u32HostMFAHighAddr);
3926 pHlp->pfnPrintf(pHlp, "u32SenseBufferHighAddr=%#x\n", pThis->u32SenseBufferHighAddr);
3927 pHlp->pfnPrintf(pHlp, "cMaxDevices=%u\n", pThis->cMaxDevices);
3928 pHlp->pfnPrintf(pHlp, "cMaxBuses=%u\n", pThis->cMaxBuses);
3929 pHlp->pfnPrintf(pHlp, "cbReplyFrame=%u\n", pThis->cbReplyFrame);
3930 pHlp->pfnPrintf(pHlp, "cReplyQueueEntries=%u\n", pThis->cReplyQueueEntries);
3931 pHlp->pfnPrintf(pHlp, "cRequestQueueEntries=%u\n", pThis->cRequestQueueEntries);
3932 pHlp->pfnPrintf(pHlp, "cPorts=%u\n", pThis->cPorts);
3933
3934 /*
3935 * Show queue status.
3936 */
3937 pHlp->pfnPrintf(pHlp, "uReplyFreeQueueNextEntryFreeWrite=%u\n", pThis->uReplyFreeQueueNextEntryFreeWrite);
3938 pHlp->pfnPrintf(pHlp, "uReplyFreeQueueNextAddressRead=%u\n", pThis->uReplyFreeQueueNextAddressRead);
3939 pHlp->pfnPrintf(pHlp, "uReplyPostQueueNextEntryFreeWrite=%u\n", pThis->uReplyPostQueueNextEntryFreeWrite);
3940 pHlp->pfnPrintf(pHlp, "uReplyPostQueueNextAddressRead=%u\n", pThis->uReplyPostQueueNextAddressRead);
3941 pHlp->pfnPrintf(pHlp, "uRequestQueueNextEntryFreeWrite=%u\n", pThis->uRequestQueueNextEntryFreeWrite);
3942 pHlp->pfnPrintf(pHlp, "uRequestQueueNextAddressRead=%u\n", pThis->uRequestQueueNextAddressRead);
3943
3944 /*
3945 * Show queue content if verbose
3946 */
3947 if (fVerbose)
3948 {
3949 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
3950 pHlp->pfnPrintf(pHlp, "RFQ[%u]=%#x\n", i, pThis->aReplyFreeQueue[i]);
3951
3952 pHlp->pfnPrintf(pHlp, "\n");
3953
3954 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
3955 pHlp->pfnPrintf(pHlp, "RPQ[%u]=%#x\n", i, pThis->aReplyPostQueue[i]);
3956
3957 pHlp->pfnPrintf(pHlp, "\n");
3958
3959 for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
3960 pHlp->pfnPrintf(pHlp, "ReqQ[%u]=%#x\n", i, pThis->aRequestQueue[i]);
3961 }
3962
3963 /*
3964 * Print the device status.
3965 */
3966 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
3967 {
3968 PLSILOGICDEVICE pDevice = &pThisCC->paDeviceStates[i];
3969
3970 pHlp->pfnPrintf(pHlp, "\n");
3971
3972 pHlp->pfnPrintf(pHlp, "Device[%u]: device-attached=%RTbool cOutstandingRequests=%u\n",
3973 i, pDevice->pDrvBase != NULL, pDevice->cOutstandingRequests);
3974 }
3975}
3976
3977
3978/**
3979 * @callback_method_impl{FNPDMTHREADDEV}
3980 */
3981static DECLCALLBACK(int) lsilogicR3Worker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
3982{
3983 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
3984 PLSILOGICSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC);
3985 int rc = VINF_SUCCESS;
3986
3987 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
3988 return VINF_SUCCESS;
3989
3990 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
3991 {
3992 ASMAtomicWriteBool(&pThis->fWrkThreadSleeping, true);
3993 bool fNotificationSent = ASMAtomicXchgBool(&pThis->fNotificationSent, false);
3994 if (!fNotificationSent)
3995 {
3996 Assert(ASMAtomicReadBool(&pThis->fWrkThreadSleeping));
3997 rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEvtProcess, RT_INDEFINITE_WAIT);
3998 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
3999 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
4000 break;
4001 LogFlowFunc(("Woken up with rc=%Rrc\n", rc));
4002 ASMAtomicWriteBool(&pThis->fNotificationSent, false);
4003 }
4004
4005 ASMAtomicWriteBool(&pThis->fWrkThreadSleeping, false);
4006
4007 /* Only process request which arrived before we received the notification. */
4008 uint32_t uRequestQueueNextEntryWrite = ASMAtomicReadU32(&pThis->uRequestQueueNextEntryFreeWrite);
4009
4010 /* Go through the messages now and process them. */
4011 while ( RT_LIKELY(pThis->enmState == LSILOGICSTATE_OPERATIONAL)
4012 && (pThis->uRequestQueueNextAddressRead != uRequestQueueNextEntryWrite))
4013 {
4014 MptRequestUnion GuestRequest;
4015 uint32_t u32RequestMessageFrameDesc = pThis->aRequestQueue[pThis->uRequestQueueNextAddressRead];
4016 RTGCPHYS GCPhysMessageFrameAddr = LSILOGIC_RTGCPHYS_FROM_U32(pThis->u32HostMFAHighAddr,
4017 (u32RequestMessageFrameDesc & ~0x07));
4018
4019 /* Read the message header from the guest first. */
4020 PDMDevHlpPCIPhysReadMeta(pDevIns, GCPhysMessageFrameAddr, &GuestRequest, sizeof(MptMessageHdr));
4021
4022 /* Determine the size of the request. */
4023 uint32_t cbRequest = 0;
4024 switch (GuestRequest.Header.u8Function)
4025 {
4026 case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST:
4027 cbRequest = sizeof(MptSCSIIORequest);
4028 break;
4029 case MPT_MESSAGE_HDR_FUNCTION_SCSI_TASK_MGMT:
4030 cbRequest = sizeof(MptSCSITaskManagementRequest);
4031 break;
4032 case MPT_MESSAGE_HDR_FUNCTION_IOC_INIT:
4033 cbRequest = sizeof(MptIOCInitRequest);
4034 break;
4035 case MPT_MESSAGE_HDR_FUNCTION_IOC_FACTS:
4036 cbRequest = sizeof(MptIOCFactsRequest);
4037 break;
4038 case MPT_MESSAGE_HDR_FUNCTION_CONFIG:
4039 cbRequest = sizeof(MptConfigurationRequest);
4040 break;
4041 case MPT_MESSAGE_HDR_FUNCTION_PORT_FACTS:
4042 cbRequest = sizeof(MptPortFactsRequest);
4043 break;
4044 case MPT_MESSAGE_HDR_FUNCTION_PORT_ENABLE:
4045 cbRequest = sizeof(MptPortEnableRequest);
4046 break;
4047 case MPT_MESSAGE_HDR_FUNCTION_EVENT_NOTIFICATION:
4048 cbRequest = sizeof(MptEventNotificationRequest);
4049 break;
4050 case MPT_MESSAGE_HDR_FUNCTION_EVENT_ACK:
4051 AssertMsgFailed(("todo\n"));
4052 //cbRequest = sizeof(MptEventAckRequest);
4053 break;
4054 case MPT_MESSAGE_HDR_FUNCTION_FW_DOWNLOAD:
4055 cbRequest = sizeof(MptFWDownloadRequest);
4056 break;
4057 case MPT_MESSAGE_HDR_FUNCTION_FW_UPLOAD:
4058 cbRequest = sizeof(MptFWUploadRequest);
4059 break;
4060 default:
4061 AssertMsgFailed(("Unknown function issued %u\n", GuestRequest.Header.u8Function));
4062 lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INVALID_FUNCTION);
4063 }
4064
4065 if (cbRequest != 0)
4066 {
4067 /* Read the complete message frame from guest memory now. */
4068 PDMDevHlpPCIPhysReadMeta(pDevIns, GCPhysMessageFrameAddr, &GuestRequest, cbRequest);
4069
4070 /* Handle SCSI I/O requests now. */
4071 if (GuestRequest.Header.u8Function == MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST)
4072 {
4073 rc = lsilogicR3ProcessSCSIIORequest(pDevIns, pThis, pThisCC, GCPhysMessageFrameAddr, &GuestRequest);
4074 AssertRC(rc);
4075 }
4076 else
4077 {
4078 MptReplyUnion Reply;
4079 rc = lsilogicR3ProcessMessageRequest(pDevIns, pThis, pThisCC, &GuestRequest.Header, &Reply);
4080 AssertRC(rc);
4081 }
4082
4083 pThis->uRequestQueueNextAddressRead++;
4084 pThis->uRequestQueueNextAddressRead %= pThis->cRequestQueueEntries;
4085 }
4086 } /* While request frames available. */
4087 } /* While running */
4088
4089 return VINF_SUCCESS;
4090}
4091
4092
4093/**
4094 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
4095 */
4096static DECLCALLBACK(int) lsilogicR3WorkerWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
4097{
4098 RT_NOREF(pThread);
4099 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4100 return PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEvtProcess);
4101}
4102
4103
4104/**
4105 * Kicks the controller to process pending tasks after the VM was resumed
4106 * or loaded from a saved state.
4107 *
4108 * @returns nothing.
4109 * @param pDevIns The device instance.
4110 * @param pThis Pointer to the shared LsiLogic device state.
4111 */
4112static void lsilogicR3Kick(PPDMDEVINS pDevIns, PLSILOGICSCSI pThis)
4113{
4114 if (pThis->fNotificationSent)
4115 {
4116 /* Notify the worker thread that there are pending requests. */
4117 LogFlowFunc(("Signal event semaphore\n"));
4118 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEvtProcess);
4119 AssertRC(rc);
4120 }
4121}
4122
4123
4124/*
4125 * Saved state.
4126 */
4127
4128/**
4129 * @callback_method_impl{FNSSMDEVLIVEEXEC}
4130 */
4131static DECLCALLBACK(int) lsilogicR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
4132{
4133 RT_NOREF(uPass);
4134 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4135 PLSILOGICSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC);
4136 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4137
4138 pHlp->pfnSSMPutU32(pSSM, pThis->enmCtrlType);
4139 pHlp->pfnSSMPutU32(pSSM, pThis->cDeviceStates);
4140 pHlp->pfnSSMPutU32(pSSM, pThis->cPorts);
4141
4142 /* Save the device config. */
4143 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
4144 pHlp->pfnSSMPutBool(pSSM, pThisCC->paDeviceStates[i].pDrvBase != NULL);
4145
4146 return VINF_SSM_DONT_CALL_AGAIN;
4147}
4148
4149/**
4150 * @callback_method_impl{FNSSMDEVSAVEEXEC}
4151 */
4152static DECLCALLBACK(int) lsilogicR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4153{
4154 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4155 PLSILOGICSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC);
4156 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4157
4158 /* Every device first. */
4159 lsilogicR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
4160 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
4161 {
4162 PLSILOGICDEVICE pDevice = &pThisCC->paDeviceStates[i];
4163
4164 AssertMsg(!pDevice->cOutstandingRequests,
4165 ("There are still outstanding requests on this device\n"));
4166 pHlp->pfnSSMPutU32(pSSM, pDevice->cOutstandingRequests);
4167
4168 /* Query all suspended requests and store them in the request queue. */
4169 if (pDevice->pDrvMediaEx)
4170 {
4171 uint32_t cReqsRedo = pDevice->pDrvMediaEx->pfnIoReqGetSuspendedCount(pDevice->pDrvMediaEx);
4172 if (cReqsRedo)
4173 {
4174 PDMMEDIAEXIOREQ hIoReq;
4175 PLSILOGICREQ pReq;
4176 int rc = pDevice->pDrvMediaEx->pfnIoReqQuerySuspendedStart(pDevice->pDrvMediaEx, &hIoReq,
4177 (void **)&pReq);
4178 AssertRCBreak(rc);
4179
4180 for (;;)
4181 {
4182 /* Write only the lower 32bit part of the address. */
4183 ASMAtomicWriteU32(&pThis->aRequestQueue[pThis->uRequestQueueNextEntryFreeWrite],
4184 pReq->GCPhysMessageFrameAddr & UINT32_C(0xffffffff));
4185
4186 pThis->uRequestQueueNextEntryFreeWrite++;
4187 pThis->uRequestQueueNextEntryFreeWrite %= pThis->cRequestQueueEntries;
4188
4189 cReqsRedo--;
4190 if (!cReqsRedo)
4191 break;
4192
4193 rc = pDevice->pDrvMediaEx->pfnIoReqQuerySuspendedNext(pDevice->pDrvMediaEx, hIoReq,
4194 &hIoReq, (void **)&pReq);
4195 AssertRCBreak(rc);
4196 }
4197 }
4198 }
4199 }
4200
4201 /* Now the main device state. */
4202 pHlp->pfnSSMPutU32(pSSM, pThis->enmState);
4203 pHlp->pfnSSMPutU32(pSSM, pThis->enmWhoInit);
4204 pHlp->pfnSSMPutU32(pSSM, pThis->enmDoorbellState);
4205 pHlp->pfnSSMPutBool(pSSM, pThis->fDiagnosticEnabled);
4206 pHlp->pfnSSMPutBool(pSSM, pThis->fNotificationSent);
4207 pHlp->pfnSSMPutBool(pSSM, pThis->fEventNotificationEnabled);
4208 pHlp->pfnSSMPutU32(pSSM, pThis->uInterruptMask);
4209 pHlp->pfnSSMPutU32(pSSM, pThis->uInterruptStatus);
4210 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aMessage); i++)
4211 pHlp->pfnSSMPutU32(pSSM, pThis->aMessage[i]);
4212 pHlp->pfnSSMPutU32(pSSM, pThis->iMessage);
4213 pHlp->pfnSSMPutU32(pSSM, pThis->cMessage);
4214 pHlp->pfnSSMPutMem(pSSM, &pThis->ReplyBuffer, sizeof(pThis->ReplyBuffer));
4215 pHlp->pfnSSMPutU32(pSSM, pThis->uNextReplyEntryRead);
4216 pHlp->pfnSSMPutU32(pSSM, pThis->cReplySize);
4217 pHlp->pfnSSMPutU16(pSSM, pThis->u16IOCFaultCode);
4218 pHlp->pfnSSMPutU32(pSSM, pThis->u32HostMFAHighAddr);
4219 pHlp->pfnSSMPutU32(pSSM, pThis->u32SenseBufferHighAddr);
4220 pHlp->pfnSSMPutU8(pSSM, pThis->cMaxDevices);
4221 pHlp->pfnSSMPutU8(pSSM, pThis->cMaxBuses);
4222 pHlp->pfnSSMPutU16(pSSM, pThis->cbReplyFrame);
4223 pHlp->pfnSSMPutU32(pSSM, pThis->iDiagnosticAccess);
4224 pHlp->pfnSSMPutU32(pSSM, pThis->cReplyQueueEntries);
4225 pHlp->pfnSSMPutU32(pSSM, pThis->cRequestQueueEntries);
4226 pHlp->pfnSSMPutU32(pSSM, pThis->uReplyFreeQueueNextEntryFreeWrite);
4227 pHlp->pfnSSMPutU32(pSSM, pThis->uReplyFreeQueueNextAddressRead);
4228 pHlp->pfnSSMPutU32(pSSM, pThis->uReplyPostQueueNextEntryFreeWrite);
4229 pHlp->pfnSSMPutU32(pSSM, pThis->uReplyPostQueueNextAddressRead);
4230 pHlp->pfnSSMPutU32(pSSM, pThis->uRequestQueueNextEntryFreeWrite);
4231 pHlp->pfnSSMPutU32(pSSM, pThis->uRequestQueueNextAddressRead);
4232
4233 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
4234 pHlp->pfnSSMPutU32(pSSM, pThis->aReplyFreeQueue[i]);
4235 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
4236 pHlp->pfnSSMPutU32(pSSM, pThis->aReplyPostQueue[i]);
4237 for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
4238 pHlp->pfnSSMPutU32(pSSM, pThis->aRequestQueue[i]);
4239
4240 pHlp->pfnSSMPutU16(pSSM, pThis->u16NextHandle);
4241
4242 /* Save diagnostic memory register and data regions. */
4243 pHlp->pfnSSMPutU32(pSSM, pThis->u32DiagMemAddr);
4244 pHlp->pfnSSMPutU32(pSSM, lsilogicR3MemRegionsCount(pThisCC));
4245
4246 PLSILOGICMEMREGN pIt;
4247 RTListForEach(&pThisCC->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
4248 {
4249 pHlp->pfnSSMPutU32(pSSM, pIt->u32AddrStart);
4250 pHlp->pfnSSMPutU32(pSSM, pIt->u32AddrEnd);
4251 pHlp->pfnSSMPutMem(pSSM, &pIt->au32Data[0], (pIt->u32AddrEnd - pIt->u32AddrStart + 1) * sizeof(uint32_t));
4252 }
4253
4254 PMptConfigurationPagesSupported pPages = pThisCC->pConfigurationPages;
4255
4256 pHlp->pfnSSMPutMem(pSSM, &pPages->ManufacturingPage0, sizeof(MptConfigurationPageManufacturing0));
4257 pHlp->pfnSSMPutMem(pSSM, &pPages->ManufacturingPage1, sizeof(MptConfigurationPageManufacturing1));
4258 pHlp->pfnSSMPutMem(pSSM, &pPages->ManufacturingPage2, sizeof(MptConfigurationPageManufacturing2));
4259 pHlp->pfnSSMPutMem(pSSM, &pPages->ManufacturingPage3, sizeof(MptConfigurationPageManufacturing3));
4260 pHlp->pfnSSMPutMem(pSSM, &pPages->ManufacturingPage4, sizeof(MptConfigurationPageManufacturing4));
4261 pHlp->pfnSSMPutMem(pSSM, &pPages->ManufacturingPage5, sizeof(MptConfigurationPageManufacturing5));
4262 pHlp->pfnSSMPutMem(pSSM, &pPages->ManufacturingPage6, sizeof(MptConfigurationPageManufacturing6));
4263 pHlp->pfnSSMPutMem(pSSM, &pPages->ManufacturingPage8, sizeof(MptConfigurationPageManufacturing8));
4264 pHlp->pfnSSMPutMem(pSSM, &pPages->ManufacturingPage9, sizeof(MptConfigurationPageManufacturing9));
4265 pHlp->pfnSSMPutMem(pSSM, &pPages->ManufacturingPage10, sizeof(MptConfigurationPageManufacturing10));
4266 pHlp->pfnSSMPutMem(pSSM, &pPages->IOUnitPage0, sizeof(MptConfigurationPageIOUnit0));
4267 pHlp->pfnSSMPutMem(pSSM, &pPages->IOUnitPage1, sizeof(MptConfigurationPageIOUnit1));
4268 pHlp->pfnSSMPutMem(pSSM, &pPages->IOUnitPage2, sizeof(MptConfigurationPageIOUnit2));
4269 pHlp->pfnSSMPutMem(pSSM, &pPages->IOUnitPage3, sizeof(MptConfigurationPageIOUnit3));
4270 pHlp->pfnSSMPutMem(pSSM, &pPages->IOUnitPage4, sizeof(MptConfigurationPageIOUnit4));
4271 pHlp->pfnSSMPutMem(pSSM, &pPages->IOCPage0, sizeof(MptConfigurationPageIOC0));
4272 pHlp->pfnSSMPutMem(pSSM, &pPages->IOCPage1, sizeof(MptConfigurationPageIOC1));
4273 pHlp->pfnSSMPutMem(pSSM, &pPages->IOCPage2, sizeof(MptConfigurationPageIOC2));
4274 pHlp->pfnSSMPutMem(pSSM, &pPages->IOCPage3, sizeof(MptConfigurationPageIOC3));
4275 pHlp->pfnSSMPutMem(pSSM, &pPages->IOCPage4, sizeof(MptConfigurationPageIOC4));
4276 pHlp->pfnSSMPutMem(pSSM, &pPages->IOCPage6, sizeof(MptConfigurationPageIOC6));
4277 pHlp->pfnSSMPutMem(pSSM, &pPages->BIOSPage1, sizeof(MptConfigurationPageBIOS1));
4278 pHlp->pfnSSMPutMem(pSSM, &pPages->BIOSPage2, sizeof(MptConfigurationPageBIOS2));
4279 pHlp->pfnSSMPutMem(pSSM, &pPages->BIOSPage4, sizeof(MptConfigurationPageBIOS4));
4280
4281 /* Device dependent pages */
4282 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
4283 {
4284 PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
4285
4286 pHlp->pfnSSMPutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage0, sizeof(MptConfigurationPageSCSISPIPort0));
4287 pHlp->pfnSSMPutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage1, sizeof(MptConfigurationPageSCSISPIPort1));
4288 pHlp->pfnSSMPutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage2, sizeof(MptConfigurationPageSCSISPIPort2));
4289
4290 for (unsigned i = 0; i < RT_ELEMENTS(pSpiPages->aBuses[0].aDevicePages); i++)
4291 {
4292 pHlp->pfnSSMPutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage0, sizeof(MptConfigurationPageSCSISPIDevice0));
4293 pHlp->pfnSSMPutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage1, sizeof(MptConfigurationPageSCSISPIDevice1));
4294 pHlp->pfnSSMPutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage2, sizeof(MptConfigurationPageSCSISPIDevice2));
4295 pHlp->pfnSSMPutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3, sizeof(MptConfigurationPageSCSISPIDevice3));
4296 }
4297 }
4298 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
4299 {
4300 PMptConfigurationPagesSas pSasPages = &pPages->u.SasPages;
4301
4302 pHlp->pfnSSMPutU32(pSSM, pSasPages->cbManufacturingPage7);
4303 pHlp->pfnSSMPutU32(pSSM, pSasPages->cbSASIOUnitPage0);
4304 pHlp->pfnSSMPutU32(pSSM, pSasPages->cbSASIOUnitPage1);
4305
4306 pHlp->pfnSSMPutMem(pSSM, pSasPages->pManufacturingPage7, pSasPages->cbManufacturingPage7);
4307 pHlp->pfnSSMPutMem(pSSM, pSasPages->pSASIOUnitPage0, pSasPages->cbSASIOUnitPage0);
4308 pHlp->pfnSSMPutMem(pSSM, pSasPages->pSASIOUnitPage1, pSasPages->cbSASIOUnitPage1);
4309
4310 pHlp->pfnSSMPutMem(pSSM, &pSasPages->SASIOUnitPage2, sizeof(MptConfigurationPageSASIOUnit2));
4311 pHlp->pfnSSMPutMem(pSSM, &pSasPages->SASIOUnitPage3, sizeof(MptConfigurationPageSASIOUnit3));
4312
4313 pHlp->pfnSSMPutU32(pSSM, pSasPages->cPHYs);
4314 for (unsigned i = 0; i < pSasPages->cPHYs; i++)
4315 {
4316 pHlp->pfnSSMPutMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage0, sizeof(MptConfigurationPageSASPHY0));
4317 pHlp->pfnSSMPutMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage1, sizeof(MptConfigurationPageSASPHY1));
4318 }
4319
4320 /* The number of devices first. */
4321 pHlp->pfnSSMPutU32(pSSM, pSasPages->cDevices);
4322
4323 for (PMptSASDevice pCurr = pSasPages->pSASDeviceHead; pCurr; pCurr = pCurr->pNext)
4324 {
4325 pHlp->pfnSSMPutMem(pSSM, &pCurr->SASDevicePage0, sizeof(MptConfigurationPageSASDevice0));
4326 pHlp->pfnSSMPutMem(pSSM, &pCurr->SASDevicePage1, sizeof(MptConfigurationPageSASDevice1));
4327 pHlp->pfnSSMPutMem(pSSM, &pCurr->SASDevicePage2, sizeof(MptConfigurationPageSASDevice2));
4328 }
4329 }
4330 else
4331 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
4332
4333 return pHlp->pfnSSMPutU32(pSSM, UINT32_MAX);
4334}
4335
4336/**
4337 * @callback_method_impl{FNSSMDEVLOADDONE}
4338 */
4339static DECLCALLBACK(int) lsilogicR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4340{
4341 RT_NOREF(pSSM);
4342 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4343
4344 lsilogicR3Kick(pDevIns, pThis);
4345 return VINF_SUCCESS;
4346}
4347
4348/**
4349 * @callback_method_impl{FNSSMDEVLOADEXEC}
4350 */
4351static DECLCALLBACK(int) lsilogicR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
4352{
4353 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4354 PLSILOGICSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC);
4355 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4356 int rc;
4357
4358 if ( uVersion > LSILOGIC_SAVED_STATE_VERSION
4359 || uVersion < LSILOGIC_SAVED_STATE_VERSION_VBOX_30)
4360 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4361
4362 /* device config */
4363 if (uVersion > LSILOGIC_SAVED_STATE_VERSION_PRE_SAS)
4364 {
4365 LSILOGICCTRLTYPE enmCtrlType;
4366 uint32_t cDeviceStates, cPorts;
4367
4368 PDMDEVHLP_SSM_GET_ENUM32_RET(pHlp, pSSM, enmCtrlType, LSILOGICCTRLTYPE);
4369 pHlp->pfnSSMGetU32(pSSM, &cDeviceStates);
4370 rc = pHlp->pfnSSMGetU32(pSSM, &cPorts);
4371 AssertRCReturn(rc, rc);
4372
4373 if (enmCtrlType != pThis->enmCtrlType)
4374 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Controller type): config=%d state=%d"),
4375 pThis->enmCtrlType, enmCtrlType);
4376 if (cDeviceStates != pThis->cDeviceStates)
4377 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Device states): config=%u state=%u"),
4378 pThis->cDeviceStates, cDeviceStates);
4379 if (cPorts != pThis->cPorts)
4380 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Ports): config=%u state=%u"),
4381 pThis->cPorts, cPorts);
4382 }
4383 if (uVersion > LSILOGIC_SAVED_STATE_VERSION_VBOX_30)
4384 {
4385 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
4386 {
4387 bool fPresent;
4388 rc = pHlp->pfnSSMGetBool(pSSM, &fPresent);
4389 AssertRCReturn(rc, rc);
4390 if (fPresent != (pThisCC->paDeviceStates[i].pDrvBase != NULL))
4391 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Target %u config mismatch: config=%RTbool state=%RTbool"),
4392 i, pThisCC->paDeviceStates[i].pDrvBase != NULL, fPresent);
4393 }
4394 }
4395 if (uPass != SSM_PASS_FINAL)
4396 return VINF_SUCCESS;
4397
4398 /* Every device first. */
4399 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
4400 {
4401 PLSILOGICDEVICE pDevice = &pThisCC->paDeviceStates[i];
4402
4403 AssertMsg(!pDevice->cOutstandingRequests,
4404 ("There are still outstanding requests on this device\n"));
4405 pHlp->pfnSSMGetU32V(pSSM, &pDevice->cOutstandingRequests);
4406 }
4407 /* Now the main device state. */
4408 PDMDEVHLP_SSM_GET_ENUM32_RET(pHlp, pSSM, pThis->enmState, LSILOGICSTATE);
4409 PDMDEVHLP_SSM_GET_ENUM32_RET(pHlp, pSSM, pThis->enmWhoInit, LSILOGICWHOINIT);
4410 if (uVersion <= LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL)
4411 {
4412 /*
4413 * The doorbell status flag distinguishes only between
4414 * doorbell not in use or a Function handshake is currently in progress.
4415 */
4416 bool fDoorbellInProgress = false;
4417 rc = pHlp->pfnSSMGetBool(pSSM, &fDoorbellInProgress);
4418 AssertRCReturn(rc, rc);
4419 if (fDoorbellInProgress)
4420 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_FN_HANDSHAKE;
4421 else
4422 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
4423 }
4424 else
4425 PDMDEVHLP_SSM_GET_ENUM32_RET(pHlp, pSSM, pThis->enmDoorbellState, LSILOGICDOORBELLSTATE);
4426 pHlp->pfnSSMGetBool(pSSM, &pThis->fDiagnosticEnabled);
4427 pHlp->pfnSSMGetBool(pSSM, &pThis->fNotificationSent);
4428 pHlp->pfnSSMGetBool(pSSM, &pThis->fEventNotificationEnabled);
4429 pHlp->pfnSSMGetU32V(pSSM, &pThis->uInterruptMask);
4430 pHlp->pfnSSMGetU32V(pSSM, &pThis->uInterruptStatus);
4431 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aMessage); i++)
4432 pHlp->pfnSSMGetU32(pSSM, &pThis->aMessage[i]);
4433 pHlp->pfnSSMGetU32(pSSM, &pThis->iMessage);
4434 pHlp->pfnSSMGetU32(pSSM, &pThis->cMessage);
4435 pHlp->pfnSSMGetMem(pSSM, &pThis->ReplyBuffer, sizeof(pThis->ReplyBuffer));
4436 pHlp->pfnSSMGetU32(pSSM, &pThis->uNextReplyEntryRead);
4437 pHlp->pfnSSMGetU32(pSSM, &pThis->cReplySize);
4438 pHlp->pfnSSMGetU16(pSSM, &pThis->u16IOCFaultCode);
4439 pHlp->pfnSSMGetU32(pSSM, &pThis->u32HostMFAHighAddr);
4440 pHlp->pfnSSMGetU32(pSSM, &pThis->u32SenseBufferHighAddr);
4441 pHlp->pfnSSMGetU8(pSSM, &pThis->cMaxDevices);
4442 pHlp->pfnSSMGetU8(pSSM, &pThis->cMaxBuses);
4443 pHlp->pfnSSMGetU16(pSSM, &pThis->cbReplyFrame);
4444 pHlp->pfnSSMGetU32(pSSM, &pThis->iDiagnosticAccess);
4445
4446 uint32_t cReplyQueueEntries, cRequestQueueEntries;
4447 pHlp->pfnSSMGetU32(pSSM, &cReplyQueueEntries);
4448 rc = pHlp->pfnSSMGetU32(pSSM, &cRequestQueueEntries);
4449 AssertRCReturn(rc, rc);
4450
4451 if ( cReplyQueueEntries != pThis->cReplyQueueEntries
4452 || cRequestQueueEntries != pThis->cRequestQueueEntries)
4453 {
4454 LogRel(("Changing queue sizes: cReplyQueueEntries=%u cRequestQueuEntries=%u\n", cReplyQueueEntries, cRequestQueueEntries));
4455 if ( cReplyQueueEntries > RT_ELEMENTS(pThis->aReplyFreeQueue)
4456 || cReplyQueueEntries < LSILOGICSCSI_REQUEST_QUEUE_DEPTH_MIN
4457 || cRequestQueueEntries > RT_ELEMENTS(pThis->aRequestQueue)
4458 || cRequestQueueEntries < LSILOGICSCSI_REPLY_QUEUE_DEPTH_MIN)
4459 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Out of bounds: cReplyQueueEntries=%u cRequestQueueEntries=%u"),
4460 cReplyQueueEntries, cRequestQueueEntries);
4461 pThis->cReplyQueueEntries = cReplyQueueEntries;
4462 pThis->cRequestQueueEntries = cRequestQueueEntries;
4463 }
4464
4465 pHlp->pfnSSMGetU32V(pSSM, &pThis->uReplyFreeQueueNextEntryFreeWrite);
4466 pHlp->pfnSSMGetU32V(pSSM, &pThis->uReplyFreeQueueNextAddressRead);
4467 pHlp->pfnSSMGetU32V(pSSM, &pThis->uReplyPostQueueNextEntryFreeWrite);
4468 pHlp->pfnSSMGetU32V(pSSM, &pThis->uReplyPostQueueNextAddressRead);
4469 pHlp->pfnSSMGetU32V(pSSM, &pThis->uRequestQueueNextEntryFreeWrite);
4470 pHlp->pfnSSMGetU32V(pSSM, &pThis->uRequestQueueNextAddressRead);
4471
4472 PMptConfigurationPagesSupported pPages = pThisCC->pConfigurationPages;
4473
4474 if (uVersion <= LSILOGIC_SAVED_STATE_VERSION_PRE_SAS)
4475 {
4476 PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
4477 MptConfigurationPagesSupported_SSM_V2 ConfigPagesV2;
4478
4479 if (pThis->enmCtrlType != LSILOGICCTRLTYPE_SCSI_SPI)
4480 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch: Expected SPI SCSI controller"));
4481
4482 pHlp->pfnSSMGetMem(pSSM, &ConfigPagesV2, sizeof(MptConfigurationPagesSupported_SSM_V2));
4483
4484 pPages->ManufacturingPage0 = ConfigPagesV2.ManufacturingPage0;
4485 pPages->ManufacturingPage1 = ConfigPagesV2.ManufacturingPage1;
4486 pPages->ManufacturingPage2 = ConfigPagesV2.ManufacturingPage2;
4487 pPages->ManufacturingPage3 = ConfigPagesV2.ManufacturingPage3;
4488 pPages->ManufacturingPage4 = ConfigPagesV2.ManufacturingPage4;
4489 pPages->IOUnitPage0 = ConfigPagesV2.IOUnitPage0;
4490 pPages->IOUnitPage1 = ConfigPagesV2.IOUnitPage1;
4491 pPages->IOUnitPage2 = ConfigPagesV2.IOUnitPage2;
4492 pPages->IOUnitPage3 = ConfigPagesV2.IOUnitPage3;
4493 pPages->IOCPage0 = ConfigPagesV2.IOCPage0;
4494 pPages->IOCPage1 = ConfigPagesV2.IOCPage1;
4495 pPages->IOCPage2 = ConfigPagesV2.IOCPage2;
4496 pPages->IOCPage3 = ConfigPagesV2.IOCPage3;
4497 pPages->IOCPage4 = ConfigPagesV2.IOCPage4;
4498 pPages->IOCPage6 = ConfigPagesV2.IOCPage6;
4499
4500 pSpiPages->aPortPages[0].SCSISPIPortPage0 = ConfigPagesV2.aPortPages[0].SCSISPIPortPage0;
4501 pSpiPages->aPortPages[0].SCSISPIPortPage1 = ConfigPagesV2.aPortPages[0].SCSISPIPortPage1;
4502 pSpiPages->aPortPages[0].SCSISPIPortPage2 = ConfigPagesV2.aPortPages[0].SCSISPIPortPage2;
4503
4504 for (unsigned i = 0; i < RT_ELEMENTS(pPages->u.SpiPages.aBuses[0].aDevicePages); i++)
4505 {
4506 pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage0 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage0;
4507 pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage1 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage1;
4508 pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage2 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage2;
4509 pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage3;
4510 }
4511 }
4512 else
4513 {
4514 /* Queue content */
4515 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
4516 pHlp->pfnSSMGetU32V(pSSM, &pThis->aReplyFreeQueue[i]);
4517 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
4518 pHlp->pfnSSMGetU32V(pSSM, &pThis->aReplyPostQueue[i]);
4519 for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
4520 pHlp->pfnSSMGetU32V(pSSM, &pThis->aRequestQueue[i]);
4521
4522 pHlp->pfnSSMGetU16(pSSM, &pThis->u16NextHandle);
4523
4524 if (uVersion > LSILOGIC_SAVED_STATE_VERSION_PRE_DIAG_MEM)
4525 {
4526
4527 /* Save diagnostic memory register and data regions. */
4528 pHlp->pfnSSMGetU32(pSSM, &pThis->u32DiagMemAddr);
4529 uint32_t cMemRegions = 0;
4530 rc = pHlp->pfnSSMGetU32(pSSM, &cMemRegions);
4531 AssertLogRelRCReturn(rc, rc);
4532
4533 while (cMemRegions)
4534 {
4535 uint32_t u32AddrStart = 0;
4536 pHlp->pfnSSMGetU32(pSSM, &u32AddrStart);
4537 uint32_t u32AddrEnd = 0;
4538 rc = pHlp->pfnSSMGetU32(pSSM, &u32AddrEnd);
4539 AssertLogRelRCReturn(rc, rc);
4540
4541 uint32_t cRegion = u32AddrEnd - u32AddrStart + 1;
4542 PLSILOGICMEMREGN pRegion = (PLSILOGICMEMREGN)RTMemAllocZ(RT_UOFFSETOF_DYN(LSILOGICMEMREGN, au32Data[cRegion]));
4543 if (pRegion)
4544 {
4545 pRegion->u32AddrStart = u32AddrStart;
4546 pRegion->u32AddrEnd = u32AddrEnd;
4547 pHlp->pfnSSMGetMem(pSSM, &pRegion->au32Data[0], cRegion * sizeof(uint32_t));
4548 lsilogicR3MemRegionInsert(pThisCC, pRegion);
4549 pThisCC->cbMemRegns += cRegion * sizeof(uint32_t);
4550 }
4551 else
4552 {
4553 /* Leave a log message but continue. */
4554 LogRel(("LsiLogic: Out of memory while restoring the state, might not work as expected\n"));
4555 pHlp->pfnSSMSkip(pSSM, cRegion * sizeof(uint32_t));
4556 }
4557 cMemRegions--;
4558 }
4559 }
4560
4561 /* Configuration pages */
4562 pHlp->pfnSSMGetMem(pSSM, &pPages->ManufacturingPage0, sizeof(MptConfigurationPageManufacturing0));
4563 pHlp->pfnSSMGetMem(pSSM, &pPages->ManufacturingPage1, sizeof(MptConfigurationPageManufacturing1));
4564 pHlp->pfnSSMGetMem(pSSM, &pPages->ManufacturingPage2, sizeof(MptConfigurationPageManufacturing2));
4565 pHlp->pfnSSMGetMem(pSSM, &pPages->ManufacturingPage3, sizeof(MptConfigurationPageManufacturing3));
4566 pHlp->pfnSSMGetMem(pSSM, &pPages->ManufacturingPage4, sizeof(MptConfigurationPageManufacturing4));
4567 pHlp->pfnSSMGetMem(pSSM, &pPages->ManufacturingPage5, sizeof(MptConfigurationPageManufacturing5));
4568 pHlp->pfnSSMGetMem(pSSM, &pPages->ManufacturingPage6, sizeof(MptConfigurationPageManufacturing6));
4569 pHlp->pfnSSMGetMem(pSSM, &pPages->ManufacturingPage8, sizeof(MptConfigurationPageManufacturing8));
4570 pHlp->pfnSSMGetMem(pSSM, &pPages->ManufacturingPage9, sizeof(MptConfigurationPageManufacturing9));
4571 pHlp->pfnSSMGetMem(pSSM, &pPages->ManufacturingPage10, sizeof(MptConfigurationPageManufacturing10));
4572 pHlp->pfnSSMGetMem(pSSM, &pPages->IOUnitPage0, sizeof(MptConfigurationPageIOUnit0));
4573 pHlp->pfnSSMGetMem(pSSM, &pPages->IOUnitPage1, sizeof(MptConfigurationPageIOUnit1));
4574 pHlp->pfnSSMGetMem(pSSM, &pPages->IOUnitPage2, sizeof(MptConfigurationPageIOUnit2));
4575 pHlp->pfnSSMGetMem(pSSM, &pPages->IOUnitPage3, sizeof(MptConfigurationPageIOUnit3));
4576 pHlp->pfnSSMGetMem(pSSM, &pPages->IOUnitPage4, sizeof(MptConfigurationPageIOUnit4));
4577 pHlp->pfnSSMGetMem(pSSM, &pPages->IOCPage0, sizeof(MptConfigurationPageIOC0));
4578 pHlp->pfnSSMGetMem(pSSM, &pPages->IOCPage1, sizeof(MptConfigurationPageIOC1));
4579 pHlp->pfnSSMGetMem(pSSM, &pPages->IOCPage2, sizeof(MptConfigurationPageIOC2));
4580 pHlp->pfnSSMGetMem(pSSM, &pPages->IOCPage3, sizeof(MptConfigurationPageIOC3));
4581 pHlp->pfnSSMGetMem(pSSM, &pPages->IOCPage4, sizeof(MptConfigurationPageIOC4));
4582 pHlp->pfnSSMGetMem(pSSM, &pPages->IOCPage6, sizeof(MptConfigurationPageIOC6));
4583 pHlp->pfnSSMGetMem(pSSM, &pPages->BIOSPage1, sizeof(MptConfigurationPageBIOS1));
4584 pHlp->pfnSSMGetMem(pSSM, &pPages->BIOSPage2, sizeof(MptConfigurationPageBIOS2));
4585 pHlp->pfnSSMGetMem(pSSM, &pPages->BIOSPage4, sizeof(MptConfigurationPageBIOS4));
4586
4587 /* Device dependent pages */
4588 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
4589 {
4590 PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
4591
4592 pHlp->pfnSSMGetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage0, sizeof(MptConfigurationPageSCSISPIPort0));
4593 pHlp->pfnSSMGetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage1, sizeof(MptConfigurationPageSCSISPIPort1));
4594 pHlp->pfnSSMGetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage2, sizeof(MptConfigurationPageSCSISPIPort2));
4595
4596 for (unsigned i = 0; i < RT_ELEMENTS(pSpiPages->aBuses[0].aDevicePages); i++)
4597 {
4598 pHlp->pfnSSMGetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage0, sizeof(MptConfigurationPageSCSISPIDevice0));
4599 pHlp->pfnSSMGetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage1, sizeof(MptConfigurationPageSCSISPIDevice1));
4600 pHlp->pfnSSMGetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage2, sizeof(MptConfigurationPageSCSISPIDevice2));
4601 pHlp->pfnSSMGetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3, sizeof(MptConfigurationPageSCSISPIDevice3));
4602 }
4603 }
4604 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
4605 {
4606 uint32_t cbPage0, cbPage1, cPHYs, cbManufacturingPage7;
4607 PMptConfigurationPagesSas pSasPages = &pPages->u.SasPages;
4608
4609 pHlp->pfnSSMGetU32(pSSM, &cbManufacturingPage7);
4610 pHlp->pfnSSMGetU32(pSSM, &cbPage0);
4611 rc = pHlp->pfnSSMGetU32(pSSM, &cbPage1);
4612 AssertRCReturn(rc, rc);
4613
4614 if ( (cbPage0 != pSasPages->cbSASIOUnitPage0)
4615 || (cbPage1 != pSasPages->cbSASIOUnitPage1)
4616 || (cbManufacturingPage7 != pSasPages->cbManufacturingPage7))
4617 return VERR_SSM_LOAD_CONFIG_MISMATCH;
4618
4619 AssertPtr(pSasPages->pManufacturingPage7);
4620 AssertPtr(pSasPages->pSASIOUnitPage0);
4621 AssertPtr(pSasPages->pSASIOUnitPage1);
4622
4623 pHlp->pfnSSMGetMem(pSSM, pSasPages->pManufacturingPage7, pSasPages->cbManufacturingPage7);
4624 pHlp->pfnSSMGetMem(pSSM, pSasPages->pSASIOUnitPage0, pSasPages->cbSASIOUnitPage0);
4625 pHlp->pfnSSMGetMem(pSSM, pSasPages->pSASIOUnitPage1, pSasPages->cbSASIOUnitPage1);
4626
4627 pHlp->pfnSSMGetMem(pSSM, &pSasPages->SASIOUnitPage2, sizeof(MptConfigurationPageSASIOUnit2));
4628 pHlp->pfnSSMGetMem(pSSM, &pSasPages->SASIOUnitPage3, sizeof(MptConfigurationPageSASIOUnit3));
4629
4630 rc = pHlp->pfnSSMGetU32(pSSM, &cPHYs);
4631 AssertRCReturn(rc, rc);
4632 if (cPHYs != pSasPages->cPHYs)
4633 return VERR_SSM_LOAD_CONFIG_MISMATCH;
4634
4635 AssertPtr(pSasPages->paPHYs);
4636 for (unsigned i = 0; i < pSasPages->cPHYs; i++)
4637 {
4638 pHlp->pfnSSMGetMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage0, sizeof(MptConfigurationPageSASPHY0));
4639 pHlp->pfnSSMGetMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage1, sizeof(MptConfigurationPageSASPHY1));
4640 }
4641
4642 /* The number of devices first. */
4643 rc = pHlp->pfnSSMGetU32(pSSM, &pSasPages->cDevices);
4644 AssertRCReturn(rc, rc);
4645
4646 PMptSASDevice pCurr = pSasPages->pSASDeviceHead;
4647
4648 for (unsigned i = 0; i < pSasPages->cDevices; i++)
4649 {
4650 AssertReturn(pCurr, VERR_SSM_LOAD_CONFIG_MISMATCH);
4651
4652 pHlp->pfnSSMGetMem(pSSM, &pCurr->SASDevicePage0, sizeof(MptConfigurationPageSASDevice0));
4653 pHlp->pfnSSMGetMem(pSSM, &pCurr->SASDevicePage1, sizeof(MptConfigurationPageSASDevice1));
4654 rc = pHlp->pfnSSMGetMem(pSSM, &pCurr->SASDevicePage2, sizeof(MptConfigurationPageSASDevice2));
4655 AssertRCReturn(rc, rc);
4656
4657 pCurr = pCurr->pNext;
4658 }
4659
4660 Assert(!pCurr);
4661 }
4662 else
4663 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
4664 }
4665
4666 if (uVersion <= LSILOGIC_SAVED_STATE_VERSION_PRE_VBOXSCSI_REMOVAL)
4667 vboxscsiR3LoadExecLegacy(pHlp, pSSM);
4668
4669 uint32_t u32;
4670 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
4671 if (RT_FAILURE(rc))
4672 return rc;
4673 AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
4674
4675 return VINF_SUCCESS;
4676}
4677
4678
4679/*
4680 * The device level IBASE and LED interfaces.
4681 */
4682
4683/**
4684 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed, For a SCSI device.}
4685 *
4686 * @remarks Called by the scsi driver, proxying the main calls.
4687 */
4688static DECLCALLBACK(int) lsilogicR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4689{
4690 PLSILOGICDEVICE pDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, ILed);
4691 if (iLUN == 0)
4692 {
4693 *ppLed = &pDevice->Led;
4694 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
4695 return VINF_SUCCESS;
4696 }
4697 return VERR_PDM_LUN_NOT_FOUND;
4698}
4699
4700
4701/**
4702 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4703 */
4704static DECLCALLBACK(void *) lsilogicR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4705{
4706 PLSILOGICDEVICE pDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IBase);
4707
4708 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevice->IBase);
4709 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pDevice->IMediaPort);
4710 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pDevice->IMediaExPort);
4711 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pDevice->ILed);
4712 return NULL;
4713}
4714
4715
4716/*
4717 * The controller level IBASE and LED interfaces.
4718 */
4719
4720/**
4721 * Gets the pointer to the status LED of a unit.
4722 *
4723 * @returns VBox status code.
4724 * @param pInterface Pointer to the interface structure containing the called function pointer.
4725 * @param iLUN The unit which status LED we desire.
4726 * @param ppLed Where to store the LED pointer.
4727 */
4728static DECLCALLBACK(int) lsilogicR3StatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4729{
4730 PLSILOGICSCSICC pThisCC = RT_FROM_MEMBER(pInterface, LSILOGICSCSICC, ILeds);
4731 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PLSILOGICSCSI);
4732 if (iLUN < pThis->cDeviceStates)
4733 {
4734 *ppLed = &pThisCC->paDeviceStates[iLUN].Led;
4735 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
4736 return VINF_SUCCESS;
4737 }
4738 return VERR_PDM_LUN_NOT_FOUND;
4739}
4740
4741/**
4742 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4743 */
4744static DECLCALLBACK(void *) lsilogicR3StatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4745{
4746 PLSILOGICSCSICC pThisCC = RT_FROM_MEMBER(pInterface, LSILOGICSCSICC, IBase);
4747 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
4748 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
4749 return NULL;
4750}
4751
4752
4753/*
4754 * The PDM device interface and some helpers.
4755 */
4756
4757/**
4758 * Checks if all asynchronous I/O is finished.
4759 *
4760 * Used by lsilogicR3Reset, lsilogicR3Suspend and lsilogicR3PowerOff.
4761 *
4762 * @returns true if quiesced, false if busy.
4763 * @param pDevIns The device instance.
4764 */
4765static bool lsilogicR3AllAsyncIOIsFinished(PPDMDEVINS pDevIns)
4766{
4767 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4768 PLSILOGICSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC);
4769
4770 for (uint32_t i = 0; i < pThis->cDeviceStates; i++)
4771 {
4772 PLSILOGICDEVICE pThisDevice = &pThisCC->paDeviceStates[i];
4773 if (pThisDevice->pDrvBase)
4774 {
4775 if (pThisDevice->cOutstandingRequests != 0)
4776 return false;
4777 }
4778 }
4779
4780 return true;
4781}
4782
4783/**
4784 * @callback_method_impl{FNPDMDEVASYNCNOTIFY,
4785 * Callback employed by lsilogicR3Suspend and lsilogicR3PowerOff.}
4786 */
4787static DECLCALLBACK(bool) lsilogicR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns)
4788{
4789 if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
4790 return false;
4791
4792 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4793 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
4794 return true;
4795}
4796
4797/**
4798 * Common worker for ahciR3Suspend and ahciR3PowerOff.
4799 */
4800static void lsilogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns)
4801{
4802 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4803 PLSILOGICSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC);
4804
4805 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
4806 if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
4807 PDMDevHlpSetAsyncNotification(pDevIns, lsilogicR3IsAsyncSuspendOrPowerOffDone);
4808 else
4809 {
4810 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
4811
4812 AssertMsg(!pThis->fNotificationSent, ("The PDM Queue should be empty at this point\n"));
4813 }
4814
4815 for (uint32_t i = 0; i < pThis->cDeviceStates; i++)
4816 {
4817 PLSILOGICDEVICE pThisDevice = &pThisCC->paDeviceStates[i];
4818 if (pThisDevice->pDrvMediaEx)
4819 pThisDevice->pDrvMediaEx->pfnNotifySuspend(pThisDevice->pDrvMediaEx);
4820 }
4821}
4822
4823/**
4824 * @interface_method_impl{PDMDEVREG,pfnSuspend}
4825 */
4826static DECLCALLBACK(void) lsilogicR3Suspend(PPDMDEVINS pDevIns)
4827{
4828 Log(("lsilogicR3Suspend\n"));
4829 lsilogicR3SuspendOrPowerOff(pDevIns);
4830}
4831
4832/**
4833 * @interface_method_impl{PDMDEVREG,pfnResume}
4834 */
4835static DECLCALLBACK(void) lsilogicR3Resume(PPDMDEVINS pDevIns)
4836{
4837 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4838
4839 Log(("lsilogicR3Resume\n"));
4840
4841 lsilogicR3Kick(pDevIns, pThis);
4842}
4843
4844/**
4845 * @interface_method_impl{PDMDEVREG,pfnDetach}
4846 *
4847 * One harddisk at one port has been unplugged.
4848 * The VM is suspended at this point.
4849 */
4850static DECLCALLBACK(void) lsilogicR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4851{
4852 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4853 PLSILOGICSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC);
4854 Log(("%s: iLUN=%#x\n", __FUNCTION__, iLUN));
4855 RT_NOREF(fFlags);
4856
4857 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, ("LsiLogic: Device does not support hotplugging\n"));
4858
4859 if (iLUN >= pThis->cDeviceStates)
4860 return;
4861
4862 /*
4863 * Zero some important members.
4864 */
4865 PLSILOGICDEVICE pDevice = &pThisCC->paDeviceStates[iLUN];
4866 pDevice->pDrvBase = NULL;
4867 pDevice->pDrvMedia = NULL;
4868 pDevice->pDrvMediaEx = NULL;
4869}
4870
4871/**
4872 * @interface_method_impl{PDMDEVREG,pfnAttach}
4873 */
4874static DECLCALLBACK(int) lsilogicR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4875{
4876 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4877 PLSILOGICSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC);
4878 PLSILOGICDEVICE pDevice = &pThisCC->paDeviceStates[iLUN];
4879 int rc;
4880
4881 if (iLUN >= pThis->cDeviceStates)
4882 return VERR_PDM_LUN_NOT_FOUND;
4883
4884 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
4885 ("LsiLogic: Device does not support hotplugging\n"),
4886 VERR_INVALID_PARAMETER);
4887
4888 /* the usual paranoia */
4889 AssertRelease(!pDevice->pDrvBase);
4890 AssertRelease(!pDevice->pDrvMedia);
4891 AssertRelease(!pDevice->pDrvMediaEx);
4892 Assert(pDevice->iLUN == iLUN);
4893
4894 /*
4895 * Try attach the block device and get the interfaces,
4896 * required as well as optional.
4897 */
4898 rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, NULL);
4899 if (RT_SUCCESS(rc))
4900 {
4901 /* Query the media interface. */
4902 pDevice->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMIMEDIA);
4903 AssertMsgReturn(RT_VALID_PTR(pDevice->pDrvMedia),
4904 ("LsiLogic configuration error: LUN#%d misses the basic media interface!\n", pDevice->iLUN),
4905 VERR_PDM_MISSING_INTERFACE);
4906
4907 /* Get the extended media interface. */
4908 pDevice->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMIMEDIAEX);
4909 AssertMsgReturn(RT_VALID_PTR(pDevice->pDrvMediaEx),
4910 ("LsiLogic configuration error: LUN#%d misses the extended media interface!\n", pDevice->iLUN),
4911 VERR_PDM_MISSING_INTERFACE);
4912
4913 rc = pDevice->pDrvMediaEx->pfnIoReqAllocSizeSet(pDevice->pDrvMediaEx, sizeof(LSILOGICREQ));
4914 AssertMsgRCReturn(rc, ("LsiLogic configuration error: LUN#%u: Failed to set I/O request size!", pDevice->iLUN),
4915 rc);
4916 }
4917 else
4918 AssertMsgFailed(("Failed to attach LUN#%d. rc=%Rrc\n", pDevice->iLUN, rc));
4919
4920 if (RT_FAILURE(rc))
4921 {
4922 pDevice->pDrvBase = NULL;
4923 pDevice->pDrvMedia = NULL;
4924 pDevice->pDrvMediaEx = NULL;
4925 }
4926 return rc;
4927}
4928
4929/**
4930 * Common reset worker.
4931 *
4932 * @param pDevIns The device instance data.
4933 */
4934static void lsilogicR3ResetCommon(PPDMDEVINS pDevIns)
4935{
4936 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4937 PLSILOGICSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC);
4938 int rc;
4939
4940 rc = lsilogicR3HardReset(pDevIns, pThis, pThisCC);
4941 AssertRC(rc);
4942}
4943
4944/**
4945 * @callback_method_impl{FNPDMDEVASYNCNOTIFY,
4946 * Callback employed by lsilogicR3Reset.}
4947 */
4948static DECLCALLBACK(bool) lsilogicR3IsAsyncResetDone(PPDMDEVINS pDevIns)
4949{
4950 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4951
4952 if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
4953 return false;
4954 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
4955
4956 lsilogicR3ResetCommon(pDevIns);
4957 return true;
4958}
4959
4960/**
4961 * @interface_method_impl{PDMDEVREG,pfnReset}
4962 */
4963static DECLCALLBACK(void) lsilogicR3Reset(PPDMDEVINS pDevIns)
4964{
4965 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4966
4967 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
4968 if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
4969 PDMDevHlpSetAsyncNotification(pDevIns, lsilogicR3IsAsyncResetDone);
4970 else
4971 {
4972 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
4973 lsilogicR3ResetCommon(pDevIns);
4974 }
4975}
4976
4977/**
4978 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
4979 */
4980static DECLCALLBACK(void) lsilogicR3PowerOff(PPDMDEVINS pDevIns)
4981{
4982 Log(("lsilogicR3PowerOff\n"));
4983 lsilogicR3SuspendOrPowerOff(pDevIns);
4984}
4985
4986/**
4987 * @interface_method_impl{PDMDEVREG,pfnDestruct}
4988 */
4989static DECLCALLBACK(int) lsilogicR3Destruct(PPDMDEVINS pDevIns)
4990{
4991 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
4992 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
4993 PLSILOGICSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC);
4994
4995 PDMDevHlpCritSectDelete(pDevIns, &pThis->ReplyFreeQueueCritSect);
4996 PDMDevHlpCritSectDelete(pDevIns, &pThis->ReplyPostQueueCritSect);
4997 PDMDevHlpCritSectDelete(pDevIns, &pThis->RequestQueueCritSect);
4998 PDMDevHlpCritSectDelete(pDevIns, &pThis->ReplyFreeQueueWriteCritSect);
4999
5000 if (RTCritSectIsInitialized(&pThisCC->CritSectMemRegns))
5001 RTCritSectDelete(&pThisCC->CritSectMemRegns);
5002
5003 RTMemFree(pThisCC->paDeviceStates);
5004 pThisCC->paDeviceStates = NULL;
5005
5006 if (pThis->hEvtProcess != NIL_SUPSEMEVENT)
5007 {
5008 PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEvtProcess);
5009 pThis->hEvtProcess = NIL_SUPSEMEVENT;
5010 }
5011
5012 lsilogicR3ConfigurationPagesFree(pThis, pThisCC);
5013 lsilogicR3MemRegionsFree(pThisCC);
5014 return VINF_SUCCESS;
5015}
5016
5017/**
5018 * @interface_method_impl{PDMDEVREG,pfnConstruct}
5019 */
5020static DECLCALLBACK(int) lsilogicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5021{
5022 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5023 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
5024 PLSILOGICSCSICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PLSILOGICSCSICC);
5025 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5026 int rc = VINF_SUCCESS;
5027
5028 /*
5029 * Initialize enought of the state to make the destructure not trip up.
5030 */
5031 pThis->hEvtProcess = NIL_SUPSEMEVENT;
5032 RTListInit(&pThisCC->ListMemRegns);
5033 pThis->hMmioReg = NIL_IOMMMIOHANDLE;
5034 pThis->hMmioDiag = NIL_IOMMMIOHANDLE;
5035 pThis->hIoPortsReg = NIL_IOMIOPORTHANDLE;
5036 pThis->hIoPortsBios = NIL_IOMIOPORTHANDLE;
5037 pThisCC->pDevIns = pDevIns;
5038 pThisCC->IBase.pfnQueryInterface = lsilogicR3StatusQueryInterface;
5039 pThisCC->ILeds.pfnQueryStatusLed = lsilogicR3StatusQueryStatusLed;
5040
5041
5042 /*
5043 * Validate and read configuration.
5044 */
5045 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns,
5046 "ReplyQueueDepth|"
5047 "RequestQueueDepth|"
5048 "ControllerType|"
5049 "NumPorts|"
5050 "Bootable", /* Keep it for legacy configs, even though it doesn't do anything anymore, see @bugref{4841}. */
5051 "");
5052
5053 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "ReplyQueueDepth",
5054 &pThis->cReplyQueueEntries, LSILOGICSCSI_REPLY_QUEUE_DEPTH_DEFAULT);
5055 if (RT_FAILURE(rc))
5056 return PDMDEV_SET_ERROR(pDevIns, rc,
5057 N_("LsiLogic configuration error: failed to read ReplyQueue as integer"));
5058 if ( pThis->cReplyQueueEntries < LSILOGICSCSI_REPLY_QUEUE_DEPTH_MIN
5059 || pThis->cReplyQueueEntries > LSILOGICSCSI_REPLY_QUEUE_DEPTH_MAX - 1 /* see +1 later in the function */)
5060 return PDMDevHlpVMSetError(pDevIns, VERR_OUT_OF_RANGE, RT_SRC_POS,
5061 N_("LsiLogic configuration error: 'ReplyQueueDepth' = %u is out of ranage (%u..%u)"),
5062 pThis->cReplyQueueEntries, LSILOGICSCSI_REPLY_QUEUE_DEPTH_MIN,
5063 LSILOGICSCSI_REPLY_QUEUE_DEPTH_MAX - 1);
5064 Log(("%s: ReplyQueueDepth=%u\n", __FUNCTION__, pThis->cReplyQueueEntries));
5065
5066 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "RequestQueueDepth",
5067 &pThis->cRequestQueueEntries, LSILOGICSCSI_REQUEST_QUEUE_DEPTH_DEFAULT);
5068 if (RT_FAILURE(rc))
5069 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic configuration error: failed to read RequestQueue as integer"));
5070 if ( pThis->cRequestQueueEntries < LSILOGICSCSI_REQUEST_QUEUE_DEPTH_MIN
5071 || pThis->cRequestQueueEntries > LSILOGICSCSI_REQUEST_QUEUE_DEPTH_MAX - 1 /* see +1 later in the function */)
5072 return PDMDevHlpVMSetError(pDevIns, VERR_OUT_OF_RANGE, RT_SRC_POS,
5073 N_("LsiLogic configuration error: 'RequestQueue' = %u is out of ranage (%u..%u)"),
5074 pThis->cRequestQueueEntries, LSILOGICSCSI_REQUEST_QUEUE_DEPTH_MIN,
5075 LSILOGICSCSI_REQUEST_QUEUE_DEPTH_MIN - 1);
5076 Log(("%s: RequestQueueDepth=%u\n", __FUNCTION__, pThis->cRequestQueueEntries));
5077
5078 char szCtrlType[64];
5079 rc = pHlp->pfnCFGMQueryStringDef(pCfg, "ControllerType", szCtrlType, sizeof(szCtrlType), LSILOGICSCSI_PCI_SPI_CTRLNAME);
5080 if (RT_FAILURE(rc))
5081 return PDMDEV_SET_ERROR(pDevIns, rc,
5082 N_("LsiLogic configuration error: failed to read ControllerType as string"));
5083 Log(("%s: ControllerType=%s\n", __FUNCTION__, szCtrlType));
5084 rc = lsilogicR3GetCtrlTypeFromString(pThis, szCtrlType);
5085 if (RT_FAILURE(rc))
5086 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic configuration error: failed to determine controller type from string"));
5087
5088 char szDevTag[20];
5089 RTStrPrintf(szDevTag, sizeof(szDevTag), "LSILOGIC%s-%u",
5090 pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI ? "SPI" : "SAS",
5091 iInstance);
5092
5093 rc = pHlp->pfnCFGMQueryU8(pCfg, "NumPorts", &pThis->cPorts);
5094 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
5095 {
5096 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
5097 pThis->cPorts = LSILOGICSCSI_PCI_SPI_PORTS_MAX;
5098 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
5099 pThis->cPorts = LSILOGICSCSI_PCI_SAS_PORTS_DEFAULT;
5100 else
5101 AssertMsgFailed(("Invalid controller type: %d\n", pThis->enmCtrlType));
5102 }
5103 else if (RT_FAILURE(rc))
5104 return PDMDEV_SET_ERROR(pDevIns, rc,
5105 N_("LsiLogic configuration error: failed to read NumPorts as integer"));
5106
5107 /* Init static parts. */
5108 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
5109 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
5110
5111 PDMPciDevSetVendorId(pPciDev, LSILOGICSCSI_PCI_VENDOR_ID); /* LsiLogic */
5112 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
5113 {
5114 PDMPciDevSetDeviceId(pPciDev, LSILOGICSCSI_PCI_SPI_DEVICE_ID); /* LSI53C1030 */
5115 PDMPciDevSetSubSystemVendorId(pPciDev, LSILOGICSCSI_PCI_SPI_SUBSYSTEM_VENDOR_ID);
5116 PDMPciDevSetSubSystemId(pPciDev, LSILOGICSCSI_PCI_SPI_SUBSYSTEM_ID);
5117 }
5118 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
5119 {
5120 PDMPciDevSetDeviceId(pPciDev, LSILOGICSCSI_PCI_SAS_DEVICE_ID); /* SAS1068 */
5121 PDMPciDevSetSubSystemVendorId(pPciDev, LSILOGICSCSI_PCI_SAS_SUBSYSTEM_VENDOR_ID);
5122 PDMPciDevSetSubSystemId(pPciDev, LSILOGICSCSI_PCI_SAS_SUBSYSTEM_ID);
5123 }
5124 else
5125 AssertMsgFailed(("Invalid controller type: %d\n", pThis->enmCtrlType));
5126
5127 PDMPciDevSetClassProg(pPciDev, 0x00); /* SCSI */
5128 PDMPciDevSetClassSub(pPciDev, 0x00); /* SCSI */
5129 PDMPciDevSetClassBase(pPciDev, 0x01); /* Mass storage */
5130 PDMPciDevSetInterruptPin(pPciDev, 0x01); /* Interrupt pin A */
5131
5132# ifdef VBOX_WITH_MSI_DEVICES
5133 PDMPciDevSetStatus(pPciDev, VBOX_PCI_STATUS_CAP_LIST);
5134 PDMPciDevSetCapabilityList(pPciDev, 0x80);
5135# endif
5136
5137 /*
5138 * Create critical sections protecting the reply post and free queues.
5139 */
5140 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyFreeQueueCritSect, RT_SRC_POS, "%sRFQ", szDevTag);
5141 if (RT_FAILURE(rc))
5142 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply free queue"));
5143
5144 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyPostQueueCritSect, RT_SRC_POS, "%sRPQ", szDevTag);
5145 if (RT_FAILURE(rc))
5146 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply post queue"));
5147
5148 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->RequestQueueCritSect, RT_SRC_POS, "%sRQ", szDevTag);
5149 if (RT_FAILURE(rc))
5150 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for request queue"));
5151
5152 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyFreeQueueWriteCritSect, RT_SRC_POS, "%sRFQW", szDevTag);
5153 if (RT_FAILURE(rc))
5154 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply free queue write access"));
5155
5156 /*
5157 * Critical section protecting the memory regions.
5158 */
5159 rc = RTCritSectInit(&pThisCC->CritSectMemRegns);
5160 if (RT_FAILURE(rc))
5161 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: Failed to initialize critical section protecting the memory regions"));
5162
5163 /*
5164 * Register the PCI device, it's I/O regions.
5165 */
5166 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
5167 if (RT_FAILURE(rc))
5168 return rc;
5169
5170# ifdef VBOX_WITH_MSI_DEVICES
5171 PDMMSIREG MsiReg;
5172 RT_ZERO(MsiReg);
5173 /* use this code for MSI-X support */
5174# if 0
5175 MsiReg.cMsixVectors = 1;
5176 MsiReg.iMsixCapOffset = 0x80;
5177 MsiReg.iMsixNextOffset = 0x00;
5178 MsiReg.iMsixBar = 3;
5179 Assert(pDevIns->pReg->cMaxMsixVectors >= MsiReg.cMsixVectors); /* fix device registration when enabling this */
5180# else
5181 MsiReg.cMsiVectors = 1;
5182 MsiReg.iMsiCapOffset = 0x80;
5183 MsiReg.iMsiNextOffset = 0x00;
5184# endif
5185 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
5186 if (RT_FAILURE (rc))
5187 {
5188 /* That's OK, we can work without MSI */
5189 PDMPciDevSetCapabilityList(pPciDev, 0x0);
5190 }
5191# endif
5192
5193 /* Region #0: I/O ports. */
5194 rc = PDMDevHlpPCIIORegionCreateIo(pDevIns, 0 /*iPciRegion*/, LSILOGIC_PCI_SPACE_IO_SIZE,
5195 lsilogicIOPortWrite, lsilogicIOPortRead, NULL /*pvUser*/,
5196 pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI ? "LsiLogic" : "LsiLogicSas",
5197 NULL /*paExtDesc*/, &pThis->hIoPortsReg);
5198 AssertRCReturn(rc, rc);
5199
5200 /* Region #1: MMIO.
5201 *
5202 * Non-4-byte read access to LSILOGIC_REG_REPLY_QUEUE may cause real strange behavior
5203 * because the data is part of a physical guest address. But some drivers use 1-byte
5204 * access to scan for SCSI controllers. So, we simplify our code by telling IOM to
5205 * read DWORDs.
5206 *
5207 * Regarding writes, we couldn't find anything specific in the specs about what should
5208 * happen. So far we've ignored unaligned writes and assumed the missing bytes of
5209 * byte and word access to be zero. We suspect that IOMMMIO_FLAGS_WRITE_ONLY_DWORD
5210 * or IOMMMIO_FLAGS_WRITE_DWORD_ZEROED would be the most appropriate here, but since we
5211 * don't have real hw to test one, the old behavior is kept exactly like it used to be.
5212 */
5213 /** @todo Check out unaligned writes and non-dword writes on real LsiLogic
5214 * hardware. */
5215 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, 1 /*iPciRegion*/, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM,
5216 lsilogicMMIOWrite, lsilogicMMIORead, NULL /*pvUser*/,
5217 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_PASSTHRU,
5218 pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI ? "LsiLogic" : "LsiLogicSas",
5219 &pThis->hMmioReg);
5220 AssertRCReturn(rc, rc);
5221
5222 /* Region #2: MMIO - Diag. */
5223 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, 2 /*iPciRegion*/, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM,
5224 lsilogicDiagnosticWrite, lsilogicDiagnosticRead, NULL /*pvUser*/,
5225 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
5226 pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI ? "LsiLogicDiag" : "LsiLogicSasDiag",
5227 &pThis->hMmioDiag);
5228 AssertRCReturn(rc, rc);
5229
5230 /*
5231 * We need one entry free in the queue.
5232 */
5233 pThis->cReplyQueueEntries++;
5234 AssertLogRelReturn(pThis->cReplyQueueEntries <= RT_ELEMENTS(pThis->aReplyFreeQueue), VERR_INTERNAL_ERROR_3);
5235 AssertLogRelReturn(pThis->cReplyQueueEntries <= RT_ELEMENTS(pThis->aReplyPostQueue), VERR_INTERNAL_ERROR_3);
5236
5237 pThis->cRequestQueueEntries++;
5238 AssertLogRelReturn(pThis->cRequestQueueEntries <= RT_ELEMENTS(pThis->aRequestQueue), VERR_INTERNAL_ERROR_3);
5239
5240 /*
5241 * Device states.
5242 */
5243 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
5244 pThis->cDeviceStates = pThis->cPorts * LSILOGICSCSI_PCI_SPI_DEVICES_PER_BUS_MAX;
5245 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
5246 pThis->cDeviceStates = pThis->cPorts * LSILOGICSCSI_PCI_SAS_DEVICES_PER_PORT_MAX;
5247 else
5248 AssertLogRelMsgFailedReturn(("Invalid controller type: %d\n", pThis->enmCtrlType), VERR_INTERNAL_ERROR_4);
5249
5250 /*
5251 * Create event semaphore and worker thread.
5252 */
5253 rc = PDMDevHlpThreadCreate(pDevIns, &pThisCC->pThreadWrk, pThis, lsilogicR3Worker,
5254 lsilogicR3WorkerWakeUp, 0, RTTHREADTYPE_IO, szDevTag);
5255 if (RT_FAILURE(rc))
5256 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
5257 N_("LsiLogic: Failed to create worker thread %s"), szDevTag);
5258
5259 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEvtProcess);
5260 if (RT_FAILURE(rc))
5261 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
5262 N_("LsiLogic: Failed to create SUP event semaphore"));
5263
5264 /*
5265 * Allocate device states.
5266 */
5267 pThisCC->paDeviceStates = (PLSILOGICDEVICE)RTMemAllocZ(sizeof(LSILOGICDEVICE) * pThis->cDeviceStates);
5268 if (!pThisCC->paDeviceStates)
5269 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to allocate memory for device states"));
5270
5271 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
5272 {
5273 PLSILOGICDEVICE pDevice = &pThisCC->paDeviceStates[i];
5274
5275 /* Initialize static parts of the device. */
5276 pDevice->iLUN = i;
5277 pDevice->pDevIns = pDevIns;
5278 pDevice->Led.u32Magic = PDMLED_MAGIC;
5279 pDevice->IBase.pfnQueryInterface = lsilogicR3DeviceQueryInterface;
5280 pDevice->IMediaPort.pfnQueryDeviceLocation = lsilogicR3QueryDeviceLocation;
5281 pDevice->IMediaExPort.pfnIoReqCompleteNotify = lsilogicR3IoReqCompleteNotify;
5282 pDevice->IMediaExPort.pfnIoReqCopyFromBuf = lsilogicR3IoReqCopyFromBuf;
5283 pDevice->IMediaExPort.pfnIoReqCopyToBuf = lsilogicR3IoReqCopyToBuf;
5284 pDevice->IMediaExPort.pfnIoReqQueryBuf = NULL;
5285 pDevice->IMediaExPort.pfnIoReqQueryDiscardRanges = NULL;
5286 pDevice->IMediaExPort.pfnIoReqStateChanged = lsilogicR3IoReqStateChanged;
5287 pDevice->IMediaExPort.pfnMediumEjected = lsilogicR3MediumEjected;
5288 pDevice->ILed.pfnQueryStatusLed = lsilogicR3DeviceQueryStatusLed;
5289 RTStrPrintf(pDevice->szName, sizeof(pDevice->szName), "Device%u", i);
5290
5291 /* Attach SCSI driver. */
5292 rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, pDevice->szName);
5293 if (RT_SUCCESS(rc))
5294 {
5295 /* Query the media interface. */
5296 pDevice->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMIMEDIA);
5297 AssertMsgReturn(RT_VALID_PTR(pDevice->pDrvMedia),
5298 ("LsiLogic configuration error: LUN#%d misses the basic media interface!\n", pDevice->iLUN),
5299 VERR_PDM_MISSING_INTERFACE);
5300
5301 /* Get the extended media interface. */
5302 pDevice->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMIMEDIAEX);
5303 AssertMsgReturn(RT_VALID_PTR(pDevice->pDrvMediaEx),
5304 ("LsiLogic configuration error: LUN#%d misses the extended media interface!\n", pDevice->iLUN),
5305 VERR_PDM_MISSING_INTERFACE);
5306
5307 rc = pDevice->pDrvMediaEx->pfnIoReqAllocSizeSet(pDevice->pDrvMediaEx, sizeof(LSILOGICREQ));
5308 if (RT_FAILURE(rc))
5309 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
5310 N_("LsiLogic configuration error: LUN#%u: Failed to set I/O request size!"),
5311 pDevice->iLUN);
5312 }
5313 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
5314 {
5315 pDevice->pDrvBase = NULL;
5316 rc = VINF_SUCCESS;
5317 Log(("LsiLogic: no driver attached to device %s\n", pDevice->szName));
5318 }
5319 else
5320 {
5321 AssertLogRelMsgFailed(("LsiLogic: Failed to attach %s\n", pDevice->szName));
5322 return rc;
5323 }
5324 }
5325
5326 /*
5327 * Attach status driver (optional).
5328 */
5329 PPDMIBASE pBase;
5330 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
5331 if (RT_SUCCESS(rc))
5332 {
5333 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
5334 pThisCC->pMediaNotify = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIANOTIFY);
5335 }
5336 else
5337 AssertMsgReturn(rc == VERR_PDM_NO_ATTACHED_DRIVER,
5338 ("Failed to attach to status driver. rc=%Rrc\n", rc),
5339 PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot attach to status driver")));
5340
5341 /* Register save state handlers. */
5342 rc = PDMDevHlpSSMRegisterEx(pDevIns, LSILOGIC_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
5343 NULL, lsilogicR3LiveExec, NULL,
5344 NULL, lsilogicR3SaveExec, NULL,
5345 NULL, lsilogicR3LoadExec, lsilogicR3LoadDone);
5346 if (RT_FAILURE(rc))
5347 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot register save state handlers"));
5348
5349 pThis->enmWhoInit = LSILOGICWHOINIT_SYSTEM_BIOS;
5350
5351 /*
5352 * Register the info item.
5353 */
5354 char szTmp[128];
5355 RTStrPrintf(szTmp, sizeof(szTmp), "%s%u", pDevIns->pReg->szName, pDevIns->iInstance);
5356 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp,
5357 pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
5358 ? "LsiLogic SPI info."
5359 : "LsiLogic SAS info.", lsilogicR3Info);
5360
5361 /* Allocate configuration pages. */
5362 rc = lsilogicR3ConfigurationPagesAlloc(pThis, pThisCC);
5363 if (RT_FAILURE(rc))
5364 PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: Failed to allocate memory for configuration pages"));
5365
5366 /* Perform hard reset. */
5367 rc = lsilogicR3HardReset(pDevIns, pThis, pThisCC);
5368 AssertRC(rc);
5369
5370 return rc;
5371}
5372
5373#else /* !IN_RING3 */
5374
5375/**
5376 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
5377 */
5378static DECLCALLBACK(int) lsilogicRZConstruct(PPDMDEVINS pDevIns)
5379{
5380 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5381 PLSILOGICSCSI pThis = PDMDEVINS_2_DATA(pDevIns, PLSILOGICSCSI);
5382
5383 /* Setup callbacks for this context: */
5384 int rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsReg, lsilogicIOPortWrite, lsilogicIOPortRead, NULL /*pvUser*/);
5385 AssertRCReturn(rc, rc);
5386
5387 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmioReg, lsilogicMMIOWrite, lsilogicMMIORead, NULL /*pvUser*/);
5388 AssertRCReturn(rc, rc);
5389
5390 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmioDiag, lsilogicDiagnosticWrite, lsilogicDiagnosticRead, NULL /*pvUser*/);
5391 AssertRCReturn(rc, rc);
5392
5393 return VINF_SUCCESS;
5394}
5395
5396#endif /* !IN_RING3 */
5397
5398/**
5399 * The device registration structure - SPI SCSI controller.
5400 */
5401const PDMDEVREG g_DeviceLsiLogicSCSI =
5402{
5403 /* .u32Version = */ PDM_DEVREG_VERSION,
5404 /* .uReserved0 = */ 0,
5405 /* .szName = */ "lsilogicscsi",
5406 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE
5407 | PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
5408 /* .fClass = */ PDM_DEVREG_CLASS_STORAGE,
5409 /* .cMaxInstances = */ ~0U,
5410 /* .uSharedVersion = */ 42,
5411 /* .cbInstanceShared = */ sizeof(LSILOGICSCSI),
5412 /* .cbInstanceCC = */ sizeof(LSILOGICSCSICC),
5413 /* .cbInstanceRC = */ sizeof(LSILOGICSCSIRC),
5414 /* .cMaxPciDevices = */ 1,
5415 /* .cMaxMsixVectors = */ 0,
5416 /* .pszDescription = */ "LSI Logic 53c1030 SCSI controller.\n",
5417#if defined(IN_RING3)
5418 /* .pszRCMod = */ "VBoxDDRC.rc",
5419 /* .pszR0Mod = */ "VBoxDDR0.r0",
5420 /* .pfnConstruct = */ lsilogicR3Construct,
5421 /* .pfnDestruct = */ lsilogicR3Destruct,
5422 /* .pfnRelocate = */ NULL,
5423 /* .pfnMemSetup = */ NULL,
5424 /* .pfnPowerOn = */ NULL,
5425 /* .pfnReset = */ lsilogicR3Reset,
5426 /* .pfnSuspend = */ lsilogicR3Suspend,
5427 /* .pfnResume = */ lsilogicR3Resume,
5428 /* .pfnAttach = */ lsilogicR3Attach,
5429 /* .pfnDetach = */ lsilogicR3Detach,
5430 /* .pfnQueryInterface = */ NULL,
5431 /* .pfnInitComplete = */ NULL,
5432 /* .pfnPowerOff = */ lsilogicR3PowerOff,
5433 /* .pfnSoftReset = */ NULL,
5434 /* .pfnReserved0 = */ NULL,
5435 /* .pfnReserved1 = */ NULL,
5436 /* .pfnReserved2 = */ NULL,
5437 /* .pfnReserved3 = */ NULL,
5438 /* .pfnReserved4 = */ NULL,
5439 /* .pfnReserved5 = */ NULL,
5440 /* .pfnReserved6 = */ NULL,
5441 /* .pfnReserved7 = */ NULL,
5442#elif defined(IN_RING0)
5443 /* .pfnEarlyConstruct = */ NULL,
5444 /* .pfnConstruct = */ lsilogicRZConstruct,
5445 /* .pfnDestruct = */ NULL,
5446 /* .pfnFinalDestruct = */ NULL,
5447 /* .pfnRequest = */ NULL,
5448 /* .pfnReserved0 = */ NULL,
5449 /* .pfnReserved1 = */ NULL,
5450 /* .pfnReserved2 = */ NULL,
5451 /* .pfnReserved3 = */ NULL,
5452 /* .pfnReserved4 = */ NULL,
5453 /* .pfnReserved5 = */ NULL,
5454 /* .pfnReserved6 = */ NULL,
5455 /* .pfnReserved7 = */ NULL,
5456#elif defined(IN_RC)
5457 /* .pfnConstruct = */ lsilogicRZConstruct,
5458 /* .pfnReserved0 = */ NULL,
5459 /* .pfnReserved1 = */ NULL,
5460 /* .pfnReserved2 = */ NULL,
5461 /* .pfnReserved3 = */ NULL,
5462 /* .pfnReserved4 = */ NULL,
5463 /* .pfnReserved5 = */ NULL,
5464 /* .pfnReserved6 = */ NULL,
5465 /* .pfnReserved7 = */ NULL,
5466#else
5467# error "Not in IN_RING3, IN_RING0 or IN_RC!"
5468#endif
5469 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
5470};
5471
5472/**
5473 * The device registration structure - SAS controller.
5474 */
5475const PDMDEVREG g_DeviceLsiLogicSAS =
5476{
5477 /* .u32Version = */ PDM_DEVREG_VERSION,
5478 /* .uReserved0 = */ 0,
5479 /* .szName = */ "lsilogicsas",
5480 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE
5481 | PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION
5482 | PDM_DEVREG_FLAGS_FIRST_RESET_NOTIFICATION,
5483 /* .fClass = */ PDM_DEVREG_CLASS_STORAGE,
5484 /* .cMaxInstances = */ ~0U,
5485 /* .uSharedVersion = */ 42,
5486 /* .cbInstanceShared = */ sizeof(LSILOGICSCSI),
5487 /* .cbInstanceCC = */ sizeof(LSILOGICSCSICC),
5488 /* .cbInstanceRC = */ sizeof(LSILOGICSCSIRC),
5489 /* .cMaxPciDevices = */ 1,
5490 /* .cMaxMsixVectors = */ 0,
5491 /* .pszDescription = */ "LSI Logic SAS1068 controller.\n",
5492#if defined(IN_RING3)
5493 /* .pszRCMod = */ "VBoxDDRC.rc",
5494 /* .pszR0Mod = */ "VBoxDDR0.r0",
5495 /* .pfnConstruct = */ lsilogicR3Construct,
5496 /* .pfnDestruct = */ lsilogicR3Destruct,
5497 /* .pfnRelocate = */ NULL,
5498 /* .pfnMemSetup = */ NULL,
5499 /* .pfnPowerOn = */ NULL,
5500 /* .pfnReset = */ lsilogicR3Reset,
5501 /* .pfnSuspend = */ lsilogicR3Suspend,
5502 /* .pfnResume = */ lsilogicR3Resume,
5503 /* .pfnAttach = */ lsilogicR3Attach,
5504 /* .pfnDetach = */ lsilogicR3Detach,
5505 /* .pfnQueryInterface = */ NULL,
5506 /* .pfnInitComplete = */ NULL,
5507 /* .pfnPowerOff = */ lsilogicR3PowerOff,
5508 /* .pfnSoftReset = */ NULL,
5509 /* .pfnReserved0 = */ NULL,
5510 /* .pfnReserved1 = */ NULL,
5511 /* .pfnReserved2 = */ NULL,
5512 /* .pfnReserved3 = */ NULL,
5513 /* .pfnReserved4 = */ NULL,
5514 /* .pfnReserved5 = */ NULL,
5515 /* .pfnReserved6 = */ NULL,
5516 /* .pfnReserved7 = */ NULL,
5517#elif defined(IN_RING0)
5518 /* .pfnEarlyConstruct = */ NULL,
5519 /* .pfnConstruct = */ lsilogicRZConstruct,
5520 /* .pfnDestruct = */ NULL,
5521 /* .pfnFinalDestruct = */ NULL,
5522 /* .pfnRequest = */ NULL,
5523 /* .pfnReserved0 = */ NULL,
5524 /* .pfnReserved1 = */ NULL,
5525 /* .pfnReserved2 = */ NULL,
5526 /* .pfnReserved3 = */ NULL,
5527 /* .pfnReserved4 = */ NULL,
5528 /* .pfnReserved5 = */ NULL,
5529 /* .pfnReserved6 = */ NULL,
5530 /* .pfnReserved7 = */ NULL,
5531#elif defined(IN_RC)
5532 /* .pfnConstruct = */ lsilogicRZConstruct,
5533 /* .pfnReserved0 = */ NULL,
5534 /* .pfnReserved1 = */ NULL,
5535 /* .pfnReserved2 = */ NULL,
5536 /* .pfnReserved3 = */ NULL,
5537 /* .pfnReserved4 = */ NULL,
5538 /* .pfnReserved5 = */ NULL,
5539 /* .pfnReserved6 = */ NULL,
5540 /* .pfnReserved7 = */ NULL,
5541#else
5542# error "Not in IN_RING3, IN_RING0 or IN_RC!"
5543#endif
5544 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
5545};
5546
5547#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
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