dsp: base: Add encode req & decode resp for MultipartReceive command

Added encode/decode APIs for MultipartReceive command(0x09) which is
defined in DSP0240 Version 1.2.0 section 9.6.

Change-Id: I577997978728cbaa9132e0685cdd85e277427554
Signed-off-by: Dung Cao <dung@os.amperecomputing.com>
Signed-off-by: Chau Ly <chaul@amperecomputing.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 99dd335..06d4684 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -34,6 +34,8 @@
 
 - Support for building the documentation with doxygen
 
+- base: Add encode req & decode resp for MultipartReceive
+
 ### Changed
 
 - dsp: firmware_update: Expand "params" in symbol names
diff --git a/include/libpldm/base.h b/include/libpldm/base.h
index 0fc73f4..f74404e 100644
--- a/include/libpldm/base.h
+++ b/include/libpldm/base.h
@@ -6,7 +6,9 @@
 extern "C" {
 #endif
 
+#include <libpldm/compiler.h>
 #include <libpldm/pldm_types.h>
+#include <libpldm/utils.h>
 
 #include <asm/byteorder.h>
 #include <stdalign.h>
@@ -71,6 +73,14 @@
 	PLDM_XFER_CURRENT_PART = 4,
 };
 
+enum pldm_base_multipart_receive_transfer_flag {
+	PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_START = 0x01,
+	PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_MIDDLE = 0x02,
+	PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_END = 0x04,
+	PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_START_AND_END = 0x05,
+	PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_ACK_COMPLETION = 0x08,
+};
+
 enum transfer_resp_flag {
 	PLDM_START = 0x01,
 	PLDM_MIDDLE = 0x02,
@@ -116,8 +126,9 @@
 #define PLDM_SET_TID_RESP_BYTES	     1
 #define PLDM_GET_COMMANDS_RESP_BYTES 33
 /* Response data has only one version and does not contain the checksum */
-#define PLDM_GET_VERSION_RESP_BYTES	 10
-#define PLDM_MULTIPART_RECEIVE_REQ_BYTES 18
+#define PLDM_GET_VERSION_RESP_BYTES		   10
+#define PLDM_MULTIPART_RECEIVE_REQ_BYTES	   18
+#define PLDM_BASE_MULTIPART_RECEIVE_RESP_MIN_BYTES 10
 
 #define PLDM_VERSION_0	     0
 #define PLDM_CURRENT_VERSION PLDM_VERSION_0
@@ -340,6 +351,19 @@
 	uint32_t section_length;  //!< The length (in bytes) of the section
 				  //!< requested.
 } __attribute__((packed));
+
+/** @struct pldm_multipart_receive_resp
+ *
+ * Structure representing PLDM multipart receive request.
+ */
+struct pldm_multipart_receive_resp {
+	uint8_t completion_code;       //!< Completion code of the command.
+	uint8_t transfer_flag;	       //!< PLDM MultipartReceive transfer flag.
+	uint32_t next_transfer_handle; //!< The handle for the next part of
+				       //!< data for this section transfer.
+	struct variable_field data;
+};
+
 /**
  * @brief Populate the PLDM message with the PLDM header.The caller of this API
  *        allocates buffer for the PLDM header when forming the PLDM message.
@@ -633,6 +657,44 @@
 				 uint32_t *section_offset,
 				 uint32_t *section_length);
 
+/** @brief Encode a PLDM MultipartReceive request message
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] req - The pointer to the request message to be encoded
+ *  @param[in,out] msg - Message will be written to this
+ *  @param[in] payload_length - length of request message payload
+ *  @return 0 on success
+ *          -EINVAL if the input parameters' memory are not allocated,
+ *          or message type or instance in request header is invalid
+ *          -ENOMSG if the PLDM type in the request header is invalid
+ *          -EOVERFLOW if the input message length is invalid
+ */
+int encode_base_multipart_receive_req(
+	uint8_t instance_id, const struct pldm_multipart_receive_req *req,
+	struct pldm_msg *msg, size_t payload_length);
+
+/** @brief Decode a PLDM MultipartReceive response message
+ *
+ *  @param[in] msg - Response message
+ *  @param[in] payload_length - length of request message payload
+ *  @param[out] resp - pointer to the decoded response message,
+ *         excluding the data integrity checksum
+ *  @param[out] data_integrity_checksum - The checksum of data field
+ *         of the decoded response message
+ *  @return 0 on success
+ *          -EINVAL if the input parameters' memory are not allocated
+ *          -EOVERFLOW if the input message buffer is too short for the output
+ *          response struct
+ *          -EBADMSG if the input message buffer is too large for the output
+ *          response struct
+ *  @note  Caller is responsible for memory alloc and dealloc of param
+ *         'msg.payload'
+ */
+int decode_base_multipart_receive_resp(const struct pldm_msg *msg,
+				       size_t payload_length,
+				       struct pldm_multipart_receive_resp *resp,
+				       uint32_t *data_integrity_checksum);
+
 /** @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 f33b9b2..42f33e2 100644
--- a/src/dsp/base.c
+++ b/src/dsp/base.c
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
 #include "api.h"
 #include "dsp/base.h"
+#include "msgbuf.h"
 
 #include <assert.h>
 #include <libpldm/base.h>
@@ -555,6 +556,100 @@
 	return PLDM_SUCCESS;
 }
 
+LIBPLDM_ABI_TESTING
+int encode_base_multipart_receive_req(
+	uint8_t instance_id, const struct pldm_multipart_receive_req *req,
+	struct pldm_msg *msg, size_t payload_length)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+
+	if (req == NULL || msg == NULL) {
+		return -EINVAL;
+	}
+
+	struct pldm_header_info header = { 0 };
+	header.instance = instance_id;
+	header.msg_type = PLDM_REQUEST;
+	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_MULTIPART_RECEIVE_REQ_BYTES,
+				    msg->payload, payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_insert(buf, req->pldm_type);
+	pldm_msgbuf_insert(buf, req->transfer_opflag);
+	pldm_msgbuf_insert(buf, req->transfer_ctx);
+	pldm_msgbuf_insert(buf, req->transfer_handle);
+	pldm_msgbuf_insert(buf, req->section_offset);
+	pldm_msgbuf_insert(buf, req->section_length);
+
+	return pldm_msgbuf_complete(buf);
+}
+
+LIBPLDM_ABI_TESTING
+int decode_base_multipart_receive_resp(const struct pldm_msg *msg,
+				       size_t payload_length,
+				       struct pldm_multipart_receive_resp *resp,
+				       uint32_t *data_integrity_checksum)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+
+	if (msg == NULL || resp == NULL || data_integrity_checksum == NULL) {
+		return -EINVAL;
+	}
+
+	rc = pldm_msg_has_error(msg, payload_length);
+
+	if (rc) {
+		resp->completion_code = rc;
+		return 0;
+	}
+
+	rc = pldm_msgbuf_init_errno(buf,
+				    PLDM_BASE_MULTIPART_RECEIVE_RESP_MIN_BYTES,
+				    msg->payload, payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract(buf, resp->completion_code);
+	rc = pldm_msgbuf_extract(buf, resp->transfer_flag);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+	pldm_msgbuf_extract(buf, resp->next_transfer_handle);
+
+	rc = pldm_msgbuf_extract_uint32_to_size(buf, resp->data.length);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+
+	if (resp->data.length > 0) {
+		resp->data.ptr = NULL;
+		pldm_msgbuf_span_required(buf, resp->data.length,
+					  (void **)&resp->data.ptr);
+	}
+
+	if (resp->transfer_flag ==
+		    PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_END ||
+	    resp->transfer_flag ==
+		    PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_START_AND_END) {
+		pldm_msgbuf_extract_p(buf, data_integrity_checksum);
+	}
+
+	return pldm_msgbuf_complete_consumed(buf);
+}
+
 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 5e71c80..7b5bfcf 100644
--- a/tests/dsp/base.cpp
+++ b/tests/dsp/base.cpp
@@ -6,6 +6,8 @@
 #include <cstring>
 #include <vector>
 
+#include "msgbuf.h"
+
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
@@ -759,6 +761,213 @@
               PLDM_ERROR_INVALID_DATA);
 }
 
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeMultipartReceiveRequest, GoodTest)
+{
+    uint8_t instance_id = 0;
+
+    const struct pldm_multipart_receive_req req_data = {
+        PLDM_BASE, PLDM_XFER_FIRST_PART, 0x01, 0x10, 0x00, 0x10};
+
+    std::array<uint8_t, PLDM_MULTIPART_RECEIVE_REQ_BYTES> requestMsg = {
+        PLDM_BASE, PLDM_XFER_FIRST_PART,
+        0x01,      0x00,
+        0x00,      0x00,
+        0x10,      0x00,
+        0x00,      0x00,
+        0x00,      0x00,
+        0x00,      0x00,
+        0x10,      0x00,
+        0x00,      0x00};
+
+    PLDM_MSG_DEFINE_P(requestPtr, PLDM_MULTIPART_RECEIVE_REQ_BYTES);
+    auto rc = encode_base_multipart_receive_req(
+        instance_id, &req_data, requestPtr, PLDM_MULTIPART_RECEIVE_REQ_BYTES);
+
+    ASSERT_EQ(rc, 0);
+    EXPECT_EQ(
+        0, memcmp(requestPtr->payload, requestMsg.data(), sizeof(requestMsg)));
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeMultipartReceiveRequest, BadTestUnAllocatedPtrParams)
+{
+    uint8_t instance_id = 0;
+    int rc;
+
+    const struct pldm_multipart_receive_req req_data = {
+        PLDM_BASE, PLDM_XFER_FIRST_PART, 0x01, 0x10, 0x00, 0x10};
+
+    PLDM_MSG_DEFINE_P(requestPtr, PLDM_MULTIPART_RECEIVE_REQ_BYTES);
+    rc = encode_base_multipart_receive_req(instance_id, nullptr, requestPtr,
+                                           PLDM_MULTIPART_RECEIVE_REQ_BYTES);
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = encode_base_multipart_receive_req(instance_id, &req_data, nullptr,
+                                           PLDM_MULTIPART_RECEIVE_REQ_BYTES);
+    EXPECT_EQ(rc, -EINVAL);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeMultipartReceiveRequest, BadTestInvalidExpectedOutputMsgLength)
+{
+    uint8_t instance_id = 0;
+    int rc;
+
+    const struct pldm_multipart_receive_req req_data = {
+        PLDM_BASE, PLDM_XFER_FIRST_PART, 0x01, 0x10, 0x00, 0x10};
+
+    PLDM_MSG_DEFINE_P(requestPtr, PLDM_MULTIPART_RECEIVE_REQ_BYTES);
+
+    rc = encode_base_multipart_receive_req(instance_id, &req_data, requestPtr,
+                                           1);
+    EXPECT_EQ(rc, -EOVERFLOW);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodeMultipartReceiveResponse, GoodTest)
+{
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint8_t transferFlag = PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_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;
+
+    struct pldm_multipart_receive_resp resp_data = {};
+
+    PLDM_MSGBUF_DEFINE_P(buf);
+    int rc;
+
+    static constexpr const size_t payload_length =
+        PLDM_BASE_MULTIPART_RECEIVE_RESP_MIN_BYTES + dataLength +
+        sizeof(dataIntegrityChecksum);
+    PLDM_MSG_DEFINE_P(responseMsg, payload_length);
+
+    rc = pldm_msgbuf_init_errno(buf, 0, responseMsg->payload, payload_length);
+    ASSERT_EQ(rc, 0);
+
+    pldm_msgbuf_insert_uint8(buf, completionCode);
+    pldm_msgbuf_insert_uint8(buf, transferFlag);
+    pldm_msgbuf_insert_uint32(buf, nextDataTransferHandle);
+    pldm_msgbuf_insert_uint32(buf, dataLength);
+    rc = pldm_msgbuf_insert_array_uint8(buf, dataLength, data.data(),
+                                        dataLength);
+    EXPECT_EQ(rc, 0);
+    pldm_msgbuf_insert_uint32(buf, dataIntegrityChecksum);
+
+    ASSERT_EQ(pldm_msgbuf_complete_consumed(buf), 0);
+
+    uint32_t respDataIntegrityChecksum = 0;
+
+    rc = decode_base_multipart_receive_resp(
+        responseMsg, payload_length, &resp_data, &respDataIntegrityChecksum);
+
+    ASSERT_EQ(rc, 0);
+    EXPECT_EQ(resp_data.completion_code, completionCode);
+    EXPECT_EQ(resp_data.transfer_flag, transferFlag);
+    EXPECT_EQ(resp_data.next_transfer_handle, nextDataTransferHandle);
+    EXPECT_EQ(resp_data.data.length, dataLength);
+    EXPECT_EQ(0,
+              memcmp(data.data(), resp_data.data.ptr, resp_data.data.length));
+    EXPECT_EQ(respDataIntegrityChecksum, dataIntegrityChecksum);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodeMultipartReceiveResponse, BadTestUnAllocatedPtrParams)
+{
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint8_t transferFlag = PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_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;
+
+    struct pldm_multipart_receive_resp resp_data = {};
+
+    PLDM_MSGBUF_DEFINE_P(buf);
+    int rc;
+
+    static constexpr const size_t payload_length =
+        PLDM_BASE_MULTIPART_RECEIVE_RESP_MIN_BYTES + dataLength +
+        sizeof(dataIntegrityChecksum);
+    PLDM_MSG_DEFINE_P(responseMsg, payload_length);
+
+    rc = pldm_msgbuf_init_errno(buf, 0, responseMsg->payload, payload_length);
+    ASSERT_EQ(rc, 0);
+
+    pldm_msgbuf_insert_uint8(buf, completionCode);
+    pldm_msgbuf_insert_uint8(buf, transferFlag);
+    pldm_msgbuf_insert_uint32(buf, nextDataTransferHandle);
+    pldm_msgbuf_insert_uint32(buf, dataLength);
+    rc = pldm_msgbuf_insert_array_uint8(buf, dataLength, data.data(),
+                                        dataLength);
+    EXPECT_EQ(rc, 0);
+    pldm_msgbuf_insert_uint32(buf, dataIntegrityChecksum);
+
+    ASSERT_EQ(pldm_msgbuf_complete_consumed(buf), 0);
+
+    uint32_t respDataIntegrityChecksum = 0;
+
+    rc = decode_base_multipart_receive_resp(nullptr, payload_length, &resp_data,
+                                            &respDataIntegrityChecksum);
+
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = decode_base_multipart_receive_resp(
+        responseMsg, payload_length, nullptr, &respDataIntegrityChecksum);
+
+    EXPECT_EQ(rc, -EINVAL);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodeMultipartReceiveResponse, BadTestInvalidExpectedInputMsgLength)
+{
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint8_t transferFlag = PLDM_BASE_MULTIPART_RECEIVE_TRANSFER_FLAG_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;
+
+    struct pldm_multipart_receive_resp resp_data = {};
+
+    PLDM_MSGBUF_DEFINE_P(buf);
+    int rc;
+
+    static constexpr const size_t payload_length =
+        PLDM_BASE_MULTIPART_RECEIVE_RESP_MIN_BYTES + dataLength +
+        sizeof(dataIntegrityChecksum);
+    PLDM_MSG_DEFINE_P(responseMsg, payload_length);
+
+    rc = pldm_msgbuf_init_errno(buf, 0, responseMsg->payload, payload_length);
+    ASSERT_EQ(rc, 0);
+
+    pldm_msgbuf_insert_uint8(buf, completionCode);
+    pldm_msgbuf_insert_uint8(buf, transferFlag);
+    pldm_msgbuf_insert_uint32(buf, nextDataTransferHandle);
+    pldm_msgbuf_insert_uint32(buf, dataLength);
+    rc = pldm_msgbuf_insert_array_uint8(buf, dataLength, data.data(),
+                                        dataLength);
+    EXPECT_EQ(rc, 0);
+    pldm_msgbuf_insert_uint32(buf, dataIntegrityChecksum);
+
+    ASSERT_EQ(pldm_msgbuf_complete_consumed(buf), 0);
+
+    uint32_t respDataIntegrityChecksum = 0;
+
+    rc = decode_base_multipart_receive_resp(responseMsg, 0, &resp_data,
+                                            &respDataIntegrityChecksum);
+
+    EXPECT_EQ(rc, -EOVERFLOW);
+}
+#endif
+
 TEST(CcOnlyResponse, testEncode)
 {
     struct pldm_msg responseMsg;