X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=OvmfPkg%2FMptScsiDxe%2FMptScsi.c;h=9ed1831bac6dcf051b0a9e7bcb4ff07e6a93bb3a;hp=d396bff85cb6df7f81e2f6e63089deb51f28b7af;hb=c635a56384bfc4fdf3e00d102ea0eb7aa8d840fd;hpb=093cceaf79b5d16880050a08e4bd465954ea6a97 diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index d396bff85c..9ed1831bac 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include @@ -31,17 +33,522 @@ // 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 // @@ -57,7 +564,33 @@ MptScsiPassThru ( IN EFI_EVENT Event OPTIONAL ) { - return EFI_UNSUPPORTED; + 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 @@ -147,7 +680,36 @@ MptScsiBuildDevicePath ( IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath ) { - return EFI_UNSUPPORTED; + 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 @@ -160,7 +722,35 @@ MptScsiGetTargetLun ( OUT UINT64 *Lun ) { - return EFI_UNSUPPORTED; + 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 @@ -173,6 +763,20 @@ MptScsiResetChannel ( 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 @@ -255,6 +859,8 @@ MptScsiControllerStart ( { EFI_STATUS Status; MPT_SCSI_DEV *Dev; + UINTN Pages; + UINTN BytesMapped; Dev = AllocateZeroPool (sizeof (*Dev)); if (Dev == NULL) { @@ -264,6 +870,116 @@ MptScsiControllerStart ( 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 @@ -289,11 +1005,46 @@ MptScsiControllerStart ( &Dev->PassThru ); if (EFI_ERROR (Status)) { - goto FreePool; + 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); @@ -337,6 +1088,35 @@ MptScsiControllerStop ( 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;