DevBusLogic.cpp revision 547d50767a3ea8d3aa1d18aa3596c86367fdf5c4
/* $Id$ */
/** @file
* VBox storage devices: BusLogic SCSI host adapter BT-958.
*/
/*
* Copyright (C) 2006-2009 Sun Microsystems, Inc.
*
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
//#define DEBUG
#define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC
#ifdef IN_RING3
# include <iprt/memcache.h>
#endif
#include "VBoxSCSI.h"
#include "../Builtins.h"
/* 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
/* I/O port registered in the ISA compatible range to let the BIOS access
* the controller.
*/
#define BUSLOGIC_ISA_IO_PORT 0x330
/** State saved version. */
#define BUSLOGIC_SAVED_STATE_MINOR_VERSION 1
/**
* 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 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. */
#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_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()
#pragma pack(1)
/**
* The local Ram.
*/
typedef union HostAdapterLocalRam
{
/* Byte view. */
/* Structured view. */
struct
{
/** Offset 0 - 63 is for BIOS. */
/** Auto SCSI structure. */
} structured;
#pragma pack()
/**
* 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 GC 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;
/** 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;
/** Number of mailboxes the guest set up. */
#if HC_ARCH_BITS == 64
#endif
/** 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 */
#if HC_ARCH_BITS == 64
#endif
/** 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. */
/** 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. */
#pragma pack(1)
typedef struct ReplyInquirePCIHostAdapterInformation
{
unsigned char LowByteTerminated:1;
unsigned char HighByteTerminated:1;
/** Whether the provided info is valid. */
unsigned char InformationIsValid: 1;
#pragma pack()
/* Structure for the INQUIRE_CONFIGURATION reply. */
#pragma pack(1)
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;
#pragma pack()
/* Structure for the INQUIRE_SETUP_INFORMATION reply. */
#pragma pack(1)
typedef struct ReplyInquireSetupInformationSynchronousValue
{
unsigned char uOffset: 4;
unsigned char uTransferPeriod: 3;
bool fSynchronous: 1;
#pragma pack()
#pragma pack(1)
typedef struct ReplyInquireSetupInformation
{
bool fSynchronousInitiationEnabled: 1;
bool fParityCheckingEnabled: 1;
unsigned char uReserved1: 6;
#pragma pack()
/* 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 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.
*/
#pragma pack(1)
typedef struct Mailbox
{
/** Physical adress 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;
#pragma pack()
/*
* 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.
*/
#pragma pack(1)
typedef struct CommandControlBlock
{
/** 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 send 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. */
/** Reserved. */
/** Sense data pointer. */
#pragma pack()
#pragma pack(1)
typedef struct ScatterGatherEntry
{
#pragma pack()
/*
* Task state for a CCB request.
*/
typedef struct BUSLOGICTASKSTATE
{
/** 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;
#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)) )
/**
* Deasserts the interrupt line of the BusLogic adapter.
*
* @returns nothing
* @param pBuslogic Pointer to the BusLogic device instance.
*/
{
pBusLogic->regInterrupt = 0;
}
/**
* Assert IRQ line of the BusLogic adapter.
*
* @returns nothing.
* @param pBusLogic Pointer to the BusLogic device instance.
*/
{
}
#if defined(IN_RING3)
/**
* 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.
*/
{
/* Reset registers to default value. */
pBusLogic->regInterrupt = 0;
pBusLogic->iParameter = 0;
pBusLogic->fIRQEnabled = true;
pBusLogic->fISAEnabled = true;
return VINF_SUCCESS;
}
#endif
/**
* Resets the command state machine for the next command and notifies the guest.
*
* @returns nothing.
* @param pBusLogic Pointer to the BusLogic device instance
*/
{
pBusLogic->fUseLocalRam = false;
/* Modify I/O address does not generate an interrupt. */
{
/* Notify that the command is complete. */
if (pBusLogic->fIRQEnabled)
}
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.
*/
{
/* We set the diagnostic active in the status register. */
}
/**
* Send a mailbox with set status codes to the guest.
*
* @returns nothing.
* @param pBusLogicR Pointer to the BubsLogic 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.
*/
{
RTGCPHYS GCPhysAddrMailboxIncoming = pBusLogic->GCPhysAddrMailboxIncomingBase + (pBusLogic->uMailboxIncomingPositionCurrent * sizeof(Mailbox));
/* Update CCB. */
PDMDevHlpPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB, &pTaskState->CommandControlBlockGuest, sizeof(CommandControlBlock));
/* Update mailbox. */
PDMDevHlpPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming, &pTaskState->MailboxGuest, sizeof(Mailbox));
/* Advance to next mailbox position. */
if (pBusLogic->fIRQEnabled)
}
#if defined(DEBUG)
/**
* Dumps the content of a mailbox for debugging purposes.
*
* @return nothing
* @param pMailbox The mialbox 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.
*/
{
}
#endif
/**
* Allocate data buffer.
*
* @returns VBox status code.
* @param pTaskState Pointer to the task state.
*/
{
{
/*
* @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.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER)
|| (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
{
ScatterGatherEntry aScatterGatherReadGC[32]; /* Number of scatter gather list entries read from guest memory. */
uint32_t cScatterGatherGCLeft = pTaskState->CommandControlBlockGuest.cbData / sizeof(ScatterGatherEntry);
RTGCPHYS GCPhysAddrScatterGatherCurrent = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData;
size_t cbDataToTransfer = 0;
/* Count number of bytes to transfer. */
do
{
/* Read the SG entries. */
cScatterGatherGCRead * sizeof(ScatterGatherEntry));
{
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
{
/* Read the SG entries. */
cScatterGatherGCRead * sizeof(ScatterGatherEntry));
{
Log(("%s: GCPhysAddrDataBase=%RGp cbDataToTransfer=%u\n", __FUNCTION__, GCPhysAddrDataBase, cbDataToTransfer));
}
/* Set address to the next entries to read. */
} while (cScatterGatherGCLeft > 0);
}
}
|| pTaskState->CommandControlBlockGuest.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.
*/
{
{
if ( (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER)
|| (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
{
ScatterGatherEntry aScatterGatherReadGC[32]; /* Number of scatter gather list entries read from guest memory. */
uint32_t cScatterGatherGCLeft = pTaskState->CommandControlBlockGuest.cbData / sizeof(ScatterGatherEntry);
RTGCPHYS GCPhysAddrScatterGatherCurrent = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData;
do
{
/* Read the SG entries. */
cScatterGatherGCRead * sizeof(ScatterGatherEntry));
{
Log(("%s: GCPhysAddrDataBase=%RGp cbDataToTransfer=%u\n", __FUNCTION__, GCPhysAddrDataBase, cbDataToTransfer));
}
/* Set address to the next entries to read. */
} while (cScatterGatherGCLeft > 0);
}
|| pTaskState->CommandControlBlockGuest.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. */
PDMDevHlpPhysWrite(pDevIns, GCPhysAddrDataBase, pTaskState->DataSeg.pvSeg, pTaskState->DataSeg.cbSeg);
}
}
}
/**
* Free the sense buffer.
*
* @returns nothing.
* @param pTaskState Pointer to the task state.
*/
{
RTGCPHYS GCPhysAddrSenseBuffer = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrSenseData;
/* Copy into guest memory. */
}
/**
* 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.
*/
{
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;
switch (pBusLogic->uOperationCode)
{
{
PReplyInquirePCIHostAdapterInformation pReply = (PReplyInquirePCIHostAdapterInformation)pBusLogic->aReplyBuffer;
/* It seems VMware does not provide valid information here too, lets do the same :) */
pReply->InformationIsValid = 0;
break;
}
{
{
Log(("Disabling ISA I/O ports.\n"));
pBusLogic->fISAEnabled = false;
}
break;
}
{
/* We report version 5.07B. This reply will provide the first two digits. */
break;
}
{
break;
}
{
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);
for (int i = 0; i < cCharsToTransfer; i++)
break;
}
{
/*
* The rest of this reply only applies for ISA adapters.
* This is a PCI adapter so they are not important and are skipped.
*/
break;
}
{
/* The reply length is set by the guest and is found in the first byte of the command buffer. */
PReplyInquireExtendedSetupInformation pReply = (PReplyInquireExtendedSetupInformation)pBusLogic->aReplyBuffer;
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. */
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;
}
{
PRequestInitializeExtendedMailbox pRequest = (PRequestInitializeExtendedMailbox)pBusLogic->aCommandBuffer;
/* The area for incoming mailboxes is right after the last entry of outgoing mailboxes. */
pBusLogic->GCPhysAddrMailboxIncomingBase = (RTGCPHYS)pRequest->uMailboxBaseAddress + (pBusLogic->cMailbox * sizeof(Mailbox));
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;
}
{
/* 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;
break;
}
case BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND: /* Should be handled already. */
default:
}
/* Set the data in ready bit in the status register in case the command has a reply. */
else
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 hard reset initiated from the guest.
* The guest reads the status register and waits that the host adapter ready bit is set.
*/
{
}
break;
}
case BUSLOGIC_REGISTER_DATAIN:
{
if (pBusLogic->fUseLocalRam)
else
if (!pBusLogic->cbReplyParametersLeft)
{
/*
* Reply finished, set command complete bit, unset data in ready bit and
* interrupt the guest if enabled.
*/
}
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;
}
{
/* Fast path for mailbox execution command. */
{
{
/* 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. */
/* Get the number of bytes for parameters from the command code. */
switch (pBusLogic->uOperationCode)
{
break;
break;
break;
break;
case BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND: /* Should not come here anymore. */
default:
}
}
else
{
/*
* 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;
}
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.
*/
{
/* 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.
*/
{
/* 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.
*/
{
}
/**
* 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.
*/
{
int rc = VINF_SUCCESS;
Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x rc=%Rrc\n",
return rc;
}
#ifdef IN_RING3
/**
* 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;
if (!pBusLogic->fISAEnabled)
return VERR_IOM_IOPORT_UNUSED;
//Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
// __FUNCTION__, pu32, 1, pu32, (Port - BUSLOGIC_ISA_IO_PORT), rc));
return rc;
}
{
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 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 (!pBusLogic->fISAEnabled)
return VERR_IOM_IOPORT_UNUSED;
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) buslogicIsaIOPortWriteStr(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) buslogicIsaIOPortReadStr(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;
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)
{
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;
}
static DECLCALLBACK(int) buslogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest, int rcCompletion)
{
int rc;
if (pTaskState->fBIOS)
{
}
else
{
if (pTaskState->pbSenseBuffer)
}
/* Add task to the cache. */
return VINF_SUCCESS;
}
/**
* 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. */
do
{
/* Fetch mailbox from guest memory. */
GCPhysAddrMailboxCurrent = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox));
/* Check if we reached the end and start from the beginning if so. */
}
else
{
/* Fetch mailbox from guest memory. */
GCPhysAddrMailboxCurrent = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox));
}
#ifdef DEBUG
#endif
{
/* Fetch CCB now. */
PBUSLOGICDEVICE pTargetDevice = &pBusLogic->aDeviceStates[pTaskState->CommandControlBlockGuest.uTargetId];
#ifdef DEBUG
#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)
}
else
{
/* Setup SCSI request. */
else
AssertMsgFailed(("Invalid data direction type %d\n", pTaskState->CommandControlBlockGuest.uDataDirection));
{
}
else
{
}
rc = pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTargetDevice->pDrvSCSIConnector, &pTaskState->PDMScsiRequest);
}
}
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));
/* We got the mailbox, mark it as free in the guest. */
PDMDevHlpPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent, &pTaskState->MailboxGuest, sizeof(Mailbox));
{
/* Check if we reached the end and start from the beginning if so. */
}
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.
*/
{
/* Reset notification send flag now. */
/* Process mailboxes. */
do
{
int rc;
return true;
}
{
/* Save the device config. */
return VINF_SSM_DONT_CALL_AGAIN;
}
{
/* Every device first. */
{
("There are still outstanding requests on this device\n"));
}
/* Now the main device state. */
/* Now the data for the BIOS interface. */
return SSMR3PutU32(pSSM, ~0);
}
static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
int rc;
/* 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"));
}
}
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) buslogicDeviceQueryStatusLed(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) buslogicStatusQueryStatusLed(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}
*/
{
return NULL;
}
/**
* 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;
}
{
uint32_t i;
for (i = 0; i < BUSLOGIC_MAX_DEVICES; i++)
{
}
}
/**
* Reset notification.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
{
}
/**
* 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.
*/
{
return rc;
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
{
int rc = VINF_SUCCESS;
/*
* Validate and read configuration.
*/
if (!CFGMR3AreValuesValid(pCfg,
"GCEnabled\0"
"R0Enabled\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"));
/*
* Register the PCI device, it's I/O regions.
*/
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
/* Register I/O port space in ISA region for BIOS access. */
"BusLogic BIOS");
if (RT_FAILURE(rc))
/* Initialize task cache. */
if (RT_FAILURE(rc))
N_("BusLogic: Failed to initialize task cache\n"));
/* Intialize task queue. */
if (RT_FAILURE(rc))
return 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))
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 */
~0,
/* cbInstance */
sizeof(BUSLOGIC),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
/* pfnDetach */
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */