libpldm: Add encode API for UpdateComponent request

The update agent sends UpdateComponent command to request updating a
specific firmware component. This implementation works with
DSP0267_1.1.0, DSP0267_1.0.1 and DSP0267_1.0.0.

Tested: Unit tests passed

Signed-off-by: gokulsanker <gokul.sanker.v.g@intel.com>
Change-Id: I9f3328dfbbafd8dab03305e2f489ff28adf250a4
diff --git a/libpldm/firmware_update.c b/libpldm/firmware_update.c
index 898a7ef..8699bb5 100644
--- a/libpldm/firmware_update.c
+++ b/libpldm/firmware_update.c
@@ -840,3 +840,61 @@
 

 	return PLDM_SUCCESS;

 }

+

+int encode_update_component_req(

+    uint8_t instance_id, uint16_t comp_classification, uint16_t comp_identifier,

+    uint8_t comp_classification_index, uint32_t comp_comparison_stamp,

+    uint32_t comp_image_size, bitfield32_t update_option_flags,

+    uint8_t comp_ver_str_type, uint8_t comp_ver_str_len,

+    const struct variable_field *comp_ver_str, struct pldm_msg *msg,

+    size_t payload_length)

+{

+	if (comp_ver_str == NULL || comp_ver_str->ptr == NULL || msg == NULL) {

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	if (payload_length !=

+	    sizeof(struct pldm_update_component_req) + comp_ver_str->length) {

+		return PLDM_ERROR_INVALID_LENGTH;

+	}

+

+	if (!comp_image_size) {

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	if ((comp_ver_str_len == 0) ||

+	    (comp_ver_str_len != comp_ver_str->length)) {

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	if (!is_string_type_valid(comp_ver_str_type)) {

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	struct pldm_header_info header = {0};

+	header.instance = instance_id;

+	header.msg_type = PLDM_REQUEST;

+	header.pldm_type = PLDM_FWUP;

+	header.command = PLDM_UPDATE_COMPONENT;

+	uint8_t rc = pack_pldm_header(&header, &(msg->hdr));

+	if (rc) {

+		return rc;

+	}

+

+	struct pldm_update_component_req *request =

+	    (struct pldm_update_component_req *)msg->payload;

+

+	request->comp_classification = htole16(comp_classification);

+	request->comp_identifier = htole16(comp_identifier);

+	request->comp_classification_index = comp_classification_index;

+	request->comp_comparison_stamp = htole32(comp_comparison_stamp);

+	request->comp_image_size = htole32(comp_image_size);

+	request->update_option_flags.value = htole32(update_option_flags.value);

+	request->comp_ver_str_type = comp_ver_str_type;

+	request->comp_ver_str_len = comp_ver_str_len;

+

+	memcpy(msg->payload + sizeof(struct pldm_update_component_req),

+	       comp_ver_str->ptr, comp_ver_str->length);

+

+	return PLDM_SUCCESS;

+}
\ No newline at end of file
diff --git a/libpldm/firmware_update.h b/libpldm/firmware_update.h
index 1237da8..64e023a 100644
--- a/libpldm/firmware_update.h
+++ b/libpldm/firmware_update.h
@@ -24,7 +24,8 @@
 	PLDM_QUERY_DEVICE_IDENTIFIERS = 0x01,

 	PLDM_GET_FIRMWARE_PARAMETERS = 0x02,

 	PLDM_REQUEST_UPDATE = 0x10,

-	PLDM_PASS_COMPONENT_TABLE = 0x13

+	PLDM_PASS_COMPONENT_TABLE = 0x13,

+	PLDM_UPDATE_COMPONENT = 0x14

 };

 

 /** @brief PLDM Firmware update completion codes

@@ -310,6 +311,21 @@
 	uint8_t comp_resp_code;

 } __attribute__((packed));

 

+/** @struct pldm_update_component_req

+ *

+ *  Structure representing UpdateComponent request

+ */

+struct pldm_update_component_req {

+	uint16_t comp_classification;

+	uint16_t comp_identifier;

+	uint8_t comp_classification_index;

+	uint32_t comp_comparison_stamp;

+	uint32_t comp_image_size;

+	bitfield32_t update_option_flags;

+	uint8_t comp_ver_str_type;

+	uint8_t comp_ver_str_len;

+} __attribute__((packed));

+

 /** @brief Decode the PLDM package header information

  *

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

@@ -574,6 +590,35 @@
 				     uint8_t *comp_resp,

 				     uint8_t *comp_resp_code);

 

+/** @brief Create PLDM request message for UpdateComponent

+ *

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

+ *  @param[in] comp_classification - ComponentClassification

+ *  @param[in] comp_identifier - ComponentIdentifier

+ *  @param[in] comp_classification_index - ComponentClassificationIndex

+ *  @param[in] comp_comparison_stamp - ComponentComparisonStamp

+ *  @param[in] comp_image_size - ComponentImageSize

+ *  @param[in] update_option_flags - UpdateOptionFlags

+ *  @param[in] comp_ver_str_type - ComponentVersionStringType

+ *  @param[in] comp_ver_str_len - ComponentVersionStringLength

+ *  @param[in] comp_ver_str - ComponentVersionString

+ *  @param[in,out] msg - Message will be written to this

+ *  @param[in] payload_length - Length of request message payload

+ *                              information

+ *

+ *  @return pldm_completion_codes

+ *

+ *  @note  Caller is responsible for memory alloc and dealloc of param

+ *         'msg.payload'

+ */

+int encode_update_component_req(

+    uint8_t instance_id, uint16_t comp_classification, uint16_t comp_identifier,

+    uint8_t comp_classification_index, uint32_t comp_comparison_stamp,

+    uint32_t comp_image_size, bitfield32_t update_option_flags,

+    uint8_t comp_ver_str_type, uint8_t comp_ver_str_len,

+    const struct variable_field *comp_ver_str, struct pldm_msg *msg,

+    size_t payload_length);

+

 #ifdef __cplusplus

 }

 #endif

diff --git a/libpldm/tests/libpldm_firmware_update_test.cpp b/libpldm/tests/libpldm_firmware_update_test.cpp
index 091f8bb..f0ae99d 100644
--- a/libpldm/tests/libpldm_firmware_update_test.cpp
+++ b/libpldm/tests/libpldm_firmware_update_test.cpp
@@ -1704,3 +1704,116 @@
         &compResp, &compRespCode);

     EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

 }

+

+TEST(UpdateComponent, goodPathEncodeRequest)

+{

+    constexpr uint8_t instanceId = 2;

+    constexpr uint16_t compIdentifier = 500;

+    constexpr uint8_t compClassificationIndex = 50;

+    constexpr uint32_t compComparisonStamp = 0x89ABCDEF;

+    constexpr uint32_t compImageSize = 4096;

+    constexpr bitfield32_t updateOptionFlags{1};

+    constexpr std::string_view compVerStr = "OpenBmcv2.2";

+    constexpr uint8_t compVerStrLen = static_cast<uint8_t>(compVerStr.size());

+    variable_field compVerStrInfo{};

+    compVerStrInfo.ptr = reinterpret_cast<const uint8_t*>(compVerStr.data());

+    compVerStrInfo.length = compVerStrLen;

+

+    std::array<uint8_t,

+               hdrSize + sizeof(pldm_update_component_req) + compVerStrLen>

+        request{};

+    auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());

+

+    auto rc = encode_update_component_req(

+        instanceId, PLDM_COMP_FIRMWARE, compIdentifier, compClassificationIndex,

+        compComparisonStamp, compImageSize, updateOptionFlags,

+        PLDM_STR_TYPE_ASCII, compVerStrLen, &compVerStrInfo, requestMsg,

+        sizeof(pldm_update_component_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_SUCCESS);

+

+    std::array<uint8_t,

+               hdrSize + sizeof(pldm_update_component_req) + compVerStrLen>

+        outRequest{0x82, 0x05, 0x14, 0x0A, 0x00, 0xF4, 0x01, 0x32, 0xEF,

+                   0xCD, 0xAB, 0x89, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00,

+                   0x00, 0x00, 0x01, 0x0B, 0x4f, 0x70, 0x65, 0x6E, 0x42,

+                   0x6D, 0x63, 0x76, 0x32, 0x2E, 0x32};

+    EXPECT_EQ(request, outRequest);

+}

+

+TEST(UpdateComponent, errorPathEncodeRequest)

+{

+    constexpr uint8_t instanceId = 2;

+    constexpr uint16_t compIdentifier = 500;

+    constexpr uint8_t compClassificationIndex = 50;

+    constexpr uint32_t compComparisonStamp = 0x89ABCDEF;

+    constexpr uint32_t compImageSize = 4096;

+    constexpr bitfield32_t updateOptionFlags{1};

+    constexpr std::string_view compVerStr = "OpenBmcv2.2";

+    constexpr uint8_t compVerStrLen = static_cast<uint8_t>(compVerStr.size());

+    variable_field compVerStrInfo{};

+    compVerStrInfo.ptr = reinterpret_cast<const uint8_t*>(compVerStr.data());

+    compVerStrInfo.length = compVerStrLen;

+

+    std::array<uint8_t,

+               hdrSize + sizeof(pldm_update_component_req) + compVerStrLen>

+        request{};

+    auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());

+

+    auto rc = encode_update_component_req(

+        instanceId, PLDM_COMP_FIRMWARE, compIdentifier, compClassificationIndex,

+        compComparisonStamp, compImageSize, updateOptionFlags,

+        PLDM_STR_TYPE_ASCII, compVerStrLen, nullptr, requestMsg,

+        sizeof(pldm_update_component_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    compVerStrInfo.ptr = nullptr;

+    rc = encode_update_component_req(

+        instanceId, PLDM_COMP_FIRMWARE, compIdentifier, compClassificationIndex,

+        compComparisonStamp, compImageSize, updateOptionFlags,

+        PLDM_STR_TYPE_ASCII, compVerStrLen, &compVerStrInfo, requestMsg,

+        sizeof(pldm_update_component_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+    compVerStrInfo.ptr = reinterpret_cast<const uint8_t*>(compVerStr.data());

+

+    rc = encode_update_component_req(

+        instanceId, PLDM_COMP_FIRMWARE, compIdentifier, compClassificationIndex,

+        compComparisonStamp, compImageSize, updateOptionFlags,

+        PLDM_STR_TYPE_ASCII, compVerStrLen, &compVerStrInfo, nullptr,

+        sizeof(pldm_update_component_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = encode_update_component_req(

+        instanceId, PLDM_COMP_FIRMWARE, compIdentifier, compClassificationIndex,

+        compComparisonStamp, compImageSize, updateOptionFlags,

+        PLDM_STR_TYPE_ASCII, compVerStrLen, &compVerStrInfo, requestMsg,

+        sizeof(pldm_update_component_req));

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

+

+    rc = encode_update_component_req(

+        instanceId, PLDM_COMP_FIRMWARE, compIdentifier, compClassificationIndex,

+        compComparisonStamp, 0, updateOptionFlags, PLDM_STR_TYPE_ASCII,

+        compVerStrLen, &compVerStrInfo, requestMsg,

+        sizeof(pldm_update_component_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = encode_update_component_req(

+        instanceId, PLDM_COMP_FIRMWARE, compIdentifier, compClassificationIndex,

+        compComparisonStamp, compImageSize, updateOptionFlags,

+        PLDM_STR_TYPE_ASCII, 0, &compVerStrInfo, requestMsg,

+        sizeof(pldm_update_component_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = encode_update_component_req(

+        instanceId, PLDM_COMP_FIRMWARE, compIdentifier, compClassificationIndex,

+        compComparisonStamp, compImageSize, updateOptionFlags,

+        PLDM_STR_TYPE_ASCII, compVerStrLen - 1, &compVerStrInfo, requestMsg,

+        sizeof(pldm_update_component_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = encode_update_component_req(

+        instanceId, PLDM_COMP_FIRMWARE, compIdentifier, compClassificationIndex,

+        compComparisonStamp, compImageSize, updateOptionFlags,

+        PLDM_STR_TYPE_UNKNOWN, compVerStrLen, &compVerStrInfo, requestMsg,

+        sizeof(pldm_update_component_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+}
\ No newline at end of file