dsp: base: Add encode resp for MultipartReceive command

Added encode APIs for MultipartReceive command (0x09) that
defined in DSP0240 1.2.0 section 9.6.

Change-Id: I08eb9be3685dd6eb35e7559eb37101604409562f
Signed-off-by: John Chung <john.chung@arm.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e81524c..ce9bea3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,7 @@
   their contents.
 
 - file: Add encode req & decode resp for DfOpen and DfClose command
+- base: Add encode resp for MultipartReceive command
 
 ### Changed
 
diff --git a/include/libpldm/base.h b/include/libpldm/base.h
index 0ba51c8..d149ab6 100644
--- a/include/libpldm/base.h
+++ b/include/libpldm/base.h
@@ -752,6 +752,24 @@
 				       struct pldm_multipart_receive_resp *resp,
 				       uint32_t *data_integrity_checksum);
 
+/** @brief Encode a PLDM MultipartReceive response message
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] resp - The pointer to the response message to be encoded
+ *  @param[in] checksum - Checksum of the entirely data payload
+ *  @param[in,out] msg - Message will be written to this
+ *  @param[in,out] payload_length - length of request message payload
+ *  @return 0 on success
+ *          -EINVAL if argument values are invalid for the invocation
+ *          -ENOMSG if the PLDM type in the request header is invalid
+ *          -EOVERFLOW if the input message length is invalid
+ *  @note  Caller is responsible for memory alloc and dealloc of param
+ *         'msg.payload'
+ */
+int encode_base_multipart_receive_resp(
+	uint8_t instance_id, const struct pldm_multipart_receive_resp *resp,
+	uint32_t checksum, struct pldm_msg *msg, size_t *payload_length);
+
 /** @brief Create a PLDM response message containing only cc
  *
  *  @param[in] instance_id - Message's instance id
diff --git a/src/dsp/base.c b/src/dsp/base.c
index c3a5ed9..c1bf63d 100644
--- a/src/dsp/base.c
+++ b/src/dsp/base.c
@@ -672,6 +672,67 @@
 	return pldm_msgbuf_complete_consumed(buf);
 }
 
+LIBPLDM_ABI_TESTING
+int encode_base_multipart_receive_resp(
+	uint8_t instance_id, const struct pldm_multipart_receive_resp *resp,
+	uint32_t checksum, struct pldm_msg *msg, size_t *payload_length)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+
+	if (!msg || !resp || !payload_length || !resp->data.ptr) {
+		return -EINVAL;
+	}
+
+	struct pldm_header_info header = { 0 };
+	header.instance = instance_id;
+	header.msg_type = PLDM_RESPONSE;
+	header.pldm_type = PLDM_BASE;
+	header.command = PLDM_MULTIPART_RECEIVE;
+
+	rc = pack_pldm_header_errno(&header, &msg->hdr);
+	if (rc) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_init_errno(buf,
+				    PLDM_BASE_MULTIPART_RECEIVE_RESP_MIN_BYTES,
+				    msg->payload, *payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_insert(buf, resp->completion_code);
+	if (resp->completion_code != PLDM_SUCCESS) {
+		// Return without encoding the rest of the field
+		return pldm_msgbuf_complete_used(buf, *payload_length,
+						 payload_length);
+	}
+
+	pldm_msgbuf_insert(buf, resp->transfer_flag);
+	pldm_msgbuf_insert(buf, resp->next_transfer_handle);
+
+	pldm_msgbuf_insert_uint32(buf, resp->data.length);
+	if (resp->data.length == 0) {
+		// Return without encoding data payload
+		return pldm_msgbuf_complete_used(buf, *payload_length,
+						 payload_length);
+	}
+
+	rc = pldm_msgbuf_insert_array(buf, resp->data.length, resp->data.ptr,
+				      resp->data.length);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+
+	if (resp->transfer_flag == PLDM_END ||
+	    resp->transfer_flag == PLDM_START_AND_END) {
+		pldm_msgbuf_insert(buf, checksum);
+	}
+
+	return pldm_msgbuf_complete_used(buf, *payload_length, payload_length);
+}
+
 LIBPLDM_ABI_STABLE
 int encode_cc_only_resp(uint8_t instance_id, uint8_t type, uint8_t command,
 			uint8_t cc, struct pldm_msg *msg)
diff --git a/tests/dsp/base.cpp b/tests/dsp/base.cpp
index c544766..0c10ff7 100644
--- a/tests/dsp/base.cpp
+++ b/tests/dsp/base.cpp
@@ -968,6 +968,216 @@
 }
 #endif
 
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeMultipartReceiveResponse, GoodTestWithChecksum)
+{
+    uint8_t instance_id = 0;
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint8_t transferFlag =
+        PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_START_AND_END;
+    uint32_t nextDataTransferHandle = 0x15;
+    static constexpr const uint32_t dataLength = 9;
+    std::vector<uint8_t> data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+    uint32_t dataIntegrityChecksum = 0x3C;
+    static constexpr const size_t responseMsgLength =
+        PLDM_BASE_MULTIPART_RECEIVE_RESP_MIN_BYTES + dataLength +
+        sizeof(dataIntegrityChecksum);
+    size_t payload_length = responseMsgLength;
+
+    struct variable_field payload = {data.data(), dataLength};
+    struct pldm_multipart_receive_resp resp_data = {
+        completionCode, transferFlag, nextDataTransferHandle, payload};
+    std::array<uint8_t, responseMsgLength> responseMsg = {
+        completionCode,
+        transferFlag,
+        0x15, // nextDataTransferHandle
+        0x00,
+        0x00,
+        0x00,
+        0x09, // dataLength
+        0x00,
+        0x00,
+        0x00,
+        0x1, // data
+        0x2,
+        0x3,
+        0x4,
+        0x5,
+        0x6,
+        0x7,
+        0x8,
+        0x9,
+        0x3c, // dataIntegrityChecksum
+        0x00,
+        0x00,
+        0x00};
+
+    PLDM_MSG_DEFINE_P(responsePtr, responseMsgLength);
+    int rc;
+
+    rc = encode_base_multipart_receive_resp(instance_id, &resp_data,
+                                            dataIntegrityChecksum, responsePtr,
+                                            &payload_length);
+
+    ASSERT_EQ(0, rc);
+    EXPECT_EQ(0, memcmp(responsePtr->payload, responseMsg.data(),
+                        sizeof(responseMsg)));
+    EXPECT_EQ(payload_length, responseMsgLength);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeMultipartReceiveResponse, GoodTestWithoutChecksum)
+{
+    uint8_t instance_id = 0;
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint8_t transferFlag = PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_START;
+    uint32_t nextDataTransferHandle = 0x16;
+    static constexpr const uint32_t dataLength = 9;
+    std::vector<uint8_t> data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+    static constexpr const size_t responseMsgLength =
+        PLDM_BASE_MULTIPART_RECEIVE_RESP_MIN_BYTES + dataLength;
+    size_t payload_length = responseMsgLength;
+
+    struct variable_field payload = {data.data(), dataLength};
+    struct pldm_multipart_receive_resp resp_data = {
+        completionCode, transferFlag, nextDataTransferHandle, payload};
+    std::array<uint8_t, responseMsgLength> responseMsg = {
+        completionCode,
+        transferFlag,
+        0x16, // nextDataTransferHandle
+        0x00,
+        0x00,
+        0x00,
+        0x09, // dataLength
+        0x00,
+        0x00,
+        0x00,
+        0x1, // data
+        0x2,
+        0x3,
+        0x4,
+        0x5,
+        0x6,
+        0x7,
+        0x8,
+        0x9};
+
+    PLDM_MSG_DEFINE_P(responsePtr, responseMsgLength);
+    int rc;
+
+    rc = encode_base_multipart_receive_resp(instance_id, &resp_data, 0,
+                                            responsePtr, &payload_length);
+
+    ASSERT_EQ(0, rc);
+    EXPECT_EQ(0, memcmp(responsePtr->payload, responseMsg.data(),
+                        sizeof(responseMsg)));
+    EXPECT_EQ(payload_length, responseMsgLength);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeMultipartReceiveResponse, GoodTestCompletionCode)
+{
+    uint8_t instance_id = 0;
+    uint8_t completionCode = PLDM_MULTIPART_RECEIVE_NEGOTIATION_INCOMPLETE;
+    uint8_t transferFlag = PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_START;
+    uint32_t nextDataTransferHandle = 0x16;
+    static constexpr const uint32_t dataLength = 9;
+    std::vector<uint8_t> data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+    static constexpr const size_t responseMsgLength =
+        PLDM_BASE_MULTIPART_RECEIVE_RESP_MIN_BYTES + dataLength;
+    size_t payload_length = responseMsgLength;
+
+    struct variable_field payload = {data.data(), dataLength};
+    struct pldm_multipart_receive_resp resp_data = {
+        completionCode, transferFlag, nextDataTransferHandle, payload};
+    std::array<uint8_t, 1> responseMsg = {completionCode};
+
+    PLDM_MSG_DEFINE_P(responsePtr, responseMsgLength);
+    int rc;
+
+    rc = encode_base_multipart_receive_resp(instance_id, &resp_data, 0,
+                                            responsePtr, &payload_length);
+
+    ASSERT_EQ(0, rc);
+    EXPECT_EQ(0, memcmp(responsePtr->payload, responseMsg.data(),
+                        sizeof(responseMsg)));
+    EXPECT_EQ(payload_length, 1);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeMultipartReceiveResponse, BadTestUnAllocatedParams)
+{
+    uint8_t instance_id = 0;
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint8_t transferFlag =
+        PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_START_AND_END;
+    uint32_t nextDataTransferHandle = 0x15;
+    static constexpr const uint32_t dataLength = 9;
+    std::vector<uint8_t> data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+    uint32_t dataIntegrityChecksum = 0x3C;
+    static constexpr const size_t responseMsgLength =
+        PLDM_BASE_MULTIPART_RECEIVE_RESP_MIN_BYTES + dataLength +
+        sizeof(dataIntegrityChecksum);
+    size_t payload_length = responseMsgLength;
+
+    struct variable_field payload = {data.data(), dataLength};
+    struct pldm_multipart_receive_resp resp_data = {
+        completionCode, transferFlag, nextDataTransferHandle, payload};
+
+    PLDM_MSG_DEFINE_P(responsePtr, responseMsgLength);
+    int rc;
+
+    rc = encode_base_multipart_receive_resp(instance_id, nullptr,
+                                            dataIntegrityChecksum, responsePtr,
+                                            &payload_length);
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = encode_base_multipart_receive_resp(instance_id, &resp_data,
+                                            dataIntegrityChecksum, nullptr,
+                                            &payload_length);
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = encode_base_multipart_receive_resp(
+        instance_id, &resp_data, dataIntegrityChecksum, responsePtr, nullptr);
+    EXPECT_EQ(rc, -EINVAL);
+
+    resp_data.data.ptr = nullptr;
+    rc = encode_base_multipart_receive_resp(instance_id, &resp_data,
+                                            dataIntegrityChecksum, responsePtr,
+                                            &payload_length);
+    EXPECT_EQ(rc, -EINVAL);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeMultipartReceiveResponse, BadTestInvalidExpectedOutputMsgLength)
+{
+    uint8_t instance_id = 0;
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint8_t transferFlag = PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_START;
+    uint32_t nextDataTransferHandle = 0x16;
+    static constexpr const uint32_t dataLength = 9;
+    std::vector<uint8_t> data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+    static constexpr const size_t responseMsgLength =
+        PLDM_BASE_MULTIPART_RECEIVE_RESP_MIN_BYTES;
+    size_t payload_length = responseMsgLength;
+
+    struct variable_field payload = {data.data(), dataLength};
+    struct pldm_multipart_receive_resp resp_data = {
+        completionCode, transferFlag, nextDataTransferHandle, payload};
+
+    PLDM_MSG_DEFINE_P(responsePtr, responseMsgLength);
+    int rc;
+
+    rc = encode_base_multipart_receive_resp(instance_id, &resp_data, 0,
+                                            responsePtr, &payload_length);
+    EXPECT_EQ(rc, -EOVERFLOW);
+}
+#endif
+
 TEST(CcOnlyResponse, testEncode)
 {
     struct pldm_msg responseMsg;