Machines should be able to boot after this commit. Tested with different Linux distributions (Ubuntu, CentOS) and different Windows versions (Windows 7, Windows 10, Server 2016).
Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Nikita Leshenko <nikita.leshche...@oracle.com> Reviewed-by: Aaron Young <aaron.yo...@oracle.com> Reviewed-by: Liran Alon <liran.a...@oracle.com> --- OvmfPkg/MptScsiDxe/MptScsi.c | 255 +++++++++++++++++++++++++++++- OvmfPkg/MptScsiDxe/MptScsiDxe.inf | 3 + OvmfPkg/OvmfPkg.dec | 2 + 3 files changed, 256 insertions(+), 4 deletions(-) diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index fbabeb22ca..a7dab0f71a 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -54,9 +54,15 @@ #define MPT_MESSAGE_HDR_FUNCTION_IOC_INIT 0x02 #define MPT_SG_ENTRY_TYPE_SIMPLE 0x01 +#define MPT_SG_ENTRY_SIMPLE_MAX_LENGTH ((1 << 24) - 1) #define MPT_IOC_WHOINIT_ROM_BIOS 0x02 +#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE (0x01 << 24) +#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ (0x02 << 24) + +#define MPT_SCSI_IO_ERROR_IOCSTATUS_DEVICE_NOT_THERE 0x0043 + // // Runtime Structures // @@ -110,6 +116,8 @@ typedef struct { UINT32 Length: 24; UINT32 EndOfList: 1; UINT32 Is64BitAddress: 1; + // True when the buffer contains data to be transfered. Otherwise it's the + // destination buffer UINT32 BufferContainsData: 1; UINT32 LocalAddress: 1; UINT32 ElementType: 2; @@ -136,6 +144,11 @@ typedef struct { UINT32 ResponseInfo; } __attribute__((aligned(8), packed)) MPT_SCSI_IO_ERROR_REPLY; // Align required by HW +typedef struct { + MPT_SCSI_IO_REQUEST Header; + MPT_SG_ENTRY_SIMPLE Sg; +} __attribute__((aligned(8), packed)) MPT_SCSI_REQUEST_WITH_SG; // Align required by HW + #define MPT_SCSI_DEV_SIGNATURE SIGNATURE_32 ('M','P','T','S') typedef struct { UINT32 Signature; @@ -143,6 +156,9 @@ typedef struct { EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; EFI_PCI_IO_PROTOCOL *PciIo; UINT64 OriginalPciAttributes; + UINT32 StallPerPollUsec; + MPT_SCSI_IO_ERROR_REPLY IoErrorReply; + MPT_SCSI_REQUEST_WITH_SG IoRequest; } MPT_SCSI_DEV; #define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \ @@ -228,6 +244,8 @@ MptScsiInit ( { EFI_STATUS Status; + Dev->StallPerPollUsec = PcdGet32 (PcdMptScsiStallPerPollUsec); + Status = MptScsiReset (Dev); if (EFI_ERROR (Status)) { return Status; @@ -273,6 +291,193 @@ MptScsiInit ( return Status; } + // Put one free reply frame on the reply queue, the hardware may use it to + // report an error to us. + ASSERT ((UINTN) &Dev->IoErrorReply <= MAX_UINT32); + Status = Out32 (Dev, MPT_REG_REP_Q, (UINT32)(UINTN) &Dev->IoErrorReply); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +MptScsiPopulateRequest ( + IN UINT8 Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + OUT MPT_SCSI_REQUEST_WITH_SG *Request + ) +{ + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL || + Packet->CdbLength > sizeof (Request->Header.CDB)) { + return EFI_UNSUPPORTED; + } + + if (Target > 0 || Lun > 0) { + return EFI_INVALID_PARAMETER; + } + + if (Packet->InTransferLength > MPT_SG_ENTRY_SIMPLE_MAX_LENGTH) { + Packet->InTransferLength = MPT_SG_ENTRY_SIMPLE_MAX_LENGTH; + return EFI_BAD_BUFFER_SIZE; + } + if (Packet->OutTransferLength > MPT_SG_ENTRY_SIMPLE_MAX_LENGTH) { + Packet->OutTransferLength = MPT_SG_ENTRY_SIMPLE_MAX_LENGTH; + return EFI_BAD_BUFFER_SIZE; + } + + ZeroMem (Request, sizeof (*Request)); + Request->Header.TargetID = Target; + // It's 1 and not 0, for some reason... + Request->Header.LUN[1] = Lun; + Request->Header.Function = MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST; + Request->Header.MessageContext = 1; // Hardcoded, we handle one request at a time + + Request->Header.CDBLength = Packet->CdbLength; + CopyMem (Request->Header.CDB, Packet->Cdb, Packet->CdbLength); + + Request->Header.SenseBufferLength = Packet->SenseDataLength; + ASSERT ((UINTN) Packet->SenseData <= MAX_UINT32); + Request->Header.SenseBufferLowAddress = (UINT32)(UINTN) Packet->SenseData; + + Request->Sg.EndOfList = 1; + Request->Sg.EndOfBuffer = 1; + Request->Sg.LastElement = 1; + Request->Sg.ElementType = MPT_SG_ENTRY_TYPE_SIMPLE; + + switch (Packet->DataDirection) + { + case EFI_EXT_SCSI_DATA_DIRECTION_READ: + Request->Header.DataLength = Packet->InTransferLength; + Request->Sg.Length = Packet->InTransferLength; + ASSERT ((UINTN) Packet->InDataBuffer <= MAX_UINT32); + Request->Sg.DataBufferAddress = (UINT32)(UINTN) Packet->InDataBuffer; + Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ; + break; + case EFI_EXT_SCSI_DATA_DIRECTION_WRITE: + Request->Header.DataLength = Packet->OutTransferLength; + Request->Sg.Length = Packet->OutTransferLength; + ASSERT ((UINTN) Packet->OutDataBuffer <= MAX_UINT32); + Request->Sg.DataBufferAddress = (UINT32)(UINTN) Packet->OutDataBuffer; + Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE; + Request->Sg.BufferContainsData = 1; + break; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +MptScsiSendRequest ( + IN MPT_SCSI_DEV *Dev, + IN MPT_SCSI_REQUEST_WITH_SG *Request, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + EFI_STATUS Status; + + // Make sure Request is fully written + MemoryFence (); + + ASSERT ((UINTN) Request <= MAX_UINT32); + Status = Out32 (Dev, MPT_REG_REQ_Q, (UINT32)(UINTN) Request); + if (EFI_ERROR (Status)) { + // We couldn't enqueue the request, report it as an adapter error + Packet->InTransferLength = 0; + Packet->OutTransferLength = 0; + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER; + Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD; + Packet->SenseDataLength = 0; + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +MptScsiGetReply ( + IN MPT_SCSI_DEV *Dev, + OUT UINT32 *Reply + ) +{ + EFI_STATUS Status; + + for (;;) { + UINT32 Istatus; + 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. + UINT32 EmptyReply; + 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 MPT_SCSI_REQUEST_WITH_SG *Request, + IN UINT32 Reply, + OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + if (Reply == Request->Header.MessageContext) { + // Everything is good + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK; + Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD; + + } else if (Reply & (1 << 31)) { + DEBUG ((EFI_D_ERROR, "MptScsi request failed\n")); + // When reply MSB is set, it's an error frame. + + switch (Dev->IoErrorReply.IOCStatus) { + case MPT_SCSI_IO_ERROR_IOCSTATUS_DEVICE_NOT_THERE: + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT; + break; + default: + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER; + break; + } + + // Resubmit the reply frame to the reply queue + EFI_STATUS Status; + Status = Out32 (Dev, MPT_REG_REP_Q, (UINT32)(UINTN) &Dev->IoErrorReply); + if (EFI_ERROR (Status)) { + return Status; + } + + } else { + DEBUG ((EFI_D_ERROR, "MptScsi unexpected reply\n")); + return EFI_DEVICE_ERROR; + } + return EFI_SUCCESS; } @@ -291,10 +496,52 @@ MptScsiPassThru ( IN EFI_EVENT Event OPTIONAL ) { - DEBUG ((EFI_D_INFO, "MptScsiPassThru Direction %d In %d Out %d\n", - Packet->DataDirection, - Packet->InTransferLength, Packet->OutTransferLength)); - return EFI_UNSUPPORTED; + EFI_STATUS Status; + MPT_SCSI_DEV *Dev = MPT_SCSI_FROM_PASS_THRU (This); + + // Noisy print, but useful when debugging this area + // DEBUG ((EFI_D_INFO, "MptScsiPassThru Direction %d In %d Out %d\n", + // Packet->DataDirection, + // Packet->InTransferLength, Packet->OutTransferLength)); + + Status = MptScsiPopulateRequest (*Target, Lun, Packet, &Dev->IoRequest); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = MptScsiSendRequest (Dev, &Dev->IoRequest, Packet); + if (EFI_ERROR (Status)) { + return Status; + } + + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK; + + UINT32 Reply; + Status = MptScsiGetReply (Dev, &Reply); + if (EFI_ERROR (Status)) { + goto Fatal; + } + + Status = MptScsiHandleReply (Dev, &Dev->IoRequest, Reply, Packet); + if (EFI_ERROR (Status)) { + goto Fatal; + } + + return Status; + +Fatal: + // We erred in the middle of a transaction, a very serious problem has occured + // and it's not clear if it's possible to recover without leaving the hardware + // in an inconsistent state. Perhaps we would want to reset the device... + DEBUG ((EFI_D_ERROR, "MptScsi fatal error in scsi request\n")); + Packet->InTransferLength = 0; + Packet->OutTransferLength = 0; + if (Packet->HostAdapterStatus == EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK) { + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER; + } + Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_TASK_ABORTED; + Packet->SenseDataLength = 0; + return EFI_DEVICE_ERROR; } STATIC diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf index 5d424606a5..5875d6c94b 100644 --- a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf +++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf @@ -40,3 +40,6 @@ [Protocols] gEfiPciIoProtocolGuid ## TO_START gEfiExtScsiPassThruProtocolGuid ## BY_START + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec ## CONSUMES diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 7666297cf8..4ca98ff09f 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -122,6 +122,8 @@ gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize|0x0|UINT32|0x1a gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDecompressionScratchEnd|0x0|UINT32|0x1f + gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec|5|UINT32|0x28 + [PcdsDynamic, PcdsDynamicEx] gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10 -- 2.20.1 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel