DevLsiLogicSCSI.cpp revision 9d2fb81a3dedb355d3975d39d474a7935d05261e
/* $Id$ */
/** @file
* VBox storage devices: LsiLogic LSI53c1030 SCSI controller.
*/
/*
* Copyright (C) 2006-2009 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.
*/
//#define DEBUG
#define LOG_GROUP LOG_GROUP_DEV_LSILOGICSCSI
#include <VBox/pdmqueue.h>
#include <VBox/pdmcritsect.h>
#ifdef IN_RING3
# include <iprt/memcache.h>
#endif
#include "DevLsiLogicSCSI.h"
#include "VBoxSCSI.h"
#include "../Builtins.h"
/** The current saved state version. */
#define LSILOGIC_SAVED_STATE_VERSION 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
/**
* 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. */
/**
* 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 interace. */
/** 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. */
/**
* 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. */
bool fDoorbellInProgress;
/** 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. */
#if HC_ARCH_BITS == 64
#endif
/** Queue to send tasks to R3. - R3 ptr */
/** Queue to send tasks to R3. - R0 ptr */
/** Queue to send tasks to R3. - RC ptr */
#if HC_ARCH_BITS == 64
#endif
/** Number of device states allocated. */
#if HC_ARCH_BITS == 64
#endif
/** States for attached devices. */
/** MMIO address the device is mapped to. */
/** I/O port address the device is mapped to. */
/** 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. */
/** Actual position in the buffer. */
/** Size of the message which is given in the doorbell message in dwords. */
/** Reply buffer. */
/** 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. */
/** 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. */
/** 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. */
#if HC_ARCH_BITS == 64
#endif
/** 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. */
#if HC_ARCH_BITS == 64
#endif
/** Indicates that PDMDevHlpAsyncNotificationCompleted should be called when
* a port is entering the idle state. */
bool volatile fSignalIdle;
} LSILOGISCSI, *PLSILOGICSCSI;
/**
* Scatter gather list entry data.
*/
typedef struct LSILOGICTASKSTATESGENTRY
{
/** Flag whether the buffer in the list is from the guest or an
* allocated temporary buffer because the segments in the guest
* are not sector aligned.
*/
bool fGuestMemory;
/** Flag whether the buffer contains data or is the destination for the transfer. */
bool fBufferContainsData;
/** Pointer to the start of the buffer. */
void *pvBuf;
/** Size of the buffer. */
/** Flag dependent data. */
union
{
/** Data to handle direct mappings of guest buffers. */
/** The segment in the guest which is not sector aligned. */
} u;
/**
* Task state object which holds all neccessary data while
* processing the request from the guest.
*/
typedef struct LSILOGICTASKSTATE
{
/** 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. */
/** Number of scatter gather list entries. */
/** How many entries would fit into the sg list. */
/** How many times the list was too big. */
/** Pointer to the first entry of the scatter gather list. */
/** How many entries would fit into the sg info list. */
/** Number of entries for the information entries. */
/** How many times the list was too big. */
/** Pointer to the first mapping information entry. */
/** Size of the temporary buffer for unaligned guest segments. */
/** Pointer to the temporary buffer. */
void *pvBufferUnaligned;
/** Pointer to the sense buffer. */
/** Flag whether the request was issued from the BIOS. */
bool fBIOS;
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
#ifdef IN_RING3
static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConfigurationRequest pConfigurationReq,
#endif
#define PDMIBASE_2_PLSILOGICDEVICE(pInterface) ( (PLSILOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICDEVICE, IBase)) )
#define PDMISCSIPORT_2_PLSILOGICDEVICE(pInterface) ( (PLSILOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICDEVICE, ISCSIPort)) )
#define PDMILEDPORTS_2_PLSILOGICDEVICE(pInterface) ( (PLSILOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICDEVICE, ILed)) )
#define PDMIBASE_2_PLSILOGICSCSI(pInterface) ( (PLSILOGICSCSI)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICSCSI, IBase)) )
#define PDMILEDPORTS_2_PLSILOGICSCSI(pInterface) ( (PLSILOGICSCSI)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICSCSI, ILeds)) )
/** 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 device instance data.
*/
{
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 interupt status.
*
* @returns nothing.
* @param pLsiLogic Pointer to the device instance.
* @param uStatus The status bit to set.
*/
{
}
/**
* Clears a given interrupt status bit in the status register and
* updates the interupt status.
*
* @returns nothing.
* @param pLsiLogic Pointer to the device instance.
* @param uStatus The status bit to set.
*/
{
}
/**
* Sets the I/O controller into fault state and sets the fault code.
*
* @returns nothing
* @param pLsiLogic Pointer to the controller device instance.
* @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"));
}
}
#ifdef IN_RING3
/**
* Performs a hard reset on the controller.
*
* @returns VBox status code.
* @param pThis Pointer to the device instance to initialize.
*/
{
/* The interrupts are masked out. */
/* Reset interrupt states. */
pThis->uInterruptStatus = 0;
/* Reset the queues. */
/* Disable diagnostic access. */
pThis->iDiagnosticAccess = 0;
/* Set default values. */
/** @todo: Put stuff to reset here. */
/* 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 pLsiLogic Pointer to the device instance
* @param u32MessageContext The message context ID to post.
*/
{
int rc;
/* Write message context ID into reply post queue. */
#if 0
/* Check for a entry in the queue. */
if (RT_UNLIKELY(pLsiLogic->uReplyPostQueueNextAddressRead != pLsiLogic->uReplyPostQueueNextEntryFreeWrite))
{
/* Set error code. */
return;
}
#endif
/* We have a context reply. */
ASMAtomicWriteU32(&pLsiLogic->CTX_SUFF(pReplyPostQueueBase)[pLsiLogic->uReplyPostQueueNextEntryFreeWrite], u32MessageContext);
/* Set interrupt. */
}
{
if (pTaskState->pvBufferUnaligned)
pTaskState->cSGListSize = 0;
pTaskState->cSGInfoSize = 0;
pTaskState->cSGInfoEntries = 0;
pTaskState->cSGListTooBig = 0;
pTaskState->cbBufferUnaligned = 0;
}
{
return VINF_SUCCESS;
}
{
}
#endif /* IN_RING3 */
/**
* Takes neccessary steps to finish a reply frame.
*
* @returns nothing
* @param pLsiLogic Pointer to the device instance
* @param pReply Pointer to the reply message.
* @param fForceReplyFifo Flag whether the use of the reply post fifo is forced.
*/
static void lsilogicFinishAddressReply(PLSILOGICSCSI pLsiLogic, 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. */
pLsiLogic->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. */
#if 0
/* Check for a free reply frame. */
if (RT_UNLIKELY(pLsiLogic->uReplyFreeQueueNextAddressRead != pLsiLogic->uReplyFreeQueueNextEntryFreeWrite))
{
/* Set error code. */
return;
}
#endif
uint32_t u32ReplyFrameAddressLow = pLsiLogic->CTX_SUFF(pReplyFreeQueueBase)[pLsiLogic->uReplyFreeQueueNextAddressRead];
/* Build 64bit physical address. */
RTGCPHYS GCPhysReplyMessage = LSILOGIC_RTGCPHYS_FROM_U32(pLsiLogic->u32HostMFAHighAddr, u32ReplyFrameAddressLow);
size_t cbReplyCopied = (pLsiLogic->cbReplyFrame < sizeof(MptReplyUnion)) ? pLsiLogic->cbReplyFrame : sizeof(MptReplyUnion);
/* Write reply to guest memory. */
/* Write low 32bits of reply frame into post reply queue. */
#if 0
/* Check for a entry in the queue. */
if (RT_UNLIKELY(pLsiLogic->uReplyPostQueueNextAddressRead != pLsiLogic->uReplyPostQueueNextEntryFreeWrite))
{
/* Set error code. */
return;
}
#endif
/* We have a address reply. Set the 31th bit to indicate that. */
ASMAtomicWriteU32(&pLsiLogic->CTX_SUFF(pReplyPostQueueBase)[pLsiLogic->uReplyPostQueueNextEntryFreeWrite],
if (fForceReplyFifo)
{
pLsiLogic->fDoorbellInProgress = false;
}
/* Set interrupt. */
#else
AssertMsgFailed(("This is not allowed to happen.\n"));
#endif
}
}
#ifdef IN_RING3
/**
* Processes a given Request from the guest
*
* @returns VBox status code.
* @param pLsiLogic Pointer to the device instance.
* @param pMessageHdr Pointer to the message header of the request.
* @param pReply Pointer to the reply.
*/
static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr pMessageHdr, PMptReplyUnion pReply)
{
int rc = VINF_SUCCESS;
bool fForceReplyPostFifo = false;
#ifdef DEBUG
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 = pLsiLogic->cReplyQueueEntries - 1; /* One entry is always free. */
pReply->IOCFacts.u16GlobalCredits = pLsiLogic->cRequestQueueEntries - 1; /* One entry is always free. */
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;
}
{
pLsiLogic->fEventNotificationEnabled = true;
else
pLsiLogic->fEventNotificationEnabled = false;
break;
}
{
AssertMsgFailed(("todo"));
break;
}
{
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
/**
* Writes a value to a register at a given offset.
*
* @returns VBox status code.
* @param pThis Pointer to the LsiLogic SCSI controller instance data.
* @param uOffset Offset of the register to write.
* @param pv Pointer to the value to write
* @param cb Number of bytes to write.
*/
{
switch (uOffset)
{
case LSILOGIC_REG_REPLY_QUEUE:
{
/* Add the entry to the reply free queue. */
ASMAtomicWriteU32(&pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextEntryFreeWrite], u32);
break;
}
{
ASMAtomicWriteU32(&pThis->CTX_SUFF(pRequestQueueBase)[pThis->uRequestQueueNextEntryFreeWrite], u32);
/* Send notification to R3 if there is not one send already. */
{
}
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->fDoorbellInProgress)
{
switch (uFunction)
{
{
/* Reset interrupt states. */
pThis->uInterruptMask = 0;
pThis->uInterruptStatus = 0;
/* Reset the queues. */
break;
}
{
AssertMsgFailed(("todo\n"));
break;
}
{
pThis->fDoorbellInProgress = true;
/* Update the interrupt status to notify the guest that a doorbell function was started. */
break;
}
{
AssertMsgFailed(("todo\n"));
break;
}
default:
}
}
else
{
/*
* 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_HC_MMIO_WRITE;
#endif
#ifdef IN_RING3
{
int rc = lsilogicProcessMessageRequest(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.
*/
if ( (pThis->fDoorbellInProgress)
{
{
/* Reply finished. Reset doorbell in progress status. */
pThis->fDoorbellInProgress = false;
}
}
break;
}
{
break;
}
{
if (pThis->fDiagnosticEnabled)
{
/* Any value will cause a reset and disabling access. */
pThis->fDiagnosticEnabled = false;
pThis->iDiagnosticAccess = 0;
}
{
{
/*
* 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;
}
{
#ifndef IN_RING3
return VINF_IOM_HC_IOPORT_WRITE;
#else
{
}
break;
#endif
}
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 SCSI controller instance data.
* @param uOffset Offset of the register to read.
* @param pv Where to store the content of the register.
* @param cb Number of bytes to read.
*/
{
int rc = VINF_SUCCESS;
/* Align to a 4 byte offset. */
switch (uOffset & ~3)
{
case LSILOGIC_REG_REPLY_QUEUE:
{
/*
* Non 4-byte access 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.
*/
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.
*/
if (pThis->fDoorbellInProgress)
{
/* Return next 16bit value. */
}
else
{
/* We return the status code of the I/O controller. */
}
break;
}
{
break;
}
{
break;
}
{
if (pThis->fDiagnosticEnabled)
else
u32 = 0;
break;
}
case LSILOGIC_REG_TEST_BASE_ADDRESS: /* The spec doesn't say anything about these registers, so we just ignore them */
default: /* Ignore. */
{
break;
}
}
/* Clip data according to the read size. */
switch (cb)
{
case 4:
{
break;
}
case 2:
{
break;
}
case 1:
{
break;
}
default:
}
return rc;
}
{
if (rc == VINF_IOM_HC_MMIO_WRITE)
return rc;
}
{
if (rc == VINF_IOM_HC_MMIO_READ)
return rc;
}
{
}
{
}
{
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
/**
* Copies a contigous buffer into the scatter gather list provided by the guest.
*
* @returns nothing
* @param pTaskState Pointer to the task state which contains the SGL.
* @param pvBuf Pointer to the buffer to copy.
* @param cbCopy Number of bytes to copy.
*/
static void lsilogicScatterGatherListCopyFromBuffer(PLSILOGICTASKSTATE pTaskState, void *pvBuf, size_t cbCopy)
{
unsigned cSGEntry = 0;
{
/* We finished. */
if (!cbCopy)
break;
/* Advance the buffer. */
/* Go to the next entry in the list. */
pSGEntry++;
cSGEntry++;
}
}
/**
* Copy a temporary buffer into a part of the guest scatter gather list
* described by the given descriptor entry.
*
* @returns nothing.
* @param pDevIns Pointer to the device instance data.
* @param pSGInfo Pointer to the segment info structure which describes the guest segments
* to write to which are unaligned.
*/
{
/* Copy into SG entry. */
}
/**
* Copy a part of the guest scatter gather list into a temporary buffer.
*
* @returns nothing.
* @param pDevIns Pointer to the device instance data.
* @param pSGInfo Pointer to the segment info structure which describes the guest segments
* to read from which are unaligned.
*/
{
/* Copy into temporary buffer. */
}
static int lsilogicScatterGatherListAllocate(PLSILOGICTASKSTATE pTaskState, uint32_t cSGList, uint32_t cSGInfo, uint32_t cbUnaligned)
{
{
/* The entries are not allocated yet or the number is too small. */
if (pTaskState->cSGListSize)
/* Allocate R3 scatter gather list. */
if (!pTaskState->pSGListHead)
return VERR_NO_MEMORY;
/* Reset usage statistics. */
pTaskState->cSGListTooBig = 0;
}
{
/*
* The list is too big. Increment counter.
* So that the destroying function can free
* the list if it is too big too many times
* in a row.
*/
}
else
{
/*
* Needed entries matches current size.
* Reset counter.
*/
pTaskState->cSGListTooBig = 0;
}
{
/* The entries are not allocated yet or the number is too small. */
if (pTaskState->cSGInfoSize)
pTaskState->paSGEntries = (PLSILOGICTASKSTATESGENTRY)RTMemAllocZ(cSGInfo * sizeof(LSILOGICTASKSTATESGENTRY));
if (!pTaskState->paSGEntries)
return VERR_NO_MEMORY;
/* Reset usage statistics. */
pTaskState->cSGInfoTooBig = 0;
}
{
/*
* The list is too big. Increment counter.
* So that the destroying function can free
* the list if it is too big too many times
* in a row.
*/
}
else
{
/*
* Needed entries matches current size.
* Reset counter.
*/
pTaskState->cSGInfoTooBig = 0;
}
{
if (pTaskState->pvBufferUnaligned)
if (!pTaskState->pvBufferUnaligned)
return VERR_NO_MEMORY;
}
/* Make debugging easier. */
#ifdef DEBUG
if (pTaskState->pvBufferUnaligned)
#endif
return VINF_SUCCESS;
}
/**
* Destroy a scatter gather list.
*
* @returns nothing.
* @param pLsiLogic Pointer to the LsiLogic SCSI controller.
* @param pTaskState Pointer to the task state.
*/
static void lsilogicScatterGatherListDestroy(PLSILOGICSCSI pLsiLogic, PLSILOGICTASKSTATE pTaskState)
{
for (unsigned i = 0; i < pTaskState->cSGInfoEntries; i++)
{
if (pSGInfoCurr->fGuestMemory)
{
/* Release the lock. */
}
else if (!pSGInfoCurr->fBufferContainsData)
{
/* Copy the data into the guest segments now. */
}
pSGInfoCurr++;
}
/* Free allocated memory if the list was too big too many times. */
}
#ifdef DEBUG
/**
* Dump an SG entry.
*
* @returns nothing.
* @param pSGEntry Pointer to the SG entry to dump
*/
{
{
case MPTSGENTRYTYPE_SIMPLE:
{
Log(("%s: u32DataBufferAddressLow=%u\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow));
{
Log(("%s: u32DataBufferAddressHigh=%u\n", __FUNCTION__, pSGEntry->Simple64.u32DataBufferAddressHigh));
((uint64_t)pSGEntry->Simple64.u32DataBufferAddressHigh << 32) | pSGEntry->Simple64.u32DataBufferAddressLow));
}
else
break;
}
case MPTSGENTRYTYPE_CHAIN:
{
else
break;
}
}
}
#endif
/**
* Create scatter gather list descriptors.
*
* @returns VBox status code.
* @param pLsiLogic Pointer to the LsiLogic SCSI controller.
* @param pTaskState Pointer to the task state.
* @param GCPhysSGLStart Guest physical address of the first SG entry.
* @param uChainOffset Offset in bytes from the beginning of the SGL segment to the chain element.
* @thread EMT
*/
{
int rc = VINF_SUCCESS;
bool fUnaligned; /* Flag whether the current buffer is unaligned. */
uint32_t cSGEntriesR3 = 0;
bool fDoMapping = false;
bool fEndOfList;
/*
* Two passes - one to count needed scatter gather list entries and needed unaligned
* buffers and one to actually map the SG list into R3.
*/
for (int i = 0; i < 2; i++)
{
fUnaligned = false;
cbUnaligned = 0;
fEndOfList = false;
if (fDoMapping)
{
/* The number of needed SG entries in R3 is known. Allocate needed memory. */
/* We are now able to map the pages into R3. */
/* Initialize first segment to remove the need for additional if checks later in the code. */
pSGInfoCurr->fGuestMemory= false;
}
/* Go through the list until we reach the end. */
while (!fEndOfList)
{
bool fEndOfSegment = false;
while (!fEndOfSegment)
{
/* Read the entry. */
#ifdef DEBUG
#endif
/* Check if this is a zero element. */
{
pTaskState->cSGListEntries = 0;
pTaskState->cSGInfoEntries = 0;
return VINF_SUCCESS;
}
{
GCPhysSGEntryNext += sizeof(MptSGEntrySimple64);
}
else
GCPhysSGEntryNext += sizeof(MptSGEntrySimple32);
if (fDoMapping)
{
pSGInfoCurr->fGuestMemory = false;
if (fBufferContainsData)
pSGInfoCurr++;
}
else
{
cSGInfo++;
}
/* Check if we reached the end of the list. */
{
/* We finished. */
fEndOfSegment = true;
fEndOfList = true;
}
{
fEndOfSegment = true;
}
} /* while (!fEndOfSegment) */
/* Get next chain element. */
if (uChainOffsetNext)
{
PDMDevHlpPhysRead(pDevIns, GCPhysSegmentStart + uChainOffsetNext, &SGEntryChain, sizeof(MptSGEntryChain));
/* Set the next address now. */
}
} /* while (!fEndOfList) */
fDoMapping = true;
if (fUnaligned)
}
/* Initialize first entry. */
pSGInfoCurr++;
cSGEntries = 1;
/* Construct the scatter gather list. */
{
{
("Buffer ist not sector aligned but the buffer addresses are not adjacent\n"));
}
else
{
{
}
else
{
pSGEntryCurr++;
cSGEntries++;
}
}
pSGInfoCurr++;
}
return rc;
}
/*
* Disabled because the sense buffer provided by the LsiLogic driver for Windows XP
* crosses page boundaries.
*/
#if 0
/**
* Free the sense buffer.
*
* @returns nothing.
* @param pTaskState Pointer to the task state.
*/
{
}
/**
* Map the sense buffer into R3.
*
* @returns VBox status code.
* @param pTaskState Pointer to the task state.
* @note Current assumption is that the sense buffer is not scattered and does not cross a page boundary.
*/
{
int rc = VINF_SUCCESS;
#ifdef RT_STRICT
#endif
("Impossible GCPhysAddrSenseBuffer < GCPhysAddrSenseBufferBase\n"));
/* Sanity checks for the assumption. */
("Sense buffer crosses page boundary\n"));
rc = PDMDevHlpPhysGCPhys2CCPtr(pDevIns, GCPhysAddrSenseBufferBase, (void **)&pTaskState->pbSenseBuffer, &pTaskState->PageLockSense);
/* Correct start address of the sense buffer. */
return rc;
}
#endif
#ifdef DEBUG
{
Log(("%s: u32SenseBufferLowAddress=%#x\n", __FUNCTION__, pSCSIIORequest->u32SenseBufferLowAddress));
}
#endif
/**
* 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 pLsiLogic Pointer to the device instance which sends the request.
* @param pTaskState Pointer to the task state data.
*/
{
int rc = VINF_SUCCESS;
#ifdef DEBUG
#endif
pTaskState->fBIOS = false;
if (uChainOffset)
/* Create Scatter gather list. */
#if 0
/* Map sense buffer. */
#endif
{
if (pTargetDevice->pDrvBase)
{
/* Setup the SCSI request. */
uint8_t uDataDirection = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pTaskState->GuestRequest.SCSIIO.u32Control);
else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE)
else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ)
rc = pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTargetDevice->pDrvSCSIConnector, &pTaskState->PDMScsiRequest);
return VINF_SUCCESS;
}
else
{
/* Device is not present report SCSI selection timeout. */
}
}
else
{
/* Report out of bounds target ID or bus. */
else
}
/* The rest is equal to both errors. */
pTaskState->IOCReply.SCSIIOError.u8SenseBufferLength = pTaskState->GuestRequest.SCSIIO.u8SenseBufferLength;
pTaskState->IOCReply.SCSIIOError.u32MessageContext = pTaskState->GuestRequest.SCSIIO.u32MessageContext;
return rc;
}
/**
* Called upon completion of the request from the SCSI driver below.
* This function frees all allocated ressources and notifies the guest
* that the process finished by asserting an interrupt.
*
* @returns VBox status code.
* @param pInterface Pointer to the interface the called funtion belongs to.
* @param pSCSIRequest Pointer to the SCSI request which finished.
*/
static DECLCALLBACK(int) lsilogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest, int rcCompletion)
{
{
}
else
{
#if 0
#else
/* Copy the sense buffer over. */
RT_UNLIKELY(pTaskState->GuestRequest.SCSIIO.u8SenseBufferLength < pTaskState->PDMScsiRequest.cbSenseBuffer)
#endif
else
{
/* The SCSI target encountered an error during processing post a reply. */
pTaskState->IOCReply.SCSIIOError.u8SenseBufferLength = pTaskState->GuestRequest.SCSIIO.u8SenseBufferLength;
pTaskState->IOCReply.SCSIIOError.u32MessageContext = pTaskState->GuestRequest.SCSIIO.u32MessageContext;
}
}
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 pLsiLogic The LsiLogic controller instance.
* @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 lsilogicConfigurationPageGetExtended(PLSILOGICSCSI pLsiLogic, 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 pLsiLogic Pointer to the device instance which sends the request.
* @param pConfigurationReq Pointer to the request structure.
* @param pReply Pointer to the reply message frame
*/
static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, 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;
}
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 pLsiLogic Pointer to the Lsilogic SCSI instance.
*/
{
AssertMsg(pLsiLogic->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 The LsiLogic instance.
*/
{
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 SCSI instance.
*/
{
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 informations. */
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 informations. */
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 informations. */
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 pLsiLogic Pointer to the Lsilogic SCSI instance.
*/
{
/* 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 sepcific settings. */
/* Manufacturing Page 8 - Product sepcific settings. */
/* Manufacturing Page 9 - Product sepcific settings. */
/* Manufacturing Page 10 - Product sepcific 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
}
/**
* Transmit queue consumer
* Queue a new async task.
*
* @returns Success indicator.
* If false the item will not be removed and the flushing will stop.
* @param pDevIns The device instance.
* @param pItem The item to consume. Upon return this item will be freed.
*/
{
int rc = VINF_SUCCESS;
/* Only process request which arrived before we received the notification. */
uint32_t uRequestQueueNextEntryWrite = ASMAtomicReadU32(&pLsiLogic->uRequestQueueNextEntryFreeWrite);
/* Reset notification event. */
/* Go through the messages now and process them. */
{
uint32_t u32RequestMessageFrameDesc = pLsiLogic->CTX_SUFF(pRequestQueueBase)[pLsiLogic->uRequestQueueNextAddressRead];
(u32RequestMessageFrameDesc & ~0x07));
/* Get new task state. */
/* Read the message header from the guest first. */
PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &pTaskState->GuestRequest, sizeof(MptMessageHdr));
/* 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;
AssertMsgFailed(("todo\n"));
break;
default:
}
if (cbRequest != 0)
{
/* Read the complete message frame from guest memory now. */
/* Handle SCSI I/O requests now. */
{
}
else
{
}
}
}
return true;
}
/**
* Sets the emulated controller type from a given string.
*
* @returns VBox status code.
*
* @param pThis The LsiLogic devi state.
* @param pcszCtrlType The string to use.
*/
{
int rc = VERR_INVALID_PARAMETER;
{
rc = VINF_SUCCESS;
}
{
rc = VINF_SUCCESS;
}
return rc;
}
/**
* Port I/O Handler for IN operations - legacy port.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param uPort Port number used for the IN operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes read.
*/
{
int rc;
Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
return rc;
}
/**
* Prepares a request from the BIOS.
*
* @returns VBox status code.
* @param pLsiLogic Pointer to the LsiLogic device instance.
*/
{
int rc;
pTaskState->fBIOS = true;
{
{
rc = pTaskState->pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTaskState->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;
}
/**
* Port I/O Handler for OUT operations - legacy port.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param uPort Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
{
int rc;
Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x\n",
if (rc == VERR_MORE_DATA)
{
}
else if (RT_FAILURE(rc))
return VINF_SUCCESS;
}
/**
* Port I/O Handler for primary port range OUT string operations.
* @see FNIOMIOPORTOUTSTRING for details.
*/
static DECLCALLBACK(int) lsilogicIsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
{
int rc;
Log2(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
if (rc == VERR_MORE_DATA)
{
}
else if (RT_FAILURE(rc))
return rc;
}
/**
* Port I/O Handler for primary port range IN string operations.
* @see FNIOMIOPORTINSTRING for details.
*/
static DECLCALLBACK(int) lsilogicIsaIOPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
{
LogFlowFunc(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
}
{
int rc = VINF_SUCCESS;
? "LsiLogic"
: "LsiLogicSas";
? "LsiLogicDiag"
: "LsiLogicSasDiag";
("PCI region type and size do not match\n"));
{
/* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
if (RT_FAILURE(rc))
return rc;
if (pThis->fR0Enabled)
{
if (RT_FAILURE(rc))
return rc;
}
if (pThis->fGCEnabled)
{
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)
{
if (RT_FAILURE(rc))
return rc;
}
if (pThis->fGCEnabled)
{
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;
}
/**
* LsiLogic status info callback.
*
* @param pDevIns The device instance.
* @param pHlp The output helpers.
* @param pszArgs The arguments.
*/
{
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 The LsiLogic device instance.
*/
{
(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 The LsiLogic device instance.
*/
{
int rc = VINF_SUCCESS;
}
{
/* Save the device config. */
for (unsigned i = 0; i < pThis->cDeviceStates; i++)
return VINF_SSM_DONT_CALL_AGAIN;
}
{
/* Every device first. */
for (unsigned i = 0; i < pLsiLogic->cDeviceStates; i++)
{
("There are still outstanding requests on this device\n"));
}
/* Now the main device state. */
for (unsigned i = 0; i < pLsiLogic->cReplyQueueEntries; i++)
for (unsigned i = 0; i < pLsiLogic->cReplyQueueEntries; i++)
for (unsigned i = 0; i < pLsiLogic->cRequestQueueEntries; i++)
/* 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);
}
static DECLCALLBACK(int) lsilogicLoadExec(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 < pLsiLogic->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 < pLsiLogic->cDeviceStates; i++)
{
("There are still outstanding requests on this device\n"));
}
/* Now the main device state. */
{
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 < pLsiLogic->cReplyQueueEntries; i++)
for (unsigned i = 0; i < pLsiLogic->cReplyQueueEntries; i++)
for (unsigned i = 0; i < pLsiLogic->cRequestQueueEntries; i++)
/* 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;
}
/**
* Gets the pointer to the status LED of a device - called from the SCSi driver.
*
* @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. Always 0 here as the driver
* doesn't know about other LUN's.
* @param ppLed Where to store the LED pointer.
*/
static DECLCALLBACK(int) lsilogicDeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
{
if (iLUN == 0)
{
return VINF_SUCCESS;
}
return VERR_PDM_LUN_NOT_FOUND;
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
{
return NULL;
}
/**
* 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) lsilogicStatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
{
{
return VINF_SUCCESS;
}
return VERR_PDM_LUN_NOT_FOUND;
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
{
return NULL;
}
/* -=-=-=-=- Helper -=-=-=-=- */
/**
* Checks if all asynchronous I/O is finished.
*
* Used by lsilogicReset, lsilogicSuspend and lsilogicPowerOff.
*
* @returns true if quiesced, false if busy.
* @param pDevIns The device instance.
*/
{
{
if (pThisDevice->pDrvBase)
{
if (pThisDevice->cOutstandingRequests != 0)
return false;
}
}
return true;
}
/**
* Callback employed by lsilogicR3Suspend and lsilogicR3PowerOff..
*
* @returns true if we've quiesced, false if we're still working.
* @param pDevIns The device instance.
*/
{
return false;
return true;
}
/**
* Common worker for ahciR3Suspend and ahciR3PowerOff.
*/
{
else
}
/**
* Suspend notification.
*
* @param pDevIns The device instance data.
*/
{
Log(("lsilogicSuspend\n"));
}
/**
* Detach notification.
*
* One harddisk at one port has been unplugged.
* The VM is suspended at this point.
*
* @param pDevIns The device instance.
* @param iLUN The logical unit which is being detached.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*/
{
return;
("LsiLogic: Device does not support hotplugging\n"));
/*
* Zero some important members.
*/
}
/**
* Attach command.
*
* This is called when we change block driver.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param iLUN The logical unit which is being detached.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*/
{
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 employed by lsilogicR3Reset.
*
* @returns true if we've quiesced, false if we're still working.
* @param pDevIns The device instance.
*/
{
return false;
return true;
}
/**
* @copydoc FNPDMDEVRESET
*/
{
else
{
}
}
/**
* @copydoc FNPDMDEVRELOCATE
*/
{
/* Relocate queues. */
}
/**
* @copydoc FNPDMDEVDESTRUCT
*/
{
if (pThis->paDeviceStates)
/* Destroy task cache. */
int rc = VINF_SUCCESS;
return rc;
}
/**
* Poweroff notification.
*
* @param pDevIns Pointer to the device instance
*/
{
Log(("lsilogicPowerOff\n"));
}
/**
* @copydoc FNPDMDEVCONSTRUCT
*/
{
int rc = VINF_SUCCESS;
char *pszCtrlType = NULL;
/*
* Validate and read configuration.
*/
"R0Enabled\0"
"ReplyQueueDepth\0"
"RequestQueueDepth\0"
"ControllerType\0"
"NumPorts\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"));
if (RT_FAILURE(rc))
N_("LsiLogic configuration error: failed to read ControllerType as string"));
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"));
/* Init static parts. */
{
}
{
}
else
/*
* Register the PCI device, it's I/O regions.
*/
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, LSILOGIC_PCI_SPACE_IO_SIZE, PCI_ADDRESS_SPACE_IO, lsilogicMap);
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicMap);
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicMap);
if (RT_FAILURE(rc))
return rc;
/* Intialize task queue. */
lsilogicNotifyQueueConsumer, true,
? "LsiLogic-Task"
: "LsiLogicSAS-Task",
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;
/*
* Create critical sections protecting the reply post and free queues.
*/
? "LsiLogicRFQ"
: "LsiLogicSasRFQ");
if (RT_FAILURE(rc))
N_("LsiLogic: cannot create critical section for reply free queue"));
? "LsiLogicRPQ"
: "LsiLogicSasRPQ");
if (RT_FAILURE(rc))
N_("LsiLogic: cannot create critical section for reply post queue"));
/*
* Allocate task cache.
*/
if (RT_FAILURE(rc))
N_("Cannot create task cache"));
else
/*
* Allocate device states.
*/
pThis->paDeviceStates = (PLSILOGICDEVICE)RTMemAllocZ(sizeof(LSILOGICDEVICE) * pThis->cDeviceStates);
if (!pThis->paDeviceStates)
N_("Failed to allocate memory for device states"));
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. */
"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.", lsilogicInfo);
/* 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 */
~0,
/* cbInstance */
sizeof(LSILOGICSCSI),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
/* pfnResume */
NULL,
/* 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 */
~0,
/* cbInstance */
sizeof(LSILOGICSCSI),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
/* pfnResume */
NULL,
/* pfnAttach */
/* pfnDetach */
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */