libpldm: Add decode API for CancelUpdate response

Update agent sends this command to FD/FDP to exit from update mode
even if activation is required to begin operating at the new firmware
level. FD/FDP will transition to IDLE state on CancelUpdate. 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: I302418f85dd0b870c4d3ace7e04fcc4b873f54a7
diff --git a/libpldm/firmware_update.c b/libpldm/firmware_update.c
index 53c1387..fd66549 100644
--- a/libpldm/firmware_update.c
+++ b/libpldm/firmware_update.c
@@ -280,6 +280,24 @@
 	}

 }

 

+/** @brief Check if non functioning component indication in CancelUpdate

+ *         response is valid

+ *

+ *  @return true if non functioning component indication is valid, false if not

+ */

+static bool is_non_functioning_component_indication_valid(

+    bool8_t non_functioning_component_indication)

+{

+	switch (non_functioning_component_indication) {

+	case PLDM_FWUP_COMPONENTS_FUNCTIONING:

+	case PLDM_FWUP_COMPONENTS_NOT_FUNCTIONING:

+		return true;

+

+	default:

+		return false;

+	}

+}

+

 int decode_pldm_package_header_info(

     const uint8_t *data, size_t length,

     struct pldm_package_header_information *package_header_info,

@@ -1507,3 +1525,41 @@
 

 	return PLDM_SUCCESS;

 }

+

+int decode_cancel_update_resp(const struct pldm_msg *msg, size_t payload_length,

+			      uint8_t *completion_code,

+			      bool8_t *non_functioning_component_indication,

+			      bitfield64_t *non_functioning_component_bitmap)

+{

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

+	    non_functioning_component_indication == NULL ||

+	    non_functioning_component_bitmap == 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_cancel_update_resp)) {

+		return PLDM_ERROR_INVALID_LENGTH;

+	}

+	struct pldm_cancel_update_resp *response =

+	    (struct pldm_cancel_update_resp *)msg->payload;

+

+	if (!is_non_functioning_component_indication_valid(

+		response->non_functioning_component_indication)) {

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	*non_functioning_component_indication =

+	    response->non_functioning_component_indication;

+

+	if (*non_functioning_component_indication) {

+		non_functioning_component_bitmap->value =

+		    le64toh(response->non_functioning_component_bitmap);

+	}

+

+	return PLDM_SUCCESS;

+}

diff --git a/libpldm/firmware_update.h b/libpldm/firmware_update.h
index 697a479..ed403bb 100644
--- a/libpldm/firmware_update.h
+++ b/libpldm/firmware_update.h
@@ -311,6 +311,13 @@
 	PLDM_FD_STATUS_VENDOR_DEFINED_MAX = 255

 };

 

+/** @brief Components functional indicator in CancelUpdate response

+ */

+enum pldm_firmware_update_non_functioning_component_indication {

+	PLDM_FWUP_COMPONENTS_FUNCTIONING = 0,

+	PLDM_FWUP_COMPONENTS_NOT_FUNCTIONING = 1

+};

+

 /** @struct pldm_package_header_information

  *

  *  Structure representing fixed part of package header information

@@ -543,6 +550,16 @@
 	bitfield32_t update_option_flags_enabled;

 } __attribute__((packed));

 

+/** @struct pldm_cancel_update_resp

+ *

+ *  Structure representing CancelUpdate response.

+ */

+struct pldm_cancel_update_resp {

+	uint8_t completion_code;

+	bool8_t non_functioning_component_indication;

+	uint64_t non_functioning_component_bitmap;

+} __attribute__((packed));

+

 /** @brief Decode the PLDM package header information

  *

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

@@ -1089,6 +1106,25 @@
 int encode_cancel_update_req(uint8_t instance_id, struct pldm_msg *msg,

 			     size_t payload_length);

 

+/** @brief Decode CancelUpdate response message

+ *

+ *	@param[in] msg - Response message

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

+ *	@param[out] completion_code - Pointer to completion code

+ *	@param[out] non_functioning_component_indication - Pointer to non

+						       functioning

+ *                                                     component indication

+ *	@param[out] non_functioning_component_bitmap - Pointer to non

+ functioning

+ *                                                 component bitmap

+ *

+ *	@return pldm_completion_codes

+ */

+int decode_cancel_update_resp(const struct pldm_msg *msg, size_t payload_length,

+			      uint8_t *completion_code,

+			      bool8_t *non_functioning_component_indication,

+			      bitfield64_t *non_functioning_component_bitmap);

+

 #ifdef __cplusplus

 }

 #endif

diff --git a/libpldm/pldm_types.h b/libpldm/pldm_types.h
index 5e7e068..3c5f086 100644
--- a/libpldm/pldm_types.h
+++ b/libpldm/pldm_types.h
@@ -90,6 +90,76 @@
 	} __attribute__((packed)) bits;
 } bitfield32_t;
 
+typedef union {
+	uint64_t value;
+	struct {
+		uint8_t bit0 : 1;
+		uint8_t bit1 : 1;
+		uint8_t bit2 : 1;
+		uint8_t bit3 : 1;
+		uint8_t bit4 : 1;
+		uint8_t bit5 : 1;
+		uint8_t bit6 : 1;
+		uint8_t bit7 : 1;
+		uint8_t bit8 : 1;
+		uint8_t bit9 : 1;
+		uint8_t bit10 : 1;
+		uint8_t bit11 : 1;
+		uint8_t bit12 : 1;
+		uint8_t bit13 : 1;
+		uint8_t bit14 : 1;
+		uint8_t bit15 : 1;
+		uint8_t bit16 : 1;
+		uint8_t bit17 : 1;
+		uint8_t bit18 : 1;
+		uint8_t bit19 : 1;
+		uint8_t bit20 : 1;
+		uint8_t bit21 : 1;
+		uint8_t bit22 : 1;
+		uint8_t bit23 : 1;
+		uint8_t bit24 : 1;
+		uint8_t bit25 : 1;
+		uint8_t bit26 : 1;
+		uint8_t bit27 : 1;
+		uint8_t bit28 : 1;
+		uint8_t bit29 : 1;
+		uint8_t bit30 : 1;
+		uint8_t bit31 : 1;
+		uint8_t bit32 : 1;
+		uint8_t bit33 : 1;
+		uint8_t bit34 : 1;
+		uint8_t bit35 : 1;
+		uint8_t bit36 : 1;
+		uint8_t bit37 : 1;
+		uint8_t bit38 : 1;
+		uint8_t bit39 : 1;
+		uint8_t bit40 : 1;
+		uint8_t bit41 : 1;
+		uint8_t bit42 : 1;
+		uint8_t bit43 : 1;
+		uint8_t bit44 : 1;
+		uint8_t bit45 : 1;
+		uint8_t bit46 : 1;
+		uint8_t bit47 : 1;
+		uint8_t bit48 : 1;
+		uint8_t bit49 : 1;
+		uint8_t bit50 : 1;
+		uint8_t bit51 : 1;
+		uint8_t bit52 : 1;
+		uint8_t bit53 : 1;
+		uint8_t bit54 : 1;
+		uint8_t bit55 : 1;
+		uint8_t bit56 : 1;
+		uint8_t bit57 : 1;
+		uint8_t bit58 : 1;
+		uint8_t bit59 : 1;
+		uint8_t bit60 : 1;
+		uint8_t bit61 : 1;
+		uint8_t bit62 : 1;
+		uint8_t bit63 : 1;
+	} __attribute__((packed)) bits;
+} bitfield64_t;
+
 typedef float real32_t;
 
 #endif /* PLDM_TYPES_H */
diff --git a/libpldm/tests/libpldm_firmware_update_test.cpp b/libpldm/tests/libpldm_firmware_update_test.cpp
index 9ef53f7..746dfd6 100644
--- a/libpldm/tests/libpldm_firmware_update_test.cpp
+++ b/libpldm/tests/libpldm_firmware_update_test.cpp
@@ -2817,3 +2817,106 @@
                                   PLDM_CANCEL_UPDATE_REQ_BYTES + 1);

     EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

 }

+

+TEST(CancelUpdate, goodPathDecodeResponse)

+{

+    constexpr std::bitset<64> nonFunctioningComponentBitmap1{0};

+    constexpr std::array<uint8_t, hdrSize + sizeof(pldm_cancel_update_resp)>

+        cancelUpdateResponse1{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

+    auto responseMsg1 =

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

+    uint8_t completionCode = 0;

+    bool8_t nonFunctioningComponentIndication = 0;

+    bitfield64_t nonFunctioningComponentBitmap{0};

+    auto rc = decode_cancel_update_resp(

+        responseMsg1, cancelUpdateResponse1.size() - hdrSize, &completionCode,

+        &nonFunctioningComponentIndication, &nonFunctioningComponentBitmap);

+    EXPECT_EQ(rc, PLDM_SUCCESS);

+    EXPECT_EQ(completionCode, PLDM_SUCCESS);

+    EXPECT_EQ(nonFunctioningComponentIndication,

+              PLDM_FWUP_COMPONENTS_FUNCTIONING);

+    EXPECT_EQ(nonFunctioningComponentBitmap.value,

+              nonFunctioningComponentBitmap1);

+

+    constexpr std::bitset<64> nonFunctioningComponentBitmap2{0x0101};

+    constexpr std::array<uint8_t, hdrSize + sizeof(pldm_cancel_update_resp)>

+        cancelUpdateResponse2{0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,

+                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

+    auto responseMsg2 =

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

+    rc = decode_cancel_update_resp(

+        responseMsg2, cancelUpdateResponse2.size() - hdrSize, &completionCode,

+        &nonFunctioningComponentIndication, &nonFunctioningComponentBitmap);

+    EXPECT_EQ(rc, PLDM_SUCCESS);

+    EXPECT_EQ(completionCode, PLDM_SUCCESS);

+    EXPECT_EQ(nonFunctioningComponentIndication,

+              PLDM_FWUP_COMPONENTS_NOT_FUNCTIONING);

+    EXPECT_EQ(nonFunctioningComponentBitmap.value,

+              nonFunctioningComponentBitmap2);

+

+    constexpr std::array<uint8_t, hdrSize + sizeof(completionCode)>

+        cancelUpdateResponse3{0x00, 0x00, 0x00, 0x86};

+    auto responseMsg3 =

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

+    rc = decode_cancel_update_resp(

+        responseMsg3, cancelUpdateResponse3.size() - hdrSize, &completionCode,

+        &nonFunctioningComponentIndication, &nonFunctioningComponentBitmap);

+    EXPECT_EQ(rc, PLDM_SUCCESS);

+    EXPECT_EQ(completionCode, PLDM_FWUP_BUSY_IN_BACKGROUND);

+}

+

+TEST(CancelUpdate, errorPathDecodeResponse)

+{

+    constexpr std::array<uint8_t, hdrSize> cancelUpdateResponse1{0x00, 0x00,

+                                                                 0x00};

+    auto responseMsg1 =

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

+    uint8_t completionCode = 0;

+    bool8_t nonFunctioningComponentIndication = 0;

+    bitfield64_t nonFunctioningComponentBitmap{0};

+

+    auto rc = decode_cancel_update_resp(

+        nullptr, cancelUpdateResponse1.size() - hdrSize, &completionCode,

+        &nonFunctioningComponentIndication, &nonFunctioningComponentBitmap);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_cancel_update_resp(

+        responseMsg1, cancelUpdateResponse1.size() - hdrSize, nullptr,

+        &nonFunctioningComponentIndication, &nonFunctioningComponentBitmap);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_cancel_update_resp(

+        responseMsg1, cancelUpdateResponse1.size() - hdrSize, &completionCode,

+        nullptr, &nonFunctioningComponentBitmap);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_cancel_update_resp(

+        responseMsg1, cancelUpdateResponse1.size() - hdrSize, &completionCode,

+        &nonFunctioningComponentIndication, nullptr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_cancel_update_resp(

+        responseMsg1, cancelUpdateResponse1.size() - hdrSize, &completionCode,

+        &nonFunctioningComponentIndication, &nonFunctioningComponentBitmap);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    constexpr std::array<uint8_t, hdrSize + sizeof(completionCode)>

+        cancelUpdateResponse2{0x00, 0x00, 0x00, 0x00};

+    auto responseMsg2 =

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

+    rc = decode_cancel_update_resp(

+        responseMsg2, cancelUpdateResponse2.size() - hdrSize, &completionCode,

+        &nonFunctioningComponentIndication, &nonFunctioningComponentBitmap);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

+

+    constexpr std::array<uint8_t, hdrSize + sizeof(pldm_cancel_update_resp)>

+        cancelUpdateResponse3{0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,

+                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

+    auto responseMsg3 =

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

+    rc = decode_cancel_update_resp(

+        responseMsg3, cancelUpdateResponse3.size() - hdrSize, &completionCode,

+        &nonFunctioningComponentIndication, &nonFunctioningComponentBitmap);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+}