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/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, &section_offset, &section_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, &section_offset,
+                  &section_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, &section_offset,
+                                           &section_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, &section_offset,
+                                           &section_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, &section_offset,
+                                           &section_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, &section_offset,
+                                           &section_length),
+              PLDM_ERROR_INVALID_DATA);
+}
+
 TEST(CcOnlyResponse, testEncode)
 {
     struct pldm_msg responseMsg;