DevBusLogic.cpp revision b14c09e8af60e4d1ba4da27da03cbd175617f298
/* $Id$ */
/** @file
* VBox storage devices - BusLogic SCSI host adapter BT-958.
*
* Based on the Multi-Master Ultra SCSI Systems Technical Reference Manual.
*/
/*
* 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_BUSLOGIC
#ifdef IN_RING3
# include <iprt/memcache.h>
#endif
#include "VBoxSCSI.h"
#include "VBoxDD.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** Maximum number of attached devices the adapter can handle. */
#define BUSLOGIC_MAX_DEVICES 16
/** Maximum number of scatter gather elements this device can handle. */
#define BUSLOGIC_MAX_SCATTER_GATHER_LIST_SIZE 128
/** Size of the command buffer. */
#define BUSLOGIC_COMMAND_SIZE_MAX 5
/** Size of the reply buffer. */
#define BUSLOGIC_REPLY_SIZE_MAX 64
/** Custom fixed I/O ports for BIOS controller access.
* Note that these should not be in the ISA range (below 400h) to avoid
* conflicts with ISA device probing. Addresses in the 300h-340h range should be
* especially avoided.
*/
#define BUSLOGIC_BIOS_IO_PORT 0x430
/** State saved version. */
#define BUSLOGIC_SAVED_STATE_MINOR_VERSION 3
/** Saved state version before the suspend on error feature was implemented. */
/** Saved state version before 24-bit mailbox support was implemented. */
/** The duration of software-initiated reset (in nano seconds).
* Not documented, set to 50 ms. */
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* State of a device attached to the buslogic host adapter.
*
* @implements PDMIBASE
* @implements PDMISCSIPORT
* @implements PDMILEDPORTS
*/
typedef struct BUSLOGICDEVICE
{
/** Pointer to the owning buslogic device instance. - R3 pointer */
/** Pointer to the owning buslogic device instance. - R0 pointer */
/** Pointer to the owning buslogic device instance. - RC pointer */
/** Flag whether device is present. */
bool fPresent;
/** LUN of the device. */
#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. */
#if HC_ARCH_BITS == 64
#endif
/** Number of outstanding tasks on the port. */
volatile uint32_t cOutstandingRequests;
/**
* Commands the BusLogic adapter supports.
*/
enum BUSLOGICCOMMAND
{
BUSLOGICCOMMAND_INQUIRE_BOARD_ID = 0x04,
BUSLOGICCOMMAND_SET_TIME_OFF_BUS = 0x08,
BUSLOGICCOMMAND_EXT_BIOS_INFO = 0x28,
BUSLOGICCOMMAND_UNLOCK_MAILBOX = 0x29,
BUSLOGICCOMMAND_SET_CCB_FORMAT = 0x96,
BUSLOGICCOMMAND_READ_SCAM_DATA = 0xa8,
#pragma pack(1)
/**
* Auto SCSI structure which is located
* in host adapter RAM and contains several
* configuration parameters.
*/
typedef struct AutoSCSIRam
{
bool fFloppyEnabled : 1;
bool fFloppySecondary : 1;
bool fLevelSensitiveInterrupt : 1;
unsigned char uReserved2 : 2;
unsigned char uSystemRAMAreForBIOS : 3;
unsigned char uDMAChannel : 7;
bool fDMAAutoConfiguration : 1;
unsigned char uIrqChannel : 7;
bool fIrqAutoConfiguration : 1;
bool fLowByteTerminated : 1;
bool fParityCheckingEnabled : 1;
bool fHighByteTerminated : 1;
bool fNoisyCablingEnvironment : 1;
bool fFastSynchronousNeogtiation : 1;
bool fBusResetEnabled : 1;
bool fReserved3 : 1;
bool fActiveNegotiationEnabled : 1;
bool fHostAdapterBIOSEnabled : 1;
bool fBIOSRedirectionOfInt19 : 1;
bool fExtendedTranslation : 1;
bool fMapRemovableAsFixed : 1;
bool fReserved4 : 1;
bool fBIOSSupportsMoreThan2Drives : 1;
bool fBIOSInterruptMode : 1;
bool fFlopticalSupport : 1;
unsigned char uPCIInterruptPin : 2;
unsigned char uHostAdapterIoPortAddress : 2;
bool fStrictRoundRobinMode : 1;
bool fVesaBusSpeedGreaterThan33MHz : 1;
bool fVesaBurstWrite : 1;
bool fVesaBurstRead : 1;
bool fReserved7 : 1;
bool fSCAMDominant : 1;
bool fSCAMenabled : 1;
bool fSCAMLevel2 : 1;
unsigned char uReserved8 : 4;
bool fInt13Extension : 1;
bool fReserved9 : 1;
bool fCDROMBoot : 1;
unsigned char uReserved10 : 5;
unsigned char uBootTargetId : 4;
unsigned char uBootChannel : 4;
bool fForceBusDeviceScanningOrder : 1;
unsigned char uReserved11 : 7;
} AutoSCSIRam, *PAutoSCSIRam;
#pragma pack()
/**
* The local Ram.
*/
typedef union HostAdapterLocalRam
{
/** Byte view. */
/** Structured view. */
struct
{
/** Offset 0 - 63 is for BIOS. */
/** Auto SCSI structure. */
} structured;
/** Ugly 24-bit big-endian addressing. */
typedef struct
{
#define LEN_TO_U32 ADDR_TO_U32
#define U32_TO_LEN U32_TO_ADDR
/** @name Compatible ISA base I/O port addresses. Disabled if zero.
* @{ */
#define NUM_ISA_BASES 8
#define ISA_BASE_DISABLED 6
{
0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0, 0
};
/** @} */
/** Pointer to a task state structure. */
typedef struct BUSLOGICTASKSTATE *PBUSLOGICTASKSTATE;
/**
* Main BusLogic device state.
*
* @extends PCIDEVICE
* @implements PDMILEDPORTS
*/
typedef struct BUSLOGIC
{
/** The PCI device structure. */
/** Pointer to the device instance - HC ptr */
/** Pointer to the device instance - R0 ptr */
/** Pointer to the device instance - RC ptr. */
/** Whether R0 is enabled. */
bool fR0Enabled;
/** Whether RC is enabled. */
bool fGCEnabled;
/** Base address of the I/O ports. */
/** Base address of the memory mapping. */
/** Status register - Readonly. */
/** Interrupt register - Readonly. */
volatile uint8_t regInterrupt;
/** Geometry register - Readonly. */
volatile uint8_t regGeometry;
/** Pending (delayed) interrupt. */
/** Local RAM for the fetch hostadapter local RAM request.
* I don't know how big the buffer really is but the maximum
* seems to be 256 bytes because the offset and count field in the command request
* are only one byte big.
*/
/** Command code the guest issued. */
/** Buffer for the command parameters the adapter is currently receiving from the guest.
* Size of the largest command which is possible.
*/
/** Current position in the command buffer. */
/** Parameters left until the command is complete. */
/** Whether we are using the RAM or reply buffer. */
bool fUseLocalRam;
/** Buffer to store reply data from the controller to the guest. */
/** Position in the buffer we are reading next. */
/** Bytes left until the reply buffer is empty. */
/** Flag whether IRQs are enabled. */
bool fIRQEnabled;
/** Flag whether the ISA I/O port range is disabled
* to prevent the BIOS to access the device. */
bool fISAEnabled; /**< @todo unused, to be removed */
/** Flag whether 24-bit mailboxes are in use (default is 32-bit). */
bool fMbxIs24Bit;
/** ISA I/O port base (encoded in FW-compatible format). */
/** ISA I/O port base (disabled if zero). */
/** Default ISA I/O port base in FW-compatible format. */
/** Number of mailboxes the guest set up. */
#if HC_ARCH_BITS == 64
#endif
/** Time when HBA reset was last initiated. */ /**< @todo does this need to be saved? */
/** Physical base address of the outgoing mailboxes. */
/** Current outgoing mailbox position. */
/** Number of mailboxes ready. */
volatile uint32_t cMailboxesReady;
/** Whether a notification to R3 was send. */
volatile bool fNotificationSend;
#if HC_ARCH_BITS == 64
#endif
/** Physical base address of the incoming mailboxes. */
/** Current incoming mailbox position. */
/** Whether strict round robin is enabled. */
bool fStrictRoundRobinMode;
/** Whether the extended LUN CCB format is enabled for 32 possible logical units. */
bool fExtendedLunCCBFormat;
/** Queue to send tasks to R3. - HC ptr */
/** Queue to send tasks to R3. - HC ptr */
/** Queue to send tasks to R3. - RC ptr */
/** Critical section protecting access to the interrupt status register. */
/** Cache for task states. */
/** Device state for BIOS access. */
/** BusLogic device states. */
/** The base interface.
* @todo use PDMDEVINS::IBase */
/** Status Port - Leds interface. */
/** Partner of ILeds. */
#if HC_ARCH_BITS == 64
#endif
/** 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;
/** List of tasks which can be redone. */
#ifdef LOG_ENABLED
# if HC_ARCH_BITS == 64
# endif
volatile uint32_t cInMailboxesReady;
#endif
/** Register offsets in the I/O port space. */
#define BUSLOGIC_REGISTER_CONTROL 0 /**< Writeonly */
/** Fields for the control register. */
#define BUSLOGIC_REGISTER_STATUS 0 /**< Readonly */
/** Fields for the status register. */
# define BUSLOGIC_REGISTER_STATUS_COMMAND_INVALID RT_BIT(0)
/** Fields for the interrupt register. */
/** Structure for the INQUIRE_PCI_HOST_ADAPTER_INFORMATION reply. */
typedef struct ReplyInquirePCIHostAdapterInformation
{
unsigned char LowByteTerminated : 1;
unsigned char HighByteTerminated : 1;
/** Whether the provided info is valid. */
unsigned char InformationIsValid: 1;
/** Structure for the INQUIRE_CONFIGURATION reply. */
typedef struct ReplyInquireConfiguration
{
unsigned char uReserved1 : 5;
bool fDmaChannel5 : 1;
bool fDmaChannel6 : 1;
bool fDmaChannel7 : 1;
bool fIrqChannel9 : 1;
bool fIrqChannel10 : 1;
bool fIrqChannel11 : 1;
bool fIrqChannel12 : 1;
unsigned char uReserved2 : 1;
bool fIrqChannel14 : 1;
bool fIrqChannel15 : 1;
unsigned char uReserved3 : 1;
unsigned char uHostAdapterId : 4;
unsigned char uReserved4 : 4;
/** Structure for the INQUIRE_SETUP_INFORMATION reply. */
typedef struct ReplyInquireSetupInformationSynchronousValue
{
unsigned char uOffset : 4;
unsigned char uTransferPeriod : 3;
bool fSynchronous : 1;
typedef struct ReplyInquireSetupInformation
{
bool fSynchronousInitiationEnabled : 1;
bool fParityCheckingEnabled : 1;
unsigned char uReserved1 : 6;
/** Structure for the INQUIRE_EXTENDED_SETUP_INFORMATION. */
#pragma pack(1)
typedef struct ReplyInquireExtendedSetupInformation
{
unsigned char uReserved1 : 2;
bool fFastEISA : 1;
unsigned char uReserved2 : 3;
bool fLevelSensitiveInterrupt : 1;
unsigned char uReserved3 : 1;
unsigned char aFirmwareRevision[3];
bool fHostWideSCSI : 1;
bool fHostDifferentialSCSI : 1;
bool fHostSupportsSCAM : 1;
bool fHostUltraSCSI : 1;
bool fHostSmartTermination : 1;
unsigned char uReserved4 : 3;
#pragma pack()
/** Structure for the INITIALIZE EXTENDED MAILBOX request. */
#pragma pack(1)
typedef struct RequestInitializeExtendedMailbox
{
/** Number of mailboxes in guest memory. */
/** Physical address of the first mailbox. */
#pragma pack()
/** Structure for the INITIALIZE MAILBOX request. */
typedef struct
{
/** Number of mailboxes to set up. */
/** Physical address of the first mailbox. */
/**
* Structure of a mailbox in guest memory.
* The incoming and outgoing mailbox have the same size
* but the incoming one has some more fields defined which
* are marked as reserved in the outgoing one.
* The last field is also different from the type.
* For outgoing mailboxes it is the action and
* for incoming ones the completion status code for the task.
* We use one structure for both types.
*/
typedef struct Mailbox32
{
/** Physical address of the CCB structure in the guest memory. */
/** Type specific data. */
union
{
/** For outgoing mailboxes. */
struct
{
/** Reserved */
/** Action code. */
} out;
/** For incoming mailboxes. */
struct
{
/** The host adapter status after finishing the request. */
/** The status of the device which executed the request after executing it. */
/** Reserved. */
/** The completion status code of the request. */
} in;
} u;
} Mailbox32, *PMailbox32;
/** Old style 24-bit mailbox entry. */
typedef struct Mailbox24
{
/** Mailbox command (incoming) or state (outgoing). */
/** Physical address of the CCB structure in the guest memory. */
} Mailbox24, *PMailbox24;
/**
* Action codes for outgoing mailboxes.
*/
{
};
/**
* Completion codes for incoming mailboxes.
*/
{
};
/**
* Host adapter status for incoming mailboxes.
*/
{
};
/**
* Device status codes for incoming mailboxes.
*/
{
};
/**
* Opcode types for CCB.
*/
enum BUSLOGIC_CCB_OPCODE
{
BUSLOGIC_CCB_OPCODE_TARGET_CCB = 0x01,
};
/**
* Data transfer direction.
*/
{
BUSLOGIC_CCB_DIRECTION_UNKNOWN = 0x00,
BUSLOGIC_CCB_DIRECTION_IN = 0x01,
BUSLOGIC_CCB_DIRECTION_OUT = 0x02,
};
/**
* The command control block for a SCSI request.
*/
typedef struct CCB32
{
/** Opcode. */
/** Reserved */
unsigned char uReserved1 : 3;
/** Data direction for the request. */
unsigned char uDataDirection : 2;
/** Whether the request is tag queued. */
bool fTagQueued : 1;
/** Queue tag mode. */
unsigned char uQueueTag : 2;
/** Length of the SCSI CDB. */
/** Sense data length. */
/** Data length. */
/** Data pointer.
* This points to the data region or a scatter gather list based on the opcode.
*/
/** Reserved. */
/** Host adapter status. */
/** Device adapter status. */
/** The device the request is sent to. */
/**The LUN in the device. */
unsigned char uLogicalUnit : 5;
/** Legacy tag. */
bool fLegacyTagEnable : 1;
/** Legacy queue tag. */
unsigned char uLegacyQueueTag : 2;
/** The SCSI CDB. (A CDB can be 12 bytes long.) */
/** Reserved. */
/** Sense data pointer. */
/**
* The 24-bit command control block.
*/
typedef struct CCB24
{
/** Opcode. */
/** The LUN in the device. */
unsigned char uLogicalUnit : 3;
/** Data direction for the request. */
unsigned char uDataDirection : 2;
/** The target device ID. */
unsigned char uTargetId : 3;
/** Length of the SCSI CDB. */
/** Sense data length. */
/** Data length. */
/** Data pointer.
* This points to the data region or a scatter gather list based on the opc
*/
/** Pointer to next CCB for linked commands. */
/** Command linking identifier. */
/** Host adapter status. */
/** Device adapter status. */
/** Two unused bytes. */
/** The SCSI CDB. (A CDB can be 12 bytes long.) */
/**
* The common 24-bit/32-bit command control block. The 32-bit CCB is laid out
* such that many fields are in the same location as in the older 24-bit CCB.
*/
typedef struct CCBC
{
/** Opcode. */
/** The LUN in the device. */
unsigned char uPad1 : 3;
/** Data direction for the request. */
unsigned char uDataDirection : 2;
/** The target device ID. */
unsigned char uPad2 : 3;
/** Length of the SCSI CDB. */
/** Sense data length. */
/** Host adapter status. */
/** Device adapter status. */
/** The SCSI CDB (up to 12 bytes). */
typedef union CCBU
{
CCB32 n; /**< New 32-bit CCB. */
CCB24 o; /**< Old 24-bit CCB. */
CCBC c; /**< Common CCB subset. */
/** 32-bit scatter-gather list entry. */
typedef struct SGE32
{
/** 24-bit scatter-gather list entry. */
typedef struct SGE24
{
/**
* The structure for the "Execute SCSI Command" command.
*/
typedef struct ESCMD
{
/** Data length. */
/** Data pointer. */
/** The device the request is sent to. */
/** The LUN in the device. */
/** Reserved */
unsigned char uReserved1 : 3;
/** Data direction for the request. */
unsigned char uDataDirection : 2;
/** Reserved */
unsigned char uReserved2 : 3;
/** Length of the SCSI CDB. */
/** The SCSI CDB. (A CDB can be 12 bytes long.) */
/**
* Task state for a CCB request.
*/
typedef struct BUSLOGICTASKSTATE
{
/** Next in the redo list. */
/** Device this task is assigned to. */
/** The command control block from the guest. */
/** Mailbox read from guest memory. */
/** The SCSI request we pass to the underlying SCSI engine. */
/** Data buffer segment */
/** Pointer to the R3 sense buffer. */
/** Flag whether this is a request from the BIOS. */
bool fBIOS;
/** 24-bit request flag (default is 32-bit). */
bool fIs24Bit;
/** S/G entry size (depends on the above flag). */
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
#define PDMIBASE_2_PBUSLOGICDEVICE(pInterface) ( (PBUSLOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGICDEVICE, IBase)) )
#define PDMISCSIPORT_2_PBUSLOGICDEVICE(pInterface) ( (PBUSLOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGICDEVICE, ISCSIPort)) )
#define PDMILEDPORTS_2_PBUSLOGICDEVICE(pInterface) ( (PBUSLOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGICDEVICE, ILed)) )
#define PDMIBASE_2_PBUSLOGIC(pInterface) ( (PBUSLOGIC)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGIC, IBase)) )
#define PDMILEDPORTS_2_PBUSLOGIC(pInterface) ( (PBUSLOGIC)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGIC, ILeds)) )
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Assert IRQ line of the BusLogic adapter.
*
* @returns nothing.
* @param pBusLogic Pointer to the BusLogic device instance.
* @param fSuppressIrq Flag to suppress IRQ generation regardless of fIRQEnabled
* @param uFlag Type of interrupt being generated.
*/
{
/* The CMDC interrupt has priority over IMBL and MBOR. */
if (uIrqType & (BUSLOGIC_REGISTER_INTERRUPT_INCOMING_MAILBOX_LOADED | BUSLOGIC_REGISTER_INTERRUPT_OUTGOING_MAILBOX_AVAILABLE))
{
else
}
else if (uIrqType & BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE)
{
}
else
AssertMsgFailed(("Invalid interrupt state!\n"));
}
/**
* Deasserts the interrupt line of the BusLogic adapter.
*
* @returns nothing
* @param pBuslogic Pointer to the BusLogic device instance.
*/
{
pBusLogic->regInterrupt = 0;
/* If there's another pending interrupt, report it now. */
if (pBusLogic->uPendingIntr)
{
pBusLogic->uPendingIntr = 0;
}
}
#if defined(IN_RING3)
/**
* Advances the mailbox pointer to the next slot.
*/
{
pBusLogic->uMailboxOutgoingPositionCurrent = (pBusLogic->uMailboxOutgoingPositionCurrent + 1) % pBusLogic->cMailbox;
}
/**
* Initialize local RAM of host adapter with default values.
*
* @returns nothing.
* @param pBusLogic.
*/
{
/*
* These values are mostly from what I think is right
* looking at the dmesg output from a Linux guest inside
* a VMware server VM.
*
* So they don't have to be right :)
*/
pBusLogic->LocalRam.structured.autoSCSIData.fExtendedTranslation = true; /* Same as in geometry register. */
pBusLogic->LocalRam.structured.autoSCSIData.u16DeviceEnabledMask = ~0; /* All enabled. Maybe mask out non present devices? */
pBusLogic->LocalRam.structured.autoSCSIData.fStrictRoundRobinMode = pBusLogic->fStrictRoundRobinMode;
/** @todo calculate checksum? */
}
/**
* Do a hardware reset of the buslogic adapter.
*
* @returns VBox status code.
* @param pBusLogic Pointer to the BusLogic device instance.
* @param fResetIO Flag determining whether ISA I/O should be reset.
*/
{
/* Reset registers to default value. */
pBusLogic->regStatus = BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY | BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED;
pBusLogic->regInterrupt = 0;
pBusLogic->uPendingIntr = 0;
pBusLogic->iParameter = 0;
pBusLogic->fIRQEnabled = true;
/* Guest-initiated HBA reset does not affect ISA port I/O. */
if (fResetIO)
{
}
return VINF_SUCCESS;
}
#endif /* IN_RING3 */
/**
* Resets the command state machine for the next command and notifies the guest.
*
* @returns nothing.
* @param pBusLogic Pointer to the BusLogic device instance
* @param fSuppressIrq Flag to suppress IRQ generation regardless of current state
*/
{
pBusLogic->fUseLocalRam = false;
/* Modify I/O address does not generate an interrupt. */
{
/* Notify that the command is complete. */
}
pBusLogic->iParameter = 0;
}
#if defined(IN_RING3)
/**
* Initiates a hard reset which was issued from the guest.
*
* @returns nothing
* @param pBusLogic Pointer to the BusLogic device instance.
* @param fHardReset Flag initiating a hard (vs. soft) reset.
*/
{
buslogicR3HwReset(pBusLogic, false);
if (fHardReset)
{
/* Set the diagnostic active bit in the status register and clear the ready state. */
/* Remember when the guest initiated a reset (after we're done resetting). */
}
}
/**
* Send a mailbox with set status codes to the guest.
*
* @returns nothing.
* @param pBusLogic Pointer to the BusLogic device instance.
* @param pTaskState Pointer to the task state with the mailbox to send.
* @param uHostAdapterStatus The host adapter status code to set.
* @param uDeviceStatus The target device status to set.
* @param uMailboxCompletionCode Completion status code to set in the mailbox.
*/
{
/* Update CCB. */
/* Rewrite CCB up to the CDB; perhaps more than necessary. */
# ifdef RT_STRICT
unsigned uCodeOffs = pTaskState->fIs24Bit ? RT_OFFSETOF(Mailbox24, uCmdState) : RT_OFFSETOF(Mailbox32, u.out.uActionCode);
PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming + uCodeOffs, &uCode, sizeof(uCode));
# endif
/* Update mailbox. */
if (pTaskState->fIs24Bit)
{
Log(("24-bit mailbox: completion code=%u, CCB at %RGp\n", Mbx24.uCmdState, (RTGCPHYS)ADDR_TO_U32(Mbx24.aPhysAddrCCB)));
PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming, &Mbx24, sizeof(Mailbox24));
}
else
{
Log(("32-bit mailbox: completion code=%u, CCB at %RGp\n", pTaskState->MailboxGuest.u.in.uCompletionCode, (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB));
}
/* Advance to next mailbox position. */
# ifdef LOG_ENABLED
# endif
}
# ifdef LOG_ENABLED
/**
* Dumps the content of a mailbox for debugging purposes.
*
* @return nothing
* @param pMailbox The mailbox to dump.
* @param fOutgoing true if dumping the outgoing state.
* false if dumping the incoming state.
*/
{
if (fOutgoing)
{
}
else
{
}
}
/**
* Dumps the content of a command control block for debugging purposes.
*
* @returns nothing.
* @param pCCB Pointer to the command control block to dump.
* @param fIs24BitCCB Flag to determine CCB format.
*/
{
if (fIs24BitCCB)
{
}
else
{
}
}
# endif /* LOG_ENABLED */
/**
* Allocate data buffer.
*
* @param pTaskState Pointer to the task state.
* @param GCSGList Guest physical address of S/G list.
* @param cEntries Number of list entries to read.
* @param pSGEList Pointer to 32-bit S/G list storage.
*/
static void buslogicR3ReadSGEntries(PBUSLOGICTASKSTATE pTaskState, RTGCPHYS GCSGList, uint32_t cEntries, SGE32 *pSGEList)
{
/* Read the S/G entries. Convert 24-bit entries to 32-bit format. */
if (pTaskState->fIs24Bit)
{
{
}
}
else
}
/**
* Allocate data buffer.
*
* @returns VBox status code.
* @param pTaskState Pointer to the task state.
*/
{
/* Extract the data length and physical address from the CCB. */
if (pTaskState->fIs24Bit)
{
}
else
{
}
&& cbDataCCB)
{
/** @todo Check following assumption and what residual means. */
/*
* The BusLogic adapter can handle two different data buffer formats.
* The first one is that the data pointer entry in the CCB points to
* the buffer directly. In second mode the data pointer points to a
* scatter gather list which describes the buffer.
*/
if ( (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER)
|| (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
{
SGE32 aScatterGatherReadGC[32]; /* A buffer for scatter gather list entries read from guest memory. */
size_t cbDataToTransfer = 0;
/* Count number of bytes to transfer. */
do
{
buslogicR3ReadSGEntries(pTaskState, GCPhysAddrScatterGatherCurrent, cScatterGatherGCRead, aScatterGatherReadGC);
{
Log(("%s: GCPhysAddrDataBase=%RGp cbDataToTransfer=%u\n",
}
/* Set address to the next entries to read. */
} while (cScatterGatherGCLeft > 0);
/* Allocate buffer */
return VERR_NO_MEMORY;
/* Copy the data if needed */
{
do
{
buslogicR3ReadSGEntries(pTaskState, GCPhysAddrScatterGatherCurrent, cScatterGatherGCRead, aScatterGatherReadGC);
{
Log(("%s: GCPhysAddrDataBase=%RGp cbDataToTransfer=%u\n", __FUNCTION__, GCPhysAddrDataBase, cbDataToTransfer));
}
/* Set address to the next entries to read. */
} while (cScatterGatherGCLeft > 0);
}
}
|| pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH)
{
/* The buffer is not scattered. */
return VERR_NO_MEMORY;
Log(("Non scattered buffer:\n"));
/* Copy the data into the buffer. */
PDMDevHlpPhysRead(pDevIns, GCPhysAddrDataBase, pTaskState->DataSeg.pvSeg, pTaskState->DataSeg.cbSeg);
}
}
return VINF_SUCCESS;
}
/**
* Free allocated resources used for the scatter gather list.
*
* @returns nothing.
* @param pTaskState Pointer to the task state.
*/
{
/* Extract the data length and physical address from the CCB. */
if (pTaskState->fIs24Bit)
{
}
else
{
}
#if 1
/* Hack for NT 10/91: A CCB describes a 2K buffer, but TEST UNIT READY is executed. This command
* returns no data, hence the buffer must be left alone!
*/
cbDataCCB = 0;
#endif
if ( (cbDataCCB > 0)
{
if ( (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER)
|| (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
{
do
{
buslogicR3ReadSGEntries(pTaskState, GCPhysAddrScatterGatherCurrent, cScatterGatherGCRead, aScatterGatherReadGC);
{
Log(("%s: GCPhysAddrDataBase=%RGp cbDataToTransfer=%u\n", __FUNCTION__, GCPhysAddrDataBase, cbDataToTransfer));
}
/* Set address to the next entries to read. */
} while (cScatterGatherGCLeft > 0);
}
|| pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH)
{
/* The buffer is not scattered. */
Log(("Non-scattered buffer:\n"));
/* Copy the data into the guest memory. */
PDMDevHlpPCIPhysWrite(pDevIns, GCPhysAddrDataBase, pTaskState->DataSeg.pvSeg, pTaskState->DataSeg.cbSeg);
}
}
/* Update residual data length. */
if ( (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH)
|| (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
{
/** @todo we need to get the actual transfer length from the VSCSI layer?! */
cbResidual = 0; //LEN_TO_U32(pTaskState->CCBGuest.acbData) - ???;
if (pTaskState->fIs24Bit)
else
}
}
/** Convert sense buffer length taking into account shortcut values. */
{
/* Convert special sense buffer length values. */
if (cbSense == 0)
else if (cbSense == 1)
cbSense = 0; /* 1 means no sense data. */
else if (cbSense < 8)
return cbSense;
}
/**
* Free the sense buffer.
*
* @returns nothing.
* @param pTaskState Pointer to the task state.
* @param fCopy If sense data should be copied to guest memory.
*/
{
cbSenseBuffer = buslogicR3ConvertSenseBufferLength(pTaskState->CommandControlBlockGuest.c.cbSenseData);
/* Copy the sense buffer into guest memory if requested. */
if (fCopy && cbSenseBuffer)
{
/* With 32-bit CCBs, the (optional) sense buffer physical address is provided separately.
* On the other hand, with 24-bit CCBs, the sense buffer is simply located at the end of
* the CCB, right after the variable-length CDB.
*/
if (pTaskState->fIs24Bit)
{
}
else
}
}
/**
* Alloc the sense buffer.
*
* @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.
*/
{
cbSenseBuffer = buslogicR3ConvertSenseBufferLength(pTaskState->CommandControlBlockGuest.c.cbSenseData);
if (cbSenseBuffer)
{
if (!pTaskState->pbSenseBuffer)
return VERR_NO_MEMORY;
}
return VINF_SUCCESS;
}
#endif /* IN_RING3 */
/**
* Parses the command buffer and executes it.
*
* @returns VBox status code.
* @param pBusLogic Pointer to the BusLogic device instance.
*/
{
int rc = VINF_SUCCESS;
bool fSuppressIrq = false;
switch (pBusLogic->uOperationCode)
{
/* Valid command, no reply. */
break;
{
PReplyInquirePCIHostAdapterInformation pReply = (PReplyInquirePCIHostAdapterInformation)pBusLogic->aReplyBuffer;
/* It seems VMware does not provide valid information here too, lets do the same :) */
pReply->InformationIsValid = 0;
break;
}
{
/* Modify the ISA-compatible I/O port base. Note that this technically
* violates the PCI spec, as this address is not reported through PCI.
* However, it is required for compatibility with old drivers.
*/
#ifdef IN_RING3
fSuppressIrq = true;
break;
#else
AssertMsgFailed(("Must never get here!\n"));
#endif
}
{
/* The special option byte is important: If it is '0' or 'B', Windows NT drivers
* for Adaptec AHA-154x may claim the adapter. The BusLogic drivers will claim
* the adapter only when the byte is *not* '0' or 'B'.
*/
/* We report version 5.07B. This reply will provide the first two digits. */
break;
}
{
break;
}
{
break;
}
/* The parameter list length is determined by the first byte of the command buffer. */
{
/* First pass - set the number of following parameter bytes. */
}
else
{
/* Second pass - process received data. */
/* We ignore the data - it only concerns the SCSI hardware protocol. */
}
break;
{
/* The reply length is set by the guest and is found in the first byte of the command buffer. */
const char aModelName[] = "958";
: sizeof(aModelName) - 1;
for (int i = 0; i < cCharsToTransfer; i++)
break;
}
{
/* The PCI IRQ is not necessarily representable in this structure.
* If that is the case, the guest likely won't function correctly,
* therefore we log a warning.
*/
switch (uPciIrq)
{
default:
break;
}
break;
}
{
/* Some Adaptec AHA-154x drivers (e.g. OS/2) execute this command and expect
* it to fail. If it succeeds, the drivers refuse to load. However, some newer
* Adaptec 154x models supposedly support it too??
*/
/* The reply length is set by the guest and is found in the first byte of the command buffer. */
PReplyInquireExtendedSetupInformation pReply = (PReplyInquireExtendedSetupInformation)pBusLogic->aReplyBuffer;
/** @todo should this reflect the RAM contents (AutoSCSIRam)? */
pReply->fLevelSensitiveInterrupt = true;
pReply->fHostWideSCSI = true;
pReply->fHostUltraSCSI = true;
break;
}
{
/* The reply length is set by the guest and is found in the first byte of the command buffer. */
pReply->fSynchronousInitiationEnabled = true;
pReply->fParityCheckingEnabled = true;
/* The 'D' signature prevents Adaptec's OS/2 drivers from getting too
* friendly with BusLogic hardware and upsetting the HBA state.
*/
break;
}
{
/*
* First element in the command buffer contains start offset to read from
* and second one the number of bytes to read.
*/
pBusLogic->fUseLocalRam = true;
break;
}
{
pBusLogic->fMbxIs24Bit = true;
/* The area for incoming mailboxes is right after the last entry of outgoing mailboxes. */
pBusLogic->GCPhysAddrMailboxIncomingBase = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->cMailbox * sizeof(Mailbox24));
LogRel(("Initialized 24-bit mailbox, %d entries at %08x\n", pRequest->cMailbox, ADDR_TO_U32(pRequest->aMailboxBaseAddr)));
break;
}
{
PRequestInitializeExtendedMailbox pRequest = (PRequestInitializeExtendedMailbox)pBusLogic->aCommandBuffer;
pBusLogic->fMbxIs24Bit = false;
/* The area for incoming mailboxes is right after the last entry of outgoing mailboxes. */
pBusLogic->GCPhysAddrMailboxIncomingBase = (RTGCPHYS)pRequest->uMailboxBaseAddress + (pBusLogic->cMailbox * sizeof(Mailbox32));
LogRel(("Initialized 32-bit mailbox, %d entries at %08x\n", pRequest->cMailbox, pRequest->uMailboxBaseAddress));
break;
}
{
if (pBusLogic->aCommandBuffer[0] == 0)
pBusLogic->fStrictRoundRobinMode = false;
pBusLogic->fStrictRoundRobinMode = true;
else
break;
}
{
if (pBusLogic->aCommandBuffer[0] == 0)
pBusLogic->fExtendedLunCCBFormat = false;
pBusLogic->fExtendedLunCCBFormat = true;
else
break;
}
* We cheat and skip that, since we already know what's attached
*/
for (int i = 0; i < 8; ++i)
{
}
break;
/* See note about cheating above. */
for (int i = 0; i < 8; ++i)
{
}
break;
{
/* Each bit which is set in the 16bit wide variable means a present device. */
{
u16TargetsPresentMask |= (1 << i);
}
break;
}
{
pBusLogic->aReplyBuffer[i] = 0; /** @todo Figure if we need something other here. It's not needed for the linux driver */
break;
}
{
if (pBusLogic->aCommandBuffer[0] == 0)
pBusLogic->fIRQEnabled = false;
else
pBusLogic->fIRQEnabled = true;
fSuppressIrq = true;
break;
}
{
break;
}
{
break;
}
{
break;
}
{
break;
}
default:
/* Commands valid for Adaptec 154xC which we don't handle since
* we pretend being 154xB compatible. Just mark the command as invalid.
*/
break;
case BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND: /* Should be handled already. */
AssertMsgFailed(("Invalid mailbox execute state!\n"));
}
Log(("uOperationCode=%#x, cbReplyParametersLeft=%d\n", pBusLogic->uOperationCode, pBusLogic->cbReplyParametersLeft));
/* Set the data in ready bit in the status register in case the command has a reply. */
else if (!pBusLogic->cbCommandParametersLeft)
return rc;
}
/**
* Read a register from the BusLogic adapter.
*
* @returns VBox status code.
* @param pBusLogic Pointer to the BusLogic instance data.
* @param iRegister The index of the register to read.
* @param pu32 Where to store the register content.
*/
{
int rc = VINF_SUCCESS;
switch (iRegister)
{
case BUSLOGIC_REGISTER_STATUS:
{
/* If the diagnostic active bit is set, we are in a guest-initiated
* hard reset. If the guest reads the status register and waits for
* the host adapter ready bit to be set, we terminate the reset right
* away. However, guests may also expect the reset condition to clear
* automatically after a period of time, in which case we can't show
* the DIAG bit at all.
*/
{
{
/* If reset already expired, let the guest see that right away. */
pBusLogic->u64ResetTime = 0;
}
}
break;
}
case BUSLOGIC_REGISTER_DATAIN:
{
if (pBusLogic->fUseLocalRam)
else
/* Careful about underflow - guest can read data register even if
* no data is available.
*/
{
if (!pBusLogic->cbReplyParametersLeft)
{
/*
* Reply finished, set command complete bit, unset data-in ready bit and
* interrupt the guest if enabled.
*/
buslogicCommandComplete(pBusLogic, false);
}
}
break;
}
{
break;
}
{
break;
}
default:
}
Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
return rc;
}
/**
* Write a value to a register.
*
* @returns VBox status code.
* @param pBusLogic Pointer to the BusLogic instance data.
* @param iRegister The index of the register to read.
* @param uVal The value to write.
*/
{
int rc = VINF_SUCCESS;
switch (iRegister)
{
{
{
#ifdef IN_RING3
#else
#endif
break;
}
if (rc != VINF_SUCCESS)
return rc;
#ifdef LOG_ENABLED
#endif
break;
}
{
/* Fast path for mailbox execution command. */
{
/* If there are no mailboxes configured, don't even try to do anything. */
{
/* Send new notification to the queue. */
}
}
return rc;
}
/*
* Check if we are already fetch command parameters from the guest.
* If not we initialize executing a new command.
*/
{
pBusLogic->iParameter = 0;
/* Mark host adapter as busy and clear the invalid status bit. */
pBusLogic->regStatus &= ~(BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY | BUSLOGIC_REGISTER_STATUS_COMMAND_INVALID);
/* Get the number of bytes for parameters from the command code. */
switch (pBusLogic->uOperationCode)
{
break;
break;
break;
break;
break;
/* There must be at least one byte following this command. */
break;
/* Invalid commands. */
break;
case BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND: /* Should not come here anymore. */
default:
}
}
else
{
#ifndef IN_RING3
/* This command must be executed in R3 as it rehooks the ISA I/O port. */
{
break;
}
#endif
/*
* The real adapter would set the Command register busy bit in the status register.
* The guest has to wait until it is unset.
* We don't need to do it because the guest does not continue execution while we are in this
* function.
*/
pBusLogic->iParameter++;
}
/* Start execution of command if there are no parameters left. */
if (!pBusLogic->cbCommandParametersLeft)
{
}
break;
}
/* On BusLogic adapters, the interrupt and geometry registers are R/W.
* That is different from Adaptec 154x where those are read only.
*/
break;
break;
default:
AssertMsgFailed(("Register not available\n"));
}
return rc;
}
/**
* Memory mapped I/O Handler for read operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param GCPhysAddr Physical address (in GC) where the read starts.
* @param pv Where to store the result.
* @param cb Number of bytes read.
*/
PDMBOTHCBDECL(int) buslogicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
{
/* the linux driver does not make use of the MMIO area. */
AssertMsgFailed(("MMIO Read\n"));
return VINF_SUCCESS;
}
/**
* Memory mapped I/O Handler for write operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param GCPhysAddr Physical address (in GC) where the read starts.
* @param pv Where to fetch the result.
* @param cb Number of bytes to write.
*/
PDMBOTHCBDECL(int) buslogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
{
/* the linux driver does not make use of the MMIO area. */
AssertMsgFailed(("MMIO Write\n"));
return VINF_SUCCESS;
}
/**
* Port I/O Handler for IN operations.
*
* @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.
*/
PDMBOTHCBDECL(int) buslogicIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
}
/**
* Port I/O Handler for OUT operations.
*
* @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.
*/
PDMBOTHCBDECL(int) buslogicIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
int rc = VINF_SUCCESS;
Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x rc=%Rrc\n",
return rc;
}
#ifdef IN_RING3
{
int rc;
pTaskState->fBIOS = true;
{
/* 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;
}
else
{
rc = pTaskState->CTX_SUFF(pTargetDevice)->pDrvSCSIConnector->pfnSCSIRequestSend(pTaskState->CTX_SUFF(pTargetDevice)->pDrvSCSIConnector,
}
return rc;
}
/**
* Port I/O Handler for IN operations - BIOS 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.
*/
static DECLCALLBACK(int) buslogicR3BiosIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
int rc;
//Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
// __FUNCTION__, pu32, 1, pu32, (Port - BUSLOGIC_BIOS_IO_PORT), rc));
return rc;
}
/**
* Port I/O Handler for OUT operations - BIOS 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.
*/
static DECLCALLBACK(int) buslogicR3BiosIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
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) buslogicR3BiosIoPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc,
{
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) buslogicR3BiosIoPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst,
{
LogFlowFunc(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
}
/**
* Update the ISA I/O range.
*
* @returns nothing.
* @param pBusLogic Pointer to the BusLogic device instance.
* @param uBaseCode Encoded ISA I/O base; only low 3 bits are used.
*/
{
int rc = VINF_SUCCESS;
/* Check if the same port range is already registered. */
{
/* Unregister the old range, if any. */
if (RT_SUCCESS(rc))
{
if (uNewBase)
{
/* Register the new range if requested. */
"BusLogic ISA");
if (RT_SUCCESS(rc))
{
}
}
}
if (RT_SUCCESS(rc))
{
if (uNewBase)
{
}
else
{
Log(("Disabling ISA I/O ports.\n"));
LogRel(("BusLogic: ISA I/O disabled\n"));
}
}
}
return rc;
}
{
int rc;
rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_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, "DevBusLogic_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, "DevBusLogic_ISCSIDOWN",
N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again"));
}
{
int rc2;
LogRel(("BusLogic#%d: Unknown but recoverable error has occurred (rc=%Rrc)\n", pDevIns->iInstance, rc));
rc2 = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_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
}
{
int rc = VINF_SUCCESS;
if (enmType == PCI_ADDRESS_SPACE_MEM)
{
/* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
if (RT_FAILURE(rc))
return rc;
if (pThis->fR0Enabled)
{
"buslogicMMIOWrite", "buslogicMMIORead");
if (RT_FAILURE(rc))
return rc;
}
if (pThis->fGCEnabled)
{
"buslogicMMIOWrite", "buslogicMMIORead");
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;
}
static DECLCALLBACK(int) buslogicR3DeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest,
{
int rc;
if (fRedo)
{
if (!pTaskState->fBIOS)
{
if (pTaskState->pbSenseBuffer)
}
/* Add to the list. */
do
{
/* Suspend the VM if not done already. */
}
else
{
if (pTaskState->fBIOS)
{
}
else
{
if (pTaskState->pbSenseBuffer)
if (rcCompletion == SCSI_STATUS_OK)
else if (rcCompletion == SCSI_STATUS_CHECK_CONDITION)
else
}
#ifdef LOG_ENABLED
#endif
/* Remove task from the cache. */
}
return VINF_SUCCESS;
}
static DECLCALLBACK(int) buslogicR3QueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController,
{
return VINF_SUCCESS;
}
{
int rc = VINF_SUCCESS;
/* Fetch the CCB from guest memory. */
/** @todo How much do we really have to read? */
uTargetIdCCB = pTaskState->fIs24Bit ? pTaskState->CommandControlBlockGuest.o.uTargetId : pTaskState->CommandControlBlockGuest.n.uTargetId;
#ifdef LOG_ENABLED
#endif
/* Alloc required buffers. */
/* Check if device is present on bus. If not return error immediately and don't process this further. */
{
if (pTaskState->pbSenseBuffer)
buslogicR3SenseBufferFree(pTaskState, true);
}
else
{
/* Setup SCSI request. */
pTaskState->PDMScsiRequest.uLogicalUnit = pTaskState->fIs24Bit ? pTaskState->CommandControlBlockGuest.o.uLogicalUnit
else
AssertMsgFailed(("Invalid data direction type %d\n", pTaskState->CommandControlBlockGuest.c.uDataDirection));
{
}
else
{
}
pTaskState->PDMScsiRequest.cbSenseBuffer = buslogicR3ConvertSenseBufferLength(pTaskState->CommandControlBlockGuest.c.cbSenseData);
rc = pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTargetDevice->pDrvSCSIConnector, &pTaskState->PDMScsiRequest);
}
return rc;
}
/**
* Read a mailbox from guest memory. Convert 24-bit mailboxes to
* 32-bit format.
*
* @returns Mailbox guest physical address.
* @param pBusLogic Pointer to the BusLogic instance data.
* @param pTaskStat Pointer to the task state being set up.
*/
{
if (pBusLogic->fMbxIs24Bit)
{
GCMailbox = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox24));
}
else
{
GCMailbox = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox32));
PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCMailbox, &pTaskState->MailboxGuest, sizeof(Mailbox32));
}
return GCMailbox;
}
/**
* Read mailbox from the guest and execute command.
*
* @returns VBox status code.
* @param pBusLogic Pointer to the BusLogic instance data.
*/
{
int rc;
AssertMsgReturn(RT_SUCCESS(rc) && (pTaskState != NULL), ("Failed to get task state from cache\n"), rc);
pTaskState->fBIOS = false;
if (!pBusLogic->fStrictRoundRobinMode)
{
/* Search for a filled mailbox - stop if we have scanned all mailboxes. */
do
{
/* Fetch mailbox from guest memory. */
/* Check the next mailbox. */
}
else
{
/* Fetch mailbox from guest memory. */
}
/*
* Check if the mailbox is actually loaded.
* It might be possible that the guest notified us without
* a loaded mailbox. Do nothing in that case but leave a
* log entry.
*/
{
Log(("No loaded mailbox left\n"));
return VERR_NO_DATA;
}
LogFlow(("Got loaded mailbox at slot %u, CCB phys %RGp\n", pBusLogic->uMailboxOutgoingPositionCurrent, (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB));
#ifdef LOG_ENABLED
#endif
/* We got the mailbox, mark it as free in the guest. */
unsigned uCodeOffs = pTaskState->fIs24Bit ? RT_OFFSETOF(Mailbox24, uCmdState) : RT_OFFSETOF(Mailbox32, u.out.uActionCode);
PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent + uCodeOffs, &uActionCode, sizeof(uActionCode));
else if (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_ABORT_COMMAND)
{
AssertMsgFailed(("Not implemented yet\n"));
}
else
AssertMsgFailed(("Invalid outgoing mailbox action code %u\n", pTaskState->MailboxGuest.u.out.uActionCode));
/* Advance to the next mailbox. */
return rc;
}
/**
* 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.
*/
static DECLCALLBACK(bool) buslogicR3NotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
{
/* Reset notification send flag now. */
ASMAtomicXchgU32(&pBusLogic->cMailboxesReady, 0); /** @todo Actually not required anymore but to stay compatible with older saved states. */
/* Process mailboxes. */
int rc;
do
{
} while (RT_SUCCESS(rc));
return true;
}
/**
* Kicks the controller to process pending tasks after the VM was resumed
* or loaded from a saved state.
*
* @returns nothing.
* @param pThis The BusLogic device instance.
*/
{
{
{
/* The BIOS had a request active when we got suspended. Resume it. */
}
else
{
/* Queue all pending tasks again. */
while (pTaskState)
{
}
}
}
}
/** @callback_method_impl{FNSSMDEVLIVEEXEC} */
{
/* Save the device config. */
return VINF_SSM_DONT_CALL_AGAIN;
}
/** @callback_method_impl{FNSSMDEVSAVEEXEC} */
{
/* Every device first. */
{
("There are still outstanding requests on this device\n"));
}
/* Now the main device state. */
/* Now the data for the BIOS interface. */
/*
* Save the physical addresses of the command control blocks of still pending tasks.
* They are processed again on resume.
*
* The number of pending tasks needs to be determined first.
*/
{
while (pTaskState)
{
cTasks++;
}
}
/* Write the address of every task now. */
while (pTaskState)
{
}
return SSMR3PutU32(pSSM, ~0);
}
/** @callback_method_impl{FNSSMDEVLOADDONE} */
{
return VINF_SUCCESS;
}
/** @callback_method_impl{FNSSMDEVLOADEXEC} */
static DECLCALLBACK(int) buslogicR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
int rc = VINF_SUCCESS;
/* We support saved states only from this and older versions. */
/* Every device first. */
{
("There are still outstanding requests on this device\n"));
bool fPresent;
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target %u config mismatch: config=%RTbool state=%RTbool"), i, pDevice->fPresent, fPresent);
if (uPass == SSM_PASS_FINAL)
}
if (uPass != SSM_PASS_FINAL)
return VINF_SUCCESS;
/* Now the main device state. */
/* Now the data for the BIOS interface. */
{
{
LogRel(("BusLogic: Out of memory during restore.\n"));
N_("BusLogic: Out of memory during restore\n"));
}
}
{
/* Check if there are pending tasks saved. */
if (cTasks)
{
if (!pTaskState)
{
rc = VERR_NO_MEMORY;
break;
}
if (RT_FAILURE(rc))
{
break;
}
/* Link into the list. */
}
}
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
}
return rc;
}
/**
* 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) buslogicR3DeviceQueryStatusLed(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 *) buslogicR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
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) buslogicR3StatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
{
if (iLUN < BUSLOGIC_MAX_DEVICES)
{
return VINF_SUCCESS;
}
return VERR_PDM_LUN_NOT_FOUND;
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
static DECLCALLBACK(void *) buslogicR3StatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
return NULL;
}
/**
* BusLogic debugger info callback.
*
* @param pDevIns The device instance.
* @param pHlp The output helpers.
* @param pszArgs The arguments.
*/
static DECLCALLBACK(void) buslogicR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
unsigned i;
bool fVerbose = false;
/* Parse arguments. */
if (pszArgs)
/* Show basic information. */
"%s#%d: PCI I/O=%RTiop ISA I/O=%RTiop MMIO=%RGp IRQ=%u GC=%RTbool R0=%RTbool\n",
/* Print mailbox state. */
else
/* Print register contents. */
/* Print the current command, if any. */
{
/* Dump the mailbox contents. */
if (pThis->fMbxIs24Bit)
{
/* Outgoing mailbox, 24-bit format. */
{
pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %06X action code %02X", i, ADDR_TO_U32(Mbx24.aPhysAddrCCB), Mbx24.uCmdState);
}
/* Incoming mailbox, 24-bit format. */
{
pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %06X completion code %02X", i, ADDR_TO_U32(Mbx24.aPhysAddrCCB), Mbx24.uCmdState);
}
}
else
{
/* Outgoing mailbox, 32-bit format. */
{
pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %08X action code %02X", i, Mbx32.u32PhysAddrCCB, Mbx32.u.out.uActionCode);
}
/* Incoming mailbox, 32-bit format. */
{
Mbx32.u32PhysAddrCCB, Mbx32.u.in.uCompletionCode, Mbx32.u.in.uHostAdapterStatus, Mbx32.u.in.uTargetDeviceStatus);
}
}
}
}
/* -=-=-=-=- Helper -=-=-=-=- */
/**
* Checks if all asynchronous I/O is finished.
*
* Used by buslogicR3Reset, buslogicR3Suspend and buslogicR3PowerOff.
*
* @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 buslogicR3Suspend and buslogicR3PowerOff..
*
* @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
{
{
if (fPowerOff)
{
/* Free tasks which would have been queued again on resume. */
while (pTaskState)
{
pFree = pTaskState;
}
}
{
/* Destroy the task because the BIOS interface has all necessary information. */
}
}
}
}
/**
* Suspend notification.
*
* @param pDevIns The device instance data.
*/
{
Log(("buslogicR3Suspend\n"));
}
/**
* Resume notification.
*
* @param pDevIns The device instance data.
*/
{
Log(("buslogicR3Resume\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.
*/
{
("BusLogic: 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;
("BusLogic: 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;
}
/**
* Callback employed by buslogicR3Reset.
*
* @returns true if we've quiesced, false if we're still working.
* @param pDevIns The device instance.
*/
{
return false;
buslogicR3HwReset(pThis, true);
return true;
}
/**
* @copydoc FNPDMDEVRESET
*/
{
else
{
buslogicR3HwReset(pThis, true);
}
}
{
for (uint32_t i = 0; i < BUSLOGIC_MAX_DEVICES; i++)
{
}
}
/**
* Poweroff notification.
*
* @param pDevIns Pointer to the device instance
*/
{
Log(("buslogicR3PowerOff\n"));
}
/**
* Destroy a driver instance.
*
* Most VM resources are freed by the VM. This callback is provided so that any non-VM
* resources can be freed correctly.
*
* @param pDevIns The device instance data.
*/
{
/*
* Free all tasks which are still hanging around
* (Power off after the VM was suspended).
*/
{
/* Free tasks which would have been queued again on resume. */
while (pTaskState)
{
pFree = pTaskState;
}
}
return rc;
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
{
int rc = VINF_SUCCESS;
bool fBootable = true;
char achISACompat[16];
/*
* Init instance data (do early because of constructor).
*/
/*
* Validate and read configuration.
*/
if (!CFGMR3AreValuesValid(pCfg,
"GCEnabled\0"
"R0Enabled\0"
"Bootable\0"
"ISACompat\0"))
N_("BusLogic configuration error: unknown option specified"));
if (RT_FAILURE(rc))
N_("BusLogic configuration error: failed to read GCEnabled as boolean"));
if (RT_FAILURE(rc))
N_("BusLogic configuration error: failed to read R0Enabled as boolean"));
if (RT_FAILURE(rc))
N_("BusLogic configuration error: failed to read Bootable as boolean"));
if (RT_FAILURE(rc))
N_("BusLogic configuration error: failed to read ISACompat as string"));
/* Grok the ISACompat setting. */
else
N_("BusLogic configuration error: invalid ISACompat setting"));
/*
* Register the PCI device and its I/O regions.
*/
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
if (fBootable)
{
/* Register I/O port space for BIOS access. */
"BusLogic BIOS");
if (RT_FAILURE(rc))
}
/* Set up the compatibility I/O range. */
if (RT_FAILURE(rc))
/* Initialize task cache. */
if (RT_FAILURE(rc))
N_("BusLogic: Failed to initialize task cache\n"));
/* Initialize task queue. */
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIntr, RT_SRC_POS, "BusLogic-Intr#%u", pDevIns->iInstance);
if (RT_FAILURE(rc))
/* Initialize per device state. */
{
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)
{
}
if (RT_FAILURE(rc))
/*
* Register the debugger info callback.
*/
char szTmp[128];
return rc;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceBusLogic =
{
/* u32Version */
/* szName */
"buslogic",
/* szRCMod */
"VBoxDDGC.gc",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
"BusLogic BT-958 SCSI host adapter.\n",
/* fFlags */
/* fClass */
/* cMaxInstances */
~0U,
/* cbInstance */
sizeof(BUSLOGIC),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
/* pfnResume */
/* pfnAttach */
/* pfnDetach */
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */