public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [edk2-devel] [edk2-platforms][PATCH v2] ManageabilityPkg: add support for the phosphor ipmi blob transfer protocol
@ 2024-05-15 15:06 Nickle Wang via groups.io
  2024-05-17  7:49 ` Nhi Pham via groups.io
  0 siblings, 1 reply; 6+ messages in thread
From: Nickle Wang via groups.io @ 2024-05-15 15:06 UTC (permalink / raw)
  To: devel
  Cc: Abner Chang, Abdul Lateef Attar, Tinh Nguyen, Nhi Pham,
	Thang Nguyen OS, Mike Maslenkin

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
+**/
+#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 };
+
+//
+//  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
+
+**/
+
+#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
+#define BLOB_MAX_DATA_PER_PACKET    64
+
+// 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,
+  OUT UINT8   *ResponseData,
+  OUT UINT32  *ResponseDataSize
+  )
+{
+  EFI_STATUS                 Status;
+  UINT8                      CompletionCode;
+  UINT16                     Crc;
+  UINT8                      Oen[3];
+  UINT8                      *IpmiSendData;
+  UINT32                     IpmiSendDataSize;
+  UINT8                      *IpmiResponseData;
+  UINT8                      *ModifiedResponseData;
+  UINT32                     IpmiResponseDataSize;
+  IPMI_BLOB_TRANSFER_HEADER  Header;
+
+  Crc = 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) {
+    //
+    // 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) {
+    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;
+  }
+
+  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) {
+    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
+  @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
+  //
+  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;
+
+  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,
+  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;
+  }
+
+  ResponseDataSize = RequestedSize * sizeof (UINT8);
+  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) {
+    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;
+  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) {
+    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)) {
+    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,
+  IN  UINT32  WriteLength
+  )
+{
+  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,
+                &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.
-- 
2.34.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#118922): https://edk2.groups.io/g/devel/message/118922
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]
-=-=-=-=-=-=-=-=-=-=-=-



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [edk2-devel] [edk2-platforms][PATCH v2] ManageabilityPkg: add support for the phosphor ipmi blob transfer protocol
  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
  2024-05-17  8:16   ` Chang, Abner via groups.io
  2024-07-10 15:12   ` Nickle Wang via groups.io
  0 siblings, 2 replies; 6+ messages in thread
From: Nhi Pham via groups.io @ 2024-05-17  7:49 UTC (permalink / raw)
  To: Nickle Wang, devel
  Cc: Abner Chang, Abdul Lateef Attar, Tinh Nguyen, Thang Nguyen OS,
	Mike Maslenkin

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]
-=-=-=-=-=-=-=-=-=-=-=-



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [edk2-devel] [edk2-platforms][PATCH v2] ManageabilityPkg: add support for the phosphor ipmi blob transfer protocol
  2024-05-17  7:49 ` Nhi Pham via groups.io
@ 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
  1 sibling, 1 reply; 6+ messages in thread
From: Chang, Abner via groups.io @ 2024-05-17  8:16 UTC (permalink / raw)
  To: Nhi Pham, Nickle Wang, devel@edk2.groups.io
  Cc: Attar, AbdulLateef (Abdul Lateef), Tinh Nguyen, Thang Nguyen OS,
	Mike Maslenkin

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Nhi,
How much effort you think to have the SSIF ManageabilityPkg port?

Regards,
Abner

> -----Original Message-----
> From: Nhi Pham <nhi@os.amperecomputing.com>
> Sent: Friday, May 17, 2024 3:49 PM
> To: Nickle Wang <nicklew@nvidia.com>; devel@edk2.groups.io
> Cc: Chang, Abner <Abner.Chang@amd.com>; Attar, AbdulLateef (Abdul
> Lateef) <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-platforms][PATCH v2] ManageabilityPkg: add support for
> the phosphor ipmi blob transfer protocol
>
> Caution: This message originated from an External Source. Use proper caution
> when opening attachments, clicking links, or responding.
>
>
> 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/IpmiBlobTransfer
> Dxe.inf
> >   create mode 100644
> Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlo
> bTransferTestUnitTestsHost.inf
> >   create mode 100644
> Features/ManageabilityPkg/Include/Protocol/IpmiBlobTransfer.h
> >   create mode 100644
> Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/InternalIpmiBlob
> Transfer.h
> >   create mode 100644
> Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransfer
> Dxe.c
> >   create mode 100644
> Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlo
> bTransferTestUnitTests.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/IpmiBlobTransf
> erDxe.inf
> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransf
> erDxe.inf
> > new file mode 100644
> > index 0000000000..108f4bb5f8
> > --- /dev/null
> > +++
> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransf
> erDxe.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/IpmiB
> lobTransferTestUnitTestsHost.inf
> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiB
> lobTransferTestUnitTestsHost.inf
> > new file mode 100644
> > index 0000000000..dab6858f09
> > --- /dev/null
> > +++
> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiB
> lobTransferTestUnitTestsHost.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/InternalIpmiBlo
> bTransfer.h
> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/InternalIpmiBl
> obTransfer.h
> > new file mode 100644
> > index 0000000000..3e90dc6871
> > --- /dev/null
> > +++
> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/InternalIpmiBl
> obTransfer.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/IpmiBlobTransf
> erDxe.c
> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransf
> erDxe.c
> > new file mode 100644
> > index 0000000000..b8a2db193b
> > --- /dev/null
> > +++
> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransf
> erDxe.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)*IpmiBlobTransferGe
> tCount,
> > +
> (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_ENUMERATE)*IpmiBlobTransferEn
> umerate,
> > +
> (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)*IpmiBlobTransferCom
> mit,
> > +
> (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)*IpmiBlobTransfer
> SessionStat,
> > +
> (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_WRITE_META)*IpmiBlobTransfer
> WriteMeta
> > +};
> > +
> > +/**
> > +  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/IpmiB
> lobTransferTestUnitTests.c
> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiB
> lobTransferTestUnitTests.c
> > new file mode 100644
> > index 0000000000..0f728527b8
> > --- /dev/null
> > +++
> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiB
> lobTransferTestUnitTests.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 (#118994): https://edk2.groups.io/g/devel/message/118994
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]
-=-=-=-=-=-=-=-=-=-=-=-



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [edk2-devel] [edk2-platforms][PATCH v2] ManageabilityPkg: add support for the phosphor ipmi blob transfer protocol
  2024-05-17  8:16   ` Chang, Abner via groups.io
@ 2024-05-17  8:34     ` Nhi Pham via groups.io
  0 siblings, 0 replies; 6+ messages in thread
From: Nhi Pham via groups.io @ 2024-05-17  8:34 UTC (permalink / raw)
  To: Chang, Abner, Nickle Wang, devel@edk2.groups.io
  Cc: Attar, AbdulLateef (Abdul Lateef), Tinh Nguyen, Thang Nguyen OS,
	Mike Maslenkin

Hi Abner,

It' hard to say actually. I don't spend full-time for open-source work. 
But I will try to complete it within 2 weeks or sooner.

Most of Ampere Altra drivers including IPMI SSIF are living at 
https://github.com/AmpereComputing/edk2-platforms. The effort now is to 
port to be compatible with ManageabilityPkg.

Regards,
Nhi

On 5/17/2024 3:16 PM, Chang, Abner wrote:
> [AMD Official Use Only - AMD Internal Distribution Only]
> 
> Hi Nhi,
> How much effort you think to have the SSIF ManageabilityPkg port?
> 
> Regards,
> Abner
> 
>> -----Original Message-----
>> From: Nhi Pham <nhi@os.amperecomputing.com>
>> Sent: Friday, May 17, 2024 3:49 PM
>> To: Nickle Wang <nicklew@nvidia.com>; devel@edk2.groups.io
>> Cc: Chang, Abner <Abner.Chang@amd.com>; Attar, AbdulLateef (Abdul
>> Lateef) <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-platforms][PATCH v2] ManageabilityPkg: add support for
>> the phosphor ipmi blob transfer protocol
>>
>> Caution: This message originated from an External Source. Use proper caution
>> when opening attachments, clicking links, or responding.
>>
>>
>> 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/IpmiBlobTransfer
>> Dxe.inf
>>>    create mode 100644
>> Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlo
>> bTransferTestUnitTestsHost.inf
>>>    create mode 100644
>> Features/ManageabilityPkg/Include/Protocol/IpmiBlobTransfer.h
>>>    create mode 100644
>> Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/InternalIpmiBlob
>> Transfer.h
>>>    create mode 100644
>> Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransfer
>> Dxe.c
>>>    create mode 100644
>> Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlo
>> bTransferTestUnitTests.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/IpmiBlobTransf
>> erDxe.inf
>> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransf
>> erDxe.inf
>>> new file mode 100644
>>> index 0000000000..108f4bb5f8
>>> --- /dev/null
>>> +++
>> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransf
>> erDxe.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/IpmiB
>> lobTransferTestUnitTestsHost.inf
>> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiB
>> lobTransferTestUnitTestsHost.inf
>>> new file mode 100644
>>> index 0000000000..dab6858f09
>>> --- /dev/null
>>> +++
>> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiB
>> lobTransferTestUnitTestsHost.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/InternalIpmiBlo
>> bTransfer.h
>> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/InternalIpmiBl
>> obTransfer.h
>>> new file mode 100644
>>> index 0000000000..3e90dc6871
>>> --- /dev/null
>>> +++
>> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/InternalIpmiBl
>> obTransfer.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/IpmiBlobTransf
>> erDxe.c
>> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransf
>> erDxe.c
>>> new file mode 100644
>>> index 0000000000..b8a2db193b
>>> --- /dev/null
>>> +++
>> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransf
>> erDxe.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)*IpmiBlobTransferGe
>> tCount,
>>> +
>> (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_ENUMERATE)*IpmiBlobTransferEn
>> umerate,
>>> +
>> (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)*IpmiBlobTransferCom
>> mit,
>>> +
>> (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)*IpmiBlobTransfer
>> SessionStat,
>>> +
>> (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_WRITE_META)*IpmiBlobTransfer
>> WriteMeta
>>> +};
>>> +
>>> +/**
>>> +  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/IpmiB
>> lobTransferTestUnitTests.c
>> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiB
>> lobTransferTestUnitTests.c
>>> new file mode 100644
>>> index 0000000000..0f728527b8
>>> --- /dev/null
>>> +++
>> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiB
>> lobTransferTestUnitTests.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 (#118995): https://edk2.groups.io/g/devel/message/118995
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]
-=-=-=-=-=-=-=-=-=-=-=-



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [edk2-devel] [edk2-platforms][PATCH v2] ManageabilityPkg: add support for the phosphor ipmi blob transfer protocol
  2024-05-17  7:49 ` Nhi Pham via groups.io
  2024-05-17  8:16   ` Chang, Abner via groups.io
@ 2024-07-10 15:12   ` Nickle Wang via groups.io
  2024-07-11  1:58     ` Nhi Pham via groups.io
  1 sibling, 1 reply; 6+ messages in thread
From: Nickle Wang via groups.io @ 2024-07-10 15:12 UTC (permalink / raw)
  To: Nhi Pham, devel@edk2.groups.io
  Cc: Abner Chang, Abdul Lateef Attar, Tinh Nguyen, Thang Nguyen OS,
	Mike Maslenkin

[-- Attachment #1: Type: text/plain, Size: 133558 bytes --]

Hi @Nhi Pham<mailto:nhi@os.amperecomputing.com>,



I am sorry for taking so long to address your review comments. I updated pull request here: https://github.com/tianocore/edk2-platforms/pull/76



I addressed most of comments and I have questions about below review comments. Please find my feedback inline below.



> Should we add a PCD for the OEN to be configured by platform specific

> BMC? Or this protocol is only to support OpenBMC.



Yes, per description in Readme.md, this is the protocol only to support OpenBMC implementation.



> 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?



With this implementation, user can call open directly and it will check the presence of blob for us. And yes, this is how we use it in NVIDIA driver.



> 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?



Caller can only get the session ID by calling open(). Session ID is required when calling read, write, and commit. If caller make up a session ID, caller is expected to see error return.



> How do callers know the data format of metadata for writing correctly?



I check the spec here: https://github.com/openbmc/phosphor-ipmi-blobs?tab=readme-ov-file#bmcblobwritemeta-10  And there is no description about data format.  Since we don't use this function in our driver, may I know if you have suggestion for this?





> +IpmiBlobTransferDxeDriverEntryPoint (

> +  IN EFI_HANDLE        ImageHandle,

> +  IN EFI_SYSTEM_TABLE  *SystemTable

> +  )

> +{

> +  return gBS->InstallMultipleProtocolInterfaces (

> +                &ImageHandle,

> Nit: Typically, we could also use gImageHandle instead.



Sorry I don't follow you here.

gImageHandle is used as global variable in driver. But here we just use the ImageHandle from function parameter and we don't keep it as global variable, right?





Regards,

Nickle



> -----Original Message-----

> From: Nhi Pham <nhi@os.amperecomputing.com>

> Sent: Friday, May 17, 2024 3:49 PM

> 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-platforms][PATCH v2] ManageabilityPkg: add support for the

> phosphor ipmi blob transfer protocol

>

> External email: Use caution opening links or attachments

>

>

> 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://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzi

> lla.tianocore.org%2Fshow_bug.cgi%3Fid%3D4773&data=05%7C02%7Cnicklew%4

> 0nvidia.com%7Cd22932100e16475feff608dc7645e177%7C43083d15727340c1b7

> db39efd9ccc17a%7C0%7C0%7C638515289789817250%7CUnknown%7CTWFpb

> GZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn

> 0%3D%7C0%7C%7C%7C&sdata=99Xm%2B3kir7CnWHFt%2FI04nBAuLT2SPeOKPE

> DIGkZpz%2FM%3D&reserved=0

> >

> > This change implements the blob transfer protocol used in OpenBmc

> > documented here:

> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.co<https://github.com/openbmc/phosphor-ipmi-blobs>

> m%2Fopenbmc%2Fphosphor-ipmi-<https://github.com/openbmc/phosphor-ipmi-blobs>

> blobs&data=05%7C02%7Cnicklew%40nvidia.com%7Cd22932100e16475feff608d<https://github.com/openbmc/phosphor-ipmi-blobs>

> c7645e177%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C638515289<https://github.com/openbmc/phosphor-ipmi-blobs>

> 789828686%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2<https://github.com/openbmc/phosphor-ipmi-blobs>

> luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=cqngm6B%<https://github.com/openbmc/phosphor-ipmi-blobs>

> 2F%2BGOfyIp5YqxVLVUvyHzRdpfiwcWNSD3SwZk%3D&reserved=0<https://github.com/openbmc/phosphor-ipmi-blobs>

> >

> > Signed-off-by: Nick Ramirez <nramirez@nvidia.com<mailto:nramirez@nvidia.com>>

> > Co-authored-by: Nickle Wang <nicklew@nvidia.com<mailto:nicklew@nvidia.com>>

> > Cc: Abner Chang <abner.chang@amd.com<mailto:abner.chang@amd.com>>

> > Cc: Abdul Lateef Attar <AbdulLateef.Attar@amd.com<mailto:AbdulLateef.Attar@amd.com>>

> > Cc: Tinh Nguyen <tinhnguyen@amperemail.onmicrosoft.com<mailto:tinhnguyen@amperemail.onmicrosoft.com>>

> > Cc: Nhi Pham <nhi@os.amperecomputing.com<mailto:nhi@os.amperecomputing.com>>

> > Cc: Thang Nguyen OS <thang@amperemail.onmicrosoft.com<mailto:thang@amperemail.onmicrosoft.com>>

> > Cc: Mike Maslenkin <mike.maslenkin@gmail.com<mailto: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/IpmiBlobTr

> ansferTestUnitTestsHost.inf

> >   create mode 100644

> Features/ManageabilityPkg/Include/Protocol/IpmiBlobTransfer.h

> >   create mode 100644

> Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/InternalIpmiBlobTra

> nsfer.h

> >   create mode 100644

> Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransferDxe

> .c

> >   create mode 100644

> Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlobTr

> ansferTestUnitTests.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/IpmiBlobTransferD

> xe.inf

> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransferD

> xe.inf

> > new file mode 100644

> > index 0000000000..108f4bb5f8

> > --- /dev/null

> > +++

> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransferD

> xe.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/IpmiBlob

> TransferTestUnitTestsHost.inf

> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlob

> TransferTestUnitTestsHost.inf

> > new file mode 100644

> > index 0000000000..dab6858f09

> > --- /dev/null

> > +++

> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlob

> TransferTestUnitTestsHost.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://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.co<https://github.com/openbmc/phosphor-ipmi-blobs/blob/master/README.md>

> m%2Fopenbmc%2Fphosphor-ipmi-<https://github.com/openbmc/phosphor-ipmi-blobs/blob/master/README.md>

> blobs%2Fblob%2Fmaster%2FREADME.md&data=05%7C02%7Cnicklew%40nvidia<https://github.com/openbmc/phosphor-ipmi-blobs/blob/master/README.md>

> .com%7Cd22932100e16475feff608dc7645e177%7C43083d15727340c1b7db39ef<https://github.com/openbmc/phosphor-ipmi-blobs/blob/master/README.md>

> d9ccc17a%7C0%7C0%7C638515289789834490%7CUnknown%7CTWFpbGZsb3d<https://github.com/openbmc/phosphor-ipmi-blobs/blob/master/README.md>

> 8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D<https://github.com/openbmc/phosphor-ipmi-blobs/blob/master/README.md>

> %7C0%7C%7C%7C&sdata=q6CCTU5qwF8mHHYus0AcBlZGCJpqNARsB9SO697NT<https://github.com/openbmc/phosphor-ipmi-blobs/blob/master/README.md>

> o0%3D&reserved=0<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/InternalIpmiBlobTr

> ansfer.h

> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/InternalIpmiBlobTr

> ansfer.h

> > new file mode 100644

> > index 0000000000..3e90dc6871

> > --- /dev/null

> > +++

> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/InternalIpmiBlobTr

> ansfer.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/IpmiBlobTransferD

> xe.c

> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransferD

> xe.c

> > new file mode 100644

> > index 0000000000..b8a2db193b

> > --- /dev/null

> > +++

> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/IpmiBlobTransferD

> xe.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)*IpmiBlobTransferGetC

> ount,

> > +

> (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_ENUMERATE)*IpmiBlobTransferEnu

> merate,

> > +  (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)*IpmiBlobTransferSes

> sionStat,

> > +

> (EDKII_IPMI_BLOB_TRANSFER_PROTOCOL_WRITE_META)*IpmiBlobTransferWrit

> eMeta

> > +};

> > +

> > +/**

> > +  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://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.co

> m%2Fopenbmc%2Fphosphor-ipmi-

> blobs&data=05%7C02%7Cnicklew%40nvidia.com%7Cd22932100e16475feff608d

> c7645e177%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C638515289

> 789839336%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2

> luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=fJbeNhF74

> Co7aHmkYc3U0ym1ISeNznrV2UXDNMEFcoE%3D&reserved=0,

> the commit data is block-specific optional.

>

> For instance, the commit data is optional for SMBIOS blob transfer. Look

> at

> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.co

> m%2Fopenbmc%2Fsmbios-

> mdr&data=05%7C02%7Cnicklew%40nvidia.com%7Cd22932100e16475feff608dc

> 7645e177%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C6385152897

> 89844890%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2l

> uMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=4OBktrF6b1

> vyHaI%2FvIOcXOCZQxaFZKwO3lPwLsjIgWY%3D&reserved=0

>

> > +    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/IpmiBlob

> TransferTestUnitTests.c

> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlob

> TransferTestUnitTests.c

> > new file mode 100644

> > index 0000000000..0f728527b8

> > --- /dev/null

> > +++

> b/Features/ManageabilityPkg/Universal/IpmiBlobTransferDxe/UnitTest/IpmiBlob

> TransferTestUnitTests.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://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.c

> om%2Fopenbmc%2Fphosphor-ipmi-

> blobs&data=05%7C02%7Cnicklew%40nvidia.com%7Cd22932100e16475feff608d

> c7645e177%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C638515289

> 789849374%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2

> luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=D7SscKqXG

> OZWyXJUtSBIMmBUUf%2FAAK0G%2FHrH0nrqCUE%3D&reserved=0

> > +

> > +## 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 (#119869): https://edk2.groups.io/g/devel/message/119869
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]
-=-=-=-=-=-=-=-=-=-=-=-



[-- Attachment #2: Type: text/html, Size: 305869 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [edk2-devel] [edk2-platforms][PATCH v2] ManageabilityPkg: add support for the phosphor ipmi blob transfer protocol
  2024-07-10 15:12   ` Nickle Wang via groups.io
@ 2024-07-11  1:58     ` Nhi Pham via groups.io
  0 siblings, 0 replies; 6+ messages in thread
From: Nhi Pham via groups.io @ 2024-07-11  1:58 UTC (permalink / raw)
  To: Nickle Wang, devel@edk2.groups.io
  Cc: Abner Chang, Abdul Lateef Attar, Tinh Nguyen, Thang Nguyen OS,
	Mike Maslenkin

On 7/10/2024 10:12 PM, Nickle Wang wrote:
> Hi @Nhi Pham <mailto:nhi@os.amperecomputing.com>,
> 
> I am sorry for taking so long to address your review comments. I updated 
> pull request here: https://github.com/tianocore/edk2-platforms/pull/76 
> <https://github.com/tianocore/edk2-platforms/pull/76>

Thanks, Nickle. I will continue reviewing (if any comments) on the PR 
instead.

> 
> I addressed most of comments and I have questions about below review 
> comments. Please find my feedback inline below.
> 
>  > Should we add a PCD for the OEN to be configured by platform specific
> 
>  > BMC? Or this protocol is only to support OpenBMC.
> 
> Yes, per description in Readme.md, this is the protocol only to support 
> OpenBMC implementation.
> 
>  > 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?
> 
> With this implementation, user can call open directly and it will check 
> the presence of blob for us. And yes, this is how we use it in NVIDIA 
> driver.
> 
>  > 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?
> 
> Caller can only get the session ID by calling open(). Session ID is 
> required when calling read, write, and commit. If caller make up a 
> session ID, caller is expected to see error return.
> 
>  > How do callers know the data format of metadata for writing correctly?
> 
> I check the spec here: 
> https://github.com/openbmc/phosphor-ipmi-blobs?tab=readme-ov-file#bmcblobwritemeta-10 <https://github.com/openbmc/phosphor-ipmi-blobs?tab=readme-ov-file#bmcblobwritemeta-10>  And there is no description about data format.  Since we don't use this function in our driver, may I know if you have suggestion for this?

I don't have a strong opinion here (**That was just my curiosity**). So, 
we can just leave it as your implementation or drop it as there is no 
consumer.

> 
>  > +IpmiBlobTransferDxeDriverEntryPoint (
> 
>  > +  IN EFI_HANDLE        ImageHandle,
> 
>  > +  IN EFI_SYSTEM_TABLE  *SystemTable
> 
>  > +  )
> 
>  > +{
> 
>  > +  return gBS->InstallMultipleProtocolInterfaces (
> 
>  > +                &ImageHandle,
> 
>  > Nit: Typically, we could also use gImageHandle instead.
> 
> Sorry I don't follow you here.
> 
> gImageHandle is used as global variable in driver. But here we just use 
> the ImageHandle from function parameter and we don't keep it as global 
> variable, right?

The pointers should be identical since they are cached by the 
UefiBootServicesTableLib's constructor prior to the call to 
IpmiBlobTransferDxeDriverEntryPoint.

Thanks,
Nhi


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#119879): https://edk2.groups.io/g/devel/message/119879
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]
-=-=-=-=-=-=-=-=-=-=-=-



^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-07-11  1:59 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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
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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox