dsp: platform: Add file PDR descriptor encoding

Change-Id: Ic1627b0ba2a4c87f4a8352e7c63eede0a2513b80
Signed-off-by: Kasun Athukorala <kasunath@google.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d02b000..3a03e21 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,9 @@
 
 ### Added
 
+- platform: Added file descriptor PDR encoding support
+  - Added `encode_pldm_platform_file_descriptor_pdr()`
+
 ### Changed
 
 - base:
diff --git a/include/libpldm/platform.h b/include/libpldm/platform.h
index 298c1cd..0ab01d7 100644
--- a/include/libpldm/platform.h
+++ b/include/libpldm/platform.h
@@ -2705,6 +2705,23 @@
 uint8_t *
 pldm_platform_cper_event_event_data(struct pldm_platform_cper_event *event);
 
+/** @brief Encode data in to File Descriptor PDR
+ *
+ *  @param[in] pdr - Populated pldm_platform_file_descriptor_pdr struct
+ *  @param[out] data - Pointer to a buffer to save encoded PDR data
+ *  @param[in/out] data_len - Length of the response PDR buffer (data)
+ *
+ *  @return error code: 0 on success
+ *          -EINVAL if the input values are invalid
+ *          -EBADMSG if the original length of the data buffer is larger
+ *          than the target extract length
+ *          -EOVERFLOW if the original length of the data buffer is smaller
+ *          than the target extract length
+ */
+int encode_pldm_platform_file_descriptor_pdr(
+	const struct pldm_platform_file_descriptor_pdr *pdr, void *data,
+	size_t *data_len);
+
 /** @brief Decode date fields from File Descriptor PDR
  *
  *  @param[in] data - PLDM response message which includes the File
diff --git a/src/dsp/platform.c b/src/dsp/platform.c
index 1a2c962..33861e1 100644
--- a/src/dsp/platform.c
+++ b/src/dsp/platform.c
@@ -3537,3 +3537,96 @@
 
 	return pldm_msgbuf_complete_consumed(buf);
 }
+
+/* Maximum length possible for file descriptor PDR FileName property */
+#define PLDM_FILE_PDR_FILE_NAME_MAX_LENGTH 255
+
+LIBPLDM_ABI_TESTING
+int encode_pldm_platform_file_descriptor_pdr(
+	const struct pldm_platform_file_descriptor_pdr *pdr, void *data,
+	size_t *data_len)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+
+	if (!pdr || !pdr->file_name.ptr || !data ||
+	    (pdr->file_name.length <= 1)) {
+		return -EINVAL;
+	}
+
+	if ((pdr->oem_file_classification_name.length > 1) &&
+	    !pdr->oem_file_classification_name.ptr) {
+		return -EINVAL;
+	}
+
+	if ((pdr->file_name.length > PLDM_FILE_PDR_FILE_NAME_MAX_LENGTH) ||
+	    (pdr->oem_file_classification_name.length >
+	     PLDM_FILE_PDR_FILE_NAME_MAX_LENGTH)) {
+		return -EINVAL;
+	}
+
+	size_t total_oem_name_segment_size = 0;
+	if (pdr->oem_file_classification_name.length > 1) {
+		total_oem_name_segment_size =
+			pdr->oem_file_classification_name.length +
+			sizeof(uint8_t);
+	}
+
+	// Length of the PDR in the response
+	size_t pdr_len = PLDM_PDR_FILE_DESCRIPTOR_PDR_MIN_LENGTH +
+			 pdr->file_name.length + total_oem_name_segment_size;
+	if (pdr->hdr.length != (pdr_len - sizeof(struct pldm_pdr_hdr))) {
+		return -EINVAL;
+	}
+
+	int rc = pldm_msgbuf_init_errno(buf, pdr_len, data, *data_len);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_insert(buf, pdr->hdr.record_handle);
+	pldm_msgbuf_insert(buf, pdr->hdr.version);
+	pldm_msgbuf_insert(buf, pdr->hdr.type);
+	pldm_msgbuf_insert(buf, pdr->hdr.record_change_num);
+	pldm_msgbuf_insert(buf, pdr->hdr.length);
+	pldm_msgbuf_insert(buf, pdr->terminus_handle);
+	pldm_msgbuf_insert(buf, pdr->file_identifier);
+	pldm_msgbuf_insert(buf, pdr->container.entity_type);
+	pldm_msgbuf_insert(buf, pdr->container.entity_instance_num);
+	pldm_msgbuf_insert(buf, pdr->container.entity_container_id);
+	pldm_msgbuf_insert(buf, pdr->superior_directory_file_identifier);
+	pldm_msgbuf_insert(buf, pdr->file_classification);
+	pldm_msgbuf_insert(buf, pdr->oem_file_classification);
+	pldm_msgbuf_insert(buf, pdr->file_capabilities.value);
+
+	rc = pldm_msgbuf_insert_array(buf, sizeof(pdr->file_version),
+				      (uint8_t *)(&pdr->file_version),
+				      sizeof(pdr->file_version));
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+
+	pldm_msgbuf_insert(buf, pdr->file_maximum_size);
+	pldm_msgbuf_insert(buf, pdr->file_maximum_file_descriptor_count);
+	pldm_msgbuf_insert(buf, (uint8_t)pdr->file_name.length);
+
+	rc = pldm_msgbuf_insert_array(buf, pdr->file_name.length,
+				      pdr->file_name.ptr,
+				      pdr->file_name.length);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+
+	if (pdr->oem_file_classification_name.length > 1) {
+		pldm_msgbuf_insert(
+			buf, (uint8_t)pdr->oem_file_classification_name.length);
+		rc = pldm_msgbuf_insert_array(
+			buf, pdr->oem_file_classification_name.length,
+			pdr->oem_file_classification_name.ptr,
+			pdr->oem_file_classification_name.length);
+		if (rc) {
+			return pldm_msgbuf_discard(buf, rc);
+		}
+	}
+
+	return pldm_msgbuf_complete_used(buf, *data_len, data_len);
+}
diff --git a/tests/dsp/platform.cpp b/tests/dsp/platform.cpp
index 6f4c517..8450e11 100644
--- a/tests/dsp/platform.cpp
+++ b/tests/dsp/platform.cpp
@@ -6397,3 +6397,221 @@
     EXPECT_EQ(-EOVERFLOW, rc);
 }
 #endif
