/** @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 #include #include #include #include #include #include #include #include #include #include #include #include // // 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", __func__)); 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", __func__)); // // 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", __func__, 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", __func__, 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", __func__ )); } // // 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 ); }