libpldm: Add decode API for UpdateComponent response
The update agent sends UpdateComponent command to request updating a
specific firmware component. The decode API will decode the response
from the firmware device. 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: Icf841a9a6feb4678206ba8b19bf8184467a42276
diff --git a/libpldm/firmware_update.c b/libpldm/firmware_update.c
index 8699bb5..3b881e5 100644
--- a/libpldm/firmware_update.c
+++ b/libpldm/firmware_update.c
@@ -119,6 +119,55 @@
 	}
 }
 
+/** @brief Check whether ComponentCompatibilityResponse is valid
+ *
+ *  @return true if ComponentCompatibilityResponse is valid, false if not
+ */
+static bool is_comp_compatibility_resp_valid(uint8_t comp_compatibility_resp)
+{
+	switch (comp_compatibility_resp) {
+	case PLDM_CCR_COMP_CAN_BE_UPDATED:
+	case PLDM_CCR_COMP_CANNOT_BE_UPDATED:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+/** @brief Check whether ComponentCompatibilityResponse Code is valid
+ *
+ *  @return true if ComponentCompatibilityResponse Code is valid, false if not
+ */
+static bool
+is_comp_compatibility_resp_code_valid(uint8_t comp_compatibility_resp_code)
+{
+	switch (comp_compatibility_resp_code) {
+	case PLDM_CCRC_NO_RESPONSE_CODE:
+	case PLDM_CCRC_COMP_COMPARISON_STAMP_IDENTICAL:
+	case PLDM_CCRC_COMP_COMPARISON_STAMP_LOWER:
+	case PLDM_CCRC_INVALID_COMP_COMPARISON_STAMP:
+	case PLDM_CCRC_COMP_CONFLICT:
+	case PLDM_CCRC_COMP_PREREQUISITES_NOT_MET:
+	case PLDM_CCRC_COMP_NOT_SUPPORTED:
+	case PLDM_CCRC_COMP_SECURITY_RESTRICTIONS:
+	case PLDM_CRC_INCOMPLETE_COMP_IMAGE_SET:
+	case PLDM_CCRC_COMP_INFO_NO_MATCH:
+	case PLDM_CCRC_COMP_VER_STR_IDENTICAL:
+	case PLDM_CCRC_COMP_VER_STR_LOWER:
+		return true;
+
+	default:
+		if (comp_compatibility_resp_code >=
+			PLDM_CCRC_VENDOR_COMP_RESP_CODE_RANGE_MIN &&
+		    comp_compatibility_resp_code <=
+			PLDM_CCRC_VENDOR_COMP_RESP_CODE_RANGE_MAX) {
+			return true;
+		}
+		return false;
+	}
+}
+
 int decode_pldm_package_header_info(
     const uint8_t *data, size_t length,
     struct pldm_package_header_information *package_header_info,
@@ -897,4 +946,51 @@
 	       comp_ver_str->ptr, comp_ver_str->length);
 
 	return PLDM_SUCCESS;
+}
+
+int decode_update_component_resp(const struct pldm_msg *msg,
+				 size_t payload_length,
+				 uint8_t *completion_code,
+				 uint8_t *comp_compatability_resp,
+				 uint8_t *comp_compatability_resp_code,
+				 bitfield32_t *update_option_flags_enabled,
+				 uint16_t *time_before_req_fw_data)
+{
+	if (msg == NULL || completion_code == NULL ||
+	    comp_compatability_resp == NULL ||
+	    comp_compatability_resp_code == NULL ||
+	    update_option_flags_enabled == NULL ||
+	    time_before_req_fw_data == NULL || !payload_length) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	*completion_code = msg->payload[0];
+	if (*completion_code != PLDM_SUCCESS) {
+		return PLDM_SUCCESS;
+	}
+
+	if (payload_length != sizeof(struct pldm_update_component_resp)) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	struct pldm_update_component_resp *response =
+	    (struct pldm_update_component_resp *)msg->payload;
+
+	if (!is_comp_compatibility_resp_valid(
+		response->comp_compatability_resp)) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (!is_comp_compatibility_resp_code_valid(
+		response->comp_compatability_resp_code)) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	*comp_compatability_resp = response->comp_compatability_resp;
+	*comp_compatability_resp_code = response->comp_compatability_resp_code;
+	update_option_flags_enabled->value =
+	    le32toh(response->update_option_flags_enabled.value);
+	*time_before_req_fw_data = le16toh(response->time_before_req_fw_data);
+
+	return PLDM_SUCCESS;
 }
\ No newline at end of file
diff --git a/libpldm/firmware_update.h b/libpldm/firmware_update.h
index 64e023a..e213ea0 100644
--- a/libpldm/firmware_update.h
+++ b/libpldm/firmware_update.h
@@ -156,6 +156,34 @@
 	PLDM_CRC_VENDOR_COMP_RESP_CODE_RANGE_MAX = 0xEF
 };
 
+/** @brief ComponentCompatibilityResponse values in the response of
+ *         UpdateComponent
+ */
+enum pldm_component_compatability_responses {
+	PLDM_CCR_COMP_CAN_BE_UPDATED = 0,
+	PLDM_CCR_COMP_CANNOT_BE_UPDATED = 1
+};
+
+/** @brief ComponentCompatibilityResponse Code values in the response of
+ *         UpdateComponent
+ */
+enum pldm_component_compatability_response_codes {
+	PLDM_CCRC_NO_RESPONSE_CODE = 0x00,
+	PLDM_CCRC_COMP_COMPARISON_STAMP_IDENTICAL = 0x01,
+	PLDM_CCRC_COMP_COMPARISON_STAMP_LOWER = 0x02,
+	PLDM_CCRC_INVALID_COMP_COMPARISON_STAMP = 0x03,
+	PLDM_CCRC_COMP_CONFLICT = 0x04,
+	PLDM_CCRC_COMP_PREREQUISITES_NOT_MET = 0x05,
+	PLDM_CCRC_COMP_NOT_SUPPORTED = 0x06,
+	PLDM_CCRC_COMP_SECURITY_RESTRICTIONS = 0x07,
+	PLDM_CCRC_INCOMPLETE_COMP_IMAGE_SET = 0x08,
+	PLDM_CCRC_COMP_INFO_NO_MATCH = 0x09,
+	PLDM_CCRC_COMP_VER_STR_IDENTICAL = 0x0A,
+	PLDM_CCRC_COMP_VER_STR_LOWER = 0x0B,
+	PLDM_CCRC_VENDOR_COMP_RESP_CODE_RANGE_MIN = 0xD0,
+	PLDM_CCRC_VENDOR_COMP_RESP_CODE_RANGE_MAX = 0xEF
+};
+
 /** @struct pldm_package_header_information
  *
  *  Structure representing fixed part of package header information
@@ -326,6 +354,18 @@
 	uint8_t comp_ver_str_len;
 } __attribute__((packed));
 
+/** @struct pldm_update_component_resp
+ *
+ *  Structure representing UpdateComponent response
+ */
+struct pldm_update_component_resp {
+	uint8_t completion_code;
+	uint8_t comp_compatability_resp;
+	uint8_t comp_compatability_resp_code;
+	bitfield32_t update_option_flags_enabled;
+	uint16_t time_before_req_fw_data;
+} __attribute__((packed));
+
 /** @brief Decode the PLDM package header information
  *
  *  @param[in] data - pointer to package header information
@@ -619,6 +659,29 @@
     const struct variable_field *comp_ver_str, struct pldm_msg *msg,
     size_t payload_length);
 
+/** @brief Decode UpdateComponent response message
+ *
+ *  @param[in] msg - Response message
+ *  @param[in] payload_length - Length of response message payload
+ *  @param[out] completion_code - Pointer to hold completion code
+ *  @param[out] comp_compatability_resp - Pointer to hold component
+ *                                        compatibility response
+ *  @param[out] comp_compatability_resp_code - Pointer to hold component
+ *                                             compatibility response code
+ *  @param[out] update_option_flags_enabled - Pointer to hold
+ *                                            UpdateOptionsFlagEnabled
+ *  @param[out] time_before_req_fw_data - Pointer to hold the estimated time
+ *                                        before sending RequestFirmwareData
+ *
+ *  @return pldm_completion_codes
+ */
+int decode_update_component_resp(const struct pldm_msg *msg,
+				 size_t payload_length,
+				 uint8_t *completion_code,
+				 uint8_t *comp_compatability_resp,
+				 uint8_t *comp_compatability_resp_code,
+				 bitfield32_t *update_option_flags_enabled,
+				 uint16_t *time_before_req_fw_data);
 #ifdef __cplusplus
 }
 #endif
