summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp
parentInitial commit. (diff)
downloadvirtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz
virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp')
-rw-r--r--src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp5849
1 files changed, 5849 insertions, 0 deletions
diff --git a/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp b/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp
new file mode 100644
index 00000000..05a06d4f
--- /dev/null
+++ b/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp
@@ -0,0 +1,5849 @@
+/* $Id: DevLsiLogicSCSI.cpp $ */
+/** @file
+ * DevLsiLogicSCSI - LsiLogic LSI53c1030 SCSI controller.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_LSILOGICSCSI
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdmstorageifs.h>
+#include <VBox/vmm/pdmqueue.h>
+#include <VBox/vmm/pdmthread.h>
+#include <VBox/vmm/pdmcritsect.h>
+#include <VBox/scsi.h>
+#include <VBox/sup.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/string.h>
+#include <iprt/list.h>
+#ifdef IN_RING3
+# include <iprt/memcache.h>
+# include <iprt/mem.h>
+# include <iprt/param.h>
+# include <iprt/uuid.h>
+# include <iprt/time.h>
+#endif
+
+#include "DevLsiLogicSCSI.h"
+#include "VBoxSCSI.h"
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The current saved state version. */
+#define LSILOGIC_SAVED_STATE_VERSION 5
+/** The saved state version used by VirtualBox before the diagnostic
+ * memory access was implemented. */
+#define LSILOGIC_SAVED_STATE_VERSION_PRE_DIAG_MEM 4
+/** The saved state version used by VirtualBox before the doorbell status flag
+ * was changed from bool to a 32bit enum. */
+#define LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL 3
+/** The saved state version used by VirtualBox before SAS support was added. */
+#define LSILOGIC_SAVED_STATE_VERSION_PRE_SAS 2
+/** The saved state version used by VirtualBox 3.0 and earlier. It does not
+ * include the device config part. */
+#define LSILOGIC_SAVED_STATE_VERSION_VBOX_30 1
+
+/** Maximum number of entries in the release log. */
+#define MAX_REL_LOG_ERRORS 1024
+
+#define LSILOGIC_RTGCPHYS_FROM_U32(Hi, Lo) ( (RTGCPHYS)RT_MAKE_U64(Lo, Hi) )
+
+/** Upper number a buffer is freed if it was too big before. */
+#define LSILOGIC_MAX_ALLOC_TOO_MUCH 20
+
+/** Maximum size of the memory regions (prevents teh guest from DOSing the host by
+ * allocating loadds of memory). */
+#define LSILOGIC_MEMORY_REGIONS_MAX (_1M)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/** Pointer to the device instance data of the LsiLogic emulation. */
+typedef struct LSILOGICSCSI *PLSILOGICSCSI;
+
+#ifdef IN_RING3
+/**
+ * Memory buffer callback.
+ *
+ * @returns nothing.
+ * @param pThis The LsiLogic controller instance.
+ * @param GCPhys The guest physical address of the memory buffer.
+ * @param pSgBuf The pointer to the host R3 S/G buffer.
+ * @param cbCopy How many bytes to copy between the two buffers.
+ * @param pcbSkip Initially contains the amount of bytes to skip
+ * starting from the guest physical address before
+ * accessing the S/G buffer and start copying data.
+ * On return this contains the remaining amount if
+ * cbCopy < *pcbSkip or 0 otherwise.
+ */
+typedef DECLCALLBACK(void) LSILOGICR3MEMCOPYCALLBACK(PLSILOGICSCSI pThis, RTGCPHYS GCPhys, PRTSGBUF pSgBuf, size_t cbCopy,
+ size_t *pcbSkip);
+/** Pointer to a memory copy buffer callback. */
+typedef LSILOGICR3MEMCOPYCALLBACK *PLSILOGICR3MEMCOPYCALLBACK;
+#endif
+
+/**
+ * Reply data.
+ */
+typedef struct LSILOGICSCSIREPLY
+{
+ /** Lower 32 bits of the reply address in memory. */
+ uint32_t u32HostMFALowAddress;
+ /** Full address of the reply in guest memory. */
+ RTGCPHYS GCPhysReplyAddress;
+ /** Size of the reply. */
+ uint32_t cbReply;
+ /** Different views to the reply depending on the request type. */
+ MptReplyUnion Reply;
+} LSILOGICSCSIREPLY;
+/** Pointer to reply data. */
+typedef LSILOGICSCSIREPLY *PLSILOGICSCSIREPLY;
+
+/**
+ * Memory region of the IOC.
+ */
+typedef struct LSILOGICMEMREGN
+{
+ /** List node. */
+ RTLISTNODE NodeList;
+ /** 32bit address the region starts to describe. */
+ uint32_t u32AddrStart;
+ /** 32bit address the region ends (inclusive). */
+ uint32_t u32AddrEnd;
+ /** Data for this region - variable. */
+ uint32_t au32Data[1];
+} LSILOGICMEMREGN;
+/** Pointer to a memory region. */
+typedef LSILOGICMEMREGN *PLSILOGICMEMREGN;
+
+/**
+ * State of a device attached to the buslogic host adapter.
+ *
+ * @implements PDMIBASE
+ * @implements PDMISCSIPORT
+ * @implements PDMILEDPORTS
+ */
+typedef struct LSILOGICDEVICE
+{
+ /** Pointer to the owning lsilogic device instance. - R3 pointer */
+ R3PTRTYPE(PLSILOGICSCSI) pLsiLogicR3;
+
+ /** LUN of the device. */
+ uint32_t iLUN;
+ /** Number of outstanding tasks on the port. */
+ volatile uint32_t cOutstandingRequests;
+
+#if HC_ARCH_BITS == 64
+ uint32_t Alignment0;
+#endif
+
+ /** Our base interface. */
+ PDMIBASE IBase;
+ /** Media port interface. */
+ PDMIMEDIAPORT IMediaPort;
+ /** Extended media port interface. */
+ PDMIMEDIAEXPORT IMediaExPort;
+ /** Led interface. */
+ PDMILEDPORTS ILed;
+ /** Pointer to the attached driver's base interface. */
+ R3PTRTYPE(PPDMIBASE) pDrvBase;
+ /** Pointer to the attached driver's media interface. */
+ R3PTRTYPE(PPDMIMEDIA) pDrvMedia;
+ /** Pointer to the attached driver's extended media interface. */
+ R3PTRTYPE(PPDMIMEDIAEX) pDrvMediaEx;
+ /** The status LED state for this device. */
+ PDMLED Led;
+
+} LSILOGICDEVICE;
+/** Pointer to a device state. */
+typedef LSILOGICDEVICE *PLSILOGICDEVICE;
+
+/** Pointer to a task state. */
+typedef struct LSILOGICREQ *PLSILOGICREQ;
+
+/**
+ * Device instance data for the emulated SCSI controller.
+ */
+typedef struct LSILOGICSCSI
+{
+ /** PCI device structure. */
+ PDMPCIDEV PciDev;
+ /** Pointer to the device instance. - R3 ptr. */
+ PPDMDEVINSR3 pDevInsR3;
+ /** Pointer to the device instance. - R0 ptr. */
+ PPDMDEVINSR0 pDevInsR0;
+ /** Pointer to the device instance. - RC ptr. */
+ PPDMDEVINSRC pDevInsRC;
+
+ /** Flag whether the GC part of the device is enabled. */
+ bool fGCEnabled;
+ /** Flag whether the R0 part of the device is enabled. */
+ bool fR0Enabled;
+
+ /** The state the controller is currently in. */
+ LSILOGICSTATE enmState;
+ /** Who needs to init the driver to get into operational state. */
+ LSILOGICWHOINIT enmWhoInit;
+ /** Flag whether we are in doorbell function. */
+ LSILOGICDOORBELLSTATE enmDoorbellState;
+ /** Flag whether diagnostic access is enabled. */
+ bool fDiagnosticEnabled;
+ /** Flag whether a notification was send to R3. */
+ bool fNotificationSent;
+ /** Flag whether the guest enabled event notification from the IOC. */
+ bool fEventNotificationEnabled;
+ /** Flag whether the diagnostic address and RW registers are enabled. */
+ bool fDiagRegsEnabled;
+
+ /** Queue to send tasks to R3. - R3 ptr */
+ R3PTRTYPE(PPDMQUEUE) pNotificationQueueR3;
+ /** Queue to send tasks to R3. - R0 ptr */
+ R0PTRTYPE(PPDMQUEUE) pNotificationQueueR0;
+ /** Queue to send tasks to R3. - RC ptr */
+ RCPTRTYPE(PPDMQUEUE) pNotificationQueueRC;
+
+ /** Number of device states allocated. */
+ uint32_t cDeviceStates;
+
+ /** States for attached devices. */
+ R3PTRTYPE(PLSILOGICDEVICE) paDeviceStates;
+#if HC_ARCH_BITS == 32
+ RTR3PTR R3PtrPadding0;
+#endif
+
+ /** Interrupt mask. */
+ volatile uint32_t uInterruptMask;
+ /** Interrupt status register. */
+ volatile uint32_t uInterruptStatus;
+
+ /** Buffer for messages which are passed through the doorbell using the
+ * handshake method. */
+ uint32_t aMessage[sizeof(MptConfigurationRequest)]; /** @todo r=bird: Looks like 4 tims the required size? Please explain in comment if this correct... */
+ /** Actual position in the buffer. */
+ uint32_t iMessage;
+ /** Size of the message which is given in the doorbell message in dwords. */
+ uint32_t cMessage;
+
+ /** Reply buffer.
+ * @note 60 bytes */
+ MptReplyUnion ReplyBuffer;
+ /** Next entry to read. */
+ uint32_t uNextReplyEntryRead;
+ /** Size of the reply in the buffer in 16bit words. */
+ uint32_t cReplySize;
+
+ /** The fault code of the I/O controller if we are in the fault state. */
+ uint16_t u16IOCFaultCode;
+
+ /** I/O port address the device is mapped to. */
+ RTIOPORT IOPortBase;
+ /** MMIO address the device is mapped to. */
+ RTGCPHYS GCPhysMMIOBase;
+
+ /** Upper 32 bits of the message frame address to locate requests in guest memory. */
+ uint32_t u32HostMFAHighAddr;
+ /** Upper 32 bits of the sense buffer address. */
+ uint32_t u32SenseBufferHighAddr;
+ /** Maximum number of devices the driver reported he can handle. */
+ uint8_t cMaxDevices;
+ /** Maximum number of buses the driver reported he can handle. */
+ uint8_t cMaxBuses;
+ /** Current size of reply message frames in the guest. */
+ uint16_t cbReplyFrame;
+
+ /** Next key to write in the sequence to get access
+ * to diagnostic memory. */
+ uint32_t iDiagnosticAccess;
+
+ /** Number entries allocated for the reply queue. */
+ uint32_t cReplyQueueEntries;
+ /** Number entries allocated for the outstanding request queue. */
+ uint32_t cRequestQueueEntries;
+
+
+ /** Critical section protecting the reply post queue. */
+ PDMCRITSECT ReplyPostQueueCritSect;
+ /** Critical section protecting the reply free queue. */
+ PDMCRITSECT ReplyFreeQueueCritSect;
+ /** Critical section protecting the request queue against
+ * concurrent access from the guest. */
+ PDMCRITSECT RequestQueueCritSect;
+ /** Critical section protecting the reply free queue against
+ * concurrent write access from the guest. */
+ PDMCRITSECT ReplyFreeQueueWriteCritSect;
+
+ /** Pointer to the start of the reply free queue - R3. */
+ R3PTRTYPE(volatile uint32_t *) pReplyFreeQueueBaseR3;
+ /** Pointer to the start of the reply post queue - R3. */
+ R3PTRTYPE(volatile uint32_t *) pReplyPostQueueBaseR3;
+ /** Pointer to the start of the request queue - R3. */
+ R3PTRTYPE(volatile uint32_t *) pRequestQueueBaseR3;
+
+ /** Pointer to the start of the reply queue - R0. */
+ R0PTRTYPE(volatile uint32_t *) pReplyFreeQueueBaseR0;
+ /** Pointer to the start of the reply queue - R0. */
+ R0PTRTYPE(volatile uint32_t *) pReplyPostQueueBaseR0;
+ /** Pointer to the start of the request queue - R0. */
+ R0PTRTYPE(volatile uint32_t *) pRequestQueueBaseR0;
+
+ /** Pointer to the start of the reply queue - RC. */
+ RCPTRTYPE(volatile uint32_t *) pReplyFreeQueueBaseRC;
+ /** Pointer to the start of the reply queue - RC. */
+ RCPTRTYPE(volatile uint32_t *) pReplyPostQueueBaseRC;
+ /** Pointer to the start of the request queue - RC. */
+ RCPTRTYPE(volatile uint32_t *) pRequestQueueBaseRC;
+ /** End these RC pointers on a 64-bit boundrary. */
+ RTRCPTR RCPtrPadding1;
+
+ /** Next free entry in the reply queue the guest can write a address to. */
+ volatile uint32_t uReplyFreeQueueNextEntryFreeWrite;
+ /** Next valid entry the controller can read a valid address for reply frames from. */
+ volatile uint32_t uReplyFreeQueueNextAddressRead;
+
+ /** Next free entry in the reply queue the guest can write a address to. */
+ volatile uint32_t uReplyPostQueueNextEntryFreeWrite;
+ /** Next valid entry the controller can read a valid address for reply frames from. */
+ volatile uint32_t uReplyPostQueueNextAddressRead;
+
+ /** Next free entry the guest can write a address to a request frame to. */
+ volatile uint32_t uRequestQueueNextEntryFreeWrite;
+ /** Next valid entry the controller can read a valid address for request frames from. */
+ volatile uint32_t uRequestQueueNextAddressRead;
+
+ /** Emulated controller type */
+ LSILOGICCTRLTYPE enmCtrlType;
+ /** Handle counter */
+ uint16_t u16NextHandle;
+
+ /** Number of ports this controller has. */
+ uint8_t cPorts;
+
+ /** BIOS emulation. */
+ VBOXSCSI VBoxSCSI;
+
+ /** Status LUN: The base interface. */
+ PDMIBASE IBase;
+ /** Status LUN: Leds interface. */
+ PDMILEDPORTS ILeds;
+ /** Status LUN: Partner of ILeds. */
+ R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
+ /** Status LUN: Media Notifys. */
+ R3PTRTYPE(PPDMIMEDIANOTIFY) pMediaNotify;
+ /** Pointer to the configuration page area. */
+ R3PTRTYPE(PMptConfigurationPagesSupported) pConfigurationPages;
+
+ /** Indicates that PDMDevHlpAsyncNotificationCompleted should be called when
+ * a port is entering the idle state. */
+ bool volatile fSignalIdle;
+ /** Flag whether we have tasks which need to be processed again- */
+ bool volatile fRedo;
+ /** Flag whether the worker thread is sleeping. */
+ volatile bool fWrkThreadSleeping;
+ /** Flag whether a request from the BIOS is pending which the
+ * worker thread needs to process. */
+ volatile bool fBiosReqPending;
+#if HC_ARCH_BITS == 64
+ /** Alignment padding. */
+ bool afPadding2[4];
+#endif
+ /** List of tasks which can be redone. */
+ R3PTRTYPE(volatile PLSILOGICREQ) pTasksRedoHead;
+
+ /** Current address to read from or write to in the diagnostic memory region. */
+ uint32_t u32DiagMemAddr;
+ /** Current size of the memory regions. */
+ uint32_t cbMemRegns;
+
+#if HC_ARCH_BITS ==32
+ uint32_t u32Padding3;
+#endif
+
+ union
+ {
+ /** List of memory regions - PLSILOGICMEMREGN. */
+ RTLISTANCHOR ListMemRegns;
+ uint8_t u8Padding[2 * sizeof(RTUINTPTR)];
+ };
+
+ /** The support driver session handle. */
+ R3R0PTRTYPE(PSUPDRVSESSION) pSupDrvSession;
+ /** Worker thread. */
+ R3PTRTYPE(PPDMTHREAD) pThreadWrk;
+ /** The event semaphore the processing thread waits on. */
+ SUPSEMEVENT hEvtProcess;
+
+} LSILOGISCSI;
+
+/**
+ * Task state object which holds all necessary data while
+ * processing the request from the guest.
+ */
+typedef struct LSILOGICREQ
+{
+ /** I/O request handle. */
+ PDMMEDIAEXIOREQ hIoReq;
+ /** Next in the redo list. */
+ PLSILOGICREQ pRedoNext;
+ /** Target device. */
+ PLSILOGICDEVICE pTargetDevice;
+ /** The message request from the guest. */
+ MptRequestUnion GuestRequest;
+ /** Address of the message request frame in guests memory.
+ * Used to read the S/G entries in the second step. */
+ RTGCPHYS GCPhysMessageFrameAddr;
+ /** Physical start address of the S/G list. */
+ RTGCPHYS GCPhysSgStart;
+ /** Chain offset */
+ uint32_t cChainOffset;
+ /** Pointer to the sense buffer. */
+ uint8_t abSenseBuffer[18];
+ /** Flag whether the request was issued from the BIOS. */
+ bool fBIOS;
+ /** SCSI status code. */
+ uint8_t u8ScsiSts;
+} LSILOGICREQ;
+
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN
+#ifdef IN_RING3
+static void lsilogicR3InitializeConfigurationPages(PLSILOGICSCSI pThis);
+static void lsilogicR3ConfigurationPagesFree(PLSILOGICSCSI pThis);
+static int lsilogicR3ProcessConfigurationRequest(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq,
+ PMptConfigurationReply pReply);
+#endif
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Key sequence the guest has to write to enable access
+ * to diagnostic memory. */
+static const uint8_t g_lsilogicDiagnosticAccess[] = {0x04, 0x0b, 0x02, 0x07, 0x0d};
+
+/**
+ * Updates the status of the interrupt pin of the device.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+static void lsilogicUpdateInterrupt(PLSILOGICSCSI pThis)
+{
+ uint32_t uIntSts;
+
+ LogFlowFunc(("Updating interrupts\n"));
+
+ /* Mask out doorbell status so that it does not affect interrupt updating. */
+ uIntSts = (ASMAtomicReadU32(&pThis->uInterruptStatus) & ~LSILOGIC_REG_HOST_INTR_STATUS_DOORBELL_STS);
+ /* Check maskable interrupts. */
+ uIntSts &= ~(ASMAtomicReadU32(&pThis->uInterruptMask) & ~LSILOGIC_REG_HOST_INTR_MASK_IRQ_ROUTING);
+
+ if (uIntSts)
+ {
+ LogFlowFunc(("Setting interrupt\n"));
+ PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0, 1);
+ }
+ else
+ {
+ LogFlowFunc(("Clearing interrupt\n"));
+ PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0, 0);
+ }
+}
+
+/**
+ * Sets a given interrupt status bit in the status register and
+ * updates the interrupt status.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param uStatus The status bit to set.
+ */
+DECLINLINE(void) lsilogicSetInterrupt(PLSILOGICSCSI pThis, uint32_t uStatus)
+{
+ ASMAtomicOrU32(&pThis->uInterruptStatus, uStatus);
+ lsilogicUpdateInterrupt(pThis);
+}
+
+/**
+ * Clears a given interrupt status bit in the status register and
+ * updates the interrupt status.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param uStatus The status bit to set.
+ */
+DECLINLINE(void) lsilogicClearInterrupt(PLSILOGICSCSI pThis, uint32_t uStatus)
+{
+ ASMAtomicAndU32(&pThis->uInterruptStatus, ~uStatus);
+ lsilogicUpdateInterrupt(pThis);
+}
+
+
+#ifdef IN_RING3
+/**
+ * Sets the I/O controller into fault state and sets the fault code.
+ *
+ * @returns nothing
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param uIOCFaultCode Fault code to set.
+ */
+DECLINLINE(void) lsilogicSetIOCFaultCode(PLSILOGICSCSI pThis, uint16_t uIOCFaultCode)
+{
+ if (pThis->enmState != LSILOGICSTATE_FAULT)
+ {
+ LogFunc(("Setting I/O controller into FAULT state: uIOCFaultCode=%u\n", uIOCFaultCode));
+ pThis->enmState = LSILOGICSTATE_FAULT;
+ pThis->u16IOCFaultCode = uIOCFaultCode;
+ }
+ else
+ LogFunc(("We are already in FAULT state\n"));
+}
+#endif /* IN_RING3 */
+
+
+/**
+ * Returns the number of frames in the reply free queue.
+ *
+ * @returns Number of frames in the reply free queue.
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+DECLINLINE(uint32_t) lsilogicReplyFreeQueueGetFrameCount(PLSILOGICSCSI pThis)
+{
+ uint32_t cReplyFrames = 0;
+
+ if (pThis->uReplyFreeQueueNextAddressRead <= pThis->uReplyFreeQueueNextEntryFreeWrite)
+ cReplyFrames = pThis->uReplyFreeQueueNextEntryFreeWrite - pThis->uReplyFreeQueueNextAddressRead;
+ else
+ cReplyFrames = pThis->cReplyQueueEntries - pThis->uReplyFreeQueueNextAddressRead + pThis->uReplyFreeQueueNextEntryFreeWrite;
+
+ return cReplyFrames;
+}
+
+#ifdef IN_RING3
+
+/**
+ * Returns the number of free entries in the reply post queue.
+ *
+ * @returns Number of frames in the reply free queue.
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+DECLINLINE(uint32_t) lsilogicReplyPostQueueGetFrameCount(PLSILOGICSCSI pThis)
+{
+ uint32_t cReplyFrames = 0;
+
+ if (pThis->uReplyPostQueueNextAddressRead <= pThis->uReplyPostQueueNextEntryFreeWrite)
+ cReplyFrames = pThis->cReplyQueueEntries - pThis->uReplyPostQueueNextEntryFreeWrite + pThis->uReplyPostQueueNextAddressRead;
+ else
+ cReplyFrames = pThis->uReplyPostQueueNextEntryFreeWrite - pThis->uReplyPostQueueNextAddressRead;
+
+ return cReplyFrames;
+}
+
+
+/**
+ * Performs a hard reset on the controller.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+static int lsilogicR3HardReset(PLSILOGICSCSI pThis)
+{
+ pThis->enmState = LSILOGICSTATE_RESET;
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
+
+ /* The interrupts are masked out. */
+ pThis->uInterruptMask |= LSILOGIC_REG_HOST_INTR_MASK_DOORBELL
+ | LSILOGIC_REG_HOST_INTR_MASK_REPLY;
+ /* Reset interrupt states. */
+ pThis->uInterruptStatus = 0;
+ lsilogicUpdateInterrupt(pThis);
+
+ /* Reset the queues. */
+ pThis->uReplyFreeQueueNextEntryFreeWrite = 0;
+ pThis->uReplyFreeQueueNextAddressRead = 0;
+ pThis->uReplyPostQueueNextEntryFreeWrite = 0;
+ pThis->uReplyPostQueueNextAddressRead = 0;
+ pThis->uRequestQueueNextEntryFreeWrite = 0;
+ pThis->uRequestQueueNextAddressRead = 0;
+
+ /* Disable diagnostic access. */
+ pThis->iDiagnosticAccess = 0;
+ pThis->fDiagnosticEnabled = false;
+ pThis->fDiagRegsEnabled = false;
+
+ /* Set default values. */
+ pThis->cMaxDevices = pThis->cDeviceStates;
+ pThis->cMaxBuses = 1;
+ pThis->cbReplyFrame = 128; /** @todo Figure out where it is needed. */
+ pThis->u16NextHandle = 1;
+ pThis->u32DiagMemAddr = 0;
+
+ lsilogicR3ConfigurationPagesFree(pThis);
+ lsilogicR3InitializeConfigurationPages(pThis);
+
+ /* Mark that we finished performing the reset. */
+ pThis->enmState = LSILOGICSTATE_READY;
+ return VINF_SUCCESS;
+}
+
+/**
+ * Frees the configuration pages if allocated.
+ *
+ * @returns nothing.
+ * @param pThis The LsiLogic controller instance
+ */
+static void lsilogicR3ConfigurationPagesFree(PLSILOGICSCSI pThis)
+{
+
+ if (pThis->pConfigurationPages)
+ {
+ /* Destroy device list if we emulate a SAS controller. */
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ {
+ PMptConfigurationPagesSas pSasPages = &pThis->pConfigurationPages->u.SasPages;
+ PMptSASDevice pSASDeviceCurr = pSasPages->pSASDeviceHead;
+
+ while (pSASDeviceCurr)
+ {
+ PMptSASDevice pFree = pSASDeviceCurr;
+
+ pSASDeviceCurr = pSASDeviceCurr->pNext;
+ RTMemFree(pFree);
+ }
+ if (pSasPages->paPHYs)
+ RTMemFree(pSasPages->paPHYs);
+ if (pSasPages->pManufacturingPage7)
+ RTMemFree(pSasPages->pManufacturingPage7);
+ if (pSasPages->pSASIOUnitPage0)
+ RTMemFree(pSasPages->pSASIOUnitPage0);
+ if (pSasPages->pSASIOUnitPage1)
+ RTMemFree(pSasPages->pSASIOUnitPage1);
+ }
+
+ RTMemFree(pThis->pConfigurationPages);
+ }
+}
+
+/**
+ * Finishes a context reply.
+ *
+ * @returns nothing
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param u32MessageContext The message context ID to post.
+ */
+static void lsilogicR3FinishContextReply(PLSILOGICSCSI pThis, uint32_t u32MessageContext)
+{
+ int rc;
+
+ LogFlowFunc(("pThis=%#p u32MessageContext=%#x\n", pThis, u32MessageContext));
+
+ AssertMsg(pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_NOT_IN_USE, ("We are in a doorbell function\n"));
+
+ /* Write message context ID into reply post queue. */
+ rc = PDMCritSectEnter(&pThis->ReplyPostQueueCritSect, VINF_SUCCESS);
+ AssertRC(rc);
+
+ /* Check for a entry in the queue. */
+ if (!lsilogicReplyPostQueueGetFrameCount(pThis))
+ {
+ /* Set error code. */
+ lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
+ return;
+ }
+
+ /* We have a context reply. */
+ ASMAtomicWriteU32(&pThis->CTX_SUFF(pReplyPostQueueBase)[pThis->uReplyPostQueueNextEntryFreeWrite], u32MessageContext);
+ ASMAtomicIncU32(&pThis->uReplyPostQueueNextEntryFreeWrite);
+ pThis->uReplyPostQueueNextEntryFreeWrite %= pThis->cReplyQueueEntries;
+
+ /* Set interrupt. */
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR);
+
+ PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
+}
+
+
+/**
+ * Takes necessary steps to finish a reply frame.
+ *
+ * @returns nothing
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pReply Pointer to the reply message.
+ * @param fForceReplyFifo Flag whether the use of the reply post fifo is forced.
+ */
+static void lsilogicFinishAddressReply(PLSILOGICSCSI pThis, PMptReplyUnion pReply, bool fForceReplyFifo)
+{
+ /*
+ * If we are in a doorbell function we set the reply size now and
+ * set the system doorbell status interrupt to notify the guest that
+ * we are ready to send the reply.
+ */
+ if (pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_NOT_IN_USE && !fForceReplyFifo)
+ {
+ /* Set size of the reply in 16bit words. The size in the reply is in 32bit dwords. */
+ pThis->cReplySize = pReply->Header.u8MessageLength * 2;
+ Log(("%s: cReplySize=%u\n", __FUNCTION__, pThis->cReplySize));
+ pThis->uNextReplyEntryRead = 0;
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ }
+ else
+ {
+ /*
+ * The reply queues are only used if the request was fetched from the request queue.
+ * Requests from the request queue are always transferred to R3. So it is not possible
+ * that this case happens in R0 or GC.
+ */
+# ifdef IN_RING3
+ int rc;
+ /* Grab a free reply message from the queue. */
+ rc = PDMCritSectEnter(&pThis->ReplyFreeQueueCritSect, VINF_SUCCESS);
+ AssertRC(rc);
+
+ /* Check for a free reply frame. */
+ if (!lsilogicReplyFreeQueueGetFrameCount(pThis))
+ {
+ /* Set error code. */
+ lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ PDMCritSectLeave(&pThis->ReplyFreeQueueCritSect);
+ return;
+ }
+
+ uint32_t u32ReplyFrameAddressLow = pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead];
+
+ pThis->uReplyFreeQueueNextAddressRead++;
+ pThis->uReplyFreeQueueNextAddressRead %= pThis->cReplyQueueEntries;
+
+ PDMCritSectLeave(&pThis->ReplyFreeQueueCritSect);
+
+ /* Build 64bit physical address. */
+ RTGCPHYS GCPhysReplyMessage = LSILOGIC_RTGCPHYS_FROM_U32(pThis->u32HostMFAHighAddr, u32ReplyFrameAddressLow);
+ size_t cbReplyCopied = (pThis->cbReplyFrame < sizeof(MptReplyUnion)) ? pThis->cbReplyFrame : sizeof(MptReplyUnion);
+
+ /* Write reply to guest memory. */
+ PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhysReplyMessage, pReply, cbReplyCopied);
+
+ /* Write low 32bits of reply frame into post reply queue. */
+ rc = PDMCritSectEnter(&pThis->ReplyPostQueueCritSect, VINF_SUCCESS);
+ AssertRC(rc);
+
+ /* Check for a entry in the queue. */
+ if (!lsilogicReplyPostQueueGetFrameCount(pThis))
+ {
+ /* Set error code. */
+ lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
+ return;
+ }
+
+ /* We have a address reply. Set the 31th bit to indicate that. */
+ ASMAtomicWriteU32(&pThis->CTX_SUFF(pReplyPostQueueBase)[pThis->uReplyPostQueueNextEntryFreeWrite],
+ RT_BIT(31) | (u32ReplyFrameAddressLow >> 1));
+ ASMAtomicIncU32(&pThis->uReplyPostQueueNextEntryFreeWrite);
+ pThis->uReplyPostQueueNextEntryFreeWrite %= pThis->cReplyQueueEntries;
+
+ if (fForceReplyFifo)
+ {
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ }
+
+ /* Set interrupt. */
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR);
+
+ PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
+# else
+ AssertMsgFailed(("This is not allowed to happen.\n"));
+# endif
+ }
+}
+
+
+/**
+ * Tries to find a memory region which covers the given address.
+ *
+ * @returns Pointer to memory region or NULL if not found.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param u32Addr The 32bit address to search for.
+ */
+static PLSILOGICMEMREGN lsilogicR3MemRegionFindByAddr(PLSILOGICSCSI pThis, uint32_t u32Addr)
+{
+ PLSILOGICMEMREGN pRegion = NULL;
+
+ PLSILOGICMEMREGN pIt;
+ RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
+ {
+ if ( u32Addr >= pIt->u32AddrStart
+ && u32Addr <= pIt->u32AddrEnd)
+ {
+ pRegion = pIt;
+ break;
+ }
+ }
+
+ return pRegion;
+}
+
+/**
+ * Frees all allocated memory regions.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+static void lsilogicR3MemRegionsFree(PLSILOGICSCSI pThis)
+{
+ PLSILOGICMEMREGN pItNext;
+
+ PLSILOGICMEMREGN pIt;
+ RTListForEachSafe(&pThis->ListMemRegns, pIt, pItNext, LSILOGICMEMREGN, NodeList)
+ {
+ RTListNodeRemove(&pIt->NodeList);
+ RTMemFree(pIt);
+ }
+ pThis->cbMemRegns = 0;
+}
+
+/**
+ * Inserts a given memory region into the list.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pRegion The region to insert.
+ */
+static void lsilogicR3MemRegionInsert(PLSILOGICSCSI pThis, PLSILOGICMEMREGN pRegion)
+{
+ bool fInserted = false;
+
+ /* Insert at the right position. */
+ PLSILOGICMEMREGN pIt;
+ RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
+ {
+ if (pRegion->u32AddrEnd < pIt->u32AddrStart)
+ {
+ RTListNodeInsertBefore(&pIt->NodeList, &pRegion->NodeList);
+ fInserted = true;
+ break;
+ }
+ }
+ if (!fInserted)
+ RTListAppend(&pThis->ListMemRegns, &pRegion->NodeList);
+}
+
+/**
+ * Count number of memory regions.
+ *
+ * @returns Number of memory regions.
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+static uint32_t lsilogicR3MemRegionsCount(PLSILOGICSCSI pThis)
+{
+ uint32_t cRegions = 0;
+
+ PLSILOGICMEMREGN pIt;
+ RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
+ {
+ cRegions++;
+ }
+
+ return cRegions;
+}
+
+/**
+ * Handles a write to the diagnostic data register.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param u32Data Data to write.
+ */
+static void lsilogicR3DiagRegDataWrite(PLSILOGICSCSI pThis, uint32_t u32Data)
+{
+ PLSILOGICMEMREGN pRegion = lsilogicR3MemRegionFindByAddr(pThis, pThis->u32DiagMemAddr);
+
+ if (pRegion)
+ {
+ uint32_t offRegion = pThis->u32DiagMemAddr - pRegion->u32AddrStart;
+
+ AssertMsg( offRegion % 4 == 0
+ && pThis->u32DiagMemAddr <= pRegion->u32AddrEnd,
+ ("Region offset not on a word boundary or crosses memory region\n"));
+
+ offRegion /= 4;
+ pRegion->au32Data[offRegion] = u32Data;
+ }
+ else
+ {
+ pRegion = NULL;
+
+ /* Create new region, first check whether we can extend another region. */
+ PLSILOGICMEMREGN pIt;
+ RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
+ {
+ if (pThis->u32DiagMemAddr == pIt->u32AddrEnd + sizeof(uint32_t))
+ {
+ pRegion = pIt;
+ break;
+ }
+ }
+
+ if (pRegion)
+ {
+ /* Reallocate. */
+ RTListNodeRemove(&pRegion->NodeList);
+
+ uint32_t cRegionSizeOld = (pRegion->u32AddrEnd - pRegion->u32AddrStart) / 4 + 1;
+ uint32_t cRegionSizeNew = cRegionSizeOld + 512;
+
+ if (pThis->cbMemRegns + 512 * sizeof(uint32_t) < LSILOGIC_MEMORY_REGIONS_MAX)
+ {
+ PLSILOGICMEMREGN pRegionNew;
+ pRegionNew = (PLSILOGICMEMREGN)RTMemRealloc(pRegion, RT_UOFFSETOF_DYN(LSILOGICMEMREGN, au32Data[cRegionSizeNew]));
+ if (pRegionNew)
+ {
+ pRegion = pRegionNew;
+ memset(&pRegion->au32Data[cRegionSizeOld], 0, 512 * sizeof(uint32_t));
+ pRegion->au32Data[cRegionSizeOld] = u32Data;
+ pRegion->u32AddrEnd = pRegion->u32AddrStart + (cRegionSizeNew - 1) * sizeof(uint32_t);
+ pThis->cbMemRegns += 512 * sizeof(uint32_t);
+ }
+ /* else: Silently fail, there is nothing we can do here and the guest might work nevertheless. */
+
+ lsilogicR3MemRegionInsert(pThis, pRegion);
+ }
+ }
+ else
+ {
+ if (pThis->cbMemRegns + 512 * sizeof(uint32_t) < LSILOGIC_MEMORY_REGIONS_MAX)
+ {
+ /* Create completely new. */
+ pRegion = (PLSILOGICMEMREGN)RTMemAllocZ(RT_OFFSETOF(LSILOGICMEMREGN, au32Data[512]));
+ if (pRegion)
+ {
+ pRegion->u32AddrStart = pThis->u32DiagMemAddr;
+ pRegion->u32AddrEnd = pRegion->u32AddrStart + (512 - 1) * sizeof(uint32_t);
+ pRegion->au32Data[0] = u32Data;
+ pThis->cbMemRegns += 512 * sizeof(uint32_t);
+
+ lsilogicR3MemRegionInsert(pThis, pRegion);
+ }
+ /* else: Silently fail, there is nothing we can do here and the guest might work nevertheless. */
+ }
+ }
+
+ }
+
+ /* Memory access is always 32bit big. */
+ pThis->u32DiagMemAddr += sizeof(uint32_t);
+}
+
+/**
+ * Handles a read from the diagnostic data register.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pu32Data Where to store the data.
+ */
+static void lsilogicR3DiagRegDataRead(PLSILOGICSCSI pThis, uint32_t *pu32Data)
+{
+ PLSILOGICMEMREGN pRegion = lsilogicR3MemRegionFindByAddr(pThis, pThis->u32DiagMemAddr);
+
+ if (pRegion)
+ {
+ uint32_t offRegion = pThis->u32DiagMemAddr - pRegion->u32AddrStart;
+
+ AssertMsg( offRegion % 4 == 0
+ && pThis->u32DiagMemAddr <= pRegion->u32AddrEnd,
+ ("Region offset not on a word boundary or crosses memory region\n"));
+
+ offRegion /= 4;
+ *pu32Data = pRegion->au32Data[offRegion];
+ }
+ else /* No region, default value 0. */
+ *pu32Data = 0;
+
+ /* Memory access is always 32bit big. */
+ pThis->u32DiagMemAddr += sizeof(uint32_t);
+}
+
+/**
+ * Handles a write to the diagnostic memory address register.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param u32Addr Address to write.
+ */
+static void lsilogicR3DiagRegAddressWrite(PLSILOGICSCSI pThis, uint32_t u32Addr)
+{
+ pThis->u32DiagMemAddr = u32Addr & ~UINT32_C(0x3); /* 32bit alignment. */
+}
+
+/**
+ * Handles a read from the diagnostic memory address register.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pu32Addr Where to store the current address.
+ */
+static void lsilogicR3DiagRegAddressRead(PLSILOGICSCSI pThis, uint32_t *pu32Addr)
+{
+ *pu32Addr = pThis->u32DiagMemAddr;
+}
+
+/**
+ * Processes a given Request from the guest
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pMessageHdr Pointer to the message header of the request.
+ * @param pReply Pointer to the reply.
+ */
+static int lsilogicR3ProcessMessageRequest(PLSILOGICSCSI pThis, PMptMessageHdr pMessageHdr, PMptReplyUnion pReply)
+{
+ int rc = VINF_SUCCESS;
+ bool fForceReplyPostFifo = false;
+
+# ifdef LOG_ENABLED
+ if (pMessageHdr->u8Function < RT_ELEMENTS(g_apszMPTFunctionNames))
+ Log(("Message request function: %s\n", g_apszMPTFunctionNames[pMessageHdr->u8Function]));
+ else
+ Log(("Message request function: <unknown>\n"));
+# endif
+
+ memset(pReply, 0, sizeof(MptReplyUnion));
+
+ switch (pMessageHdr->u8Function)
+ {
+ case MPT_MESSAGE_HDR_FUNCTION_SCSI_TASK_MGMT:
+ {
+ PMptSCSITaskManagementRequest pTaskMgmtReq = (PMptSCSITaskManagementRequest)pMessageHdr;
+
+ LogFlow(("u8TaskType=%u\n", pTaskMgmtReq->u8TaskType));
+ LogFlow(("u32TaskMessageContext=%#x\n", pTaskMgmtReq->u32TaskMessageContext));
+
+ pReply->SCSITaskManagement.u8MessageLength = 6; /* 6 32bit dwords. */
+ pReply->SCSITaskManagement.u8TaskType = pTaskMgmtReq->u8TaskType;
+ pReply->SCSITaskManagement.u32TerminationCount = 0;
+ fForceReplyPostFifo = true;
+ break;
+ }
+ case MPT_MESSAGE_HDR_FUNCTION_IOC_INIT:
+ {
+ /*
+ * This request sets the I/O controller to the
+ * operational state.
+ */
+ PMptIOCInitRequest pIOCInitReq = (PMptIOCInitRequest)pMessageHdr;
+
+ /* Update configuration values. */
+ pThis->enmWhoInit = (LSILOGICWHOINIT)pIOCInitReq->u8WhoInit;
+ pThis->cbReplyFrame = pIOCInitReq->u16ReplyFrameSize;
+ pThis->cMaxBuses = pIOCInitReq->u8MaxBuses;
+ pThis->cMaxDevices = pIOCInitReq->u8MaxDevices;
+ pThis->u32HostMFAHighAddr = pIOCInitReq->u32HostMfaHighAddr;
+ pThis->u32SenseBufferHighAddr = pIOCInitReq->u32SenseBufferHighAddr;
+
+ if (pThis->enmState == LSILOGICSTATE_READY)
+ {
+ pThis->enmState = LSILOGICSTATE_OPERATIONAL;
+ }
+
+ /* Return reply. */
+ pReply->IOCInit.u8MessageLength = 5;
+ pReply->IOCInit.u8WhoInit = pThis->enmWhoInit;
+ pReply->IOCInit.u8MaxDevices = pThis->cMaxDevices;
+ pReply->IOCInit.u8MaxBuses = pThis->cMaxBuses;
+ break;
+ }
+ case MPT_MESSAGE_HDR_FUNCTION_IOC_FACTS:
+ {
+ pReply->IOCFacts.u8MessageLength = 15; /* 15 32bit dwords. */
+
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ {
+ pReply->IOCFacts.u16MessageVersion = 0x0102; /* Version from the specification. */
+ pReply->IOCFacts.u8NumberOfPorts = pThis->cPorts;
+ }
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ {
+ pReply->IOCFacts.u16MessageVersion = 0x0105; /* Version from the specification. */
+ pReply->IOCFacts.u8NumberOfPorts = pThis->cPorts;
+ }
+ else
+ AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
+
+ pReply->IOCFacts.u8IOCNumber = 0; /* PCI function number. */
+ pReply->IOCFacts.u16IOCExceptions = 0;
+ pReply->IOCFacts.u8MaxChainDepth = LSILOGICSCSI_MAXIMUM_CHAIN_DEPTH;
+ pReply->IOCFacts.u8WhoInit = pThis->enmWhoInit;
+ pReply->IOCFacts.u8BlockSize = 12; /* Block size in 32bit dwords. This is the largest request we can get (SCSI I/O). */
+ pReply->IOCFacts.u8Flags = 0; /* Bit 0 is set if the guest must upload the FW prior to using the controller. Obviously not needed here. */
+ pReply->IOCFacts.u16ReplyQueueDepth = pThis->cReplyQueueEntries - 1; /* One entry is always free. */
+ pReply->IOCFacts.u16RequestFrameSize = 128; /** @todo Figure out where it is needed. */
+ pReply->IOCFacts.u32CurrentHostMFAHighAddr = pThis->u32HostMFAHighAddr;
+ pReply->IOCFacts.u16GlobalCredits = pThis->cRequestQueueEntries - 1; /* One entry is always free. */
+
+ pReply->IOCFacts.u8EventState = 0; /* Event notifications not enabled. */
+ pReply->IOCFacts.u32CurrentSenseBufferHighAddr = pThis->u32SenseBufferHighAddr;
+ pReply->IOCFacts.u16CurReplyFrameSize = pThis->cbReplyFrame;
+ pReply->IOCFacts.u8MaxDevices = pThis->cMaxDevices;
+ pReply->IOCFacts.u8MaxBuses = pThis->cMaxBuses;
+
+ /* Check for a valid firmware image in the IOC memory which was downlaoded by tzhe guest earlier. */
+ PLSILOGICMEMREGN pRegion = lsilogicR3MemRegionFindByAddr(pThis, LSILOGIC_FWIMGHDR_LOAD_ADDRESS);
+
+ if (pRegion)
+ {
+ uint32_t offImgHdr = (LSILOGIC_FWIMGHDR_LOAD_ADDRESS - pRegion->u32AddrStart) / 4;
+ PFwImageHdr pFwImgHdr = (PFwImageHdr)&pRegion->au32Data[offImgHdr];
+
+ /* Check for the signature. */
+ /** @todo Checksum validation. */
+ if ( pFwImgHdr->u32Signature1 == LSILOGIC_FWIMGHDR_SIGNATURE1
+ && pFwImgHdr->u32Signature2 == LSILOGIC_FWIMGHDR_SIGNATURE2
+ && pFwImgHdr->u32Signature3 == LSILOGIC_FWIMGHDR_SIGNATURE3)
+ {
+ LogFlowFunc(("IOC Facts: Found valid firmware image header in memory, using version (%#x), size (%d) and product ID (%#x) from there\n",
+ pFwImgHdr->u32FwVersion, pFwImgHdr->u32ImageSize, pFwImgHdr->u16ProductId));
+
+ pReply->IOCFacts.u16ProductID = pFwImgHdr->u16ProductId;
+ pReply->IOCFacts.u32FwImageSize = pFwImgHdr->u32ImageSize;
+ pReply->IOCFacts.u32FWVersion = pFwImgHdr->u32FwVersion;
+ }
+ }
+ else
+ {
+ pReply->IOCFacts.u16ProductID = 0xcafe; /* Our own product ID :) */
+ pReply->IOCFacts.u32FwImageSize = 0; /* No image needed. */
+ pReply->IOCFacts.u32FWVersion = 0;
+ }
+ break;
+ }
+ case MPT_MESSAGE_HDR_FUNCTION_PORT_FACTS:
+ {
+ PMptPortFactsRequest pPortFactsReq = (PMptPortFactsRequest)pMessageHdr;
+
+ pReply->PortFacts.u8MessageLength = 10;
+ pReply->PortFacts.u8PortNumber = pPortFactsReq->u8PortNumber;
+
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ {
+ /* This controller only supports one bus with bus number 0. */
+ if (pPortFactsReq->u8PortNumber >= pThis->cPorts)
+ {
+ pReply->PortFacts.u8PortType = 0; /* Not existant. */
+ }
+ else
+ {
+ pReply->PortFacts.u8PortType = 0x01; /* SCSI Port. */
+ pReply->PortFacts.u16MaxDevices = LSILOGICSCSI_PCI_SPI_DEVICES_PER_BUS_MAX;
+ pReply->PortFacts.u16ProtocolFlags = RT_BIT(3) | RT_BIT(0); /* SCSI initiator and LUN supported. */
+ pReply->PortFacts.u16PortSCSIID = 7; /* Default */
+ pReply->PortFacts.u16MaxPersistentIDs = 0;
+ pReply->PortFacts.u16MaxPostedCmdBuffers = 0; /* Only applies for target mode which we dont support. */
+ pReply->PortFacts.u16MaxLANBuckets = 0; /* Only for the LAN controller. */
+ }
+ }
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ {
+ if (pPortFactsReq->u8PortNumber >= pThis->cPorts)
+ {
+ pReply->PortFacts.u8PortType = 0; /* Not existant. */
+ }
+ else
+ {
+ pReply->PortFacts.u8PortType = 0x30; /* SAS Port. */
+ pReply->PortFacts.u16MaxDevices = pThis->cPorts;
+ pReply->PortFacts.u16ProtocolFlags = RT_BIT(3) | RT_BIT(0); /* SCSI initiator and LUN supported. */
+ pReply->PortFacts.u16PortSCSIID = pThis->cPorts;
+ pReply->PortFacts.u16MaxPersistentIDs = 0;
+ pReply->PortFacts.u16MaxPostedCmdBuffers = 0; /* Only applies for target mode which we dont support. */
+ pReply->PortFacts.u16MaxLANBuckets = 0; /* Only for the LAN controller. */
+ }
+ }
+ else
+ AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
+ break;
+ }
+ case MPT_MESSAGE_HDR_FUNCTION_PORT_ENABLE:
+ {
+ /*
+ * The port enable request notifies the IOC to make the port available and perform
+ * appropriate discovery on the associated link.
+ */
+ PMptPortEnableRequest pPortEnableReq = (PMptPortEnableRequest)pMessageHdr;
+
+ pReply->PortEnable.u8MessageLength = 5;
+ pReply->PortEnable.u8PortNumber = pPortEnableReq->u8PortNumber;
+ break;
+ }
+ case MPT_MESSAGE_HDR_FUNCTION_EVENT_NOTIFICATION:
+ {
+ PMptEventNotificationRequest pEventNotificationReq = (PMptEventNotificationRequest)pMessageHdr;
+
+ if (pEventNotificationReq->u8Switch)
+ pThis->fEventNotificationEnabled = true;
+ else
+ pThis->fEventNotificationEnabled = false;
+
+ pReply->EventNotification.u16EventDataLength = 1; /* 1 32bit D-Word. */
+ pReply->EventNotification.u8MessageLength = 8;
+ pReply->EventNotification.u8MessageFlags = (1 << 7);
+ pReply->EventNotification.u8AckRequired = 0;
+ pReply->EventNotification.u32Event = MPT_EVENT_EVENT_CHANGE;
+ pReply->EventNotification.u32EventContext = 0;
+ pReply->EventNotification.u32EventData = pThis->fEventNotificationEnabled ? 1 : 0;
+
+ break;
+ }
+ case MPT_MESSAGE_HDR_FUNCTION_EVENT_ACK:
+ {
+ AssertMsgFailed(("todo"));
+ break;
+ }
+ case MPT_MESSAGE_HDR_FUNCTION_CONFIG:
+ {
+ PMptConfigurationRequest pConfigurationReq = (PMptConfigurationRequest)pMessageHdr;
+
+ rc = lsilogicR3ProcessConfigurationRequest(pThis, pConfigurationReq, &pReply->Configuration);
+ AssertRC(rc);
+ break;
+ }
+ case MPT_MESSAGE_HDR_FUNCTION_FW_UPLOAD:
+ {
+ PMptFWUploadRequest pFWUploadReq = (PMptFWUploadRequest)pMessageHdr;
+
+ pReply->FWUpload.u8ImageType = pFWUploadReq->u8ImageType;
+ pReply->FWUpload.u8MessageLength = 6;
+ pReply->FWUpload.u32ActualImageSize = 0;
+ break;
+ }
+ case MPT_MESSAGE_HDR_FUNCTION_FW_DOWNLOAD:
+ {
+ //PMptFWDownloadRequest pFWDownloadReq = (PMptFWDownloadRequest)pMessageHdr;
+
+ pReply->FWDownload.u8MessageLength = 5;
+ LogFlowFunc(("FW Download request issued\n"));
+ break;
+ }
+ case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST: /* Should be handled already. */
+ default:
+ AssertMsgFailed(("Invalid request function %#x\n", pMessageHdr->u8Function));
+ }
+
+ /* Copy common bits from request message frame to reply. */
+ pReply->Header.u8Function = pMessageHdr->u8Function;
+ pReply->Header.u32MessageContext = pMessageHdr->u32MessageContext;
+
+ lsilogicFinishAddressReply(pThis, pReply, fForceReplyPostFifo);
+ return rc;
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * Writes a value to a register at a given offset.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param offReg Offset of the register to write.
+ * @param u32 The value being written.
+ */
+static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t offReg, uint32_t u32)
+{
+ LogFlowFunc(("pThis=%#p offReg=%#x u32=%#x\n", pThis, offReg, u32));
+ switch (offReg)
+ {
+ case LSILOGIC_REG_REPLY_QUEUE:
+ {
+ int rc = PDMCritSectEnter(&pThis->ReplyFreeQueueWriteCritSect, VINF_IOM_R3_MMIO_WRITE);
+ if (rc != VINF_SUCCESS)
+ return rc;
+ /* Add the entry to the reply free queue. */
+ ASMAtomicWriteU32(&pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextEntryFreeWrite], u32);
+ pThis->uReplyFreeQueueNextEntryFreeWrite++;
+ pThis->uReplyFreeQueueNextEntryFreeWrite %= pThis->cReplyQueueEntries;
+ PDMCritSectLeave(&pThis->ReplyFreeQueueWriteCritSect);
+ break;
+ }
+ case LSILOGIC_REG_REQUEST_QUEUE:
+ {
+ int rc = PDMCritSectEnter(&pThis->RequestQueueCritSect, VINF_IOM_R3_MMIO_WRITE);
+ if (rc != VINF_SUCCESS)
+ return rc;
+
+ uint32_t uNextWrite = ASMAtomicReadU32(&pThis->uRequestQueueNextEntryFreeWrite);
+
+ ASMAtomicWriteU32(&pThis->CTX_SUFF(pRequestQueueBase)[uNextWrite], u32);
+
+ /*
+ * Don't update the value in place. It can happen that we get preempted
+ * after the increment but before the modulo.
+ * Another EMT will read the wrong value when processing the queues
+ * and hang in an endless loop creating thousands of requests.
+ */
+ uNextWrite++;
+ uNextWrite %= pThis->cRequestQueueEntries;
+ ASMAtomicWriteU32(&pThis->uRequestQueueNextEntryFreeWrite, uNextWrite);
+ PDMCritSectLeave(&pThis->RequestQueueCritSect);
+
+ /* Send notification to R3 if there is not one sent already. Do this
+ * only if the worker thread is not sleeping or might go sleeping. */
+ if (!ASMAtomicXchgBool(&pThis->fNotificationSent, true))
+ {
+ if (ASMAtomicReadBool(&pThis->fWrkThreadSleeping))
+ {
+#ifdef IN_RC
+ PPDMQUEUEITEMCORE pNotificationItem = PDMQueueAlloc(pThis->CTX_SUFF(pNotificationQueue));
+ AssertPtr(pNotificationItem);
+ PDMQueueInsert(pThis->CTX_SUFF(pNotificationQueue), pNotificationItem);
+#else
+ LogFlowFunc(("Signal event semaphore\n"));
+ rc = SUPSemEventSignal(pThis->pSupDrvSession, pThis->hEvtProcess);
+ AssertRC(rc);
+#endif
+ }
+ }
+ break;
+ }
+ case LSILOGIC_REG_DOORBELL:
+ {
+ /*
+ * When the guest writes to this register a real device would set the
+ * doorbell status bit in the interrupt status register to indicate that the IOP
+ * has still to process the message.
+ * The guest needs to wait with posting new messages here until the bit is cleared.
+ * Because the guest is not continuing execution while we are here we can skip this.
+ */
+ if (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_NOT_IN_USE)
+ {
+ uint32_t uFunction = LSILOGIC_REG_DOORBELL_GET_FUNCTION(u32);
+
+ switch (uFunction)
+ {
+ case LSILOGIC_DOORBELL_FUNCTION_IO_UNIT_RESET:
+ case LSILOGIC_DOORBELL_FUNCTION_IOC_MSG_UNIT_RESET:
+ {
+ /*
+ * The I/O unit reset does much more on real hardware like
+ * reloading the firmware, nothing we need to do here,
+ * so this is like the IOC message unit reset.
+ */
+ pThis->enmState = LSILOGICSTATE_RESET;
+
+ /* Reset interrupt status. */
+ pThis->uInterruptStatus = 0;
+ lsilogicUpdateInterrupt(pThis);
+
+ /* Reset the queues. */
+ pThis->uReplyFreeQueueNextEntryFreeWrite = 0;
+ pThis->uReplyFreeQueueNextAddressRead = 0;
+ pThis->uReplyPostQueueNextEntryFreeWrite = 0;
+ pThis->uReplyPostQueueNextAddressRead = 0;
+ pThis->uRequestQueueNextEntryFreeWrite = 0;
+ pThis->uRequestQueueNextAddressRead = 0;
+
+ /* Only the IOC message unit reset transisionts to the ready state. */
+ if (uFunction == LSILOGIC_DOORBELL_FUNCTION_IOC_MSG_UNIT_RESET)
+ pThis->enmState = LSILOGICSTATE_READY;
+ break;
+ }
+ case LSILOGIC_DOORBELL_FUNCTION_HANDSHAKE:
+ {
+ pThis->cMessage = LSILOGIC_REG_DOORBELL_GET_SIZE(u32);
+ pThis->iMessage = 0;
+ AssertMsg(pThis->cMessage <= RT_ELEMENTS(pThis->aMessage),
+ ("Message doesn't fit into the buffer, cMessage=%u", pThis->cMessage));
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_FN_HANDSHAKE;
+ /* Update the interrupt status to notify the guest that a doorbell function was started. */
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ break;
+ }
+ case LSILOGIC_DOORBELL_FUNCTION_REPLY_FRAME_REMOVAL:
+ {
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_LOW;
+ /* Update the interrupt status to notify the guest that a doorbell function was started. */
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ break;
+ }
+ default:
+ AssertMsgFailed(("Unknown function %u to perform\n", uFunction));
+ }
+ }
+ else if (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_FN_HANDSHAKE)
+ {
+ /*
+ * We are already performing a doorbell function.
+ * Get the remaining parameters.
+ */
+ AssertMsg(pThis->iMessage < RT_ELEMENTS(pThis->aMessage), ("Message is too big to fit into the buffer\n"));
+ /*
+ * If the last byte of the message is written, force a switch to R3 because some requests might force
+ * a reply through the FIFO which cannot be handled in GC or R0.
+ */
+#ifndef IN_RING3
+ if (pThis->iMessage == pThis->cMessage - 1)
+ return VINF_IOM_R3_MMIO_WRITE;
+#endif
+ pThis->aMessage[pThis->iMessage++] = u32;
+#ifdef IN_RING3
+ if (pThis->iMessage == pThis->cMessage)
+ {
+ int rc = lsilogicR3ProcessMessageRequest(pThis, (PMptMessageHdr)pThis->aMessage, &pThis->ReplyBuffer);
+ AssertRC(rc);
+ }
+#endif
+ }
+ break;
+ }
+ case LSILOGIC_REG_HOST_INTR_STATUS:
+ {
+ /*
+ * Clear the bits the guest wants except the system doorbell interrupt and the IO controller
+ * status bit.
+ * The former bit is always cleared no matter what the guest writes to the register and
+ * the latter one is read only.
+ */
+ ASMAtomicAndU32(&pThis->uInterruptStatus, ~LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+
+ /*
+ * Check if there is still a doorbell function in progress. Set the
+ * system doorbell interrupt bit again if it is.
+ * We do not use lsilogicSetInterrupt here because the interrupt status
+ * is updated afterwards anyway.
+ */
+ if ( (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_FN_HANDSHAKE)
+ && (pThis->cMessage == pThis->iMessage))
+ {
+ if (pThis->uNextReplyEntryRead == pThis->cReplySize)
+ {
+ /* Reply finished. Reset doorbell in progress status. */
+ Log(("%s: Doorbell function finished\n", __FUNCTION__));
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
+ }
+ ASMAtomicOrU32(&pThis->uInterruptStatus, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ }
+ else if ( pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_NOT_IN_USE
+ && pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_FN_HANDSHAKE)
+ {
+ /* Reply frame removal, check whether the reply free queue is empty. */
+ if ( pThis->uReplyFreeQueueNextAddressRead == pThis->uReplyFreeQueueNextEntryFreeWrite
+ && pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW)
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
+ ASMAtomicOrU32(&pThis->uInterruptStatus, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ }
+
+ lsilogicUpdateInterrupt(pThis);
+ break;
+ }
+ case LSILOGIC_REG_HOST_INTR_MASK:
+ {
+ ASMAtomicWriteU32(&pThis->uInterruptMask, u32 & LSILOGIC_REG_HOST_INTR_MASK_W_MASK);
+ lsilogicUpdateInterrupt(pThis);
+ break;
+ }
+ case LSILOGIC_REG_WRITE_SEQUENCE:
+ {
+ if (pThis->fDiagnosticEnabled)
+ {
+ /* Any value will cause a reset and disabling access. */
+ pThis->fDiagnosticEnabled = false;
+ pThis->iDiagnosticAccess = 0;
+ pThis->fDiagRegsEnabled = false;
+ }
+ else if ((u32 & 0xf) == g_lsilogicDiagnosticAccess[pThis->iDiagnosticAccess])
+ {
+ pThis->iDiagnosticAccess++;
+ if (pThis->iDiagnosticAccess == RT_ELEMENTS(g_lsilogicDiagnosticAccess))
+ {
+ /*
+ * Key sequence successfully written. Enable access to diagnostic
+ * memory and register.
+ */
+ pThis->fDiagnosticEnabled = true;
+ }
+ }
+ else
+ {
+ /* Wrong value written - reset to beginning. */
+ pThis->iDiagnosticAccess = 0;
+ }
+ break;
+ }
+ case LSILOGIC_REG_HOST_DIAGNOSTIC:
+ {
+ if (pThis->fDiagnosticEnabled)
+ {
+#ifndef IN_RING3
+ return VINF_IOM_R3_MMIO_WRITE;
+#else
+ if (u32 & LSILOGIC_REG_HOST_DIAGNOSTIC_RESET_ADAPTER)
+ lsilogicR3HardReset(pThis);
+ else if (u32 & LSILOGIC_REG_HOST_DIAGNOSTIC_DIAG_RW_ENABLE)
+ pThis->fDiagRegsEnabled = true;
+#endif
+ }
+ break;
+ }
+ case LSILOGIC_REG_DIAG_RW_DATA:
+ {
+ if (pThis->fDiagRegsEnabled)
+ {
+#ifndef IN_RING3
+ return VINF_IOM_R3_MMIO_WRITE;
+#else
+ lsilogicR3DiagRegDataWrite(pThis, u32);
+#endif
+ }
+ break;
+ }
+ case LSILOGIC_REG_DIAG_RW_ADDRESS:
+ {
+ if (pThis->fDiagRegsEnabled)
+ {
+#ifndef IN_RING3
+ return VINF_IOM_R3_MMIO_WRITE;
+#else
+ lsilogicR3DiagRegAddressWrite(pThis, u32);
+#endif
+ }
+ break;
+ }
+ default: /* Ignore. */
+ {
+ break;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Reads the content of a register at a given offset.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param offReg Offset of the register to read.
+ * @param pu32 Where to store the content of the register.
+ */
+static int lsilogicRegisterRead(PLSILOGICSCSI pThis, uint32_t offReg, uint32_t *pu32)
+{
+ int rc = VINF_SUCCESS;
+ uint32_t u32 = 0;
+ Assert(!(offReg & 3));
+
+ /* Align to a 4 byte offset. */
+ switch (offReg)
+ {
+ case LSILOGIC_REG_REPLY_QUEUE:
+ {
+ rc = PDMCritSectEnter(&pThis->ReplyPostQueueCritSect, VINF_IOM_R3_MMIO_READ);
+ if (rc != VINF_SUCCESS)
+ break;
+
+ uint32_t idxReplyPostQueueWrite = ASMAtomicUoReadU32(&pThis->uReplyPostQueueNextEntryFreeWrite);
+ uint32_t idxReplyPostQueueRead = ASMAtomicUoReadU32(&pThis->uReplyPostQueueNextAddressRead);
+
+ if (idxReplyPostQueueWrite != idxReplyPostQueueRead)
+ {
+ u32 = pThis->CTX_SUFF(pReplyPostQueueBase)[idxReplyPostQueueRead];
+ idxReplyPostQueueRead++;
+ idxReplyPostQueueRead %= pThis->cReplyQueueEntries;
+ ASMAtomicWriteU32(&pThis->uReplyPostQueueNextAddressRead, idxReplyPostQueueRead);
+ }
+ else
+ {
+ /* The reply post queue is empty. Reset interrupt. */
+ u32 = UINT32_C(0xffffffff);
+ lsilogicClearInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR);
+ }
+ PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
+
+ Log(("%s: Returning address %#x\n", __FUNCTION__, u32));
+ break;
+ }
+ case LSILOGIC_REG_DOORBELL:
+ {
+ u32 = LSILOGIC_REG_DOORBELL_SET_STATE(pThis->enmState);
+ u32 |= LSILOGIC_REG_DOORBELL_SET_USED(pThis->enmDoorbellState);
+ u32 |= LSILOGIC_REG_DOORBELL_SET_WHOINIT(pThis->enmWhoInit);
+ /*
+ * If there is a doorbell function in progress we pass the return value
+ * instead of the status code. We transfer 16bit of the reply
+ * during one read.
+ */
+ switch (pThis->enmDoorbellState)
+ {
+ case LSILOGICDOORBELLSTATE_NOT_IN_USE:
+ /* We return the status code of the I/O controller. */
+ u32 |= pThis->u16IOCFaultCode;
+ break;
+ case LSILOGICDOORBELLSTATE_FN_HANDSHAKE:
+ /* Return next 16bit value. */
+ if (pThis->uNextReplyEntryRead < pThis->cReplySize)
+ u32 |= pThis->ReplyBuffer.au16Reply[pThis->uNextReplyEntryRead++];
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ break;
+ case LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_LOW:
+ {
+ uint32_t cReplyFrames = lsilogicReplyFreeQueueGetFrameCount(pThis);
+
+ u32 |= cReplyFrames & UINT32_C(0xffff);
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_HIGH;
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ break;
+ }
+ case LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_HIGH:
+ {
+ uint32_t cReplyFrames = lsilogicReplyFreeQueueGetFrameCount(pThis);
+
+ u32 |= cReplyFrames >> 16;
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW;
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ break;
+ }
+ case LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW:
+ if (pThis->uReplyFreeQueueNextEntryFreeWrite != pThis->uReplyFreeQueueNextAddressRead)
+ {
+ u32 |= pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead] & UINT32_C(0xffff);
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_HIGH;
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ }
+ break;
+ case LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_HIGH:
+ u32 |= pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead] >> 16;
+ pThis->uReplyFreeQueueNextAddressRead++;
+ pThis->uReplyFreeQueueNextAddressRead %= pThis->cReplyQueueEntries;
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW;
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ break;
+ default:
+ AssertMsgFailed(("Invalid doorbell state %d\n", pThis->enmDoorbellState));
+ }
+
+ break;
+ }
+ case LSILOGIC_REG_HOST_INTR_STATUS:
+ {
+ u32 = ASMAtomicReadU32(&pThis->uInterruptStatus);
+ break;
+ }
+ case LSILOGIC_REG_HOST_INTR_MASK:
+ {
+ u32 = ASMAtomicReadU32(&pThis->uInterruptMask);
+ break;
+ }
+ case LSILOGIC_REG_HOST_DIAGNOSTIC:
+ {
+ if (pThis->fDiagnosticEnabled)
+ u32 |= LSILOGIC_REG_HOST_DIAGNOSTIC_DRWE;
+ if (pThis->fDiagRegsEnabled)
+ u32 |= LSILOGIC_REG_HOST_DIAGNOSTIC_DIAG_RW_ENABLE;
+ break;
+ }
+ case LSILOGIC_REG_DIAG_RW_DATA:
+ {
+ if (pThis->fDiagRegsEnabled)
+ {
+#ifndef IN_RING3
+ return VINF_IOM_R3_MMIO_READ;
+#else
+ lsilogicR3DiagRegDataRead(pThis, &u32);
+#endif
+ }
+ }
+ RT_FALL_THRU();
+ case LSILOGIC_REG_DIAG_RW_ADDRESS:
+ {
+ if (pThis->fDiagRegsEnabled)
+ {
+#ifndef IN_RING3
+ return VINF_IOM_R3_MMIO_READ;
+#else
+ lsilogicR3DiagRegAddressRead(pThis, &u32);
+#endif
+ }
+ }
+ RT_FALL_THRU();
+ case LSILOGIC_REG_TEST_BASE_ADDRESS: /* The spec doesn't say anything about these registers, so we just ignore them */
+ default: /* Ignore. */
+ {
+ /** @todo LSILOGIC_REG_DIAG_* should return all F's when accessed by MMIO. We
+ * return 0. Likely to apply to undefined offsets as well. */
+ break;
+ }
+ }
+
+ *pu32 = u32;
+ LogFlowFunc(("pThis=%#p offReg=%#x u32=%#x\n", pThis, offReg, u32));
+ return rc;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+PDMBOTHCBDECL(int) lsilogicIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ uint32_t offReg = uPort - pThis->IOPortBase;
+ int rc;
+ RT_NOREF2(pvUser, cb);
+
+ if (!(offReg & 3))
+ {
+ rc = lsilogicRegisterWrite(pThis, offReg, u32);
+ if (rc == VINF_IOM_R3_MMIO_WRITE)
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+ }
+ else
+ {
+ Log(("lsilogicIOPortWrite: Ignoring misaligned write - offReg=%#x u32=%#x cb=%#x\n", offReg, u32, cb));
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN}
+ */
+PDMBOTHCBDECL(int) lsilogicIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t *pu32, unsigned cb)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ uint32_t offReg = uPort - pThis->IOPortBase;
+ RT_NOREF_PV(pvUser);
+ RT_NOREF_PV(cb);
+
+ int rc = lsilogicRegisterRead(pThis, offReg & ~(uint32_t)3, pu32);
+ if (rc == VINF_IOM_R3_MMIO_READ)
+ rc = VINF_IOM_R3_IOPORT_READ;
+
+ return rc;
+}
+
+/**
+ * @callback_method_impl{FNIOMMMIOWRITE}
+ */
+PDMBOTHCBDECL(int) lsilogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ uint32_t offReg = GCPhysAddr - pThis->GCPhysMMIOBase;
+ uint32_t u32;
+ int rc;
+ RT_NOREF_PV(pvUser);
+
+ /* See comments in lsilogicR3Map regarding size and alignment. */
+ if (cb == 4)
+ u32 = *(uint32_t const *)pv;
+ else
+ {
+ if (cb > 4)
+ u32 = *(uint32_t const *)pv;
+ else if (cb >= 2)
+ u32 = *(uint16_t const *)pv;
+ else
+ u32 = *(uint8_t const *)pv;
+ Log(("lsilogicMMIOWrite: Non-DWORD write access - offReg=%#x u32=%#x cb=%#x\n", offReg, u32, cb));
+ }
+
+ if (!(offReg & 3))
+ rc = lsilogicRegisterWrite(pThis, offReg, u32);
+ else
+ {
+ Log(("lsilogicIOPortWrite: Ignoring misaligned write - offReg=%#x u32=%#x cb=%#x\n", offReg, u32, cb));
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+}
+
+/**
+ * @callback_method_impl{FNIOMMMIOREAD}
+ */
+PDMBOTHCBDECL(int) lsilogicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ uint32_t offReg = GCPhysAddr - pThis->GCPhysMMIOBase;
+ Assert(!(offReg & 3)); Assert(cb == 4);
+ RT_NOREF2(pvUser, cb);
+
+ return lsilogicRegisterRead(pThis, offReg, (uint32_t *)pv);
+}
+
+PDMBOTHCBDECL(int) lsilogicDiagnosticWrite(PPDMDEVINS pDevIns, void *pvUser,
+ RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
+{
+#ifdef LOG_ENABLED
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ LogFlowFunc(("pThis=%#p GCPhysAddr=%RGp pv=%#p{%.*Rhxs} cb=%u\n", pThis, GCPhysAddr, pv, cb, pv, cb));
+#endif
+
+ RT_NOREF_PV(pDevIns); RT_NOREF_PV(pvUser); RT_NOREF_PV(GCPhysAddr); RT_NOREF_PV(pv); RT_NOREF_PV(cb);
+ return VINF_SUCCESS;
+}
+
+PDMBOTHCBDECL(int) lsilogicDiagnosticRead(PPDMDEVINS pDevIns, void *pvUser,
+ RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
+{
+#ifdef LOG_ENABLED
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ LogFlowFunc(("pThis=%#p GCPhysAddr=%RGp pv=%#p{%.*Rhxs} cb=%u\n", pThis, GCPhysAddr, pv, cb, pv, cb));
+#endif
+
+ RT_NOREF_PV(pDevIns); RT_NOREF_PV(pvUser); RT_NOREF_PV(GCPhysAddr); RT_NOREF_PV(pv); RT_NOREF_PV(cb);
+ return VINF_SUCCESS;
+}
+
+#ifdef IN_RING3
+
+# ifdef LOG_ENABLED
+/**
+ * Dump an SG entry.
+ *
+ * @returns nothing.
+ * @param pSGEntry Pointer to the SG entry to dump
+ */
+static void lsilogicDumpSGEntry(PMptSGEntryUnion pSGEntry)
+{
+ if (LogIsEnabled())
+ {
+ switch (pSGEntry->Simple32.u2ElementType)
+ {
+ case MPTSGENTRYTYPE_SIMPLE:
+ {
+ Log(("%s: Dumping info for SIMPLE SG entry:\n", __FUNCTION__));
+ Log(("%s: u24Length=%u\n", __FUNCTION__, pSGEntry->Simple32.u24Length));
+ Log(("%s: fEndOfList=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfList));
+ Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.f64BitAddress));
+ Log(("%s: fBufferContainsData=%d\n", __FUNCTION__, pSGEntry->Simple32.fBufferContainsData));
+ Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.fLocalAddress));
+ Log(("%s: fEndOfBuffer=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfBuffer));
+ Log(("%s: fLastElement=%d\n", __FUNCTION__, pSGEntry->Simple32.fLastElement));
+ Log(("%s: u32DataBufferAddressLow=%u\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow));
+ if (pSGEntry->Simple32.f64BitAddress)
+ {
+ Log(("%s: u32DataBufferAddressHigh=%u\n", __FUNCTION__, pSGEntry->Simple64.u32DataBufferAddressHigh));
+ Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__,
+ ((uint64_t)pSGEntry->Simple64.u32DataBufferAddressHigh << 32)
+ | pSGEntry->Simple64.u32DataBufferAddressLow));
+ }
+ else
+ Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow));
+
+ break;
+ }
+ case MPTSGENTRYTYPE_CHAIN:
+ {
+ Log(("%s: Dumping info for CHAIN SG entry:\n", __FUNCTION__));
+ Log(("%s: u16Length=%u\n", __FUNCTION__, pSGEntry->Chain.u16Length));
+ Log(("%s: u8NExtChainOffset=%d\n", __FUNCTION__, pSGEntry->Chain.u8NextChainOffset));
+ Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Chain.f64BitAddress));
+ Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Chain.fLocalAddress));
+ Log(("%s: u32SegmentAddressLow=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow));
+ Log(("%s: u32SegmentAddressHigh=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressHigh));
+ if (pSGEntry->Chain.f64BitAddress)
+ Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__,
+ ((uint64_t)pSGEntry->Chain.u32SegmentAddressHigh << 32) | pSGEntry->Chain.u32SegmentAddressLow));
+ else
+ Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow));
+ break;
+ }
+ }
+ }
+}
+# endif /* LOG_ENABLED */
+
+/**
+ * Copy from guest to host memory worker.
+ *
+ * @copydoc LSILOGICR3MEMCOPYCALLBACK
+ */
+static DECLCALLBACK(void) lsilogicR3CopyBufferFromGuestWorker(PLSILOGICSCSI pThis, RTGCPHYS GCPhys, PRTSGBUF pSgBuf,
+ size_t cbCopy, size_t *pcbSkip)
+{
+ size_t cbSkipped = RT_MIN(cbCopy, *pcbSkip);
+ cbCopy -= cbSkipped;
+ GCPhys += cbSkipped;
+ *pcbSkip -= cbSkipped;
+
+ while (cbCopy)
+ {
+ size_t cbSeg = cbCopy;
+ void *pvSeg = RTSgBufGetNextSegment(pSgBuf, &cbSeg);
+
+ AssertPtr(pvSeg);
+ PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCPhys, pvSeg, cbSeg);
+ GCPhys += cbSeg;
+ cbCopy -= cbSeg;
+ }
+}
+
+/**
+ * Copy from host to guest memory worker.
+ *
+ * @copydoc LSILOGICR3MEMCOPYCALLBACK
+ */
+static DECLCALLBACK(void) lsilogicR3CopyBufferToGuestWorker(PLSILOGICSCSI pThis, RTGCPHYS GCPhys, PRTSGBUF pSgBuf,
+ size_t cbCopy, size_t *pcbSkip)
+{
+ size_t cbSkipped = RT_MIN(cbCopy, *pcbSkip);
+ cbCopy -= cbSkipped;
+ GCPhys += cbSkipped;
+ *pcbSkip -= cbSkipped;
+
+ while (cbCopy)
+ {
+ size_t cbSeg = cbCopy;
+ void *pvSeg = RTSgBufGetNextSegment(pSgBuf, &cbSeg);
+
+ AssertPtr(pvSeg);
+ PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhys, pvSeg, cbSeg);
+ GCPhys += cbSeg;
+ cbCopy -= cbSeg;
+ }
+}
+
+/**
+ * Walks the guest S/G buffer calling the given copy worker for every buffer.
+ *
+ * @returns The amout of bytes actually copied.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pLsiReq LSI request state.
+ * @param pfnCopyWorker The copy method to apply for each guest buffer.
+ * @param pSgBuf The host S/G buffer.
+ * @param cbSkip How many bytes to skip in advance before starting to copy.
+ * @param cbCopy How many bytes to copy.
+ */
+static size_t lsilogicSgBufWalker(PLSILOGICSCSI pThis, PLSILOGICREQ pLsiReq,
+ PLSILOGICR3MEMCOPYCALLBACK pfnCopyWorker,
+ PRTSGBUF pSgBuf, size_t cbSkip, size_t cbCopy)
+{
+ bool fEndOfList = false;
+ RTGCPHYS GCPhysSgEntryNext = pLsiReq->GCPhysSgStart;
+ RTGCPHYS GCPhysSegmentStart = pLsiReq->GCPhysSgStart;
+ uint32_t cChainOffsetNext = pLsiReq->cChainOffset;
+ PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
+ size_t cbCopied = 0;
+
+ /*
+ * Add the amount to skip to the host buffer size to avoid a
+ * few conditionals later on.
+ */
+ cbCopy += cbSkip;
+
+ /* Go through the list until we reach the end. */
+ while ( !fEndOfList
+ && cbCopy)
+ {
+ bool fEndOfSegment = false;
+
+ while ( !fEndOfSegment
+ && cbCopy)
+ {
+ MptSGEntryUnion SGEntry;
+
+ Log(("%s: Reading SG entry from %RGp\n", __FUNCTION__, GCPhysSgEntryNext));
+
+ /* Read the entry. */
+ PDMDevHlpPhysRead(pDevIns, GCPhysSgEntryNext, &SGEntry, sizeof(MptSGEntryUnion));
+
+# ifdef LOG_ENABLED
+ lsilogicDumpSGEntry(&SGEntry);
+# endif
+
+ AssertMsg(SGEntry.Simple32.u2ElementType == MPTSGENTRYTYPE_SIMPLE, ("Invalid SG entry type\n"));
+
+ /* Check if this is a zero element and abort. */
+ if ( !SGEntry.Simple32.u24Length
+ && SGEntry.Simple32.fEndOfList
+ && SGEntry.Simple32.fEndOfBuffer)
+ return cbCopied - RT_MIN(cbSkip, cbCopied);
+
+ uint32_t cbCopyThis = SGEntry.Simple32.u24Length;
+ RTGCPHYS GCPhysAddrDataBuffer = SGEntry.Simple32.u32DataBufferAddressLow;
+
+ if (SGEntry.Simple32.f64BitAddress)
+ {
+ GCPhysAddrDataBuffer |= ((uint64_t)SGEntry.Simple64.u32DataBufferAddressHigh) << 32;
+ GCPhysSgEntryNext += sizeof(MptSGEntrySimple64);
+ }
+ else
+ GCPhysSgEntryNext += sizeof(MptSGEntrySimple32);
+
+ pfnCopyWorker(pThis, GCPhysAddrDataBuffer, pSgBuf, cbCopyThis, &cbSkip);
+ cbCopy -= cbCopyThis;
+ cbCopied += cbCopyThis;
+
+ /* Check if we reached the end of the list. */
+ if (SGEntry.Simple32.fEndOfList)
+ {
+ /* We finished. */
+ fEndOfSegment = true;
+ fEndOfList = true;
+ }
+ else if (SGEntry.Simple32.fLastElement)
+ fEndOfSegment = true;
+ } /* while (!fEndOfSegment) */
+
+ /* Get next chain element. */
+ if (cChainOffsetNext)
+ {
+ MptSGEntryChain SGEntryChain;
+
+ PDMDevHlpPhysRead(pDevIns, GCPhysSegmentStart + cChainOffsetNext, &SGEntryChain, sizeof(MptSGEntryChain));
+
+ AssertMsg(SGEntryChain.u2ElementType == MPTSGENTRYTYPE_CHAIN, ("Invalid SG entry type\n"));
+
+ /* Set the next address now. */
+ GCPhysSgEntryNext = SGEntryChain.u32SegmentAddressLow;
+ if (SGEntryChain.f64BitAddress)
+ GCPhysSgEntryNext |= ((uint64_t)SGEntryChain.u32SegmentAddressHigh) << 32;
+
+ GCPhysSegmentStart = GCPhysSgEntryNext;
+ cChainOffsetNext = SGEntryChain.u8NextChainOffset * sizeof(uint32_t);
+ }
+ } /* while (!fEndOfList) */
+
+ return cbCopied - RT_MIN(cbSkip, cbCopied);
+}
+
+/**
+ * Copies a data buffer into the S/G buffer set up by the guest.
+ *
+ * @returns Amount of bytes copied to the guest.
+ * @param pThis The LsiLogic controller device instance.
+ * @param pReq Request structure.
+ * @param pSgBuf The S/G buffer to copy from.
+ * @param cbSkip How many bytes to skip in advance before starting to copy.
+ * @param cbCopy How many bytes to copy.
+ */
+static size_t lsilogicR3CopySgBufToGuest(PLSILOGICSCSI pThis, PLSILOGICREQ pReq, PRTSGBUF pSgBuf,
+ size_t cbSkip, size_t cbCopy)
+{
+ return lsilogicSgBufWalker(pThis, pReq, lsilogicR3CopyBufferToGuestWorker,
+ pSgBuf, cbSkip, cbCopy);
+}
+
+/**
+ * Copies the guest S/G buffer into a host data buffer.
+ *
+ * @returns Amount of bytes copied from the guest.
+ * @param pThis The LsiLogic controller device instance.
+ * @param pReq Request structure.
+ * @param pSgBuf The S/G buffer to copy into.
+ * @param cbSkip How many bytes to skip in advance before starting to copy.
+ * @param cbCopy How many bytes to copy.
+ */
+static size_t lsilogicR3CopySgBufFromGuest(PLSILOGICSCSI pThis, PLSILOGICREQ pReq, PRTSGBUF pSgBuf,
+ size_t cbSkip, size_t cbCopy)
+{
+ return lsilogicSgBufWalker(pThis, pReq, lsilogicR3CopyBufferFromGuestWorker,
+ pSgBuf, cbSkip, cbCopy);
+}
+
+#if 0 /* unused */
+/**
+ * Copy a simple memory buffer to the guest memory buffer.
+ *
+ * @returns Amount of bytes copied to the guest.
+ * @param pThis The LsiLogic controller device instance.
+ * @param pReq Request structure.
+ * @param pvSrc The buffer to copy from.
+ * @param cbSrc How many bytes to copy.
+ * @param cbSkip How many bytes to skip initially.
+ */
+static size_t lsilogicR3CopyBufferToGuest(PLSILOGICSCSI pThis, PLSILOGICREQ pReq, const void *pvSrc,
+ size_t cbSrc, size_t cbSkip)
+{
+ RTSGSEG Seg;
+ RTSGBUF SgBuf;
+ Seg.pvSeg = (void *)pvSrc;
+ Seg.cbSeg = cbSrc;
+ RTSgBufInit(&SgBuf, &Seg, 1);
+ return lsilogicR3CopySgBufToGuest(pThis, pReq, &SgBuf, cbSkip, cbSrc);
+}
+
+/**
+ * Copy a guest memry buffe into simple host memory buffer.
+ *
+ * @returns Amount of bytes copied to the guest.
+ * @param pThis The LsiLogic controller device instance.
+ * @param pReq Request structure.
+ * @param pvSrc The buffer to copy from.
+ * @param cbSrc How many bytes to copy.
+ * @param cbSkip How many bytes to skip initially.
+ */
+static size_t lsilogicR3CopyBufferFromGuest(PLSILOGICSCSI pThis, PLSILOGICREQ pReq, void *pvDst,
+ size_t cbDst, size_t cbSkip)
+{
+ RTSGSEG Seg;
+ RTSGBUF SgBuf;
+ Seg.pvSeg = (void *)pvDst;
+ Seg.cbSeg = cbDst;
+ RTSgBufInit(&SgBuf, &Seg, 1);
+ return lsilogicR3CopySgBufFromGuest(pThis, pReq, &SgBuf, cbSkip, cbDst);
+}
+#endif
+
+# ifdef LOG_ENABLED
+static void lsilogicR3DumpSCSIIORequest(PMptSCSIIORequest pSCSIIORequest)
+{
+ if (LogIsEnabled())
+ {
+ Log(("%s: u8TargetID=%d\n", __FUNCTION__, pSCSIIORequest->u8TargetID));
+ Log(("%s: u8Bus=%d\n", __FUNCTION__, pSCSIIORequest->u8Bus));
+ Log(("%s: u8ChainOffset=%d\n", __FUNCTION__, pSCSIIORequest->u8ChainOffset));
+ Log(("%s: u8Function=%d\n", __FUNCTION__, pSCSIIORequest->u8Function));
+ Log(("%s: u8CDBLength=%d\n", __FUNCTION__, pSCSIIORequest->u8CDBLength));
+ Log(("%s: u8SenseBufferLength=%d\n", __FUNCTION__, pSCSIIORequest->u8SenseBufferLength));
+ Log(("%s: u8MessageFlags=%d\n", __FUNCTION__, pSCSIIORequest->u8MessageFlags));
+ Log(("%s: u32MessageContext=%#x\n", __FUNCTION__, pSCSIIORequest->u32MessageContext));
+ for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8LUN); i++)
+ Log(("%s: u8LUN[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8LUN[i]));
+ Log(("%s: u32Control=%#x\n", __FUNCTION__, pSCSIIORequest->u32Control));
+ for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8CDB); i++)
+ Log(("%s: u8CDB[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8CDB[i]));
+ Log(("%s: u32DataLength=%#x\n", __FUNCTION__, pSCSIIORequest->u32DataLength));
+ Log(("%s: u32SenseBufferLowAddress=%#x\n", __FUNCTION__, pSCSIIORequest->u32SenseBufferLowAddress));
+ }
+}
+# endif
+
+/**
+ * Handles the completion of th given request.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pReq The request to complete.
+ * @param rcReq Status code of the request.
+ */
+static void lsilogicR3ReqComplete(PLSILOGICSCSI pThis, PLSILOGICREQ pReq, int rcReq)
+{
+ PLSILOGICDEVICE pTgtDev = pReq->pTargetDevice;
+
+ if (RT_UNLIKELY(pReq->fBIOS))
+ {
+ uint8_t u8ScsiSts = pReq->u8ScsiSts;
+ pTgtDev->pDrvMediaEx->pfnIoReqFree(pTgtDev->pDrvMediaEx, pReq->hIoReq);
+ int rc = vboxscsiRequestFinished(&pThis->VBoxSCSI, u8ScsiSts);
+ AssertMsgRC(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc));
+ }
+ else
+ {
+ RTGCPHYS GCPhysAddrSenseBuffer;
+
+ GCPhysAddrSenseBuffer = pReq->GuestRequest.SCSIIO.u32SenseBufferLowAddress;
+ GCPhysAddrSenseBuffer |= ((uint64_t)pThis->u32SenseBufferHighAddr << 32);
+
+ /* Copy the sense buffer over. */
+ PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhysAddrSenseBuffer, pReq->abSenseBuffer,
+ RT_UNLIKELY( pReq->GuestRequest.SCSIIO.u8SenseBufferLength
+ < sizeof(pReq->abSenseBuffer))
+ ? pReq->GuestRequest.SCSIIO.u8SenseBufferLength
+ : sizeof(pReq->abSenseBuffer));
+
+ if (RT_SUCCESS(rcReq) && RT_LIKELY(pReq->u8ScsiSts == SCSI_STATUS_OK))
+ {
+ uint32_t u32MsgCtx = pReq->GuestRequest.SCSIIO.u32MessageContext;
+
+ /* Free the request before posting completion. */
+ pTgtDev->pDrvMediaEx->pfnIoReqFree(pTgtDev->pDrvMediaEx, pReq->hIoReq);
+ lsilogicR3FinishContextReply(pThis, u32MsgCtx);
+ }
+ else
+ {
+ MptReplyUnion IOCReply;
+ RT_ZERO(IOCReply);
+
+ /* The SCSI target encountered an error during processing post a reply. */
+ IOCReply.SCSIIOError.u8TargetID = pReq->GuestRequest.SCSIIO.u8TargetID;
+ IOCReply.SCSIIOError.u8Bus = pReq->GuestRequest.SCSIIO.u8Bus;
+ IOCReply.SCSIIOError.u8MessageLength = 8;
+ IOCReply.SCSIIOError.u8Function = pReq->GuestRequest.SCSIIO.u8Function;
+ IOCReply.SCSIIOError.u8CDBLength = pReq->GuestRequest.SCSIIO.u8CDBLength;
+ IOCReply.SCSIIOError.u8SenseBufferLength = pReq->GuestRequest.SCSIIO.u8SenseBufferLength;
+ IOCReply.SCSIIOError.u8MessageFlags = pReq->GuestRequest.SCSIIO.u8MessageFlags;
+ IOCReply.SCSIIOError.u32MessageContext = pReq->GuestRequest.SCSIIO.u32MessageContext;
+ IOCReply.SCSIIOError.u8SCSIStatus = pReq->u8ScsiSts;
+ IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_AUTOSENSE_VALID;
+ IOCReply.SCSIIOError.u16IOCStatus = 0;
+ IOCReply.SCSIIOError.u32IOCLogInfo = 0;
+ IOCReply.SCSIIOError.u32TransferCount = 0;
+ IOCReply.SCSIIOError.u32SenseCount = sizeof(pReq->abSenseBuffer);
+ IOCReply.SCSIIOError.u32ResponseInfo = 0;
+
+ /* Free the request before posting completion. */
+ pTgtDev->pDrvMediaEx->pfnIoReqFree(pTgtDev->pDrvMediaEx, pReq->hIoReq);
+ lsilogicFinishAddressReply(pThis, &IOCReply, false);
+ }
+ }
+
+ ASMAtomicDecU32(&pTgtDev->cOutstandingRequests);
+
+ if (pTgtDev->cOutstandingRequests == 0 && pThis->fSignalIdle)
+ PDMDevHlpAsyncNotificationCompleted(pThis->pDevInsR3);
+}
+
+/**
+ * Processes a SCSI I/O request by setting up the request
+ * and sending it to the underlying SCSI driver.
+ * Steps needed to complete request are done in the
+ * callback called by the driver below upon completion of
+ * the request.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param GCPhysMessageFrameAddr Guest physical address where the request is located.
+ * @param pGuestReq The request read fro th guest memory.
+ */
+static int lsilogicR3ProcessSCSIIORequest(PLSILOGICSCSI pThis, RTGCPHYS GCPhysMessageFrameAddr,
+ PMptRequestUnion pGuestReq)
+{
+ MptReplyUnion IOCReply;
+ int rc = VINF_SUCCESS;
+
+# ifdef LOG_ENABLED
+ lsilogicR3DumpSCSIIORequest(&pGuestReq->SCSIIO);
+# endif
+
+ if (RT_LIKELY( (pGuestReq->SCSIIO.u8TargetID < pThis->cDeviceStates)
+ && (pGuestReq->SCSIIO.u8Bus == 0)))
+ {
+ PLSILOGICDEVICE pTgtDev = &pThis->paDeviceStates[pGuestReq->SCSIIO.u8TargetID];
+
+ if (pTgtDev->pDrvBase)
+ {
+ /* Allocate and prepare a new request. */
+ PDMMEDIAEXIOREQ hIoReq;
+ PLSILOGICREQ pLsiReq = NULL;
+ rc = pTgtDev->pDrvMediaEx->pfnIoReqAlloc(pTgtDev->pDrvMediaEx, &hIoReq, (void **)&pLsiReq,
+ pGuestReq->SCSIIO.u32MessageContext,
+ PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR);
+ if (RT_SUCCESS(rc))
+ {
+ pLsiReq->hIoReq = hIoReq;
+ pLsiReq->pTargetDevice = pTgtDev;
+ pLsiReq->GCPhysMessageFrameAddr = GCPhysMessageFrameAddr;
+ pLsiReq->fBIOS = false;
+ pLsiReq->GCPhysSgStart = GCPhysMessageFrameAddr + sizeof(MptSCSIIORequest);
+ pLsiReq->cChainOffset = pGuestReq->SCSIIO.u8ChainOffset;
+ if (pLsiReq->cChainOffset)
+ pLsiReq->cChainOffset = pLsiReq->cChainOffset * sizeof(uint32_t) - sizeof(MptSCSIIORequest);
+ memcpy(&pLsiReq->GuestRequest, pGuestReq, sizeof(MptRequestUnion));
+ RT_BZERO(&pLsiReq->abSenseBuffer[0], sizeof(pLsiReq->abSenseBuffer));
+
+ PDMMEDIAEXIOREQSCSITXDIR enmXferDir = PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN;
+ uint8_t uDataDirection = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pLsiReq->GuestRequest.SCSIIO.u32Control);
+
+ /*
+ * Keep the direction to unknown if there is a mismatch between the data length
+ * and the transfer direction bit.
+ * The Solaris 9 driver is buggy and sets it to none for INQUIRY requests.
+ */
+ if ( uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE
+ && pLsiReq->GuestRequest.SCSIIO.u32DataLength == 0)
+ enmXferDir = PDMMEDIAEXIOREQSCSITXDIR_NONE;
+ else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE)
+ enmXferDir = PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE;
+ else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ)
+ enmXferDir = PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE;
+
+ ASMAtomicIncU32(&pTgtDev->cOutstandingRequests);
+ rc = pTgtDev->pDrvMediaEx->pfnIoReqSendScsiCmd(pTgtDev->pDrvMediaEx, pLsiReq->hIoReq, pLsiReq->GuestRequest.SCSIIO.au8LUN[1],
+ &pLsiReq->GuestRequest.SCSIIO.au8CDB[0], pLsiReq->GuestRequest.SCSIIO.u8CDBLength,
+ enmXferDir, pLsiReq->GuestRequest.SCSIIO.u32DataLength,
+ &pLsiReq->abSenseBuffer[0], sizeof(pLsiReq->abSenseBuffer), &pLsiReq->u8ScsiSts,
+ 30 * RT_MS_1SEC);
+ if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
+ lsilogicR3ReqComplete(pThis, pLsiReq, rc);
+
+ return VINF_SUCCESS;
+ }
+ else
+ IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_DEVICE_NOT_THERE;
+ }
+ else
+ {
+ /* Device is not present report SCSI selection timeout. */
+ IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_DEVICE_NOT_THERE;
+ }
+ }
+ else
+ {
+ /* Report out of bounds target ID or bus. */
+ if (pGuestReq->SCSIIO.u8Bus != 0)
+ IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_BUS;
+ else
+ IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_TARGETID;
+ }
+
+ static int g_cLogged = 0;
+
+ if (g_cLogged++ < MAX_REL_LOG_ERRORS)
+ {
+ LogRel(("LsiLogic#%d: %d/%d (Bus/Target) doesn't exist\n", pThis->CTX_SUFF(pDevIns)->iInstance,
+ pGuestReq->SCSIIO.u8TargetID, pGuestReq->SCSIIO.u8Bus));
+ /* Log the CDB too */
+ LogRel(("LsiLogic#%d: Guest issued CDB {%#x",
+ pThis->CTX_SUFF(pDevIns)->iInstance, pGuestReq->SCSIIO.au8CDB[0]));
+ for (unsigned i = 1; i < pGuestReq->SCSIIO.u8CDBLength; i++)
+ LogRel((", %#x", pGuestReq->SCSIIO.au8CDB[i]));
+ LogRel(("}\n"));
+ }
+
+ /* The rest is equal to both errors. */
+ IOCReply.SCSIIOError.u8TargetID = pGuestReq->SCSIIO.u8TargetID;
+ IOCReply.SCSIIOError.u8Bus = pGuestReq->SCSIIO.u8Bus;
+ IOCReply.SCSIIOError.u8MessageLength = sizeof(MptSCSIIOErrorReply) / 4;
+ IOCReply.SCSIIOError.u8Function = pGuestReq->SCSIIO.u8Function;
+ IOCReply.SCSIIOError.u8CDBLength = pGuestReq->SCSIIO.u8CDBLength;
+ IOCReply.SCSIIOError.u8SenseBufferLength = pGuestReq->SCSIIO.u8SenseBufferLength;
+ IOCReply.SCSIIOError.u32MessageContext = pGuestReq->SCSIIO.u32MessageContext;
+ IOCReply.SCSIIOError.u8SCSIStatus = SCSI_STATUS_OK;
+ IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_TERMINATED;
+ IOCReply.SCSIIOError.u32IOCLogInfo = 0;
+ IOCReply.SCSIIOError.u32TransferCount = 0;
+ IOCReply.SCSIIOError.u32SenseCount = 0;
+ IOCReply.SCSIIOError.u32ResponseInfo = 0;
+
+ lsilogicFinishAddressReply(pThis, &IOCReply, false);
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMIMEDIAPORT,pfnQueryDeviceLocation}
+ */
+static DECLCALLBACK(int) lsilogicR3QueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
+ uint32_t *piInstance, uint32_t *piLUN)
+{
+ PLSILOGICDEVICE pTgtDev = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IMediaPort);
+ PPDMDEVINS pDevIns = pTgtDev->CTX_SUFF(pLsiLogic)->CTX_SUFF(pDevIns);
+
+ AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
+ AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
+ AssertPtrReturn(piLUN, VERR_INVALID_POINTER);
+
+ *ppcszController = pDevIns->pReg->szName;
+ *piInstance = pDevIns->iInstance;
+ *piLUN = pTgtDev->iLUN;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
+ */
+static DECLCALLBACK(int) lsilogicR3IoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
+ void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf,
+ size_t cbCopy)
+{
+ RT_NOREF1(hIoReq);
+ PLSILOGICDEVICE pTgtDev = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IMediaExPort);
+ PLSILOGICREQ pReq = (PLSILOGICREQ)pvIoReqAlloc;
+
+ size_t cbCopied = 0;
+ if (RT_UNLIKELY(pReq->fBIOS))
+ cbCopied = vboxscsiCopyToBuf(&pTgtDev->CTX_SUFF(pLsiLogic)->VBoxSCSI, pSgBuf, offDst, cbCopy);
+ else
+ cbCopied = lsilogicR3CopySgBufToGuest(pTgtDev->CTX_SUFF(pLsiLogic), pReq, pSgBuf, offDst, cbCopy);
+ return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_OVERFLOW;
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
+ */
+static DECLCALLBACK(int) lsilogicR3IoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
+ void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf,
+ size_t cbCopy)
+{
+ RT_NOREF1(hIoReq);
+ PLSILOGICDEVICE pTgtDev = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IMediaExPort);
+ PLSILOGICREQ pReq = (PLSILOGICREQ)pvIoReqAlloc;
+
+ size_t cbCopied = 0;
+ if (RT_UNLIKELY(pReq->fBIOS))
+ cbCopied = vboxscsiCopyFromBuf(&pTgtDev->CTX_SUFF(pLsiLogic)->VBoxSCSI, pSgBuf, offSrc, cbCopy);
+ else
+ cbCopied = lsilogicR3CopySgBufFromGuest(pTgtDev->CTX_SUFF(pLsiLogic), pReq, pSgBuf, offSrc, cbCopy);
+ return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_UNDERRUN;
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
+ */
+static DECLCALLBACK(int) lsilogicR3IoReqCompleteNotify(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
+ void *pvIoReqAlloc, int rcReq)
+{
+ RT_NOREF(hIoReq);
+ PLSILOGICDEVICE pTgtDev = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IMediaExPort);
+ lsilogicR3ReqComplete(pTgtDev->CTX_SUFF(pLsiLogic), (PLSILOGICREQ)pvIoReqAlloc, rcReq);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
+ */
+static DECLCALLBACK(void) lsilogicR3IoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
+ void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
+{
+ RT_NOREF3(hIoReq, pvIoReqAlloc, enmState);
+ PLSILOGICDEVICE pTgtDev = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IMediaExPort);
+
+ switch (enmState)
+ {
+ case PDMMEDIAEXIOREQSTATE_SUSPENDED:
+ {
+ /* Make sure the request is not accounted for so the VM can suspend successfully. */
+ uint32_t cTasksActive = ASMAtomicDecU32(&pTgtDev->cOutstandingRequests);
+ if (!cTasksActive && pTgtDev->CTX_SUFF(pLsiLogic)->fSignalIdle)
+ PDMDevHlpAsyncNotificationCompleted(pTgtDev->CTX_SUFF(pLsiLogic)->pDevInsR3);
+ break;
+ }
+ case PDMMEDIAEXIOREQSTATE_ACTIVE:
+ /* Make sure the request is accounted for so the VM suspends only when the request is complete. */
+ ASMAtomicIncU32(&pTgtDev->cOutstandingRequests);
+ break;
+ default:
+ AssertMsgFailed(("Invalid request state given %u\n", enmState));
+ }
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEXPORT,pfnMediumEjected}
+ */
+static DECLCALLBACK(void) lsilogicR3MediumEjected(PPDMIMEDIAEXPORT pInterface)
+{
+ PLSILOGICDEVICE pTgtDev = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IMediaExPort);
+ PLSILOGICSCSI pThis = pTgtDev->CTX_SUFF(pLsiLogic);
+
+ if (pThis->pMediaNotify)
+ {
+ int rc = VMR3ReqCallNoWait(PDMDevHlpGetVM(pThis->CTX_SUFF(pDevIns)), VMCPUID_ANY,
+ (PFNRT)pThis->pMediaNotify->pfnEjected, 2,
+ pThis->pMediaNotify, pTgtDev->iLUN);
+ AssertRC(rc);
+ }
+}
+
+
+/**
+ * Return the configuration page header and data
+ * which matches the given page type and number.
+ *
+ * @returns VINF_SUCCESS if successful
+ * VERR_NOT_FOUND if the requested page could be found.
+ * @param pThis The LsiLogic controller instance data.
+ * @param pPages The pages supported by the controller.
+ * @param u8PageNumber Number of the page to get.
+ * @param ppPageHeader Where to store the pointer to the page header.
+ * @param ppbPageData Where to store the pointer to the page data.
+ * @param pcbPage Where to store the size of the page data in bytes on success.
+ */
+static int lsilogicR3ConfigurationIOUnitPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ PMptConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
+{
+ RT_NOREF(pThis);
+ int rc = VINF_SUCCESS;
+
+ AssertPtr(ppPageHeader); Assert(ppbPageData);
+
+ switch (u8PageNumber)
+ {
+ case 0:
+ *ppPageHeader = &pPages->IOUnitPage0.u.fields.Header;
+ *ppbPageData = pPages->IOUnitPage0.u.abPageData;
+ *pcbPage = sizeof(pPages->IOUnitPage0);
+ break;
+ case 1:
+ *ppPageHeader = &pPages->IOUnitPage1.u.fields.Header;
+ *ppbPageData = pPages->IOUnitPage1.u.abPageData;
+ *pcbPage = sizeof(pPages->IOUnitPage1);
+ break;
+ case 2:
+ *ppPageHeader = &pPages->IOUnitPage2.u.fields.Header;
+ *ppbPageData = pPages->IOUnitPage2.u.abPageData;
+ *pcbPage = sizeof(pPages->IOUnitPage2);
+ break;
+ case 3:
+ *ppPageHeader = &pPages->IOUnitPage3.u.fields.Header;
+ *ppbPageData = pPages->IOUnitPage3.u.abPageData;
+ *pcbPage = sizeof(pPages->IOUnitPage3);
+ break;
+ case 4:
+ *ppPageHeader = &pPages->IOUnitPage4.u.fields.Header;
+ *ppbPageData = pPages->IOUnitPage4.u.abPageData;
+ *pcbPage = sizeof(pPages->IOUnitPage4);
+ break;
+ default:
+ rc = VERR_NOT_FOUND;
+ }
+
+ return rc;
+}
+
+/**
+ * Return the configuration page header and data
+ * which matches the given page type and number.
+ *
+ * @returns VINF_SUCCESS if successful
+ * VERR_NOT_FOUND if the requested page could be found.
+ * @param pThis The LsiLogic controller instance data.
+ * @param pPages The pages supported by the controller.
+ * @param u8PageNumber Number of the page to get.
+ * @param ppPageHeader Where to store the pointer to the page header.
+ * @param ppbPageData Where to store the pointer to the page data.
+ * @param pcbPage Where to store the size of the page data in bytes on success.
+ */
+static int lsilogicR3ConfigurationIOCPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ PMptConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
+{
+ RT_NOREF(pThis);
+ int rc = VINF_SUCCESS;
+
+ AssertPtr(ppPageHeader); Assert(ppbPageData);
+
+ switch (u8PageNumber)
+ {
+ case 0:
+ *ppPageHeader = &pPages->IOCPage0.u.fields.Header;
+ *ppbPageData = pPages->IOCPage0.u.abPageData;
+ *pcbPage = sizeof(pPages->IOCPage0);
+ break;
+ case 1:
+ *ppPageHeader = &pPages->IOCPage1.u.fields.Header;
+ *ppbPageData = pPages->IOCPage1.u.abPageData;
+ *pcbPage = sizeof(pPages->IOCPage1);
+ break;
+ case 2:
+ *ppPageHeader = &pPages->IOCPage2.u.fields.Header;
+ *ppbPageData = pPages->IOCPage2.u.abPageData;
+ *pcbPage = sizeof(pPages->IOCPage2);
+ break;
+ case 3:
+ *ppPageHeader = &pPages->IOCPage3.u.fields.Header;
+ *ppbPageData = pPages->IOCPage3.u.abPageData;
+ *pcbPage = sizeof(pPages->IOCPage3);
+ break;
+ case 4:
+ *ppPageHeader = &pPages->IOCPage4.u.fields.Header;
+ *ppbPageData = pPages->IOCPage4.u.abPageData;
+ *pcbPage = sizeof(pPages->IOCPage4);
+ break;
+ case 6:
+ *ppPageHeader = &pPages->IOCPage6.u.fields.Header;
+ *ppbPageData = pPages->IOCPage6.u.abPageData;
+ *pcbPage = sizeof(pPages->IOCPage6);
+ break;
+ default:
+ rc = VERR_NOT_FOUND;
+ }
+
+ return rc;
+}
+
+/**
+ * Return the configuration page header and data
+ * which matches the given page type and number.
+ *
+ * @returns VINF_SUCCESS if successful
+ * VERR_NOT_FOUND if the requested page could be found.
+ * @param pThis The LsiLogic controller instance data.
+ * @param pPages The pages supported by the controller.
+ * @param u8PageNumber Number of the page to get.
+ * @param ppPageHeader Where to store the pointer to the page header.
+ * @param ppbPageData Where to store the pointer to the page data.
+ * @param pcbPage Where to store the size of the page data in bytes on success.
+ */
+static int lsilogicR3ConfigurationManufacturingPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ PMptConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
+{
+ int rc = VINF_SUCCESS;
+
+ AssertPtr(ppPageHeader); Assert(ppbPageData);
+
+ switch (u8PageNumber)
+ {
+ case 0:
+ *ppPageHeader = &pPages->ManufacturingPage0.u.fields.Header;
+ *ppbPageData = pPages->ManufacturingPage0.u.abPageData;
+ *pcbPage = sizeof(pPages->ManufacturingPage0);
+ break;
+ case 1:
+ *ppPageHeader = &pPages->ManufacturingPage1.u.fields.Header;
+ *ppbPageData = pPages->ManufacturingPage1.u.abPageData;
+ *pcbPage = sizeof(pPages->ManufacturingPage1);
+ break;
+ case 2:
+ *ppPageHeader = &pPages->ManufacturingPage2.u.fields.Header;
+ *ppbPageData = pPages->ManufacturingPage2.u.abPageData;
+ *pcbPage = sizeof(pPages->ManufacturingPage2);
+ break;
+ case 3:
+ *ppPageHeader = &pPages->ManufacturingPage3.u.fields.Header;
+ *ppbPageData = pPages->ManufacturingPage3.u.abPageData;
+ *pcbPage = sizeof(pPages->ManufacturingPage3);
+ break;
+ case 4:
+ *ppPageHeader = &pPages->ManufacturingPage4.u.fields.Header;
+ *ppbPageData = pPages->ManufacturingPage4.u.abPageData;
+ *pcbPage = sizeof(pPages->ManufacturingPage4);
+ break;
+ case 5:
+ *ppPageHeader = &pPages->ManufacturingPage5.u.fields.Header;
+ *ppbPageData = pPages->ManufacturingPage5.u.abPageData;
+ *pcbPage = sizeof(pPages->ManufacturingPage5);
+ break;
+ case 6:
+ *ppPageHeader = &pPages->ManufacturingPage6.u.fields.Header;
+ *ppbPageData = pPages->ManufacturingPage6.u.abPageData;
+ *pcbPage = sizeof(pPages->ManufacturingPage6);
+ break;
+ case 7:
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ {
+ *ppPageHeader = &pPages->u.SasPages.pManufacturingPage7->u.fields.Header;
+ *ppbPageData = pPages->u.SasPages.pManufacturingPage7->u.abPageData;
+ *pcbPage = pPages->u.SasPages.cbManufacturingPage7;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ break;
+ case 8:
+ *ppPageHeader = &pPages->ManufacturingPage8.u.fields.Header;
+ *ppbPageData = pPages->ManufacturingPage8.u.abPageData;
+ *pcbPage = sizeof(pPages->ManufacturingPage8);
+ break;
+ case 9:
+ *ppPageHeader = &pPages->ManufacturingPage9.u.fields.Header;
+ *ppbPageData = pPages->ManufacturingPage9.u.abPageData;
+ *pcbPage = sizeof(pPages->ManufacturingPage9);
+ break;
+ case 10:
+ *ppPageHeader = &pPages->ManufacturingPage10.u.fields.Header;
+ *ppbPageData = pPages->ManufacturingPage10.u.abPageData;
+ *pcbPage = sizeof(pPages->ManufacturingPage10);
+ break;
+ default:
+ rc = VERR_NOT_FOUND;
+ }
+
+ return rc;
+}
+
+/**
+ * Return the configuration page header and data
+ * which matches the given page type and number.
+ *
+ * @returns VINF_SUCCESS if successful
+ * VERR_NOT_FOUND if the requested page could be found.
+ * @param pThis The LsiLogic controller instance data.
+ * @param pPages The pages supported by the controller.
+ * @param u8PageNumber Number of the page to get.
+ * @param ppPageHeader Where to store the pointer to the page header.
+ * @param ppbPageData Where to store the pointer to the page data.
+ * @param pcbPage Where to store the size of the page data in bytes on success.
+ */
+static int lsilogicR3ConfigurationBiosPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ PMptConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
+{
+ RT_NOREF(pThis);
+ int rc = VINF_SUCCESS;
+
+ AssertPtr(ppPageHeader); Assert(ppbPageData);
+
+ switch (u8PageNumber)
+ {
+ case 1:
+ *ppPageHeader = &pPages->BIOSPage1.u.fields.Header;
+ *ppbPageData = pPages->BIOSPage1.u.abPageData;
+ *pcbPage = sizeof(pPages->BIOSPage1);
+ break;
+ case 2:
+ *ppPageHeader = &pPages->BIOSPage2.u.fields.Header;
+ *ppbPageData = pPages->BIOSPage2.u.abPageData;
+ *pcbPage = sizeof(pPages->BIOSPage2);
+ break;
+ case 4:
+ *ppPageHeader = &pPages->BIOSPage4.u.fields.Header;
+ *ppbPageData = pPages->BIOSPage4.u.abPageData;
+ *pcbPage = sizeof(pPages->BIOSPage4);
+ break;
+ default:
+ rc = VERR_NOT_FOUND;
+ }
+
+ return rc;
+}
+
+/**
+ * Return the configuration page header and data
+ * which matches the given page type and number.
+ *
+ * @returns VINF_SUCCESS if successful
+ * VERR_NOT_FOUND if the requested page could be found.
+ * @param pThis The LsiLogic controller instance data.
+ * @param pPages The pages supported by the controller.
+ * @param u8Port The port to retrieve the page for.
+ * @param u8PageNumber Number of the page to get.
+ * @param ppPageHeader Where to store the pointer to the page header.
+ * @param ppbPageData Where to store the pointer to the page data.
+ * @param pcbPage Where to store the size of the page data in bytes on success.
+ */
+static int lsilogicR3ConfigurationSCSISPIPortPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8Port,
+ uint8_t u8PageNumber,
+ PMptConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
+{
+ RT_NOREF(pThis);
+ int rc = VINF_SUCCESS;
+ AssertPtr(ppPageHeader); Assert(ppbPageData);
+
+
+ if (u8Port >= RT_ELEMENTS(pPages->u.SpiPages.aPortPages))
+ return VERR_NOT_FOUND;
+
+ switch (u8PageNumber)
+ {
+ case 0:
+ *ppPageHeader = &pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage0.u.fields.Header;
+ *ppbPageData = pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage0.u.abPageData;
+ *pcbPage = sizeof(pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage0);
+ break;
+ case 1:
+ *ppPageHeader = &pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage1.u.fields.Header;
+ *ppbPageData = pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage1.u.abPageData;
+ *pcbPage = sizeof(pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage1);
+ break;
+ case 2:
+ *ppPageHeader = &pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage2.u.fields.Header;
+ *ppbPageData = pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage2.u.abPageData;
+ *pcbPage = sizeof(pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage2);
+ break;
+ default:
+ rc = VERR_NOT_FOUND;
+ }
+
+ return rc;
+}
+
+/**
+ * Return the configuration page header and data
+ * which matches the given page type and number.
+ *
+ * @returns VINF_SUCCESS if successful
+ * VERR_NOT_FOUND if the requested page could be found.
+ * @param pThis The LsiLogic controller instance data.
+ * @param pPages The pages supported by the controller.
+ * @param u8Bus The bus the device is on the page should be returned.
+ * @param u8TargetID The target ID of the device to return the page for.
+ * @param u8PageNumber Number of the page to get.
+ * @param ppPageHeader Where to store the pointer to the page header.
+ * @param ppbPageData Where to store the pointer to the page data.
+ * @param pcbPage Where to store the size of the page data in bytes on success.
+ */
+static int lsilogicR3ConfigurationSCSISPIDevicePageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8Bus,
+ uint8_t u8TargetID, uint8_t u8PageNumber,
+ PMptConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
+{
+ RT_NOREF(pThis);
+ int rc = VINF_SUCCESS;
+ AssertPtr(ppPageHeader); Assert(ppbPageData);
+
+ if (u8Bus >= RT_ELEMENTS(pPages->u.SpiPages.aBuses))
+ return VERR_NOT_FOUND;
+
+ if (u8TargetID >= RT_ELEMENTS(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages))
+ return VERR_NOT_FOUND;
+
+ switch (u8PageNumber)
+ {
+ case 0:
+ *ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage0.u.fields.Header;
+ *ppbPageData = pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage0.u.abPageData;
+ *pcbPage = sizeof(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage0);
+ break;
+ case 1:
+ *ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage1.u.fields.Header;
+ *ppbPageData = pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage1.u.abPageData;
+ *pcbPage = sizeof(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage1);
+ break;
+ case 2:
+ *ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage2.u.fields.Header;
+ *ppbPageData = pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage2.u.abPageData;
+ *pcbPage = sizeof(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage2);
+ break;
+ case 3:
+ *ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage3.u.fields.Header;
+ *ppbPageData = pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage3.u.abPageData;
+ *pcbPage = sizeof(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage3);
+ break;
+ default:
+ rc = VERR_NOT_FOUND;
+ }
+
+ return rc;
+}
+
+static int lsilogicR3ConfigurationSASIOUnitPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ PMptExtendedConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
+{
+ RT_NOREF(pThis);
+ int rc = VINF_SUCCESS;
+
+ switch (u8PageNumber)
+ {
+ case 0:
+ *ppPageHeader = &pPages->u.SasPages.pSASIOUnitPage0->u.fields.ExtHeader;
+ *ppbPageData = pPages->u.SasPages.pSASIOUnitPage0->u.abPageData;
+ *pcbPage = pPages->u.SasPages.cbSASIOUnitPage0;
+ break;
+ case 1:
+ *ppPageHeader = &pPages->u.SasPages.pSASIOUnitPage1->u.fields.ExtHeader;
+ *ppbPageData = pPages->u.SasPages.pSASIOUnitPage1->u.abPageData;
+ *pcbPage = pPages->u.SasPages.cbSASIOUnitPage1;
+ break;
+ case 2:
+ *ppPageHeader = &pPages->u.SasPages.SASIOUnitPage2.u.fields.ExtHeader;
+ *ppbPageData = pPages->u.SasPages.SASIOUnitPage2.u.abPageData;
+ *pcbPage = sizeof(pPages->u.SasPages.SASIOUnitPage2);
+ break;
+ case 3:
+ *ppPageHeader = &pPages->u.SasPages.SASIOUnitPage3.u.fields.ExtHeader;
+ *ppbPageData = pPages->u.SasPages.SASIOUnitPage3.u.abPageData;
+ *pcbPage = sizeof(pPages->u.SasPages.SASIOUnitPage3);
+ break;
+ default:
+ rc = VERR_NOT_FOUND;
+ }
+
+ return rc;
+}
+
+static int lsilogicR3ConfigurationSASPHYPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ MptConfigurationPageAddress PageAddress,
+ PMptExtendedConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
+{
+ RT_NOREF(pThis);
+ int rc = VINF_SUCCESS;
+ uint8_t uAddressForm = MPT_CONFIGURATION_PAGE_ADDRESS_GET_SAS_FORM(PageAddress);
+ PMptConfigurationPagesSas pPagesSas = &pPages->u.SasPages;
+ PMptPHY pPHYPages = NULL;
+
+ Log(("Address form %d\n", uAddressForm));
+
+ if (uAddressForm == 0) /* PHY number */
+ {
+ uint8_t u8PhyNumber = PageAddress.SASPHY.Form0.u8PhyNumber;
+
+ Log(("PHY number %d\n", u8PhyNumber));
+
+ if (u8PhyNumber >= pPagesSas->cPHYs)
+ return VERR_NOT_FOUND;
+
+ pPHYPages = &pPagesSas->paPHYs[u8PhyNumber];
+ }
+ else if (uAddressForm == 1) /* Index form */
+ {
+ uint16_t u16Index = PageAddress.SASPHY.Form1.u16Index;
+
+ Log(("PHY index %d\n", u16Index));
+
+ if (u16Index >= pPagesSas->cPHYs)
+ return VERR_NOT_FOUND;
+
+ pPHYPages = &pPagesSas->paPHYs[u16Index];
+ }
+ else
+ rc = VERR_NOT_FOUND; /* Correct? */
+
+ if (pPHYPages)
+ {
+ switch (u8PageNumber)
+ {
+ case 0:
+ *ppPageHeader = &pPHYPages->SASPHYPage0.u.fields.ExtHeader;
+ *ppbPageData = pPHYPages->SASPHYPage0.u.abPageData;
+ *pcbPage = sizeof(pPHYPages->SASPHYPage0);
+ break;
+ case 1:
+ *ppPageHeader = &pPHYPages->SASPHYPage1.u.fields.ExtHeader;
+ *ppbPageData = pPHYPages->SASPHYPage1.u.abPageData;
+ *pcbPage = sizeof(pPHYPages->SASPHYPage1);
+ break;
+ default:
+ rc = VERR_NOT_FOUND;
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ return rc;
+}
+
+static int lsilogicR3ConfigurationSASDevicePageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ MptConfigurationPageAddress PageAddress,
+ PMptExtendedConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
+{
+ RT_NOREF(pThis);
+ int rc = VINF_SUCCESS;
+ uint8_t uAddressForm = MPT_CONFIGURATION_PAGE_ADDRESS_GET_SAS_FORM(PageAddress);
+ PMptConfigurationPagesSas pPagesSas = &pPages->u.SasPages;
+ PMptSASDevice pSASDevice = NULL;
+
+ Log(("Address form %d\n", uAddressForm));
+
+ if (uAddressForm == 0)
+ {
+ uint16_t u16Handle = PageAddress.SASDevice.Form0And2.u16Handle;
+
+ Log(("Get next handle %#x\n", u16Handle));
+
+ pSASDevice = pPagesSas->pSASDeviceHead;
+
+ /* Get the first device? */
+ if (u16Handle != 0xffff)
+ {
+ /* No, search for the right one. */
+
+ while ( pSASDevice
+ && pSASDevice->SASDevicePage0.u.fields.u16DevHandle != u16Handle)
+ pSASDevice = pSASDevice->pNext;
+
+ if (pSASDevice)
+ pSASDevice = pSASDevice->pNext;
+ }
+ }
+ else if (uAddressForm == 1)
+ {
+ uint8_t u8TargetID = PageAddress.SASDevice.Form1.u8TargetID;
+ uint8_t u8Bus = PageAddress.SASDevice.Form1.u8Bus;
+
+ Log(("u8TargetID=%d u8Bus=%d\n", u8TargetID, u8Bus));
+
+ pSASDevice = pPagesSas->pSASDeviceHead;
+
+ while ( pSASDevice
+ && ( pSASDevice->SASDevicePage0.u.fields.u8TargetID != u8TargetID
+ || pSASDevice->SASDevicePage0.u.fields.u8Bus != u8Bus))
+ pSASDevice = pSASDevice->pNext;
+ }
+ else if (uAddressForm == 2)
+ {
+ uint16_t u16Handle = PageAddress.SASDevice.Form0And2.u16Handle;
+
+ Log(("Handle %#x\n", u16Handle));
+
+ pSASDevice = pPagesSas->pSASDeviceHead;
+
+ while ( pSASDevice
+ && pSASDevice->SASDevicePage0.u.fields.u16DevHandle != u16Handle)
+ pSASDevice = pSASDevice->pNext;
+ }
+
+ if (pSASDevice)
+ {
+ switch (u8PageNumber)
+ {
+ case 0:
+ *ppPageHeader = &pSASDevice->SASDevicePage0.u.fields.ExtHeader;
+ *ppbPageData = pSASDevice->SASDevicePage0.u.abPageData;
+ *pcbPage = sizeof(pSASDevice->SASDevicePage0);
+ break;
+ case 1:
+ *ppPageHeader = &pSASDevice->SASDevicePage1.u.fields.ExtHeader;
+ *ppbPageData = pSASDevice->SASDevicePage1.u.abPageData;
+ *pcbPage = sizeof(pSASDevice->SASDevicePage1);
+ break;
+ case 2:
+ *ppPageHeader = &pSASDevice->SASDevicePage2.u.fields.ExtHeader;
+ *ppbPageData = pSASDevice->SASDevicePage2.u.abPageData;
+ *pcbPage = sizeof(pSASDevice->SASDevicePage2);
+ break;
+ default:
+ rc = VERR_NOT_FOUND;
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ return rc;
+}
+
+/**
+ * Returns the extended configuration page header and data.
+ * @returns VINF_SUCCESS if successful
+ * VERR_NOT_FOUND if the requested page could be found.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pConfigurationReq The configuration request.
+ * @param ppPageHeader Where to return the pointer to the page header on success.
+ * @param ppbPageData Where to store the pointer to the page data.
+ * @param pcbPage Where to store the size of the page in bytes.
+ */
+static int lsilogicR3ConfigurationPageGetExtended(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq,
+ PMptExtendedConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
+{
+ int rc = VINF_SUCCESS;
+
+ Log(("Extended page requested:\n"));
+ Log(("u8ExtPageType=%#x\n", pConfigurationReq->u8ExtPageType));
+ Log(("u8ExtPageLength=%d\n", pConfigurationReq->u16ExtPageLength));
+
+ switch (pConfigurationReq->u8ExtPageType)
+ {
+ case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT:
+ {
+ rc = lsilogicR3ConfigurationSASIOUnitPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
+ pConfigurationReq->u8PageNumber,
+ ppPageHeader, ppbPageData, pcbPage);
+ break;
+ }
+ case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASPHYS:
+ {
+ rc = lsilogicR3ConfigurationSASPHYPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
+ pConfigurationReq->u8PageNumber,
+ pConfigurationReq->PageAddress,
+ ppPageHeader, ppbPageData, pcbPage);
+ break;
+ }
+ case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE:
+ {
+ rc = lsilogicR3ConfigurationSASDevicePageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
+ pConfigurationReq->u8PageNumber,
+ pConfigurationReq->PageAddress,
+ ppPageHeader, ppbPageData, pcbPage);
+ break;
+ }
+ case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASEXPANDER: /* No expanders supported */
+ case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_ENCLOSURE: /* No enclosures supported */
+ default:
+ rc = VERR_NOT_FOUND;
+ }
+
+ return rc;
+}
+
+/**
+ * Processes a Configuration request.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pConfigurationReq Pointer to the request structure.
+ * @param pReply Pointer to the reply message frame
+ */
+static int lsilogicR3ProcessConfigurationRequest(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq,
+ PMptConfigurationReply pReply)
+{
+ int rc = VINF_SUCCESS;
+ uint8_t *pbPageData = NULL;
+ PMptConfigurationPageHeader pPageHeader = NULL;
+ PMptExtendedConfigurationPageHeader pExtPageHeader = NULL;
+ uint8_t u8PageType;
+ uint8_t u8PageAttribute;
+ size_t cbPage = 0;
+
+ LogFlowFunc(("pThis=%#p\n", pThis));
+
+ u8PageType = MPT_CONFIGURATION_PAGE_TYPE_GET(pConfigurationReq->u8PageType);
+ u8PageAttribute = MPT_CONFIGURATION_PAGE_ATTRIBUTE_GET(pConfigurationReq->u8PageType);
+
+ Log(("GuestRequest:\n"));
+ Log(("u8Action=%#x\n", pConfigurationReq->u8Action));
+ Log(("u8PageType=%#x\n", u8PageType));
+ Log(("u8PageNumber=%d\n", pConfigurationReq->u8PageNumber));
+ Log(("u8PageLength=%d\n", pConfigurationReq->u8PageLength));
+ Log(("u8PageVersion=%d\n", pConfigurationReq->u8PageVersion));
+
+ /* Copy common bits from the request into the reply. */
+ pReply->u8MessageLength = 6; /* 6 32bit D-Words. */
+ pReply->u8Action = pConfigurationReq->u8Action;
+ pReply->u8Function = pConfigurationReq->u8Function;
+ pReply->u32MessageContext = pConfigurationReq->u32MessageContext;
+
+ switch (u8PageType)
+ {
+ case MPT_CONFIGURATION_PAGE_TYPE_IO_UNIT:
+ {
+ /* Get the page data. */
+ rc = lsilogicR3ConfigurationIOUnitPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
+ pConfigurationReq->u8PageNumber,
+ &pPageHeader, &pbPageData, &cbPage);
+ break;
+ }
+ case MPT_CONFIGURATION_PAGE_TYPE_IOC:
+ {
+ /* Get the page data. */
+ rc = lsilogicR3ConfigurationIOCPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
+ pConfigurationReq->u8PageNumber,
+ &pPageHeader, &pbPageData, &cbPage);
+ break;
+ }
+ case MPT_CONFIGURATION_PAGE_TYPE_MANUFACTURING:
+ {
+ /* Get the page data. */
+ rc = lsilogicR3ConfigurationManufacturingPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
+ pConfigurationReq->u8PageNumber,
+ &pPageHeader, &pbPageData, &cbPage);
+ break;
+ }
+ case MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT:
+ {
+ /* Get the page data. */
+ rc = lsilogicR3ConfigurationSCSISPIPortPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
+ pConfigurationReq->PageAddress.MPIPortNumber.u8PortNumber,
+ pConfigurationReq->u8PageNumber,
+ &pPageHeader, &pbPageData, &cbPage);
+ break;
+ }
+ case MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE:
+ {
+ /* Get the page data. */
+ rc = lsilogicR3ConfigurationSCSISPIDevicePageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
+ pConfigurationReq->PageAddress.BusAndTargetId.u8Bus,
+ pConfigurationReq->PageAddress.BusAndTargetId.u8TargetID,
+ pConfigurationReq->u8PageNumber,
+ &pPageHeader, &pbPageData, &cbPage);
+ break;
+ }
+ case MPT_CONFIGURATION_PAGE_TYPE_BIOS:
+ {
+ rc = lsilogicR3ConfigurationBiosPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
+ pConfigurationReq->u8PageNumber,
+ &pPageHeader, &pbPageData, &cbPage);
+ break;
+ }
+ case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED:
+ {
+ rc = lsilogicR3ConfigurationPageGetExtended(pThis,
+ pConfigurationReq,
+ &pExtPageHeader, &pbPageData, &cbPage);
+ break;
+ }
+ default:
+ rc = VERR_NOT_FOUND;
+ }
+
+ if (rc == VERR_NOT_FOUND)
+ {
+ Log(("Page not found\n"));
+ pReply->u8PageType = pConfigurationReq->u8PageType;
+ pReply->u8PageNumber = pConfigurationReq->u8PageNumber;
+ pReply->u8PageLength = pConfigurationReq->u8PageLength;
+ pReply->u8PageVersion = pConfigurationReq->u8PageVersion;
+ pReply->u16IOCStatus = MPT_IOCSTATUS_CONFIG_INVALID_PAGE;
+ return VINF_SUCCESS;
+ }
+
+ if (u8PageType == MPT_CONFIGURATION_PAGE_TYPE_EXTENDED)
+ {
+ pReply->u8PageType = pExtPageHeader->u8PageType;
+ pReply->u8PageNumber = pExtPageHeader->u8PageNumber;
+ pReply->u8PageVersion = pExtPageHeader->u8PageVersion;
+ pReply->u8ExtPageType = pExtPageHeader->u8ExtPageType;
+ pReply->u16ExtPageLength = pExtPageHeader->u16ExtPageLength;
+
+ for (int i = 0; i < pExtPageHeader->u16ExtPageLength; i++)
+ LogFlowFunc(("PageData[%d]=%#x\n", i, ((uint32_t *)pbPageData)[i]));
+ }
+ else
+ {
+ pReply->u8PageType = pPageHeader->u8PageType;
+ pReply->u8PageNumber = pPageHeader->u8PageNumber;
+ pReply->u8PageLength = pPageHeader->u8PageLength;
+ pReply->u8PageVersion = pPageHeader->u8PageVersion;
+
+ for (int i = 0; i < pReply->u8PageLength; i++)
+ LogFlowFunc(("PageData[%d]=%#x\n", i, ((uint32_t *)pbPageData)[i]));
+ }
+
+ /*
+ * Don't use the scatter gather handling code as the configuration request always have only one
+ * simple element.
+ */
+ switch (pConfigurationReq->u8Action)
+ {
+ case MPT_CONFIGURATION_REQUEST_ACTION_DEFAULT: /* Nothing to do. We are always using the defaults. */
+ case MPT_CONFIGURATION_REQUEST_ACTION_HEADER:
+ {
+ /* Already copied above nothing to do. */
+ break;
+ }
+ case MPT_CONFIGURATION_REQUEST_ACTION_READ_NVRAM:
+ case MPT_CONFIGURATION_REQUEST_ACTION_READ_CURRENT:
+ case MPT_CONFIGURATION_REQUEST_ACTION_READ_DEFAULT:
+ {
+ uint32_t cbBuffer = pConfigurationReq->SimpleSGElement.u24Length;
+ if (cbBuffer != 0)
+ {
+ RTGCPHYS GCPhysAddrPageBuffer = pConfigurationReq->SimpleSGElement.u32DataBufferAddressLow;
+ if (pConfigurationReq->SimpleSGElement.f64BitAddress)
+ GCPhysAddrPageBuffer |= (uint64_t)pConfigurationReq->SimpleSGElement.u32DataBufferAddressHigh << 32;
+
+ PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhysAddrPageBuffer, pbPageData, RT_MIN(cbBuffer, cbPage));
+ }
+ break;
+ }
+ case MPT_CONFIGURATION_REQUEST_ACTION_WRITE_CURRENT:
+ case MPT_CONFIGURATION_REQUEST_ACTION_WRITE_NVRAM:
+ {
+ uint32_t cbBuffer = pConfigurationReq->SimpleSGElement.u24Length;
+ if (cbBuffer != 0)
+ {
+ RTGCPHYS GCPhysAddrPageBuffer = pConfigurationReq->SimpleSGElement.u32DataBufferAddressLow;
+ if (pConfigurationReq->SimpleSGElement.f64BitAddress)
+ GCPhysAddrPageBuffer |= (uint64_t)pConfigurationReq->SimpleSGElement.u32DataBufferAddressHigh << 32;
+
+ LogFlow(("cbBuffer=%u cbPage=%u\n", cbBuffer, cbPage));
+
+ PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCPhysAddrPageBuffer, pbPageData,
+ RT_MIN(cbBuffer, cbPage));
+ }
+ break;
+ }
+ default:
+ AssertMsgFailed(("todo\n"));
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Initializes the configuration pages for the SPI SCSI controller.
+ *
+ * @returns nothing
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+static void lsilogicR3InitializeConfigurationPagesSpi(PLSILOGICSCSI pThis)
+{
+ PMptConfigurationPagesSpi pPages = &pThis->pConfigurationPages->u.SpiPages;
+
+ AssertMsg(pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI, ("Controller is not the SPI SCSI one\n"));
+
+ LogFlowFunc(("pThis=%#p\n", pThis));
+
+ /* Clear everything first. */
+ memset(pPages, 0, sizeof(MptConfigurationPagesSpi));
+
+ for (unsigned i = 0; i < RT_ELEMENTS(pPages->aPortPages); i++)
+ {
+ /* SCSI-SPI port page 0. */
+ pPages->aPortPages[i].SCSISPIPortPage0.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
+ | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT;
+ pPages->aPortPages[i].SCSISPIPortPage0.u.fields.Header.u8PageNumber = 0;
+ pPages->aPortPages[i].SCSISPIPortPage0.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIPort0) / 4;
+ pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fInformationUnitTransfersCapable = true;
+ pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fDTCapable = true;
+ pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fQASCapable = true;
+ pPages->aPortPages[i].SCSISPIPortPage0.u.fields.u8MinimumSynchronousTransferPeriod = 0;
+ pPages->aPortPages[i].SCSISPIPortPage0.u.fields.u8MaximumSynchronousOffset = 0xff;
+ pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fWide = true;
+ pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fAIPCapable = true;
+ pPages->aPortPages[i].SCSISPIPortPage0.u.fields.u2SignalingType = 0x3; /* Single Ended. */
+
+ /* SCSI-SPI port page 1. */
+ pPages->aPortPages[i].SCSISPIPortPage1.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
+ | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT;
+ pPages->aPortPages[i].SCSISPIPortPage1.u.fields.Header.u8PageNumber = 1;
+ pPages->aPortPages[i].SCSISPIPortPage1.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIPort1) / 4;
+ pPages->aPortPages[i].SCSISPIPortPage1.u.fields.u8SCSIID = 7;
+ pPages->aPortPages[i].SCSISPIPortPage1.u.fields.u16PortResponseIDsBitmask = (1 << 7);
+ pPages->aPortPages[i].SCSISPIPortPage1.u.fields.u32OnBusTimerValue = 0;
+
+ /* SCSI-SPI port page 2. */
+ pPages->aPortPages[i].SCSISPIPortPage2.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
+ | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT;
+ pPages->aPortPages[i].SCSISPIPortPage2.u.fields.Header.u8PageNumber = 2;
+ pPages->aPortPages[i].SCSISPIPortPage2.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIPort2) / 4;
+ pPages->aPortPages[i].SCSISPIPortPage2.u.fields.u4HostSCSIID = 7;
+ pPages->aPortPages[i].SCSISPIPortPage2.u.fields.u2InitializeHBA = 0x3;
+ pPages->aPortPages[i].SCSISPIPortPage2.u.fields.fTerminationDisabled = true;
+ for (unsigned iDevice = 0; iDevice < RT_ELEMENTS(pPages->aPortPages[i].SCSISPIPortPage2.u.fields.aDeviceSettings); iDevice++)
+ {
+ pPages->aPortPages[i].SCSISPIPortPage2.u.fields.aDeviceSettings[iDevice].fBootChoice = true;
+ }
+ /* Everything else 0 for now. */
+ }
+
+ for (unsigned uBusCurr = 0; uBusCurr < RT_ELEMENTS(pPages->aBuses); uBusCurr++)
+ {
+ for (unsigned uDeviceCurr = 0; uDeviceCurr < RT_ELEMENTS(pPages->aBuses[uBusCurr].aDevicePages); uDeviceCurr++)
+ {
+ /* SCSI-SPI device page 0. */
+ pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage0.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
+ | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE;
+ pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage0.u.fields.Header.u8PageNumber = 0;
+ pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage0.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIDevice0) / 4;
+ /* Everything else 0 for now. */
+
+ /* SCSI-SPI device page 1. */
+ pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage1.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
+ | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE;
+ pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage1.u.fields.Header.u8PageNumber = 1;
+ pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage1.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIDevice1) / 4;
+ /* Everything else 0 for now. */
+
+ /* SCSI-SPI device page 2. */
+ pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage2.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
+ | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE;
+ pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage2.u.fields.Header.u8PageNumber = 2;
+ pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage2.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIDevice2) / 4;
+ /* Everything else 0 for now. */
+
+ pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage3.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
+ | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE;
+ pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage3.u.fields.Header.u8PageNumber = 3;
+ pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage3.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIDevice3) / 4;
+ /* Everything else 0 for now. */
+ }
+ }
+}
+
+/**
+ * Generates a handle.
+ *
+ * @returns the handle.
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+DECLINLINE(uint16_t) lsilogicGetHandle(PLSILOGICSCSI pThis)
+{
+ uint16_t u16Handle = pThis->u16NextHandle++;
+ return u16Handle;
+}
+
+/**
+ * Generates a SAS address (WWID)
+ *
+ * @returns nothing.
+ * @param pSASAddress Pointer to an unitialised SAS address.
+ * @param iId iId which will go into the address.
+ *
+ * @todo Generate better SAS addresses. (Request a block from SUN probably)
+ */
+void lsilogicSASAddressGenerate(PSASADDRESS pSASAddress, unsigned iId)
+{
+ pSASAddress->u8Address[0] = (0x5 << 5);
+ pSASAddress->u8Address[1] = 0x01;
+ pSASAddress->u8Address[2] = 0x02;
+ pSASAddress->u8Address[3] = 0x03;
+ pSASAddress->u8Address[4] = 0x04;
+ pSASAddress->u8Address[5] = 0x05;
+ pSASAddress->u8Address[6] = 0x06;
+ pSASAddress->u8Address[7] = iId;
+}
+
+/**
+ * Initializes the configuration pages for the SAS SCSI controller.
+ *
+ * @returns nothing
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+static void lsilogicR3InitializeConfigurationPagesSas(PLSILOGICSCSI pThis)
+{
+ PMptConfigurationPagesSas pPages = &pThis->pConfigurationPages->u.SasPages;
+
+ AssertMsg(pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS, ("Controller is not the SAS SCSI one\n"));
+
+ LogFlowFunc(("pThis=%#p\n", pThis));
+
+ /* Manufacturing Page 7 - Connector settings. */
+ pPages->cbManufacturingPage7 = LSILOGICSCSI_MANUFACTURING7_GET_SIZE(pThis->cPorts);
+ PMptConfigurationPageManufacturing7 pManufacturingPage7 = (PMptConfigurationPageManufacturing7)RTMemAllocZ(pPages->cbManufacturingPage7);
+ AssertPtr(pManufacturingPage7);
+ MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(pManufacturingPage7,
+ 0, 7,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
+ /* Set size manually. */
+ if (pPages->cbManufacturingPage7 / 4 > 255)
+ pManufacturingPage7->u.fields.Header.u8PageLength = 255;
+ else
+ pManufacturingPage7->u.fields.Header.u8PageLength = pPages->cbManufacturingPage7 / 4;
+ pManufacturingPage7->u.fields.u8NumPhys = pThis->cPorts;
+ pPages->pManufacturingPage7 = pManufacturingPage7;
+
+ /* SAS I/O unit page 0 - Port specific information. */
+ pPages->cbSASIOUnitPage0 = LSILOGICSCSI_SASIOUNIT0_GET_SIZE(pThis->cPorts);
+ PMptConfigurationPageSASIOUnit0 pSASPage0 = (PMptConfigurationPageSASIOUnit0)RTMemAllocZ(pPages->cbSASIOUnitPage0);
+ AssertPtr(pSASPage0);
+
+ MPT_CONFIG_EXTENDED_PAGE_HEADER_INIT(pSASPage0, pPages->cbSASIOUnitPage0,
+ 0, MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY,
+ MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT);
+ pSASPage0->u.fields.u8NumPhys = pThis->cPorts;
+ pPages->pSASIOUnitPage0 = pSASPage0;
+
+ /* SAS I/O unit page 1 - Port specific settings. */
+ pPages->cbSASIOUnitPage1 = LSILOGICSCSI_SASIOUNIT1_GET_SIZE(pThis->cPorts);
+ PMptConfigurationPageSASIOUnit1 pSASPage1 = (PMptConfigurationPageSASIOUnit1)RTMemAllocZ(pPages->cbSASIOUnitPage1);
+ AssertPtr(pSASPage1);
+
+ MPT_CONFIG_EXTENDED_PAGE_HEADER_INIT(pSASPage1, pPages->cbSASIOUnitPage1,
+ 1, MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE,
+ MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT);
+ pSASPage1->u.fields.u8NumPhys = pSASPage0->u.fields.u8NumPhys;
+ pSASPage1->u.fields.u16ControlFlags = 0;
+ pSASPage1->u.fields.u16AdditionalControlFlags = 0;
+ pPages->pSASIOUnitPage1 = pSASPage1;
+
+ /* SAS I/O unit page 2 - Port specific information. */
+ pPages->SASIOUnitPage2.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
+ | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
+ pPages->SASIOUnitPage2.u.fields.ExtHeader.u8PageNumber = 2;
+ pPages->SASIOUnitPage2.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT;
+ pPages->SASIOUnitPage2.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASIOUnit2) / 4;
+
+ /* SAS I/O unit page 3 - Port specific information. */
+ pPages->SASIOUnitPage3.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
+ | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
+ pPages->SASIOUnitPage3.u.fields.ExtHeader.u8PageNumber = 3;
+ pPages->SASIOUnitPage3.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT;
+ pPages->SASIOUnitPage3.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASIOUnit3) / 4;
+
+ pPages->cPHYs = pThis->cPorts;
+ pPages->paPHYs = (PMptPHY)RTMemAllocZ(pPages->cPHYs * sizeof(MptPHY));
+ AssertPtr(pPages->paPHYs);
+
+ /* Initialize the PHY configuration */
+ for (unsigned i = 0; i < pThis->cPorts; i++)
+ {
+ PMptPHY pPHYPages = &pPages->paPHYs[i];
+ uint16_t u16ControllerHandle = lsilogicGetHandle(pThis);
+
+ pManufacturingPage7->u.fields.aPHY[i].u8Location = LSILOGICSCSI_MANUFACTURING7_LOCATION_AUTO;
+
+ pSASPage0->u.fields.aPHY[i].u8Port = i;
+ pSASPage0->u.fields.aPHY[i].u8PortFlags = 0;
+ pSASPage0->u.fields.aPHY[i].u8PhyFlags = 0;
+ pSASPage0->u.fields.aPHY[i].u8NegotiatedLinkRate = LSILOGICSCSI_SASIOUNIT0_NEGOTIATED_RATE_FAILED;
+ pSASPage0->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_NO);
+ pSASPage0->u.fields.aPHY[i].u16ControllerDevHandle = u16ControllerHandle;
+ pSASPage0->u.fields.aPHY[i].u16AttachedDevHandle = 0; /* No device attached. */
+ pSASPage0->u.fields.aPHY[i].u32DiscoveryStatus = 0; /* No errors */
+
+ pSASPage1->u.fields.aPHY[i].u8Port = i;
+ pSASPage1->u.fields.aPHY[i].u8PortFlags = 0;
+ pSASPage1->u.fields.aPHY[i].u8PhyFlags = 0;
+ pSASPage1->u.fields.aPHY[i].u8MaxMinLinkRate = LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MIN_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_15GB)
+ | LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MAX_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_30GB);
+ pSASPage1->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_NO);
+
+ /* SAS PHY page 0. */
+ pPHYPages->SASPHYPage0.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
+ | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
+ pPHYPages->SASPHYPage0.u.fields.ExtHeader.u8PageNumber = 0;
+ pPHYPages->SASPHYPage0.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASPHYS;
+ pPHYPages->SASPHYPage0.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASPHY0) / 4;
+ pPHYPages->SASPHYPage0.u.fields.u8AttachedPhyIdentifier = i;
+ pPHYPages->SASPHYPage0.u.fields.u32AttachedDeviceInfo = LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_SET(LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_NO);
+ pPHYPages->SASPHYPage0.u.fields.u8ProgrammedLinkRate = LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MIN_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_15GB)
+ | LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MAX_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_30GB);
+ pPHYPages->SASPHYPage0.u.fields.u8HwLinkRate = LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MIN_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_15GB)
+ | LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MAX_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_30GB);
+
+ /* SAS PHY page 1. */
+ pPHYPages->SASPHYPage1.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
+ | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
+ pPHYPages->SASPHYPage1.u.fields.ExtHeader.u8PageNumber = 1;
+ pPHYPages->SASPHYPage1.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASPHYS;
+ pPHYPages->SASPHYPage1.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASPHY1) / 4;
+
+ /* Settings for present devices. */
+ if (pThis->paDeviceStates[i].pDrvBase)
+ {
+ uint16_t u16DeviceHandle = lsilogicGetHandle(pThis);
+ SASADDRESS SASAddress;
+ PMptSASDevice pSASDevice = (PMptSASDevice)RTMemAllocZ(sizeof(MptSASDevice));
+ AssertPtr(pSASDevice);
+
+ memset(&SASAddress, 0, sizeof(SASADDRESS));
+ lsilogicSASAddressGenerate(&SASAddress, i);
+
+ pSASPage0->u.fields.aPHY[i].u8NegotiatedLinkRate = LSILOGICSCSI_SASIOUNIT0_NEGOTIATED_RATE_SET(LSILOGICSCSI_SASIOUNIT0_NEGOTIATED_RATE_30GB);
+ pSASPage0->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_END)
+ | LSILOGICSCSI_SASIOUNIT0_DEVICE_SSP_TARGET;
+ pSASPage0->u.fields.aPHY[i].u16AttachedDevHandle = u16DeviceHandle;
+ pSASPage1->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_END)
+ | LSILOGICSCSI_SASIOUNIT0_DEVICE_SSP_TARGET;
+ pSASPage0->u.fields.aPHY[i].u16ControllerDevHandle = u16DeviceHandle;
+
+ pPHYPages->SASPHYPage0.u.fields.u32AttachedDeviceInfo = LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_SET(LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_END);
+ pPHYPages->SASPHYPage0.u.fields.SASAddress = SASAddress;
+ pPHYPages->SASPHYPage0.u.fields.u16OwnerDevHandle = u16DeviceHandle;
+ pPHYPages->SASPHYPage0.u.fields.u16AttachedDevHandle = u16DeviceHandle;
+
+ /* SAS device page 0. */
+ pSASDevice->SASDevicePage0.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
+ | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
+ pSASDevice->SASDevicePage0.u.fields.ExtHeader.u8PageNumber = 0;
+ pSASDevice->SASDevicePage0.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE;
+ pSASDevice->SASDevicePage0.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASDevice0) / 4;
+ pSASDevice->SASDevicePage0.u.fields.SASAddress = SASAddress;
+ pSASDevice->SASDevicePage0.u.fields.u16ParentDevHandle = u16ControllerHandle;
+ pSASDevice->SASDevicePage0.u.fields.u8PhyNum = i;
+ pSASDevice->SASDevicePage0.u.fields.u8AccessStatus = LSILOGICSCSI_SASDEVICE0_STATUS_NO_ERRORS;
+ pSASDevice->SASDevicePage0.u.fields.u16DevHandle = u16DeviceHandle;
+ pSASDevice->SASDevicePage0.u.fields.u8TargetID = i;
+ pSASDevice->SASDevicePage0.u.fields.u8Bus = 0;
+ pSASDevice->SASDevicePage0.u.fields.u32DeviceInfo = LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_SET(LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_END)
+ | LSILOGICSCSI_SASIOUNIT0_DEVICE_SSP_TARGET;
+ pSASDevice->SASDevicePage0.u.fields.u16Flags = LSILOGICSCSI_SASDEVICE0_FLAGS_DEVICE_PRESENT
+ | LSILOGICSCSI_SASDEVICE0_FLAGS_DEVICE_MAPPED_TO_BUS_AND_TARGET_ID
+ | LSILOGICSCSI_SASDEVICE0_FLAGS_DEVICE_MAPPING_PERSISTENT;
+ pSASDevice->SASDevicePage0.u.fields.u8PhysicalPort = i;
+
+ /* SAS device page 1. */
+ pSASDevice->SASDevicePage1.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
+ | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
+ pSASDevice->SASDevicePage1.u.fields.ExtHeader.u8PageNumber = 1;
+ pSASDevice->SASDevicePage1.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE;
+ pSASDevice->SASDevicePage1.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASDevice1) / 4;
+ pSASDevice->SASDevicePage1.u.fields.SASAddress = SASAddress;
+ pSASDevice->SASDevicePage1.u.fields.u16DevHandle = u16DeviceHandle;
+ pSASDevice->SASDevicePage1.u.fields.u8TargetID = i;
+ pSASDevice->SASDevicePage1.u.fields.u8Bus = 0;
+
+ /* SAS device page 2. */
+ pSASDevice->SASDevicePage2.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
+ | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
+ pSASDevice->SASDevicePage2.u.fields.ExtHeader.u8PageNumber = 2;
+ pSASDevice->SASDevicePage2.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE;
+ pSASDevice->SASDevicePage2.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASDevice2) / 4;
+ pSASDevice->SASDevicePage2.u.fields.SASAddress = SASAddress;
+
+ /* Link into device list. */
+ if (!pPages->cDevices)
+ {
+ pPages->pSASDeviceHead = pSASDevice;
+ pPages->pSASDeviceTail = pSASDevice;
+ pPages->cDevices = 1;
+ }
+ else
+ {
+ pSASDevice->pPrev = pPages->pSASDeviceTail;
+ pPages->pSASDeviceTail->pNext = pSASDevice;
+ pPages->pSASDeviceTail = pSASDevice;
+ pPages->cDevices++;
+ }
+ }
+ }
+}
+
+/**
+ * Initializes the configuration pages.
+ *
+ * @returns nothing
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+static void lsilogicR3InitializeConfigurationPages(PLSILOGICSCSI pThis)
+{
+ /* Initialize the common pages. */
+ PMptConfigurationPagesSupported pPages = (PMptConfigurationPagesSupported)RTMemAllocZ(sizeof(MptConfigurationPagesSupported));
+
+ pThis->pConfigurationPages = pPages;
+
+ LogFlowFunc(("pThis=%#p\n", pThis));
+
+ /* Clear everything first. */
+ memset(pPages, 0, sizeof(MptConfigurationPagesSupported));
+
+ /* Manufacturing Page 0. */
+ MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage0,
+ MptConfigurationPageManufacturing0, 0,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
+ strncpy((char *)pPages->ManufacturingPage0.u.fields.abChipName, "VBox MPT Fusion", 16);
+ strncpy((char *)pPages->ManufacturingPage0.u.fields.abChipRevision, "1.0", 8);
+ strncpy((char *)pPages->ManufacturingPage0.u.fields.abBoardName, "VBox MPT Fusion", 16);
+ strncpy((char *)pPages->ManufacturingPage0.u.fields.abBoardAssembly, "SUN", 8);
+ memcpy(pPages->ManufacturingPage0.u.fields.abBoardTracerNumber, "CAFECAFECAFECAFE", 16);
+
+ /* Manufacturing Page 1 - I don't know what this contains so we leave it 0 for now. */
+ MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage1,
+ MptConfigurationPageManufacturing1, 1,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
+
+ /* Manufacturing Page 2. */
+ MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage2,
+ MptConfigurationPageManufacturing2, 2,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
+
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ {
+ pPages->ManufacturingPage2.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SPI_DEVICE_ID;
+ pPages->ManufacturingPage2.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SPI_REVISION_ID;
+ }
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ {
+ pPages->ManufacturingPage2.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SAS_DEVICE_ID;
+ pPages->ManufacturingPage2.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SAS_REVISION_ID;
+ }
+
+ /* Manufacturing Page 3. */
+ MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage3,
+ MptConfigurationPageManufacturing3, 3,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
+
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ {
+ pPages->ManufacturingPage3.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SPI_DEVICE_ID;
+ pPages->ManufacturingPage3.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SPI_REVISION_ID;
+ }
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ {
+ pPages->ManufacturingPage3.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SAS_DEVICE_ID;
+ pPages->ManufacturingPage3.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SAS_REVISION_ID;
+ }
+
+ /* Manufacturing Page 4 - I don't know what this contains so we leave it 0 for now. */
+ MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage4,
+ MptConfigurationPageManufacturing4, 4,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
+
+ /* Manufacturing Page 5 - WWID settings. */
+ MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage5,
+ MptConfigurationPageManufacturing5, 5,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
+
+ /* Manufacturing Page 6 - Product specific settings. */
+ MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage6,
+ MptConfigurationPageManufacturing6, 6,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
+
+ /* Manufacturing Page 8 - Product specific settings. */
+ MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage8,
+ MptConfigurationPageManufacturing8, 8,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
+
+ /* Manufacturing Page 9 - Product specific settings. */
+ MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage9,
+ MptConfigurationPageManufacturing9, 9,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
+
+ /* Manufacturing Page 10 - Product specific settings. */
+ MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage10,
+ MptConfigurationPageManufacturing10, 10,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
+
+ /* I/O Unit page 0. */
+ MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage0,
+ MptConfigurationPageIOUnit0, 0,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
+ pPages->IOUnitPage0.u.fields.u64UniqueIdentifier = 0xcafe;
+
+ /* I/O Unit page 1. */
+ MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage1,
+ MptConfigurationPageIOUnit1, 1,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
+ pPages->IOUnitPage1.u.fields.fSingleFunction = true;
+ pPages->IOUnitPage1.u.fields.fAllPathsMapped = false;
+ pPages->IOUnitPage1.u.fields.fIntegratedRAIDDisabled = true;
+ pPages->IOUnitPage1.u.fields.f32BitAccessForced = false;
+
+ /* I/O Unit page 2. */
+ MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage2,
+ MptConfigurationPageIOUnit2, 2,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT);
+ pPages->IOUnitPage2.u.fields.fPauseOnError = false;
+ pPages->IOUnitPage2.u.fields.fVerboseModeEnabled = false;
+ pPages->IOUnitPage2.u.fields.fDisableColorVideo = false;
+ pPages->IOUnitPage2.u.fields.fNotHookInt40h = false;
+ pPages->IOUnitPage2.u.fields.u32BIOSVersion = 0xcafecafe;
+ pPages->IOUnitPage2.u.fields.aAdapterOrder[0].fAdapterEnabled = true;
+ pPages->IOUnitPage2.u.fields.aAdapterOrder[0].fAdapterEmbedded = true;
+ pPages->IOUnitPage2.u.fields.aAdapterOrder[0].u8PCIBusNumber = 0;
+ pPages->IOUnitPage2.u.fields.aAdapterOrder[0].u8PCIDevFn = pThis->PciDev.uDevFn;
+
+ /* I/O Unit page 3. */
+ MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage3,
+ MptConfigurationPageIOUnit3, 3,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
+ pPages->IOUnitPage3.u.fields.u8GPIOCount = 0;
+
+ /* I/O Unit page 4. */
+ MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage4,
+ MptConfigurationPageIOUnit4, 4,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
+
+ /* IOC page 0. */
+ MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage0,
+ MptConfigurationPageIOC0, 0,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
+ pPages->IOCPage0.u.fields.u32TotalNVStore = 0;
+ pPages->IOCPage0.u.fields.u32FreeNVStore = 0;
+
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ {
+ pPages->IOCPage0.u.fields.u16VendorId = LSILOGICSCSI_PCI_VENDOR_ID;
+ pPages->IOCPage0.u.fields.u16DeviceId = LSILOGICSCSI_PCI_SPI_DEVICE_ID;
+ pPages->IOCPage0.u.fields.u8RevisionId = LSILOGICSCSI_PCI_SPI_REVISION_ID;
+ pPages->IOCPage0.u.fields.u32ClassCode = LSILOGICSCSI_PCI_SPI_CLASS_CODE;
+ pPages->IOCPage0.u.fields.u16SubsystemVendorId = LSILOGICSCSI_PCI_SPI_SUBSYSTEM_VENDOR_ID;
+ pPages->IOCPage0.u.fields.u16SubsystemId = LSILOGICSCSI_PCI_SPI_SUBSYSTEM_ID;
+ }
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ {
+ pPages->IOCPage0.u.fields.u16VendorId = LSILOGICSCSI_PCI_VENDOR_ID;
+ pPages->IOCPage0.u.fields.u16DeviceId = LSILOGICSCSI_PCI_SAS_DEVICE_ID;
+ pPages->IOCPage0.u.fields.u8RevisionId = LSILOGICSCSI_PCI_SAS_REVISION_ID;
+ pPages->IOCPage0.u.fields.u32ClassCode = LSILOGICSCSI_PCI_SAS_CLASS_CODE;
+ pPages->IOCPage0.u.fields.u16SubsystemVendorId = LSILOGICSCSI_PCI_SAS_SUBSYSTEM_VENDOR_ID;
+ pPages->IOCPage0.u.fields.u16SubsystemId = LSILOGICSCSI_PCI_SAS_SUBSYSTEM_ID;
+ }
+
+ /* IOC page 1. */
+ MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage1,
+ MptConfigurationPageIOC1, 1,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
+ pPages->IOCPage1.u.fields.fReplyCoalescingEnabled = false;
+ pPages->IOCPage1.u.fields.u32CoalescingTimeout = 0;
+ pPages->IOCPage1.u.fields.u8CoalescingDepth = 0;
+
+ /* IOC page 2. */
+ MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage2,
+ MptConfigurationPageIOC2, 2,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
+ /* Everything else here is 0. */
+
+ /* IOC page 3. */
+ MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage3,
+ MptConfigurationPageIOC3, 3,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
+ /* Everything else here is 0. */
+
+ /* IOC page 4. */
+ MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage4,
+ MptConfigurationPageIOC4, 4,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
+ /* Everything else here is 0. */
+
+ /* IOC page 6. */
+ MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage6,
+ MptConfigurationPageIOC6, 6,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
+ /* Everything else here is 0. */
+
+ /* BIOS page 1. */
+ MPT_CONFIG_PAGE_HEADER_INIT_BIOS(&pPages->BIOSPage1,
+ MptConfigurationPageBIOS1, 1,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
+
+ /* BIOS page 2. */
+ MPT_CONFIG_PAGE_HEADER_INIT_BIOS(&pPages->BIOSPage2,
+ MptConfigurationPageBIOS2, 2,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
+
+ /* BIOS page 4. */
+ MPT_CONFIG_PAGE_HEADER_INIT_BIOS(&pPages->BIOSPage4,
+ MptConfigurationPageBIOS4, 4,
+ MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
+
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ lsilogicR3InitializeConfigurationPagesSpi(pThis);
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ lsilogicR3InitializeConfigurationPagesSas(pThis);
+ else
+ AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
+}
+
+/**
+ * @callback_method_impl{FNPDMQUEUEDEV, Transmit queue consumer.}
+ */
+static DECLCALLBACK(bool) lsilogicR3NotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
+{
+ RT_NOREF(pItem);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("pDevIns=%#p pItem=%#p\n", pDevIns, pItem));
+
+ rc = SUPSemEventSignal(pThis->pSupDrvSession, pThis->hEvtProcess);
+ AssertRC(rc);
+
+ return true;
+}
+
+/**
+ * Sets the emulated controller type from a given string.
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pcszCtrlType The string to use.
+ */
+static int lsilogicR3GetCtrlTypeFromString(PLSILOGICSCSI pThis, const char *pcszCtrlType)
+{
+ int rc = VERR_INVALID_PARAMETER;
+
+ if (!RTStrCmp(pcszCtrlType, LSILOGICSCSI_PCI_SPI_CTRLNAME))
+ {
+ pThis->enmCtrlType = LSILOGICCTRLTYPE_SCSI_SPI;
+ rc = VINF_SUCCESS;
+ }
+ else if (!RTStrCmp(pcszCtrlType, LSILOGICSCSI_PCI_SAS_CTRLNAME))
+ {
+ pThis->enmCtrlType = LSILOGICCTRLTYPE_SCSI_SAS;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN, Legacy ISA port.}
+ */
+static DECLCALLBACK(int) lsilogicR3IsaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
+{
+ RT_NOREF(pvUser, cb);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+
+ Assert(cb == 1);
+
+ uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
+ ? Port - LSILOGIC_BIOS_IO_PORT
+ : Port - LSILOGIC_SAS_BIOS_IO_PORT;
+ int rc = vboxscsiReadRegister(&pThis->VBoxSCSI, iRegister, pu32);
+
+ Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
+ __FUNCTION__, pu32, 1, pu32, iRegister, rc));
+
+ return rc;
+}
+
+/**
+ * Prepares a request from the BIOS.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+static int lsilogicR3PrepareBiosScsiRequest(PLSILOGICSCSI pThis)
+{
+ int rc;
+ uint32_t uTargetDevice;
+ uint32_t uLun;
+ uint8_t *pbCdb;
+ size_t cbCdb;
+ size_t cbBuf;
+
+ rc = vboxscsiSetupRequest(&pThis->VBoxSCSI, &uLun, &pbCdb, &cbCdb, &cbBuf, &uTargetDevice);
+ AssertMsgRCReturn(rc, ("Setting up SCSI request failed rc=%Rrc\n", rc), rc);
+
+ if ( uTargetDevice < pThis->cDeviceStates
+ && pThis->paDeviceStates[uTargetDevice].pDrvBase)
+ {
+ PLSILOGICDEVICE pTgtDev = &pThis->paDeviceStates[uTargetDevice];
+ PDMMEDIAEXIOREQ hIoReq;
+ PLSILOGICREQ pReq;
+
+ rc = pTgtDev->pDrvMediaEx->pfnIoReqAlloc(pTgtDev->pDrvMediaEx, &hIoReq, (void **)&pReq,
+ 0, PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR);
+ AssertMsgRCReturn(rc, ("Getting task from cache failed rc=%Rrc\n", rc), rc);
+
+ pReq->fBIOS = true;
+ pReq->hIoReq = hIoReq;
+ pReq->pTargetDevice = pTgtDev;
+
+ ASMAtomicIncU32(&pTgtDev->cOutstandingRequests);
+
+ rc = pTgtDev->pDrvMediaEx->pfnIoReqSendScsiCmd(pTgtDev->pDrvMediaEx, pReq->hIoReq, uLun,
+ pbCdb, cbCdb, PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN,
+ cbBuf, NULL, 0, &pReq->u8ScsiSts, 30 * RT_MS_1SEC);
+ if (rc == VINF_SUCCESS || rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
+ {
+ uint8_t u8ScsiSts = pReq->u8ScsiSts;
+ pTgtDev->pDrvMediaEx->pfnIoReqFree(pTgtDev->pDrvMediaEx, pReq->hIoReq);
+ rc = vboxscsiRequestFinished(&pThis->VBoxSCSI, u8ScsiSts);
+ }
+ else if (rc == VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
+ rc = VINF_SUCCESS;
+
+ return rc;
+ }
+
+ /* Device is not present. */
+ AssertMsg(pbCdb[0] == SCSI_INQUIRY,
+ ("Device is not present but command is not inquiry\n"));
+
+ SCSIINQUIRYDATA ScsiInquiryData;
+
+ memset(&ScsiInquiryData, 0, sizeof(SCSIINQUIRYDATA));
+ ScsiInquiryData.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
+ ScsiInquiryData.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
+
+ memcpy(pThis->VBoxSCSI.pbBuf, &ScsiInquiryData, 5);
+
+ rc = vboxscsiRequestFinished(&pThis->VBoxSCSI, SCSI_STATUS_OK);
+ AssertMsgRCReturn(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc), rc);
+
+ return rc;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT, Legacy ISA port.}
+ */
+static DECLCALLBACK(int) lsilogicR3IsaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
+{
+ RT_NOREF(pvUser, cb);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x\n", pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port));
+
+ Assert(cb == 1);
+
+ /*
+ * If there is already a request form the BIOS pending ignore this write
+ * because it should not happen.
+ */
+ if (ASMAtomicReadBool(&pThis->fBiosReqPending))
+ return VINF_SUCCESS;
+
+ uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
+ ? Port - LSILOGIC_BIOS_IO_PORT
+ : Port - LSILOGIC_SAS_BIOS_IO_PORT;
+ int rc = vboxscsiWriteRegister(&pThis->VBoxSCSI, iRegister, (uint8_t)u32);
+ if (rc == VERR_MORE_DATA)
+ {
+ ASMAtomicXchgBool(&pThis->fBiosReqPending, true);
+ /* Send a notifier to the PDM queue that there are pending requests. */
+ PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pThis->CTX_SUFF(pNotificationQueue));
+ AssertMsg(pItem, ("Allocating item for queue failed\n"));
+ PDMQueueInsert(pThis->CTX_SUFF(pNotificationQueue), (PPDMQUEUEITEMCORE)pItem);
+ }
+ else if (RT_FAILURE(rc))
+ AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUTSTRING,
+ * Port I/O Handler for primary port range OUT string operations.}
+ */
+static DECLCALLBACK(int) lsilogicR3IsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port,
+ uint8_t const *pbSrc, uint32_t *pcTransfers, unsigned cb)
+{
+ RT_NOREF(pvUser);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ Log2(("#%d %s: pvUser=%#p cb=%d Port=%#x\n", pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
+
+ uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
+ ? Port - LSILOGIC_BIOS_IO_PORT
+ : Port - LSILOGIC_SAS_BIOS_IO_PORT;
+ int rc = vboxscsiWriteString(pDevIns, &pThis->VBoxSCSI, iRegister, pbSrc, pcTransfers, cb);
+ if (rc == VERR_MORE_DATA)
+ {
+ ASMAtomicXchgBool(&pThis->fBiosReqPending, true);
+ /* Send a notifier to the PDM queue that there are pending requests. */
+ PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pThis->CTX_SUFF(pNotificationQueue));
+ AssertMsg(pItem, ("Allocating item for queue failed\n"));
+ PDMQueueInsert(pThis->CTX_SUFF(pNotificationQueue), (PPDMQUEUEITEMCORE)pItem);
+ }
+ else if (RT_FAILURE(rc))
+ AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTINSTRING,
+ * Port I/O Handler for primary port range IN string operations.}
+ */
+static DECLCALLBACK(int) lsilogicR3IsaIOPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port,
+ uint8_t *pbDst, uint32_t *pcTransfers, unsigned cb)
+{
+ RT_NOREF(pvUser);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ LogFlowFunc(("#%d %s: pvUser=%#p cb=%d Port=%#x\n", pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
+
+ uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
+ ? Port - LSILOGIC_BIOS_IO_PORT
+ : Port - LSILOGIC_SAS_BIOS_IO_PORT;
+ return vboxscsiReadString(pDevIns, &pThis->VBoxSCSI, iRegister, pbDst, pcTransfers, cb);
+}
+
+/**
+ * @callback_method_impl{FNPCIIOREGIONMAP}
+ */
+static DECLCALLBACK(int) lsilogicR3Map(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
+ RTGCPHYS GCPhysAddress, RTGCPHYS cb,
+ PCIADDRESSSPACE enmType)
+{
+ RT_NOREF(pPciDev);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ int rc = VINF_SUCCESS;
+ const char *pcszCtrl = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
+ ? "LsiLogic"
+ : "LsiLogicSas";
+ const char *pcszDiag = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
+ ? "LsiLogicDiag"
+ : "LsiLogicSasDiag";
+
+ Log2(("%s: registering area at GCPhysAddr=%RGp cb=%RGp\n", __FUNCTION__, GCPhysAddress, cb));
+
+ AssertMsg( (enmType == PCI_ADDRESS_SPACE_MEM && cb >= LSILOGIC_PCI_SPACE_MEM_SIZE)
+ || (enmType == PCI_ADDRESS_SPACE_IO && cb >= LSILOGIC_PCI_SPACE_IO_SIZE),
+ ("PCI region type and size do not match\n"));
+
+ if (enmType == PCI_ADDRESS_SPACE_MEM && iRegion == 1)
+ {
+ /*
+ * Non-4-byte read access to LSILOGIC_REG_REPLY_QUEUE may cause real strange behavior
+ * because the data is part of a physical guest address. But some drivers use 1-byte
+ * access to scan for SCSI controllers. So, we simplify our code by telling IOM to
+ * read DWORDs.
+ *
+ * Regarding writes, we couldn't find anything specific in the specs about what should
+ * happen. So far we've ignored unaligned writes and assumed the missing bytes of
+ * byte and word access to be zero. We suspect that IOMMMIO_FLAGS_WRITE_ONLY_DWORD
+ * or IOMMMIO_FLAGS_WRITE_DWORD_ZEROED would be the most appropriate here, but since we
+ * don't have real hw to test one, the old behavior is kept exactly like it used to be.
+ */
+ /** @todo Check out unaligned writes and non-dword writes on real LsiLogic
+ * hardware. */
+ rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
+ IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+ lsilogicMMIOWrite, lsilogicMMIORead, pcszCtrl);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (pThis->fR0Enabled)
+ {
+ rc = PDMDevHlpMMIORegisterR0(pDevIns, GCPhysAddress, cb, NIL_RTR0PTR /*pvUser*/,
+ "lsilogicMMIOWrite", "lsilogicMMIORead");
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ if (pThis->fGCEnabled)
+ {
+ rc = PDMDevHlpMMIORegisterRC(pDevIns, GCPhysAddress, cb, NIL_RTRCPTR /*pvUser*/,
+ "lsilogicMMIOWrite", "lsilogicMMIORead");
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ pThis->GCPhysMMIOBase = GCPhysAddress;
+ }
+ else if (enmType == PCI_ADDRESS_SPACE_MEM && iRegion == 2)
+ {
+ /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
+ rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
+ IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+ lsilogicDiagnosticWrite, lsilogicDiagnosticRead, pcszDiag);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (pThis->fR0Enabled)
+ {
+ rc = PDMDevHlpMMIORegisterR0(pDevIns, GCPhysAddress, cb, NIL_RTR0PTR /*pvUser*/,
+ "lsilogicDiagnosticWrite", "lsilogicDiagnosticRead");
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ if (pThis->fGCEnabled)
+ {
+ rc = PDMDevHlpMMIORegisterRC(pDevIns, GCPhysAddress, cb, NIL_RTRCPTR /*pvUser*/,
+ "lsilogicDiagnosticWrite", "lsilogicDiagnosticRead");
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ }
+ else if (enmType == PCI_ADDRESS_SPACE_IO)
+ {
+ rc = PDMDevHlpIOPortRegister(pDevIns, (RTIOPORT)GCPhysAddress, LSILOGIC_PCI_SPACE_IO_SIZE,
+ NULL, lsilogicIOPortWrite, lsilogicIOPortRead, NULL, NULL, pcszCtrl);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (pThis->fR0Enabled)
+ {
+ rc = PDMDevHlpIOPortRegisterR0(pDevIns, (RTIOPORT)GCPhysAddress, LSILOGIC_PCI_SPACE_IO_SIZE,
+ 0, "lsilogicIOPortWrite", "lsilogicIOPortRead", NULL, NULL, pcszCtrl);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ if (pThis->fGCEnabled)
+ {
+ rc = PDMDevHlpIOPortRegisterRC(pDevIns, (RTIOPORT)GCPhysAddress, LSILOGIC_PCI_SPACE_IO_SIZE,
+ 0, "lsilogicIOPortWrite", "lsilogicIOPortRead", NULL, NULL, pcszCtrl);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ pThis->IOPortBase = (RTIOPORT)GCPhysAddress;
+ }
+ else
+ AssertMsgFailed(("Invalid enmType=%d iRegion=%d\n", enmType, iRegion));
+
+ return rc;
+}
+
+/**
+ * @callback_method_impl{PFNDBGFHANDLERDEV}
+ */
+static DECLCALLBACK(void) lsilogicR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ bool fVerbose = false;
+
+ /*
+ * Parse args.
+ */
+ if (pszArgs)
+ fVerbose = strstr(pszArgs, "verbose") != NULL;
+
+ /*
+ * Show info.
+ */
+ pHlp->pfnPrintf(pHlp,
+ "%s#%d: port=%RTiop mmio=%RGp max-devices=%u GC=%RTbool R0=%RTbool\n",
+ pDevIns->pReg->szName,
+ pDevIns->iInstance,
+ pThis->IOPortBase, pThis->GCPhysMMIOBase,
+ pThis->cDeviceStates,
+ pThis->fGCEnabled ? true : false,
+ pThis->fR0Enabled ? true : false);
+
+ /*
+ * Show general state.
+ */
+ pHlp->pfnPrintf(pHlp, "enmState=%u\n", pThis->enmState);
+ pHlp->pfnPrintf(pHlp, "enmWhoInit=%u\n", pThis->enmWhoInit);
+ pHlp->pfnPrintf(pHlp, "enmDoorbellState=%d\n", pThis->enmDoorbellState);
+ pHlp->pfnPrintf(pHlp, "fDiagnosticEnabled=%RTbool\n", pThis->fDiagnosticEnabled);
+ pHlp->pfnPrintf(pHlp, "fNotificationSent=%RTbool\n", pThis->fNotificationSent);
+ pHlp->pfnPrintf(pHlp, "fEventNotificationEnabled=%RTbool\n", pThis->fEventNotificationEnabled);
+ pHlp->pfnPrintf(pHlp, "uInterruptMask=%#x\n", pThis->uInterruptMask);
+ pHlp->pfnPrintf(pHlp, "uInterruptStatus=%#x\n", pThis->uInterruptStatus);
+ pHlp->pfnPrintf(pHlp, "u16IOCFaultCode=%#06x\n", pThis->u16IOCFaultCode);
+ pHlp->pfnPrintf(pHlp, "u32HostMFAHighAddr=%#x\n", pThis->u32HostMFAHighAddr);
+ pHlp->pfnPrintf(pHlp, "u32SenseBufferHighAddr=%#x\n", pThis->u32SenseBufferHighAddr);
+ pHlp->pfnPrintf(pHlp, "cMaxDevices=%u\n", pThis->cMaxDevices);
+ pHlp->pfnPrintf(pHlp, "cMaxBuses=%u\n", pThis->cMaxBuses);
+ pHlp->pfnPrintf(pHlp, "cbReplyFrame=%u\n", pThis->cbReplyFrame);
+ pHlp->pfnPrintf(pHlp, "cReplyQueueEntries=%u\n", pThis->cReplyQueueEntries);
+ pHlp->pfnPrintf(pHlp, "cRequestQueueEntries=%u\n", pThis->cRequestQueueEntries);
+ pHlp->pfnPrintf(pHlp, "cPorts=%u\n", pThis->cPorts);
+
+ /*
+ * Show queue status.
+ */
+ pHlp->pfnPrintf(pHlp, "uReplyFreeQueueNextEntryFreeWrite=%u\n", pThis->uReplyFreeQueueNextEntryFreeWrite);
+ pHlp->pfnPrintf(pHlp, "uReplyFreeQueueNextAddressRead=%u\n", pThis->uReplyFreeQueueNextAddressRead);
+ pHlp->pfnPrintf(pHlp, "uReplyPostQueueNextEntryFreeWrite=%u\n", pThis->uReplyPostQueueNextEntryFreeWrite);
+ pHlp->pfnPrintf(pHlp, "uReplyPostQueueNextAddressRead=%u\n", pThis->uReplyPostQueueNextAddressRead);
+ pHlp->pfnPrintf(pHlp, "uRequestQueueNextEntryFreeWrite=%u\n", pThis->uRequestQueueNextEntryFreeWrite);
+ pHlp->pfnPrintf(pHlp, "uRequestQueueNextAddressRead=%u\n", pThis->uRequestQueueNextAddressRead);
+
+ /*
+ * Show queue content if verbose
+ */
+ if (fVerbose)
+ {
+ for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
+ pHlp->pfnPrintf(pHlp, "RFQ[%u]=%#x\n", i, pThis->pReplyFreeQueueBaseR3[i]);
+
+ pHlp->pfnPrintf(pHlp, "\n");
+
+ for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
+ pHlp->pfnPrintf(pHlp, "RPQ[%u]=%#x\n", i, pThis->pReplyPostQueueBaseR3[i]);
+
+ pHlp->pfnPrintf(pHlp, "\n");
+
+ for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
+ pHlp->pfnPrintf(pHlp, "ReqQ[%u]=%#x\n", i, pThis->pRequestQueueBaseR3[i]);
+ }
+
+ /*
+ * Print the device status.
+ */
+ for (unsigned i = 0; i < pThis->cDeviceStates; i++)
+ {
+ PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
+
+ pHlp->pfnPrintf(pHlp, "\n");
+
+ pHlp->pfnPrintf(pHlp, "Device[%u]: device-attached=%RTbool cOutstandingRequests=%u\n",
+ i, pDevice->pDrvBase != NULL, pDevice->cOutstandingRequests);
+ }
+}
+
+/**
+ * Allocate the queues.
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+static int lsilogicR3QueuesAlloc(PLSILOGICSCSI pThis)
+{
+ PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
+ uint32_t cbQueues;
+
+ Assert(!pThis->pReplyFreeQueueBaseR3);
+
+ cbQueues = 2*pThis->cReplyQueueEntries * sizeof(uint32_t);
+ cbQueues += pThis->cRequestQueueEntries * sizeof(uint32_t);
+ int rc = MMHyperAlloc(pVM, cbQueues, 1, MM_TAG_PDM_DEVICE_USER,
+ (void **)&pThis->pReplyFreeQueueBaseR3);
+ if (RT_FAILURE(rc))
+ return VERR_NO_MEMORY;
+ pThis->pReplyFreeQueueBaseR0 = MMHyperR3ToR0(pVM, (void *)pThis->pReplyFreeQueueBaseR3);
+ pThis->pReplyFreeQueueBaseRC = MMHyperR3ToRC(pVM, (void *)pThis->pReplyFreeQueueBaseR3);
+
+ pThis->pReplyPostQueueBaseR3 = pThis->pReplyFreeQueueBaseR3 + pThis->cReplyQueueEntries;
+ pThis->pReplyPostQueueBaseR0 = MMHyperR3ToR0(pVM, (void *)pThis->pReplyPostQueueBaseR3);
+ pThis->pReplyPostQueueBaseRC = MMHyperR3ToRC(pVM, (void *)pThis->pReplyPostQueueBaseR3);
+
+ pThis->pRequestQueueBaseR3 = pThis->pReplyPostQueueBaseR3 + pThis->cReplyQueueEntries;
+ pThis->pRequestQueueBaseR0 = MMHyperR3ToR0(pVM, (void *)pThis->pRequestQueueBaseR3);
+ pThis->pRequestQueueBaseRC = MMHyperR3ToRC(pVM, (void *)pThis->pRequestQueueBaseR3);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Free the hyper memory used or the queues.
+ *
+ * @returns nothing.
+ *
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+static void lsilogicR3QueuesFree(PLSILOGICSCSI pThis)
+{
+ PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
+ int rc = VINF_SUCCESS;
+
+ AssertPtr(pThis->pReplyFreeQueueBaseR3);
+
+ rc = MMHyperFree(pVM, (void *)pThis->pReplyFreeQueueBaseR3);
+ AssertRC(rc);
+
+ pThis->pReplyFreeQueueBaseR3 = NULL;
+ pThis->pReplyPostQueueBaseR3 = NULL;
+ pThis->pRequestQueueBaseR3 = NULL;
+}
+
+
+/* The worker thread. */
+static DECLCALLBACK(int) lsilogicR3Worker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
+{
+ PLSILOGICSCSI pThis = (PLSILOGICSCSI)pThread->pvUser;
+ int rc = VINF_SUCCESS;
+
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ ASMAtomicWriteBool(&pThis->fWrkThreadSleeping, true);
+ bool fNotificationSent = ASMAtomicXchgBool(&pThis->fNotificationSent, false);
+ if (!fNotificationSent)
+ {
+ Assert(ASMAtomicReadBool(&pThis->fWrkThreadSleeping));
+ rc = SUPSemEventWaitNoResume(pThis->pSupDrvSession, pThis->hEvtProcess, RT_INDEFINITE_WAIT);
+ AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
+ if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
+ break;
+ LogFlowFunc(("Woken up with rc=%Rrc\n", rc));
+ ASMAtomicWriteBool(&pThis->fNotificationSent, false);
+ }
+
+ ASMAtomicWriteBool(&pThis->fWrkThreadSleeping, false);
+
+ /* Check whether there is a BIOS request pending and process it first. */
+ if (ASMAtomicReadBool(&pThis->fBiosReqPending))
+ {
+ rc = lsilogicR3PrepareBiosScsiRequest(pThis);
+ AssertRC(rc);
+ ASMAtomicXchgBool(&pThis->fBiosReqPending, false);
+ }
+
+ /* Only process request which arrived before we received the notification. */
+ uint32_t uRequestQueueNextEntryWrite = ASMAtomicReadU32(&pThis->uRequestQueueNextEntryFreeWrite);
+
+ /* Go through the messages now and process them. */
+ while ( RT_LIKELY(pThis->enmState == LSILOGICSTATE_OPERATIONAL)
+ && (pThis->uRequestQueueNextAddressRead != uRequestQueueNextEntryWrite))
+ {
+ MptRequestUnion GuestRequest;
+ uint32_t u32RequestMessageFrameDesc = pThis->CTX_SUFF(pRequestQueueBase)[pThis->uRequestQueueNextAddressRead];
+ RTGCPHYS GCPhysMessageFrameAddr = LSILOGIC_RTGCPHYS_FROM_U32(pThis->u32HostMFAHighAddr,
+ (u32RequestMessageFrameDesc & ~0x07));
+
+ /* Read the message header from the guest first. */
+ PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &GuestRequest, sizeof(MptMessageHdr));
+
+ /* Determine the size of the request. */
+ uint32_t cbRequest = 0;
+ switch (GuestRequest.Header.u8Function)
+ {
+ case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST:
+ cbRequest = sizeof(MptSCSIIORequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_SCSI_TASK_MGMT:
+ cbRequest = sizeof(MptSCSITaskManagementRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_IOC_INIT:
+ cbRequest = sizeof(MptIOCInitRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_IOC_FACTS:
+ cbRequest = sizeof(MptIOCFactsRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_CONFIG:
+ cbRequest = sizeof(MptConfigurationRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_PORT_FACTS:
+ cbRequest = sizeof(MptPortFactsRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_PORT_ENABLE:
+ cbRequest = sizeof(MptPortEnableRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_EVENT_NOTIFICATION:
+ cbRequest = sizeof(MptEventNotificationRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_EVENT_ACK:
+ AssertMsgFailed(("todo\n"));
+ //cbRequest = sizeof(MptEventAckRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_FW_DOWNLOAD:
+ cbRequest = sizeof(MptFWDownloadRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_FW_UPLOAD:
+ cbRequest = sizeof(MptFWUploadRequest);
+ break;
+ default:
+ AssertMsgFailed(("Unknown function issued %u\n", GuestRequest.Header.u8Function));
+ lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INVALID_FUNCTION);
+ }
+
+ if (cbRequest != 0)
+ {
+ /* Read the complete message frame from guest memory now. */
+ PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &GuestRequest, cbRequest);
+
+ /* Handle SCSI I/O requests now. */
+ if (GuestRequest.Header.u8Function == MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST)
+ {
+ rc = lsilogicR3ProcessSCSIIORequest(pThis, GCPhysMessageFrameAddr, &GuestRequest);
+ AssertRC(rc);
+ }
+ else
+ {
+ MptReplyUnion Reply;
+ rc = lsilogicR3ProcessMessageRequest(pThis, &GuestRequest.Header, &Reply);
+ AssertRC(rc);
+ }
+
+ pThis->uRequestQueueNextAddressRead++;
+ pThis->uRequestQueueNextAddressRead %= pThis->cRequestQueueEntries;
+ }
+ } /* While request frames available. */
+ } /* While running */
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Unblock the worker thread so it can respond to a state change.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The pcnet device instance.
+ * @param pThread The send thread.
+ */
+static DECLCALLBACK(int) lsilogicR3WorkerWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
+{
+ RT_NOREF(pThread);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ return SUPSemEventSignal(pThis->pSupDrvSession, pThis->hEvtProcess);
+}
+
+
+/**
+ * Kicks the controller to process pending tasks after the VM was resumed
+ * or loaded from a saved state.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+static void lsilogicR3Kick(PLSILOGICSCSI pThis)
+{
+ if (pThis->fNotificationSent)
+ {
+ /* Send a notifier to the PDM queue that there are pending requests. */
+ PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pThis->CTX_SUFF(pNotificationQueue));
+ AssertMsg(pItem, ("Allocating item for queue failed\n"));
+ PDMQueueInsert(pThis->CTX_SUFF(pNotificationQueue), (PPDMQUEUEITEMCORE)pItem);
+ }
+}
+
+
+/*
+ * Saved state.
+ */
+
+/**
+ * @callback_method_impl{FNSSMDEVLIVEEXEC}
+ */
+static DECLCALLBACK(int) lsilogicR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
+{
+ RT_NOREF(uPass);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+
+ SSMR3PutU32(pSSM, pThis->enmCtrlType);
+ SSMR3PutU32(pSSM, pThis->cDeviceStates);
+ SSMR3PutU32(pSSM, pThis->cPorts);
+
+ /* Save the device config. */
+ for (unsigned i = 0; i < pThis->cDeviceStates; i++)
+ SSMR3PutBool(pSSM, pThis->paDeviceStates[i].pDrvBase != NULL);
+
+ return VINF_SSM_DONT_CALL_AGAIN;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) lsilogicR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+
+ /* Every device first. */
+ lsilogicR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
+ for (unsigned i = 0; i < pThis->cDeviceStates; i++)
+ {
+ PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
+
+ AssertMsg(!pDevice->cOutstandingRequests,
+ ("There are still outstanding requests on this device\n"));
+ SSMR3PutU32(pSSM, pDevice->cOutstandingRequests);
+
+ /* Query all suspended requests and store them in the request queue. */
+ if (pDevice->pDrvMediaEx)
+ {
+ uint32_t cReqsRedo = pDevice->pDrvMediaEx->pfnIoReqGetSuspendedCount(pDevice->pDrvMediaEx);
+ if (cReqsRedo)
+ {
+ PDMMEDIAEXIOREQ hIoReq;
+ PLSILOGICREQ pReq;
+ int rc = pDevice->pDrvMediaEx->pfnIoReqQuerySuspendedStart(pDevice->pDrvMediaEx, &hIoReq,
+ (void **)&pReq);
+ AssertRCBreak(rc);
+
+ for (;;)
+ {
+ if (!pReq->fBIOS)
+ {
+ /* Write only the lower 32bit part of the address. */
+ ASMAtomicWriteU32(&pThis->CTX_SUFF(pRequestQueueBase)[pThis->uRequestQueueNextEntryFreeWrite],
+ pReq->GCPhysMessageFrameAddr & UINT32_C(0xffffffff));
+
+ pThis->uRequestQueueNextEntryFreeWrite++;
+ pThis->uRequestQueueNextEntryFreeWrite %= pThis->cRequestQueueEntries;
+ }
+ else
+ {
+ AssertMsg(!pReq->pRedoNext, ("Only one BIOS task can be active!\n"));
+ vboxscsiSetRequestRedo(&pThis->VBoxSCSI);
+ }
+
+ cReqsRedo--;
+ if (!cReqsRedo)
+ break;
+
+ rc = pDevice->pDrvMediaEx->pfnIoReqQuerySuspendedNext(pDevice->pDrvMediaEx, hIoReq,
+ &hIoReq, (void **)&pReq);
+ AssertRCBreak(rc);
+ }
+ }
+ }
+ }
+
+ /* Now the main device state. */
+ SSMR3PutU32 (pSSM, pThis->enmState);
+ SSMR3PutU32 (pSSM, pThis->enmWhoInit);
+ SSMR3PutU32 (pSSM, pThis->enmDoorbellState);
+ SSMR3PutBool (pSSM, pThis->fDiagnosticEnabled);
+ SSMR3PutBool (pSSM, pThis->fNotificationSent);
+ SSMR3PutBool (pSSM, pThis->fEventNotificationEnabled);
+ SSMR3PutU32 (pSSM, pThis->uInterruptMask);
+ SSMR3PutU32 (pSSM, pThis->uInterruptStatus);
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->aMessage); i++)
+ SSMR3PutU32 (pSSM, pThis->aMessage[i]);
+ SSMR3PutU32 (pSSM, pThis->iMessage);
+ SSMR3PutU32 (pSSM, pThis->cMessage);
+ SSMR3PutMem (pSSM, &pThis->ReplyBuffer, sizeof(pThis->ReplyBuffer));
+ SSMR3PutU32 (pSSM, pThis->uNextReplyEntryRead);
+ SSMR3PutU32 (pSSM, pThis->cReplySize);
+ SSMR3PutU16 (pSSM, pThis->u16IOCFaultCode);
+ SSMR3PutU32 (pSSM, pThis->u32HostMFAHighAddr);
+ SSMR3PutU32 (pSSM, pThis->u32SenseBufferHighAddr);
+ SSMR3PutU8 (pSSM, pThis->cMaxDevices);
+ SSMR3PutU8 (pSSM, pThis->cMaxBuses);
+ SSMR3PutU16 (pSSM, pThis->cbReplyFrame);
+ SSMR3PutU32 (pSSM, pThis->iDiagnosticAccess);
+ SSMR3PutU32 (pSSM, pThis->cReplyQueueEntries);
+ SSMR3PutU32 (pSSM, pThis->cRequestQueueEntries);
+ SSMR3PutU32 (pSSM, pThis->uReplyFreeQueueNextEntryFreeWrite);
+ SSMR3PutU32 (pSSM, pThis->uReplyFreeQueueNextAddressRead);
+ SSMR3PutU32 (pSSM, pThis->uReplyPostQueueNextEntryFreeWrite);
+ SSMR3PutU32 (pSSM, pThis->uReplyPostQueueNextAddressRead);
+ SSMR3PutU32 (pSSM, pThis->uRequestQueueNextEntryFreeWrite);
+ SSMR3PutU32 (pSSM, pThis->uRequestQueueNextAddressRead);
+
+ for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
+ SSMR3PutU32(pSSM, pThis->pReplyFreeQueueBaseR3[i]);
+ for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
+ SSMR3PutU32(pSSM, pThis->pReplyPostQueueBaseR3[i]);
+ for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
+ SSMR3PutU32(pSSM, pThis->pRequestQueueBaseR3[i]);
+
+ SSMR3PutU16 (pSSM, pThis->u16NextHandle);
+
+ /* Save diagnostic memory register and data regions. */
+ SSMR3PutU32 (pSSM, pThis->u32DiagMemAddr);
+ SSMR3PutU32 (pSSM, lsilogicR3MemRegionsCount(pThis));
+
+ PLSILOGICMEMREGN pIt;
+ RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
+ {
+ SSMR3PutU32(pSSM, pIt->u32AddrStart);
+ SSMR3PutU32(pSSM, pIt->u32AddrEnd);
+ SSMR3PutMem(pSSM, &pIt->au32Data[0], (pIt->u32AddrEnd - pIt->u32AddrStart + 1) * sizeof(uint32_t));
+ }
+
+ PMptConfigurationPagesSupported pPages = pThis->pConfigurationPages;
+
+ SSMR3PutMem (pSSM, &pPages->ManufacturingPage0, sizeof(MptConfigurationPageManufacturing0));
+ SSMR3PutMem (pSSM, &pPages->ManufacturingPage1, sizeof(MptConfigurationPageManufacturing1));
+ SSMR3PutMem (pSSM, &pPages->ManufacturingPage2, sizeof(MptConfigurationPageManufacturing2));
+ SSMR3PutMem (pSSM, &pPages->ManufacturingPage3, sizeof(MptConfigurationPageManufacturing3));
+ SSMR3PutMem (pSSM, &pPages->ManufacturingPage4, sizeof(MptConfigurationPageManufacturing4));
+ SSMR3PutMem (pSSM, &pPages->ManufacturingPage5, sizeof(MptConfigurationPageManufacturing5));
+ SSMR3PutMem (pSSM, &pPages->ManufacturingPage6, sizeof(MptConfigurationPageManufacturing6));
+ SSMR3PutMem (pSSM, &pPages->ManufacturingPage8, sizeof(MptConfigurationPageManufacturing8));
+ SSMR3PutMem (pSSM, &pPages->ManufacturingPage9, sizeof(MptConfigurationPageManufacturing9));
+ SSMR3PutMem (pSSM, &pPages->ManufacturingPage10, sizeof(MptConfigurationPageManufacturing10));
+ SSMR3PutMem (pSSM, &pPages->IOUnitPage0, sizeof(MptConfigurationPageIOUnit0));
+ SSMR3PutMem (pSSM, &pPages->IOUnitPage1, sizeof(MptConfigurationPageIOUnit1));
+ SSMR3PutMem (pSSM, &pPages->IOUnitPage2, sizeof(MptConfigurationPageIOUnit2));
+ SSMR3PutMem (pSSM, &pPages->IOUnitPage3, sizeof(MptConfigurationPageIOUnit3));
+ SSMR3PutMem (pSSM, &pPages->IOUnitPage4, sizeof(MptConfigurationPageIOUnit4));
+ SSMR3PutMem (pSSM, &pPages->IOCPage0, sizeof(MptConfigurationPageIOC0));
+ SSMR3PutMem (pSSM, &pPages->IOCPage1, sizeof(MptConfigurationPageIOC1));
+ SSMR3PutMem (pSSM, &pPages->IOCPage2, sizeof(MptConfigurationPageIOC2));
+ SSMR3PutMem (pSSM, &pPages->IOCPage3, sizeof(MptConfigurationPageIOC3));
+ SSMR3PutMem (pSSM, &pPages->IOCPage4, sizeof(MptConfigurationPageIOC4));
+ SSMR3PutMem (pSSM, &pPages->IOCPage6, sizeof(MptConfigurationPageIOC6));
+ SSMR3PutMem (pSSM, &pPages->BIOSPage1, sizeof(MptConfigurationPageBIOS1));
+ SSMR3PutMem (pSSM, &pPages->BIOSPage2, sizeof(MptConfigurationPageBIOS2));
+ SSMR3PutMem (pSSM, &pPages->BIOSPage4, sizeof(MptConfigurationPageBIOS4));
+
+ /* Device dependent pages */
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ {
+ PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
+
+ SSMR3PutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage0, sizeof(MptConfigurationPageSCSISPIPort0));
+ SSMR3PutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage1, sizeof(MptConfigurationPageSCSISPIPort1));
+ SSMR3PutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage2, sizeof(MptConfigurationPageSCSISPIPort2));
+
+ for (unsigned i = 0; i < RT_ELEMENTS(pSpiPages->aBuses[0].aDevicePages); i++)
+ {
+ SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage0, sizeof(MptConfigurationPageSCSISPIDevice0));
+ SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage1, sizeof(MptConfigurationPageSCSISPIDevice1));
+ SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage2, sizeof(MptConfigurationPageSCSISPIDevice2));
+ SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3, sizeof(MptConfigurationPageSCSISPIDevice3));
+ }
+ }
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ {
+ PMptConfigurationPagesSas pSasPages = &pPages->u.SasPages;
+
+ SSMR3PutU32(pSSM, pSasPages->cbManufacturingPage7);
+ SSMR3PutU32(pSSM, pSasPages->cbSASIOUnitPage0);
+ SSMR3PutU32(pSSM, pSasPages->cbSASIOUnitPage1);
+
+ SSMR3PutMem(pSSM, pSasPages->pManufacturingPage7, pSasPages->cbManufacturingPage7);
+ SSMR3PutMem(pSSM, pSasPages->pSASIOUnitPage0, pSasPages->cbSASIOUnitPage0);
+ SSMR3PutMem(pSSM, pSasPages->pSASIOUnitPage1, pSasPages->cbSASIOUnitPage1);
+
+ SSMR3PutMem(pSSM, &pSasPages->SASIOUnitPage2, sizeof(MptConfigurationPageSASIOUnit2));
+ SSMR3PutMem(pSSM, &pSasPages->SASIOUnitPage3, sizeof(MptConfigurationPageSASIOUnit3));
+
+ SSMR3PutU32(pSSM, pSasPages->cPHYs);
+ for (unsigned i = 0; i < pSasPages->cPHYs; i++)
+ {
+ SSMR3PutMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage0, sizeof(MptConfigurationPageSASPHY0));
+ SSMR3PutMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage1, sizeof(MptConfigurationPageSASPHY1));
+ }
+
+ /* The number of devices first. */
+ SSMR3PutU32(pSSM, pSasPages->cDevices);
+
+ PMptSASDevice pCurr = pSasPages->pSASDeviceHead;
+
+ while (pCurr)
+ {
+ SSMR3PutMem(pSSM, &pCurr->SASDevicePage0, sizeof(MptConfigurationPageSASDevice0));
+ SSMR3PutMem(pSSM, &pCurr->SASDevicePage1, sizeof(MptConfigurationPageSASDevice1));
+ SSMR3PutMem(pSSM, &pCurr->SASDevicePage2, sizeof(MptConfigurationPageSASDevice2));
+
+ pCurr = pCurr->pNext;
+ }
+ }
+ else
+ AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
+
+ vboxscsiR3SaveExec(&pThis->VBoxSCSI, pSSM);
+ return SSMR3PutU32(pSSM, UINT32_MAX);
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADDONE}
+ */
+static DECLCALLBACK(int) lsilogicR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ RT_NOREF(pSSM);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+
+ lsilogicR3Kick(pThis);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) lsilogicR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ int rc;
+
+ if ( uVersion != LSILOGIC_SAVED_STATE_VERSION
+ && uVersion != LSILOGIC_SAVED_STATE_VERSION_PRE_DIAG_MEM
+ && uVersion != LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL
+ && uVersion != LSILOGIC_SAVED_STATE_VERSION_PRE_SAS
+ && uVersion != LSILOGIC_SAVED_STATE_VERSION_VBOX_30)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+ /* device config */
+ if (uVersion > LSILOGIC_SAVED_STATE_VERSION_PRE_SAS)
+ {
+ LSILOGICCTRLTYPE enmCtrlType;
+ uint32_t cDeviceStates, cPorts;
+
+ rc = SSMR3GetU32(pSSM, (uint32_t *)&enmCtrlType);
+ AssertRCReturn(rc, rc);
+ rc = SSMR3GetU32(pSSM, &cDeviceStates);
+ AssertRCReturn(rc, rc);
+ rc = SSMR3GetU32(pSSM, &cPorts);
+ AssertRCReturn(rc, rc);
+
+ if (enmCtrlType != pThis->enmCtrlType)
+ return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Controller type): config=%d state=%d"),
+ pThis->enmCtrlType, enmCtrlType);
+ if (cDeviceStates != pThis->cDeviceStates)
+ return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Device states): config=%u state=%u"),
+ pThis->cDeviceStates, cDeviceStates);
+ if (cPorts != pThis->cPorts)
+ return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Ports): config=%u state=%u"),
+ pThis->cPorts, cPorts);
+ }
+ if (uVersion > LSILOGIC_SAVED_STATE_VERSION_VBOX_30)
+ {
+ for (unsigned i = 0; i < pThis->cDeviceStates; i++)
+ {
+ bool fPresent;
+ rc = SSMR3GetBool(pSSM, &fPresent);
+ AssertRCReturn(rc, rc);
+ if (fPresent != (pThis->paDeviceStates[i].pDrvBase != NULL))
+ return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target %u config mismatch: config=%RTbool state=%RTbool"),
+ i, pThis->paDeviceStates[i].pDrvBase != NULL, fPresent);
+ }
+ }
+ if (uPass != SSM_PASS_FINAL)
+ return VINF_SUCCESS;
+
+ /* Every device first. */
+ for (unsigned i = 0; i < pThis->cDeviceStates; i++)
+ {
+ PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
+
+ AssertMsg(!pDevice->cOutstandingRequests,
+ ("There are still outstanding requests on this device\n"));
+ SSMR3GetU32(pSSM, (uint32_t *)&pDevice->cOutstandingRequests);
+ }
+ /* Now the main device state. */
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->enmState);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->enmWhoInit);
+ if (uVersion <= LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL)
+ {
+ bool fDoorbellInProgress = false;
+
+ /*
+ * The doorbell status flag distinguishes only between
+ * doorbell not in use or a Function handshake is currently in progress.
+ */
+ SSMR3GetBool (pSSM, &fDoorbellInProgress);
+ if (fDoorbellInProgress)
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_FN_HANDSHAKE;
+ else
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
+ }
+ else
+ SSMR3GetU32(pSSM, (uint32_t *)&pThis->enmDoorbellState);
+ SSMR3GetBool (pSSM, &pThis->fDiagnosticEnabled);
+ SSMR3GetBool (pSSM, &pThis->fNotificationSent);
+ SSMR3GetBool (pSSM, &pThis->fEventNotificationEnabled);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uInterruptMask);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uInterruptStatus);
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->aMessage); i++)
+ SSMR3GetU32 (pSSM, &pThis->aMessage[i]);
+ SSMR3GetU32 (pSSM, &pThis->iMessage);
+ SSMR3GetU32 (pSSM, &pThis->cMessage);
+ SSMR3GetMem (pSSM, &pThis->ReplyBuffer, sizeof(pThis->ReplyBuffer));
+ SSMR3GetU32 (pSSM, &pThis->uNextReplyEntryRead);
+ SSMR3GetU32 (pSSM, &pThis->cReplySize);
+ SSMR3GetU16 (pSSM, &pThis->u16IOCFaultCode);
+ SSMR3GetU32 (pSSM, &pThis->u32HostMFAHighAddr);
+ SSMR3GetU32 (pSSM, &pThis->u32SenseBufferHighAddr);
+ SSMR3GetU8 (pSSM, &pThis->cMaxDevices);
+ SSMR3GetU8 (pSSM, &pThis->cMaxBuses);
+ SSMR3GetU16 (pSSM, &pThis->cbReplyFrame);
+ SSMR3GetU32 (pSSM, &pThis->iDiagnosticAccess);
+
+ uint32_t cReplyQueueEntries, cRequestQueueEntries;
+ SSMR3GetU32 (pSSM, &cReplyQueueEntries);
+ SSMR3GetU32 (pSSM, &cRequestQueueEntries);
+
+ if ( cReplyQueueEntries != pThis->cReplyQueueEntries
+ || cRequestQueueEntries != pThis->cRequestQueueEntries)
+ {
+ LogFlow(("Reallocating queues cReplyQueueEntries=%u cRequestQueuEntries=%u\n",
+ cReplyQueueEntries, cRequestQueueEntries));
+ lsilogicR3QueuesFree(pThis);
+ pThis->cReplyQueueEntries = cReplyQueueEntries;
+ pThis->cRequestQueueEntries = cRequestQueueEntries;
+ rc = lsilogicR3QueuesAlloc(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyFreeQueueNextEntryFreeWrite);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyFreeQueueNextAddressRead);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyPostQueueNextEntryFreeWrite);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyPostQueueNextAddressRead);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uRequestQueueNextEntryFreeWrite);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uRequestQueueNextAddressRead);
+
+ PMptConfigurationPagesSupported pPages = pThis->pConfigurationPages;
+
+ if (uVersion <= LSILOGIC_SAVED_STATE_VERSION_PRE_SAS)
+ {
+ PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
+ MptConfigurationPagesSupported_SSM_V2 ConfigPagesV2;
+
+ if (pThis->enmCtrlType != LSILOGICCTRLTYPE_SCSI_SPI)
+ return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch: Expected SPI SCSI controller"));
+
+ SSMR3GetMem(pSSM, &ConfigPagesV2,
+ sizeof(MptConfigurationPagesSupported_SSM_V2));
+
+ pPages->ManufacturingPage0 = ConfigPagesV2.ManufacturingPage0;
+ pPages->ManufacturingPage1 = ConfigPagesV2.ManufacturingPage1;
+ pPages->ManufacturingPage2 = ConfigPagesV2.ManufacturingPage2;
+ pPages->ManufacturingPage3 = ConfigPagesV2.ManufacturingPage3;
+ pPages->ManufacturingPage4 = ConfigPagesV2.ManufacturingPage4;
+ pPages->IOUnitPage0 = ConfigPagesV2.IOUnitPage0;
+ pPages->IOUnitPage1 = ConfigPagesV2.IOUnitPage1;
+ pPages->IOUnitPage2 = ConfigPagesV2.IOUnitPage2;
+ pPages->IOUnitPage3 = ConfigPagesV2.IOUnitPage3;
+ pPages->IOCPage0 = ConfigPagesV2.IOCPage0;
+ pPages->IOCPage1 = ConfigPagesV2.IOCPage1;
+ pPages->IOCPage2 = ConfigPagesV2.IOCPage2;
+ pPages->IOCPage3 = ConfigPagesV2.IOCPage3;
+ pPages->IOCPage4 = ConfigPagesV2.IOCPage4;
+ pPages->IOCPage6 = ConfigPagesV2.IOCPage6;
+
+ pSpiPages->aPortPages[0].SCSISPIPortPage0 = ConfigPagesV2.aPortPages[0].SCSISPIPortPage0;
+ pSpiPages->aPortPages[0].SCSISPIPortPage1 = ConfigPagesV2.aPortPages[0].SCSISPIPortPage1;
+ pSpiPages->aPortPages[0].SCSISPIPortPage2 = ConfigPagesV2.aPortPages[0].SCSISPIPortPage2;
+
+ for (unsigned i = 0; i < RT_ELEMENTS(pPages->u.SpiPages.aBuses[0].aDevicePages); i++)
+ {
+ pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage0 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage0;
+ pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage1 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage1;
+ pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage2 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage2;
+ pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage3;
+ }
+ }
+ else
+ {
+ /* Queue content */
+ for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
+ SSMR3GetU32(pSSM, (uint32_t *)&pThis->pReplyFreeQueueBaseR3[i]);
+ for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
+ SSMR3GetU32(pSSM, (uint32_t *)&pThis->pReplyPostQueueBaseR3[i]);
+ for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
+ SSMR3GetU32(pSSM, (uint32_t *)&pThis->pRequestQueueBaseR3[i]);
+
+ SSMR3GetU16(pSSM, &pThis->u16NextHandle);
+
+ if (uVersion > LSILOGIC_SAVED_STATE_VERSION_PRE_DIAG_MEM)
+ {
+
+ /* Save diagnostic memory register and data regions. */
+ SSMR3GetU32(pSSM, &pThis->u32DiagMemAddr);
+ uint32_t cMemRegions = 0;
+ rc = SSMR3GetU32(pSSM, &cMemRegions);
+ AssertLogRelReturn(rc, rc);
+
+ while (cMemRegions)
+ {
+ uint32_t u32AddrStart = 0;
+ SSMR3GetU32(pSSM, &u32AddrStart);
+ uint32_t u32AddrEnd = 0;
+ rc = SSMR3GetU32(pSSM, &u32AddrEnd);
+ AssertLogRelReturn(rc, rc);
+
+ uint32_t cRegion = u32AddrEnd - u32AddrStart + 1;
+ PLSILOGICMEMREGN pRegion = (PLSILOGICMEMREGN)RTMemAllocZ(RT_UOFFSETOF_DYN(LSILOGICMEMREGN, au32Data[cRegion]));
+ if (pRegion)
+ {
+ pRegion->u32AddrStart = u32AddrStart;
+ pRegion->u32AddrEnd = u32AddrEnd;
+ SSMR3GetMem(pSSM, &pRegion->au32Data[0], cRegion * sizeof(uint32_t));
+ lsilogicR3MemRegionInsert(pThis, pRegion);
+ pThis->cbMemRegns += cRegion * sizeof(uint32_t);
+ }
+ else
+ {
+ /* Leave a log message but continue. */
+ LogRel(("LsiLogic: Out of memory while restoring the state, might not work as expected\n"));
+ SSMR3Skip(pSSM, cRegion * sizeof(uint32_t));
+ }
+ cMemRegions--;
+ }
+ }
+
+ /* Configuration pages */
+ SSMR3GetMem(pSSM, &pPages->ManufacturingPage0, sizeof(MptConfigurationPageManufacturing0));
+ SSMR3GetMem(pSSM, &pPages->ManufacturingPage1, sizeof(MptConfigurationPageManufacturing1));
+ SSMR3GetMem(pSSM, &pPages->ManufacturingPage2, sizeof(MptConfigurationPageManufacturing2));
+ SSMR3GetMem(pSSM, &pPages->ManufacturingPage3, sizeof(MptConfigurationPageManufacturing3));
+ SSMR3GetMem(pSSM, &pPages->ManufacturingPage4, sizeof(MptConfigurationPageManufacturing4));
+ SSMR3GetMem(pSSM, &pPages->ManufacturingPage5, sizeof(MptConfigurationPageManufacturing5));
+ SSMR3GetMem(pSSM, &pPages->ManufacturingPage6, sizeof(MptConfigurationPageManufacturing6));
+ SSMR3GetMem(pSSM, &pPages->ManufacturingPage8, sizeof(MptConfigurationPageManufacturing8));
+ SSMR3GetMem(pSSM, &pPages->ManufacturingPage9, sizeof(MptConfigurationPageManufacturing9));
+ SSMR3GetMem(pSSM, &pPages->ManufacturingPage10, sizeof(MptConfigurationPageManufacturing10));
+ SSMR3GetMem(pSSM, &pPages->IOUnitPage0, sizeof(MptConfigurationPageIOUnit0));
+ SSMR3GetMem(pSSM, &pPages->IOUnitPage1, sizeof(MptConfigurationPageIOUnit1));
+ SSMR3GetMem(pSSM, &pPages->IOUnitPage2, sizeof(MptConfigurationPageIOUnit2));
+ SSMR3GetMem(pSSM, &pPages->IOUnitPage3, sizeof(MptConfigurationPageIOUnit3));
+ SSMR3GetMem(pSSM, &pPages->IOUnitPage4, sizeof(MptConfigurationPageIOUnit4));
+ SSMR3GetMem(pSSM, &pPages->IOCPage0, sizeof(MptConfigurationPageIOC0));
+ SSMR3GetMem(pSSM, &pPages->IOCPage1, sizeof(MptConfigurationPageIOC1));
+ SSMR3GetMem(pSSM, &pPages->IOCPage2, sizeof(MptConfigurationPageIOC2));
+ SSMR3GetMem(pSSM, &pPages->IOCPage3, sizeof(MptConfigurationPageIOC3));
+ SSMR3GetMem(pSSM, &pPages->IOCPage4, sizeof(MptConfigurationPageIOC4));
+ SSMR3GetMem(pSSM, &pPages->IOCPage6, sizeof(MptConfigurationPageIOC6));
+ SSMR3GetMem(pSSM, &pPages->BIOSPage1, sizeof(MptConfigurationPageBIOS1));
+ SSMR3GetMem(pSSM, &pPages->BIOSPage2, sizeof(MptConfigurationPageBIOS2));
+ SSMR3GetMem(pSSM, &pPages->BIOSPage4, sizeof(MptConfigurationPageBIOS4));
+
+ /* Device dependent pages */
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ {
+ PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
+
+ SSMR3GetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage0, sizeof(MptConfigurationPageSCSISPIPort0));
+ SSMR3GetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage1, sizeof(MptConfigurationPageSCSISPIPort1));
+ SSMR3GetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage2, sizeof(MptConfigurationPageSCSISPIPort2));
+
+ for (unsigned i = 0; i < RT_ELEMENTS(pSpiPages->aBuses[0].aDevicePages); i++)
+ {
+ SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage0, sizeof(MptConfigurationPageSCSISPIDevice0));
+ SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage1, sizeof(MptConfigurationPageSCSISPIDevice1));
+ SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage2, sizeof(MptConfigurationPageSCSISPIDevice2));
+ SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3, sizeof(MptConfigurationPageSCSISPIDevice3));
+ }
+ }
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ {
+ uint32_t cbPage0, cbPage1, cPHYs, cbManufacturingPage7;
+ PMptConfigurationPagesSas pSasPages = &pPages->u.SasPages;
+
+ SSMR3GetU32(pSSM, &cbManufacturingPage7);
+ SSMR3GetU32(pSSM, &cbPage0);
+ SSMR3GetU32(pSSM, &cbPage1);
+
+ if ( (cbPage0 != pSasPages->cbSASIOUnitPage0)
+ || (cbPage1 != pSasPages->cbSASIOUnitPage1)
+ || (cbManufacturingPage7 != pSasPages->cbManufacturingPage7))
+ return VERR_SSM_LOAD_CONFIG_MISMATCH;
+
+ AssertPtr(pSasPages->pManufacturingPage7);
+ AssertPtr(pSasPages->pSASIOUnitPage0);
+ AssertPtr(pSasPages->pSASIOUnitPage1);
+
+ SSMR3GetMem(pSSM, pSasPages->pManufacturingPage7, pSasPages->cbManufacturingPage7);
+ SSMR3GetMem(pSSM, pSasPages->pSASIOUnitPage0, pSasPages->cbSASIOUnitPage0);
+ SSMR3GetMem(pSSM, pSasPages->pSASIOUnitPage1, pSasPages->cbSASIOUnitPage1);
+
+ SSMR3GetMem(pSSM, &pSasPages->SASIOUnitPage2, sizeof(MptConfigurationPageSASIOUnit2));
+ SSMR3GetMem(pSSM, &pSasPages->SASIOUnitPage3, sizeof(MptConfigurationPageSASIOUnit3));
+
+ SSMR3GetU32(pSSM, &cPHYs);
+ if (cPHYs != pSasPages->cPHYs)
+ return VERR_SSM_LOAD_CONFIG_MISMATCH;
+
+ AssertPtr(pSasPages->paPHYs);
+ for (unsigned i = 0; i < pSasPages->cPHYs; i++)
+ {
+ SSMR3GetMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage0, sizeof(MptConfigurationPageSASPHY0));
+ SSMR3GetMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage1, sizeof(MptConfigurationPageSASPHY1));
+ }
+
+ /* The number of devices first. */
+ SSMR3GetU32(pSSM, &pSasPages->cDevices);
+
+ PMptSASDevice pCurr = pSasPages->pSASDeviceHead;
+
+ for (unsigned i = 0; i < pSasPages->cDevices; i++)
+ {
+ SSMR3GetMem(pSSM, &pCurr->SASDevicePage0, sizeof(MptConfigurationPageSASDevice0));
+ SSMR3GetMem(pSSM, &pCurr->SASDevicePage1, sizeof(MptConfigurationPageSASDevice1));
+ SSMR3GetMem(pSSM, &pCurr->SASDevicePage2, sizeof(MptConfigurationPageSASDevice2));
+
+ pCurr = pCurr->pNext;
+ }
+
+ Assert(!pCurr);
+ }
+ else
+ AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
+ }
+
+ rc = vboxscsiR3LoadExec(&pThis->VBoxSCSI, pSSM);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("LsiLogic: Failed to restore BIOS state: %Rrc.\n", rc));
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("LsiLogic: Failed to restore BIOS state\n"));
+ }
+
+ uint32_t u32;
+ rc = SSMR3GetU32(pSSM, &u32);
+ if (RT_FAILURE(rc))
+ return rc;
+ AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
+
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * The device level IBASE and LED interfaces.
+ */
+
+/**
+ * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed, For a SCSI device.}
+ *
+ * @remarks Called by the scsi driver, proxying the main calls.
+ */
+static DECLCALLBACK(int) lsilogicR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
+{
+ PLSILOGICDEVICE pDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, ILed);
+ if (iLUN == 0)
+ {
+ *ppLed = &pDevice->Led;
+ Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
+ return VINF_SUCCESS;
+ }
+ return VERR_PDM_LUN_NOT_FOUND;
+}
+
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) lsilogicR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PLSILOGICDEVICE pDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IBase);
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevice->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pDevice->IMediaPort);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pDevice->IMediaExPort);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pDevice->ILed);
+ return NULL;
+}
+
+
+/*
+ * The controller level IBASE and LED interfaces.
+ */
+
+/**
+ * Gets the pointer to the status LED of a unit.
+ *
+ * @returns VBox status code.
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @param iLUN The unit which status LED we desire.
+ * @param ppLed Where to store the LED pointer.
+ */
+static DECLCALLBACK(int) lsilogicR3StatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
+{
+ PLSILOGICSCSI pThis = RT_FROM_MEMBER(pInterface, LSILOGICSCSI, ILeds);
+ if (iLUN < pThis->cDeviceStates)
+ {
+ *ppLed = &pThis->paDeviceStates[iLUN].Led;
+ Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
+ return VINF_SUCCESS;
+ }
+ return VERR_PDM_LUN_NOT_FOUND;
+}
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) lsilogicR3StatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PLSILOGICSCSI pThis = RT_FROM_MEMBER(pInterface, LSILOGICSCSI, IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
+ return NULL;
+}
+
+
+/*
+ * The PDM device interface and some helpers.
+ */
+
+/**
+ * Checks if all asynchronous I/O is finished.
+ *
+ * Used by lsilogicR3Reset, lsilogicR3Suspend and lsilogicR3PowerOff.
+ *
+ * @returns true if quiesced, false if busy.
+ * @param pDevIns The device instance.
+ */
+static bool lsilogicR3AllAsyncIOIsFinished(PPDMDEVINS pDevIns)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+
+ for (uint32_t i = 0; i < pThis->cDeviceStates; i++)
+ {
+ PLSILOGICDEVICE pThisDevice = &pThis->paDeviceStates[i];
+ if (pThisDevice->pDrvBase)
+ {
+ if (pThisDevice->cOutstandingRequests != 0)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * @callback_method_impl{FNPDMDEVASYNCNOTIFY,
+ * Callback employed by lsilogicR3Suspend and lsilogicR3PowerOff.}
+ */
+static DECLCALLBACK(bool) lsilogicR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns)
+{
+ if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
+ return false;
+
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ ASMAtomicWriteBool(&pThis->fSignalIdle, false);
+ return true;
+}
+
+/**
+ * Common worker for ahciR3Suspend and ahciR3PowerOff.
+ */
+static void lsilogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+
+ ASMAtomicWriteBool(&pThis->fSignalIdle, true);
+ if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
+ PDMDevHlpSetAsyncNotification(pDevIns, lsilogicR3IsAsyncSuspendOrPowerOffDone);
+ else
+ {
+ ASMAtomicWriteBool(&pThis->fSignalIdle, false);
+
+ AssertMsg(!pThis->fNotificationSent, ("The PDM Queue should be empty at this point\n"));
+ }
+
+ for (uint32_t i = 0; i < pThis->cDeviceStates; i++)
+ {
+ PLSILOGICDEVICE pThisDevice = &pThis->paDeviceStates[i];
+ if (pThisDevice->pDrvMediaEx)
+ pThisDevice->pDrvMediaEx->pfnNotifySuspend(pThisDevice->pDrvMediaEx);
+ }
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnSuspend}
+ */
+static DECLCALLBACK(void) lsilogicR3Suspend(PPDMDEVINS pDevIns)
+{
+ Log(("lsilogicR3Suspend\n"));
+ lsilogicR3SuspendOrPowerOff(pDevIns);
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnResume}
+ */
+static DECLCALLBACK(void) lsilogicR3Resume(PPDMDEVINS pDevIns)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+
+ Log(("lsilogicR3Resume\n"));
+
+ lsilogicR3Kick(pThis);
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDetach}
+ *
+ * One harddisk at one port has been unplugged.
+ * The VM is suspended at this point.
+ */
+static DECLCALLBACK(void) lsilogicR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[iLUN];
+
+ if (iLUN >= pThis->cDeviceStates)
+ return;
+
+ AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
+ ("LsiLogic: Device does not support hotplugging\n"));
+
+ Log(("%s:\n", __FUNCTION__));
+
+ /*
+ * Zero some important members.
+ */
+ pDevice->pDrvBase = NULL;
+ pDevice->pDrvMedia = NULL;
+ pDevice->pDrvMediaEx = NULL;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnAttach}
+ */
+static DECLCALLBACK(int) lsilogicR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[iLUN];
+ int rc;
+
+ if (iLUN >= pThis->cDeviceStates)
+ return VERR_PDM_LUN_NOT_FOUND;
+
+ AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
+ ("LsiLogic: Device does not support hotplugging\n"),
+ VERR_INVALID_PARAMETER);
+
+ /* the usual paranoia */
+ AssertRelease(!pDevice->pDrvBase);
+ AssertRelease(!pDevice->pDrvMedia);
+ AssertRelease(!pDevice->pDrvMediaEx);
+ Assert(pDevice->iLUN == iLUN);
+
+ /*
+ * Try attach the block device and get the interfaces,
+ * required as well as optional.
+ */
+ rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /* Query the media interface. */
+ pDevice->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMIMEDIA);
+ AssertMsgReturn(VALID_PTR(pDevice->pDrvMedia),
+ ("LsiLogic configuration error: LUN#%d misses the basic media interface!\n", pDevice->iLUN),
+ VERR_PDM_MISSING_INTERFACE);
+
+ /* Get the extended media interface. */
+ pDevice->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMIMEDIAEX);
+ AssertMsgReturn(VALID_PTR(pDevice->pDrvMediaEx),
+ ("LsiLogic configuration error: LUN#%d misses the extended media interface!\n", pDevice->iLUN),
+ VERR_PDM_MISSING_INTERFACE);
+
+ rc = pDevice->pDrvMediaEx->pfnIoReqAllocSizeSet(pDevice->pDrvMediaEx, sizeof(LSILOGICREQ));
+ AssertMsgRCReturn(rc, ("LsiLogic configuration error: LUN#%u: Failed to set I/O request size!", pDevice->iLUN),
+ rc);
+ }
+ else
+ AssertMsgFailed(("Failed to attach LUN#%d. rc=%Rrc\n", pDevice->iLUN, rc));
+
+ if (RT_FAILURE(rc))
+ {
+ pDevice->pDrvBase = NULL;
+ pDevice->pDrvMedia = NULL;
+ pDevice->pDrvMediaEx = NULL;
+ }
+ return rc;
+}
+
+/**
+ * Common reset worker.
+ *
+ * @param pDevIns The device instance data.
+ */
+static void lsilogicR3ResetCommon(PPDMDEVINS pDevIns)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ int rc;
+
+ rc = lsilogicR3HardReset(pThis);
+ AssertRC(rc);
+
+ vboxscsiInitialize(&pThis->VBoxSCSI);
+}
+
+/**
+ * @callback_method_impl{FNPDMDEVASYNCNOTIFY,
+ * Callback employed by lsilogicR3Reset.}
+ */
+static DECLCALLBACK(bool) lsilogicR3IsAsyncResetDone(PPDMDEVINS pDevIns)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+
+ if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
+ return false;
+ ASMAtomicWriteBool(&pThis->fSignalIdle, false);
+
+ lsilogicR3ResetCommon(pDevIns);
+ return true;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) lsilogicR3Reset(PPDMDEVINS pDevIns)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+
+ ASMAtomicWriteBool(&pThis->fSignalIdle, true);
+ if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
+ PDMDevHlpSetAsyncNotification(pDevIns, lsilogicR3IsAsyncResetDone);
+ else
+ {
+ ASMAtomicWriteBool(&pThis->fSignalIdle, false);
+ lsilogicR3ResetCommon(pDevIns);
+ }
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnRelocate}
+ */
+static DECLCALLBACK(void) lsilogicR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+
+ pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ pThis->pNotificationQueueRC = PDMQueueRCPtr(pThis->pNotificationQueueR3);
+
+ /* Relocate queues. */
+ pThis->pReplyFreeQueueBaseRC += offDelta;
+ pThis->pReplyPostQueueBaseRC += offDelta;
+ pThis->pRequestQueueBaseRC += offDelta;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnPowerOff}
+ */
+static DECLCALLBACK(void) lsilogicR3PowerOff(PPDMDEVINS pDevIns)
+{
+ Log(("lsilogicR3PowerOff\n"));
+ lsilogicR3SuspendOrPowerOff(pDevIns);
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) lsilogicR3Destruct(PPDMDEVINS pDevIns)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+
+ PDMR3CritSectDelete(&pThis->ReplyFreeQueueCritSect);
+ PDMR3CritSectDelete(&pThis->ReplyPostQueueCritSect);
+ PDMR3CritSectDelete(&pThis->RequestQueueCritSect);
+ PDMR3CritSectDelete(&pThis->ReplyFreeQueueWriteCritSect);
+
+ RTMemFree(pThis->paDeviceStates);
+ pThis->paDeviceStates = NULL;
+
+ if (pThis->hEvtProcess != NIL_SUPSEMEVENT)
+ {
+ SUPSemEventClose(pThis->pSupDrvSession, pThis->hEvtProcess);
+ pThis->hEvtProcess = NIL_SUPSEMEVENT;
+ }
+
+ lsilogicR3ConfigurationPagesFree(pThis);
+ lsilogicR3MemRegionsFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) lsilogicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ int rc = VINF_SUCCESS;
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+
+ /*
+ * Initialize enought of the state to make the destructure not trip up.
+ */
+ pThis->hEvtProcess = NIL_SUPSEMEVENT;
+ pThis->fBiosReqPending = false;
+ RTListInit(&pThis->ListMemRegns);
+
+ /*
+ * Validate and read configuration.
+ */
+ rc = CFGMR3AreValuesValid(pCfg, "GCEnabled\0"
+ "R0Enabled\0"
+ "ReplyQueueDepth\0"
+ "RequestQueueDepth\0"
+ "ControllerType\0"
+ "NumPorts\0"
+ "Bootable\0");
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
+ N_("LsiLogic configuration error: unknown option specified"));
+ rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("LsiLogic configuration error: failed to read GCEnabled as boolean"));
+ Log(("%s: fGCEnabled=%d\n", __FUNCTION__, pThis->fGCEnabled));
+
+ rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("LsiLogic configuration error: failed to read R0Enabled as boolean"));
+ Log(("%s: fR0Enabled=%d\n", __FUNCTION__, pThis->fR0Enabled));
+
+ rc = CFGMR3QueryU32Def(pCfg, "ReplyQueueDepth",
+ &pThis->cReplyQueueEntries,
+ LSILOGICSCSI_REPLY_QUEUE_DEPTH_DEFAULT);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("LsiLogic configuration error: failed to read ReplyQueue as integer"));
+ Log(("%s: ReplyQueueDepth=%u\n", __FUNCTION__, pThis->cReplyQueueEntries));
+
+ rc = CFGMR3QueryU32Def(pCfg, "RequestQueueDepth",
+ &pThis->cRequestQueueEntries,
+ LSILOGICSCSI_REQUEST_QUEUE_DEPTH_DEFAULT);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("LsiLogic configuration error: failed to read RequestQueue as integer"));
+ Log(("%s: RequestQueueDepth=%u\n", __FUNCTION__, pThis->cRequestQueueEntries));
+
+ char *pszCtrlType;
+ rc = CFGMR3QueryStringAllocDef(pCfg, "ControllerType",
+ &pszCtrlType, LSILOGICSCSI_PCI_SPI_CTRLNAME);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("LsiLogic configuration error: failed to read ControllerType as string"));
+ Log(("%s: ControllerType=%s\n", __FUNCTION__, pszCtrlType));
+
+ rc = lsilogicR3GetCtrlTypeFromString(pThis, pszCtrlType);
+ MMR3HeapFree(pszCtrlType);
+
+ char szDevTag[20];
+ RTStrPrintf(szDevTag, sizeof(szDevTag), "LSILOGIC%s-%u",
+ pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI ? "SPI" : "SAS",
+ iInstance);
+
+
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("LsiLogic configuration error: failed to determine controller type from string"));
+
+ rc = CFGMR3QueryU8(pCfg, "NumPorts",
+ &pThis->cPorts);
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ {
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ pThis->cPorts = LSILOGICSCSI_PCI_SPI_PORTS_MAX;
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ pThis->cPorts = LSILOGICSCSI_PCI_SAS_PORTS_DEFAULT;
+ else
+ AssertMsgFailed(("Invalid controller type: %d\n", pThis->enmCtrlType));
+ }
+ else if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("LsiLogic configuration error: failed to read NumPorts as integer"));
+
+ bool fBootable;
+ rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &fBootable, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("LsiLogic configuration error: failed to read Bootable as boolean"));
+ Log(("%s: Bootable=%RTbool\n", __FUNCTION__, fBootable));
+
+ /* Init static parts. */
+ PCIDevSetVendorId(&pThis->PciDev, LSILOGICSCSI_PCI_VENDOR_ID); /* LsiLogic */
+
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ {
+ PCIDevSetDeviceId (&pThis->PciDev, LSILOGICSCSI_PCI_SPI_DEVICE_ID); /* LSI53C1030 */
+ PCIDevSetSubSystemVendorId(&pThis->PciDev, LSILOGICSCSI_PCI_SPI_SUBSYSTEM_VENDOR_ID);
+ PCIDevSetSubSystemId (&pThis->PciDev, LSILOGICSCSI_PCI_SPI_SUBSYSTEM_ID);
+ }
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ {
+ PCIDevSetDeviceId (&pThis->PciDev, LSILOGICSCSI_PCI_SAS_DEVICE_ID); /* SAS1068 */
+ PCIDevSetSubSystemVendorId(&pThis->PciDev, LSILOGICSCSI_PCI_SAS_SUBSYSTEM_VENDOR_ID);
+ PCIDevSetSubSystemId (&pThis->PciDev, LSILOGICSCSI_PCI_SAS_SUBSYSTEM_ID);
+ }
+ else
+ AssertMsgFailed(("Invalid controller type: %d\n", pThis->enmCtrlType));
+
+ PCIDevSetClassProg (&pThis->PciDev, 0x00); /* SCSI */
+ PCIDevSetClassSub (&pThis->PciDev, 0x00); /* SCSI */
+ PCIDevSetClassBase (&pThis->PciDev, 0x01); /* Mass storage */
+ PCIDevSetInterruptPin(&pThis->PciDev, 0x01); /* Interrupt pin A */
+
+# ifdef VBOX_WITH_MSI_DEVICES
+ PCIDevSetStatus(&pThis->PciDev, VBOX_PCI_STATUS_CAP_LIST);
+ PCIDevSetCapabilityList(&pThis->PciDev, 0x80);
+# endif
+
+ pThis->pDevInsR3 = pDevIns;
+ pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
+ pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ pThis->pSupDrvSession = PDMDevHlpGetSupDrvSession(pDevIns);
+ pThis->IBase.pfnQueryInterface = lsilogicR3StatusQueryInterface;
+ pThis->ILeds.pfnQueryStatusLed = lsilogicR3StatusQueryStatusLed;
+
+ /*
+ * Create critical sections protecting the reply post and free queues.
+ * Note! We do our own syncronization, so NOP the default crit sect for the device.
+ */
+ rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ AssertRCReturn(rc, rc);
+
+ rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyFreeQueueCritSect, RT_SRC_POS, "%sRFQ", szDevTag);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply free queue"));
+
+ rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyPostQueueCritSect, RT_SRC_POS, "%sRPQ", szDevTag);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply post queue"));
+
+ rc = PDMDevHlpCritSectInit(pDevIns, &pThis->RequestQueueCritSect, RT_SRC_POS, "%sRQ", szDevTag);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for request queue"));
+
+ rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyFreeQueueWriteCritSect, RT_SRC_POS, "%sRFQW", szDevTag);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply free queue write access"));
+
+ /*
+ * Register the PCI device, it's I/O regions.
+ */
+ rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
+ if (RT_FAILURE(rc))
+ return rc;
+
+# ifdef VBOX_WITH_MSI_DEVICES
+ PDMMSIREG MsiReg;
+ RT_ZERO(MsiReg);
+ /* use this code for MSI-X support */
+# if 0
+ MsiReg.cMsixVectors = 1;
+ MsiReg.iMsixCapOffset = 0x80;
+ MsiReg.iMsixNextOffset = 0x00;
+ MsiReg.iMsixBar = 3;
+# else
+ MsiReg.cMsiVectors = 1;
+ MsiReg.iMsiCapOffset = 0x80;
+ MsiReg.iMsiNextOffset = 0x00;
+# endif
+ rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
+ if (RT_FAILURE (rc))
+ {
+ /* That's OK, we can work without MSI */
+ PCIDevSetCapabilityList(&pThis->PciDev, 0x0);
+ }
+# endif
+
+ rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, LSILOGIC_PCI_SPACE_IO_SIZE, PCI_ADDRESS_SPACE_IO, lsilogicR3Map);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicR3Map);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicR3Map);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* Initialize task queue. (Need two items to handle SMP guest concurrency.) */
+ char szTaggedText[64];
+ RTStrPrintf(szTaggedText, sizeof(szTaggedText), "%s-Task", szDevTag);
+ rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 2, 0,
+ lsilogicR3NotifyQueueConsumer, true,
+ szTaggedText,
+ &pThis->pNotificationQueueR3);
+ if (RT_FAILURE(rc))
+ return rc;
+ pThis->pNotificationQueueR0 = PDMQueueR0Ptr(pThis->pNotificationQueueR3);
+ pThis->pNotificationQueueRC = PDMQueueRCPtr(pThis->pNotificationQueueR3);
+
+ /*
+ * We need one entry free in the queue.
+ */
+ pThis->cReplyQueueEntries++;
+ pThis->cRequestQueueEntries++;
+
+ /*
+ * Allocate memory for the queues.
+ */
+ rc = lsilogicR3QueuesAlloc(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ pThis->cDeviceStates = pThis->cPorts * LSILOGICSCSI_PCI_SPI_DEVICES_PER_BUS_MAX;
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ pThis->cDeviceStates = pThis->cPorts * LSILOGICSCSI_PCI_SAS_DEVICES_PER_PORT_MAX;
+ else
+ AssertMsgFailed(("Invalid controller type: %d\n", pThis->enmCtrlType));
+
+ /*
+ * Create event semaphore and worker thread.
+ */
+ rc = PDMDevHlpThreadCreate(pDevIns, &pThis->pThreadWrk, pThis, lsilogicR3Worker,
+ lsilogicR3WorkerWakeUp, 0, RTTHREADTYPE_IO, szDevTag);
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("LsiLogic: Failed to create worker thread %s"), szDevTag);
+
+ rc = SUPSemEventCreate(pThis->pSupDrvSession, &pThis->hEvtProcess);
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("LsiLogic: Failed to create SUP event semaphore"));
+
+ /*
+ * Allocate device states.
+ */
+ pThis->paDeviceStates = (PLSILOGICDEVICE)RTMemAllocZ(sizeof(LSILOGICDEVICE) * pThis->cDeviceStates);
+ if (!pThis->paDeviceStates)
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to allocate memory for device states"));
+
+ for (unsigned i = 0; i < pThis->cDeviceStates; i++)
+ {
+ PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
+
+ /* Initialize static parts of the device. */
+ pDevice->iLUN = i;
+ pDevice->pLsiLogicR3 = pThis;
+ pDevice->Led.u32Magic = PDMLED_MAGIC;
+ pDevice->IBase.pfnQueryInterface = lsilogicR3DeviceQueryInterface;
+ pDevice->IMediaPort.pfnQueryDeviceLocation = lsilogicR3QueryDeviceLocation;
+ pDevice->IMediaExPort.pfnIoReqCompleteNotify = lsilogicR3IoReqCompleteNotify;
+ pDevice->IMediaExPort.pfnIoReqCopyFromBuf = lsilogicR3IoReqCopyFromBuf;
+ pDevice->IMediaExPort.pfnIoReqCopyToBuf = lsilogicR3IoReqCopyToBuf;
+ pDevice->IMediaExPort.pfnIoReqQueryBuf = NULL;
+ pDevice->IMediaExPort.pfnIoReqQueryDiscardRanges = NULL;
+ pDevice->IMediaExPort.pfnIoReqStateChanged = lsilogicR3IoReqStateChanged;
+ pDevice->IMediaExPort.pfnMediumEjected = lsilogicR3MediumEjected;
+ pDevice->ILed.pfnQueryStatusLed = lsilogicR3DeviceQueryStatusLed;
+
+ char *pszName;
+ if (RTStrAPrintf(&pszName, "Device%u", i) <= 0)
+ AssertLogRelFailedReturn(VERR_NO_MEMORY);
+
+ /* Attach SCSI driver. */
+ rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, pszName);
+ if (RT_SUCCESS(rc))
+ {
+ /* Query the media interface. */
+ pDevice->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMIMEDIA);
+ AssertMsgReturn(VALID_PTR(pDevice->pDrvMedia),
+ ("LsiLogic configuration error: LUN#%d misses the basic media interface!\n", pDevice->iLUN),
+ VERR_PDM_MISSING_INTERFACE);
+
+ /* Get the extended media interface. */
+ pDevice->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMIMEDIAEX);
+ AssertMsgReturn(VALID_PTR(pDevice->pDrvMediaEx),
+ ("LsiLogic configuration error: LUN#%d misses the extended media interface!\n", pDevice->iLUN),
+ VERR_PDM_MISSING_INTERFACE);
+
+ rc = pDevice->pDrvMediaEx->pfnIoReqAllocSizeSet(pDevice->pDrvMediaEx, sizeof(LSILOGICREQ));
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("LsiLogic configuration error: LUN#%u: Failed to set I/O request size!"),
+ pDevice->iLUN);
+ }
+ else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+ {
+ pDevice->pDrvBase = NULL;
+ rc = VINF_SUCCESS;
+ Log(("LsiLogic: no driver attached to device %s\n", pszName));
+ }
+ else
+ {
+ AssertLogRelMsgFailed(("LsiLogic: Failed to attach %s\n", pszName));
+ return rc;
+ }
+ }
+
+ /*
+ * Attach status driver (optional).
+ */
+ PPDMIBASE pBase;
+ rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
+ pThis->pMediaNotify = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIANOTIFY);
+ }
+ else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
+ {
+ AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot attach to status driver"));
+ }
+
+ /* Initialize the SCSI emulation for the BIOS. */
+ rc = vboxscsiInitialize(&pThis->VBoxSCSI);
+ AssertRC(rc);
+
+ /*
+ * Register I/O port space in ISA region for BIOS access
+ * if the controller is marked as bootable.
+ */
+ if (fBootable)
+ {
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ rc = PDMDevHlpIOPortRegister(pDevIns, LSILOGIC_BIOS_IO_PORT, 4, NULL,
+ lsilogicR3IsaIOPortWrite, lsilogicR3IsaIOPortRead,
+ lsilogicR3IsaIOPortWriteStr, lsilogicR3IsaIOPortReadStr,
+ "LsiLogic BIOS");
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ rc = PDMDevHlpIOPortRegister(pDevIns, LSILOGIC_SAS_BIOS_IO_PORT, 4, NULL,
+ lsilogicR3IsaIOPortWrite, lsilogicR3IsaIOPortRead,
+ lsilogicR3IsaIOPortWriteStr, lsilogicR3IsaIOPortReadStr,
+ "LsiLogic SAS BIOS");
+ else
+ AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
+
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot register legacy I/O handlers"));
+ }
+
+ /* Register save state handlers. */
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, LSILOGIC_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
+ NULL, lsilogicR3LiveExec, NULL,
+ NULL, lsilogicR3SaveExec, NULL,
+ NULL, lsilogicR3LoadExec, lsilogicR3LoadDone);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot register save state handlers"));
+
+ pThis->enmWhoInit = LSILOGICWHOINIT_SYSTEM_BIOS;
+
+ /*
+ * Register the info item.
+ */
+ char szTmp[128];
+ RTStrPrintf(szTmp, sizeof(szTmp), "%s%u", pDevIns->pReg->szName, pDevIns->iInstance);
+ PDMDevHlpDBGFInfoRegister(pDevIns, szTmp,
+ pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
+ ? "LsiLogic SPI info."
+ : "LsiLogic SAS info.", lsilogicR3Info);
+
+ /* Perform hard reset. */
+ rc = lsilogicR3HardReset(pThis);
+ AssertRC(rc);
+
+ return rc;
+}
+
+/**
+ * The device registration structure - SPI SCSI controller.
+ */
+const PDMDEVREG g_DeviceLsiLogicSCSI =
+{
+ /* u32Version */
+ PDM_DEVREG_VERSION,
+ /* szName */
+ "lsilogicscsi",
+ /* szRCMod */
+ "VBoxDDRC.rc",
+ /* szR0Mod */
+ "VBoxDDR0.r0",
+ /* pszDescription */
+ "LSI Logic 53c1030 SCSI controller.\n",
+ /* fFlags */
+ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0 |
+ PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
+ /* fClass */
+ PDM_DEVREG_CLASS_STORAGE,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(LSILOGICSCSI),
+ /* pfnConstruct */
+ lsilogicR3Construct,
+ /* pfnDestruct */
+ lsilogicR3Destruct,
+ /* pfnRelocate */
+ lsilogicR3Relocate,
+ /* pfnMemSetup */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ lsilogicR3Reset,
+ /* pfnSuspend */
+ lsilogicR3Suspend,
+ /* pfnResume */
+ lsilogicR3Resume,
+ /* pfnAttach */
+ lsilogicR3Attach,
+ /* pfnDetach */
+ lsilogicR3Detach,
+ /* pfnQueryInterface. */
+ NULL,
+ /* pfnInitComplete */
+ NULL,
+ /* pfnPowerOff */
+ lsilogicR3PowerOff,
+ /* pfnSoftReset */
+ NULL,
+ /* u32VersionEnd */
+ PDM_DEVREG_VERSION
+};
+
+/**
+ * The device registration structure - SAS controller.
+ */
+const PDMDEVREG g_DeviceLsiLogicSAS =
+{
+ /* u32Version */
+ PDM_DEVREG_VERSION,
+ /* szName */
+ "lsilogicsas",
+ /* szRCMod */
+ "VBoxDDRC.rc",
+ /* szR0Mod */
+ "VBoxDDR0.r0",
+ /* pszDescription */
+ "LSI Logic SAS1068 controller.\n",
+ /* fFlags */
+ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0 |
+ PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION |
+ PDM_DEVREG_FLAGS_FIRST_RESET_NOTIFICATION,
+ /* fClass */
+ PDM_DEVREG_CLASS_STORAGE,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(LSILOGICSCSI),
+ /* pfnConstruct */
+ lsilogicR3Construct,
+ /* pfnDestruct */
+ lsilogicR3Destruct,
+ /* pfnRelocate */
+ lsilogicR3Relocate,
+ /* pfnMemSetup */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ lsilogicR3Reset,
+ /* pfnSuspend */
+ lsilogicR3Suspend,
+ /* pfnResume */
+ lsilogicR3Resume,
+ /* pfnAttach */
+ lsilogicR3Attach,
+ /* pfnDetach */
+ lsilogicR3Detach,
+ /* pfnQueryInterface. */
+ NULL,
+ /* pfnInitComplete */
+ NULL,
+ /* pfnPowerOff */
+ lsilogicR3PowerOff,
+ /* pfnSoftReset */
+ NULL,
+ /* u32VersionEnd */
+ PDM_DEVREG_VERSION
+};
+
+#endif /* IN_RING3 */
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */