libpldm: Add decode API for PassComponentTable response

PassComponentTable command is used to pass component information to
the firmware device. PassComponentTable command response will contain
data such as whether the component can be updated or not and the
reason code. 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: I53e7ceaf15a71981e88a7985e73efa9a41a1785d
diff --git a/libpldm/firmware_update.c b/libpldm/firmware_update.c
index 6b59273..898a7ef 100644
--- a/libpldm/firmware_update.c
+++ b/libpldm/firmware_update.c
@@ -71,6 +71,54 @@
 	}

 }

 

+/** @brief Check whether ComponentResponse is valid

+ *

+ *  @return true if ComponentResponse is valid, false if not

+ */

+static bool is_comp_resp_valid(uint8_t comp_resp)

+{

+	switch (comp_resp) {

+	case PLDM_CR_COMP_CAN_BE_UPDATED:

+	case PLDM_CR_COMP_MAY_BE_UPDATEABLE:

+		return true;

+

+	default:

+		return false;

+	}

+}

+

+/** @brief Check whether ComponentResponseCode is valid

+ *

+ *  @return true if ComponentResponseCode is valid, false if not

+ */

+static bool is_comp_resp_code_valid(uint8_t comp_resp_code)

+{

+	switch (comp_resp_code) {

+	case PLDM_CRC_COMP_CAN_BE_UPDATED:

+	case PLDM_CRC_COMP_COMPARISON_STAMP_IDENTICAL:

+	case PLDM_CRC_COMP_COMPARISON_STAMP_LOWER:

+	case PLDM_CRC_INVALID_COMP_COMPARISON_STAMP:

+	case PLDM_CRC_COMP_CONFLICT:

+	case PLDM_CRC_COMP_PREREQUISITES_NOT_MET:

+	case PLDM_CRC_COMP_NOT_SUPPORTED:

+	case PLDM_CRC_COMP_SECURITY_RESTRICTIONS:

+	case PLDM_CRC_INCOMPLETE_COMP_IMAGE_SET:

+	case PLDM_CRC_ACTIVE_IMAGE_NOT_UPDATEABLE_SUBSEQUENTLY:

+	case PLDM_CRC_COMP_VER_STR_IDENTICAL:

+	case PLDM_CRC_COMP_VER_STR_LOWER:

+		return true;

+

+	default:

+		if (comp_resp_code >=

+			PLDM_CRC_VENDOR_COMP_RESP_CODE_RANGE_MIN &&

+		    comp_resp_code <=

+			PLDM_CRC_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,

@@ -755,3 +803,40 @@
 

 	return PLDM_SUCCESS;

 }

+

+int decode_pass_component_table_resp(const struct pldm_msg *msg,

+				     const size_t payload_length,

+				     uint8_t *completion_code,

+				     uint8_t *comp_resp,

+				     uint8_t *comp_resp_code)

+{

+	if (msg == NULL || completion_code == NULL || comp_resp == NULL ||

+	    comp_resp_code == 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_pass_component_table_resp)) {

+		return PLDM_ERROR_INVALID_LENGTH;

+	}

+

+	struct pldm_pass_component_table_resp *response =

+	    (struct pldm_pass_component_table_resp *)msg->payload;

+

+	if (!is_comp_resp_valid(response->comp_resp)) {

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	if (!is_comp_resp_code_valid(response->comp_resp_code)) {

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	*comp_resp = response->comp_resp;

+	*comp_resp_code = response->comp_resp_code;

+

+	return PLDM_SUCCESS;

+}

diff --git a/libpldm/firmware_update.h b/libpldm/firmware_update.h
index 9533f22..1237da8 100644
--- a/libpldm/firmware_update.h
+++ b/libpldm/firmware_update.h
@@ -129,6 +129,32 @@
 	PLDM_COMP_DOWNSTREAM_DEVICE = 0xFFFF

 };

 

+/** @brief ComponentResponse values in the response of PassComponentTable

+ */

+enum pldm_component_responses {

+	PLDM_CR_COMP_CAN_BE_UPDATED = 0,

+	PLDM_CR_COMP_MAY_BE_UPDATEABLE = 1

+};

+

+/** @brief ComponentResponseCode values in the response of PassComponentTable

+ */

+enum pldm_component_response_codes {

+	PLDM_CRC_COMP_CAN_BE_UPDATED = 0x00,

+	PLDM_CRC_COMP_COMPARISON_STAMP_IDENTICAL = 0x01,

+	PLDM_CRC_COMP_COMPARISON_STAMP_LOWER = 0x02,

+	PLDM_CRC_INVALID_COMP_COMPARISON_STAMP = 0x03,

+	PLDM_CRC_COMP_CONFLICT = 0x04,

+	PLDM_CRC_COMP_PREREQUISITES_NOT_MET = 0x05,

+	PLDM_CRC_COMP_NOT_SUPPORTED = 0x06,

+	PLDM_CRC_COMP_SECURITY_RESTRICTIONS = 0x07,

+	PLDM_CRC_INCOMPLETE_COMP_IMAGE_SET = 0x08,

+	PLDM_CRC_ACTIVE_IMAGE_NOT_UPDATEABLE_SUBSEQUENTLY = 0x09,

+	PLDM_CRC_COMP_VER_STR_IDENTICAL = 0x0A,

+	PLDM_CRC_COMP_VER_STR_LOWER = 0x0B,

+	PLDM_CRC_VENDOR_COMP_RESP_CODE_RANGE_MIN = 0xD0,

+	PLDM_CRC_VENDOR_COMP_RESP_CODE_RANGE_MAX = 0xEF

+};

+

 /** @struct pldm_package_header_information

  *

  *  Structure representing fixed part of package header information

@@ -274,6 +300,16 @@
 	uint8_t comp_ver_str_len;

 } __attribute__((packed));

 

+/** @struct pldm_pass_component_table_resp

+ *

+ *  Structure representing PassComponentTable response

+ */

+struct pldm_pass_component_table_resp {

+	uint8_t completion_code;

+	uint8_t comp_resp;

+	uint8_t comp_resp_code;

+} __attribute__((packed));

+

 /** @brief Decode the PLDM package header information

  *

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

@@ -522,6 +558,22 @@
     uint8_t comp_ver_str_len, const struct variable_field *comp_ver_str,

     struct pldm_msg *msg, size_t payload_length);

 

+/** @brief Decode PassComponentTable 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_resp - Pointer to hold component response

+ *  @param[out] comp_resp_code - Pointer to hold component response code

+ *

+ *  @return pldm_completion_codes

+ */

+int decode_pass_component_table_resp(const struct pldm_msg *msg,

+				     size_t payload_length,

+				     uint8_t *completion_code,

+				     uint8_t *comp_resp,

+				     uint8_t *comp_resp_code);

+

 #ifdef __cplusplus

 }

 #endif

diff --git a/libpldm/tests/libpldm_firmware_update_test.cpp b/libpldm/tests/libpldm_firmware_update_test.cpp
index 1c750b1..091f8bb 100644
--- a/libpldm/tests/libpldm_firmware_update_test.cpp
+++ b/libpldm/tests/libpldm_firmware_update_test.cpp
@@ -1583,3 +1583,124 @@
         sizeof(pldm_pass_component_table_req) + compVerStrLen);

     EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

 }

+

+TEST(PassComponentTable, goodPathDecodeResponse)

+{

+    constexpr std::array<uint8_t,

+                         hdrSize + sizeof(pldm_pass_component_table_resp)>

+        passCompTableResponse1{0x00, 0x00, 0x00, 0x00, 0x00, 0x01};

+    auto responseMsg1 =

+        reinterpret_cast<const pldm_msg*>(passCompTableResponse1.data());

+

+    uint8_t completionCode = 0;

+    uint8_t compResp = 0;

+    uint8_t compRespCode = 0;

+

+    auto rc = decode_pass_component_table_resp(

+        responseMsg1, sizeof(pldm_pass_component_table_resp), &completionCode,

+        &compResp, &compRespCode);

+

+    EXPECT_EQ(rc, PLDM_SUCCESS);

+    EXPECT_EQ(completionCode, PLDM_SUCCESS);

+    EXPECT_EQ(compResp, PLDM_CR_COMP_CAN_BE_UPDATED);

+    EXPECT_EQ(compRespCode, PLDM_CRC_COMP_COMPARISON_STAMP_IDENTICAL);

+

+    constexpr std::array<uint8_t,

+                         hdrSize + sizeof(pldm_pass_component_table_resp)>

+        passCompTableResponse2{0x00, 0x00, 0x00, 0x00, 0x00, 0xD0};

+    auto responseMsg2 =

+        reinterpret_cast<const pldm_msg*>(passCompTableResponse2.data());

+    rc = decode_pass_component_table_resp(

+        responseMsg2, sizeof(pldm_pass_component_table_resp), &completionCode,

+        &compResp, &compRespCode);

+

+    EXPECT_EQ(rc, PLDM_SUCCESS);

+    EXPECT_EQ(completionCode, PLDM_SUCCESS);

+    EXPECT_EQ(compResp, PLDM_CR_COMP_CAN_BE_UPDATED);

+    EXPECT_EQ(compRespCode, PLDM_CRC_VENDOR_COMP_RESP_CODE_RANGE_MIN);

+

+    constexpr std::array<uint8_t,

+                         hdrSize + sizeof(pldm_pass_component_table_resp)>

+        passCompTableResponse3{0x00, 0x00, 0x00, 0x80};

+    auto responseMsg3 =

+        reinterpret_cast<const pldm_msg*>(passCompTableResponse3.data());

+

+    rc = decode_pass_component_table_resp(

+        responseMsg3, sizeof(pldm_pass_component_table_resp), &completionCode,

+        &compResp, &compRespCode);

+

+    EXPECT_EQ(rc, PLDM_SUCCESS);

+    EXPECT_EQ(completionCode, PLDM_FWUP_NOT_IN_UPDATE_MODE);

+}

