libpldm: Add API to decode component image info

This patch provides API to decode the individual component image
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: Ife0ab62cf0890d0144b1fda1c4ba5e9262be4714
diff --git a/libpldm/firmware_update.c b/libpldm/firmware_update.c
index 379549d..23487eb 100644
--- a/libpldm/firmware_update.c
+++ b/libpldm/firmware_update.c
@@ -297,6 +297,70 @@
 	return PLDM_SUCCESS;

 }

 

+int decode_pldm_comp_image_info(

+    const uint8_t *data, size_t length,

+    struct pldm_component_image_information *pldm_comp_image_info,

+    struct variable_field *comp_version_str)

+{

+	if (data == NULL || pldm_comp_image_info == NULL ||

+	    comp_version_str == NULL) {

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

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

+		return PLDM_ERROR_INVALID_LENGTH;

+	}

+

+	struct pldm_component_image_information *data_header =

+	    (struct pldm_component_image_information *)(data);

+

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

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

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	if (length < sizeof(struct pldm_component_image_information) +

+			 data_header->comp_version_string_length) {

+		return PLDM_ERROR_INVALID_LENGTH;

+	}

+

+	pldm_comp_image_info->comp_classification =

+	    le16toh(data_header->comp_classification);

+	pldm_comp_image_info->comp_identifier =

+	    le16toh(data_header->comp_identifier);

+	pldm_comp_image_info->comp_comparison_stamp =

+	    le32toh(data_header->comp_comparison_stamp);

+	pldm_comp_image_info->comp_options.value =

+	    le16toh(data_header->comp_options.value);

+	pldm_comp_image_info->requested_comp_activation_method.value =

+	    le16toh(data_header->requested_comp_activation_method.value);

+	pldm_comp_image_info->comp_location_offset =

+	    le32toh(data_header->comp_location_offset);

+	pldm_comp_image_info->comp_size = le32toh(data_header->comp_size);

+	pldm_comp_image_info->comp_version_string_type =

+	    data_header->comp_version_string_type;

+	pldm_comp_image_info->comp_version_string_length =

+	    data_header->comp_version_string_length;

+

+	if ((pldm_comp_image_info->comp_options.bits.bit1 == false &&

+	     pldm_comp_image_info->comp_comparison_stamp !=

+		 PLDM_FWUP_INVALID_COMPONENT_COMPARISON_TIMESTAMP)) {

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	if (pldm_comp_image_info->comp_location_offset == 0 ||

+	    pldm_comp_image_info->comp_size == 0) {

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	comp_version_str->ptr =

+	    data + sizeof(struct pldm_component_image_information);

+	comp_version_str->length =

+	    pldm_comp_image_info->comp_version_string_length;

+

+	return PLDM_SUCCESS;

+}

+

 int encode_query_device_identifiers_req(uint8_t instance_id,

 					size_t payload_length,

 					struct pldm_msg *msg)

diff --git a/libpldm/firmware_update.h b/libpldm/firmware_update.h
index f84b8c7..f7039db 100644
--- a/libpldm/firmware_update.h
+++ b/libpldm/firmware_update.h
@@ -8,6 +8,7 @@
 #include "utils.h"

 

 #define PLDM_FWUP_COMPONENT_BITMAP_MULTIPLE 8

+#define PLDM_FWUP_INVALID_COMPONENT_COMPARISON_TIMESTAMP 0xFFFFFFFF

 #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

@@ -125,6 +126,23 @@
 	uint8_t vendor_defined_descriptor_title_str[1];

 } __attribute__((packed));

 

+/** @struct pldm_component_image_information

+ *

+ *  Structure representing fixed part of individual component information in

+ *  PLDM firmware update package

+ */

+struct pldm_component_image_information {

+	uint16_t comp_classification;

+	uint16_t comp_identifier;

+	uint32_t comp_comparison_stamp;

+	bitfield16_t comp_options;

+	bitfield16_t requested_comp_activation_method;

+	uint32_t comp_location_offset;

+	uint32_t comp_size;

+	uint8_t comp_version_string_type;

+	uint8_t comp_version_string_length;

+} __attribute__((packed));

+

 /** @struct pldm_query_device_identifiers_resp

  *

  *  Structure representing query device identifiers response.

@@ -239,6 +257,21 @@
     struct variable_field *descriptor_title_str,

     struct variable_field *descriptor_data);

 

+/** @brief Decode individual component image information

+ *

+ *  @param[in] data - pointer to component image information

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

+ *  @param[out] pldm_comp_image_info - pointer to fixed part of component image

+ *                                     information

+ *  @param[out] comp_version_str - pointer to component version string

+ *

+ *  @return pldm_completion_codes

+ */

+int decode_pldm_comp_image_info(

+    const uint8_t *data, size_t length,

+    struct pldm_component_image_information *pldm_comp_image_info,

+    struct variable_field *comp_version_str);

+

 /** @brief Create a PLDM request message for QueryDeviceIdentifiers

  *

  *  @param[in] instance_id - Message's instance id

diff --git a/libpldm/tests/libpldm_firmware_update_test.cpp b/libpldm/tests/libpldm_firmware_update_test.cpp
index cb5d22e..444d71c 100644
--- a/libpldm/tests/libpldm_firmware_update_test.cpp
+++ b/libpldm/tests/libpldm_firmware_update_test.cpp
@@ -635,6 +635,145 @@
     EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

 }

 

+TEST(DecodeComponentImageInfo, goodPath)

+{

+    // Firmware

+    constexpr uint16_t compClassification = 16;

+    constexpr uint16_t compIdentifier = 300;

+    constexpr uint32_t compComparisonStamp = 0xFFFFFFFF;

+    // Force update

+    constexpr std::bitset<16> compOptions{1};

+    // System reboot[Bit position 3] & Medium-specific reset[Bit position 2]

+    constexpr std::bitset<16> reqCompActivationMethod{0x0c};

+    // Random ComponentLocationOffset

+    constexpr uint32_t compLocOffset = 357;

+    // Random ComponentSize

+    constexpr uint32_t compSize = 27;

+    // ComponentVersionString

+    constexpr std::string_view compVersionStr{"VersionString1"};

+    constexpr size_t compImageInfoSize =

+        sizeof(pldm_component_image_information) + compVersionStr.size();

+

+    constexpr std::array<uint8_t, compImageInfoSize> compImageInfo{

+        0x10, 0x00, 0x2c, 0x01, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x0c, 0x00,

+        0x65, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x56, 0x65,

+        0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x31};

+    pldm_component_image_information outCompImageInfo{};

+    variable_field outCompVersionStr{};

+

+    auto rc =

+        decode_pldm_comp_image_info(compImageInfo.data(), compImageInfo.size(),

+                                    &outCompImageInfo, &outCompVersionStr);

+

+    EXPECT_EQ(rc, PLDM_SUCCESS);

+    EXPECT_EQ(outCompImageInfo.comp_classification, compClassification);

+    EXPECT_EQ(outCompImageInfo.comp_identifier, compIdentifier);

+    EXPECT_EQ(outCompImageInfo.comp_comparison_stamp, compComparisonStamp);

+    EXPECT_EQ(outCompImageInfo.comp_options.value, compOptions);

+    EXPECT_EQ(outCompImageInfo.requested_comp_activation_method.value,

+              reqCompActivationMethod);

+    EXPECT_EQ(outCompImageInfo.comp_location_offset, compLocOffset);

+    EXPECT_EQ(outCompImageInfo.comp_size, compSize);

+    EXPECT_EQ(outCompImageInfo.comp_version_string_type, PLDM_STR_TYPE_ASCII);

+    EXPECT_EQ(outCompImageInfo.comp_version_string_length,

+              compVersionStr.size());

+

+    EXPECT_EQ(outCompVersionStr.length,

+              outCompImageInfo.comp_version_string_length);

+    std::string componentVersionString(

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

+        outCompVersionStr.length);

+    EXPECT_EQ(componentVersionString, compVersionStr);

+}

+

+TEST(DecodeComponentImageInfo, errorPaths)

+{

+    int rc = 0;

+    // ComponentVersionString

+    constexpr std::string_view compVersionStr{"VersionString1"};

+    constexpr size_t compImageInfoSize =

+        sizeof(pldm_component_image_information) + compVersionStr.size();

+    // Invalid ComponentVersionStringType - 0x06

+    constexpr std::array<uint8_t, compImageInfoSize> invalidCompImageInfo1{

+        0x10, 0x00, 0x2c, 0x01, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x0c, 0x00,

+        0x65, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x06, 0x0e, 0x56, 0x65,

+        0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x31};

+    pldm_component_image_information outCompImageInfo{};

+    variable_field outCompVersionStr{};

+

+    rc = decode_pldm_comp_image_info(nullptr, invalidCompImageInfo1.size(),

+                                     &outCompImageInfo, &outCompVersionStr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_pldm_comp_image_info(invalidCompImageInfo1.data(),

+                                     invalidCompImageInfo1.size(), nullptr,

+                                     &outCompVersionStr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_pldm_comp_image_info(invalidCompImageInfo1.data(),

+                                     invalidCompImageInfo1.size(),

+                                     &outCompImageInfo, nullptr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_pldm_comp_image_info(invalidCompImageInfo1.data(),

+                                     sizeof(pldm_component_image_information) -

+                                         1,

+                                     &outCompImageInfo, &outCompVersionStr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

+

+    rc = decode_pldm_comp_image_info(invalidCompImageInfo1.data(),

+                                     invalidCompImageInfo1.size(),

+                                     &outCompImageInfo, &outCompVersionStr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    // Invalid ComponentVersionStringLength - 0x00

+    constexpr std::array<uint8_t, compImageInfoSize> invalidCompImageInfo2{

+        0x10, 0x00, 0x2c, 0x01, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x0c, 0x00,

+        0x65, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x56, 0x65,

+        0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x31};

+    rc = decode_pldm_comp_image_info(invalidCompImageInfo2.data(),

+                                     invalidCompImageInfo2.size(),

+                                     &outCompImageInfo, &outCompVersionStr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    // Use Component Comparison Stamp is not set, but ComponentComparisonStamp

+    // is not 0xFFFFFFFF

+    constexpr std::array<uint8_t, compImageInfoSize> invalidCompImageInfo3{

+        0x10, 0x00, 0x2c, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00,

+        0x65, 0x01, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x56, 0x65,

+        0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x31};

+

+    rc = decode_pldm_comp_image_info(invalidCompImageInfo3.data(),

+                                     invalidCompImageInfo3.size() - 1,

+                                     &outCompImageInfo, &outCompVersionStr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

+

+    rc = decode_pldm_comp_image_info(invalidCompImageInfo3.data(),

+                                     invalidCompImageInfo3.size(),

+                                     &outCompImageInfo, &outCompVersionStr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    // Invalid ComponentLocationOffset - 0

+    constexpr std::array<uint8_t, compImageInfoSize> invalidCompImageInfo4{

+        0x10, 0x00, 0x2c, 0x01, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x0c, 0x00,

+        0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x56, 0x65,

+        0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x31};

+    rc = decode_pldm_comp_image_info(invalidCompImageInfo4.data(),

+                                     invalidCompImageInfo4.size(),

+                                     &outCompImageInfo, &outCompVersionStr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    // Invalid ComponentSize - 0

+    constexpr std::array<uint8_t, compImageInfoSize> invalidCompImageInfo5{

+        0x10, 0x00, 0x2c, 0x01, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x0c, 0x00,

+        0x65, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x56, 0x65,

+        0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x31};

+    rc = decode_pldm_comp_image_info(invalidCompImageInfo5.data(),

+                                     invalidCompImageInfo5.size(),

+                                     &outCompImageInfo, &outCompVersionStr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+}

+

 TEST(QueryDeviceIdentifiers, goodPathEncodeRequest)

 {

     std::array<uint8_t, sizeof(pldm_msg_hdr)> requestMsg{};