diff --git a/libpldm/tests/libpldm_firmware_update_test.cpp b/libpldm/tests/libpldm_firmware_update_test.cpp
index f0ae99d..cc2d0f0 100644
--- a/libpldm/tests/libpldm_firmware_update_test.cpp
+++ b/libpldm/tests/libpldm_firmware_update_test.cpp
@@ -1816,4 +1816,162 @@
         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
+}
+
+TEST(UpdateComponent, goodPathDecodeResponse)
+{
+    constexpr std::bitset<32> forceUpdateComp{1};
+    constexpr uint16_t timeBeforeSendingReqFwData100s = 100;
+    constexpr std::array<uint8_t, hdrSize + sizeof(pldm_update_component_resp)>
+        updateComponentResponse1{0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                 0x01, 0x00, 0x00, 0x00, 0x64, 0x00};
+    auto responseMsg1 =
+        reinterpret_cast<const pldm_msg*>(updateComponentResponse1.data());
+
+    uint8_t completionCode = 0;
+    uint8_t compCompatibilityResp = 0;
+    uint8_t compCompatibilityRespCode = 0;
+    bitfield32_t updateOptionFlagsEnabled{};
+    uint16_t timeBeforeReqFWData = 0;
+
+    auto rc = decode_update_component_resp(
+        responseMsg1, sizeof(pldm_update_component_resp), &completionCode,
+        &compCompatibilityResp, &compCompatibilityRespCode,
+        &updateOptionFlagsEnabled, &timeBeforeReqFWData);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(completionCode, PLDM_SUCCESS);
+    EXPECT_EQ(compCompatibilityResp, PLDM_CCR_COMP_CAN_BE_UPDATED);
+    EXPECT_EQ(compCompatibilityRespCode, PLDM_CCRC_NO_RESPONSE_CODE);
+    EXPECT_EQ(updateOptionFlagsEnabled.value, forceUpdateComp);
+    EXPECT_EQ(timeBeforeReqFWData, timeBeforeSendingReqFwData100s);
+
+    constexpr std::bitset<32> noFlags{};
+    constexpr uint16_t timeBeforeSendingReqFwData0s = 0;
+    constexpr std::array<uint8_t, hdrSize + sizeof(pldm_update_component_resp)>
+        updateComponentResponse2{0x00, 0x00, 0x00, 0x00, 0x01, 0x09,
+                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    auto responseMsg2 =
+        reinterpret_cast<const pldm_msg*>(updateComponentResponse2.data());
+    rc = decode_update_component_resp(
+        responseMsg2, sizeof(pldm_update_component_resp), &completionCode,
+        &compCompatibilityResp, &compCompatibilityRespCode,
+        &updateOptionFlagsEnabled, &timeBeforeReqFWData);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(completionCode, PLDM_SUCCESS);
+    EXPECT_EQ(compCompatibilityResp, PLDM_CCR_COMP_CANNOT_BE_UPDATED);
+    EXPECT_EQ(compCompatibilityRespCode, PLDM_CCRC_COMP_INFO_NO_MATCH);
+    EXPECT_EQ(updateOptionFlagsEnabled.value, noFlags);
+    EXPECT_EQ(timeBeforeReqFWData, timeBeforeSendingReqFwData0s);
+
+    constexpr std::array<uint8_t, hdrSize + sizeof(pldm_update_component_resp)>
+        updateComponentResponse3{0x00, 0x00, 0x00, 0x80};
+    auto responseMsg3 =
+        reinterpret_cast<const pldm_msg*>(updateComponentResponse3.data());
+
+    rc = decode_update_component_resp(
+        responseMsg3, sizeof(pldm_update_component_resp), &completionCode,
+        &compCompatibilityResp, &compCompatibilityRespCode,
+        &updateOptionFlagsEnabled, &timeBeforeReqFWData);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(completionCode, PLDM_FWUP_NOT_IN_UPDATE_MODE);
+}
+
+TEST(UpdateComponent, errorPathDecodeResponse)
+{
+    constexpr std::array<uint8_t,
+                         hdrSize + sizeof(pldm_update_component_resp) - 1>
+        updateComponentResponse1{0x00, 0x00, 0x00, 0x00, 0x01, 0x09,
+                                 0x00, 0x00, 0x00, 0x00, 0x00};
+    auto responseMsg1 =
+        reinterpret_cast<const pldm_msg*>(updateComponentResponse1.data());
+
+    uint8_t completionCode = 0;
+    uint8_t compCompatibilityResp = 0;
+    uint8_t compCompatibilityRespCode = 0;
+    bitfield32_t updateOptionFlagsEnabled{};
+    uint16_t timeBeforeReqFWData = 0;
+
+    auto rc = decode_update_component_resp(
+        nullptr, sizeof(pldm_update_component_resp) - 1, &completionCode,
+        &compCompatibilityResp, &compCompatibilityRespCode,
+        &updateOptionFlagsEnabled, &timeBeforeReqFWData);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    rc = decode_update_component_resp(
+        responseMsg1, sizeof(pldm_update_component_resp) - 1, nullptr,
+        &compCompatibilityResp, &compCompatibilityRespCode,
+        &updateOptionFlagsEnabled, &timeBeforeReqFWData);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    rc = decode_update_component_resp(
+        responseMsg1, sizeof(pldm_update_component_resp) - 1, &completionCode,
+        nullptr, &compCompatibilityRespCode, &updateOptionFlagsEnabled,
+        &timeBeforeReqFWData);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    rc = decode_update_component_resp(
+        responseMsg1, sizeof(pldm_update_component_resp) - 1, &completionCode,
+        &compCompatibilityResp, nullptr, &updateOptionFlagsEnabled,
+        &timeBeforeReqFWData);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    rc = decode_update_component_resp(
+        responseMsg1, sizeof(pldm_update_component_resp) - 1, &completionCode,
+        &compCompatibilityResp, &compCompatibilityRespCode, nullptr,
+        &timeBeforeReqFWData);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    rc = decode_update_component_resp(
+        responseMsg1, sizeof(pldm_update_component_resp) - 1, &completionCode,
+        &compCompatibilityResp, &compCompatibilityRespCode,
+        &updateOptionFlagsEnabled, nullptr);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    rc = decode_update_component_resp(
+        responseMsg1, 0, &completionCode, &compCompatibilityResp,
+        &compCompatibilityRespCode, &updateOptionFlagsEnabled,
+        &timeBeforeReqFWData);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    rc = decode_update_component_resp(
+        responseMsg1, sizeof(pldm_update_component_resp) - 1, &completionCode,
+        &compCompatibilityResp, &compCompatibilityRespCode,
+        &updateOptionFlagsEnabled, &timeBeforeReqFWData);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
+
+    constexpr std::array<uint8_t, hdrSize + sizeof(pldm_update_component_resp)>
+        updateComponentResponse2{0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+                                 0x01, 0x00, 0x00, 0x00, 0x64, 0x00};
+    auto responseMsg2 =
+        reinterpret_cast<const pldm_msg*>(updateComponentResponse2.data());
+    rc = decode_update_component_resp(
+        responseMsg2, sizeof(pldm_update_component_resp), &completionCode,
+        &compCompatibilityResp, &compCompatibilityRespCode,
+        &updateOptionFlagsEnabled, &timeBeforeReqFWData);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    constexpr std::array<uint8_t, hdrSize + sizeof(pldm_update_component_resp)>
+        updateComponentResponse3{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C,
+                                 0x01, 0x00, 0x00, 0x00, 0x64, 0x00};
+    auto responseMsg3 =
+        reinterpret_cast<const pldm_msg*>(updateComponentResponse3.data());
+    rc = decode_update_component_resp(
+        responseMsg3, sizeof(pldm_update_component_resp), &completionCode,
+        &compCompatibilityResp, &compCompatibilityRespCode,
+        &updateOptionFlagsEnabled, &timeBeforeReqFWData);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    constexpr std::array<uint8_t, hdrSize + sizeof(pldm_update_component_resp)>
+        updateComponentResponse4{0x00, 0x00, 0x00, 0x00, 0x00, 0xF0,
+                                 0x01, 0x00, 0x00, 0x00, 0x64, 0x00};
+    auto responseMsg4 =
+        reinterpret_cast<const pldm_msg*>(updateComponentResponse4.data());
+    rc = decode_update_component_resp(
+        responseMsg4, sizeof(pldm_update_component_resp), &completionCode,
+        &compCompatibilityResp, &compCompatibilityRespCode,
+        &updateOptionFlagsEnabled, &timeBeforeReqFWData);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}