audk/OvmfPkg/MptScsiDxe/MptScsi.c
Nikita Leshenko c635a56384 OvmfPkg/MptScsiDxe: Reset device on ExitBootServices()
This causes the device to forget about the reply frame. We allocated the
reply frame in EfiBootServicesData type memory, and code executing after
ExitBootServices() is permitted to overwrite it.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390
Signed-off-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200504210607.144434-13-nikita.leshchenko@oracle.com>
2020-05-05 20:43:02 +00:00

1212 lines
32 KiB
C

/** @file
This driver produces Extended SCSI Pass Thru Protocol instances for
LSI Fusion MPT SCSI devices.
Copyright (C) 2020, Oracle and/or its affiliates.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <IndustryStandard/FusionMptScsi.h>
#include <IndustryStandard/Pci.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PcdLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Protocol/PciIo.h>
#include <Protocol/PciRootBridgeIo.h>
#include <Protocol/ScsiPassThruExt.h>
#include <Uefi/UefiSpec.h>
//
// Higher versions will be used before lower, 0x10-0xffffffef is the version
// range for IVH (Indie Hardware Vendors)
//
#define MPT_SCSI_BINDING_VERSION 0x10
//
// Runtime Structures
//
typedef struct {
MPT_SCSI_REQUEST_ALIGNED IoRequest;
MPT_SCSI_IO_REPLY_ALIGNED IoReply;
//
// As EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET.SenseDataLength is defined
// as UINT8, defining here SenseData size to MAX_UINT8 will guarantee it
// cannot overflow when passed to device.
//
UINT8 Sense[MAX_UINT8];
//
// This size of the data is arbitrarily chosen.
// It seems to be sufficient for all I/O requests sent through
// EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() for common boot scenarios.
//
UINT8 Data[0x2000];
} MPT_SCSI_DMA_BUFFER;
#define MPT_SCSI_DEV_SIGNATURE SIGNATURE_32 ('M','P','T','S')
typedef struct {
UINT32 Signature;
EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode;
UINT8 MaxTarget;
UINT32 StallPerPollUsec;
EFI_PCI_IO_PROTOCOL *PciIo;
UINT64 OriginalPciAttributes;
EFI_EVENT ExitBoot;
MPT_SCSI_DMA_BUFFER *Dma;
EFI_PHYSICAL_ADDRESS DmaPhysical;
VOID *DmaMapping;
BOOLEAN IoReplyEnqueued;
} MPT_SCSI_DEV;
#define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \
CR (PassThruPtr, MPT_SCSI_DEV, PassThru, MPT_SCSI_DEV_SIGNATURE)
#define MPT_SCSI_DMA_ADDR(Dev, MemberName) \
(Dev->DmaPhysical + OFFSET_OF (MPT_SCSI_DMA_BUFFER, MemberName))
#define MPT_SCSI_DMA_ADDR_HIGH(Dev, MemberName) \
((UINT32)RShiftU64 (MPT_SCSI_DMA_ADDR (Dev, MemberName), 32))
#define MPT_SCSI_DMA_ADDR_LOW(Dev, MemberName) \
((UINT32)MPT_SCSI_DMA_ADDR (Dev, MemberName))
//
// Hardware functions
//
STATIC
EFI_STATUS
Out32 (
IN MPT_SCSI_DEV *Dev,
IN UINT32 Addr,
IN UINT32 Data
)
{
return Dev->PciIo->Io.Write (
Dev->PciIo,
EfiPciIoWidthUint32,
PCI_BAR_IDX0,
Addr,
1,
&Data
);
}
STATIC
EFI_STATUS
In32 (
IN MPT_SCSI_DEV *Dev,
IN UINT32 Addr,
OUT UINT32 *Data
)
{
return Dev->PciIo->Io.Read (
Dev->PciIo,
EfiPciIoWidthUint32,
PCI_BAR_IDX0,
Addr,
1,
Data
);
}
STATIC
EFI_STATUS
MptDoorbell (
IN MPT_SCSI_DEV *Dev,
IN UINT8 DoorbellFunc,
IN UINT8 DoorbellArg
)
{
return Out32 (
Dev,
MPT_REG_DOORBELL,
(((UINT32)DoorbellFunc) << 24) | (DoorbellArg << 16)
);
}
STATIC
EFI_STATUS
MptScsiReset (
IN MPT_SCSI_DEV *Dev
)
{
EFI_STATUS Status;
//
// Reset hardware
//
Status = MptDoorbell (Dev, MPT_DOORBELL_RESET, 0);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Mask interrupts
//
Status = Out32 (Dev, MPT_REG_IMASK, MPT_IMASK_DOORBELL | MPT_IMASK_REPLY);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Clear interrupt status
//
Status = Out32 (Dev, MPT_REG_ISTATUS, 0);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
MptScsiInit (
IN MPT_SCSI_DEV *Dev
)
{
EFI_STATUS Status;
union {
MPT_IO_CONTROLLER_INIT_REQUEST Data;
UINT32 Uint32;
} AlignedReq;
MPT_IO_CONTROLLER_INIT_REQUEST *Req;
MPT_IO_CONTROLLER_INIT_REPLY Reply;
UINT8 *ReplyBytes;
UINT32 ReplyWord;
Req = &AlignedReq.Data;
Status = MptScsiReset (Dev);
if (EFI_ERROR (Status)) {
return Status;
}
ZeroMem (Req, sizeof (*Req));
ZeroMem (&Reply, sizeof (Reply));
Req->WhoInit = MPT_IOC_WHOINIT_ROM_BIOS;
Req->Function = MPT_MESSAGE_HDR_FUNCTION_IOC_INIT;
STATIC_ASSERT (
FixedPcdGet8 (PcdMptScsiMaxTargetLimit) < 255,
"Req supports 255 targets only (max target is 254)"
);
Req->MaxDevices = Dev->MaxTarget + 1;
Req->MaxBuses = 1;
Req->ReplyFrameSize = sizeof Dev->Dma->IoReply.Data;
Req->HostMfaHighAddr = MPT_SCSI_DMA_ADDR_HIGH (Dev, IoRequest);
Req->SenseBufferHighAddr = MPT_SCSI_DMA_ADDR_HIGH (Dev, Sense);
//
// Send controller init through doorbell
//
STATIC_ASSERT (
sizeof (*Req) % sizeof (UINT32) == 0,
"Req must be multiple of UINT32"
);
STATIC_ASSERT (
sizeof (*Req) / sizeof (UINT32) <= MAX_UINT8,
"Req must fit in MAX_UINT8 Dwords"
);
Status = MptDoorbell (
Dev,
MPT_DOORBELL_HANDSHAKE,
(UINT8)(sizeof (*Req) / sizeof (UINT32))
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = Dev->PciIo->Io.Write (
Dev->PciIo,
EfiPciIoWidthFifoUint32,
PCI_BAR_IDX0,
MPT_REG_DOORBELL,
sizeof (*Req) / sizeof (UINT32),
Req
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Read reply through doorbell
// Each 32bit (Dword) read produces 16bit (Word) of data
//
// The reply is read back to complete the doorbell function but it
// isn't useful because it doesn't contain relevant data or status
// codes.
//
STATIC_ASSERT (
sizeof (Reply) % sizeof (UINT16) == 0,
"Reply must be multiple of UINT16"
);
ReplyBytes = (UINT8 *)&Reply;
while (ReplyBytes != (UINT8 *)(&Reply + 1)) {
Status = In32 (Dev, MPT_REG_DOORBELL, &ReplyWord);
if (EFI_ERROR (Status)) {
return Status;
}
CopyMem (ReplyBytes, &ReplyWord, sizeof (UINT16));
ReplyBytes += sizeof (UINT16);
}
//
// Clear interrupts generated by doorbell reply
//
Status = Out32 (Dev, MPT_REG_ISTATUS, 0);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
ReportHostAdapterError (
OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
)
{
DEBUG ((DEBUG_ERROR, "%a: fatal error in scsi request\n", __FUNCTION__));
Packet->InTransferLength = 0;
Packet->OutTransferLength = 0;
Packet->SenseDataLength = 0;
Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_TASK_ABORTED;
return EFI_DEVICE_ERROR;
}
STATIC
EFI_STATUS
ReportHostAdapterOverrunError (
OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
)
{
Packet->SenseDataLength = 0;
Packet->HostAdapterStatus =
EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
return EFI_BAD_BUFFER_SIZE;
}
STATIC
EFI_STATUS
MptScsiPopulateRequest (
IN MPT_SCSI_DEV *Dev,
IN UINT8 Target,
IN UINT64 Lun,
IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
)
{
MPT_SCSI_REQUEST_WITH_SG *Request;
Request = &Dev->Dma->IoRequest.Data;
if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL ||
(Packet->InTransferLength > 0 && Packet->OutTransferLength > 0) ||
Packet->CdbLength > sizeof (Request->Header.Cdb)) {
return EFI_UNSUPPORTED;
}
if (Target > Dev->MaxTarget || Lun > 0 ||
Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL ||
//
// Trying to receive, but destination pointer is NULL, or contradicting
// transfer direction
//
(Packet->InTransferLength > 0 &&
(Packet->InDataBuffer == NULL ||
Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE
)
) ||
//
// Trying to send, but source pointer is NULL, or contradicting transfer
// direction
//
(Packet->OutTransferLength > 0 &&
(Packet->OutDataBuffer == NULL ||
Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ
)
)
) {
return EFI_INVALID_PARAMETER;
}
if (Packet->InTransferLength > sizeof (Dev->Dma->Data)) {
Packet->InTransferLength = sizeof (Dev->Dma->Data);
return ReportHostAdapterOverrunError (Packet);
}
if (Packet->OutTransferLength > sizeof (Dev->Dma->Data)) {
Packet->OutTransferLength = sizeof (Dev->Dma->Data);
return ReportHostAdapterOverrunError (Packet);
}
ZeroMem (Request, sizeof (*Request));
Request->Header.TargetId = Target;
//
// Only LUN 0 is currently supported, hence the cast is safe
//
Request->Header.Lun[1] = (UINT8)Lun;
Request->Header.Function = MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST;
Request->Header.MessageContext = 1; // We handle one request at a time
Request->Header.CdbLength = Packet->CdbLength;
CopyMem (Request->Header.Cdb, Packet->Cdb, Packet->CdbLength);
//
// SenseDataLength is UINT8, Sense[] is MAX_UINT8, so we can't overflow
//
ZeroMem (Dev->Dma->Sense, Packet->SenseDataLength);
Request->Header.SenseBufferLength = Packet->SenseDataLength;
Request->Header.SenseBufferLowAddress = MPT_SCSI_DMA_ADDR_LOW (Dev, Sense);
Request->Sg.EndOfList = 1;
Request->Sg.EndOfBuffer = 1;
Request->Sg.LastElement = 1;
Request->Sg.ElementType = MPT_SG_ENTRY_TYPE_SIMPLE;
Request->Sg.Is64BitAddress = 1;
Request->Sg.DataBufferAddress = MPT_SCSI_DMA_ADDR (Dev, Data);
//
// "MPT_SG_ENTRY_SIMPLE.Length" is a 24-bit quantity.
//
STATIC_ASSERT (
sizeof (Dev->Dma->Data) < SIZE_16MB,
"MPT_SCSI_DMA_BUFFER.Data must be smaller than 16MB"
);
if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
Request->Header.DataLength = Packet->InTransferLength;
Request->Sg.Length = Packet->InTransferLength;
Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ;
} else {
Request->Header.DataLength = Packet->OutTransferLength;
Request->Sg.Length = Packet->OutTransferLength;
Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE;
CopyMem (Dev->Dma->Data, Packet->OutDataBuffer, Packet->OutTransferLength);
Request->Sg.BufferContainsData = 1;
}
if (Request->Header.DataLength == 0) {
Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
MptScsiSendRequest (
IN MPT_SCSI_DEV *Dev,
IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
)
{
EFI_STATUS Status;
if (!Dev->IoReplyEnqueued) {
//
// Put one free reply frame on the reply queue, the hardware may use it to
// report an error to us.
//
Status = Out32 (Dev, MPT_REG_REP_Q, MPT_SCSI_DMA_ADDR_LOW (Dev, IoReply));
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
Dev->IoReplyEnqueued = TRUE;
}
Status = Out32 (Dev, MPT_REG_REQ_Q, MPT_SCSI_DMA_ADDR_LOW (Dev, IoRequest));
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
MptScsiGetReply (
IN MPT_SCSI_DEV *Dev,
OUT UINT32 *Reply
)
{
EFI_STATUS Status;
UINT32 Istatus;
UINT32 EmptyReply;
//
// Timeouts are not supported for
// EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() in this implementation.
//
for (;;) {
Status = In32 (Dev, MPT_REG_ISTATUS, &Istatus);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Interrupt raised
//
if (Istatus & MPT_IMASK_REPLY) {
break;
}
gBS->Stall (Dev->StallPerPollUsec);
}
Status = In32 (Dev, MPT_REG_REP_Q, Reply);
if (EFI_ERROR (Status)) {
return Status;
}
//
// The driver is supposed to fetch replies until 0xffffffff is returned, which
// will reset the interrupt status. We put only one request, so we expect the
// next read reply to be the last.
//
Status = In32 (Dev, MPT_REG_REP_Q, &EmptyReply);
if (EFI_ERROR (Status) || EmptyReply != MAX_UINT32) {
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
MptScsiHandleReply (
IN MPT_SCSI_DEV *Dev,
IN UINT32 Reply,
OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
)
{
if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
CopyMem (Packet->InDataBuffer, Dev->Dma->Data, Packet->InTransferLength);
}
if (Reply == Dev->Dma->IoRequest.Data.Header.MessageContext) {
//
// This is a turbo reply, everything is good
//
Packet->SenseDataLength = 0;
Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
} else if ((Reply & BIT31) != 0) {
DEBUG ((DEBUG_INFO, "%a: Full reply returned\n", __FUNCTION__));
//
// When reply MSB is set, we got a full reply. Since we submitted only one
// reply frame, we know it's IoReply.
//
Dev->IoReplyEnqueued = FALSE;
Packet->TargetStatus = Dev->Dma->IoReply.Data.ScsiStatus;
//
// Make sure device only lowers SenseDataLength before copying sense
//
ASSERT (Dev->Dma->IoReply.Data.SenseCount <= Packet->SenseDataLength);
Packet->SenseDataLength =
(UINT8)MIN (Dev->Dma->IoReply.Data.SenseCount, Packet->SenseDataLength);
CopyMem (Packet->SenseData, Dev->Dma->Sense, Packet->SenseDataLength);
if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
Packet->InTransferLength = Dev->Dma->IoReply.Data.TransferCount;
} else {
Packet->OutTransferLength = Dev->Dma->IoReply.Data.TransferCount;
}
switch (Dev->Dma->IoReply.Data.IocStatus) {
case MPT_SCSI_IOCSTATUS_SUCCESS:
Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
break;
case MPT_SCSI_IOCSTATUS_DEVICE_NOT_THERE:
Packet->HostAdapterStatus =
EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT;
return EFI_TIMEOUT;
case MPT_SCSI_IOCSTATUS_DATA_UNDERRUN:
case MPT_SCSI_IOCSTATUS_DATA_OVERRUN:
Packet->HostAdapterStatus =
EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
break;
default:
Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
return EFI_DEVICE_ERROR;
}
} else {
DEBUG ((DEBUG_ERROR, "%a: unexpected reply (%x)\n", __FUNCTION__, Reply));
return ReportHostAdapterError (Packet);
}
return EFI_SUCCESS;
}
//
// Ext SCSI Pass Thru
//
STATIC
EFI_STATUS
EFIAPI
MptScsiPassThru (
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
IN UINT8 *Target,
IN UINT64 Lun,
IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
IN EFI_EVENT Event OPTIONAL
)
{
EFI_STATUS Status;
MPT_SCSI_DEV *Dev;
UINT32 Reply;
Dev = MPT_SCSI_FROM_PASS_THRU (This);
//
// We only use first byte of target identifer
//
Status = MptScsiPopulateRequest (Dev, *Target, Lun, Packet);
if (EFI_ERROR (Status)) {
//
// MptScsiPopulateRequest modified packet according to the error
//
return Status;
}
Status = MptScsiSendRequest (Dev, Packet);
if (EFI_ERROR (Status)) {
return ReportHostAdapterError (Packet);
}
Status = MptScsiGetReply (Dev, &Reply);
if (EFI_ERROR (Status)) {
return ReportHostAdapterError (Packet);
}
return MptScsiHandleReply (Dev, Reply, Packet);
}
STATIC
BOOLEAN
IsTargetInitialized (
IN UINT8 *Target
)
{
UINTN Idx;
for (Idx = 0; Idx < TARGET_MAX_BYTES; ++Idx) {
if (Target[Idx] != 0xFF) {
return TRUE;
}
}
return FALSE;
}
STATIC
EFI_STATUS
EFIAPI
MptScsiGetNextTargetLun (
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
IN OUT UINT8 **Target,
IN OUT UINT64 *Lun
)
{
MPT_SCSI_DEV *Dev;
Dev = MPT_SCSI_FROM_PASS_THRU (This);
//
// Currently support only LUN 0, so hardcode it
//
if (!IsTargetInitialized (*Target)) {
ZeroMem (*Target, TARGET_MAX_BYTES);
*Lun = 0;
} else if (**Target > Dev->MaxTarget || *Lun > 0) {
return EFI_INVALID_PARAMETER;
} else if (**Target < Dev->MaxTarget) {
//
// This device interface support 256 targets only, so it's enough to
// increment the LSB of Target, as it will never overflow.
//
**Target += 1;
} else {
return EFI_NOT_FOUND;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
MptScsiGetNextTarget (
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
IN OUT UINT8 **Target
)
{
MPT_SCSI_DEV *Dev;
Dev = MPT_SCSI_FROM_PASS_THRU (This);
if (!IsTargetInitialized (*Target)) {
ZeroMem (*Target, TARGET_MAX_BYTES);
} else if (**Target > Dev->MaxTarget) {
return EFI_INVALID_PARAMETER;
} else if (**Target < Dev->MaxTarget) {
//
// This device interface support 256 targets only, so it's enough to
// increment the LSB of Target, as it will never overflow.
//
**Target += 1;
} else {
return EFI_NOT_FOUND;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
MptScsiBuildDevicePath (
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
IN UINT8 *Target,
IN UINT64 Lun,
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
)
{
MPT_SCSI_DEV *Dev;
SCSI_DEVICE_PATH *ScsiDevicePath;
if (DevicePath == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// This device support 256 targets only, so it's enough to dereference
// the LSB of Target.
//
Dev = MPT_SCSI_FROM_PASS_THRU (This);
if (*Target > Dev->MaxTarget || Lun > 0) {
return EFI_NOT_FOUND;
}
ScsiDevicePath = AllocateZeroPool (sizeof (*ScsiDevicePath));
if (ScsiDevicePath == NULL) {
return EFI_OUT_OF_RESOURCES;
}
ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH;
ScsiDevicePath->Header.SubType = MSG_SCSI_DP;
ScsiDevicePath->Header.Length[0] = (UINT8)sizeof (*ScsiDevicePath);
ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof (*ScsiDevicePath) >> 8);
ScsiDevicePath->Pun = *Target;
ScsiDevicePath->Lun = (UINT16)Lun;
*DevicePath = &ScsiDevicePath->Header;
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
MptScsiGetTargetLun (
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
OUT UINT8 **Target,
OUT UINT64 *Lun
)
{
MPT_SCSI_DEV *Dev;
SCSI_DEVICE_PATH *ScsiDevicePath;
if (DevicePath == NULL ||
Target == NULL || *Target == NULL || Lun == NULL) {
return EFI_INVALID_PARAMETER;
}
if (DevicePath->Type != MESSAGING_DEVICE_PATH ||
DevicePath->SubType != MSG_SCSI_DP) {
return EFI_UNSUPPORTED;
}
Dev = MPT_SCSI_FROM_PASS_THRU (This);
ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath;
if (ScsiDevicePath->Pun > Dev->MaxTarget ||
ScsiDevicePath->Lun > 0) {
return EFI_NOT_FOUND;
}
ZeroMem (*Target, TARGET_MAX_BYTES);
//
// This device support 256 targets only, so it's enough to set the LSB
// of Target.
//
**Target = (UINT8)ScsiDevicePath->Pun;
*Lun = ScsiDevicePath->Lun;
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
MptScsiResetChannel (
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
)
{
return EFI_UNSUPPORTED;
}
STATIC
VOID
EFIAPI
MptScsiExitBoot (
IN EFI_EVENT Event,
IN VOID *Context
)
{
MPT_SCSI_DEV *Dev;
Dev = Context;
DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));
MptScsiReset (Dev);
}
STATIC
EFI_STATUS
EFIAPI
MptScsiResetTargetLun (
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
IN UINT8 *Target,
IN UINT64 Lun
)
{
return EFI_UNSUPPORTED;
}
//
// Driver Binding
//
STATIC
EFI_STATUS
EFIAPI
MptScsiControllerSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
EFI_STATUS Status;
EFI_PCI_IO_PROTOCOL *PciIo;
PCI_TYPE00 Pci;
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
(VOID **)&PciIo,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = PciIo->Pci.Read (
PciIo,
EfiPciIoWidthUint32,
0,
sizeof (Pci) / sizeof (UINT32),
&Pci
);
if (EFI_ERROR (Status)) {
goto Done;
}
if (Pci.Hdr.VendorId == LSI_LOGIC_PCI_VENDOR_ID &&
(Pci.Hdr.DeviceId == LSI_53C1030_PCI_DEVICE_ID ||
Pci.Hdr.DeviceId == LSI_SAS1068_PCI_DEVICE_ID ||
Pci.Hdr.DeviceId == LSI_SAS1068E_PCI_DEVICE_ID)) {
Status = EFI_SUCCESS;
} else {
Status = EFI_UNSUPPORTED;
}
Done:
gBS->CloseProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return Status;
}
STATIC
EFI_STATUS
EFIAPI
MptScsiControllerStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
EFI_STATUS Status;
MPT_SCSI_DEV *Dev;
UINTN Pages;
UINTN BytesMapped;
Dev = AllocateZeroPool (sizeof (*Dev));
if (Dev == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Dev->Signature = MPT_SCSI_DEV_SIGNATURE;
Dev->MaxTarget = PcdGet8 (PcdMptScsiMaxTargetLimit);
Dev->StallPerPollUsec = PcdGet32 (PcdMptScsiStallPerPollUsec);
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
(VOID **)&Dev->PciIo,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
goto FreePool;
}
Status = Dev->PciIo->Attributes (
Dev->PciIo,
EfiPciIoAttributeOperationGet,
0,
&Dev->OriginalPciAttributes
);
if (EFI_ERROR (Status)) {
goto CloseProtocol;
}
//
// Enable I/O Space & Bus-Mastering
//
Status = Dev->PciIo->Attributes (
Dev->PciIo,
EfiPciIoAttributeOperationEnable,
(EFI_PCI_IO_ATTRIBUTE_IO |
EFI_PCI_IO_ATTRIBUTE_BUS_MASTER),
NULL
);
if (EFI_ERROR (Status)) {
goto CloseProtocol;
}
//
// Signal device supports 64-bit DMA addresses
//
Status = Dev->PciIo->Attributes (
Dev->PciIo,
EfiPciIoAttributeOperationEnable,
EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE,
NULL
);
if (EFI_ERROR (Status)) {
//
// Warn user that device will only be using 32-bit DMA addresses.
//
// Note that this does not prevent the device/driver from working
// and therefore we only warn and continue as usual.
//
DEBUG ((
DEBUG_WARN,
"%a: failed to enable 64-bit DMA addresses\n",
__FUNCTION__
));
}
//
// Create buffers for data transfer
//
Pages = EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma));
Status = Dev->PciIo->AllocateBuffer (
Dev->PciIo,
AllocateAnyPages,
EfiBootServicesData,
Pages,
(VOID **)&Dev->Dma,
EFI_PCI_ATTRIBUTE_MEMORY_CACHED
);
if (EFI_ERROR (Status)) {
goto RestoreAttributes;
}
BytesMapped = EFI_PAGES_TO_SIZE (Pages);
Status = Dev->PciIo->Map (
Dev->PciIo,
EfiPciIoOperationBusMasterCommonBuffer,
Dev->Dma,
&BytesMapped,
&Dev->DmaPhysical,
&Dev->DmaMapping
);
if (EFI_ERROR (Status)) {
goto FreeBuffer;
}
if (BytesMapped != EFI_PAGES_TO_SIZE (Pages)) {
Status = EFI_OUT_OF_RESOURCES;
goto Unmap;
}
Status = MptScsiInit (Dev);
if (EFI_ERROR (Status)) {
goto Unmap;
}
Status = gBS->CreateEvent (
EVT_SIGNAL_EXIT_BOOT_SERVICES,
TPL_CALLBACK,
&MptScsiExitBoot,
Dev,
&Dev->ExitBoot
);
if (EFI_ERROR (Status)) {
goto UninitDev;
}
//
// Host adapter channel, doesn't exist
//
Dev->PassThruMode.AdapterId = MAX_UINT32;
Dev->PassThruMode.Attributes =
EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |
EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
Dev->PassThru.Mode = &Dev->PassThruMode;
Dev->PassThru.PassThru = &MptScsiPassThru;
Dev->PassThru.GetNextTargetLun = &MptScsiGetNextTargetLun;
Dev->PassThru.BuildDevicePath = &MptScsiBuildDevicePath;
Dev->PassThru.GetTargetLun = &MptScsiGetTargetLun;
Dev->PassThru.ResetChannel = &MptScsiResetChannel;
Dev->PassThru.ResetTargetLun = &MptScsiResetTargetLun;
Dev->PassThru.GetNextTarget = &MptScsiGetNextTarget;
Status = gBS->InstallProtocolInterface (
&ControllerHandle,
&gEfiExtScsiPassThruProtocolGuid,
EFI_NATIVE_INTERFACE,
&Dev->PassThru
);
if (EFI_ERROR (Status)) {
goto CloseExitBoot;
}
return EFI_SUCCESS;
CloseExitBoot:
gBS->CloseEvent (Dev->ExitBoot);
UninitDev:
MptScsiReset (Dev);
Unmap:
Dev->PciIo->Unmap (
Dev->PciIo,
Dev->DmaMapping
);
FreeBuffer:
Dev->PciIo->FreeBuffer (
Dev->PciIo,
Pages,
Dev->Dma
);
RestoreAttributes:
Dev->PciIo->Attributes (
Dev->PciIo,
EfiPciIoAttributeOperationSet,
Dev->OriginalPciAttributes,
NULL
);
CloseProtocol:
gBS->CloseProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
FreePool:
FreePool (Dev);
return Status;
}
STATIC
EFI_STATUS
EFIAPI
MptScsiControllerStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_STATUS Status;
EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
MPT_SCSI_DEV *Dev;
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiExtScsiPassThruProtocolGuid,
(VOID **)&PassThru,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only
);
if (EFI_ERROR (Status)) {
return Status;
}
Dev = MPT_SCSI_FROM_PASS_THRU (PassThru);
Status = gBS->UninstallProtocolInterface (
ControllerHandle,
&gEfiExtScsiPassThruProtocolGuid,
&Dev->PassThru
);
if (EFI_ERROR (Status)) {
return Status;
}
gBS->CloseEvent (Dev->ExitBoot);
MptScsiReset (Dev);
Dev->PciIo->Unmap (
Dev->PciIo,
Dev->DmaMapping
);
Dev->PciIo->FreeBuffer (
Dev->PciIo,
EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma)),
Dev->Dma
);
Dev->PciIo->Attributes (
Dev->PciIo,
EfiPciIoAttributeOperationSet,
Dev->OriginalPciAttributes,
NULL
);
gBS->CloseProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
FreePool (Dev);
return Status;
}
STATIC
EFI_DRIVER_BINDING_PROTOCOL mMptScsiDriverBinding = {
&MptScsiControllerSupported,
&MptScsiControllerStart,
&MptScsiControllerStop,
MPT_SCSI_BINDING_VERSION,
NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2
NULL, // DriverBindingHandle, filled as well
};
//
// Component Name
//
STATIC
EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
{ "eng;en", L"LSI Fusion MPT SCSI Driver" },
{ NULL, NULL }
};
STATIC
EFI_COMPONENT_NAME_PROTOCOL mComponentName;
EFI_STATUS
EFIAPI
MptScsiGetDriverName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN CHAR8 *Language,
OUT CHAR16 **DriverName
)
{
return LookupUnicodeString2 (
Language,
This->SupportedLanguages,
mDriverNameTable,
DriverName,
(BOOLEAN)(This == &mComponentName) // Iso639Language
);
}
EFI_STATUS
EFIAPI
MptScsiGetDeviceName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN EFI_HANDLE DeviceHandle,
IN EFI_HANDLE ChildHandle,
IN CHAR8 *Language,
OUT CHAR16 **ControllerName
)
{
return EFI_UNSUPPORTED;
}
STATIC
EFI_COMPONENT_NAME_PROTOCOL mComponentName = {
&MptScsiGetDriverName,
&MptScsiGetDeviceName,
"eng" // SupportedLanguages, ISO 639-2 language codes
};
STATIC
EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {
(EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &MptScsiGetDriverName,
(EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &MptScsiGetDeviceName,
"en" // SupportedLanguages, RFC 4646 language codes
};
//
// Entry Point
//
EFI_STATUS
EFIAPI
MptScsiEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
return EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&mMptScsiDriverBinding,
ImageHandle, // The handle to install onto
&mComponentName,
&mComponentName2
);
}