From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=141.146.126.79; helo=aserp2130.oracle.com; envelope-from=nikita.leshchenko@oracle.com; receiver=edk2-devel@lists.01.org Received: from aserp2130.oracle.com (aserp2130.oracle.com [141.146.126.79]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 457A5211C3864 for ; Thu, 31 Jan 2019 02:08:20 -0800 (PST) Received: from pps.filterd (aserp2130.oracle.com [127.0.0.1]) by aserp2130.oracle.com (8.16.0.22/8.16.0.22) with SMTP id x0VA4SPj154151 for ; Thu, 31 Jan 2019 10:08:19 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-2018-07-02; bh=etqwHKk0lUPuCSaXlvAQU/M4GhgxNxUbq3l4/nSgaOQ=; b=ZR6+mlWd0s9gk8vkwalzpjxJ0BBj0cpX96mvnZz2ll7Qwjk9O72TLd5srSWAqJdu097U j6RV8+FZTdIjD8BRvC2q30vW6XyeuEl3wT4+FhjSMef9kIWTLcfoaUWI1mG2NW5Rtq9V 0dlCIOYGbPQtSHBenAHeFJ8xGZtIufCA8g0kKMrlb18imG/A1jbB0yGAXtbFGWkcNudp x9zsRVop2ys+bZS9K2uD6/pbd8ghj+nvusS2B0VFBYpO2eIyPb98Z1obkI6NpDWTx2/i OmBZZedpKDwXsah06Ncnqyr5zxazZ5FRovjrlb+vU1D7R70XdV/c+DVVnz0mkyFhV91P Fg== Received: from userv0021.oracle.com (userv0021.oracle.com [156.151.31.71]) by aserp2130.oracle.com with ESMTP id 2q8d2efvb1-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK) for ; Thu, 31 Jan 2019 10:08:19 +0000 Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by userv0021.oracle.com (8.14.4/8.14.4) with ESMTP id x0VA8IkZ005044 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK) for ; Thu, 31 Jan 2019 10:08:18 GMT Received: from abhmp0013.oracle.com (abhmp0013.oracle.com [141.146.116.19]) by aserv0121.oracle.com (8.14.4/8.13.8) with ESMTP id x0VA8I2b012950 for ; Thu, 31 Jan 2019 10:08:18 GMT Received: from spark.ravello.local (/213.57.127.2) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Thu, 31 Jan 2019 02:08:17 -0800 From: Nikita Leshenko To: edk2-devel@lists.01.org Cc: liran.alon@oracle.com, Nikita Leshenko Date: Thu, 31 Jan 2019 12:07:22 +0200 Message-Id: <20190131100724.20907-13-nikita.leshchenko@oracle.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190131100724.20907-1-nikita.leshchenko@oracle.com> References: <20190131100724.20907-1-nikita.leshchenko@oracle.com> MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=5900 definitions=9152 signatures=668682 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=1 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1810050000 definitions=main-1901310080 Subject: [PATCH 12/14] OvmfPkg/MptScsiDxe: Implement the PassThru method X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 31 Jan 2019 10:08:20 -0000 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). Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Nikita Leshenko Reviewed-by: Aaron Young Reviewed-by: Liran Alon --- 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