From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from userp2130.oracle.com (userp2130.oracle.com [156.151.31.86]) by mx.groups.io with SMTP id smtpd.web09.16657.1583349748158356832 for ; Wed, 04 Mar 2020 11:22:28 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@oracle.com header.s=corp-2020-01-29 header.b=hTWpVtQf; spf=pass (domain: oracle.com, ip: 156.151.31.86, mailfrom: nikita.leshchenko@oracle.com) Received: from pps.filterd (userp2130.oracle.com [127.0.0.1]) by userp2130.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 024Iw9aE058148; Wed, 4 Mar 2020 19:22:27 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=corp-2020-01-29; bh=kOkw+kj2tJBeOQSKWusnJy/M/K6NEMID0QENNpUJ0y0=; b=hTWpVtQf53s9v/Xy7f11jfEKHBDylRxJYSGPwjp48pudFruWlVNLbbl5GzC+2W04j9wq /+AW00yQA5EQhz8Q5dWphsKwS7xr5tRQjpeU5A6Wp7d8SY+R2afEPAIGn3lK+eMLrlqU 0IhNxpHt+tFdH5UbufQOaAzfxKfcDPYKTElK+ppsrjle1N7JqhDyzaOppmMCvDpDlSZd pBvZmkwcW3ykXjgCpO4PMLd83Y0HRABy6F+KvcEsbFkmNYZxCUxkI2AvrXSTFSdsKVDT JZ0M9tdMZYLffadAFZx+fjQWAbBNCHzmepmbluPpWDDJtOQ9GSRmvwpk9Q6Ex3SOWLpq jw== Received: from aserp3030.oracle.com (aserp3030.oracle.com [141.146.126.71]) by userp2130.oracle.com with ESMTP id 2yffcurgvs-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Wed, 04 Mar 2020 19:22:27 +0000 Received: from pps.filterd (aserp3030.oracle.com [127.0.0.1]) by aserp3030.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 024JDEoh149393; Wed, 4 Mar 2020 19:22:26 GMT Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by aserp3030.oracle.com with ESMTP id 2yg1h1h4xv-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Wed, 04 Mar 2020 19:22:26 +0000 Received: from abhmp0017.oracle.com (abhmp0017.oracle.com [141.146.116.23]) by aserv0121.oracle.com (8.14.4/8.13.8) with ESMTP id 024JMQ2f024634; Wed, 4 Mar 2020 19:22:26 GMT Received: from spark.ravello.local (/213.57.127.2) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Wed, 04 Mar 2020 11:22:25 -0800 From: "Nikita Leshenko" To: devel@edk2.groups.io Cc: Nikita Leshenko , liran.alon@oracle.com, aaron.young@oracle.com, jordan.l.justen@intel.com, lersek@redhat.com, ard.biesheuvel@linaro.org Subject: [PATCH v3 12/13] OvmfPkg/MptScsiDxe: Implement the PassThru method Date: Wed, 4 Mar 2020 21:22:56 +0200 Message-Id: <20200304192257.96736-13-nikita.leshchenko@oracle.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200304192257.96736-1-nikita.leshchenko@oracle.com> References: <20200304192257.96736-1-nikita.leshchenko@oracle.com> MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9550 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 adultscore=0 phishscore=0 suspectscore=0 malwarescore=0 mlxlogscore=999 mlxscore=0 spamscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2001150001 definitions=main-2003040126 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9550 signatures=668685 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 phishscore=0 mlxscore=0 bulkscore=0 adultscore=0 suspectscore=0 spamscore=0 malwarescore=0 impostorscore=0 priorityscore=1501 mlxlogscore=999 lowpriorityscore=0 clxscore=1015 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2001150001 definitions=main-2003040126 Content-Transfer-Encoding: 8bit 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). Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2390 Signed-off-by: Nikita Leshenko --- .../Include/IndustryStandard/FusionMptScsi.h | 18 + OvmfPkg/MptScsiDxe/MptScsi.c | 344 +++++++++++++++++- OvmfPkg/MptScsiDxe/MptScsiDxe.inf | 3 + OvmfPkg/OvmfPkg.dec | 3 + 4 files changed, 365 insertions(+), 3 deletions(-) diff --git a/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h index 1ce2432bd3c2..e793f4856d0b 100644 --- a/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h +++ b/OvmfPkg/Include/IndustryStandard/FusionMptScsi.h @@ -50,6 +50,12 @@ #define MPT_IOC_WHOINIT_ROM_BIOS 0x02 +#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE (0x00 << 24) +#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 + // // Device structures // @@ -109,6 +115,10 @@ 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; @@ -141,4 +151,12 @@ typedef struct { UINT64 Uint64; // 8 byte alignment required by HW } MPT_SCSI_IO_ERROR_REPLY; +typedef union { + struct { + MPT_SCSI_IO_REQUEST Header; + MPT_SG_ENTRY_SIMPLE Sg; + } Data; + UINT64 Uint64; // 8 byte alignment required by HW +} MPT_SCSI_REQUEST_WITH_SG; + #endif // __FUSION_MPT_SCSI_H__ diff --git a/OvmfPkg/MptScsiDxe/MptScsi.c b/OvmfPkg/MptScsiDxe/MptScsi.c index 37f1ea4b3506..0985be07bc8e 100644 --- a/OvmfPkg/MptScsiDxe/MptScsi.c +++ b/OvmfPkg/MptScsiDxe/MptScsi.c @@ -23,6 +23,7 @@ #include #include #include +#include #include // @@ -35,6 +36,13 @@ // Runtime Structures // +typedef struct { + MPT_SCSI_IO_ERROR_REPLY IoErrorReply; + MPT_SCSI_REQUEST_WITH_SG IoRequest; + UINT8 Sense[MAX_UINT8]; + UINT8 Data[0x2000]; +} MPT_SCSI_DMA_BUFFER; + #define MPT_SCSI_DEV_SIGNATURE SIGNATURE_32 ('M','P','T','S') typedef struct { UINT32 Signature; @@ -42,11 +50,18 @@ typedef struct { EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; EFI_PCI_IO_PROTOCOL *PciIo; UINT64 OriginalPciAttributes; + UINT32 StallPerPollUsec; + MPT_SCSI_DMA_BUFFER *Dma; + EFI_PHYSICAL_ADDRESS DmaPhysical; + VOID *DmaMapping; } 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)) + // // Hardware functions // @@ -147,6 +162,8 @@ MptScsiInit ( UINT8 *ReplyBytes; UINT32 Reply32; + Dev->StallPerPollUsec = PcdGet32 (PcdMptScsiStallPerPollUsec); + Status = MptScsiReset (Dev); if (EFI_ERROR (Status)) { return Status; @@ -205,6 +222,227 @@ MptScsiInit ( return Status; } + // + // 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 (Dev, IoErrorReply)); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +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; + + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL || + Packet->CdbLength > sizeof (Request->Data.Header.CDB)) { + return EFI_UNSUPPORTED; + } + + if (Target > 0 || Lun > 0) { + return EFI_INVALID_PARAMETER; + } + + if (Packet->InTransferLength > sizeof (Dev->Dma->Data)) { + Packet->InTransferLength = sizeof (Dev->Dma->Data); + return EFI_BAD_BUFFER_SIZE; + } + if (Packet->OutTransferLength > sizeof (Dev->Dma->Data)) { + Packet->OutTransferLength = sizeof (Dev->Dma->Data); + return EFI_BAD_BUFFER_SIZE; + } + + ZeroMem (Request, sizeof (*Request)); + Request->Data.Header.TargetID = Target; + // + // It's 1 and not 0, for some reason... + // + Request->Data.Header.LUN[1] = Lun; + Request->Data.Header.Function = MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST; + Request->Data.Header.MessageContext = 1; // We handle one request at a time + + Request->Data.Header.CDBLength = Packet->CdbLength; + CopyMem (Request->Data.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->Data.Header.SenseBufferLength = Packet->SenseDataLength; + Request->Data.Header.SenseBufferLowAddress = MPT_SCSI_DMA_ADDR (Dev, Sense); + + Request->Data.Sg.EndOfList = 1; + Request->Data.Sg.EndOfBuffer = 1; + Request->Data.Sg.LastElement = 1; + Request->Data.Sg.ElementType = MPT_SG_ENTRY_TYPE_SIMPLE; + Request->Data.Sg.DataBufferAddress = MPT_SCSI_DMA_ADDR (Dev, Data); + + Request->Data.Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE; + switch (Packet->DataDirection) + { + case EFI_EXT_SCSI_DATA_DIRECTION_READ: + if (Packet->InTransferLength == 0) { + break; + } + Request->Data.Header.DataLength = Packet->InTransferLength; + Request->Data.Sg.Length = Packet->InTransferLength; + Request->Data.Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ; + break; + case EFI_EXT_SCSI_DATA_DIRECTION_WRITE: + if (Packet->OutTransferLength == 0) { + break; + } + Request->Data.Header.DataLength = Packet->OutTransferLength; + Request->Data.Sg.Length = Packet->OutTransferLength; + Request->Data.Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE; + + CopyMem (Dev->Dma->Data, Packet->OutDataBuffer, Packet->OutTransferLength); + Request->Data.Sg.BufferContainsData = 1; + break; + } + + 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; + + // + // Make sure Request is fully written + // + MemoryFence (); + + Status = Out32 (Dev, MPT_REG_REQ_Q, MPT_SCSI_DMA_ADDR (Dev, IoRequest)); + 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; + UINT32 Istatus; + UINT32 EmptyReply; + + 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 + ) +{ + EFI_STATUS Status; + + CopyMem (Packet->SenseData, Dev->Dma->Sense, Packet->SenseDataLength); + 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) { + // + // 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 ((DEBUG_ERROR, "%a: request failed\n", __FUNCTION__)); + // + // When reply MSB is set, it's an error frame. + // + + switch (Dev->Dma->IoErrorReply.Data.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 + // + Status = Out32 (Dev, MPT_REG_REP_Q, MPT_SCSI_DMA_ADDR (Dev, IoErrorReply)); + if (EFI_ERROR (Status)) { + return Status; + } + + } else { + DEBUG ((DEBUG_ERROR, "%a: unexpected reply\n", __FUNCTION__)); + return EFI_DEVICE_ERROR; + } + return EFI_SUCCESS; } @@ -223,7 +461,50 @@ 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); + Status = MptScsiPopulateRequest (Dev, *Target, Lun, Packet); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = MptScsiSendRequest (Dev, Packet); + if (EFI_ERROR (Status)) { + return Status; + } + + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK; + + Status = MptScsiGetReply (Dev, &Reply); + if (EFI_ERROR (Status)) { + goto Fatal; + } + + Status = MptScsiHandleReply (Dev, 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 ((DEBUG_ERROR, "%a: fatal error in scsi request\n", __FUNCTION__)); + 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 @@ -453,6 +734,7 @@ MptScsiControllerStart ( { EFI_STATUS Status; MPT_SCSI_DEV *Dev; + UINTN BytesMapped; Dev = AllocateZeroPool (sizeof (*Dev)); if (Dev == NULL) { @@ -497,9 +779,42 @@ MptScsiControllerStart ( goto CloseProtocol; } + // + // Create buffers for data transfer + // + Status = Dev->PciIo->AllocateBuffer ( + Dev->PciIo, + AllocateAnyPages, + EfiBootServicesData, + EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma)), + (VOID **)&Dev->Dma, + EFI_PCI_ATTRIBUTE_MEMORY_CACHED + ); + if (EFI_ERROR (Status)) { + goto RestoreAttributes; + } + + BytesMapped = sizeof (*Dev->Dma); + Status = Dev->PciIo->Map ( + Dev->PciIo, + EfiPciIoOperationBusMasterCommonBuffer, + Dev->Dma, + &BytesMapped, + &Dev->DmaPhysical, + &Dev->DmaMapping + ); + if (EFI_ERROR (Status)) { + goto FreeBuffer; + } + + if (BytesMapped != sizeof (*Dev->Dma)) { + Status = EFI_OUT_OF_RESOURCES; + goto Unmap; + } + Status = MptScsiInit (Dev); if (EFI_ERROR (Status)) { - goto CloseProtocol; + goto Unmap; } // @@ -526,11 +841,23 @@ MptScsiControllerStart ( &Dev->PassThru ); if (EFI_ERROR (Status)) { - goto RestoreAttributes; + goto Unmap; } return EFI_SUCCESS; +Unmap: + Dev->PciIo->Unmap ( + Dev->PciIo, + Dev->DmaMapping + ); + +FreeBuffer: + Dev->PciIo->FreeBuffer ( + Dev->PciIo, + EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma)), + Dev->Dma + ); RestoreAttributes: Dev->PciIo->Attributes ( Dev->PciIo, @@ -590,6 +917,17 @@ MptScsiControllerStop ( 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, EfiPciIoAttributeOperationEnable, diff --git a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf index 8f366b92eb72..1ba65f2fbbdf 100644 --- a/OvmfPkg/MptScsiDxe/MptScsiDxe.inf +++ b/OvmfPkg/MptScsiDxe/MptScsiDxe.inf @@ -40,3 +40,6 @@ [LibraryClasses] [Protocols] gEfiPciIoProtocolGuid ## TO_START gEfiExtScsiPassThruProtocolGuid ## BY_START + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec ## CONSUMES diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 4c5b6511cb97..7e8097f9952e 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -228,6 +228,9 @@ [PcdsFixedAtBuild] ## Number of page frames to use for storing grant table entries. gUefiOvmfPkgTokenSpaceGuid.PcdXenGrantFrames|4|UINT32|0x33 + ## Microseconds to stall between polling for MptScsi request result + gUefiOvmfPkgTokenSpaceGuid.PcdMptScsiStallPerPollUsec|5|UINT32|0x36 + [PcdsDynamic, PcdsDynamicEx] gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10 -- 2.20.1