libpldm: Add encode API for PassComponentTable request

PassComponentTable command is used to pass component information to
the firmware device. PassComponentTable command contain component
information table for a specific component including
ComponentClassificationIndex, ComponentClassification, and version
details. 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: I5503faf86e65de1bfd2968dc8dbe0d29d88e008a
diff --git a/libpldm/base.h b/libpldm/base.h
index 8644a5a..d9f5faf 100644
--- a/libpldm/base.h
+++ b/libpldm/base.h
@@ -40,7 +40,8 @@
 	PLDM_ERROR_INVALID_LENGTH = 0x03,
 	PLDM_ERROR_NOT_READY = 0x04,
 	PLDM_ERROR_UNSUPPORTED_PLDM_CMD = 0x05,
-	PLDM_ERROR_INVALID_PLDM_TYPE = 0x20
+	PLDM_ERROR_INVALID_PLDM_TYPE = 0x20,
+	PLDM_INVALID_TRANSFER_OPERATION_FLAG = 0x21
 };
 
 enum transfer_op_flag {
diff --git a/libpldm/firmware_update.c b/libpldm/firmware_update.c
index 5427c95..6b59273 100644
--- a/libpldm/firmware_update.c
+++ b/libpldm/firmware_update.c
@@ -698,4 +698,60 @@
 	*fd_will_send_pkg_data = response->fd_will_send_pkg_data;

 

 	return PLDM_SUCCESS;

-}
\ No newline at end of file
+}

+

+int encode_pass_component_table_req(

+    uint8_t instance_id, uint8_t transfer_flag, uint16_t comp_classification,

+    uint16_t comp_identifier, uint8_t comp_classification_index,

+    uint32_t comp_comparison_stamp, 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_pass_component_table_req) +

+				  comp_ver_str->length) {

+		return PLDM_ERROR_INVALID_LENGTH;

+	}

+

+	if ((comp_ver_str_len == 0) ||

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

+		return PLDM_ERROR_INVALID_DATA;

+	}

+

+	if (!is_transfer_flag_valid(transfer_flag)) {

+		return PLDM_INVALID_TRANSFER_OPERATION_FLAG;

+	}

+

+	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_PASS_COMPONENT_TABLE;

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

+	if (rc) {

+		return rc;

+	}

+

+	struct pldm_pass_component_table_req *request =

+	    (struct pldm_pass_component_table_req *)msg->payload;

+

+	request->transfer_flag = transfer_flag;

+	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_ver_str_type = comp_ver_str_type;

+	request->comp_ver_str_len = comp_ver_str_len;

+

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

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

+

+	return PLDM_SUCCESS;

+}

diff --git a/libpldm/firmware_update.h b/libpldm/firmware_update.h
index a73e083..9533f22 100644
--- a/libpldm/firmware_update.h
+++ b/libpldm/firmware_update.h
@@ -23,7 +23,8 @@
 enum pldm_firmware_update_commands {

 	PLDM_QUERY_DEVICE_IDENTIFIERS = 0x01,

 	PLDM_GET_FIRMWARE_PARAMETERS = 0x02,

-	PLDM_REQUEST_UPDATE = 0x10

+	PLDM_REQUEST_UPDATE = 0x10,

+	PLDM_PASS_COMPONENT_TABLE = 0x13

 };

 

 /** @brief PLDM Firmware update completion codes

@@ -107,6 +108,27 @@
 	PLDM_FWUP_UBM_CONTROLLER_DEVICE_CODE_LENGTH = 4

 };

 

+/** @brief ComponentClassification values defined in firmware update

+ *         specification

+ */

+enum pldm_component_classification_values {

+	PLDM_COMP_UNKNOWN = 0x0000,

+	PLDM_COMP_OTHER = 0x0001,

+	PLDM_COMP_DRIVER = 0x0002,

+	PLDM_COMP_CONFIGURATION_SOFTWARE = 0x0003,

+	PLDM_COMP_APPLICATION_SOFTWARE = 0x0004,

+	PLDM_COMP_INSTRUMENTATION = 0x0005,

+	PLDM_COMP_FIRMWARE_OR_BIOS = 0x0006,

+	PLDM_COMP_DIAGNOSTIC_SOFTWARE = 0x0007,

+	PLDM_COMP_OPERATING_SYSTEM = 0x0008,

+	PLDM_COMP_MIDDLEWARE = 0x0009,

+	PLDM_COMP_FIRMWARE = 0x000A,

+	PLDM_COMP_BIOS_OR_FCODE = 0x000B,

+	PLDM_COMP_SUPPORT_OR_SERVICEPACK = 0x000C,

+	PLDM_COMP_SOFTWARE_BUNDLE = 0x000D,

+	PLDM_COMP_DOWNSTREAM_DEVICE = 0xFFFF

+};

+

 /** @struct pldm_package_header_information

  *

  *  Structure representing fixed part of package header information

@@ -238,6 +260,20 @@
 	uint8_t fd_will_send_pkg_data;

 } __attribute__((packed));

 

+/** @struct pldm_pass_component_table_req

+ *

+ *  Structure representing PassComponentTable request

+ */

+struct pldm_pass_component_table_req {

+	uint8_t transfer_flag;

+	uint16_t comp_classification;

+	uint16_t comp_identifier;

+	uint8_t comp_classification_index;

+	uint32_t comp_comparison_stamp;

+	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

@@ -459,6 +495,33 @@
 			       uint16_t *fd_meta_data_len,

 			       uint8_t *fd_will_send_pkg_data);

 

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

+ *

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

+ *  @param[in] transfer_flag - TransferFlag

+ *  @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_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_pass_component_table_req(

+    uint8_t instance_id, uint8_t transfer_flag, uint16_t comp_classification,

+    uint16_t comp_identifier, uint8_t comp_classification_index,

+    uint32_t comp_comparison_stamp, 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 bb47742..1c750b1 100644
--- a/libpldm/tests/libpldm_firmware_update_test.cpp
+++ b/libpldm/tests/libpldm_firmware_update_test.cpp
@@ -1475,3 +1475,111 @@
         &outFdMetaDataLen, &outFdWillSendPkgData);

     EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

 }

+

+TEST(PassComponentTable, goodPathEncodeRequest)

+{

+    constexpr uint8_t instanceId = 1;

+    constexpr uint16_t compIdentifier = 400;

+    constexpr uint8_t compClassificationIndex = 40;

+    constexpr uint32_t compComparisonStamp = 0x12345678;

+    constexpr std::string_view compVerStr = "0penBmcv1.1";

+    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_pass_component_table_req) + compVerStrLen>

+        request{};

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

+

+    auto rc = encode_pass_component_table_req(

+        instanceId, PLDM_START_AND_END, PLDM_COMP_FIRMWARE, compIdentifier,

+        compClassificationIndex, compComparisonStamp, PLDM_STR_TYPE_ASCII,

+        compVerStrLen, &compVerStrInfo, requestMsg,

+        sizeof(pldm_pass_component_table_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_SUCCESS);

+

+    std::array<uint8_t,

+               hdrSize + sizeof(pldm_pass_component_table_req) + compVerStrLen>

+        outRequest{0x81, 0x05, 0x13, 0x05, 0x0A, 0x00, 0x90, 0x01, 0x28,

+                   0x78, 0x56, 0x34, 0x12, 0x01, 0x0B, 0x30, 0x70, 0x65,

+                   0x6E, 0x42, 0x6D, 0x63, 0x76, 0x31, 0x2E, 0x31};

+    EXPECT_EQ(request, outRequest);

+}

+

+TEST(PassComponentTable, errorPathEncodeRequest)

+{

+    constexpr uint8_t instanceId = 1;

+    constexpr uint16_t compIdentifier = 400;

+    constexpr uint8_t compClassificationIndex = 40;

+    constexpr uint32_t compComparisonStamp = 0x12345678;

+    constexpr std::string_view compVerStr = "0penBmcv1.1";

+    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_pass_component_table_req) + compVerStrLen>

+        request{};

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

+

+    auto rc = encode_pass_component_table_req(

+        instanceId, PLDM_START_AND_END, PLDM_COMP_FIRMWARE, compIdentifier,

+        compClassificationIndex, compComparisonStamp, PLDM_STR_TYPE_ASCII,

+        compVerStrLen, nullptr, requestMsg,

+        sizeof(pldm_pass_component_table_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    compVerStrInfo.ptr = nullptr;

+    rc = encode_pass_component_table_req(

+        instanceId, PLDM_START_AND_END, PLDM_COMP_FIRMWARE, compIdentifier,

+        compClassificationIndex, compComparisonStamp, PLDM_STR_TYPE_ASCII,

+        compVerStrLen, &compVerStrInfo, requestMsg,

+        sizeof(pldm_pass_component_table_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

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

+

+    rc = encode_pass_component_table_req(

+        instanceId, PLDM_START_AND_END, PLDM_COMP_FIRMWARE, compIdentifier,

+        compClassificationIndex, compComparisonStamp, PLDM_STR_TYPE_ASCII,

+        compVerStrLen, &compVerStrInfo, nullptr,

+        sizeof(pldm_pass_component_table_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = encode_pass_component_table_req(

+        instanceId, PLDM_START_AND_END, PLDM_COMP_FIRMWARE, compIdentifier,

+        compClassificationIndex, compComparisonStamp, PLDM_STR_TYPE_ASCII,

+        compVerStrLen, &compVerStrInfo, requestMsg,

+        sizeof(pldm_pass_component_table_req));

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

+

+    rc = encode_pass_component_table_req(

+        instanceId, PLDM_START_AND_END, PLDM_COMP_FIRMWARE, compIdentifier,

+        compClassificationIndex, compComparisonStamp, PLDM_STR_TYPE_ASCII, 0,

+        &compVerStrInfo, requestMsg,

+        sizeof(pldm_pass_component_table_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = encode_pass_component_table_req(

+        instanceId, PLDM_START_AND_END, PLDM_COMP_FIRMWARE, compIdentifier,

+        compClassificationIndex, compComparisonStamp, PLDM_STR_TYPE_ASCII,

+        compVerStrLen - 1, &compVerStrInfo, requestMsg,

+        sizeof(pldm_pass_component_table_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = encode_pass_component_table_req(

+        instanceId, PLDM_START_AND_END + 1, PLDM_COMP_FIRMWARE, compIdentifier,

+        compClassificationIndex, compComparisonStamp, PLDM_STR_TYPE_ASCII,

+        compVerStrLen, &compVerStrInfo, requestMsg,

+        sizeof(pldm_pass_component_table_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_INVALID_TRANSFER_OPERATION_FLAG);

+

+    rc = encode_pass_component_table_req(

+        instanceId, PLDM_START_AND_END, PLDM_COMP_FIRMWARE, compIdentifier,

+        compClassificationIndex, compComparisonStamp, PLDM_STR_TYPE_UNKNOWN,

+        compVerStrLen, &compVerStrInfo, requestMsg,

+        sizeof(pldm_pass_component_table_req) + compVerStrLen);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+}

diff --git a/libpldm/utils.c b/libpldm/utils.c
index 3d25381..0a1e33c 100644
--- a/libpldm/utils.c
+++ b/libpldm/utils.c
@@ -1,5 +1,5 @@
 #include "utils.h"
-
+#include "base.h"
 #include <stdio.h>
 
 /** CRC32 code derived from work by Gary S. Brown.
@@ -195,3 +195,17 @@
 	}
 	return true;
 }
+
+bool is_transfer_flag_valid(uint8_t transfer_flag)
+{
+	switch (transfer_flag) {
+	case PLDM_START:
+	case PLDM_MIDDLE:
+	case PLDM_END:
+	case PLDM_START_AND_END:
+		return true;
+
+	default:
+		return false;
+	}
+}
diff --git a/libpldm/utils.h b/libpldm/utils.h
index 3011a5a..35b039b 100644
--- a/libpldm/utils.h
+++ b/libpldm/utils.h
@@ -93,6 +93,14 @@
 bool is_time_legal(uint8_t seconds, uint8_t minutes, uint8_t hours, uint8_t day,
 		   uint8_t month, uint16_t year);
 
+/** @brief Check whether transfer flag is valid
+ *
+ *  @param[in] transfer_flag - TransferFlag
+ *
+ *  @return true if transfer flag is valid, false if not
+ */
+bool is_transfer_flag_valid(uint8_t transfer_flag);
+
 #ifdef __cplusplus
 }
 #endif