+

+TEST(PassComponentTable, errorPathDecodeResponse)

+{

+    constexpr std::array<uint8_t,

+                         hdrSize + sizeof(pldm_pass_component_table_resp) - 1>

+        passCompTableResponse1{0x00, 0x00, 0x00, 0x00, 0x00};

+    auto responseMsg1 =

+        reinterpret_cast<const pldm_msg*>(passCompTableResponse1.data());

+

+    uint8_t completionCode = 0;

+    uint8_t compResp = 0;

+    uint8_t compRespCode = 0;

+

+    auto rc = decode_pass_component_table_resp(

+        nullptr, sizeof(pldm_pass_component_table_resp) - 1, &completionCode,

+        &compResp, &compRespCode);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_pass_component_table_resp(

+        responseMsg1, sizeof(pldm_pass_component_table_resp) - 1, nullptr,

+        &compResp, &compRespCode);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_pass_component_table_resp(

+        responseMsg1, sizeof(pldm_pass_component_table_resp) - 1,

+        &completionCode, nullptr, &compRespCode);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_pass_component_table_resp(

+        responseMsg1, sizeof(pldm_pass_component_table_resp) - 1,

+        &completionCode, &compResp, nullptr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_pass_component_table_resp(responseMsg1, 0, &completionCode,

+                                          &compResp, &compRespCode);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_pass_component_table_resp(

+        responseMsg1, sizeof(pldm_pass_component_table_resp) - 1,

+        &completionCode, &compResp, &compRespCode);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

+

+    constexpr std::array<uint8_t,

+                         hdrSize + sizeof(pldm_pass_component_table_resp)>

+        passCompTableResponse2{0x00, 0x00, 0x00, 0x00, 0x02, 0x00};

+    auto responseMsg2 =

+        reinterpret_cast<const pldm_msg*>(passCompTableResponse2.data());

+    rc = decode_pass_component_table_resp(

+        responseMsg2, sizeof(pldm_pass_component_table_resp), &completionCode,

+        &compResp, &compRespCode);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    constexpr std::array<uint8_t,

+                         hdrSize + sizeof(pldm_pass_component_table_resp)>

+        passCompTableResponse3{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C};

+    auto responseMsg3 =

+        reinterpret_cast<const pldm_msg*>(passCompTableResponse3.data());

+    rc = decode_pass_component_table_resp(

+        responseMsg3, sizeof(pldm_pass_component_table_resp), &completionCode,

+        &compResp, &compRespCode);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    constexpr std::array<uint8_t,

+                         hdrSize + sizeof(pldm_pass_component_table_resp)>

+        passCompTableResponse4{0x00, 0x00, 0x00, 0x00, 0x00, 0xF0};

+    auto responseMsg4 =

+        reinterpret_cast<const pldm_msg*>(passCompTableResponse4.data());

+    rc = decode_pass_component_table_resp(

+        responseMsg4, sizeof(pldm_pass_component_table_resp), &completionCode,

+        &compResp, &compRespCode);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+}