DevLsiLogicSCSI.cpp revision c203ec58f3d8dccdee3a9515ec5997b7ad340270
/* $Id$ */
/** @file
* DevLsiLogicSCSI - LsiLogic LSI53c1030 SCSI controller.
*/
/*
* Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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
#ifdef IN_RING3
# include <iprt/memcache.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. */
/** The saved state version used by VirtualBox before the doorbell status flag
* was changed from bool to a 32bit enum. */
/** 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
/** 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 *
*******************************************************************************/
/**
* I/O buffer copy worker.
*
* @returns nothing.
* @param pDevIns Device instance data.
* @param GCPhysIoBuf Guest physical address of the I/O buffer.
* @param pvBuf R3 buffer pointer.
* @param cbCopy How much to copy.
*/
/** Pointer to a I/O buffer copy worker. */
typedef FNLSILOGICIOBUFCOPY *PFNLSILOGICIOBUFCOPY;
/**
* Reply data.
*/
typedef struct LSILOGICSCSIREPLY
{
/** Lower 32 bits of the reply address in memory. */
/** Full address of the reply in guest memory. */
/** Size of the reply. */
/** Different views to the reply depending on the request type. */
/** Pointer to reply data. */
typedef LSILOGICSCSIREPLY *PLSILOGICSCSIREPLY;
/**
* Memory region of the IOC.
*/
typedef struct LSILOGICMEMREGN
{
/** List node. */
/** 32bit address the region starts to describe. */
/** 32bit address the region ends (inclusive). */
/** Data for this region - variable. */
/** 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 */
/** LUN of the device. */
/** Number of outstanding tasks on the port. */
volatile uint32_t cOutstandingRequests;
#if HC_ARCH_BITS == 64
#endif
/** Our base interface. */
/** SCSI port interface. */
/** Led interface. */
/** Pointer to the attached driver's base interface. */
/** Pointer to the underlying SCSI connector interface. */
/** The status LED state for this device. */
/** 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. */
/** Pointer to the device instance. - R3 ptr. */
/** Pointer to the device instance. - R0 ptr. */
/** Pointer to the device instance. - RC ptr. */
/** 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. */
/** Who needs to init the driver to get into operational state. */
/** Flag whether we are in doorbell function. */
/** Flag whether diagnostic access is enabled. */
bool fDiagnosticEnabled;
/** Flag whether a notification was send to R3. */
bool fNotificationSend;
/** Flag whether the guest enabled event notification from the IOC. */
/** Flag whether the diagnostic address and RW registers are enabled. */
bool fDiagRegsEnabled;
/** Queue to send tasks to R3. - R3 ptr */
/** Queue to send tasks to R3. - R0 ptr */
/** Queue to send tasks to R3. - RC ptr */
/** Number of device states allocated. */
/** States for attached devices. */
#if HC_ARCH_BITS == 32
#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. */
/** Size of the message which is given in the doorbell message in dwords. */
/** Reply buffer.
* @note 60 bytes */
/** Next entry to read. */
/** Size of the reply in the buffer in 16bit words. */
/** The fault code of the I/O controller if we are in the fault state. */
/** I/O port address the device is mapped to. */
/** MMIO address the device is mapped to. */
/** Upper 32 bits of the message frame address to locate requests in guest memory. */
/** Upper 32 bits of the sense buffer address. */
/** Maximum number of devices the driver reported he can handle. */
/** Maximum number of buses the driver reported he can handle. */
/** Current size of reply message frames in the guest. */
/** Next key to write in the sequence to get access
* to diagnostic memory. */
/** Number entries allocated for the reply queue. */
/** Number entries allocated for the outstanding request queue. */
/** Critical section protecting the reply post queue. */
/** Critical section protecting the reply free queue. */
/** Pointer to the start of the reply free queue - R3. */
/** Pointer to the start of the reply post queue - R3. */
/** Pointer to the start of the request queue - R3. */
/** Pointer to the start of the reply queue - R0. */
/** Pointer to the start of the reply queue - R0. */
/** Pointer to the start of the request queue - R0. */
/** Pointer to the start of the reply queue - RC. */
/** Pointer to the start of the reply queue - RC. */
/** Pointer to the start of the request queue - RC. */
/** End these RC pointers on a 64-bit boundrary. */
/** 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 */
/** Handle counter */
/** Number of ports this controller has. */
/** BIOS emulation. */
/** Cache for allocated tasks. */
/** Status LUN: The base interface. */
/** Status LUN: Leds interface. */
/** Status LUN: Partner of ILeds. */
/** Pointer to the configuration page area. */
/** 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;
/** Alignment padding. */
/** List of tasks which can be redone. */
/** Current address to read from or write to in the diagnostic memory region. */
/** Current size of the memory regions. */
#if HC_ARCH_BITS ==32
#endif
union
{
/** List of memory regions - PLSILOGICMEMREGN. */
};
/** The support driver session handle. */
/** Worker thread. */
/** The event semaphore the processing thread waits on. */
} LSILOGISCSI;
/** Pointer to the device instance data of the LsiLogic emulation. */
typedef LSILOGICSCSI *PLSILOGICSCSI;
/**
* Task state object which holds all necessary data while
* processing the request from the guest.
*/
typedef struct LSILOGICREQ
{
/** Next in the redo list. */
/** Target device. */
/** The message request from the guest. */
/** Reply message if the request produces one. */
/** SCSI request structure for the SCSI driver. */
/** Address of the message request frame in guests memory.
* Used to read the S/G entries in the second step. */
/** Physical start address of the S/G list. */
/** Chain offset */
/** Segment describing the I/O buffer. */
/** Additional memory allocation for this task. */
void *pvAlloc;
/** Siize of the allocation. */
/** Number of times we had too much memory allocated for the request. */
unsigned cAllocTooMuch;
/** Pointer to the sense buffer. */
/** Flag whether the request was issued from the BIOS. */
bool fBIOS;
} LSILOGICREQ;
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
#ifdef IN_RING3
static int lsilogicR3ProcessConfigurationRequest(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq,
#endif
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Key sequence the guest has to write to enable access
* to diagnostic memory. */
/**
* Updates the status of the interrupt pin of the device.
*
* @returns nothing.
* @param pThis Pointer to the LsiLogic device state.
*/
{
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. */
if (uIntSts)
{
LogFlowFunc(("Setting interrupt\n"));
}
else
{
LogFlowFunc(("Clearing interrupt\n"));
}
}
/**
* 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.
*/
{
}
/**
* 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.
*/
{
}
/**
* 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.
*/
{
{
Log(("%s: Setting I/O controller into FAULT state: uIOCFaultCode=%u\n", __FUNCTION__, uIOCFaultCode));
}
else
Log(("%s: We are already in FAULT state\n"));
}
/**
* 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.
*/
{
uint32_t cReplyFrames = 0;
else
cReplyFrames = pThis->cReplyQueueEntries - pThis->uReplyFreeQueueNextAddressRead + pThis->uReplyFreeQueueNextEntryFreeWrite;
return cReplyFrames;
}
/**
* 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.
*/
{
uint32_t cReplyFrames = 0;
cReplyFrames = pThis->cReplyQueueEntries - pThis->uReplyPostQueueNextEntryFreeWrite + pThis->uReplyPostQueueNextAddressRead;
else
return cReplyFrames;
}
#ifdef IN_RING3
/**
* Performs a hard reset on the controller.
*
* @returns VBox status code.
* @param pThis Pointer to the LsiLogic device state.
*/
{
/* The interrupts are masked out. */
/* Reset interrupt states. */
pThis->uInterruptStatus = 0;
/* Reset the queues. */
/* Disable diagnostic access. */
pThis->iDiagnosticAccess = 0;
pThis->fDiagnosticEnabled = false;
pThis->fDiagRegsEnabled = false;
/* Set default values. */
pThis->u32DiagMemAddr = 0;
/* Mark that we finished performing the reset. */
return VINF_SUCCESS;
}
/**
* Frees the configuration pages if allocated.
*
* @returns nothing.
* @param pThis The LsiLogic controller instance
*/
{
if (pThis->pConfigurationPages)
{
/* Destroy device list if we emulate a SAS controller. */
{
while (pSASDeviceCurr)
{
}
if (pSasPages->pManufacturingPage7)
if (pSasPages->pSASIOUnitPage0)
if (pSasPages->pSASIOUnitPage1)
}
}
}
/**
* Finishes a context reply.
*
* @returns nothing
* @param pThis Pointer to the LsiLogic device state.
* @param u32MessageContext The message context ID to post.
*/
{
int rc;
AssertMsg(pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_NOT_IN_USE, ("We are in a doorbell function\n"));
/* Write message context ID into reply post queue. */
/* Check for a entry in the queue. */
{
/* Set error code. */
return;
}
/* We have a context reply. */
ASMAtomicWriteU32(&pThis->CTX_SUFF(pReplyPostQueueBase)[pThis->uReplyPostQueueNextEntryFreeWrite], u32MessageContext);
/* Set interrupt. */
}
#endif /* IN_RING3 */
/**
* 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.
*/
{
/* Set size of the reply in 16bit words. The size in the reply is in 32bit dwords. */
pThis->uNextReplyEntryRead = 0;
}
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. */
/* Check for a free reply frame. */
{
/* Set error code. */
return;
}
uint32_t u32ReplyFrameAddressLow = pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead];
/* 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. */
/* Write low 32bits of reply frame into post reply queue. */
/* Check for a entry in the queue. */
{
/* Set error code. */
return;
}
/* We have a address reply. Set the 31th bit to indicate that. */
if (fForceReplyFifo)
{
}
/* Set interrupt. */
#else
AssertMsgFailed(("This is not allowed to happen.\n"));
#endif
}
}
#ifdef IN_RING3
/**
* 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.
*/
{
{
{
break;
}
}
return pRegion;
}
/**
* Frees all allocated memory regions.
*
* @returns nothing.
* @param pThis Pointer to the LsiLogic device state.
*/
{
{
}
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.
*/
{
bool fInserted = false;
/* Insert at the right position. */
{
{
fInserted = true;
break;
}
}
if (!fInserted)
}
/**
* Count number of memory regions.
*
* @returns Number of memory regions.
* @param pThis Pointer to the LsiLogic device state.
*/
{
{
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.
*/
{
if (pRegion)
{
("Region offset not on a word boundary or crosses memory region\n"));
offRegion /= 4;
}
else
{
/* Create new region, first check whether we can extend another region. */
{
{
break;
}
}
if (pRegion)
{
/* Reallocate. */
{
PLSILOGICMEMREGN pRegionNew = (PLSILOGICMEMREGN)RTMemRealloc(pRegion, RT_OFFSETOF(LSILOGICMEMREGN, au32Data[cRegionSizeNew]));
if (pRegionNew)
{
}
/* else: Silently fail, there is nothing we can do here and the guest might work nevertheless. */
}
}
else
{
{
/* Create completely new. */
if (pRegion)
{
}
/* else: Silently fail, there is nothing we can do here and the guest might work nevertheless. */
}
}
}
/* Memory access is always 32bit big. */
}
/**
* 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.
*/
{
if (pRegion)
{
("Region offset not on a word boundary or crosses memory region\n"));
offRegion /= 4;
}
else /* No region, default value 0. */
*pu32Data = 0;
/* Memory access is always 32bit big. */
}
/**
* Handles a write to the diagnostic memory address register.
*
* @returns nothing.
* @param pThis Pointer to the LsiLogic device state.
* @param u32Addr Address to write.
*/
{
}
/**
* 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.
*/
{
}
/**
* 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
else
Log(("Message request function: <unknown>\n"));
# endif
switch (pMessageHdr->u8Function)
{
{
fForceReplyPostFifo = true;
break;
}
{
/*
* This request sets the I/O controller to the
* operational state.
*/
/* Update configuration values. */
{
}
/* Return reply. */
break;
}
{
{
}
{
}
else
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.u16GlobalCredits = pThis->cRequestQueueEntries - 1; /* One entry is always free. */
/* Check for a valid firmware image in the IOC memory which was downlaoded by tzhe guest earlier. */
if (pRegion)
{
/* Check for the signature. */
/** @todo: Checksum validation. */
{
LogFlowFunc(("IOC Facts: Found valid firmware image header in memory, using version (%#x), size (%d) and product ID (%#x) from there\n",
}
}
else
{
}
break;
}
{
{
/* This controller only supports one bus with bus number 0. */
{
}
else
{
pReply->PortFacts.u16MaxPostedCmdBuffers = 0; /* Only applies for target mode which we dont support. */
}
}
{
{
}
else
{
pReply->PortFacts.u16MaxPostedCmdBuffers = 0; /* Only applies for target mode which we dont support. */
}
}
else
break;
}
{
/*
* The port enable request notifies the IOC to make the port available and perform
* appropriate discovery on the associated link.
*/
break;
}
{
pThis->fEventNotificationEnabled = true;
else
pThis->fEventNotificationEnabled = false;
break;
}
{
AssertMsgFailed(("todo"));
break;
}
{
break;
}
{
break;
}
{
//PMptFWDownloadRequest pFWDownloadReq = (PMptFWDownloadRequest)pMessageHdr;
LogFlowFunc(("FW Download request issued\n"));
break;
}
case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST: /* Should be handled already. */
default:
}
/* Copy common bits from request message frame to reply. */
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.
*/
{
switch (offReg)
{
case LSILOGIC_REG_REPLY_QUEUE:
{
/* Add the entry to the reply free queue. */
ASMAtomicWriteU32(&pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextEntryFreeWrite], u32);
break;
}
{
/*
* 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++;
/* Send notification to R3 if there is not one send already. */
{
#ifdef IN_RC
#else
LogFlowFunc(("Signal event semaphore\n"));
#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.
*/
{
switch (uFunction)
{
{
/*
* 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.
*/
/* Reset interrupt status. */
pThis->uInterruptStatus = 0;
/* Reset the queues. */
/* Only the IOC message unit reset transisionts to the ready state. */
break;
}
{
/* Update the interrupt status to notify the guest that a doorbell function was started. */
break;
}
{
/* Update the interrupt status to notify the guest that a doorbell function was started. */
break;
}
default:
}
}
{
/*
* 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
return VINF_IOM_R3_MMIO_WRITE;
#endif
#ifdef IN_RING3
{
int rc = lsilogicR3ProcessMessageRequest(pThis, (PMptMessageHdr)pThis->aMessage, &pThis->ReplyBuffer);
}
#endif
}
break;
}
{
/*
* 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.
*/
/*
* 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.
*/
{
{
/* Reply finished. Reset doorbell in progress status. */
}
}
{
/* Reply frame removal, check whether the reply free queue is empty. */
}
break;
}
{
break;
}
{
if (pThis->fDiagnosticEnabled)
{
/* Any value will cause a reset and disabling access. */
pThis->fDiagnosticEnabled = false;
pThis->iDiagnosticAccess = 0;
pThis->fDiagRegsEnabled = false;
}
{
{
/*
* 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;
}
{
if (pThis->fDiagnosticEnabled)
{
#ifndef IN_RING3
return VINF_IOM_R3_MMIO_WRITE;
#else
else if (u32 & LSILOGIC_REG_HOST_DIAGNOSTIC_DIAG_RW_ENABLE)
pThis->fDiagRegsEnabled = true;
#endif
}
break;
}
{
if (pThis->fDiagRegsEnabled)
{
#ifndef IN_RING3
return VINF_IOM_R3_MMIO_WRITE;
#else
#endif
}
break;
}
{
if (pThis->fDiagRegsEnabled)
{
#ifndef IN_RING3
return VINF_IOM_R3_MMIO_WRITE;
#else
#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.
*/
{
int rc = VINF_SUCCESS;
/* Align to a 4 byte offset. */
switch (offReg)
{
case LSILOGIC_REG_REPLY_QUEUE:
{
if (rc != VINF_SUCCESS)
break;
{
}
else
{
/* The reply post queue is empty. Reset interrupt. */
}
break;
}
case LSILOGIC_REG_DOORBELL:
{
/*
* 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)
{
/* We return the status code of the I/O controller. */
break;
/* Return next 16bit value. */
break;
{
break;
}
{
break;
}
{
u32 |= pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead] & UINT32_C(0xffff);
}
break;
break;
default:
}
break;
}
{
break;
}
{
break;
}
{
if (pThis->fDiagnosticEnabled)
if (pThis->fDiagRegsEnabled)
break;
}
{
if (pThis->fDiagRegsEnabled)
{
#ifndef IN_RING3
return VINF_IOM_R3_MMIO_READ;
#else
#endif
}
}
{
if (pThis->fDiagRegsEnabled)
{
#ifndef IN_RING3
return VINF_IOM_R3_MMIO_READ;
#else
#endif
}
}
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;
}
}
return rc;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT}
*/
PDMBOTHCBDECL(int) lsilogicIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
int rc;
if (!(offReg & 3))
{
if (rc == VINF_IOM_R3_MMIO_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 Port, uint32_t *pu32, unsigned cb)
{
if (rc == VINF_IOM_R3_MMIO_READ)
return rc;
}
/**
* @callback_method_impl{FNIOMMMIOWRITE}
*/
PDMBOTHCBDECL(int) lsilogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
{
int rc;
/* See comments in lsilogicR3Map regarding size and alignment. */
if (cb == 4)
else
{
if (cb > 4)
else if (cb >= 2)
else
}
if (!(offReg & 3))
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)
{
}
{
LogFlowFunc(("pThis=%#p GCPhysAddr=%RGp pv=%#p{%.*Rhxs} cb=%u\n", pThis, GCPhysAddr, pv, cb, pv, cb));
return VINF_SUCCESS;
}
{
LogFlowFunc(("pThis=%#p GCPhysAddr=%RGp pv=%#p{%.*Rhxs} cb=%u\n", pThis, GCPhysAddr, pv, cb, 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
*/
{
if (LogIsEnabled())
{
{
case MPTSGENTRYTYPE_SIMPLE:
{
Log(("%s: u32DataBufferAddressLow=%u\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow));
{
Log(("%s: u32DataBufferAddressHigh=%u\n", __FUNCTION__, pSGEntry->Simple64.u32DataBufferAddressHigh));
}
else
break;
}
case MPTSGENTRYTYPE_CHAIN:
{
else
break;
}
}
}
}
# endif /* LOG_ENABLED */
/**
* Walks the guest S/G buffer calling the given copy worker for every buffer.
*
* @returns nothing.
* @param pDevIns Device instance data.
* @param pLsiReq LSI request state.
* @param cbCopy How much bytes to copy.
* @param pfnIoBufCopy Copy worker to call.
*/
{
bool fEndOfList = false;
/* Go through the list until we reach the end. */
while ( !fEndOfList
&& cbCopy)
{
bool fEndOfSegment = false;
while ( !fEndOfSegment
&& cbCopy)
{
/* Read the entry. */
# ifdef LOG_ENABLED
# endif
/* Check if this is a zero element and abort. */
return;
{
GCPhysSgEntryNext += sizeof(MptSGEntrySimple64);
}
else
GCPhysSgEntryNext += sizeof(MptSGEntrySimple32);
pbBuf += cbCopyThis;
cbCopy -= cbCopyThis;
/* Check if we reached the end of the list. */
{
/* We finished. */
fEndOfSegment = true;
fEndOfList = true;
}
fEndOfSegment = true;
} /* while (!fEndOfSegment) */
/* Get next chain element. */
if (cChainOffsetNext)
{
PDMDevHlpPhysRead(pDevIns, GCPhysSegmentStart + cChainOffsetNext, &SGEntryChain, sizeof(MptSGEntryChain));
/* Set the next address now. */
}
} /* while (!fEndOfList) */
}
{
}
{
}
/**
* Copy from a guest S/G buffer to the I/O buffer.
*
* @returns nothing.
* @param pDevIns Device instance data.
* @param pLsiReq Request data.
* @param cbCopy How much to copy over.
*/
{
}
/**
* Copy from an I/O buffer to the guest S/G buffer.
*
* @returns nothing.
* @param pDevIns Device instance data.
* @param pLsiReq Request data.
* @param cbCopy How much to copy over.
*/
{
}
/**
* Allocates memory for the given request using already allocated memory if possible.
*
* @returns Pointer to the memory or NULL on failure
* @param pLsiReq The request to allocate memory for.
* @param cb The amount of memory to allocate.
*/
{
pLsiReq->cAllocTooMuch++;
{
pLsiReq->cAllocTooMuch = 0;
}
}
/**
* Frees memory allocated for the given request.
*
* @returns nothing.
* @param pLsiReq The request.
*/
{
{
pLsiReq->cAllocTooMuch = 0;
}
}
/**
* Allocate I/O memory and copies the guest buffer for writes.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pLsiReq The request state.
* @param cbTransfer Amount of bytes to allocate.
*/
{
("Allocating I/O memory for a non I/O request is not allowed\n"));
return VERR_NO_MEMORY;
return VINF_SUCCESS;
}
/**
* Frees the I/O memory of the given request and updates the guest buffer if necessary.
*
* @returns nothing.
* @param pDevIns The device instance.
* @param pLsiReq The request state.
* @param fCopyToGuest Flag whether to update the guest buffer if necessary.
* Nothing is copied if false even if the request was a read.
*/
bool fCopyToGuest)
{
("Allocating I/O memory for a non I/O request is not allowed\n"));
if ( ( uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ
&& fCopyToGuest)
}
# ifdef LOG_ENABLED
{
if (LogIsEnabled())
{
Log(("%s: u32SenseBufferLowAddress=%#x\n", __FUNCTION__, pSCSIIORequest->u32SenseBufferLowAddress));
}
}
# endif
{
int rc;
rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevLsiLogic_DISKFULL",
N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space"));
}
{
int rc;
rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevLsiLogic_FILETOOBIG",
N_("Host system reported that the file size limit of the host file system has been exceeded. VM execution is suspended. You need to move your virtual hard disk to a filesystem which allows bigger files"));
}
{
int rc;
rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevLsiLogic_ISCSIDOWN",
N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again"));
}
{
int rc2;
LogRel(("LsiLogic#%d: Unknown but recoverable error has occurred (rc=%Rrc)\n", pDevIns->iInstance, rc));
rc2 = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevLsiLogic_UNKNOWN",
N_("An unknown but recoverable I/O error has occurred (rc=%Rrc). VM execution is suspended. You can resume when the error is fixed"), rc);
}
{
if (rc == VERR_DISK_FULL)
else if (rc == VERR_FILE_TOO_BIG)
{
/* iSCSI connection abort (first error) or failure to reestablish
* connection (second error). Pause VM. On resume we'll retry. */
}
else
}
/**
* 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 pLsiReq Pointer to the task state data.
*/
{
int rc = VINF_SUCCESS;
# ifdef LOG_ENABLED
# endif
if (pLsiReq->cChainOffset)
{
if (pTargetDevice->pDrvBase)
{
{
}
/* Setup the SCSI request. */
uint8_t uDataDirection = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pLsiReq->GuestRequest.SCSIIO.u32Control);
else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE)
else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ)
{
}
else
{
}
rc = pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTargetDevice->pDrvSCSIConnector, &pLsiReq->PDMScsiRequest);
return VINF_SUCCESS;
}
else
{
/* Device is not present report SCSI selection timeout. */
}
}
else
{
/* Report out of bounds target ID or bus. */
else
}
static int g_cLogged = 0;
if (g_cLogged++ < MAX_REL_LOG_ERRORS)
{
/* Log the CDB too */
LogRel(("LsiLogic#%d: Guest issued CDB {%#x",
LogRel(("}\n"));
}
/* The rest is equal to both errors. */
pLsiReq->IOCReply.SCSIIOError.u8SenseBufferLength = pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength;
return rc;
}
/**
* @interface_method_impl{PDMISCSIPORT,pfnSCSIRequestCompleted}
*/
static DECLCALLBACK(int) lsilogicR3DeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest,
{
/* If the task failed but it is possible to redo it again after a suspend
* add it to the list. */
if (fRedo)
{
/* Add to the list. */
do
{
/* Suspend the VM if not done already. */
}
else
{
{
}
else
{
/* Copy the sense buffer over. */
else
{
/* The SCSI target encountered an error during processing post a reply. */
pLsiReq->IOCReply.SCSIIOError.u8SenseBufferLength = pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength;
}
}
}
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMISCSIPORT,pfnQueryDeviceLocation}
*/
static DECLCALLBACK(int) lsilogicR3QueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController,
{
return VINF_SUCCESS;
}
/**
* 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 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.
*/
{
int rc = VINF_SUCCESS;
switch (u8PageNumber)
{
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
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 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.
*/
{
int rc = VINF_SUCCESS;
switch (u8PageNumber)
{
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 6:
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 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.
*/
{
int rc = VINF_SUCCESS;
switch (u8PageNumber)
{
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
{
}
else
rc = VERR_NOT_FOUND;
break;
case 8:
break;
case 9:
break;
case 10:
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 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.
*/
{
int rc = VINF_SUCCESS;
switch (u8PageNumber)
{
case 1:
break;
case 2:
break;
case 4:
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 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.
*/
{
int rc = VINF_SUCCESS;
return VERR_NOT_FOUND;
switch (u8PageNumber)
{
case 0:
break;
case 1:
break;
case 2:
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 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.
*/
{
int rc = VINF_SUCCESS;
return VERR_NOT_FOUND;
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;
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;
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;
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;
break;
default:
rc = VERR_NOT_FOUND;
}
return rc;
}
{
int rc = VINF_SUCCESS;
switch (u8PageNumber)
{
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
default:
rc = VERR_NOT_FOUND;
}
return rc;
}
{
int rc = VINF_SUCCESS;
if (uAddressForm == 0) /* PHY number */
{
return VERR_NOT_FOUND;
}
{
return VERR_NOT_FOUND;
}
else
if (pPHYPages)
{
switch (u8PageNumber)
{
case 0:
break;
case 1:
break;
default:
rc = VERR_NOT_FOUND;
}
}
else
rc = VERR_NOT_FOUND;
return rc;
}
{
int rc = VINF_SUCCESS;
if (uAddressForm == 0)
{
/* Get the first device? */
if (u16Handle != 0xffff)
{
/* No, search for the right one. */
while ( pSASDevice
if (pSASDevice)
}
}
else if (uAddressForm == 1)
{
while ( pSASDevice
}
else if (uAddressForm == 2)
{
while ( pSASDevice
}
if (pSASDevice)
{
switch (u8PageNumber)
{
case 0:
break;
case 1:
break;
case 2:
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 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.
*/
static int lsilogicR3ConfigurationPageGetExtended(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq,
{
int rc = VINF_SUCCESS;
Log(("Extended page requested:\n"));
switch (pConfigurationReq->u8ExtPageType)
{
{
break;
}
{
break;
}
{
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,
{
int rc = VINF_SUCCESS;
Log(("GuestRequest:\n"));
/* Copy common bits from the request into the reply. */
switch (u8PageType)
{
{
/* Get the page data. */
break;
}
{
/* Get the page data. */
break;
}
{
/* Get the page data. */
break;
}
{
/* Get the page data. */
break;
}
{
/* Get the page data. */
break;
}
{
break;
}
{
break;
}
default:
rc = VERR_NOT_FOUND;
}
if (rc == VERR_NOT_FOUND)
{
Log(("Page not found\n"));
return VINF_SUCCESS;
}
{
for (int i = 0; i < pExtPageHeader->u16ExtPageLength; i++)
}
else
{
for (int i = 0; i < pReply->u8PageLength; 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. */
{
/* Already copied above nothing to do. */
break;
}
{
if (cbBuffer != 0)
{
GCPhysAddrPageBuffer |= (uint64_t)pConfigurationReq->SimpleSGElement.u32DataBufferAddressHigh << 32;
PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhysAddrPageBuffer, pbPageData, RT_MIN(cbBuffer, cbPage));
}
break;
}
{
if (cbBuffer != 0)
{
GCPhysAddrPageBuffer |= (uint64_t)pConfigurationReq->SimpleSGElement.u32DataBufferAddressHigh << 32;
}
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.
*/
{
AssertMsg(pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI, ("Controller is not the SPI SCSI one\n"));
/* Clear everything first. */
{
/* SCSI-SPI port page 0. */
pPages->aPortPages[i].SCSISPIPortPage0.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
pPages->aPortPages[i].SCSISPIPortPage0.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIPort0) / 4;
/* SCSI-SPI port page 1. */
pPages->aPortPages[i].SCSISPIPortPage1.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
pPages->aPortPages[i].SCSISPIPortPage1.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIPort1) / 4;
/* SCSI-SPI port page 2. */
pPages->aPortPages[i].SCSISPIPortPage2.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
pPages->aPortPages[i].SCSISPIPortPage2.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIPort2) / 4;
for (unsigned iDevice = 0; iDevice < RT_ELEMENTS(pPages->aPortPages[i].SCSISPIPortPage2.u.fields.aDeviceSettings); iDevice++)
{
}
/* Everything else 0 for now. */
}
{
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
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
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
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
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.
*/
{
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)
*/
{
}
/**
* Initializes the configuration pages for the SAS SCSI controller.
*
* @returns nothing
* @param pThis Pointer to the LsiLogic device state.
*/
{
AssertMsg(pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS, ("Controller is not the SAS SCSI one\n"));
/* Manufacturing Page 7 - Connector settings. */
PMptConfigurationPageManufacturing7 pManufacturingPage7 = (PMptConfigurationPageManufacturing7)RTMemAllocZ(pPages->cbManufacturingPage7);
0, 7,
/* Set size manually. */
else
/* SAS I/O unit page 0 - Port specific information. */
PMptConfigurationPageSASIOUnit0 pSASPage0 = (PMptConfigurationPageSASIOUnit0)RTMemAllocZ(pPages->cbSASIOUnitPage0);
/* SAS I/O unit page 1 - Port specific settings. */
PMptConfigurationPageSASIOUnit1 pSASPage1 = (PMptConfigurationPageSASIOUnit1)RTMemAllocZ(pPages->cbSASIOUnitPage1);
/* SAS I/O unit page 2 - Port specific information. */
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.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT;
pPages->SASIOUnitPage3.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASIOUnit3) / 4;
/* Initialize the PHY configuration */
{
pSASPage0->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_NO);
pSASPage1->u.fields.aPHY[i].u8MaxMinLinkRate = LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MIN_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_15GB)
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.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASPHYS;
pPHYPages->SASPHYPage0.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASPHY0) / 4;
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)
pPHYPages->SASPHYPage0.u.fields.u8HwLinkRate = LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MIN_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_15GB)
/* SAS PHY page 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. */
{
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)
pSASPage1->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_END)
pPHYPages->SASPHYPage0.u.fields.u32AttachedDeviceInfo = LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_SET(LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_END);
/* SAS device page 0. */
pSASDevice->SASDevicePage0.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
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.u32DeviceInfo = LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_SET(LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_END)
/* SAS device page 1. */
pSASDevice->SASDevicePage1.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
pSASDevice->SASDevicePage1.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE;
pSASDevice->SASDevicePage1.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASDevice1) / 4;
/* SAS device page 2. */
pSASDevice->SASDevicePage2.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
pSASDevice->SASDevicePage2.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE;
pSASDevice->SASDevicePage2.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASDevice2) / 4;
/* Link into device list. */
{
}
else
{
}
}
}
}
/**
* Initializes the configuration pages.
*
* @returns nothing
* @param pThis Pointer to the LsiLogic device state.
*/
{
/* Initialize the common pages. */
PMptConfigurationPagesSupported pPages = (PMptConfigurationPagesSupported)RTMemAllocZ(sizeof(MptConfigurationPagesSupported));
/* Clear everything first. */
/* Manufacturing Page 0. */
/* Manufacturing Page 1 - I don't know what this contains so we leave it 0 for now. */
/* Manufacturing Page 2. */
{
}
{
}
/* Manufacturing Page 3. */
{
}
{
}
/* Manufacturing Page 4 - I don't know what this contains so we leave it 0 for now. */
/* Manufacturing Page 5 - WWID settings. */
/* Manufacturing Page 6 - Product specific settings. */
/* Manufacturing Page 8 - Product specific settings. */
/* Manufacturing Page 9 - Product specific settings. */
/* Manufacturing Page 10 - Product specific settings. */
/* I/O Unit page 0. */
/* I/O Unit page 1. */
/* I/O Unit page 2. */
/* I/O Unit page 3. */
/* I/O Unit page 4. */
/* IOC page 0. */
{
}
{
}
/* IOC page 1. */
/* IOC page 2. */
/* Everything else here is 0. */
/* IOC page 3. */
/* Everything else here is 0. */
/* IOC page 4. */
/* Everything else here is 0. */
/* IOC page 6. */
/* Everything else here is 0. */
/* BIOS page 1. */
/* BIOS page 2. */
/* BIOS page 4. */
else
}
/**
* @callback_method_impl{FNPDMQUEUEDEV, Transmit queue consumer.}
*/
static DECLCALLBACK(bool) lsilogicR3NotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
{
int rc = VINF_SUCCESS;
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.
*/
{
int rc = VERR_INVALID_PARAMETER;
{
rc = VINF_SUCCESS;
}
{
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)
{
Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
return rc;
}
/**
* Prepares a request from the BIOS.
*
* @returns VBox status code.
* @param pThis Pointer to the LsiLogic device state.
*/
{
int rc;
{
{
rc = pLsiReq->pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pLsiReq->pTargetDevice->pDrvSCSIConnector,
return VINF_SUCCESS;
}
}
/* Device is not present. */
("Device is not present but command is not inquiry\n"));
ScsiInquiryData.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
return rc;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT, Legacy ISA port.}
*/
static DECLCALLBACK(int) lsilogicR3IsaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x\n", pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port));
if (rc == VERR_MORE_DATA)
{
}
else if (RT_FAILURE(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,
{
if (rc == VERR_MORE_DATA)
{
}
else if (RT_FAILURE(rc))
return rc;
}
/**
* @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, RTGCPTR *pGCPtrDst,
{
LogFlowFunc(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
}
/**
* @callback_method_impl{FNPCIIOREGIONMAP}
*/
{
int rc = VINF_SUCCESS;
? "LsiLogic"
: "LsiLogicSas";
? "LsiLogicDiag"
: "LsiLogicSasDiag";
("PCI region type and size do not match\n"));
{
/*
* 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. */
if (RT_FAILURE(rc))
return rc;
if (pThis->fR0Enabled)
{
"lsilogicMMIOWrite", "lsilogicMMIORead");
if (RT_FAILURE(rc))
return rc;
}
if (pThis->fGCEnabled)
{
"lsilogicMMIOWrite", "lsilogicMMIORead");
if (RT_FAILURE(rc))
return rc;
}
}
{
/* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
if (RT_FAILURE(rc))
return rc;
if (pThis->fR0Enabled)
{
"lsilogicDiagnosticWrite", "lsilogicDiagnosticRead");
if (RT_FAILURE(rc))
return rc;
}
if (pThis->fGCEnabled)
{
"lsilogicDiagnosticWrite", "lsilogicDiagnosticRead");
if (RT_FAILURE(rc))
return rc;
}
}
else if (enmType == PCI_ADDRESS_SPACE_IO)
{
if (RT_FAILURE(rc))
return rc;
if (pThis->fR0Enabled)
{
if (RT_FAILURE(rc))
return rc;
}
if (pThis->fGCEnabled)
{
if (RT_FAILURE(rc))
return rc;
}
}
else
return rc;
}
/**
* @callback_method_impl{PFNDBGFHANDLERDEV}
*/
static DECLCALLBACK(void) lsilogicR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
bool fVerbose = false;
/*
* Parse args.
*/
if (pszArgs)
/*
* Show info.
*/
"%s#%d: port=%RTiop mmio=%RGp max-devices=%u GC=%RTbool R0=%RTbool\n",
pThis->fGCEnabled ? true : false,
pThis->fR0Enabled ? true : false);
/*
* Show general state.
*/
/*
* 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);
/*
* Show queue content if verbose
*/
if (fVerbose)
{
for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
}
/*
* Print the device status.
*/
for (unsigned i = 0; i < pThis->cDeviceStates; i++)
{
}
}
/**
* Allocate the queues.
*
* @returns VBox status code.
*
* @param pThis Pointer to the LsiLogic device state.
*/
{
(void **)&pThis->pReplyFreeQueueBaseR3);
if (RT_FAILURE(rc))
return VERR_NO_MEMORY;
return VINF_SUCCESS;
}
/**
* Free the hyper memory used or the queues.
*
* @returns nothing.
*
* @param pThis Pointer to the LsiLogic device state.
*/
{
int rc = VINF_SUCCESS;
}
/* The worker thread. */
{
int rc = VINF_SUCCESS;
return VINF_SUCCESS;
{
bool fNotificationSend;
if (!fNotificationSend)
{
break;
}
/* Only process request which arrived before we received the notification. */
/* Go through the messages now and process them. */
{
uint32_t u32RequestMessageFrameDesc = pThis->CTX_SUFF(pRequestQueueBase)[pThis->uRequestQueueNextAddressRead];
(u32RequestMessageFrameDesc & ~0x07));
/* Get new task state. */
/* Read the message header from the guest first. */
/* Determine the size of the request. */
{
cbRequest = sizeof(MptSCSIIORequest);
break;
cbRequest = sizeof(MptSCSITaskManagementRequest);
break;
cbRequest = sizeof(MptIOCInitRequest);
break;
cbRequest = sizeof(MptIOCFactsRequest);
break;
cbRequest = sizeof(MptConfigurationRequest);
break;
cbRequest = sizeof(MptPortFactsRequest);
break;
cbRequest = sizeof(MptPortEnableRequest);
break;
cbRequest = sizeof(MptEventNotificationRequest);
break;
AssertMsgFailed(("todo\n"));
//cbRequest = sizeof(MptEventAckRequest);
break;
cbRequest = sizeof(MptFWDownloadRequest);
break;
cbRequest = sizeof(MptFWUploadRequest);
break;
default:
}
if (cbRequest != 0)
{
/* Read the complete message frame from guest memory now. */
/* Handle SCSI I/O requests now. */
{
}
else
{
}
}
} /* 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.
*/
{
}
/**
* 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.
*/
{
if (pThis->fNotificationSend)
{
/* Send a notifier to the PDM queue that there are pending requests. */
}
{
/* The BIOS had a request active when we got suspended. Resume it. */
}
}
/*
* Saved state.
*/
/**
* @callback_method_impl{FNSSMDEVLIVEEXEC}
*/
{
/* Save the device config. */
for (unsigned i = 0; i < pThis->cDeviceStates; i++)
return VINF_SSM_DONT_CALL_AGAIN;
}
/**
* @callback_method_impl{FNSSMDEVSAVEEXEC}
*/
{
/* Every device first. */
for (unsigned i = 0; i < pThis->cDeviceStates; i++)
{
("There are still outstanding requests on this device\n"));
}
/* Now the main device state. */
for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
/* Save diagnostic memory register and data regions. */
{
}
/* Device dependent pages */
{
SSMR3PutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage0, sizeof(MptConfigurationPageSCSISPIPort0));
SSMR3PutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage1, sizeof(MptConfigurationPageSCSISPIPort1));
SSMR3PutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage2, sizeof(MptConfigurationPageSCSISPIPort2));
{
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));
}
}
{
{
}
/* The number of devices first. */
while (pCurr)
{
}
}
else
/* Now the data for the BIOS interface. */
return SSMR3PutU32(pSSM, ~0);
}
/**
* @callback_method_impl{FNSSMDEVLOADDONE}
*/
{
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNSSMDEVLOADEXEC}
*/
static DECLCALLBACK(int) lsilogicR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
int rc;
if ( uVersion != LSILOGIC_SAVED_STATE_VERSION
/* device config */
{
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Controller type): config=%d state=%d"),
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Device states): config=%u state=%u"),
}
{
for (unsigned i = 0; i < pThis->cDeviceStates; i++)
{
bool fPresent;
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target %u config mismatch: config=%RTbool state=%RTbool"),
}
}
if (uPass != SSM_PASS_FINAL)
return VINF_SUCCESS;
/* Every device first. */
for (unsigned i = 0; i < pThis->cDeviceStates; i++)
{
("There are still outstanding requests on this device\n"));
}
/* Now the main device state. */
{
bool fDoorbellInProgress = false;
/*
* The doorbell status flag distinguishes only between
* doorbell not in use or a Function handshake is currently in progress.
*/
if (fDoorbellInProgress)
else
}
else
{
LogFlow(("Reallocating queues cReplyQueueEntries=%u cRequestQueuEntries=%u\n",
if (RT_FAILURE(rc))
return rc;
}
{
sizeof(MptConfigurationPagesSupported_SSM_V2));
{
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++)
for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
{
uint32_t cMemRegions = 0;
/* Save diagnostic memory register and data regions. */
while (cMemRegions)
{
uint32_t u32AddrStart = 0;
uint32_t u32AddrEnd = 0;
if (pRegion)
{
}
else
{
/* Leave a log message but continue. */
LogRel(("LsiLogic: Out of memory while restoring the state, might not work as expected\n"));
}
cMemRegions--;
}
}
/* Configuration pages */
/* Device dependent pages */
{
SSMR3GetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage0, sizeof(MptConfigurationPageSCSISPIPort0));
SSMR3GetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage1, sizeof(MptConfigurationPageSCSISPIPort1));
SSMR3GetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage2, sizeof(MptConfigurationPageSCSISPIPort2));
{
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));
}
}
{
return VERR_SSM_LOAD_CONFIG_MISMATCH;
return VERR_SSM_LOAD_CONFIG_MISMATCH;
{
}
/* The number of devices first. */
{
}
}
else
}
/* Now the data for the BIOS interface. */
{
{
LogRel(("LsiLogic: Out of memory during restore.\n"));
N_("LsiLogic: Out of memory during restore\n"));
}
}
if (RT_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
/*
* The device level IBASE and LED interfaces.
*/
/**
* @interface_method_impl{PDMILEDPORTS,pfnQueryInterface, For a SCSI device.}
*
* @remarks Called by the scsi driver, proxying the main calls.
*/
static DECLCALLBACK(int) lsilogicR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
{
if (iLUN == 0)
{
return VINF_SUCCESS;
}
return VERR_PDM_LUN_NOT_FOUND;
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
static DECLCALLBACK(void *) lsilogicR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
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)
{
{
return VINF_SUCCESS;
}
return VERR_PDM_LUN_NOT_FOUND;
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
static DECLCALLBACK(void *) lsilogicR3StatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
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.
*/
{
{
if (pThisDevice->pDrvBase)
{
if (pThisDevice->cOutstandingRequests != 0)
return false;
}
}
return true;
}
/**
* @callback_method_impl{FNPDMDEVASYNCNOTIFY,
* Callback employed by lsilogicR3Suspend and lsilogicR3PowerOff.}
*/
{
return false;
return true;
}
/**
* Common worker for ahciR3Suspend and ahciR3PowerOff.
*/
{
else
{
{
/*
* We have tasks which we need to redo. Put the message frame addresses
* into the request queue (we save the requests).
* Guest execution is suspended at this point so there is no race between us and
* lsilogicRegisterWrite.
*/
while (pLsiReq)
{
{
/* Write only the lower 32bit part of the address. */
pThis->fNotificationSend = true;
}
else
{
}
}
}
}
}
/**
* @interface_method_impl{PDMDEVREG,pfnSuspend}
*/
{
Log(("lsilogicR3Suspend\n"));
}
/**
* @interface_method_impl{PDMDEVREG,pfnResume}
*/
{
Log(("lsilogicR3Resume\n"));
}
/**
* @interface_method_impl{PDMDEVREG,pfnDetach}
*
* One harddisk at one port has been unplugged.
* The VM is suspended at this point.
*/
{
return;
("LsiLogic: Device does not support hotplugging\n"));
/*
* Zero some important members.
*/
}
/**
* @interface_method_impl{PDMDEVREG,pfnAttach}
*/
{
int rc;
return VERR_PDM_LUN_NOT_FOUND;
("LsiLogic: Device does not support hotplugging\n"),
/* the usual paranoia */
/*
* Try attach the block device and get the interfaces,
* required as well as optional.
*/
if (RT_SUCCESS(rc))
{
/* Get SCSI connector interface. */
AssertMsgReturn(pDevice->pDrvSCSIConnector, ("Missing SCSI interface below\n"), VERR_PDM_MISSING_INTERFACE);
}
else
if (RT_FAILURE(rc))
{
}
return rc;
}
/**
* Common reset worker.
*
* @param pDevIns The device instance data.
*/
{
int rc;
}
/**
* @callback_method_impl{FNPDMDEVASYNCNOTIFY,
* Callback employed by lsilogicR3Reset.}
*/
{
return false;
return true;
}
/**
* @interface_method_impl{PDMDEVREG,pfnReset}
*/
{
else
{
}
}
/**
* @interface_method_impl{PDMDEVREG,pfnRelocate}
*/
{
/* Relocate queues. */
}
/**
* @interface_method_impl{PDMDEVREG,pfnPowerOff}
*/
{
Log(("lsilogicR3PowerOff\n"));
}
/**
* @interface_method_impl{PDMDEVREG,pfnDestruct}
*/
{
/* Destroy task cache. */
{
}
{
}
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
{
int rc = VINF_SUCCESS;
/*
* Initialize enought of the state to make the destructure not trip up.
*/
/*
* Validate and read configuration.
*/
"R0Enabled\0"
"ReplyQueueDepth\0"
"RequestQueueDepth\0"
"ControllerType\0"
"NumPorts\0"
"Bootable\0");
if (RT_FAILURE(rc))
N_("LsiLogic configuration error: unknown option specified"));
if (RT_FAILURE(rc))
N_("LsiLogic configuration error: failed to read GCEnabled as boolean"));
if (RT_FAILURE(rc))
N_("LsiLogic configuration error: failed to read R0Enabled as boolean"));
if (RT_FAILURE(rc))
N_("LsiLogic configuration error: failed to read ReplyQueue as integer"));
if (RT_FAILURE(rc))
N_("LsiLogic configuration error: failed to read RequestQueue as integer"));
char *pszCtrlType;
if (RT_FAILURE(rc))
N_("LsiLogic configuration error: failed to read ControllerType as string"));
char szDevTag[20];
if (RT_FAILURE(rc))
N_("LsiLogic configuration error: failed to determine controller type from string"));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
{
else
}
else if (RT_FAILURE(rc))
N_("LsiLogic configuration error: failed to read NumPorts as integer"));
bool fBootable;
if (RT_FAILURE(rc))
N_("LsiLogic configuration error: failed to read Bootable as boolean"));
/* Init static parts. */
{
}
{
}
else
# ifdef VBOX_WITH_MSI_DEVICES
# endif
/*
* 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.
*/
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply free queue"));
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply post queue"));
/*
* Register the PCI device, it's I/O regions.
*/
if (RT_FAILURE(rc))
return rc;
# ifdef VBOX_WITH_MSI_DEVICES
/* use this code for MSI-X support */
# if 0
# else
# endif
if (RT_FAILURE (rc))
{
/* That's OK, we can work without MSI */
}
# 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];
if (RT_FAILURE(rc))
return rc;
/*
* We need one entry free in the queue.
*/
/*
* Allocate memory for the queues.
*/
if (RT_FAILURE(rc))
return rc;
/*
* Allocate task cache.
*/
if (RT_FAILURE(rc))
else
/*
* Create event semaphore and worker thread.
*/
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
N_("LsiLogic: Failed to create SUP event semaphore"));
/*
* Allocate device states.
*/
pThis->paDeviceStates = (PLSILOGICDEVICE)RTMemAllocZ(sizeof(LSILOGICDEVICE) * pThis->cDeviceStates);
if (!pThis->paDeviceStates)
for (unsigned i = 0; i < pThis->cDeviceStates; i++)
{
char szName[24];
/* Initialize static parts of the device. */
/* Attach SCSI driver. */
if (RT_SUCCESS(rc))
{
/* Get SCSI connector interface. */
AssertMsgReturn(pDevice->pDrvSCSIConnector, ("Missing SCSI interface below\n"), VERR_PDM_MISSING_INTERFACE);
}
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
{
rc = VINF_SUCCESS;
}
else
{
return rc;
}
}
/*
* Attach status driver (optional).
*/
if (RT_SUCCESS(rc))
else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
{
}
/* Initialize the SCSI emulation for the BIOS. */
/*
* Register I/O port space in ISA region for BIOS access
* if the controller is marked as bootable.
*/
if (fBootable)
{
"LsiLogic BIOS");
"LsiLogic SAS BIOS");
else
if (RT_FAILURE(rc))
}
/* Register save state handlers. */
if (RT_FAILURE(rc))
/*
* Register the info item.
*/
char szTmp[128];
? "LsiLogic SPI info."
: "LsiLogic SAS info.", lsilogicR3Info);
/* Perform hard reset. */
return rc;
}
/**
* The device registration structure - SPI SCSI controller.
*/
const PDMDEVREG g_DeviceLsiLogicSCSI =
{
/* u32Version */
/* szName */
"lsilogicscsi",
/* szRCMod */
"VBoxDDGC.gc",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
"LSI Logic 53c1030 SCSI controller.\n",
/* fFlags */
/* fClass */
/* cMaxInstances */
~0U,
/* cbInstance */
sizeof(LSILOGICSCSI),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
/* pfnMemSetup */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
/* pfnResume */
/* pfnAttach */
/* pfnDetach */
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
/**
* The device registration structure - SAS controller.
*/
const PDMDEVREG g_DeviceLsiLogicSAS =
{
/* u32Version */
/* szName */
"lsilogicsas",
/* szRCMod */
"VBoxDDGC.gc",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
"LSI Logic SAS1068 controller.\n",
/* fFlags */
/* fClass */
/* cMaxInstances */
~0U,
/* cbInstance */
sizeof(LSILOGICSCSI),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
/* pfnMemSetup */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
/* pfnResume */
/* pfnAttach */
/* pfnDetach */
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */