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 <nrami...@nvidia.com> Co-authored-by: Nickle Wang <nick...@nvidia.com> Cc: Abner Chang <abner.ch...@amd.com> Cc: Abdul Lateef Attar <abdullateef.at...@amd.com> Cc: Tinh Nguyen <tinhngu...@amperemail.onmicrosoft.com> Cc: Nhi Pham <n...@os.amperecomputing.com> Cc: Thang Nguyen OS <th...@amperemail.onmicrosoft.com> Cc: Mike Maslenkin <mike.maslen...@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/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-