Files
system76-edk2/OvmfPkg/MptScsiDxe/MptScsi.c
Nikita Leshenko 81cada9892 OvmfPkg/MptScsiDxe: Initialize hardware
Reset and send the IO controller initialization request. The reply is
read back to complete the doorbell function but it isn't useful to us
because it doesn't contain relevant data or status codes.

See "LSI53C1030 PCI-X to Dual Channel Ultra320 SCSI Multifunction
Controller" technical manual for more information.

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

776 lines
19 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/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/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
//
#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;
EFI_PCI_IO_PROTOCOL *PciIo;
UINT64 OriginalPciAttributes;
} MPT_SCSI_DEV;
#define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \
CR (PassThruPtr, MPT_SCSI_DEV, PassThru, MPT_SCSI_DEV_SIGNATURE)
//
// 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;
//
// 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;
}
//
// 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
)
{
return EFI_UNSUPPORTED;
}
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
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;
Dev = AllocateZeroPool (sizeof (*Dev));
if (Dev == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Dev->Signature = MPT_SCSI_DEV_SIGNATURE;
Dev->MaxTarget = PcdGet8 (PcdMptScsiMaxTargetLimit);
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__
));
}
Status = MptScsiInit (Dev);
if (EFI_ERROR (Status)) {
goto RestoreAttributes;
}
//
// 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 UninitDev;
}
return EFI_SUCCESS;
UninitDev:
MptScsiReset (Dev);
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;
}
MptScsiReset (Dev);
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
);
}