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);

+}