From nobody Sun May 19 18:35:20 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+102863+1787277+3901457@groups.io; arc=fail (BodyHash is different from the expected one) Received: from mail02.groups.io (mail02.groups.io [66.175.222.108]) by mx.zohomail.com with SMTPS id 1681269464905368.8640691598529; Tue, 11 Apr 2023 20:17:44 -0700 (PDT) Return-Path: X-Received: by 127.0.0.2 with SMTP id 9WHWYY1788612xjgD7WG8xzS; Tue, 11 Apr 2023 20:17:44 -0700 X-Received: from NAM12-MW2-obe.outbound.protection.outlook.com (NAM12-MW2-obe.outbound.protection.outlook.com [40.107.244.51]) by mx.groups.io with SMTP id smtpd.web11.34611.1681269463692086051 for ; Tue, 11 Apr 2023 20:17:43 -0700 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=mtXrYbro1Xp/EluS+sFNk0PVQXWq72jkRVgz3NVx3wj5DnJmhaQK+NxTN/OiiQ0RYlWUZvWUNCeX7zHTkJIt6RC8x8Bv2h//rnpsjhSpaHHb45WPB/d4+5RfLo7tWgkbxD55lrk7dS6j5k20/nS3P8PVTY2Y4vyXN2UXsewXEQtu/GhIyjn54Uo912ggFkIg2KuQ5bHG/GiqVFcXOGTPGmlCeHdo97sFcpJYbZMa7vyx8qApK1U0dcpbw1HrJOFFrzBQa0dPyIaUXT1UeGqSj0cRC6j91PbjtH7Nqi8GQj+qFmfyJDFaGcfBUNjrBL/eluO6kgbP8L5TZ8Z/KIZXGw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=g1/jOBFVfrgDTHbdcRP6RN6D6Ziik5ruRpTkzoZG8CQ=; b=Pzn0LD/4PvkdYh4u2eQHDvqKtj3YZhNncpc+PljN60h/NXYeKRWT5OxoyiLpNvTjhFmVIbjkC4ufhD01lDeQzXpnW6Z3o6KvR665OSp7BZzpDBr9Izn1Gz6EpQqAbE4W2VZkC9xY7Sg1n2VCWSsN4wOVIlxYv1/LpbGMqxdNHgU75qHmdt0VfKHZiTkXiRsYLlsjKzypQlmSni8Lg0eQfd+v/3TftuE70pSWgO4pcnrdEJtbeoIfQ24v0ltctZCT+dN01KEINoJWcQm8JhmV6y9WF8ZqsjZGSb4iteHNHs2yOfvTOnVnTCgAQnNJ6bZ5Zzv6OOnapJybGhgsLMRhNQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 216.228.117.161) smtp.rcpttodomain=edk2.groups.io smtp.mailfrom=nvidia.com; dmarc=pass (p=reject sp=reject pct=100) action=none header.from=nvidia.com; dkim=none (message not signed); arc=none X-Received: from BN1PR10CA0023.namprd10.prod.outlook.com (2603:10b6:408:e0::28) by DM4PR12MB5964.namprd12.prod.outlook.com (2603:10b6:8:6b::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6277.36; Wed, 12 Apr 2023 03:17:38 +0000 X-Received: from BN8NAM11FT116.eop-nam11.prod.protection.outlook.com (2603:10b6:408:e0:cafe::94) by BN1PR10CA0023.outlook.office365.com (2603:10b6:408:e0::28) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6277.40 via Frontend Transport; Wed, 12 Apr 2023 03:17:37 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 216.228.117.161) smtp.mailfrom=nvidia.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=nvidia.com; Received-SPF: pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) client-ip=66.175.222.108; envelope-from=bounce+27952+102863+1787277+3901457@groups.io; helo=mail02.groups.io; Received-SPF: Pass (protection.outlook.com: domain of nvidia.com designates 216.228.117.161 as permitted sender) receiver=protection.outlook.com; client-ip=216.228.117.161; helo=mail.nvidia.com; pr=C X-Received: from mail.nvidia.com (216.228.117.161) by BN8NAM11FT116.mail.protection.outlook.com (10.13.176.67) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6298.30 via Frontend Transport; Wed, 12 Apr 2023 03:17:37 +0000 X-Received: from rnnvmail202.nvidia.com (10.129.68.7) by mail.nvidia.com (10.129.200.67) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.986.5; Tue, 11 Apr 2023 20:17:26 -0700 X-Received: from rnnvmail201.nvidia.com (10.129.68.8) by rnnvmail202.nvidia.com (10.129.68.7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.986.37; Tue, 11 Apr 2023 20:17:25 -0700 X-Received: from NV-CL38DL3.nvidia.com (10.127.8.9) by mail.nvidia.com (10.129.68.8) with Microsoft SMTP Server id 15.2.986.37 via Frontend Transport; Tue, 11 Apr 2023 20:17:24 -0700 From: "Nickle Wang via groups.io" To: CC: Abner Chang , Isaac Oram , Abdul Lateef Attar , Tinh Nguyen Subject: [edk2-devel] [edk2-platforms][PATCH] ManageabilityPkg: add support for the phosphor ipmi blob transfer protocol Date: Wed, 12 Apr 2023 11:17:24 +0800 Message-ID: <20230412031724.12690-1-nicklew@nvidia.com> MIME-Version: 1.0 X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: BN8NAM11FT116:EE_|DM4PR12MB5964:EE_ X-MS-Office365-Filtering-Correlation-Id: 5abd6773-a97c-48a5-7ccf-08db3b0477bb X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam-Message-Info: aCOibFtx7/xoZHTcvV1ytbicAVm+hE+ON9adEOEAGdQJU8RUmQlCC6aCYetm8iDameOwDjWF25VJnJfJoNjYh7aq1t1fhhoFV6SIVftCMjnhoVGnFA16N3r2sbPx4OFbPpFHJx990FCv/Il4gkYV6B0Xvvls+2Yn0jXwbfBuzvkVyBs7ATs98lJACujw0bTgF6g8Wd5wuAS5g3MBQC2UL0qraXoR/JclyvVVdTVCB3Oc1OaiIjjuoFI9NlkgI84YwJXItTYbA//0E7rrQPcl6hLtJLG343EHi8KUPePnUmo7g3xvLZUu9Tj4lURLAdlF3FlC4pJvjeP368ZrNmJ5u9XbaNDMwqQ3dlaqMj7rc+Ba5J5qZgLW5jYjOSFJWLguhug4DWzNetPP+lXrs+eCqSZYrJPr3U5iAvpEDw6YECdUHPEBIpdCweTA93hzbXljqg9+jRywGLEkm6JlOgbRSNz6lQS5cTWXDRqKjF1Kf5lGV3iuDxjPk9XywYgujqMnMYX9icS2rXwQx1pUwWwPGLedIv6omQBBTmAFyFaosXL0Vvce6sXPYN8nqTN5nRCspCZ4EVrTW7KXX4uK0BJgKw9hJsJkwBQkW3skbfcjQI4oo+PVGh/YeVYu8DzwFdAQkY7KgCiYd+w7hfRsxJ29b7EXB5DMwSSkRwGN36f6ukLEgOc2jHNorFiFsMuZfkj8y2Pme9kUUP5YjIJDdo93GE/d7cID8hcfQMxSoq4nhzj8pkRyIYKNdyYgwhBSKy9Vk3dt65B79rW+J/RzqotgKqMz1IxMqlMd+GQ1giO7PRW+xXiWjWkqLW+CDf1LCLjtsiD4FKWbJryjVPI8xel4hswh4GEDfGHdvUIbXmVWLRU= X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Apr 2023 03:17:37.5553 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 5abd6773-a97c-48a5-7ccf-08db3b0477bb X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=43083d15-7273-40c1-b7db-39efd9ccc17a;Ip=[216.228.117.161];Helo=[mail.nvidia.com] X-MS-Exchange-CrossTenant-AuthSource: BN8NAM11FT116.eop-nam11.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM4PR12MB5964 Precedence: Bulk List-Unsubscribe: List-Subscribe: List-Help: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,nicklew@nvidia.com X-Gm-Message-State: 8AfHRXfwJzN1Hok5T2oQVRdDx1787277AA= Content-Transfer-Encoding: quoted-printable DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1681269464; bh=8KnneiP2cq9GhiS+AfTl63p5PckU0tsVyz7KOv8U+9k=; h=CC:Content-Type:Date:From:Reply-To:Subject:To; b=Y3kmwteAwf0U6HW2ijGbbVNiI0Ai6Gow3ws6Bw7bVhdn6/SIA/AyK0ffeK0lJbnQeMB HGR7+yC4Rqgt/VElGo5bwBv7lRVSLPrLlxf6tN7qIioMzLFyi6KDnBE0G2E/RjJUZf/17 RGV/0rIZzgrkJ7/hMUOG438u4hwmQyidfuY= X-ZohoMail-DKIM: pass (identity @groups.io) X-ZM-MESSAGEID: 1681269466039100003 Content-Type: text/plain; charset="utf-8" This change implements the blob transfer protocol used in OpenBmc documented here: https://github.com/openbmc/phosphor-ipmi-blobs Signed-off-by: Nick Ramirez Cc: Abner Chang Cc: Isaac Oram Cc: Abdul Lateef Attar Cc: Nickle Wang Cc: Tinh Nguyen --- .../ManageabilityPkg/ManageabilityPkg.dec | 6 + .../Include/Dsc/Manageability.dsc | 4 +- .../IpmiBlobTransferDxe.inf | 39 + .../IpmiBlobTransferTestUnitTestsHost.inf | 40 + .../Include/Protocol/IpmiBlobTransfer.h | 136 ++ .../InternalIpmiBlobTransfer.h | 363 ++++++ .../IpmiBlobTransferDxe/IpmiBlobTransferDxe.c | 799 ++++++++++++ .../UnitTest/IpmiBlobTransferTestUnitTests.c | 1113 +++++++++++++++++ .../Universal/IpmiBlobTransferDxe/Readme.md | 24 + 9 files changed, 2523 insertions(+), 1 deletion(-) create mode 100644 Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe= /IpmiBlobTransferDxe.inf create mode 100644 Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe= /UnitTest/IpmiBlobTransferTestUnitTestsHost.inf create mode 100644 Features/ManageabilityPkg/Include/Protocol/IpmiBlobTran= sfer.h create mode 100644 Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe= /InternalIpmiBlobTransfer.h create mode 100644 Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe= /IpmiBlobTransferDxe.c create mode 100644 Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe= /UnitTest/IpmiBlobTransferTestUnitTests.c create mode 100644 Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe= /Readme.md diff --git a/Features/ManageabilityPkg/ManageabilityPkg.dec b/Features/Mana= geabilityPkg/ManageabilityPkg.dec index 9a930d3e4b..e2d6cccc50 100644 --- a/Features/ManageabilityPkg/ManageabilityPkg.dec +++ b/Features/ManageabilityPkg/ManageabilityPkg.dec @@ -4,6 +4,7 @@ # those are related to the platform management. # # Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
+# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: BSD-2-Clause-Patent # ## @@ -48,3 +49,8 @@ gManageabilityProtocolMctpGuid =3D { 0x76FED8F1, 0x0BE5, 0x4269, { 0x= A3, 0x1A, 0x38, 0x0F, 0x54, 0xF1, 0xA1, 0x8A } } # Manageability Protocol PLDM gManageabilityProtocolPldmGuid =3D { 0x3958090D, 0x69DD, 0x4868, { 0x= 9C, 0x41, 0xC9, 0xAC, 0x31, 0xB5, 0x25, 0xC5 } } + +[Protocols] + + ## Include/Protocol/IpmiBlobTransfer.h + gEdkiiIpmiBlobTransferProtocolGuid =3D { 0x05837c75, 0x1d65, 0x468b, { 0= xb1, 0xc2, 0x81, 0xaf, 0x9a, 0x31, 0x5b, 0x2c } } diff --git a/Features/ManageabilityPkg/Include/Dsc/Manageability.dsc b/Feat= ures/ManageabilityPkg/Include/Dsc/Manageability.dsc index 0d868fdf4a..111d6b91dc 100644 --- a/Features/ManageabilityPkg/Include/Dsc/Manageability.dsc +++ b/Features/ManageabilityPkg/Include/Dsc/Manageability.dsc @@ -2,11 +2,13 @@ # Common libraries for Manageabilty Package # # Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
+# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: BSD-2-Clause-Patent # ## [LibraryClasses] ManageabilityTransportHelperLib|ManageabilityPkg/Library/BaseManageabili= tyTransportHelperLib/BaseManageabilityTransportHelper.inf + IpmiLib|MdeModulePkg/Library/DxeIpmiLibIpmiProtocol/DxeIpmiLibIpmiProtoc= ol.inf =20 [LibraryClasses.ARM, LibraryClasses.AARCH64] # @@ -22,4 +24,4 @@ [Components.X64] ManageabilityPkg/Universal/IpmiProtocol/Dxe/IpmiProtocolDxe.inf ManageabilityPkg/Universal/IpmiProtocol/Smm/IpmiProtocolSmm.inf - + ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransferDxe.inf diff --git a/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBl= obTransferDxe.inf b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe= /IpmiBlobTransferDxe.inf new file mode 100644 index 0000000000..28e9d293c1 --- /dev/null +++ b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTrans= ferDxe.inf @@ -0,0 +1,39 @@ +## @file +# IPMI Blob Transfer Protocol DXE Driver. +# +# Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights re= served. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +[Defines] + INF_VERSION =3D 0x00010005 + BASE_NAME =3D IpmiBlobTransferDxe + FILE_GUID =3D 6357c804-78bb-4b0c-abdf-c75df942f319 + MODULE_TYPE =3D DXE_DRIVER + VERSION_STRING =3D 1.0 + ENTRY_POINT =3D IpmiBlobTransferDxeDriverEntryPoint + +[Sources.common] + IpmiBlobTransferDxe.c + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + IpmiLib + MemoryAllocationLib + PcdLib + UefiBootServicesTableLib + UefiDriverEntryPoint + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ManageabilityPkg/ManageabilityPkg.dec + +[Protocols] + gEdkiiIpmiBlobTransferProtocolGuid + +[Depex] + TRUE diff --git a/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTe= st/IpmiBlobTransferTestUnitTestsHost.inf b/Features/ManageabilityPkg/Univer= sal/IpmiBlobTransferDxe/UnitTest/IpmiBlobTransferTestUnitTestsHost.inf new file mode 100644 index 0000000000..1f071bbadc --- /dev/null +++ b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/Ipmi= BlobTransferTestUnitTestsHost.inf @@ -0,0 +1,40 @@ +## @file +# Unit tests of the Ipmi blob transfer driver that are run from a host env= ironment. +# +# Copyright (c) 2020-2023, NVIDIA CORPORATION & AFFILIATES. All rights res= erved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION =3D 0x00010006 + BASE_NAME =3D IpmiBlobTransferDxeUnitTestsHost + FILE_GUID =3D 1f5d4095-ea52-432c-b078-86097fef6004 + MODULE_TYPE =3D HOST_APPLICATION + VERSION_STRING =3D 1.0 + +# +# The following information is for reference only +# and not required by the build tools. +# +# VALID_ARCHITECTURES =3D X64 +# + +[Sources] + IpmiBlobTransferTestUnitTests.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ManageabilityPkg/ManageabilityPkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + UnitTestLib + IpmiLib + +[Protocols] + gEdkiiIpmiBlobTransferProtocolGuid diff --git a/Features/ManageabilityPkg/Include/Protocol/IpmiBlobTransfer.h = b/Features/ManageabilityPkg/Include/Protocol/IpmiBlobTransfer.h new file mode 100644 index 0000000000..8ea71d8816 --- /dev/null +++ b/Features/ManageabilityPkg/Include/Protocol/IpmiBlobTransfer.h @@ -0,0 +1,136 @@ +/** @file + + IPMI Blob Transfer driver + + Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights res= erved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include +#include +#include + +#define IPMI_NETFN_OEM 0x2E +#define IPMI_OEM_BLOB_TRANSFER_CMD 0x80 +#define IPMI_OEM_BLOB_MAX_DATA_PER_PACKET 64 + +#define BLOB_TRANSFER_STAT_OPEN_R BIT0 +#define BLOB_TRANSFER_STAT_OPEN_W BIT1 +#define BLOB_TRANSFER_STAT_COMMITING BIT2 +#define BLOB_TRANSFER_STAT_COMMITTED BIT3 +#define BLOB_TRANSFER_STAT_COMMIT_ERROR BIT4 +// Bits 5-7 are reserved +// Bits 8-15 are blob-specific definitions + +// +// Blob Transfer Function Prototypes +// +typedef +EFI_STATUS +(EFIAPI *IPMI_BLOB_TRANSFER_PROTOCOL_GET_COUNT)( + OUT UINT32 *Count + ); + +typedef +EFI_STATUS +(EFIAPI *IPMI_BLOB_TRANSFER_PROTOCOL_ENUMERATE)( + IN UINT32 BlobIndex, + OUT CHAR8 *BlobId + ); + +typedef +EFI_STATUS +(EFIAPI *IPMI_BLOB_TRANSFER_PROTOCOL_OPEN)( + IN CHAR8 *BlobId, + IN UINT16 Flags, + OUT UINT16 *SessionId + ); + +typedef +EFI_STATUS +(EFIAPI *IPMI_BLOB_TRANSFER_PROTOCOL_READ)( + IN UINT16 SessionId, + IN UINT32 Offset, + IN UINT32 RequestedSize, + OUT UINT8 *Data + ); + +typedef +EFI_STATUS +(EFIAPI *IPMI_BLOB_TRANSFER_PROTOCOL_WRITE)( + IN UINT16 SessionId, + IN UINT32 Offset, + IN UINT8 *Data, + IN UINT32 WriteLength + ); + +typedef +EFI_STATUS +(EFIAPI *IPMI_BLOB_TRANSFER_PROTOCOL_COMMIT)( + IN UINT16 SessionId, + IN UINT8 CommitDataLength, + IN UINT8 *CommitData + ); + +typedef +EFI_STATUS +(EFIAPI *IPMI_BLOB_TRANSFER_PROTOCOL_CLOSE)( + IN UINT16 SessionId + ); + +typedef +EFI_STATUS +(EFIAPI *IPMI_BLOB_TRANSFER_PROTOCOL_DELETE)( + IN CHAR8 *BlobId + ); + +typedef +EFI_STATUS +(EFIAPI *IPMI_BLOB_TRANSFER_PROTOCOL_STAT)( + IN CHAR8 *BlobId, + OUT UINT16 *BlobState, + OUT UINT32 *Size, + OUT UINT8 *MetadataLength, + OUT UINT8 *Metadata + ); + +typedef +EFI_STATUS +(EFIAPI *IPMI_BLOB_TRANSFER_PROTOCOL_SESSION_STAT)( + IN UINT16 SessionId, + OUT UINT16 *BlobState, + OUT UINT32 *Size, + OUT UINT8 *MetadataLength, + OUT UINT8 *Metadata + ); + +typedef +EFI_STATUS +(EFIAPI *IPMI_BLOB_TRANSFER_PROTOCOL_WRITE_META)( + IN UINT16 SessionId, + IN UINT32 Offset, + IN UINT8 *Data, + IN UINT32 WriteLength + ); + +// +// Structure of IPMI_BLOB_TRANSFER_PROTOCOL +// +struct _IPMI_BLOB_TRANSFER_PROTOCOL { + IPMI_BLOB_TRANSFER_PROTOCOL_GET_COUNT BlobGetCount; + IPMI_BLOB_TRANSFER_PROTOCOL_ENUMERATE BlobEnumerate; + IPMI_BLOB_TRANSFER_PROTOCOL_OPEN BlobOpen; + IPMI_BLOB_TRANSFER_PROTOCOL_READ BlobRead; + IPMI_BLOB_TRANSFER_PROTOCOL_WRITE BlobWrite; + IPMI_BLOB_TRANSFER_PROTOCOL_COMMIT BlobCommit; + IPMI_BLOB_TRANSFER_PROTOCOL_CLOSE BlobClose; + IPMI_BLOB_TRANSFER_PROTOCOL_DELETE BlobDelete; + IPMI_BLOB_TRANSFER_PROTOCOL_STAT BlobStat; + IPMI_BLOB_TRANSFER_PROTOCOL_SESSION_STAT BlobSessionStat; + IPMI_BLOB_TRANSFER_PROTOCOL_WRITE_META BlobWriteMeta; +}; + +typedef struct _IPMI_BLOB_TRANSFER_PROTOCOL IPMI_BLOB_TRANSFER_PROTOCOL; + +extern EFI_GUID gEdkiiIpmiBlobTransferProtocolGuid; diff --git a/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/Intern= alIpmiBlobTransfer.h b/Features/ManageabilityPkg/Universal/IpmiBlobTransfer= Dxe/InternalIpmiBlobTransfer.h new file mode 100644 index 0000000000..14f0dc02bc --- /dev/null +++ b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/InternalIpmiB= lobTransfer.h @@ -0,0 +1,363 @@ +/** @file + + Headers for IPMI Blob Transfer driver + + Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights res= erved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + +#define PROTOCOL_RESPONSE_OVERHEAD (4 * sizeof(UINT8)) // 1 byte comp= letion code + 3 bytes OEN + +// Subcommands for this protocol +typedef enum { + IpmiBlobTransferSubcommandGetCount =3D 0, + IpmiBlobTransferSubcommandEnumerate, + IpmiBlobTransferSubcommandOpen, + IpmiBlobTransferSubcommandRead, + IpmiBlobTransferSubcommandWrite, + IpmiBlobTransferSubcommandCommit, + IpmiBlobTransferSubcommandClose, + IpmiBlobTransferSubcommandDelete, + IpmiBlobTransferSubcommandStat, + IpmiBlobTransferSubcommandSessionStat, + IpmiBlobTransferSubcommandWriteMeta, +} IPMI_BLOB_TRANSFER_SUBCOMMANDS; + +#pragma pack(1) + +typedef struct { + UINT8 OEN[3]; + UINT8 SubCommand; +} IPMI_BLOB_TRANSFER_HEADER; + +// +// Command 0 - BmcBlobGetCount +// The BmcBlobGetCount command expects to receive an empty body. +// The BMC will return the number of enumerable blobs +// +typedef struct { + UINT32 BlobCount; +} IPMI_BLOB_TRANSFER_GET_COUNT_RESPONSE; + +// +// Command 1 - BmcBlobEnumerate +// The BmcBlobEnumerate command expects to receive a body of: +// +typedef struct { + UINT32 BlobIndex; // 0-based index of blob to receive +} IPMI_BLOB_TRANSFER_BLOB_ENUMERATE_SEND_DATA; + +typedef struct { + CHAR8 BlobId[IPMI_OEM_BLOB_MAX_DATA_PER_PACKET]; +} IPMI_BLOB_TRANSFER_BLOB_ENUMERATE_RESPONSE; + +// +// Command 2 - BmcBlobOpen +// The BmcBlobOpen command expects to receive a body of: +// +typedef struct { + UINT16 Flags; + CHAR8 BlobId[IPMI_OEM_BLOB_MAX_DATA_PER_PACKET]; +} IPMI_BLOB_TRANSFER_BLOB_OPEN_SEND_DATA; + +#define BLOB_OPEN_FLAG_READ 0 +#define BLOB_OPEN_FLAG_WRITE 1 +// Bits 2-7 are reserved +// Bits 8-15 are blob-specific definitions + +typedef struct { + UINT16 SessionId; +} IPMI_BLOB_TRANSFER_BLOB_OPEN_RESPONSE; + +// +// Command 3 - BmcBlobRead +// The BmcBlobRead command expects to receive a body of: +// +typedef struct { + UINT16 SessionId; // Returned from BlobOpen + UINT32 Offset; + UINT32 RequestedSize; +} IPMI_BLOB_TRANSFER_BLOB_READ_SEND_DATA; + +typedef struct { + UINT8 Data[IPMI_OEM_BLOB_MAX_DATA_PER_PACKET]; +} IPMI_BLOB_TRANSFER_BLOB_READ_RESPONSE; + +// +// Command 4 - BmcBlobWrite +// The BmcBlobWrite command expects to receive a body of: +// +typedef struct { + UINT16 SessionId; // Returned from BlobOpen + UINT32 Offset; + UINT8 Data[IPMI_OEM_BLOB_MAX_DATA_PER_PACKET]; +} IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA; + +// +// Command 5 - BmcBlobCommit +// The BmcBlobCommit command expects to receive a body of: +// +typedef struct { + UINT16 SessionId; // Returned from BlobOpen + UINT8 CommitDataLength; + UINT8 CommitData[IPMI_OEM_BLOB_MAX_DATA_PER_PACKET]; +} IPMI_BLOB_TRANSFER_BLOB_COMMIT_SEND_DATA; + +// +// Command 6 - BmcBlobClose +// The BmcBlobClose command expects to receive a body of: +// +typedef struct { + UINT16 SessionId; // Returned from BlobOpen +} IPMI_BLOB_TRANSFER_BLOB_CLOSE_SEND_DATA; + +// +// Command 7 - BmcBlobDelete +// NOTE: This command will fail if there are open sessions for this blob +// The BmcBlobDelete command expects to receive a body of: +// +typedef struct { + CHAR8 BlobId[IPMI_OEM_BLOB_MAX_DATA_PER_PACKET]; +} IPMI_BLOB_TRANSFER_BLOB_DELETE_SEND_DATA; + +// +// Command 8 - BmcBlobStat +// This command returns statistics about a blob. +// This command expects to receive a body of: +// +typedef struct { + CHAR8 BlobId[IPMI_OEM_BLOB_MAX_DATA_PER_PACKET]; +} IPMI_BLOB_TRANSFER_BLOB_STAT_SEND_DATA; + +typedef struct { + UINT16 BlobState; + UINT32 Size; // Size in bytes of the blob + UINT8 MetaDataLen; + UINT8 MetaData[IPMI_OEM_BLOB_MAX_DATA_PER_PACKET]; +} IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE; + +// +// Command 9 - BmcBlobSessionStat +// Returns same data as BmcBlobState expect for a session, not a blob +// This command expects to receive a body of: +// +typedef struct { + UINT16 SessionId; +} IPMI_BLOB_TRANSFER_BLOB_SESSION_STAT_SEND_DATA; + +typedef struct { + UINT16 BlobState; + UINT32 Size; // Size in bytes of the blob + UINT8 MetaDataLen; + UINT8 MetaData[IPMI_OEM_BLOB_MAX_DATA_PER_PACKET]; +} IPMI_BLOB_TRANSFER_BLOB_SESSION_STAT_RESPONSE; + +// +// Command 10 - BmcBlobWriteMeta +// The BmcBlobWriteMeta command expects to receive a body of: +// +typedef struct { + UINT16 SessionId; + UINT32 Offset; + UINT8 Data[IPMI_OEM_BLOB_MAX_DATA_PER_PACKET]; +} IPMI_BLOB_TRANSFER_BLOB_WRITE_META_SEND_DATA; + +#define IPMI_BLOB_TRANSFER_BLOB_WRITE_META_RESPONSE NULL + +#pragma pack() + +/** + Calculate CRC-16-CCITT with poly of 0x1021 + + @param[in] Data The target data. + @param[in] DataSize The target data size. + + @return UINT16 The CRC16 value. + +**/ +UINT16 +CalculateCrc16 ( + IN UINT8 *Data, + IN UINTN DataSize + ); + +EFI_STATUS +IpmiBlobTransferSendIpmi ( + IN UINT8 SubCommand, + IN UINT8 *SendData, + IN UINT32 SendDataSize, + OUT UINT8 *ResponseData, + OUT UINT32 *ResponseDataSize + ); + +/** + @param[out] Count The number of active blobs + + @retval EFI_SUCCESS Successfully retrieved the number of acti= ve blobs. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferGetCount ( + OUT UINT32 *Count + ); + +/** + @param[in] BlobIndex The 0-based Index of the blob to enum= erate + @param[out] BlobId The ID of the blob + + @retval EFI_SUCCESS Successfully enumerated the blob. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferEnumerate ( + IN UINT32 BlobIndex, + OUT CHAR8 *BlobId + ); + +/** + @param[in] BlobId The ID of the blob to open + @param[in] Flags Flags to control how the blob is open= ed + @param[out] SessionId A unique session identifier + + @retval EFI_SUCCESS Successfully opened the blob. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferOpen ( + IN CHAR8 *BlobId, + IN UINT16 Flags, + OUT UINT16 *SessionId + ); + +/** + @param[in] SessionId The session ID returned from a call t= o BlobOpen + @param[in] Offset The offset of the blob from which to = start reading + @param[in] RequestedSize The length of data to read + @param[out] Data Data read from the blob + + @retval EFI_SUCCESS Successfully read from the blob. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferRead ( + IN UINT16 SessionId, + IN UINT32 Offset, + IN UINT32 RequestedSize, + OUT UINT8 *Data + ); + +/** + @param[in] SessionId The session ID returned from a call t= o BlobOpen + @param[in] Offset The offset of the blob from which to = start writing + @param[in] Data A pointer to the data to write + + @retval EFI_SUCCESS Successfully wrote to the blob. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferWrite ( + IN UINT16 SessionId, + IN UINT32 Offset, + IN UINT8 *Data, + IN UINT32 WriteLength + ); + +/** + @param[in] SessionId The session ID returned from a call = to BlobOpen + @param[in] CommitDataLength The length of data to commit to the = blob + @param[in] CommitData A pointer to the data to commit + + @retval EFI_SUCCESS Successful commit to the blob. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferCommit ( + IN UINT16 SessionId, + IN UINT8 CommitDataLength, + IN UINT8 *CommitData + ); + +/** + @param[in] SessionId The session ID returned from a call t= o BlobOpen + + @retval EFI_SUCCESS The blob was closed. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferClose ( + IN UINT16 SessionId + ); + +/** + @param[in] BlobId The BlobId to be deleted + + @retval EFI_SUCCESS The blob was deleted. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferDelete ( + IN CHAR8 *BlobId + ); + +/** + @param[in] BlobId The Blob ID to gather statistics for + @param[out] BlobState The current state of the blob + @param[out] Size Size in bytes of the blob + @param[out] MetadataLength Length of the optional metadata + @param[out] Metadata Optional blob-specific metadata + + @retval EFI_SUCCESS The blob statistics were successfully= gathered. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferStat ( + IN CHAR8 *BlobId, + OUT UINT16 *BlobState, + OUT UINT32 *Size, + OUT UINT8 *MetadataLength, + OUT UINT8 *Metadata + ); + +/** + @param[in] SessionId The ID of the session to gather stati= stics for + @param[out] BlobState The current state of the blob + @param[out] Size Size in bytes of the blob + @param[out] MetadataLength Length of the optional metadata + @param[out] Metadata Optional blob-specific metadata + + @retval EFI_SUCCESS The blob statistics were successfully= gathered. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferSessionStat ( + IN UINT16 SessionId, + OUT UINT16 *BlobState, + OUT UINT32 *Size, + OUT UINT8 *MetadataLength, + OUT UINT8 *Metadata + ); + +/** + @param[in] SessionId The ID of the session to write metada= ta for + @param[in] Offset The offset of the metadata to write to + @param[in] Data The data to write to the metadata + + @retval EFI_SUCCESS The blob metadata was successfully wr= itten. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferWriteMeta ( + IN UINT16 SessionId, + IN UINT32 Offset, + IN UINT8 *Data, + IN UINT32 WriteLength + ); diff --git a/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBl= obTransferDxe.c b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/I= pmiBlobTransferDxe.c new file mode 100644 index 0000000000..9e663289d5 --- /dev/null +++ b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTrans= ferDxe.c @@ -0,0 +1,799 @@ +/** @file + + IPMI Blob Transfer driver + + Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights res= erved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include + +#include "InternalIpmiBlobTransfer.h" + +#define BLOB_TRANSFER_DEBUG 0 + +STATIC CONST IPMI_BLOB_TRANSFER_PROTOCOL mIpmiBlobTransfer =3D { + (IPMI_BLOB_TRANSFER_PROTOCOL_GET_COUNT)*IpmiBlobTransferGetCount, + (IPMI_BLOB_TRANSFER_PROTOCOL_ENUMERATE)*IpmiBlobTransferEnumerate, + (IPMI_BLOB_TRANSFER_PROTOCOL_OPEN)*IpmiBlobTransferOpen, + (IPMI_BLOB_TRANSFER_PROTOCOL_READ)*IpmiBlobTransferRead, + (IPMI_BLOB_TRANSFER_PROTOCOL_WRITE)*IpmiBlobTransferWrite, + (IPMI_BLOB_TRANSFER_PROTOCOL_COMMIT)*IpmiBlobTransferCommit, + (IPMI_BLOB_TRANSFER_PROTOCOL_CLOSE)*IpmiBlobTransferClose, + (IPMI_BLOB_TRANSFER_PROTOCOL_DELETE)*IpmiBlobTransferDelete, + (IPMI_BLOB_TRANSFER_PROTOCOL_STAT)*IpmiBlobTransferStat, + (IPMI_BLOB_TRANSFER_PROTOCOL_SESSION_STAT)*IpmiBlobTransferSessionStat, + (IPMI_BLOB_TRANSFER_PROTOCOL_WRITE_META)*IpmiBlobTransferWriteMeta +}; + +const UINT8 OpenBmcOen[] =3D { 0xCF, 0xC2, 0x00 }; // OpenBMC OE= N code in little endian format + +/** + Calculate CRC-16-CCITT with poly of 0x1021 + + @param[in] Data The target data. + @param[in] DataSize The target data size. + + @return UINT16 The CRC16 value. + +**/ +UINT16 +CalculateCrc16 ( + IN UINT8 *Data, + IN UINTN DataSize + ) +{ + UINTN Index; + UINTN BitIndex; + UINT16 Crc =3D 0xFFFF; + UINT16 Poly =3D 0x1021; + BOOLEAN XorFlag =3D FALSE; + + for (Index =3D 0; Index < (DataSize + 2); ++Index) { + for (BitIndex =3D 0; BitIndex < 8; ++BitIndex) { + XorFlag =3D (Crc & 0x8000) ? TRUE : FALSE; + Crc <<=3D 1; + if ((Index < DataSize) && (Data[Index] & (1 << (7 - BitIndex)))) { + Crc++; + } + + if (XorFlag =3D=3D TRUE) { + Crc ^=3D Poly; + } + } + } + + #if BLOB_TRANSFER_DEBUG + DEBUG ((DEBUG_INFO, "%a: CRC-16-CCITT %x\n", __FUNCTION__, Crc)); + #endif + + return Crc; +} + +EFI_STATUS +IpmiBlobTransferSendIpmi ( + IN UINT8 SubCommand, + IN UINT8 *SendData, + IN UINT32 SendDataSize, + OUT UINT8 *ResponseData, + OUT UINT32 *ResponseDataSize + ) +{ + EFI_STATUS Status; + UINT8 CompletionCode; + UINT16 Crc; + UINT8 Oen[3]; + UINT8 *IpmiSendData; + UINT32 IpmiSendDataSize; + UINT8 *IpmiResponseData; + UINT8 *ModifiedResponseData; + UINT32 IpmiResponseDataSize; + IPMI_BLOB_TRANSFER_HEADER Header; + + Crc =3D 0; + + // + // Prepend the proper header to the SendData + // + IpmiSendDataSize =3D (sizeof (IPMI_BLOB_TRANSFER_HEADER)); + if (SendDataSize) { + IpmiSendDataSize +=3D sizeof (Crc) + (sizeof (UINT8) * SendDataSize); + } + + IpmiSendData =3D AllocateZeroPool (IpmiSendDataSize); + if (IpmiSendData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Header.OEN[0] =3D OpenBmcOen[0]; + Header.OEN[1] =3D OpenBmcOen[1]; + Header.OEN[2] =3D OpenBmcOen[2]; + Header.SubCommand =3D SubCommand; + CopyMem (IpmiSendData, &Header, sizeof (IPMI_BLOB_TRANSFER_HEADER)); + if (SendDataSize) { + // + // Calculate the Crc of the send data + // + Crc =3D CalculateCrc16 (SendData, SendDataSize); + CopyMem (IpmiSendData + sizeof (IPMI_BLOB_TRANSFER_HEADER), &Crc, size= of (UINT16)); + CopyMem (IpmiSendData + sizeof (IPMI_BLOB_TRANSFER_HEADER) + sizeof (U= INT16), SendData, SendDataSize); + } + + #if BLOB_TRANSFER_DEBUG + DEBUG ((DEBUG_INFO, "%a: Inputs:\n", __FUNCTION__)); + DEBUG ((DEBUG_INFO, "%a: SendDataSize: %02x\nData: ", __FUNCTION__, Send= DataSize)); + UINT8 i; + for (i =3D 0; i < SendDataSize; i++) { + DEBUG ((DEBUG_INFO, "%02x", *((UINT8 *)SendData + i))); + } + + DEBUG ((DEBUG_INFO, "\n")); + DEBUG ((DEBUG_INFO, "%a: IpmiSendDataSize: %02x\nData: ", __FUNCTION__, = IpmiSendDataSize)); + for (i =3D 0; i < IpmiSendDataSize; i++) { + DEBUG ((DEBUG_INFO, "%02x", *((UINT8 *)IpmiSendData + i))); + } + + DEBUG ((DEBUG_INFO, "\n")); + #endif + + IpmiResponseDataSize =3D (*ResponseDataSize + PROTOCOL_RESPONSE_OVERHEAD= ); + // + // If expecting data to be returned, we have to also account for the 16 = bit CRC + // + if (*ResponseDataSize) { + IpmiResponseDataSize +=3D sizeof (Crc); + } + + IpmiResponseData =3D AllocateZeroPool (IpmiResponseDataSize); + if (IpmiResponseData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status =3D IpmiSubmitCommand ( + IPMI_NETFN_OEM, + IPMI_OEM_BLOB_TRANSFER_CMD, + (VOID *)IpmiSendData, + IpmiSendDataSize, + (VOID *)IpmiResponseData, + &IpmiResponseDataSize + ); + + FreePool (IpmiSendData); + ModifiedResponseData =3D IpmiResponseData; + + #if BLOB_TRANSFER_DEBUG + DEBUG ((DEBUG_INFO, "%a: IPMI Response:\n", __FUNCTION__)); + DEBUG ((DEBUG_INFO, "%a: ResponseDataSize: %02x\nData: ", __FUNCTION__, = IpmiResponseDataSize)); + for (i =3D 0; i < IpmiResponseDataSize; i++) { + DEBUG ((DEBUG_INFO, "%02x", *(ModifiedResponseData + i))); + } + + DEBUG ((DEBUG_INFO, "\n")); + #endif + + if (EFI_ERROR (Status)) { + return Status; + } + + CompletionCode =3D *ModifiedResponseData; + if (CompletionCode !=3D IPMI_COMP_CODE_NORMAL) { + DEBUG ((DEBUG_ERROR, "%a: Returning because CompletionCode =3D 0x%x\n"= , __FUNCTION__, CompletionCode)); + FreePool (IpmiResponseData); + return EFI_PROTOCOL_ERROR; + } + + // Strip completion code, we are done with it + ModifiedResponseData =3D ModifiedResponseData + sizeof (CompletionCode); + IpmiResponseDataSize -=3D sizeof (CompletionCode); + + // Check OEN code and verify it matches the OpenBMC OEN + CopyMem (Oen, ModifiedResponseData, sizeof (OpenBmcOen)); + if (CompareMem (Oen, OpenBmcOen, sizeof (OpenBmcOen)) !=3D 0) { + FreePool (IpmiResponseData); + return EFI_PROTOCOL_ERROR; + } + + if (IpmiResponseDataSize =3D=3D sizeof (OpenBmcOen)) { + // + // In this case, there was no response data sent. This is not an error. + // Some messages do not require a response. + // + *ResponseDataSize =3D 0; + FreePool (IpmiResponseData); + return Status; + // Now we need to validate the CRC then send the Response body back + } else { + // Strip the OEN, we are done with it now + ModifiedResponseData =3D ModifiedResponseData + sizeof (Oen); + IpmiResponseDataSize -=3D sizeof (Oen); + // Then validate the Crc + CopyMem (&Crc, ModifiedResponseData, sizeof (Crc)); + ModifiedResponseData =3D ModifiedResponseData + sizeof (Crc); + IpmiResponseDataSize -=3D sizeof (Crc); + + if (Crc =3D=3D CalculateCrc16 (ModifiedResponseData, IpmiResponseDataS= ize)) { + CopyMem (ResponseData, ModifiedResponseData, IpmiResponseDataSize); + CopyMem (ResponseDataSize, &IpmiResponseDataSize, sizeof (IpmiRespon= seDataSize)); + FreePool (IpmiResponseData); + return EFI_SUCCESS; + } else { + FreePool (IpmiResponseData); + return EFI_CRC_ERROR; + } + } +} + +/** + @param[out] Count The number of active blobs + + @retval EFI_SUCCESS The command byte stream was successfully = submit to the device and a response was successfully received. + @retval EFI_PROTOCOL_ERROR The Ipmi command failed + @retval EFI_CRC_ERROR The Ipmi command returned a bad checksum +**/ +EFI_STATUS +IpmiBlobTransferGetCount ( + OUT UINT32 *Count + ) +{ + EFI_STATUS Status; + UINT8 *ResponseData; + UINT32 ResponseDataSize; + + ResponseDataSize =3D sizeof (IPMI_BLOB_TRANSFER_GET_COUNT_RESPONSE); + ResponseData =3D AllocateZeroPool (ResponseDataSize); + if (ResponseData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandGetCount,= NULL, 0, (UINT8 *)ResponseData, &ResponseDataSize); + if (!EFI_ERROR (Status)) { + *Count =3D ((IPMI_BLOB_TRANSFER_GET_COUNT_RESPONSE *)ResponseData)->Bl= obCount; + } + + FreePool (ResponseData); + return Status; +} + +/** + @param[in] BlobIndex The 0-based Index of the blob to enum= erate + @param[out] BlobId The ID of the blob + + @retval EFI_SUCCESS Successfully enumerated the blob. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferEnumerate ( + IN UINT32 BlobIndex, + OUT CHAR8 *BlobId + ) +{ + EFI_STATUS Status; + + UINT8 *SendData; + UINT8 *ResponseData; + UINT32 SendDataSize; + UINT32 ResponseDataSize; + + if (BlobId =3D=3D NULL) { + ASSERT (FALSE); + return EFI_ABORTED; + } + + ResponseDataSize =3D sizeof (IPMI_BLOB_TRANSFER_BLOB_ENUMERATE_RESPONSE); + ResponseData =3D AllocateZeroPool (ResponseDataSize); + if (ResponseData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Format send data + // + SendDataSize =3D sizeof (IPMI_BLOB_TRANSFER_BLOB_ENUMERATE_SEND_DATA); + SendData =3D AllocateZeroPool (SendDataSize); + if (SendData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ((IPMI_BLOB_TRANSFER_BLOB_ENUMERATE_SEND_DATA *)SendData)->BlobIndex =3D= BlobIndex; + + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandEnumerate= , SendData, SendDataSize, (UINT8 *)ResponseData, &ResponseDataSize); + if (!EFI_ERROR (Status)) { + AsciiStrCpyS (BlobId, ResponseDataSize, (CHAR8 *)ResponseData); + } + + FreePool (ResponseData); + return Status; +} + +/** + @param[in] BlobId The ID of the blob to open + @param[in] Flags Flags to control how the blob is open= ed + @param[out] SessionId A unique session identifier + + @retval EFI_SUCCESS Successfully opened the blob. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferOpen ( + IN CHAR8 *BlobId, + IN UINT16 Flags, + OUT UINT16 *SessionId + ) +{ + EFI_STATUS Status; + UINT8 *SendData; + UINT8 *ResponseData; + UINT32 SendDataSize; + UINT32 ResponseDataSize; + CHAR8 *BlobSearch; + UINT32 NumBlobs; + UINT16 Index; + BOOLEAN BlobFound; + + // + // Before opening a blob, need to check if it exists + // + Status =3D IpmiBlobTransferGetCount (&NumBlobs); + if (EFI_ERROR (Status) || (NumBlobs =3D=3D 0)) { + if (Status =3D=3D EFI_UNSUPPORTED) { + return Status; + } + + DEBUG ((DEBUG_ERROR, "%a: Could not find any blobs: %r\n", __FUNCTION_= _, Status)); + return EFI_NOT_FOUND; + } + + BlobSearch =3D AllocateZeroPool (sizeof (CHAR8) * IPMI_OEM_BLOB_MAX_DATA= _PER_PACKET); + if (BlobSearch =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + BlobFound =3D FALSE; + for (Index =3D 0; Index < NumBlobs; Index++) { + Status =3D IpmiBlobTransferEnumerate (Index, BlobSearch); + if ((!EFI_ERROR (Status)) && (AsciiStrCmp (BlobSearch, BlobId) =3D=3D = 0)) { + BlobFound =3D TRUE; + break; + } else { + continue; + } + } + + if (!BlobFound) { + DEBUG ((DEBUG_ERROR, "%a: Could not find a blob that matches %s\n", __= FUNCTION__, BlobId)); + FreePool (BlobSearch); + return EFI_NOT_FOUND; + } + + ResponseDataSize =3D sizeof (IPMI_BLOB_TRANSFER_BLOB_OPEN_RESPONSE); + ResponseData =3D AllocateZeroPool (ResponseDataSize); + if (ResponseData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Format send data + // + SendDataSize =3D sizeof (((IPMI_BLOB_TRANSFER_BLOB_OPEN_SEND_DATA *)Send= Data)->Flags) + ((AsciiStrLen (BlobId)) * sizeof (CHAR8)) + sizeof (CHAR8); + SendData =3D AllocateZeroPool (SendDataSize); + if (SendData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + AsciiStrCpyS (((IPMI_BLOB_TRANSFER_BLOB_OPEN_SEND_DATA *)SendData)->Blob= Id, AsciiStrSize (BlobId) / sizeof (CHAR8), BlobId); + ((IPMI_BLOB_TRANSFER_BLOB_OPEN_SEND_DATA *)SendData)->Flags =3D Flags; + // append null char to SendData + SendData[SendDataSize-1] =3D 0; + + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandOpen, Sen= dData, SendDataSize, (UINT8 *)ResponseData, &ResponseDataSize); + if (!EFI_ERROR (Status)) { + *SessionId =3D ((IPMI_BLOB_TRANSFER_BLOB_OPEN_RESPONSE *)ResponseData)= ->SessionId; + } + + FreePool (ResponseData); + FreePool (SendData); + FreePool (BlobSearch); + return Status; +} + +/** + @param[in] SessionId The session ID returned from a call t= o BlobOpen + @param[in] Offset The offset of the blob from which to = start reading + @param[in] RequestedSize The length of data to read + @param[out] Data Data read from the blob + + @retval EFI_SUCCESS Successfully read from the blob. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferRead ( + IN UINT16 SessionId, + IN UINT32 Offset, + IN UINT32 RequestedSize, + OUT UINT8 *Data + ) +{ + EFI_STATUS Status; + UINT8 *SendData; + UINT8 *ResponseData; + UINT32 SendDataSize; + UINT32 ResponseDataSize; + + if (Data =3D=3D NULL) { + ASSERT (FALSE); + return EFI_ABORTED; + } + + ResponseDataSize =3D RequestedSize * sizeof (UINT8); + ResponseData =3D AllocateZeroPool (ResponseDataSize); + if (ResponseData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Format send data + // + SendDataSize =3D sizeof (IPMI_BLOB_TRANSFER_BLOB_READ_SEND_DATA); + SendData =3D AllocateZeroPool (SendDataSize); + if (SendData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ((IPMI_BLOB_TRANSFER_BLOB_READ_SEND_DATA *)SendData)->SessionId =3D = SessionId; + ((IPMI_BLOB_TRANSFER_BLOB_READ_SEND_DATA *)SendData)->Offset =3D = Offset; + ((IPMI_BLOB_TRANSFER_BLOB_READ_SEND_DATA *)SendData)->RequestedSize =3D = RequestedSize; + + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandRead, Sen= dData, SendDataSize, (UINT8 *)ResponseData, &ResponseDataSize); + if (!EFI_ERROR (Status)) { + CopyMem (Data, ((IPMI_BLOB_TRANSFER_BLOB_READ_RESPONSE *)ResponseData)= ->Data, ResponseDataSize * sizeof (UINT8)); + } + + FreePool (ResponseData); + FreePool (SendData); + return Status; +} + +/** + @param[in] SessionId The session ID returned from a call t= o BlobOpen + @param[in] Offset The offset of the blob from which to = start writing + @param[in] Data A pointer to the data to write + + @retval EFI_SUCCESS Successfully wrote to the blob. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferWrite ( + IN UINT16 SessionId, + IN UINT32 Offset, + IN UINT8 *Data, + IN UINT32 WriteLength + ) +{ + EFI_STATUS Status; + UINT8 *SendData; + UINT32 SendDataSize; + UINT32 ResponseDataSize; + + // + // Format send data + // + SendDataSize =3D sizeof (SessionId) + sizeof (Offset) + WriteLength; + SendData =3D AllocateZeroPool (SendDataSize); + if (SendData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ((IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA *)SendData)->SessionId =3D Ses= sionId; + ((IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA *)SendData)->Offset =3D Off= set; + CopyMem (((IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA *)SendData)->Data, Da= ta, sizeof (UINT8) * WriteLength); + + ResponseDataSize =3D 0; + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcomman= dWrite, SendData, SendDataSize, NULL, &ResponseDataSize); + + FreePool (SendData); + return Status; +} + +/** + @param[in] SessionId The session ID returned from a call = to BlobOpen + @param[in] CommitDataLength The length of data to commit to the = blob + @param[in] CommitData A pointer to the data to commit + + @retval EFI_SUCCESS Successful commit to the blob. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferCommit ( + IN UINT16 SessionId, + IN UINT8 CommitDataLength, + IN UINT8 *CommitData + ) +{ + EFI_STATUS Status; + UINT8 *SendData; + UINT32 SendDataSize; + UINT32 ResponseDataSize; + + // + // Format send data + // + SendDataSize =3D sizeof (SessionId) + sizeof (CommitDataLength) + Commit= DataLength; + SendData =3D AllocateZeroPool (SendDataSize); + if (SendData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ((IPMI_BLOB_TRANSFER_BLOB_COMMIT_SEND_DATA *)SendData)->SessionId = =3D SessionId; + ((IPMI_BLOB_TRANSFER_BLOB_COMMIT_SEND_DATA *)SendData)->CommitDataLength= =3D CommitDataLength; + CopyMem (((IPMI_BLOB_TRANSFER_BLOB_COMMIT_SEND_DATA *)SendData)->CommitD= ata, CommitData, sizeof (UINT8) * CommitDataLength); + + ResponseDataSize =3D 0; + + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandCommit, S= endData, SendDataSize, NULL, &ResponseDataSize); + + FreePool (SendData); + return Status; +} + +/** + @param[in] SessionId The session ID returned from a call t= o BlobOpen + + @retval EFI_SUCCESS The blob was closed. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferClose ( + IN UINT16 SessionId + ) +{ + EFI_STATUS Status; + UINT8 *SendData; + UINT32 SendDataSize; + UINT32 ResponseDataSize; + + // + // Format send data + // + SendDataSize =3D sizeof (IPMI_BLOB_TRANSFER_BLOB_CLOSE_SEND_DATA); + SendData =3D AllocateZeroPool (SendDataSize); + if (SendData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ((IPMI_BLOB_TRANSFER_BLOB_CLOSE_SEND_DATA *)SendData)->SessionId =3D Ses= sionId; + + ResponseDataSize =3D 0; + + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandClose, Se= ndData, SendDataSize, NULL, &ResponseDataSize); + + FreePool (SendData); + return Status; +} + +/** + @param[in] BlobId The BlobId to be deleted + + @retval EFI_SUCCESS The blob was deleted. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferDelete ( + IN CHAR8 *BlobId + ) +{ + EFI_STATUS Status; + UINT8 *SendData; + UINT32 SendDataSize; + UINT32 ResponseDataSize; + + // + // Format send data + // + SendDataSize =3D sizeof (IPMI_BLOB_TRANSFER_BLOB_DELETE_SEND_DATA); + SendData =3D AllocateZeroPool (SendDataSize); + if (SendData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + AsciiStrCpyS (((IPMI_BLOB_TRANSFER_BLOB_DELETE_SEND_DATA *)SendData)->Bl= obId, AsciiStrLen (BlobId), BlobId); + + ResponseDataSize =3D 0; + + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandDelete, S= endData, SendDataSize, NULL, &ResponseDataSize); + + FreePool (SendData); + return Status; +} + +/** + @param[in] BlobId The Blob ID to gather statistics for + @param[out] BlobState The current state of the blob + @param[out] Size Size in bytes of the blob + @param[out] MetadataLength Length of the optional metadata + @param[out] Metadata Optional blob-specific metadata + + @retval EFI_SUCCESS The blob statistics were successfully= gathered. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferStat ( + IN CHAR8 *BlobId, + OUT UINT16 *BlobState, + OUT UINT32 *Size, + OUT UINT8 *MetadataLength, + OUT UINT8 *Metadata + ) +{ + EFI_STATUS Status; + UINT8 *SendData; + UINT8 *ResponseData; + UINT32 SendDataSize; + UINT32 ResponseDataSize; + + if (Metadata =3D=3D NULL) { + ASSERT (FALSE); + return EFI_ABORTED; + } + + ResponseDataSize =3D sizeof (IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE); + ResponseData =3D AllocateZeroPool (ResponseDataSize); + if (ResponseData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Format send data + // + SendDataSize =3D sizeof (IPMI_BLOB_TRANSFER_BLOB_STAT_SEND_DATA); + SendData =3D AllocateZeroPool (SendDataSize); + if (SendData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + AsciiStrCpyS (((IPMI_BLOB_TRANSFER_BLOB_STAT_SEND_DATA *)SendData)->Blob= Id, IPMI_OEM_BLOB_MAX_DATA_PER_PACKET, BlobId); + + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandStat, Sen= dData, SendDataSize, (UINT8 *)ResponseData, &ResponseDataSize); + if (!EFI_ERROR (Status)) { + *BlobState =3D ((IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE *)Response= Data)->BlobState; + *Size =3D ((IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE *)Response= Data)->Size; + *MetadataLength =3D ((IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE *)Response= Data)->MetaDataLen; + + CopyMem (&Metadata, &((IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE *)Respons= eData)->MetaData, sizeof (((IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE *)Respons= eData)->MetaData)); + } + + FreePool (ResponseData); + FreePool (SendData); + return Status; +} + +/** + @param[in] SessionId The ID of the session to gather stati= stics for + @param[out] BlobState The current state of the blob + @param[out] Size Size in bytes of the blob + @param[out] MetadataLength Length of the optional metadata + @param[out] Metadata Optional blob-specific metadata + + @retval EFI_SUCCESS The blob statistics were successfully= gathered. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferSessionStat ( + IN UINT16 SessionId, + OUT UINT16 *BlobState, + OUT UINT32 *Size, + OUT UINT8 *MetadataLength, + OUT UINT8 *Metadata + ) +{ + EFI_STATUS Status; + UINT8 *SendData; + UINT8 *ResponseData; + UINT32 SendDataSize; + UINT32 ResponseDataSize; + + if ((BlobState =3D=3D NULL) || (Size =3D=3D NULL) || (MetadataLength =3D= =3D NULL) || (Metadata =3D=3D NULL)) { + ASSERT (FALSE); + return EFI_ABORTED; + } + + ResponseDataSize =3D sizeof (IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE); + ResponseData =3D AllocateZeroPool (ResponseDataSize); + if (ResponseData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Format send data + // + SendDataSize =3D sizeof (IPMI_BLOB_TRANSFER_BLOB_SESSION_STAT_SEND_DATA); + SendData =3D AllocateZeroPool (SendDataSize); + if (SendData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ((IPMI_BLOB_TRANSFER_BLOB_SESSION_STAT_SEND_DATA *)SendData)->SessionId = =3D SessionId; + + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandSessionSt= at, SendData, SendDataSize, (UINT8 *)ResponseData, &ResponseDataSize); + + if (!EFI_ERROR (Status)) { + *BlobState =3D ((IPMI_BLOB_TRANSFER_BLOB_SESSION_STAT_RESPONSE *)= ResponseData)->BlobState; + *Size =3D ((IPMI_BLOB_TRANSFER_BLOB_SESSION_STAT_RESPONSE *)= ResponseData)->Size; + *MetadataLength =3D ((IPMI_BLOB_TRANSFER_BLOB_SESSION_STAT_RESPONSE *)= ResponseData)->MetaDataLen; + + CopyMem (&Metadata, &((IPMI_BLOB_TRANSFER_BLOB_SESSION_STAT_RESPONSE *= )ResponseData)->MetaData, sizeof (((IPMI_BLOB_TRANSFER_BLOB_SESSION_STAT_RE= SPONSE *)ResponseData)->MetaData)); + } + + FreePool (ResponseData); + FreePool (SendData); + return Status; +} + +/** + @param[in] SessionId The ID of the session to write metada= ta for + @param[in] Offset The offset of the metadata to write to + @param[in] Data The data to write to the metadata + + @retval EFI_SUCCESS The blob metadata was successfully wr= itten. + @retval Other An error occurred +**/ +EFI_STATUS +IpmiBlobTransferWriteMeta ( + IN UINT16 SessionId, + IN UINT32 Offset, + IN UINT8 *Data, + IN UINT32 WriteLength + ) +{ + EFI_STATUS Status; + UINT8 *SendData; + UINT32 SendDataSize; + UINT32 ResponseDataSize; + + // + // Format send data + // + SendDataSize =3D sizeof (IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA); + SendData =3D AllocateZeroPool (SendDataSize); + if (SendData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ((IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA *)SendData)->SessionId =3D Ses= sionId; + ((IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA *)SendData)->Offset =3D Off= set; + CopyMem (((IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA *)SendData)->Data, Da= ta, sizeof (UINT8) * WriteLength); + + ResponseDataSize =3D 0; + + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandWriteMeta= , SendData, SendDataSize, NULL, &ResponseDataSize); + + FreePool (SendData); + return Status; +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers includ= ing + both device drivers and bus drivers. + + @param[in] ImageHandle The firmware allocated handle for the UEFI= image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others An unexpected error occurred. + +**/ +EFI_STATUS +EFIAPI +IpmiBlobTransferDxeDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEdkiiIpmiBlobTransferProtocolGuid, + (VOID *)&mIpmiBlobTransfer, + NULL + ); +} diff --git a/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTe= st/IpmiBlobTransferTestUnitTests.c b/Features/ManageabilityPkg/Universal/Ip= miBlobTransferDxe/UnitTest/IpmiBlobTransferTestUnitTests.c new file mode 100644 index 0000000000..f326467922 --- /dev/null +++ b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/Ipmi= BlobTransferTestUnitTests.c @@ -0,0 +1,1113 @@ +/** @file +* +* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +* +* SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIA= TES +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include "../InternalIpmiBlobTransfer.h" + +#define UNIT_TEST_NAME "IPMI Blob Transfer Unit Tests" +#define UNIT_TEST_VERSION "1.0" + +UINT8 InvalidCompletion[] =3D { + 0xC0, // CompletionCode + 0xCF, 0xC2, 0x00, // OpenBMC OEN +}; +#define INVALID_COMPLETION_SIZE 4 * sizeof(UINT8) + +UINT8 NoDataResponse[] =3D { + 0x00, // CompletionCode + 0xCF, 0xC2, 0x00, // OpenBMC OEN +}; +#define NO_DATA_RESPONSE_SIZE 4 * sizeof(UINT8) + +UINT8 BadOenResponse[] =3D { + 0x00, // CompletionCode + 0xFF, 0xC2, 0x00, // Wrong OEN +}; +#define BAD_OEN_RESPONSE_SIZE 4 * sizeof(UINT8) + +UINT8 BadCrcResponse[] =3D { + 0x00, // CompletionCode + 0xCF, 0xC2, 0x00, // OpenBMC OEN + 0x00, 0x00, // CRC + 0x01, 0x00, 0x00, 0x00, // Data +}; +#define BAD_CRC_RESPONSE_SIZE 10 * sizeof(UINT8) + +UINT8 ValidNoDataResponse[] =3D { + 0x00, // CompletionCode + 0xCF, 0xC2, 0x00, // OpenBMC OEN +}; + +#define VALID_NODATA_RESPONSE_SIZE 4 * sizeof(UINT8) + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +GoodCrc ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT8 Data[5] =3D { 0x12, 0x34, 0x56, 0x78, 0x90 }; + UINTN DataSize; + UINT16 Crc; + + DataSize =3D sizeof (Data); + + Crc =3D CalculateCrc16 (Data, DataSize); + + UT_ASSERT_EQUAL (Crc, 0xB928); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +BadCrc ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT8 Data[5] =3D { 0x12, 0x34, 0x56, 0x78, 0x90 }; + UINTN DataSize; + UINT16 Crc; + + DataSize =3D sizeof (Data); + + Crc =3D CalculateCrc16 (Data, DataSize); + + UT_ASSERT_NOT_EQUAL (Crc, 0x3409); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +SendIpmiBadCompletion ( + IN UNIT_TEST_CONTEXT Context + ) +{ + VOID *ResponseData; + UINT32 *ResponseDataSize; + EFI_STATUS Status; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (INVALID_COMPLETION_SI= ZE); + ResponseDataSize =3D (UINT32 *)AllocateZeroPool (sizeof (UINT32)); + CopyMem (MockResponseResults, &InvalidCompletion, INVALID_COMPLETION_SIZ= E); + + MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, INVALID_COMPLETION_= SIZE, EFI_SUCCESS); + + ResponseData =3D (UINT8 *)AllocateZeroPool (*ResponseDataSize); + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandGet= Count, NULL, 0, ResponseData, ResponseDataSize); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_PROTOCOL_ERROR); + FreePool (MockResponseResults); + FreePool (ResponseDataSize); + FreePool (ResponseData); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +SendIpmiNoDataResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + VOID *ResponseData; + UINT32 *ResponseDataSize; + EFI_STATUS Status; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (NO_DATA_RESPONSE_SIZE= ); + ResponseDataSize =3D (UINT32 *)AllocateZeroPool (sizeof (UINT32)); + CopyMem (MockResponseResults, &NoDataResponse, NO_DATA_RESPONSE_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, NO_DATA_= RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + ResponseData =3D (UINT8 *)AllocateZeroPool (sizeof (NoDataResponse)); + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandGet= Count, NULL, 0, ResponseData, ResponseDataSize); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (*ResponseDataSize, 0); + FreePool (MockResponseResults); + FreePool (ResponseDataSize); + FreePool (ResponseData); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +SendIpmiBadOenResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + VOID *ResponseData; + UINT32 *ResponseDataSize; + EFI_STATUS Status; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (BAD_OEN_RESPONSE_SIZE= ); + ResponseDataSize =3D (UINT32 *)AllocateZeroPool (sizeof (UINT32)); + CopyMem (MockResponseResults, &BadOenResponse, BAD_OEN_RESPONSE_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, BAD_OEN_= RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + ResponseData =3D (UINT8 *)AllocateZeroPool (sizeof (BadOenResponse)); + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandGet= Count, NULL, 0, ResponseData, ResponseDataSize); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_PROTOCOL_ERROR); + FreePool (MockResponseResults); + FreePool (ResponseDataSize); + FreePool (ResponseData); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +SendIpmiBadCrcResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + VOID *ResponseData; + UINT32 *ResponseDataSize; + EFI_STATUS Status; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (BAD_CRC_RESPO= NSE_SIZE)); + ResponseDataSize =3D (UINT32 *)AllocateZeroPool (sizeof (UINT32)); + CopyMem (MockResponseResults, &BadCrcResponse, BAD_CRC_RESPONSE_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, BAD_CRC_= RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + ResponseData =3D (UINT8 *)AllocateZeroPool (sizeof (BadCrcResponse)); + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandGet= Count, NULL, 0, ResponseData, ResponseDataSize); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_CRC_ERROR); + FreePool (MockResponseResults); + FreePool (ResponseDataSize); + FreePool (ResponseData); + return UNIT_TEST_PASSED; +} + +UINT8 ValidGetCountResponse[] =3D { + 0x00, // CompletionCode + 0xCF, 0xC2, 0x00, // OpenBMC OEN + 0xA4, 0x78, // CRC + 0x01, 0x00, 0x00, 0x00, // Data +}; +#define VALID_GET_COUNT_RESPONSE_SIZE 10 * sizeof(UINT8) + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +SendIpmiValidCountResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT8 *ResponseData; + UINT32 *ResponseDataSize; + EFI_STATUS Status; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_GET_COU= NT_RESPONSE_SIZE)); + ResponseDataSize =3D (UINT32 *)AllocateZeroPool (sizeof (UINT32)); + CopyMem (MockResponseResults, &ValidGetCountResponse, VALID_GET_COUNT_RE= SPONSE_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_GE= T_COUNT_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + ResponseData =3D AllocateZeroPool (sizeof (ValidGetCountResponse)); + Status =3D IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandGet= Count, NULL, 0, ResponseData, ResponseDataSize); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + FreePool (MockResponseResults); + FreePool (ResponseDataSize); + FreePool (ResponseData); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +GetCountValidCountResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + UINT32 Count; + VOID *MockResponseResults =3D NULL; + + Count =3D 0; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_GET_COU= NT_RESPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidGetCountResponse, VALID_GET_COUNT_RE= SPONSE_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_GE= T_COUNT_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status =3D IpmiBlobTransferGetCount (&Count); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (Count, 1); + FreePool (MockResponseResults); + return UNIT_TEST_PASSED; +} + +UINT8 ValidEnumerateResponse[] =3D { + 0x00, // CompletionCode + 0xCF, 0xC2, 0x00, // OpenBMC OEN + 0x81, 0x13, // CRC + 0x2F, 0x73, 0x6D, 0x62, // Data =3D "/smbios" + 0x69, 0x6F, 0x73, 0x00, +}; +#define VALID_ENUMERATE_RESPONSE_SIZE 14 * sizeof(UINT8) + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +EnumerateValidResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + CHAR8 *BlobId; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_ENUMERA= TE_RESPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidEnumerateResponse, VALID_ENUMERATE_R= ESPONSE_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_EN= UMERATE_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + BlobId =3D AllocateZeroPool (sizeof (CHAR8) * IPMI_OEM_BLOB_MAX_DATA_PER= _PACKET); + + Status =3D IpmiBlobTransferEnumerate (0, BlobId); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_MEM_EQUAL (BlobId, "/smbios", 7); + FreePool (MockResponseResults); + FreePool (BlobId); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +EnumerateInvalidBuffer ( + IN UNIT_TEST_CONTEXT Context + ) +{ + CHAR8 *BlobId; + EFI_STATUS Status; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_ENUMERA= TE_RESPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidEnumerateResponse, VALID_ENUMERATE_R= ESPONSE_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_EN= UMERATE_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + BlobId =3D NULL; + + UT_EXPECT_ASSERT_FAILURE (IpmiBlobTransferEnumerate (0, BlobId), NULL); + + FreePool (MockResponseResults); + return UNIT_TEST_PASSED; +} + +UINT8 ValidOpenResponse[] =3D { + 0x00, // CompletionCode + 0xCF, 0xC2, 0x00, // OpenBMC OEN + 0x93, 0xD1, // CRC + 0x03, 0x00, // SessionId =3D 3 +}; +#define VALID_OPEN_RESPONSE_SIZE 8 * sizeof(UINT8) + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +OpenValidResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + CHAR8 *BlobId; + UINT16 Flags; + UINT16 SessionId; + VOID *MockResponseResults =3D NULL; + VOID *MockResponseResults2 =3D NULL; + VOID *MockResponseResults3 =3D NULL; + + Flags =3D BLOB_TRANSFER_STAT_OPEN_W; + + // + // An open call effectively leads to three IPMI commands + // 1. GetCount of blobs + // 2. Enumerate the requested blob + // 3. Open the requested blob + // + // So we'll push three Ipmi responses in this case + // + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_OPEN_RE= SPONSE_SIZE)); + + CopyMem (MockResponseResults, &ValidOpenResponse, VALID_OPEN_RESPONSE_SI= ZE); + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_OP= EN_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + MockResponseResults2 =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_ENUMER= ATE_RESPONSE_SIZE)); + CopyMem (MockResponseResults2, &ValidEnumerateResponse, VALID_ENUMERATE_= RESPONSE_SIZE); + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults2, VALID_E= NUMERATE_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + MockResponseResults3 =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_GET_CO= UNT_RESPONSE_SIZE)); + CopyMem (MockResponseResults3, &ValidGetCountResponse, VALID_GET_COUNT_R= ESPONSE_SIZE); + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults3, VALID_G= ET_COUNT_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + BlobId =3D "/smbios"; + + Status =3D IpmiBlobTransferOpen (BlobId, Flags, &SessionId); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (SessionId, 3); + FreePool (MockResponseResults); + return UNIT_TEST_PASSED; +} + +UINT8 ValidReadResponse[] =3D { + 0x00, // CompletionCode + 0xCF, 0xC2, 0x00, // OpenBMC OEN + 0x21, 0x6F, // CRC + 0x00, 0x01, 0x02, 0x03, // Data to read +}; + +#define VALID_READ_RESPONSE_SIZE 10 * sizeof(UINT8) + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +ReadValidResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + UINT8 *ResponseData; + UINT8 ExpectedDataResponse[4] =3D { 0x00, 0x01, 0x02, 0x03 }; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_READ_RE= SPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidReadResponse, VALID_READ_RESPONSE_SI= ZE); + ResponseData =3D AllocateZeroPool (sizeof (ValidReadResponse)); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_RE= AD_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status =3D IpmiBlobTransferRead (0, 0, 4, ResponseData); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_MEM_EQUAL (ResponseData, ExpectedDataResponse, 4); + FreePool (MockResponseResults); + FreePool (ResponseData); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +ReadInvalidBuffer ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT8 *ResponseData; + EFI_STATUS Status; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_READ_RE= SPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidReadResponse, VALID_READ_RESPONSE_SI= ZE); + ResponseData =3D NULL; + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_RE= AD_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + UT_EXPECT_ASSERT_FAILURE (IpmiBlobTransferRead (0, 0, 4, ResponseData), = NULL); + + FreePool (MockResponseResults); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +WriteValidResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + UINT8 SendData[4] =3D { 0x00, 0x01, 0x02, 0x03 }; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_NODATA_= RESPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidNoDataResponse, VALID_NODATA_RESPONS= E_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_NO= DATA_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status =3D IpmiBlobTransferWrite (0, 0, SendData, 4); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + FreePool (MockResponseResults); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +CommitValidResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + UINT8 SendData[4] =3D { 0x00, 0x01, 0x02, 0x03 }; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_NODATA_= RESPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidNoDataResponse, VALID_NODATA_RESPONS= E_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_NO= DATA_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status =3D IpmiBlobTransferCommit (0, 4, SendData); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + FreePool (MockResponseResults); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +CloseValidResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_NODATA_= RESPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidNoDataResponse, VALID_NODATA_RESPONS= E_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_NO= DATA_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status =3D IpmiBlobTransferClose (1); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + FreePool (MockResponseResults); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +DeleteValidResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_NODATA_= RESPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidNoDataResponse, VALID_NODATA_RESPONS= E_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_NO= DATA_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status =3D IpmiBlobTransferDelete ("/smbios"); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + FreePool (MockResponseResults); + return UNIT_TEST_PASSED; +} + +UINT8 ValidBlobStatResponse[] =3D { + 0x00, // CompletionCode + 0xCF, 0xC2, 0x00, // OpenBMC OEN + 0x1F, 0x4F, // Crc + 0x01, 0x00, // BlobState + 0x02, 0x03, 0x04, 0x05, // BlobSize + 0x04, // MetaDataLen + 0x06, 0x07, 0x08, 0x09, // MetaData +}; + +#define VALID_BLOB_STAT_RESPONSE_SIZE 17 * sizeof(UINT8) + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +BlobStatValidResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + UINT16 *BlobState; + UINT32 *Size; + UINT8 *MetadataLength; + UINT8 *Metadata; + UINT8 *ExpectedMetadata; + CHAR8 *BlobId; + VOID *MockResponseResults =3D NULL; + + BlobState =3D AllocateZeroPool (sizeof (UINT16)); + Size =3D AllocateZeroPool (sizeof (UINT32)); + BlobId =3D "BlobId"; + MetadataLength =3D AllocateZeroPool (sizeof (UINT8)); + Metadata =3D AllocateZeroPool (4 * sizeof (UINT8)); + ExpectedMetadata =3D AllocateZeroPool (4 * sizeof (UINT8)); + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_BLOB_ST= AT_RESPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidBlobStatResponse, VALID_BLOB_STAT_RE= SPONSE_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_BL= OB_STAT_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status =3D IpmiBlobTransferStat (BlobId, BlobState, Size, MetadataLength= , Metadata); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (*BlobState, 1); + UT_ASSERT_EQUAL (*Size, 0x05040302); + UT_ASSERT_EQUAL (*MetadataLength, 4); + UT_ASSERT_MEM_EQUAL (Metadata, ExpectedMetadata, 4); + FreePool (MockResponseResults); + FreePool (BlobState); + FreePool (Size); + FreePool (MetadataLength); + FreePool (Metadata); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +BlobStatInvalidBuffer ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT8 *Metadata; + EFI_STATUS Status; + VOID *MockResponseResults =3D NULL; + + Metadata =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_BLOB_ST= AT_RESPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidBlobStatResponse, VALID_BLOB_STAT_RE= SPONSE_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_BL= OB_STAT_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + UT_EXPECT_ASSERT_FAILURE (IpmiBlobTransferStat (NULL, 0, 0, 0, Metadata)= , NULL); + + FreePool (MockResponseResults); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +SessionStatValidResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + UINT16 *BlobState; + UINT32 *Size; + UINT8 *MetadataLength; + UINT8 *Metadata; + UINT8 *ExpectedMetadata; + VOID *MockResponseResults =3D NULL; + + BlobState =3D AllocateZeroPool (sizeof (UINT16)); + Size =3D AllocateZeroPool (sizeof (UINT32)); + MetadataLength =3D AllocateZeroPool (sizeof (UINT8)); + Metadata =3D AllocateZeroPool (4 * sizeof (UINT8)); + ExpectedMetadata =3D AllocateZeroPool (4 * sizeof (UINT8)); + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_BLOB_ST= AT_RESPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidBlobStatResponse, VALID_BLOB_STAT_RE= SPONSE_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_BL= OB_STAT_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status =3D IpmiBlobTransferSessionStat (0, BlobState, Size, MetadataLeng= th, Metadata); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + UT_ASSERT_EQUAL (*BlobState, 1); + UT_ASSERT_EQUAL (*Size, 0x05040302); + UT_ASSERT_EQUAL (*MetadataLength, 4); + UT_ASSERT_MEM_EQUAL (Metadata, ExpectedMetadata, 4); + FreePool (MockResponseResults); + FreePool (BlobState); + FreePool (Size); + FreePool (MetadataLength); + FreePool (Metadata); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +SessionStatInvalidBuffer ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT8 *Metadata; + EFI_STATUS Status; + VOID *MockResponseResults =3D NULL; + + Metadata =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_BLOB_ST= AT_RESPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidBlobStatResponse, VALID_BLOB_STAT_RE= SPONSE_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_BL= OB_STAT_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + UT_EXPECT_ASSERT_FAILURE (IpmiBlobTransferSessionStat (0, 0, 0, 0, Metad= ata), NULL); + + FreePool (MockResponseResults); + return UNIT_TEST_PASSED; +} + +/** + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that = the + contents are well understood by all test cases th= at may + consume it. + @retval UNIT_TEST_PASSED The Unit test has completed and th= e test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +WriteMetaValidResponse ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + VOID *MockResponseResults =3D NULL; + + MockResponseResults =3D (UINT8 *)AllocateZeroPool (sizeof (VALID_NODATA_= RESPONSE_SIZE)); + CopyMem (MockResponseResults, &ValidNoDataResponse, VALID_NODATA_RESPONS= E_SIZE); + + Status =3D MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_NO= DATA_RESPONSE_SIZE, EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status =3D IpmiBlobTransferWriteMeta (0, 0, NULL, 0); + + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS); + FreePool (MockResponseResults); + return UNIT_TEST_PASSED; +} + +/** + Initialize the unit test framework, suite, and unit tests for the + sample unit tests and run the unit tests. + @retval EFI_SUCCESS All test cases were dispatched. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available = to + initialize the unit tests. +**/ +EFI_STATUS +EFIAPI +SetupAndRunUnitTests ( + VOID + ) +{ + EFI_STATUS Status; + UNIT_TEST_FRAMEWORK_HANDLE Framework; + UNIT_TEST_SUITE_HANDLE IpmiBlobTransfer; + + Framework =3D NULL; + DEBUG ((DEBUG_INFO, "%a: v%a\n", UNIT_TEST_NAME, UNIT_TEST_VERSION)); + + Status =3D InitUnitTestFramework (&Framework, UNIT_TEST_NAME, gEfiCaller= BaseName, UNIT_TEST_VERSION); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to setup Test Framework. Exiting with sta= tus =3D %r\n", Status)); + ASSERT (FALSE); + return Status; + } + + // + // Populate the Unit Test Suite. + // + Status =3D CreateUnitTestSuite (&IpmiBlobTransfer, Framework, "IPMI Blob= Transfer Tests", "UnitTest.IpmiBlobTransferCB", NULL, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for IPMI Blob Tran= sfer Tests\n")); + Status =3D EFI_OUT_OF_RESOURCES; + return Status; + } + + // CalculateCrc16 + Status =3D AddTestCase (IpmiBlobTransfer, "Test CRC Calculation", "GoodC= rc", GoodCrc, NULL, NULL, NULL); + Status =3D AddTestCase (IpmiBlobTransfer, "Test Bad CRC Calculation", "B= adCrc", BadCrc, NULL, NULL, NULL); + // IpmiBlobTransferSendIpmi + Status =3D AddTestCase (IpmiBlobTransfer, "Send IPMI returns bad complet= ion", "SendIpmiBadCompletion", SendIpmiBadCompletion, NULL, NULL, NULL); + Status =3D AddTestCase (IpmiBlobTransfer, "Send IPMI returns successfull= y with no data", "SendIpmiNoDataResponse", SendIpmiNoDataResponse, NULL, NU= LL, NULL); + Status =3D AddTestCase (IpmiBlobTransfer, "Send IPMI returns successfull= y with bad OEN", "SendIpmiBadOenResponse", SendIpmiBadOenResponse, NULL, NU= LL, NULL); + Status =3D AddTestCase (IpmiBlobTransfer, "Send IPMI returns successfull= y with bad CRC", "SendIpmiBadCrcResponse", SendIpmiBadCrcResponse, NULL, NU= LL, NULL); + Status =3D AddTestCase (IpmiBlobTransfer, "Send IPMI returns with valid = GetCount data", "SendIpmiValidCountResponse", SendIpmiValidCountResponse, N= ULL, NULL, NULL); + // IpmiBlobTransferGetCount + Status =3D AddTestCase (IpmiBlobTransfer, "GetCount call with valid data= ", "GetCountValidCountResponse", GetCountValidCountResponse, NULL, NULL, NU= LL); + // IpmiBlobTransferEnumerate + Status =3D AddTestCase (IpmiBlobTransfer, "Enumerate call with valid dat= a", "EnumerateValidResponse", EnumerateValidResponse, NULL, NULL, NULL); + Status =3D AddTestCase (IpmiBlobTransfer, "Enumerate call with invalid o= utput buffer", "EnumerateInvalidBuffer", EnumerateInvalidBuffer, NULL, NULL= , NULL); + // IpmiBlobTransferOpen + Status =3D AddTestCase (IpmiBlobTransfer, "Open call with valid data", "= OpenValidResponse", OpenValidResponse, NULL, NULL, NULL); + // IpmiBlobTransferRead + Status =3D AddTestCase (IpmiBlobTransfer, "Read call with valid data", "= ReadValidResponse", ReadValidResponse, NULL, NULL, NULL); + Status =3D AddTestCase (IpmiBlobTransfer, "Read call with invalid buffer= ", "ReadInvalidBuffer", ReadInvalidBuffer, NULL, NULL, NULL); + // IpmiBlobTransferWrite + Status =3D AddTestCase (IpmiBlobTransfer, "Write call with valid data", = "WriteValidResponse", WriteValidResponse, NULL, NULL, NULL); + // IpmiBlobTransferCommit + Status =3D AddTestCase (IpmiBlobTransfer, "Commit call with valid data",= "CommitValidResponse", CommitValidResponse, NULL, NULL, NULL); + // IpmiBlobTransferClose + Status =3D AddTestCase (IpmiBlobTransfer, "Close call with valid data", = "CloseValidResponse", CloseValidResponse, NULL, NULL, NULL); + // IpmiBlobTransferDelete + Status =3D AddTestCase (IpmiBlobTransfer, "Delete call with valid data",= "DeleteValidResponse", DeleteValidResponse, NULL, NULL, NULL); + // IpmiBlobTransferStat + Status =3D AddTestCase (IpmiBlobTransfer, "Blob Stat call with valid dat= a", "BlobStatValidResponse", BlobStatValidResponse, NULL, NULL, NULL); + Status =3D AddTestCase (IpmiBlobTransfer, "Blob Stat call with invalid b= uffer", "BlobStatInvalidBuffer", BlobStatInvalidBuffer, NULL, NULL, NULL); + // IpmiBlobTransferSessionStat + Status =3D AddTestCase (IpmiBlobTransfer, "Session Stat call with valid = data", "SessionStatValidResponse", SessionStatValidResponse, NULL, NULL, NU= LL); + Status =3D AddTestCase (IpmiBlobTransfer, "Session Stat call with invali= d buffer", "SessionStatInvalidBuffer", SessionStatInvalidBuffer, NULL, NULL= , NULL); + // IpmiBlobTransferWriteMeta + Status =3D AddTestCase (IpmiBlobTransfer, "WriteMeta call with valid dat= a", "WriteMetaValidResponse", WriteMetaValidResponse, NULL, NULL, NULL); + + // Execute the tests. + Status =3D RunAllTestSuites (Framework); + return Status; +} + +/** + Standard UEFI entry point for target based + unit test execution from UEFI Shell. +**/ +EFI_STATUS +EFIAPI +BaseLibUnitTestAppEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return SetupAndRunUnitTests (); +} + +/** + Standard POSIX C entry point for host based unit test execution. +**/ +int +main ( + int argc, + char *argv[] + ) +{ + return SetupAndRunUnitTests (); +} diff --git a/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/Readme= .md b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/Readme.md new file mode 100644 index 0000000000..47800f5801 --- /dev/null +++ b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/Readme.md @@ -0,0 +1,24 @@ +# IPMI Blob Transfer Interface Driver + +This DXE module is a UEFI implementation of the Phorphor Blob Transfer Int= erface defined in OpenBMC +https://github.com/openbmc/phosphor-ipmi-blobs + +## NVIDIA's OpenBMC implements this interface as a protocol, allowing UEFI= and BMC to transfer blobs over IPMI. + +### Usage: +Any DXE module that wishes to use this protocol should do the following: +1) The module should have a dependency on gEdkiiIpmiBlobTransferProtocolGu= id in its inf "Depex" section +2) The module should list gEdkiiIpmiBlobTransferProtocolGuid in its inf "P= rotocol" section +3) The module's entry point should do a LocateProtocol on gEdkiiIpmiBlobTr= ansferProtocolGuid + +### A sample flow of protocol usage is as follows: +1) A call to IpmiBlobTransferOpen () +2) Iterative calls to IpmiBlobTransferWrite +3) A call to IpmiBlobTransferClose () + +### Unit Tests: +IpmiBlobTransferDxe/UnitTest/ contains host based unit tests of this imple= mentation. +Any changes to IpmiBlobTransferDxe should include proof of successful unit= tests. + +### Debugging +To assist in debugging any issues, change BLOB_TRANSFER_DEBUG to 1 --=20 2.17.1 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#102863): https://edk2.groups.io/g/devel/message/102863 Mute This Topic: https://groups.io/mt/98212643/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-