libpldm/base: Add MultipartReceive req decoding
Adds MultipartReceive (0x09) request decoding, per DSP0240 v1.1.0
section 8.6.5.
Signed-off-by: Joe Komlodi <komlodi@google.com>
Change-Id: I8d37a9abb7361cc7a238b5b322907739343ff473
diff --git a/libpldm/base.c b/libpldm/base.c
index 83cd22a..9a5ae09 100644
--- a/libpldm/base.c
+++ b/libpldm/base.c
@@ -398,6 +398,56 @@
return PLDM_SUCCESS;
}
+int decode_multipart_receive_req(
+ const struct pldm_msg *msg, size_t payload_length, uint8_t *pldm_type,
+ uint8_t *transfer_opflag, uint32_t *transfer_ctx, uint32_t *transfer_handle,
+ uint32_t *section_offset, uint32_t *section_length)
+{
+ if (msg == NULL || pldm_type == NULL || transfer_opflag == NULL ||
+ transfer_ctx == NULL || transfer_handle == NULL ||
+ section_offset == NULL || section_length == NULL) {
+ return PLDM_ERROR_INVALID_DATA;
+ }
+
+ if (payload_length != PLDM_MULTIPART_RECEIVE_REQ_BYTES) {
+ return PLDM_ERROR_INVALID_LENGTH;
+ }
+
+ struct pldm_multipart_receive_req *request =
+ (struct pldm_multipart_receive_req *)msg->payload;
+
+ if (request->pldm_type != PLDM_BASE) {
+ return PLDM_ERROR_INVALID_PLDM_TYPE;
+ }
+
+ // Any enum value above PLDM_XFER_CURRENT_PART is invalid.
+ if (request->transfer_opflag > PLDM_XFER_CURRENT_PART) {
+ return PLDM_INVALID_TRANSFER_OPERATION_FLAG;
+ }
+
+ // A section offset of 0 is only valid on FIRST_PART or COMPLETE Xfers.
+ uint32_t sec_offset = le32toh(request->section_offset);
+ if (sec_offset == 0 &&
+ (request->transfer_opflag != PLDM_XFER_FIRST_PART &&
+ request->transfer_opflag != PLDM_XFER_COMPLETE)) {
+ return PLDM_ERROR_INVALID_DATA;
+ }
+
+ uint32_t handle = le32toh(request->transfer_handle);
+ if (handle == 0 && request->transfer_opflag != PLDM_XFER_COMPLETE) {
+ return PLDM_ERROR_INVALID_DATA;
+ }
+
+ *pldm_type = request->pldm_type;
+ *transfer_opflag = request->transfer_opflag;
+ *transfer_ctx = request->transfer_ctx;
+ *transfer_handle = handle;
+ *section_offset = sec_offset;
+ *section_length = le32toh(request->section_length);
+
+ return PLDM_SUCCESS;
+}
+
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/libpldm/base.h b/libpldm/base.h
index 99fd6af..be1e355 100644
--- a/libpldm/base.h
+++ b/libpldm/base.h
@@ -28,7 +28,8 @@
PLDM_GET_TID = 0x2,
PLDM_GET_PLDM_VERSION = 0x3,
PLDM_GET_PLDM_TYPES = 0x4,
- PLDM_GET_PLDM_COMMANDS = 0x5
+ PLDM_GET_PLDM_COMMANDS = 0x5,
+ PLDM_MULTIPART_RECEIVE = 0x9,
};
/** @brief PLDM base codes
@@ -49,6 +50,14 @@
PLDM_GET_FIRSTPART = 1,
};
+enum transfer_multipart_op_flag {
+ PLDM_XFER_FIRST_PART = 0,
+ PLDM_XFER_NEXT_PART = 1,
+ PLDM_XFER_ABORT = 2,
+ PLDM_XFER_COMPLETE = 3,
+ PLDM_XFER_CURRENT_PART = 4,
+};
+
enum transfer_resp_flag {
PLDM_START = 0x01,
PLDM_MIDDLE = 0x02,
@@ -88,6 +97,7 @@
#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_VERSION_0 0
#define PLDM_CURRENT_VERSION PLDM_VERSION_0
@@ -214,6 +224,23 @@
uint8_t tid; //!< PLDM GetTID TID field
} __attribute__((packed));
+/** @struct pldm_multipart_receive_req
+ *
+ * Structure representing PLDM multipart receive request.
+ */
+struct pldm_multipart_receive_req {
+ uint8_t pldm_type; //!< PLDM Type for the MultipartReceive
+ //!< command.
+ uint8_t transfer_opflag; //!< PLDM MultipartReceive operation flag.
+ uint32_t transfer_ctx; //!< Protocol-specifc context for this
+ //!< transfer.
+ uint32_t transfer_handle; //!< handle to identify the part of data to be
+ //!< received.
+ uint32_t section_offset; //!< The start offset for the requested
+ //!< section.
+ uint32_t section_length; //!< The length (in bytes) of the section
+ //!< requested.
+} __attribute__((packed));
/**
* @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.
@@ -472,6 +499,27 @@
int encode_get_tid_resp(uint8_t instance_id, uint8_t completion_code,
uint8_t tid, struct pldm_msg *msg);
+/* Responder */
+
+/* MultipartRecieve */
+
+/** @brief Decode a PLDM MultipartReceive request message
+ *
+ * @param[in] msg - Request message
+ * @param[in] payload_length - length of request message payload
+ * @param[out] pldm_type - PLDM type for which version is requested
+ * @param[out] transfer_opflag - Transfer Flag
+ * @param[out] transfer_ctx - The context of the packet
+ * @param[out] transfer_handle - The handle of data
+ * @param[out] section_offset - The start of the requested section
+ * @param[out] section_length - The length of the requested section
+ * @return pldm_completion_codes
+ */
+int decode_multipart_receive_req(
+ const struct pldm_msg *msg, size_t payload_length, uint8_t *pldm_type,
+ uint8_t *transfer_opflag, uint32_t *transfer_ctx, uint32_t *transfer_handle,
+ uint32_t *section_offset, uint32_t *section_length);
+
/** @brief Create a PLDM response message containing only cc
*
* @param[in] instance_id - Message's instance id
diff --git a/libpldm/tests/libpldm_base_test.cpp b/libpldm/tests/libpldm_base_test.cpp
index b8b25d3..b3b1383 100644
--- a/libpldm/tests/libpldm_base_test.cpp
+++ b/libpldm/tests/libpldm_base_test.cpp
@@ -524,6 +524,221 @@
EXPECT_EQ(tid, 1);
}
+TEST(MultipartReceive, testDecodeRequestPass)
+{
+ constexpr uint8_t kPldmType = PLDM_BASE;
+ constexpr uint8_t kFlag = PLDM_XFER_FIRST_PART;
+ constexpr uint32_t kTransferCtx = 0x01;
+ constexpr uint32_t kTransferHandle = 0x10;
+ constexpr uint32_t kSectionOffset = 0x0;
+ constexpr uint32_t kSectionLength = 0x10;
+ uint8_t pldm_type = 0x0;
+ uint8_t flag = PLDM_GET_FIRSTPART;
+ uint32_t transfer_ctx;
+ uint32_t transfer_handle;
+ uint32_t section_offset;
+ uint32_t section_length;
+
+ // Header values don't matter for this test.
+ pldm_msg_hdr hdr{};
+ // Assign values to the packet struct and memcpy to ensure correct byte
+ // ordering.
+ pldm_multipart_receive_req req_pkt = {
+ .pldm_type = kPldmType,
+ .transfer_opflag = kFlag,
+ .transfer_ctx = kTransferCtx,
+ .transfer_handle = kTransferHandle,
+ .section_offset = kSectionOffset,
+ .section_length = kSectionLength,
+ };
+ std::vector<uint8_t> req(sizeof(hdr) + PLDM_MULTIPART_RECEIVE_REQ_BYTES);
+ std::memcpy(req.data(), &hdr, sizeof(hdr));
+ std::memcpy(req.data() + sizeof(hdr), &req_pkt, sizeof(req_pkt));
+
+ pldm_msg* pldm_request = reinterpret_cast<pldm_msg*>(req.data());
+ int rc = decode_multipart_receive_req(
+ pldm_request, req.size() - hdrSize, &pldm_type, &flag, &transfer_ctx,
+ &transfer_handle, §ion_offset, §ion_length);
+
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+ EXPECT_EQ(pldm_type, kPldmType);
+ EXPECT_EQ(flag, kFlag);
+ EXPECT_EQ(transfer_ctx, kTransferCtx);
+ EXPECT_EQ(transfer_handle, kTransferHandle);
+ EXPECT_EQ(section_offset, kSectionOffset);
+ EXPECT_EQ(section_length, kSectionLength);
+}
+
+TEST(MultipartReceive, testDecodeRequestFailNullData)
+{
+ EXPECT_EQ(decode_multipart_receive_req(NULL, 0, NULL, NULL, NULL, NULL,
+ NULL, NULL),
+ PLDM_ERROR_INVALID_DATA);
+}
+
+TEST(MultipartReceive, testDecodeRequestFailBadLength)
+{
+ constexpr uint8_t kPldmType = PLDM_BASE;
+ constexpr uint8_t kFlag = PLDM_XFER_FIRST_PART;
+ uint8_t pldm_type;
+ uint8_t flag;
+ uint32_t transfer_ctx;
+ uint32_t transfer_handle;
+ uint32_t section_offset;
+ uint32_t section_length;
+
+ // Header values don't matter for this test.
+ pldm_msg_hdr hdr{};
+ // Assign values to the packet struct and memcpy to ensure correct byte
+ // ordering.
+ pldm_multipart_receive_req req_pkt{};
+ req_pkt.pldm_type = kPldmType;
+ req_pkt.transfer_opflag = kFlag;
+
+ std::vector<uint8_t> req(sizeof(hdr) + PLDM_MULTIPART_RECEIVE_REQ_BYTES);
+ std::memcpy(req.data(), &hdr, sizeof(hdr));
+ std::memcpy(req.data() + sizeof(hdr), &req_pkt, sizeof(req_pkt));
+
+ pldm_msg* pldm_request = reinterpret_cast<pldm_msg*>(req.data());
+ EXPECT_EQ(decode_multipart_receive_req(
+ pldm_request, (req.size() - hdrSize) + 1, &pldm_type, &flag,
+ &transfer_ctx, &transfer_handle, §ion_offset,
+ §ion_length),
+ PLDM_ERROR_INVALID_LENGTH);
+}
+
+TEST(MultipartReceive, testDecodeRequestFailBadPldmType)
+{
+ constexpr uint8_t kPldmType = 0xff;
+ constexpr uint8_t kFlag = PLDM_XFER_FIRST_PART;
+ uint8_t pldm_type;
+ uint8_t flag;
+ uint32_t transfer_ctx;
+ uint32_t transfer_handle;
+ uint32_t section_offset;
+ uint32_t section_length;
+
+ // Header values don't matter for this test.
+ pldm_msg_hdr hdr{};
+ // Assign values to the packet struct and memcpy to ensure correct byte
+ // ordering.
+ pldm_multipart_receive_req req_pkt{};
+ req_pkt.pldm_type = kPldmType;
+ req_pkt.transfer_opflag = kFlag;
+
+ std::vector<uint8_t> req(sizeof(hdr) + PLDM_MULTIPART_RECEIVE_REQ_BYTES);
+ std::memcpy(req.data(), &hdr, sizeof(hdr));
+ std::memcpy(req.data() + sizeof(hdr), &req_pkt, sizeof(req_pkt));
+
+ pldm_msg* pldm_request = reinterpret_cast<pldm_msg*>(req.data());
+ EXPECT_EQ(decode_multipart_receive_req(pldm_request, req.size() - hdrSize,
+ &pldm_type, &flag, &transfer_ctx,
+ &transfer_handle, §ion_offset,
+ §ion_length),
+ PLDM_ERROR_INVALID_PLDM_TYPE);
+}
+
+TEST(MultipartReceive, testDecodeRequestFailBadTransferFlag)
+{
+ constexpr uint8_t kPldmType = PLDM_BASE;
+ constexpr uint8_t kFlag = PLDM_XFER_CURRENT_PART + 0x10;
+ uint8_t pldm_type;
+ uint8_t flag;
+ uint32_t transfer_ctx;
+ uint32_t transfer_handle;
+ uint32_t section_offset;
+ uint32_t section_length;
+
+ // Header values don't matter for this test.
+ pldm_msg_hdr hdr{};
+ // Assign values to the packet struct and memcpy to ensure correct byte
+ // ordering.
+ pldm_multipart_receive_req req_pkt{};
+ req_pkt.pldm_type = kPldmType;
+ req_pkt.transfer_opflag = kFlag;
+
+ std::vector<uint8_t> req(sizeof(hdr) + PLDM_MULTIPART_RECEIVE_REQ_BYTES);
+ std::memcpy(req.data(), &hdr, sizeof(hdr));
+ std::memcpy(req.data() + sizeof(hdr), &req_pkt, sizeof(req_pkt));
+
+ pldm_msg* pldm_request = reinterpret_cast<pldm_msg*>(req.data());
+ EXPECT_EQ(decode_multipart_receive_req(pldm_request, req.size() - hdrSize,
+ &pldm_type, &flag, &transfer_ctx,
+ &transfer_handle, §ion_offset,
+ §ion_length),
+ PLDM_INVALID_TRANSFER_OPERATION_FLAG);
+}
+
+TEST(MultipartReceive, testDecodeRequestFailBadOffset)
+{
+ constexpr uint8_t kPldmType = PLDM_BASE;
+ constexpr uint8_t kFlag = PLDM_XFER_NEXT_PART;
+ constexpr uint32_t kTransferHandle = 0x01;
+ constexpr uint32_t kSectionOffset = 0x0;
+ uint8_t pldm_type;
+ uint8_t flag;
+ uint32_t transfer_ctx;
+ uint32_t transfer_handle;
+ uint32_t section_offset;
+ uint32_t section_length;
+
+ // Header values don't matter for this test.
+ pldm_msg_hdr hdr{};
+ // Assign values to the packet struct and memcpy to ensure correct byte
+ // ordering.
+ pldm_multipart_receive_req req_pkt{};
+ req_pkt.pldm_type = kPldmType;
+ req_pkt.transfer_opflag = kFlag;
+ req_pkt.transfer_handle = kTransferHandle;
+ req_pkt.section_offset = kSectionOffset;
+
+ std::vector<uint8_t> req(sizeof(hdr) + PLDM_MULTIPART_RECEIVE_REQ_BYTES);
+ std::memcpy(req.data(), &hdr, sizeof(hdr));
+ std::memcpy(req.data() + sizeof(hdr), &req_pkt, sizeof(req_pkt));
+
+ pldm_msg* pldm_request = reinterpret_cast<pldm_msg*>(req.data());
+ EXPECT_EQ(decode_multipart_receive_req(pldm_request, req.size() - hdrSize,
+ &pldm_type, &flag, &transfer_ctx,
+ &transfer_handle, §ion_offset,
+ §ion_length),
+ PLDM_ERROR_INVALID_DATA);
+}
+
+TEST(MultipartReceive, testDecodeRequestFailBadHandle)
+{
+ constexpr uint8_t kPldmType = PLDM_BASE;
+ constexpr uint8_t kFlag = PLDM_XFER_NEXT_PART;
+ constexpr uint32_t kSectionOffset = 0x100;
+ constexpr uint32_t kTransferHandle = 0x0;
+ uint8_t pldm_type;
+ uint8_t flag;
+ uint32_t transfer_ctx;
+ uint32_t transfer_handle;
+ uint32_t section_offset;
+ uint32_t section_length;
+
+ // Header values don't matter for this test.
+ pldm_msg_hdr hdr{};
+ // Assign values to the packet struct and memcpy to ensure correct byte
+ // ordering.
+ pldm_multipart_receive_req req_pkt{};
+ req_pkt.pldm_type = kPldmType;
+ req_pkt.transfer_opflag = kFlag;
+ req_pkt.transfer_handle = kTransferHandle;
+ req_pkt.section_offset = kSectionOffset;
+
+ std::vector<uint8_t> req(sizeof(hdr) + PLDM_MULTIPART_RECEIVE_REQ_BYTES);
+ std::memcpy(req.data(), &hdr, sizeof(hdr));
+ std::memcpy(req.data() + sizeof(hdr), &req_pkt, sizeof(req_pkt));
+
+ pldm_msg* pldm_request = reinterpret_cast<pldm_msg*>(req.data());
+ EXPECT_EQ(decode_multipart_receive_req(pldm_request, req.size() - hdrSize,
+ &pldm_type, &flag, &transfer_ctx,
+ &transfer_handle, §ion_offset,
+ §ion_length),
+ PLDM_ERROR_INVALID_DATA);
+}
+
TEST(CcOnlyResponse, testEncode)
{
struct pldm_msg responseMsg;