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;