+
+#ifdef LIBPLDM_API_TESTING
+namespace
+{
+void createFileDescriptorPDR(pldm_platform_file_descriptor_pdr& pdr,
+                             const std::string& fileName,
+                             const std::string& oemName)
+{
+    pdr.hdr = {1, 1, PLDM_FILE_DESCRIPTOR_PDR, 0, 0};
+    pdr.terminus_handle = 2;
+    pdr.file_identifier = 10;
+    pdr.container = {20, 1, 0};
+    pdr.superior_directory_file_identifier = 0;
+    pdr.file_classification = 1;
+    pdr.oem_file_classification = oemName.empty() ? 0 : 1;
+    pdr.file_capabilities = {0};
+    pdr.file_version = {1, 2, 3, 4};
+    pdr.file_maximum_size = 1024;
+    pdr.file_maximum_file_descriptor_count = 1;
+
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+    pdr.file_name.ptr = reinterpret_cast<const uint8_t*>(fileName.c_str());
+    pdr.file_name.length = fileName.length() + 1;
+
+    size_t total_oem_name_segment_size = 0;
+    if (!oemName.empty())
+    {
+        // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
+        pdr.oem_file_classification_name.ptr =
+            reinterpret_cast<const uint8_t*>(oemName.c_str());
+        // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
+        pdr.oem_file_classification_name.length = oemName.length() + 1;
+        total_oem_name_segment_size =
+            pdr.oem_file_classification_name.length + sizeof(uint8_t);
+    }
+    else
+    {
+        pdr.oem_file_classification_name.ptr = nullptr;
+        pdr.oem_file_classification_name.length = 0;
+        total_oem_name_segment_size = 0;
+    }
+
+    size_t pdrLen = PLDM_PDR_FILE_DESCRIPTOR_PDR_MIN_LENGTH +
+                    pdr.file_name.length + total_oem_name_segment_size;
+    pdr.hdr.length = pdrLen - sizeof(struct pldm_pdr_hdr);
+}
+} // namespace
+
+TEST(EncodePldmFileDescriptorPdr, SuccessCase)
+{
+    pldm_platform_file_descriptor_pdr pdr{};
+    std::string fileName = "test_file.txt";
+    std::string oemName = "test_oem_name";
+    createFileDescriptorPDR(pdr, fileName, oemName);
+
+    size_t pdrLen = sizeof(struct pldm_pdr_hdr) + pdr.hdr.length;
+    std::vector<uint8_t> buffer(pdrLen);
+
+    auto rc =
+        encode_pldm_platform_file_descriptor_pdr(&pdr, buffer.data(), &pdrLen);
+    EXPECT_EQ(rc, 0);
+
+    pldm_platform_file_descriptor_pdr decoded_pdr{};
+    rc = decode_pldm_platform_file_descriptor_pdr(buffer.data(), buffer.size(),
+                                                  &decoded_pdr);
+    EXPECT_EQ(rc, 0);
+
+    EXPECT_EQ(pdr.hdr.record_handle, decoded_pdr.hdr.record_handle);
+    EXPECT_EQ(pdr.hdr.version, decoded_pdr.hdr.version);
+    EXPECT_EQ(pdr.hdr.type, decoded_pdr.hdr.type);
+    EXPECT_EQ(pdr.hdr.record_change_num, decoded_pdr.hdr.record_change_num);
+    EXPECT_EQ(pdr.hdr.length, decoded_pdr.hdr.length);
+    EXPECT_EQ(pdr.terminus_handle, decoded_pdr.terminus_handle);
+    EXPECT_EQ(pdr.file_identifier, decoded_pdr.file_identifier);
+    EXPECT_EQ(pdr.container.entity_type, decoded_pdr.container.entity_type);
+    EXPECT_EQ(pdr.container.entity_instance_num,
+              decoded_pdr.container.entity_instance_num);
+    EXPECT_EQ(pdr.container.entity_container_id,
+              decoded_pdr.container.entity_container_id);
+    EXPECT_EQ(pdr.superior_directory_file_identifier,
+              decoded_pdr.superior_directory_file_identifier);
+    EXPECT_EQ(pdr.file_classification, decoded_pdr.file_classification);
+    EXPECT_EQ(pdr.oem_file_classification, decoded_pdr.oem_file_classification);
+    EXPECT_EQ(pdr.file_capabilities.value, decoded_pdr.file_capabilities.value);
+    EXPECT_EQ(0, memcmp(&pdr.file_version, &decoded_pdr.file_version,
+                        sizeof(pdr.file_version)));
+    EXPECT_EQ(pdr.file_maximum_size, decoded_pdr.file_maximum_size);
+    EXPECT_EQ(pdr.file_maximum_file_descriptor_count,
+              decoded_pdr.file_maximum_file_descriptor_count);
+    EXPECT_EQ(pdr.file_name.length, decoded_pdr.file_name.length);
+    EXPECT_EQ(0, memcmp(pdr.file_name.ptr, decoded_pdr.file_name.ptr,
+                        pdr.file_name.length));
+    EXPECT_EQ(pdr.oem_file_classification_name.length,
+              decoded_pdr.oem_file_classification_name.length);
+    EXPECT_EQ(0, memcmp(pdr.oem_file_classification_name.ptr,
+                        decoded_pdr.oem_file_classification_name.ptr,
+                        pdr.oem_file_classification_name.length));
+}
+
+TEST(EncodePldmFileDescriptorPdr, BadParamStringTooLong)
+{
+    pldm_platform_file_descriptor_pdr pdr{};
+    std::string shortFileName = "file";
+    std::string longName(256, 'a');
+
+    // Test file_name.length > 255
+    createFileDescriptorPDR(pdr, longName, "");
+    size_t pdrLen = sizeof(struct pldm_pdr_hdr) + pdr.hdr.length;
+    std::vector<uint8_t> buffer(pdrLen);
+    EXPECT_EQ(
+        encode_pldm_platform_file_descriptor_pdr(&pdr, buffer.data(), &pdrLen),
+        -EINVAL);
+
+    // Test oem_file_classification_name.length > 255
+    createFileDescriptorPDR(pdr, shortFileName, longName);
+    pdrLen = sizeof(struct pldm_pdr_hdr) + pdr.hdr.length;
+    buffer.resize(pdrLen);
+    EXPECT_EQ(
+        encode_pldm_platform_file_descriptor_pdr(&pdr, buffer.data(), &pdrLen),
+        -EINVAL);
+}
+
+TEST(EncodePldmFileDescriptorPdr, BadParamNullPdrData)
+{
+    std::vector<uint8_t> pdrBuf(100);
+    size_t pdrBufSize = pdrBuf.size();
+    EXPECT_EQ(encode_pldm_platform_file_descriptor_pdr(NULL, pdrBuf.data(),
+                                                       &pdrBufSize),
+              -EINVAL);
+}
+
+TEST(EncodePldmFileDescriptorPdr, BadParamNullRespBuffer)
+{
+    pldm_platform_file_descriptor_pdr pdr{};
+    std::string fileName = "test_file.txt";
+    createFileDescriptorPDR(pdr, fileName, "");
+    size_t pdrLen = sizeof(struct pldm_pdr_hdr) + pdr.hdr.length;
+    EXPECT_EQ(encode_pldm_platform_file_descriptor_pdr(&pdr, NULL, &pdrLen),
+              -EINVAL);
+}
+
+TEST(EncodePldmFileDescriptorPdr, BadParamNullFileNamePtr)
+{
+    pldm_platform_file_descriptor_pdr pdr{};
+    std::string fileName = "test_file.txt";
+    createFileDescriptorPDR(pdr, fileName, "");
+    pdr.file_name.ptr = nullptr;
+    size_t pdrLen = sizeof(struct pldm_pdr_hdr) + pdr.hdr.length;
+    std::vector<uint8_t> buffer(pdrLen);
+    EXPECT_EQ(
+        encode_pldm_platform_file_descriptor_pdr(&pdr, buffer.data(), &pdrLen),
+        -EINVAL);
+}
+
+TEST(EncodePldmFileDescriptorPdr, BadParamInvalidFileNameLength)
+{
+    pldm_platform_file_descriptor_pdr pdr{};
+    std::string fileName = "t";
+    createFileDescriptorPDR(pdr, "ab", "");
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+    pdr.file_name.ptr = reinterpret_cast<const uint8_t*>(fileName.c_str());
+
+    pdr.file_name.length = 1;
+    pdr.hdr.length -= 1;
+    size_t pdrLen = sizeof(struct pldm_pdr_hdr) + pdr.hdr.length;
+    std::vector<uint8_t> buffer(pdrLen);
+    EXPECT_EQ(
+        encode_pldm_platform_file_descriptor_pdr(&pdr, buffer.data(), &pdrLen),
+        -EINVAL);
+
+    pdr.file_name.length = 0;
+    pdr.hdr.length -= 1;
+    pdrLen = sizeof(struct pldm_pdr_hdr) + pdr.hdr.length;
+    EXPECT_EQ(
+        encode_pldm_platform_file_descriptor_pdr(&pdr, buffer.data(), &pdrLen),
+        -EINVAL);
+}
+
+TEST(EncodePldmFileDescriptorPdr, BadParamNullOemNamePtr)
+{
+    pldm_platform_file_descriptor_pdr pdr{};
+    std::string fileName = "test_file.txt";
+    std::string oemName = "test_oem";
+    createFileDescriptorPDR(pdr, fileName, oemName);
+    pdr.oem_file_classification_name.ptr = nullptr;
+    size_t pdrLen = sizeof(struct pldm_pdr_hdr) + pdr.hdr.length;
+    std::vector<uint8_t> buffer(pdrLen);
+    EXPECT_EQ(
+        encode_pldm_platform_file_descriptor_pdr(&pdr, buffer.data(), &pdrLen),
+        -EINVAL);
+}
+
+TEST(EncodePldmFileDescriptorPdr, BadParamIncorrectHdrLength)
+{
+    pldm_platform_file_descriptor_pdr pdr{};
+    std::string fileName = "test_file.txt";
+    createFileDescriptorPDR(pdr, fileName, "");
+    pdr.hdr.length += 1;
+    size_t pdrLen = sizeof(struct pldm_pdr_hdr) + pdr.hdr.length;
+    std::vector<uint8_t> buffer(pdrLen);
+    EXPECT_EQ(
+        encode_pldm_platform_file_descriptor_pdr(&pdr, buffer.data(), &pdrLen),
+        -EINVAL);
+}
+
+TEST(EncodePldmFileDescriptorPdr, BadParamBufferTooSmall)
+{
+    pldm_platform_file_descriptor_pdr pdr{};
+    std::string fileName = "test_file.txt";
+    createFileDescriptorPDR(pdr, fileName, "");
+    size_t pdrLen = sizeof(struct pldm_pdr_hdr) + pdr.hdr.length;
+    std::vector<uint8_t> buffer(pdrLen - 1);
+    size_t bufferSize = buffer.size();
+    EXPECT_EQ(encode_pldm_platform_file_descriptor_pdr(&pdr, buffer.data(),
+                                                       &bufferSize),
+              -EOVERFLOW);
+}
+#endif