From: "Nhi Pham via groups.io" <nhi=os.amperecomputing.com@groups.io>
To: Nickle Wang <nicklew@nvidia.com>, devel@edk2.groups.io
Cc: Abner Chang <abner.chang@amd.com>,
Abdul Lateef Attar <AbdulLateef.Attar@amd.com>,
Tinh Nguyen <tinhnguyen@amperemail.onmicrosoft.com>,
Thang Nguyen OS <thang@amperemail.onmicrosoft.com>,
Mike Maslenkin <mike.maslenkin@gmail.com>
Subject: Re: [edk2-devel] [edk2-platforms][PATCH v2] ManageabilityPkg: add support for the phosphor ipmi blob transfer protocol
Date: Fri, 17 May 2024 14:49:15 +0700 [thread overview]
Message-ID: <7d928ca7-e312-4a2e-9470-cbd96fbdc643@os.amperecomputing.com> (raw)
In-Reply-To: <20240515150629.236739-1-nicklew@nvidia.com>
Hi Nickle,
Please see my comments inline...
P/s: I just realized that I can not test this protocol without IPMI SSIF
to be compatible with ManageabilityPkg framework.
On 5/15/2024 10:06 PM, Nickle Wang wrote:
> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=4773
>
> This change implements the blob transfer protocol used in OpenBmc
> documented here: https://github.com/openbmc/phosphor-ipmi-blobs
>
> Signed-off-by: Nick Ramirez <nramirez@nvidia.com>
> Co-authored-by: Nickle Wang <nicklew@nvidia.com>
> Cc: Abner Chang <abner.chang@amd.com>
> Cc: Abdul Lateef Attar <AbdulLateef.Attar@amd.com>
> Cc: Tinh Nguyen <tinhnguyen@amperemail.onmicrosoft.com>
> Cc: Nhi Pham <nhi@os.amperecomputing.com>
> Cc: Thang Nguyen OS <thang@amperemail.onmicrosoft.com>
> Cc: Mike Maslenkin <mike.maslenkin@gmail.com>
> ---
> .../ManageabilityPkg/ManageabilityPkg.dec | 3 +
> .../Include/Manageability.dsc | 2 +
> .../IpmiBlobTransferDxe.inf | 39 +
> .../IpmiBlobTransferTestUnitTestsHost.inf | 40 +
> .../Include/Protocol/IpmiBlobTransfer.h | 253 ++++
> .../InternalIpmiBlobTransfer.h | 407 ++++++
> .../IpmiBlobTransferDxe/IpmiBlobTransferDxe.c | 872 +++++++++++++
> .../UnitTest/IpmiBlobTransferTestUnitTests.c | 1113 +++++++++++++++++
> .../Universal/IpmiBlobTransferDxe/Readme.md | 24 +
> 9 files changed, 2753 insertions(+)
> 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/IpmiBlobTransfer.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/ManageabilityPkg/ManageabilityPkg.dec
> index eb0ee67cba..dc1d00162c 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.<BR>
> +# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
> # SPDX-License-Identifier: BSD-2-Clause-Patent
> #
> ##
> @@ -58,6 +59,8 @@
> gEdkiiPldmProtocolGuid = { 0x60997616, 0xDB70, 0x4B5F, { 0x86, 0xA4, 0x09, 0x58, 0xA3, 0x71, 0x47, 0xB4 } }
> gEdkiiPldmSmbiosTransferProtocolGuid = { 0xFA431C3C, 0x816B, 0x4B32, { 0xA3, 0xE0, 0xAD, 0x9B, 0x7F, 0x64, 0x27, 0x2E } }
> gEdkiiMctpProtocolGuid = { 0xE93465C1, 0x9A31, 0x4C96, { 0x92, 0x56, 0x22, 0x0A, 0xE1, 0x80, 0xB4, 0x1B } }
> + ## Include/Protocol/IpmiBlobTransfer.h
> + gEdkiiIpmiBlobTransferProtocolGuid = { 0x05837c75, 0x1d65, 0x468b, { 0xb1, 0xc2, 0x81, 0xaf, 0x9a, 0x31, 0x5b, 0x2c } }
>
> [PcdsFixedAtBuild]
> ## This value is the MCTP Interface source and destination endpoint ID for transmiting MCTP message.
> diff --git a/Features/ManageabilityPkg/Include/Manageability.dsc b/Features/ManageabilityPkg/Include/Manageability.dsc
> index 2e410df9ba..aae343a733 100644
> --- a/Features/ManageabilityPkg/Include/Manageability.dsc
> +++ b/Features/ManageabilityPkg/Include/Manageability.dsc
> @@ -2,6 +2,7 @@
> # Common libraries for Manageabilty Package
> #
> # Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
> +# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
> # SPDX-License-Identifier: BSD-2-Clause-Patent
> #
> ##
> @@ -37,6 +38,7 @@
> [Components.X64, Components.AARCH64]
> !if gManageabilityPkgTokenSpaceGuid.PcdManageabilityDxeIpmiEnable == TRUE
> ManageabilityPkg/Universal/IpmiProtocol/Dxe/IpmiProtocolDxe.inf
> + ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransferDxe.inf
> !endif
>
> [Components.X64]
> diff --git a/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransferDxe.inf b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransferDxe.inf
> new file mode 100644
> index 0000000000..108f4bb5f8
> --- /dev/null
> +++ b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransferDxe.inf
> @@ -0,0 +1,39 @@
> +## @file
> +# IPMI Blob Transfer Protocol DXE Driver.
> +#
> +# Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +
> +[Defines]
> + INF_VERSION = 0x00010005
> + BASE_NAME = IpmiBlobTransferDxe
> + FILE_GUID = 6357c804-78bb-4b0c-abdf-c75df942f319
> + MODULE_TYPE = DXE_DRIVER
> + VERSION_STRING = 1.0
> + ENTRY_POINT = 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/UnitTest/IpmiBlobTransferTestUnitTestsHost.inf b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlobTransferTestUnitTestsHost.inf
> new file mode 100644
> index 0000000000..dab6858f09
> --- /dev/null
> +++ b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlobTransferTestUnitTestsHost.inf
> @@ -0,0 +1,40 @@
> +## @file
> +# Unit tests of the Ipmi blob transfer driver that are run from a host environment.
> +#
> +# Copyright (c) 2020-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +##
> +
> +[Defines]
> + INF_VERSION = 0x00010006
> + BASE_NAME = IpmiBlobTransferDxeUnitTestsHost
> + FILE_GUID = 1f5d4095-ea52-432c-b078-86097fef6004
> + MODULE_TYPE = HOST_APPLICATION
> + VERSION_STRING = 1.0
> +
> +#
> +# The following information is for reference only
> +# and not required by the build tools.
> +#
> +# VALID_ARCHITECTURES = 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..14b5294314
> --- /dev/null
> +++ b/Features/ManageabilityPkg/Include/Protocol/IpmiBlobTransfer.h
> @@ -0,0 +1,253 @@
> +/** @file
> +
> + IPMI Blob Transfer driver
> +
> + Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> + @Par: https://github.com/openbmc/phosphor-ipmi-blobs/blob/master/README.md
> +**/
Lack of header guard
#ifndef IPMI_BLOB_TRANSFER_H_
> +#include <Library/IpmiLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <IndustryStandard/Ipmi.h>
> +#include <IndustryStandard/IpmiNetFnOem.h>
> +
> +#define IPMI_OEM_BLOB_TRANSFER_CMD 0x80
> +
> +#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
> +
> +//
> +// OpenBMC OEN code in little endian format
> +//
> +const UINT8 OpenBmcOen[] = { 0xCF, 0xC2, 0x00 };
const -> CONST
Should we add a PCD for the OEN to be configured by platform specific
BMC? Or this protocol is only to support OpenBMC.
> +
> +//
> +// Blob Transfer Function Prototypes
> +//
> +
> +/**
> + This function retrieves the count of blob transfers available through the IPMI.
> +
> + @param[out] Count The number of active blobs
> +
> + @retval EFI_SUCCESS Successfully retrieved the number of active blobs.
> + @retval Other An error occurred
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_GET_COUNT)(
> + OUT UINT32 *Count
> + );
> +
> +/**
> + This function enumerates blob transfers available through the IPMI.
> +
> + @param[in] BlobIndex The 0-based Index of the blob to enumerate
> + @param[out] BlobId The ID of the blob
> +
> + @retval EFI_SUCCESS Successfully enumerated the blob.
> + @retval Other An error occurred
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_ENUMERATE)(
> + IN UINT32 BlobIndex,
> + OUT CHAR8 *BlobId
> + );
> +
> +/**
> + This function is designed to open a session for a specific blob
> + identified by its ID, using the IPMI.
> +
> + @param[in] BlobId The ID of the blob to open
> + @param[in] Flags Flags to control how the blob is opened
> + @param[out] SessionId A unique session identifier
> +
> + @retval EFI_SUCCESS Successfully opened the blob.
> + @retval Other An error occurred
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_OPEN)(
> + IN CHAR8 *BlobId,
> + IN UINT16 Flags,
> + OUT UINT16 *SessionId
> + );
> +
> +/**
> + This function reads data from a blob over the IPMI.
> +
> + @param[in] SessionId The session ID returned from a call to 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
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_READ)(
> + IN UINT16 SessionId,
> + IN UINT32 Offset,
> + IN UINT32 RequestedSize,
> + OUT UINT8 *Data
> + );
> +
> +/**
> + This function writes data to a blob over the IPMI.
> +
> + @param[in] SessionId The session ID returned from a call to BlobOpen
> + @param[in] Offset The offset of the blob from which to start writing
> + @param[in] Data A pointer to the data to write
> + @param[in] WriteLength The length to write
> +
> + @retval EFI_SUCCESS Successfully wrote to the blob.
> + @retval Other An error occurred
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_WRITE)(
> + IN UINT16 SessionId,
> + IN UINT32 Offset,
> + IN UINT8 *Data,
> + IN UINT32 WriteLength
> + );
> +
> +/**
> + This function commits data to a blob over the IPMI.
> +
> + @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
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_COMMIT)(
> + IN UINT16 SessionId,
> + IN UINT8 CommitDataLength,
> + IN UINT8 *CommitData
> + );
> +
> +/**
> + This function close a session associated with a blob transfer over the IPMI.
> +
> + @param[in] SessionId The session ID returned from a call to BlobOpen
> +
> + @retval EFI_SUCCESS The blob was closed.
> + @retval Other An error occurred
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_CLOSE)(
> + IN UINT16 SessionId
> + );
> +
> +/**
> + This function deletes a specific blob identified by its ID over the IPMI.
> +
> + @param[in] BlobId The BlobId to be deleted
> +
> + @retval EFI_SUCCESS The blob was deleted.
> + @retval Other An error occurred
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_DELETE)(
> + IN CHAR8 *BlobId
> + );
> +
> +/**
> + This function retrieve the status of a specific blob identified by BlobId from an IPMI.
> +
> + @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
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_STAT)(
> + IN CHAR8 *BlobId,
> + OUT UINT16 *BlobState,
> + OUT UINT32 *Size,
> + OUT UINT8 *MetadataLength,
> + OUT UINT8 *Metadata
> + );
> +
> +/**
> + This function query the status of a blob transfer session in an IPMI.
> +
> + @param[in] SessionId The ID of the session 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
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_SESSION_STAT)(
> + IN UINT16 SessionId,
> + OUT UINT16 *BlobState,
> + OUT UINT32 *Size,
> + OUT UINT8 *MetadataLength,
> + OUT UINT8 *Metadata
> + );
> +
> +/**
> + This function writes metadata to a blob associated with a session in an IPMI.
> +
> + @param[in] SessionId The ID of the session to write metadata for
> + @param[in] Offset The offset of the metadata to write to
> + @param[in] Data The data to write to the metadata
> + @param[in] WriteLength The length to write
> +
> + @retval EFI_SUCCESS The blob metadata was successfully written.
> + @retval Other An error occurred
> +**/
> +typedef
> +EFI_STATUS
> +(EFIAPI *EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_WRITE_META)(
> + IN UINT16 SessionId,
> + IN UINT32 Offset,
> + IN UINT8 *Data,
> + IN UINT32 WriteLength
> + );
> +
> +//
> +// Structure of EDKII_IPMI_BLOB_TRANSFER_PROTOCOL
> +//
> +struct _EDKII_IPMI_BLOB_TRANSFER_PROTOCOL {
> + EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_GET_COUNT BlobGetCount;
> + EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_ENUMERATE BlobEnumerate;
> + EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_OPEN BlobOpen;
> + EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_READ BlobRead;
> + EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_WRITE BlobWrite;
> + EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_COMMIT BlobCommit;
> + EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_CLOSE BlobClose;
> + EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_DELETE BlobDelete;
> + EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_STAT BlobStat;
> + EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_SESSION_STAT BlobSessionStat;
> + EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_WRITE_META BlobWriteMeta;
> +};
> +
> +typedef struct _EDKII_IPMI_BLOB_TRANSFER_PROTOCOL EDKII_IPMI_BLOB_TRANSFER_PROTOCOL;
> +
> +extern EFI_GUID gEdkiiIpmiBlobTransferProtocolGuid;
> diff --git a/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/InternalIpmiBlobTransfer.h b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/InternalIpmiBlobTransfer.h
> new file mode 100644
> index 0000000000..3e90dc6871
> --- /dev/null
> +++ b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/InternalIpmiBlobTransfer.h
> @@ -0,0 +1,407 @@
> +/** @file
> +
> + Headers for IPMI Blob Transfer driver
> +
> + Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
Lack of header guard
#ifndef INTERNAL_IPMI_BLOB_TRANSFER_H_
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/IpmiLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/PcdLib.h>
> +
> +#define PROTOCOL_RESPONSE_OVERHEAD (4 * sizeof(UINT8)) // 1 byte completion code + 3 bytes OEN
Nit: Add a space after sizeof
> +#define BLOB_MAX_DATA_PER_PACKET 64
Should it be moved to Include/Protocol/IpmiBlobTransfer.h? The caller
could need to be aware the max length of the package.
> +
> +// Subcommands for this protocol
> +typedef enum {
> + IpmiBlobTransferSubcommandGetCount = 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[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[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[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[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[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[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[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[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[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[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
> +CalculateCrc16Ccitt (
> + IN UINT8 *Data,
> + IN UINTN DataSize
> + );
> +
> +/**
> + This function does blob transfer over IPMI command.
> +
> + @param[in] SubCommand The specific sub-command to be executed as part of
> + the blob transfer operation.
> + @param[in] SendData A pointer to the data buffer that contains the data to be sent.
> + @param[in] SendDataSize The size of the data to be sent, in bytes.
> + @param[out] ResponseData A pointer to the buffer where the response data will be stored.
> + @param[out] ResponseDataSize A pointer to a variable that will hold the size of the response
> + data received.
> +
> + @retval EFI_SUCCESS Successfully sends blob data.
> + @retval EFI_OUT_OF_RESOURCES Memory allocation fails.
> + @retval EFI_PROTOCOL_ERROR Communication errors.
> + @retval EFI_CRC_ERROR Data integrity checks fail.
> + @retval Other An error occurred
> +
> +**/
> +EFI_STATUS
> +IpmiBlobTransferSendIpmi (
> + IN UINT8 SubCommand,
> + IN UINT8 *SendData,
> + IN UINT32 SendDataSize,
> + OUT UINT8 *ResponseData,
> + OUT UINT32 *ResponseDataSize
> + );
> +
> +/**
> + This function retrieves the count of blob transfers available through the IPMI.
> +
> + @param[out] Count The number of active blobs
> +
> + @retval EFI_SUCCESS Successfully retrieved the number of active blobs.
> + @retval Other An error occurred
> +**/
> +EFI_STATUS
> +IpmiBlobTransferGetCount (
> + OUT UINT32 *Count
> + );
> +
> +/**
> + This function enumerates blob transfers available through the IPMI.
> +
> + @param[in] BlobIndex The 0-based Index of the blob to enumerate
> + @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
> + );
> +
> +/**
> + This function is designed to open a session for a specific blob
> + identified by its ID, using the IPMI.
> +
> + @param[in] BlobId The ID of the blob to open
> + @param[in] Flags Flags to control how the blob is opened
> + @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
> + );
> +
> +/**
> + This function reads data from a blob over the IPMI.
> +
> + @param[in] SessionId The session ID returned from a call to 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
> + );
> +
> +/**
> + This function writes data to a blob over the IPMI.
> +
> + @param[in] SessionId The session ID returned from a call to BlobOpen
> + @param[in] Offset The offset of the blob from which to start writing
> + @param[in] Data A pointer to the data to write
> + @param[in] WriteLength The length 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
> + );
> +
> +/**
> + This function commits data to a blob over the IPMI.
> +
> + @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
> + );
> +
> +/**
> + This function close a session associated with a blob transfer over the IPMI.
> +
> + @param[in] SessionId The session ID returned from a call to BlobOpen
> +
> + @retval EFI_SUCCESS The blob was closed.
> + @retval Other An error occurred
> +**/
> +EFI_STATUS
> +IpmiBlobTransferClose (
> + IN UINT16 SessionId
> + );
> +
> +/**
> + This function deletes a specific blob identified by its ID over the IPMI.
> +
> + @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
> + );
> +
> +/**
> + This function retrieve the status of a specific blob identified by BlobId from an IPMI.
> +
> + @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
> + );
> +
> +/**
> + This function query the status of a blob transfer session in an IPMI.
> +
> + @param[in] SessionId The ID of the session 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
> +IpmiBlobTransferSessionStat (
> + IN UINT16 SessionId,
> + OUT UINT16 *BlobState,
> + OUT UINT32 *Size,
> + OUT UINT8 *MetadataLength,
> + OUT UINT8 *Metadata
> + );
> +
> +/**
> + This function writes metadata to a blob associated with a session in an IPMI.
> +
> + @param[in] SessionId The ID of the session to write metadata for
> + @param[in] Offset The offset of the metadata to write to
> + @param[in] Data The data to write to the metadata
> + @param[in] WriteLength The length to write
> +
> + @retval EFI_SUCCESS The blob metadata was successfully written.
> + @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/IpmiBlobTransferDxe.c b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransferDxe.c
> new file mode 100644
> index 0000000000..b8a2db193b
> --- /dev/null
> +++ b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransferDxe.c
> @@ -0,0 +1,872 @@
> +/** @file
> +
> + IPMI Blob Transfer driver
> +
> + Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +#include <Protocol/IpmiBlobTransfer.h>
> +
> +#include "InternalIpmiBlobTransfer.h"
> +
> +#define BLOB_TRANSFER_DEBUG DEBUG_MANAGEABILITY
> +
> +STATIC CONST EDKII_IPMI_BLOB_TRANSFER_PROTOCOL mIpmiBlobTransfer = {
> + (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_GET_COUNT)*IpmiBlobTransferGetCount,
> + (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_ENUMERATE)*IpmiBlobTransferEnumerate,
> + (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_OPEN)*IpmiBlobTransferOpen,
> + (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_READ)*IpmiBlobTransferRead,
> + (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_WRITE)*IpmiBlobTransferWrite,
> + (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_COMMIT)*IpmiBlobTransferCommit,
> + (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_CLOSE)*IpmiBlobTransferClose,
> + (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_DELETE)*IpmiBlobTransferDelete,
> + (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_STAT)*IpmiBlobTransferStat,
> + (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_SESSION_STAT)*IpmiBlobTransferSessionStat,
> + (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_WRITE_META)*IpmiBlobTransferWriteMeta
> +};
> +
> +/**
> + 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
> +CalculateCrc16Ccitt (
> + IN UINT8 *Data,
> + IN UINTN DataSize
> + )
> +{
> + UINTN Index;
> + UINTN BitIndex;
> + UINT16 Crc;
> + UINT16 Poly;
> + BOOLEAN XorFlag;
> +
> + Crc = 0xFFFF;
> + Poly = 0x1021;
> + XorFlag = FALSE;
> +
> + for (Index = 0; Index < (DataSize + 2); ++Index) {
> + for (BitIndex = 0; BitIndex < 8; ++BitIndex) {
> + XorFlag = (Crc & 0x8000) ? TRUE : FALSE;
> + Crc <<= 1;
> + if ((Index < DataSize) && (Data[Index] & (1 << (7 - BitIndex)))) {
> + Crc++;
> + }
> +
> + if (XorFlag == TRUE) {
> + Crc ^= Poly;
> + }
> + }
> + }
> +
> + DEBUG ((BLOB_TRANSFER_DEBUG, "%a: CRC-16-CCITT %x\n", __func__, Crc));
> +
> + return Crc;
> +}
> +
> +/**
> + This function does blob transfer over IPMI command.
> +
> + @param[in] SubCommand The specific sub-command to be executed as part of
> + the blob transfer operation.
> + @param[in] SendData A pointer to the data buffer that contains the data to be sent.
> + @param[in] SendDataSize The size of the data to be sent, in bytes.
> + @param[out] ResponseData A pointer to the buffer where the response data will be stored.
> + @param[out] ResponseDataSize A pointer to a variable that will hold the size of the response
> + data received.
> +
> + @retval EFI_SUCCESS Successfully sends blob data.
> + @retval EFI_OUT_OF_RESOURCES Memory allocation fails.
> + @retval EFI_PROTOCOL_ERROR Communication errors.
> + @retval EFI_CRC_ERROR Data integrity checks fail.
> + @retval Other An error occurred
> +
> +**/
> +EFI_STATUS
> +IpmiBlobTransferSendIpmi (
> + IN UINT8 SubCommand,
> + IN UINT8 *SendData,
> + IN UINT32 SendDataSize,
Should describe SendData and SendDataSize as OPTIONAL
> + 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;
> +
Should validate the pointer of input arguments: SendData, ResponseData,
ResponseDataSize.
> + Crc = 0;
> +
> + //
> + // Prepend the proper header to the SendData
> + //
> + IpmiSendDataSize = (sizeof (IPMI_BLOB_TRANSFER_HEADER));
> + if (SendDataSize) {
> + IpmiSendDataSize += sizeof (Crc) + (sizeof (UINT8) * SendDataSize);
> + }
> +
> + IpmiSendData = AllocateZeroPool (IpmiSendDataSize);
> + if (IpmiSendData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + Header.OEN[0] = OpenBmcOen[0];
> + Header.OEN[1] = OpenBmcOen[1];
> + Header.OEN[2] = OpenBmcOen[2];
> + Header.SubCommand = SubCommand;
> + CopyMem (IpmiSendData, &Header, sizeof (IPMI_BLOB_TRANSFER_HEADER));
> + if (SendDataSize) {
if (SendDataSize != 0)
> + //
> + // Calculate the Crc of the send data
> + //
> + Crc = CalculateCrc16Ccitt (SendData, SendDataSize);
> + CopyMem (IpmiSendData + sizeof (IPMI_BLOB_TRANSFER_HEADER), &Crc, sizeof (UINT16));
> + CopyMem (IpmiSendData + sizeof (IPMI_BLOB_TRANSFER_HEADER) + sizeof (UINT16), SendData, SendDataSize);
> + }
> +
> + DEBUG_CODE_BEGIN ();
> + DEBUG ((BLOB_TRANSFER_DEBUG, "%a: Inputs:\n", __func__));
> + DEBUG ((BLOB_TRANSFER_DEBUG, "%a: SendDataSize: %02x\nData: ", __func__, SendDataSize));
> + UINT8 i;
> +
> + for (i = 0; i < SendDataSize; i++) {
> + DEBUG ((BLOB_TRANSFER_DEBUG, "%02x", *((UINT8 *)SendData + i)));
> + }
> +
> + DEBUG ((BLOB_TRANSFER_DEBUG, "\n"));
> + DEBUG ((BLOB_TRANSFER_DEBUG, "%a: IpmiSendDataSize: %02x\nData: ", __func__, IpmiSendDataSize));
> + for (i = 0; i < IpmiSendDataSize; i++) {
> + DEBUG ((BLOB_TRANSFER_DEBUG, "%02x", *((UINT8 *)IpmiSendData + i)));
> + }
> +
> + DEBUG ((BLOB_TRANSFER_DEBUG, "\n"));
> + DEBUG_CODE_END ();
> +
> + IpmiResponseDataSize = (*ResponseDataSize + PROTOCOL_RESPONSE_OVERHEAD);
> + //
> + // If expecting data to be returned, we have to also account for the 16 bit CRC
> + //
> + if (*ResponseDataSize) {
if (*ResponseDataSize != 0)
> + IpmiResponseDataSize += sizeof (Crc);
> + }
> +
> + IpmiResponseData = AllocateZeroPool (IpmiResponseDataSize);
> + if (IpmiResponseData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + Status = IpmiSubmitCommand (
> + IPMI_NETFN_OEM,
> + IPMI_OEM_BLOB_TRANSFER_CMD,
> + (VOID *)IpmiSendData,
> + IpmiSendDataSize,
> + (VOID *)IpmiResponseData,
> + &IpmiResponseDataSize
> + );
> +
> + FreePool (IpmiSendData);
> + ModifiedResponseData = IpmiResponseData;
> +
> + DEBUG_CODE_BEGIN ();
> + DEBUG ((BLOB_TRANSFER_DEBUG, "%a: IPMI Response:\n", __func__));
> + DEBUG ((BLOB_TRANSFER_DEBUG, "%a: ResponseDataSize: %02x\nData: ", __func__, IpmiResponseDataSize));
> + UINT8 i;
> +
> + for (i = 0; i < IpmiResponseDataSize; i++) {
> + DEBUG ((BLOB_TRANSFER_DEBUG, "%02x", *(ModifiedResponseData + i)));
> + }
> +
> + DEBUG ((BLOB_TRANSFER_DEBUG, "\n"));
> + DEBUG_CODE_END ();
> +
> + if (EFI_ERROR (Status)) {
> + return Status;
> + }
> +
> + CompletionCode = *ModifiedResponseData;
> + if (CompletionCode != IPMI_COMP_CODE_NORMAL) {
> + DEBUG ((DEBUG_ERROR, "%a: Returning because CompletionCode = 0x%x\n", __func__, CompletionCode));
> + FreePool (IpmiResponseData);
> + return EFI_PROTOCOL_ERROR;
> + }
> +
> + // Strip completion code, we are done with it
> + ModifiedResponseData = ModifiedResponseData + sizeof (CompletionCode);
> + IpmiResponseDataSize -= sizeof (CompletionCode);
> +
> + // Check OEN code and verify it matches the OpenBMC OEN
> + CopyMem (Oen, ModifiedResponseData, sizeof (OpenBmcOen));
> + if (CompareMem (Oen, OpenBmcOen, sizeof (OpenBmcOen)) != 0) {
> + FreePool (IpmiResponseData);
> + return EFI_PROTOCOL_ERROR;
> + }
> +
> + if (IpmiResponseDataSize == sizeof (OpenBmcOen)) {
> + //
> + // In this case, there was no response data sent. This is not an error.
> + // Some messages do not require a response.
> + //
> + *ResponseDataSize = 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 = ModifiedResponseData + sizeof (Oen);
> + IpmiResponseDataSize -= sizeof (Oen);
> + // Then validate the Crc
> + CopyMem (&Crc, ModifiedResponseData, sizeof (Crc));
> + ModifiedResponseData = ModifiedResponseData + sizeof (Crc);
> + IpmiResponseDataSize -= sizeof (Crc);
> +
> + if (Crc == CalculateCrc16Ccitt (ModifiedResponseData, IpmiResponseDataSize)) {
> + CopyMem (ResponseData, ModifiedResponseData, IpmiResponseDataSize);
> + CopyMem (ResponseDataSize, &IpmiResponseDataSize, sizeof (IpmiResponseDataSize));
> + FreePool (IpmiResponseData);
> + return EFI_SUCCESS;
> + } else {
> + FreePool (IpmiResponseData);
> + return EFI_CRC_ERROR;
> + }
> + }
> +}
> +
> +/**
> + This function retrieves the count of blob transfers available through the IPMI.
> +
> + @param[out] Count The number of active blobs
> +
> + @retval EFI_SUCCESS Successfully retrieved the number of active blobs.
> + @retval Other An error occurred
> +**/
> +EFI_STATUS
> +IpmiBlobTransferGetCount (
> + OUT UINT32 *Count
> + )
> +{
> + EFI_STATUS Status;
> + UINT8 *ResponseData;
> + UINT32 ResponseDataSize;
> +
> + if (Count == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + ResponseDataSize = sizeof (IPMI_BLOB_TRANSFER_GET_COUNT_RESPONSE);
> + ResponseData = AllocateZeroPool (ResponseDataSize);
> + if (ResponseData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandGetCount, NULL, 0, (UINT8 *)ResponseData, &ResponseDataSize);
> + if (!EFI_ERROR (Status)) {
> + *Count = ((IPMI_BLOB_TRANSFER_GET_COUNT_RESPONSE *)ResponseData)->BlobCount;
> + }
> +
> + FreePool (ResponseData);
> + return Status;
> +}
> +
> +/**
> + This function enumerates blob transfers available through the IPMI.
> +
> + @param[in] BlobIndex The 0-based Index of the blob to enumerate
> + @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 == NULL) {
> + ASSERT (FALSE);
> + return EFI_ABORTED;
Should return EFI_INVALID_PARAMETER for input validation?
> + }
> +
> + ResponseDataSize = sizeof (IPMI_BLOB_TRANSFER_BLOB_ENUMERATE_RESPONSE);
> + ResponseData = AllocateZeroPool (ResponseDataSize);
> + if (ResponseData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + //
> + // Format send data
> + //
> + SendDataSize = sizeof (IPMI_BLOB_TRANSFER_BLOB_ENUMERATE_SEND_DATA);
> + SendData = AllocateZeroPool (SendDataSize);
> + if (SendData == NULL) {
FreePool (ResponseData);
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + ((IPMI_BLOB_TRANSFER_BLOB_ENUMERATE_SEND_DATA *)SendData)->BlobIndex = BlobIndex;
> +
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandEnumerate, SendData, SendDataSize, (UINT8 *)ResponseData, &ResponseDataSize);
> + if (!EFI_ERROR (Status)) {
> + AsciiStrCpyS (BlobId, ResponseDataSize, (CHAR8 *)ResponseData);
> + }
> +
> + FreePool (ResponseData);
> + return Status;
> +}
> +
> +/**
> + This function is designed to open a session for a specific blob
> + identified by its ID, using the IPMI.
> +
> + @param[in] BlobId The ID of the blob to open
> + @param[in] Flags Flags to control how the blob is opened
It would be good if we can list out all flag definitions here. Actually,
I don't know how to input for this argument.
Are they BLOB_OPEN_FLAG_READ and BLOB_OPEN_FLAG_WRITE in the private
include header?
> + @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;
> +
> + if ((BlobId == NULL) || (SessionId == NULL)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + //
> + // Before opening a blob, need to check if it exists
I'm thinking the caller sequence here. Typically, the caller might check
the presence of a blob by calling GetCount () and Enumerate () before
opening a blob session. This check here could waste time. Or, do we call
open direction the blob session without pre-checking?
> + //
> + Status = IpmiBlobTransferGetCount (&NumBlobs);
> + if (EFI_ERROR (Status) || (NumBlobs == 0)) {
> + if (Status == EFI_UNSUPPORTED) {
> + return Status;
> + }
> +
> + DEBUG ((DEBUG_ERROR, "%a: Could not find any blobs: %r\n", __func__, Status));
> + return EFI_NOT_FOUND;
> + }
> +
> + BlobSearch = AllocateZeroPool (sizeof (CHAR8) * BLOB_MAX_DATA_PER_PACKET);
> + if (BlobSearch == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + BlobFound = FALSE;
> + for (Index = 0; Index < NumBlobs; Index++) {
> + Status = IpmiBlobTransferEnumerate (Index, BlobSearch);
> + if ((!EFI_ERROR (Status)) && (AsciiStrCmp (BlobSearch, BlobId) == 0)) {
> + BlobFound = TRUE;
> + break;
> + } else {
> + continue;
> + }
> + }
> +
> + if (!BlobFound) {
> + DEBUG ((DEBUG_ERROR, "%a: Could not find a blob that matches %a\n", __func__, BlobId));
> + FreePool (BlobSearch);
> + return EFI_NOT_FOUND;
> + }
> +
> + ResponseDataSize = sizeof (IPMI_BLOB_TRANSFER_BLOB_OPEN_RESPONSE);
> + ResponseData = AllocateZeroPool (ResponseDataSize);
> + if (ResponseData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + //
> + // Format send data
> + //
> + SendDataSize = sizeof (((IPMI_BLOB_TRANSFER_BLOB_OPEN_SEND_DATA *)SendData)->Flags) + ((AsciiStrLen (BlobId)) * sizeof (CHAR8)) + sizeof (CHAR8);
> + SendData = AllocateZeroPool (SendDataSize);
> + if (SendData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + AsciiStrCpyS (((IPMI_BLOB_TRANSFER_BLOB_OPEN_SEND_DATA *)SendData)->BlobId, AsciiStrSize (BlobId) / sizeof (CHAR8), BlobId);
> + ((IPMI_BLOB_TRANSFER_BLOB_OPEN_SEND_DATA *)SendData)->Flags = Flags;
> + // append null char to SendData
> + SendData[SendDataSize-1] = 0;
Nit: add spaces around minus (-) for readability.
> +
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandOpen, SendData, SendDataSize, (UINT8 *)ResponseData, &ResponseDataSize);
> + if (!EFI_ERROR (Status)) {
> + *SessionId = ((IPMI_BLOB_TRANSFER_BLOB_OPEN_RESPONSE *)ResponseData)->SessionId;
> + }
> +
> + FreePool (ResponseData);
> + FreePool (SendData);
> + FreePool (BlobSearch);
> + return Status;
> +}
> +
> +/**
> + This function reads data from a blob over the IPMI.
> +
> + @param[in] SessionId The session ID returned from a call to 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,
There might be developer mistake when executing the transfer before
opening the session. How do we handle this failure path? Do we need to
maintain a state machine for that?
This comment applies to other functions as well.
> + IN UINT32 Offset,
> + IN UINT32 RequestedSize,
> + OUT UINT8 *Data
> + )
> +{
> + EFI_STATUS Status;
> + UINT8 *SendData;
> + UINT8 *ResponseData;
> + UINT32 SendDataSize;
> + UINT32 ResponseDataSize;
> +
> + if (Data == NULL) {
> + ASSERT (FALSE);
> + return EFI_ABORTED;
Should return EFI_INVALID_PARAMETER?
> + }
> +
> + ResponseDataSize = RequestedSize * sizeof (UINT8);
Should check the RequestedSize against BLOB_MAX_DATA_PER_PACKET?
> + ResponseData = AllocateZeroPool (ResponseDataSize);
> + if (ResponseData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + //
> + // Format send data
> + //
> + SendDataSize = sizeof (IPMI_BLOB_TRANSFER_BLOB_READ_SEND_DATA);
> + SendData = AllocateZeroPool (SendDataSize);
> + if (SendData == NULL) {
FreePool (ResponseData);
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + ((IPMI_BLOB_TRANSFER_BLOB_READ_SEND_DATA *)SendData)->SessionId = SessionId;
> + ((IPMI_BLOB_TRANSFER_BLOB_READ_SEND_DATA *)SendData)->Offset = Offset;
> + ((IPMI_BLOB_TRANSFER_BLOB_READ_SEND_DATA *)SendData)->RequestedSize = RequestedSize;
> +
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandRead, SendData, 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;
> +}
> +
> +/**
> + This function writes data to a blob over the IPMI.
> +
> + @param[in] SessionId The session ID returned from a call to BlobOpen
> + @param[in] Offset The offset of the blob from which to start writing
> + @param[in] Data A pointer to the data to write
> + @param[in] WriteLength The length 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;
> +
> + if (Data == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + //
> + // Format send data
> + //
> + SendDataSize = sizeof (SessionId) + sizeof (Offset) + WriteLength;
Should we check whether or not the WriteLength is equal to or less than
BLOB_MAX_DATA_PER_PACKET?
> + SendData = AllocateZeroPool (SendDataSize);
> + if (SendData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + ((IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA *)SendData)->SessionId = SessionId;
> + ((IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA *)SendData)->Offset = Offset;
> + CopyMem (((IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA *)SendData)->Data, Data, sizeof (UINT8) * WriteLength);
> +
> + ResponseDataSize = 0;
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandWrite, SendData, SendDataSize, NULL, &ResponseDataSize);
> +
> + FreePool (SendData);
> + return Status;
> +}
> +
> +/**
> + This function commits data to a blob over the IPMI.
> +
> + @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;
> +
> + if (CommitData == NULL) {
According to the spec https://github.com/openbmc/phosphor-ipmi-blobs,
the commit data is block-specific optional.
For instance, the commit data is optional for SMBIOS blob transfer. Look
at https://github.com/openbmc/smbios-mdr
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + //
> + // Format send data
> + //
> + SendDataSize = sizeof (SessionId) + sizeof (CommitDataLength) + CommitDataLength;
> + SendData = AllocateZeroPool (SendDataSize);
> + if (SendData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + ((IPMI_BLOB_TRANSFER_BLOB_COMMIT_SEND_DATA *)SendData)->SessionId = SessionId;
> + ((IPMI_BLOB_TRANSFER_BLOB_COMMIT_SEND_DATA *)SendData)->CommitDataLength = CommitDataLength;
> + CopyMem (((IPMI_BLOB_TRANSFER_BLOB_COMMIT_SEND_DATA *)SendData)->CommitData, CommitData, sizeof (UINT8) * CommitDataLength);
> +
> + ResponseDataSize = 0;
> +
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandCommit, SendData, SendDataSize, NULL, &ResponseDataSize);
> +
> + FreePool (SendData);
> + return Status;
> +}
> +
> +/**
> + This function close a session associated with a blob transfer over the IPMI.
> +
> + @param[in] SessionId The session ID returned from a call to 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 = sizeof (IPMI_BLOB_TRANSFER_BLOB_CLOSE_SEND_DATA);
> + SendData = AllocateZeroPool (SendDataSize);
> + if (SendData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + ((IPMI_BLOB_TRANSFER_BLOB_CLOSE_SEND_DATA *)SendData)->SessionId = SessionId;
> +
> + ResponseDataSize = 0;
> +
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandClose, SendData, SendDataSize, NULL, &ResponseDataSize);
> +
> + FreePool (SendData);
> + return Status;
> +}
> +
> +/**
> + This function deletes a specific blob identified by its ID over the IPMI.
> +
> + @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;
> +
> + if (BlobId == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + //
> + // Format send data
> + //
> + SendDataSize = sizeof (IPMI_BLOB_TRANSFER_BLOB_DELETE_SEND_DATA);
> + SendData = AllocateZeroPool (SendDataSize);
> + if (SendData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + AsciiStrCpyS (((IPMI_BLOB_TRANSFER_BLOB_DELETE_SEND_DATA *)SendData)->BlobId, AsciiStrLen (BlobId), BlobId);
> +
> + ResponseDataSize = 0;
> +
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandDelete, SendData, SendDataSize, NULL, &ResponseDataSize);
> +
> + FreePool (SendData);
> + return Status;
> +}
> +
> +/**
> + This function retrieve the status of a specific blob identified by BlobId from an IPMI.
> +
> + @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 ((BlobId == NULL) || (BlobState == NULL) || (Size == NULL) || (MetadataLength == NULL)) {
Could we make Metadata **per spec**, MetadataLength, and Size optional?
We could not care them rather than BlobState.
This comment applies to IpmiBlobTransferSessionStat () as well.
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (Metadata == NULL) {
> + ASSERT (FALSE);
> + return EFI_ABORTED;
> + }
> +
> + ResponseDataSize = sizeof (IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE);
> + ResponseData = AllocateZeroPool (ResponseDataSize);
> + if (ResponseData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + //
> + // Format send data
> + //
> + SendDataSize = sizeof (IPMI_BLOB_TRANSFER_BLOB_STAT_SEND_DATA);
> + SendData = AllocateZeroPool (SendDataSize);
> + if (SendData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + AsciiStrCpyS (((IPMI_BLOB_TRANSFER_BLOB_STAT_SEND_DATA *)SendData)->BlobId, BLOB_MAX_DATA_PER_PACKET, BlobId);
> +
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandStat, SendData, SendDataSize, (UINT8 *)ResponseData, &ResponseDataSize);
> + if (!EFI_ERROR (Status)) {
> + *BlobState = ((IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE *)ResponseData)->BlobState;
> + *Size = ((IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE *)ResponseData)->Size;
> + *MetadataLength = ((IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE *)ResponseData)->MetaDataLen;
> +
> + CopyMem (&Metadata, &((IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE *)ResponseData)->MetaData, sizeof (((IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE *)ResponseData)->MetaData));
> + }
> +
> + FreePool (ResponseData);
> + FreePool (SendData);
> + return Status;
> +}
> +
> +/**
> + This function query the status of a blob transfer session in an IPMI.
> +
> + @param[in] SessionId The ID of the session 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
> +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 == NULL) || (Size == NULL) || (MetadataLength == NULL) || (Metadata == NULL)) {
> + ASSERT (FALSE);
> + return EFI_ABORTED;
> + }
> +
> + ResponseDataSize = sizeof (IPMI_BLOB_TRANSFER_BLOB_STAT_RESPONSE);
> + ResponseData = AllocateZeroPool (ResponseDataSize);
> + if (ResponseData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + //
> + // Format send data
> + //
> + SendDataSize = sizeof (IPMI_BLOB_TRANSFER_BLOB_SESSION_STAT_SEND_DATA);
> + SendData = AllocateZeroPool (SendDataSize);
> + if (SendData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + ((IPMI_BLOB_TRANSFER_BLOB_SESSION_STAT_SEND_DATA *)SendData)->SessionId = SessionId;
> +
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandSessionStat, SendData, SendDataSize, (UINT8 *)ResponseData, &ResponseDataSize);
> +
> + if (!EFI_ERROR (Status)) {
> + *BlobState = ((IPMI_BLOB_TRANSFER_BLOB_SESSION_STAT_RESPONSE *)ResponseData)->BlobState;
> + *Size = ((IPMI_BLOB_TRANSFER_BLOB_SESSION_STAT_RESPONSE *)ResponseData)->Size;
> + *MetadataLength = ((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_RESPONSE *)ResponseData)->MetaData));
> + }
> +
> + FreePool (ResponseData);
> + FreePool (SendData);
> + return Status;
> +}
> +
> +/**
> + This function writes metadata to a blob associated with a session in an IPMI.
> +
> + @param[in] SessionId The ID of the session to write metadata for
> + @param[in] Offset The offset of the metadata to write to
> + @param[in] Data The data to write to the metadata
> + @param[in] WriteLength The length to write
> +
> + @retval EFI_SUCCESS The blob metadata was successfully written.
> + @retval Other An error occurred
> +**/
> +EFI_STATUS
> +IpmiBlobTransferWriteMeta (
> + IN UINT16 SessionId,
> + IN UINT32 Offset,
> + IN UINT8 *Data,
How do callers know the data format of metadata for writing correctly?
> + IN UINT32 WriteLength
Should check with BLOB_MAX_DATA_PER_PACKET
> + )
> +{
> + EFI_STATUS Status;
> + UINT8 *SendData;
> + UINT32 SendDataSize;
> + UINT32 ResponseDataSize;
> +
> + if (Data == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + //
> + // Format send data
> + //
> + SendDataSize = sizeof (IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA);
> + SendData = AllocateZeroPool (SendDataSize);
> + if (SendData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + ((IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA *)SendData)->SessionId = SessionId;
> + ((IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA *)SendData)->Offset = Offset;
> + CopyMem (((IPMI_BLOB_TRANSFER_BLOB_WRITE_SEND_DATA *)SendData)->Data, Data, sizeof (UINT8) * WriteLength);
> +
> + ResponseDataSize = 0;
> +
> + Status = 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 including
> + 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,
Nit: Typically, we could also use gImageHandle instead.
> + &gEdkiiIpmiBlobTransferProtocolGuid,
> + (VOID *)&mIpmiBlobTransfer,
> + NULL
> + );
> +}
> diff --git a/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlobTransferTestUnitTests.c b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlobTransferTestUnitTests.c
> new file mode 100644
> index 0000000000..0f728527b8
> --- /dev/null
> +++ b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlobTransferTestUnitTests.c
> @@ -0,0 +1,1113 @@
> +/** @file
> +*
> +* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
> +*
> +* SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES
> +* SPDX-License-Identifier: BSD-2-Clause-Patent
> +*
> +**/
> +#include <stdarg.h>
> +#include <stddef.h>
> +#include <setjmp.h>
> +#include <stdint.h>
> +#include <cmocka.h>
> +
> +#include <Uefi.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/HostBasedTestStubLib/IpmiStubLib.h>
> +
> +#include <Library/UnitTestLib.h>
> +#include <Protocol/IpmiBlobTransfer.h>
> +#include "../InternalIpmiBlobTransfer.h"
> +
> +#define UNIT_TEST_NAME "IPMI Blob Transfer Unit Tests"
> +#define UNIT_TEST_VERSION "1.0"
> +
> +UINT8 InvalidCompletion[] = {
> + 0xC0, // CompletionCode
> + 0xCF, 0xC2, 0x00, // OpenBMC OEN
> +};
> +#define INVALID_COMPLETION_SIZE 4 * sizeof(UINT8)
> +
> +UINT8 NoDataResponse[] = {
> + 0x00, // CompletionCode
> + 0xCF, 0xC2, 0x00, // OpenBMC OEN
> +};
> +#define NO_DATA_RESPONSE_SIZE 4 * sizeof(UINT8)
> +
> +UINT8 BadOenResponse[] = {
> + 0x00, // CompletionCode
> + 0xFF, 0xC2, 0x00, // Wrong OEN
> +};
> +#define BAD_OEN_RESPONSE_SIZE 4 * sizeof(UINT8)
> +
> +UINT8 BadCrcResponse[] = {
> + 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[] = {
> + 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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] = { 0x12, 0x34, 0x56, 0x78, 0x90 };
> + UINTN DataSize;
> + UINT16 Crc;
> +
> + DataSize = sizeof (Data);
> +
> + Crc = CalculateCrc16Ccitt (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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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] = { 0x12, 0x34, 0x56, 0x78, 0x90 };
> + UINTN DataSize;
> + UINT16 Crc;
> +
> + DataSize = sizeof (Data);
> +
> + Crc = CalculateCrc16Ccitt (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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (INVALID_COMPLETION_SIZE);
> + ResponseDataSize = (UINT32 *)AllocateZeroPool (sizeof (UINT32));
> + CopyMem (MockResponseResults, &InvalidCompletion, INVALID_COMPLETION_SIZE);
> +
> + MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, INVALID_COMPLETION_SIZE, EFI_SUCCESS);
> +
> + ResponseData = (UINT8 *)AllocateZeroPool (*ResponseDataSize);
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandGetCount, 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (NO_DATA_RESPONSE_SIZE);
> + ResponseDataSize = (UINT32 *)AllocateZeroPool (sizeof (UINT32));
> + CopyMem (MockResponseResults, &NoDataResponse, NO_DATA_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, NO_DATA_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + ResponseData = (UINT8 *)AllocateZeroPool (sizeof (NoDataResponse));
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandGetCount, 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (BAD_OEN_RESPONSE_SIZE);
> + ResponseDataSize = (UINT32 *)AllocateZeroPool (sizeof (UINT32));
> + CopyMem (MockResponseResults, &BadOenResponse, BAD_OEN_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, BAD_OEN_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + ResponseData = (UINT8 *)AllocateZeroPool (sizeof (BadOenResponse));
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandGetCount, 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (BAD_CRC_RESPONSE_SIZE));
> + ResponseDataSize = (UINT32 *)AllocateZeroPool (sizeof (UINT32));
> + CopyMem (MockResponseResults, &BadCrcResponse, BAD_CRC_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, BAD_CRC_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + ResponseData = (UINT8 *)AllocateZeroPool (sizeof (BadCrcResponse));
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandGetCount, NULL, 0, ResponseData, ResponseDataSize);
> +
> + UT_ASSERT_STATUS_EQUAL (Status, EFI_CRC_ERROR);
> + FreePool (MockResponseResults);
> + FreePool (ResponseDataSize);
> + FreePool (ResponseData);
> + return UNIT_TEST_PASSED;
> +}
> +
> +UINT8 ValidGetCountResponse[] = {
> + 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_GET_COUNT_RESPONSE_SIZE));
> + ResponseDataSize = (UINT32 *)AllocateZeroPool (sizeof (UINT32));
> + CopyMem (MockResponseResults, &ValidGetCountResponse, VALID_GET_COUNT_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_GET_COUNT_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + ResponseData = AllocateZeroPool (sizeof (ValidGetCountResponse));
> + Status = IpmiBlobTransferSendIpmi (IpmiBlobTransferSubcommandGetCount, 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + Count = 0;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_GET_COUNT_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidGetCountResponse, VALID_GET_COUNT_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_GET_COUNT_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + Status = IpmiBlobTransferGetCount (&Count);
> +
> + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS);
> + UT_ASSERT_EQUAL (Count, 1);
> + FreePool (MockResponseResults);
> + return UNIT_TEST_PASSED;
> +}
> +
> +UINT8 ValidEnumerateResponse[] = {
> + 0x00, // CompletionCode
> + 0xCF, 0xC2, 0x00, // OpenBMC OEN
> + 0x81, 0x13, // CRC
> + 0x2F, 0x73, 0x6D, 0x62, // Data = "/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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_ENUMERATE_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidEnumerateResponse, VALID_ENUMERATE_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_ENUMERATE_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + BlobId = AllocateZeroPool (sizeof (CHAR8) * BLOB_MAX_DATA_PER_PACKET);
> +
> + Status = 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_ENUMERATE_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidEnumerateResponse, VALID_ENUMERATE_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_ENUMERATE_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + BlobId = NULL;
> +
> + UT_EXPECT_ASSERT_FAILURE (IpmiBlobTransferEnumerate (0, BlobId), NULL);
> +
> + FreePool (MockResponseResults);
> + return UNIT_TEST_PASSED;
> +}
> +
> +UINT8 ValidOpenResponse[] = {
> + 0x00, // CompletionCode
> + 0xCF, 0xC2, 0x00, // OpenBMC OEN
> + 0x93, 0xD1, // CRC
> + 0x03, 0x00, // SessionId = 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> + VOID *MockResponseResults2 = NULL;
> + VOID *MockResponseResults3 = NULL;
> +
> + Flags = 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 = (UINT8 *)AllocateZeroPool (sizeof (VALID_OPEN_RESPONSE_SIZE));
> +
> + CopyMem (MockResponseResults, &ValidOpenResponse, VALID_OPEN_RESPONSE_SIZE);
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_OPEN_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + MockResponseResults2 = (UINT8 *)AllocateZeroPool (sizeof (VALID_ENUMERATE_RESPONSE_SIZE));
> + CopyMem (MockResponseResults2, &ValidEnumerateResponse, VALID_ENUMERATE_RESPONSE_SIZE);
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults2, VALID_ENUMERATE_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + MockResponseResults3 = (UINT8 *)AllocateZeroPool (sizeof (VALID_GET_COUNT_RESPONSE_SIZE));
> + CopyMem (MockResponseResults3, &ValidGetCountResponse, VALID_GET_COUNT_RESPONSE_SIZE);
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults3, VALID_GET_COUNT_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + BlobId = "/smbios";
> +
> + Status = IpmiBlobTransferOpen (BlobId, Flags, &SessionId);
> +
> + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS);
> + UT_ASSERT_EQUAL (SessionId, 3);
> + FreePool (MockResponseResults);
> + return UNIT_TEST_PASSED;
> +}
> +
> +UINT8 ValidReadResponse[] = {
> + 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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] = { 0x00, 0x01, 0x02, 0x03 };
> + VOID *MockResponseResults = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_READ_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidReadResponse, VALID_READ_RESPONSE_SIZE);
> + ResponseData = AllocateZeroPool (sizeof (ValidReadResponse));
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_READ_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + Status = 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_READ_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidReadResponse, VALID_READ_RESPONSE_SIZE);
> + ResponseData = NULL;
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_READ_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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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] = { 0x00, 0x01, 0x02, 0x03 };
> + VOID *MockResponseResults = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_NODATA_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidNoDataResponse, VALID_NODATA_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_NODATA_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + Status = 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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] = { 0x00, 0x01, 0x02, 0x03 };
> + VOID *MockResponseResults = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_NODATA_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidNoDataResponse, VALID_NODATA_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_NODATA_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + Status = 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_NODATA_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidNoDataResponse, VALID_NODATA_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_NODATA_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + Status = 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_NODATA_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidNoDataResponse, VALID_NODATA_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_NODATA_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + Status = IpmiBlobTransferDelete ("/smbios");
> +
> + UT_ASSERT_STATUS_EQUAL (Status, EFI_SUCCESS);
> + FreePool (MockResponseResults);
> + return UNIT_TEST_PASSED;
> +}
> +
> +UINT8 ValidBlobStatResponse[] = {
> + 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + BlobState = AllocateZeroPool (sizeof (UINT16));
> + Size = AllocateZeroPool (sizeof (UINT32));
> + BlobId = "BlobId";
> + MetadataLength = AllocateZeroPool (sizeof (UINT8));
> + Metadata = AllocateZeroPool (4 * sizeof (UINT8));
> + ExpectedMetadata = AllocateZeroPool (4 * sizeof (UINT8));
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_BLOB_STAT_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidBlobStatResponse, VALID_BLOB_STAT_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_BLOB_STAT_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + Status = 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + Metadata = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_BLOB_STAT_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidBlobStatResponse, VALID_BLOB_STAT_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_BLOB_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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + BlobState = AllocateZeroPool (sizeof (UINT16));
> + Size = AllocateZeroPool (sizeof (UINT32));
> + MetadataLength = AllocateZeroPool (sizeof (UINT8));
> + Metadata = AllocateZeroPool (4 * sizeof (UINT8));
> + ExpectedMetadata = AllocateZeroPool (4 * sizeof (UINT8));
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_BLOB_STAT_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidBlobStatResponse, VALID_BLOB_STAT_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_BLOB_STAT_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + Status = IpmiBlobTransferSessionStat (0, 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + Metadata = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_BLOB_STAT_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidBlobStatResponse, VALID_BLOB_STAT_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_BLOB_STAT_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + UT_EXPECT_ASSERT_FAILURE (IpmiBlobTransferSessionStat (0, 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 that may
> + consume it.
> + @retval UNIT_TEST_PASSED The Unit test has completed and the 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 = NULL;
> +
> + MockResponseResults = (UINT8 *)AllocateZeroPool (sizeof (VALID_NODATA_RESPONSE_SIZE));
> + CopyMem (MockResponseResults, &ValidNoDataResponse, VALID_NODATA_RESPONSE_SIZE);
> +
> + Status = MockIpmiSubmitCommand ((UINT8 *)MockResponseResults, VALID_NODATA_RESPONSE_SIZE, EFI_SUCCESS);
> + if (EFI_ERROR (Status)) {
> + return UNIT_TEST_ERROR_TEST_FAILED;
> + }
> +
> + Status = 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 = NULL;
> + DEBUG ((DEBUG_INFO, "%a: v%a\n", UNIT_TEST_NAME, UNIT_TEST_VERSION));
> +
> + Status = InitUnitTestFramework (&Framework, UNIT_TEST_NAME, gEfiCallerBaseName, UNIT_TEST_VERSION);
> + if (EFI_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "Failed to setup Test Framework. Exiting with status = %r\n", Status));
> + ASSERT (FALSE);
> + return Status;
> + }
> +
> + //
> + // Populate the Unit Test Suite.
> + //
> + Status = CreateUnitTestSuite (&IpmiBlobTransfer, Framework, "IPMI Blob Transfer Tests", "UnitTest.IpmiBlobTransferCB", NULL, NULL);
> + if (EFI_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for IPMI Blob Transfer Tests\n"));
> + Status = EFI_OUT_OF_RESOURCES;
> + return Status;
> + }
> +
> + // CalculateCrc16Ccitt
> + Status = AddTestCase (IpmiBlobTransfer, "Test CRC Calculation", "GoodCrc", GoodCrc, NULL, NULL, NULL);
> + Status = AddTestCase (IpmiBlobTransfer, "Test Bad CRC Calculation", "BadCrc", BadCrc, NULL, NULL, NULL);
> + // IpmiBlobTransferSendIpmi
> + Status = AddTestCase (IpmiBlobTransfer, "Send IPMI returns bad completion", "SendIpmiBadCompletion", SendIpmiBadCompletion, NULL, NULL, NULL);
> + Status = AddTestCase (IpmiBlobTransfer, "Send IPMI returns successfully with no data", "SendIpmiNoDataResponse", SendIpmiNoDataResponse, NULL, NULL, NULL);
> + Status = AddTestCase (IpmiBlobTransfer, "Send IPMI returns successfully with bad OEN", "SendIpmiBadOenResponse", SendIpmiBadOenResponse, NULL, NULL, NULL);
> + Status = AddTestCase (IpmiBlobTransfer, "Send IPMI returns successfully with bad CRC", "SendIpmiBadCrcResponse", SendIpmiBadCrcResponse, NULL, NULL, NULL);
> + Status = AddTestCase (IpmiBlobTransfer, "Send IPMI returns with valid GetCount data", "SendIpmiValidCountResponse", SendIpmiValidCountResponse, NULL, NULL, NULL);
> + // IpmiBlobTransferGetCount
> + Status = AddTestCase (IpmiBlobTransfer, "GetCount call with valid data", "GetCountValidCountResponse", GetCountValidCountResponse, NULL, NULL, NULL);
> + // IpmiBlobTransferEnumerate
> + Status = AddTestCase (IpmiBlobTransfer, "Enumerate call with valid data", "EnumerateValidResponse", EnumerateValidResponse, NULL, NULL, NULL);
> + Status = AddTestCase (IpmiBlobTransfer, "Enumerate call with invalid output buffer", "EnumerateInvalidBuffer", EnumerateInvalidBuffer, NULL, NULL, NULL);
> + // IpmiBlobTransferOpen
> + Status = AddTestCase (IpmiBlobTransfer, "Open call with valid data", "OpenValidResponse", OpenValidResponse, NULL, NULL, NULL);
> + // IpmiBlobTransferRead
> + Status = AddTestCase (IpmiBlobTransfer, "Read call with valid data", "ReadValidResponse", ReadValidResponse, NULL, NULL, NULL);
> + Status = AddTestCase (IpmiBlobTransfer, "Read call with invalid buffer", "ReadInvalidBuffer", ReadInvalidBuffer, NULL, NULL, NULL);
> + // IpmiBlobTransferWrite
> + Status = AddTestCase (IpmiBlobTransfer, "Write call with valid data", "WriteValidResponse", WriteValidResponse, NULL, NULL, NULL);
> + // IpmiBlobTransferCommit
> + Status = AddTestCase (IpmiBlobTransfer, "Commit call with valid data", "CommitValidResponse", CommitValidResponse, NULL, NULL, NULL);
> + // IpmiBlobTransferClose
> + Status = AddTestCase (IpmiBlobTransfer, "Close call with valid data", "CloseValidResponse", CloseValidResponse, NULL, NULL, NULL);
> + // IpmiBlobTransferDelete
> + Status = AddTestCase (IpmiBlobTransfer, "Delete call with valid data", "DeleteValidResponse", DeleteValidResponse, NULL, NULL, NULL);
> + // IpmiBlobTransferStat
> + Status = AddTestCase (IpmiBlobTransfer, "Blob Stat call with valid data", "BlobStatValidResponse", BlobStatValidResponse, NULL, NULL, NULL);
> + Status = AddTestCase (IpmiBlobTransfer, "Blob Stat call with invalid buffer", "BlobStatInvalidBuffer", BlobStatInvalidBuffer, NULL, NULL, NULL);
> + // IpmiBlobTransferSessionStat
> + Status = AddTestCase (IpmiBlobTransfer, "Session Stat call with valid data", "SessionStatValidResponse", SessionStatValidResponse, NULL, NULL, NULL);
> + Status = AddTestCase (IpmiBlobTransfer, "Session Stat call with invalid buffer", "SessionStatInvalidBuffer", SessionStatInvalidBuffer, NULL, NULL, NULL);
> + // IpmiBlobTransferWriteMeta
> + Status = AddTestCase (IpmiBlobTransfer, "WriteMeta call with valid data", "WriteMetaValidResponse", WriteMetaValidResponse, NULL, NULL, NULL);
> +
> + // Execute the tests.
> + Status = 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..9eed5d3728
> --- /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 Interface defined in OpenBMC
> +https://github.com/openbmc/phosphor-ipmi-blobs
> +
> +## 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 gEdkiiIpmiBlobTransferProtocolGuid in its inf "Depex" section
> +2) The module should list gEdkiiIpmiBlobTransferProtocolGuid in its inf "Protocol" section
> +3) The module's entry point should do a LocateProtocol on gEdkiiIpmiBlobTransferProtocolGuid
> +
> +### 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 implementation.
> +Any changes to IpmiBlobTransferDxe should include proof of successful unit tests.
> +
> +### Debugging
> +To assist in debugging any issues, change BLOB_TRANSFER_DEBUG to desired debug level, such as DEBUG_ERROR or DEBUG_INFO.
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#118993): https://edk2.groups.io/g/devel/message/118993
Mute This Topic: https://groups.io/mt/106115743/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
next prev parent reply other threads:[~2024-05-17 7:49 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-05-15 15:06 [edk2-devel] [edk2-platforms][PATCH v2] ManageabilityPkg: add support for the phosphor ipmi blob transfer protocol Nickle Wang via groups.io
2024-05-17 7:49 ` Nhi Pham via groups.io [this message]
2024-05-17 8:16 ` Chang, Abner via groups.io
2024-05-17 8:34 ` Nhi Pham via groups.io
2024-07-10 15:12 ` Nickle Wang via groups.io
2024-07-11 1:58 ` Nhi Pham via groups.io
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=7d928ca7-e312-4a2e-9470-cbd96fbdc643@os.amperecomputing.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox