libpldm: Add API to decode package header info

This patch provides API to decode the firmware update package header
information and also do basic validation.

The API works with DSP0267_1.1.0, DSP0267_1.0.1 and DSP0267_1.0.0.

Tested: Unit tests passed

Signed-off-by: Tom Joseph <rushtotom@gmail.com>
Change-Id: I208a219cd4b9c9b9869eb3e286259b2f51206487
diff --git a/libpldm/base.h b/libpldm/base.h
index 451137d..8644a5a 100644
--- a/libpldm/base.h
+++ b/libpldm/base.h
@@ -91,6 +91,8 @@
 #define PLDM_VERSION_0 0
 #define PLDM_CURRENT_VERSION PLDM_VERSION_0
 
+#define PLDM_TIMESTAMP104_SIZE 13
+
 /** @struct pldm_msg_hdr
  *
  * Structure representing PLDM message header fields
diff --git a/libpldm/firmware_update.c b/libpldm/firmware_update.c
index acb95fd..d31ec4a 100644
--- a/libpldm/firmware_update.c
+++ b/libpldm/firmware_update.c
@@ -71,6 +71,60 @@
 	}

 }

 

+int decode_pldm_package_header_info(

+    const uint8_t *data, size_t length,

+    struct pldm_package_header_information *package_header_info,

+    struct variable_field *package_version_str)

+{

+	if (data == NULL || package_header_info == NULL ||

+	    package_version_str == NULL) {

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	if (length < sizeof(struct pldm_package_header_information)) {

+		return PLDM_ERROR_INVALID_LENGTH;

+	}

+

+	struct pldm_package_header_information *data_header =

+	    (struct pldm_package_header_information *)(data);

+

+	if (!is_string_type_valid(data_header->package_version_string_type) ||

+	    (data_header->package_version_string_length == 0)) {

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	if (length < sizeof(struct pldm_package_header_information) +

+			 data_header->package_version_string_length) {

+		return PLDM_ERROR_INVALID_LENGTH;

+	}

+

+	if ((data_header->component_bitmap_bit_length %

+	     PLDM_FWUP_COMPONENT_BITMAP_MULTIPLE) != 0) {

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	memcpy(package_header_info->uuid, data_header->uuid,

+	       sizeof(data_header->uuid));

+	package_header_info->package_header_format_version =

+	    data_header->package_header_format_version;

+	package_header_info->package_header_size =

+	    le16toh(data_header->package_header_size);

+	memcpy(package_header_info->timestamp104, data_header->timestamp104,

+	       sizeof(data_header->timestamp104));

+	package_header_info->component_bitmap_bit_length =

+	    le16toh(data_header->component_bitmap_bit_length);

+	package_header_info->package_version_string_type =

+	    data_header->package_version_string_type;

+	package_header_info->package_version_string_length =

+	    data_header->package_version_string_length;

+	package_version_str->ptr =

+	    data + sizeof(struct pldm_package_header_information);

+	package_version_str->length =

+	    package_header_info->package_version_string_length;

+

+	return PLDM_SUCCESS;

+}

+

 int decode_descriptor_type_length_value(const uint8_t *data, size_t length,

 					uint16_t *descriptor_type,

 					struct variable_field *descriptor_data)

diff --git a/libpldm/firmware_update.h b/libpldm/firmware_update.h
index 97a13fb..a14bfb5 100644
--- a/libpldm/firmware_update.h
+++ b/libpldm/firmware_update.h
@@ -7,6 +7,7 @@
 #include "base.h"

 #include "utils.h"

 

+#define PLDM_FWUP_COMPONENT_BITMAP_MULTIPLE 8

 #define PLDM_QUERY_DEVICE_IDENTIFIERS_REQ_BYTES 0

 /** @brief Minimum length of device descriptor, 2 bytes for descriptor type,

  *         2 bytes for descriptor length and atleast 1 byte of descriptor data

@@ -77,6 +78,20 @@
 	PLDM_FWUP_UBM_CONTROLLER_DEVICE_CODE_LENGTH = 4

 };

 

+/** @struct pldm_package_header_information

+ *

+ *  Structure representing fixed part of package header information

+ */

+struct pldm_package_header_information {

+	uint8_t uuid[PLDM_FWUP_UUID_LENGTH];

+	uint8_t package_header_format_version;

+	uint16_t package_header_size;

+	uint8_t timestamp104[PLDM_TIMESTAMP104_SIZE];

+	uint16_t component_bitmap_bit_length;

+	uint8_t package_version_string_type;

+	uint8_t package_version_string_length;

+} __attribute__((packed));

+

 /** @struct pldm_descriptor_tlv

  *

  *  Structure representing descriptor type, length and value

@@ -141,6 +156,21 @@
 	bitfield32_t capabilities_during_update;

 } __attribute__((packed));

 

+/** @brief Decode the PLDM package header information

+ *

+ *  @param[in] data - pointer to package header information

+ *  @param[in] length - available length in the firmware update package

+ *  @param[out] package_header_info - pointer to fixed part of PLDM package

+ *                                    header information

+ *  @param[out] package_version_str - pointer to package version string

+ *

+ *  @return pldm_completion_codes

+ */

+int decode_pldm_package_header_info(

+    const uint8_t *data, size_t length,

+    struct pldm_package_header_information *package_header_info,

+    struct variable_field *package_version_str);

+

 /** @brief Decode the record descriptor entries in the firmware update package

  *         and the Descriptors in the QueryDeviceIDentifiers command

  *

diff --git a/libpldm/tests/libpldm_firmware_update_test.cpp b/libpldm/tests/libpldm_firmware_update_test.cpp
index f87fd5b..8f8775c 100644
--- a/libpldm/tests/libpldm_firmware_update_test.cpp
+++ b/libpldm/tests/libpldm_firmware_update_test.cpp
@@ -8,6 +8,142 @@
 

 constexpr auto hdrSize = sizeof(pldm_msg_hdr);

 

+TEST(DecodePackageHeaderInfo, goodPath)

+{

+    // Package header identifier for Version 1.0.x

+    constexpr std::array<uint8_t, PLDM_FWUP_UUID_LENGTH> uuid{

+        0xf0, 0x18, 0x87, 0x8c, 0xcb, 0x7d, 0x49, 0x43,

+        0x98, 0x00, 0xa0, 0x2F, 0x05, 0x9a, 0xca, 0x02};

+    // Package header version for DSP0267 version 1.0.x

+    constexpr uint8_t pkgHeaderFormatRevision = 0x01;

+    // Random PackageHeaderSize

+    constexpr uint16_t pkgHeaderSize = 303;

+    // PackageReleaseDateTime - "25/12/2021 00:00:00"

+    std::array<uint8_t, PLDM_TIMESTAMP104_SIZE> timestamp104{

+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+        0x00, 0x19, 0x0c, 0xe5, 0x07, 0x00};

+    constexpr uint16_t componentBitmapBitLength = 8;

+    // PackageVersionString

+    constexpr std::string_view packageVersionStr{"OpenBMCv1.0"};

+    constexpr size_t packagerHeaderSize =

+        sizeof(pldm_package_header_information) + packageVersionStr.size();

+

+    constexpr std::array<uint8_t, packagerHeaderSize> packagerHeaderInfo{

+        0xf0, 0x18, 0x87, 0x8c, 0xcb, 0x7d, 0x49, 0x43, 0x98, 0x00, 0xa0, 0x2F,

+        0x05, 0x9a, 0xca, 0x02, 0x01, 0x2f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,

+        0x00, 0x00, 0x00, 0x19, 0x0c, 0xe5, 0x07, 0x00, 0x08, 0x00, 0x01, 0x0b,

+        0x4f, 0x70, 0x65, 0x6e, 0x42, 0x4d, 0x43, 0x76, 0x31, 0x2e, 0x30};

+    pldm_package_header_information pkgHeader{};

+    variable_field packageVersion{};

+

+    auto rc = decode_pldm_package_header_info(packagerHeaderInfo.data(),

+                                              packagerHeaderInfo.size(),

+                                              &pkgHeader, &packageVersion);

+

+    EXPECT_EQ(rc, PLDM_SUCCESS);

+    EXPECT_EQ(true,

+              std::equal(pkgHeader.uuid, pkgHeader.uuid + PLDM_FWUP_UUID_LENGTH,

+                         uuid.begin(), uuid.end()));

+    EXPECT_EQ(pkgHeader.package_header_format_version, pkgHeaderFormatRevision);

+    EXPECT_EQ(pkgHeader.package_header_size, pkgHeaderSize);

+    EXPECT_EQ(true, std::equal(pkgHeader.timestamp104,

+                               pkgHeader.timestamp104 + PLDM_TIMESTAMP104_SIZE,

+                               timestamp104.begin(), timestamp104.end()));

+    EXPECT_EQ(pkgHeader.component_bitmap_bit_length, componentBitmapBitLength);

+    EXPECT_EQ(pkgHeader.package_version_string_type, PLDM_STR_TYPE_ASCII);

+    EXPECT_EQ(pkgHeader.package_version_string_length,

+              packageVersionStr.size());

+    std::string packageVersionString(

+        reinterpret_cast<const char*>(packageVersion.ptr),

+        packageVersion.length);

+    EXPECT_EQ(packageVersionString, packageVersionStr);

+}

+

+TEST(DecodePackageHeaderInfo, errorPaths)

+{

+    int rc = 0;

+    constexpr std::string_view packageVersionStr{"OpenBMCv1.0"};

+    constexpr size_t packagerHeaderSize =

+        sizeof(pldm_package_header_information) + packageVersionStr.size();

+

+    // Invalid Package Version String Type - 0x06

+    constexpr std::array<uint8_t, packagerHeaderSize>

+        invalidPackagerHeaderInfo1{

+            0xf0, 0x18, 0x87, 0x8c, 0xcb, 0x7d, 0x49, 0x43, 0x98, 0x00,

+            0xa0, 0x2F, 0x05, 0x9a, 0xca, 0x02, 0x02, 0x2f, 0x01, 0x00,

+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x0c, 0xe5,

+            0x07, 0x00, 0x08, 0x00, 0x06, 0x0b, 0x4f, 0x70, 0x65, 0x6e,

+            0x42, 0x4d, 0x43, 0x76, 0x31, 0x2e, 0x30};

+

+    pldm_package_header_information packageHeader{};

+    variable_field packageVersion{};

+

+    rc = decode_pldm_package_header_info(nullptr,

+                                         invalidPackagerHeaderInfo1.size(),

+                                         &packageHeader, &packageVersion);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_pldm_package_header_info(invalidPackagerHeaderInfo1.data(),

+                                         invalidPackagerHeaderInfo1.size(),

+                                         nullptr, &packageVersion);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_pldm_package_header_info(invalidPackagerHeaderInfo1.data(),

+                                         invalidPackagerHeaderInfo1.size(),

+                                         &packageHeader, nullptr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_pldm_package_header_info(

+        invalidPackagerHeaderInfo1.data(),

+        sizeof(pldm_package_header_information) - 1, &packageHeader,

+        &packageVersion);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

+

+    rc = decode_pldm_package_header_info(invalidPackagerHeaderInfo1.data(),

+                                         invalidPackagerHeaderInfo1.size(),

+                                         &packageHeader, &packageVersion);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    // Invalid Package Version String Length - 0x00

+    constexpr std::array<uint8_t, packagerHeaderSize>

+        invalidPackagerHeaderInfo2{

+            0xf0, 0x18, 0x87, 0x8c, 0xcb, 0x7d, 0x49, 0x43, 0x98, 0x00,

+            0xa0, 0x2F, 0x05, 0x9a, 0xca, 0x02, 0x02, 0x2f, 0x01, 0x00,

+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x0c, 0xe5,

+            0x07, 0x00, 0x08, 0x00, 0x01, 0x00, 0x4f, 0x70, 0x65, 0x6e,

+            0x42, 0x4d, 0x43, 0x76, 0x31, 0x2e, 0x30};

+    rc = decode_pldm_package_header_info(invalidPackagerHeaderInfo2.data(),

+                                         invalidPackagerHeaderInfo2.size(),

+                                         &packageHeader, &packageVersion);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    // Package version string length less than in the header information

+    constexpr std::array<uint8_t, packagerHeaderSize - 1>

+        invalidPackagerHeaderInfo3{

+            0xf0, 0x18, 0x87, 0x8c, 0xcb, 0x7d, 0x49, 0x43, 0x98, 0x00,

+            0xa0, 0x2F, 0x05, 0x9a, 0xca, 0x02, 0x02, 0x2f, 0x01, 0x00,

+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x0c, 0xe5,

+            0x07, 0x00, 0x08, 0x00, 0x01, 0x0b, 0x4f, 0x70, 0x65, 0x6e,

+            0x42, 0x4d, 0x43, 0x76, 0x31, 0x2e};

+    rc = decode_pldm_package_header_info(invalidPackagerHeaderInfo3.data(),

+                                         invalidPackagerHeaderInfo3.size(),

+                                         &packageHeader, &packageVersion);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

+

+    // ComponentBitmapBitLength not a multiple of 8

+    constexpr std::array<uint8_t, packagerHeaderSize>

+        invalidPackagerHeaderInfo4{

+            0xf0, 0x18, 0x87, 0x8c, 0xcb, 0x7d, 0x49, 0x43, 0x98, 0x00,

+            0xa0, 0x2F, 0x05, 0x9a, 0xca, 0x02, 0x02, 0x2f, 0x01, 0x00,

+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x0c, 0xe5,

+            0x07, 0x00, 0x09, 0x00, 0x01, 0x0b, 0x4f, 0x70, 0x65, 0x6e,

+            0x42, 0x4d, 0x43, 0x76, 0x31, 0x2e, 0x30};

+    rc = decode_pldm_package_header_info(invalidPackagerHeaderInfo4.data(),

+                                         invalidPackagerHeaderInfo4.size(),

+                                         &packageHeader, &packageVersion);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+}

+

 TEST(DecodeDescriptors, goodPath3Descriptors)

 {

     // In the descriptor data there are 3 descriptor entries