Add NegotiateTransferParam responder handle

- Adds a responder handle for NegotiateTransferParameters
- This change adds the support for part sizes from multiple devices
- Also adds the checksum for GetPLDMVersion responses

Tested:
Tested with unit tests

Google-Bug-Id: 423027264
Change-Id: I9b4b276a4740e5cc7d963e6765f93becf5393553
Signed-off-by: Kasun Athukorala <kasunath@google.com>
diff --git a/libpldmresponder/base.cpp b/libpldmresponder/base.cpp
index 37d0dab..318c3ee 100644
--- a/libpldmresponder/base.cpp
+++ b/libpldmresponder/base.cpp
@@ -5,10 +5,15 @@
 
 #include <libpldm/base.h>
 #include <libpldm/bios.h>
+#include <libpldm/file.h>
 #include <libpldm/fru.h>
 #include <libpldm/platform.h>
 
+#include <phosphor-logging/lg2.hpp>
+
+#include <algorithm>
 #include <array>
+#include <bit>
 #include <cstring>
 #include <map>
 #include <stdexcept>
@@ -19,6 +24,8 @@
 #include <libpldm/oem/ibm/host.h>
 #endif
 
+PHOSPHOR_LOG2_USING;
+
 namespace pldm
 {
 using Type = uint8_t;
@@ -30,7 +37,7 @@
 static const std::map<Type, Cmd> capabilities{
     {PLDM_BASE,
      {PLDM_GET_TID, PLDM_GET_PLDM_VERSION, PLDM_GET_PLDM_TYPES,
-      PLDM_GET_PLDM_COMMANDS}},
+      PLDM_GET_PLDM_COMMANDS, PLDM_NEGOTIATE_TRANSFER_PARAMETERS}},
     {PLDM_PLATFORM,
      {PLDM_GET_PDR, PLDM_SET_STATE_EFFECTER_STATES, PLDM_SET_EVENT_RECEIVER,
       PLDM_GET_SENSOR_READING, PLDM_GET_STATE_SENSOR_READINGS,
@@ -43,6 +50,8 @@
     {PLDM_FRU,
      {PLDM_GET_FRU_RECORD_TABLE_METADATA, PLDM_GET_FRU_RECORD_TABLE,
       PLDM_GET_FRU_RECORD_BY_OPTION}},
+    {PLDM_FILE,
+     {PLDM_FILE_CMD_DF_OPEN, PLDM_FILE_CMD_DF_CLOSE, PLDM_FILE_CMD_DF_READ}},
 #ifdef OEM_IBM
     {PLDM_OEM,
      {PLDM_HOST_GET_ALERT_STATUS, PLDM_GET_FILE_TABLE, PLDM_READ_FILE,
@@ -58,11 +67,16 @@
     {PLDM_PLATFORM, {0x00, 0xf0, 0xf2, 0xf1}},
     {PLDM_BIOS, {0x00, 0xf0, 0xf0, 0xf1}},
     {PLDM_FRU, {0x00, 0xf0, 0xf0, 0xf1}},
+    {PLDM_FILE, {0x00, 0xf0, 0xf0, 0xf1}},
 #ifdef OEM_IBM
     {PLDM_OEM, {0x00, 0xf0, 0xf0, 0xf1}},
 #endif
 };
 
+static const std::vector<Type> multipartSupportedTypes{
+    PLDM_FILE,
+};
+
 namespace base
 {
 Response Handler::getPLDMTypes(const pldm_msg* request,
@@ -136,8 +150,11 @@
     uint32_t transferHandle;
     Type type;
     uint8_t transferFlag;
+    constexpr size_t checksumLen = sizeof(uint32_t);
 
-    Response response(sizeof(pldm_msg_hdr) + PLDM_GET_VERSION_RESP_BYTES, 0);
+    // Generate the response with 32bit checksum appended
+    Response response(
+        sizeof(pldm_msg_hdr) + PLDM_GET_VERSION_RESP_BYTES + checksumLen, 0);
     auto responsePtr = new (response.data()) pldm_msg;
 
     uint8_t rc = decode_get_version_req(request, payloadLength, &transferHandle,
@@ -161,6 +178,13 @@
     rc = encode_get_version_resp(request->hdr.instance_id, PLDM_SUCCESS, 0,
                                  PLDM_START_AND_END, &version,
                                  sizeof(pldm_version), responsePtr);
+
+    // Calculate the crc32
+    uint32_t checksum = pldm_edac_crc32(&version, sizeof(version));
+    checksum = htole32(checksum);
+    memcpy(response.data() + sizeof(pldm_msg_hdr) + PLDM_GET_VERSION_RESP_BYTES,
+           &checksum, checksumLen);
+
     if (rc != PLDM_SUCCESS)
     {
         return ccOnlyResponse(request, rc);
@@ -198,6 +222,95 @@
     return response;
 }
 
+Response Handler::getNegotiateTransferParam(
+    pldm_tid_t tid, const pldm_msg* request, size_t payloadLength)
+{
+    struct pldm_base_negotiate_transfer_params_resp resp_payload_struct = {};
+    struct pldm_base_negotiate_transfer_params_req decoded_req;
+    Response response(sizeof(pldm_msg_hdr) +
+                          PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_RESP_BYTES,
+                      0);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    int rc = decode_pldm_base_negotiate_transfer_params_req(
+        request, payloadLength, &decoded_req);
+    if (rc != 0)
+    {
+        return CmdHandler::ccOnlyResponse(request, PLDM_ERROR);
+    }
+
+    // Part size should be a power of 2 and greater than PLDM_MIN_PART_SIZE
+    if ((decoded_req.requester_part_size < PLDM_MIN_PART_SIZE) ||
+        !std::has_single_bit(decoded_req.requester_part_size))
+    {
+        return CmdHandler::ccOnlyResponse(request, PLDM_ERROR_INVALID_DATA);
+    }
+
+    // Only check the multipart types supported by the responder
+    for (const auto& type : multipartSupportedTypes)
+    {
+        if (type > PLDM_TYPES_MAX)
+        {
+            error("Invalid type '{TYPE}'", "TYPE", type);
+            return CmdHandler::ccOnlyResponse(request, PLDM_ERROR);
+        }
+
+        // In the decoded_req.requester_protocol_support array, pldm type is
+        // array_index * 8 + bit
+        auto index = type / 8;
+        auto bit = type % 8;
+
+        if (decoded_req.requester_protocol_support[index].byte & (1 << bit))
+        {
+            std::map<uint8_t, uint16_t>& type_map_for_tid =
+                negotiatedPartSizes[tid];
+            type_map_for_tid[type] =
+                std::min(decoded_req.requester_part_size,
+                         (uint16_t)PLDM_RESPONDER_PART_SIZE);
+
+            info(
+                "Multipart negotiated for tid:'{TID}' type:'{TYPE}' size:'{SIZE}'",
+                "TID", tid, "TYPE", type, "SIZE",
+                (uint16_t)type_map_for_tid[type]);
+        }
+
+        resp_payload_struct.responder_protocol_support[index].byte |=
+            static_cast<uint8_t>(1 << bit);
+    }
+
+    resp_payload_struct.completion_code = PLDM_SUCCESS;
+    resp_payload_struct.responder_part_size = PLDM_RESPONDER_PART_SIZE;
+
+    size_t respPayloadLength =
+        PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_RESP_BYTES;
+    rc = encode_pldm_base_negotiate_transfer_params_resp(
+        request->hdr.instance_id, &resp_payload_struct, responsePtr,
+        &respPayloadLength);
+    if (rc != 0)
+    {
+        return ccOnlyResponse(request, PLDM_ERROR);
+    }
+
+    return response;
+}
+
+uint16_t Handler::getNegotiatedPartSize(pldm_tid_t tid, uint8_t pldmType)
+{
+    auto tid_it = negotiatedPartSizes.find(tid);
+    if (tid_it == negotiatedPartSizes.end())
+    {
+        return PLDM_MIN_PART_SIZE;
+    }
+
+    auto type_it = tid_it->second.find(pldmType);
+    if (type_it == tid_it->second.end())
+    {
+        return PLDM_MIN_PART_SIZE;
+    }
+
+    return type_it->second;
+}
+
 } // namespace base
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/base.hpp b/libpldmresponder/base.hpp
index 3ed5c8f..98d1554 100644
--- a/libpldmresponder/base.hpp
+++ b/libpldmresponder/base.hpp
@@ -18,7 +18,10 @@
 namespace base
 {
 
+#define PLDM_TYPES_MAX 63
 #define PLDM_MIN_PART_SIZE 256
+// TODO: We can make the part size configurable for different platforms
+#define PLDM_RESPONDER_PART_SIZE 4096
 
 class Handler : public CmdHandler
 {
@@ -45,6 +48,12 @@
             [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) {
                 return this->getTID(request, payloadLength);
             });
+        handlers.emplace(PLDM_NEGOTIATE_TRANSFER_PARAMETERS,
+                         [this](pldm_tid_t tid, const pldm_msg* request,
+                                size_t payloadLength) {
+                             return this->getNegotiateTransferParam(
+                                 tid, request, payloadLength);
+                         });
     }
 
     /** @brief Handler for getPLDMTypes
@@ -87,6 +96,18 @@
      */
     Response getTID(const pldm_msg* request, size_t payloadLength);
 
+    /** @brief Handler for negotiate transfer parameters
+     *
+     * This will save the negotiated part size based on the TID
+     *
+     *  @param[in] tid - TID of the requester
+     *  @param[in] request - Request message payload
+     *  @param[in] payload_length - Request message payload length
+     *  @param[return] Response - PLDM Response message
+     */
+    Response getNegotiateTransferParam(pldm_tid_t tid, const pldm_msg* request,
+                                       size_t payloadLength);
+
     /* @brief Method to set the oem platform handler in base handler class
      *
      * @param[in] handler - oem platform handler
@@ -97,6 +118,17 @@
         oemPlatformHandler = handler;
     }
 
+    /* @brief Returns the negotiated part size for a given TID and PLDM type.
+     *
+     * If no negotiation has occurred for the given TID and type, it returns a
+     * default minimum part size.
+     *
+     * @param[in] tid - The Terminus ID.
+     * @param[in] pldmType - The PLDM type.
+     * @return The negotiated part size.
+     */
+    uint16_t getNegotiatedPartSize(pldm_tid_t tid, uint8_t pldmType);
+
   private:
     /** @brief reference of main event loop of pldmd, primarily used to schedule
      *  work
@@ -108,6 +140,10 @@
 
     /** @brief sdeventplus event source */
     std::unique_ptr<sdeventplus::source::Defer> survEvent;
+
+    /** Negotiated multi part size of each possible PLDM type for different
+     * devices. */
+    std::map<pldm_tid_t, std::map<uint8_t, uint16_t>> negotiatedPartSizes;
 };
 
 } // namespace base
diff --git a/libpldmresponder/file_transfer.cpp b/libpldmresponder/file_transfer.cpp
index 1dab5ae..2e3b866 100644
--- a/libpldmresponder/file_transfer.cpp
+++ b/libpldmresponder/file_transfer.cpp
@@ -86,9 +86,10 @@
     return 0;
 }
 
-Handler::Handler(responder::platform::Handler* platformHandler) :
-    platformHandler(platformHandler), nextFileDescriptor(0),
-    multipartSize(PLDM_MIN_PART_SIZE)
+Handler::Handler(responder::base::Handler* baseHandler,
+                 responder::platform::Handler* platformHandler) :
+    baseHandler(baseHandler), platformHandler(platformHandler),
+    nextFileDescriptor(0)
 {
     // Register command handlers. The "handlers" come from CmdHandler class.
     handlers.emplace(
@@ -105,16 +106,11 @@
 
     handlers.emplace(
         PLDM_FILE_CMD_DF_READ,
-        [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) {
-            return this->dfRead(request, payloadLength);
+        [this](pldm_tid_t tid, const pldm_msg* request, size_t payloadLength) {
+            return this->dfRead(tid, request, payloadLength);
         });
 }
 
-void Handler::setMultipartSize(uint16_t size)
-{
-    multipartSize = size;
-}
-
 std::optional<uint16_t> Handler::getFreeFileDescriptor(uint16_t fileIdentifier)
 {
     uint16_t startValue = this->nextFileDescriptor;
@@ -256,7 +252,7 @@
 
 uint32_t Handler::getDataLengthBytesAndTransferFlag(
     const FileDescriptor& fileDescriptor, uint32_t currOffset,
-    uint8_t* transferFlag)
+    uint16_t multipartSize, uint8_t* transferFlag)
 {
     // Verify that the current offset is greater than the starting offset of the
     // data sequence.
@@ -397,7 +393,7 @@
 
 Response Handler::dfReadTransferFirstPart(
     const pldm_msg* request, const pldm_base_multipart_receive_req& decodedReq,
-    FileDescriptor& fileDescriptor)
+    uint16_t multipartSize, FileDescriptor& fileDescriptor)
 {
     // For the first part, transfer handle should be 0
     if (decodedReq.transfer_handle != 0)
@@ -433,7 +429,8 @@
     // the corresponding transfer flag.
     uint8_t transfer_flag;
     uint32_t data_length_bytes = getDataLengthBytesAndTransferFlag(
-        fileDescriptor, fileDescriptor.startingOffset, &transfer_flag);
+        fileDescriptor, fileDescriptor.startingOffset, multipartSize,
+        &transfer_flag);
     if (data_length_bytes == 0)
     {
         // This can happen if the client is requesting data beyond initially
@@ -457,7 +454,7 @@
 
 Response Handler::dfReadTransferCurrentOrNextPart(
     const pldm_msg* request, const pldm_base_multipart_receive_req& decodedReq,
-    FileDescriptor& fileDescriptor)
+    uint16_t multipartSize, FileDescriptor& fileDescriptor)
 {
     // Validate transfer flag. This doesn't strictly check the transfer flag
     // with an expected value. Instead veryfying that it is within valid range.
@@ -477,7 +474,8 @@
     // the corresponding transfer flag.
     uint8_t transfer_flag;
     uint32_t data_length_bytes = getDataLengthBytesAndTransferFlag(
-        fileDescriptor, decodedReq.transfer_handle, &transfer_flag);
+        fileDescriptor, decodedReq.transfer_handle, multipartSize,
+        &transfer_flag);
     if (data_length_bytes == 0)
     {
         // This can happen if the client is requesting data beyond initially
@@ -501,12 +499,13 @@
 
 Response Handler::dfReadTransferCompleteOrAbort(
     const pldm_msg* request, const pldm_base_multipart_receive_req& decodedReq,
-    FileDescriptor& fileDescriptor)
+    uint16_t multipartSize, FileDescriptor& fileDescriptor)
 {
     // Check whether the requester wants to start a new connection
     if (decodedReq.section_length > 0 || decodedReq.section_offset > 0)
     {
-        return dfReadTransferFirstPart(request, decodedReq, fileDescriptor);
+        return dfReadTransferFirstPart(request, decodedReq, multipartSize,
+                                       fileDescriptor);
     }
     return createReadResponse(
         request, decodedReq, fileDescriptor, /*current_transfer_handle=*/0,
@@ -514,7 +513,8 @@
         /*next_transfer_handle*/ 0, /*data_length_bytes*/ 0);
 }
 
-Response Handler::dfRead(const pldm_msg* request, size_t payloadLength)
+Response Handler::dfRead(pldm_tid_t tid, const pldm_msg* request,
+                         size_t payloadLength)
 {
     uint8_t pldmType;
     uint8_t transferOpflag;
@@ -550,22 +550,25 @@
                                           PLDM_FILE_CC_INVALID_FILE_DESCRIPTOR);
     }
 
+    uint16_t multipartSize = baseHandler->getNegotiatedPartSize(tid, PLDM_FILE);
+
     switch (transferOpflag)
     {
         case PLDM_XFER_FIRST_PART:
-            return dfReadTransferFirstPart(request, decodedReq, it->second);
+            return dfReadTransferFirstPart(request, decodedReq, multipartSize,
+                                           it->second);
         case PLDM_XFER_NEXT_PART:
             return dfReadTransferCurrentOrNextPart(request, decodedReq,
-                                                   it->second);
+                                                   multipartSize, it->second);
         case PLDM_XFER_ABORT:
             return dfReadTransferCompleteOrAbort(request, decodedReq,
-                                                 it->second);
+                                                 multipartSize, it->second);
         case PLDM_XFER_COMPLETE:
             return dfReadTransferCompleteOrAbort(request, decodedReq,
-                                                 it->second);
+                                                 multipartSize, it->second);
         case PLDM_XFER_CURRENT_PART:
             return dfReadTransferCurrentOrNextPart(request, decodedReq,
-                                                   it->second);
+                                                   multipartSize, it->second);
         default:
             break;
     }
diff --git a/libpldmresponder/file_transfer.hpp b/libpldmresponder/file_transfer.hpp
index 92af1be..88f7500 100644
--- a/libpldmresponder/file_transfer.hpp
+++ b/libpldmresponder/file_transfer.hpp
@@ -46,12 +46,8 @@
 class Handler : public CmdHandler
 {
   public:
-    Handler(responder::platform::Handler* platformHandler);
-
-    /**
-     * Sets the multipart size used for DfRead commands
-     */
-    void setMultipartSize(uint16_t size);
+    Handler(responder::base::Handler* baseHandler,
+            responder::platform::Handler* platformHandler);
 
   private:
     /**
@@ -79,7 +75,7 @@
      */
     uint32_t getDataLengthBytesAndTransferFlag(
         const FileDescriptor& fileDescriptor, uint32_t currOffset,
-        uint8_t* transferFlag);
+        uint16_t multipartSize, uint8_t* transferFlag);
 
     /**
      * @brief Calculates the next offset for a DfRead response
@@ -102,7 +98,7 @@
     Response dfReadTransferFirstPart(
         const pldm_msg* request,
         const pldm_base_multipart_receive_req& decodedReq,
-        FileDescriptor& fileDescriptor);
+        uint16_t multipartSize, FileDescriptor& fileDescriptor);
 
     /**
      * @brief Handles the current or next part of a DfRead transfer
@@ -110,7 +106,7 @@
     Response dfReadTransferCurrentOrNextPart(
         const pldm_msg* request,
         const pldm_base_multipart_receive_req& decodedReq,
-        FileDescriptor& fileDescriptor);
+        uint16_t multipartSize, FileDescriptor& fileDescriptor);
 
     /**
      * @brief Handles the completion or abort of a DfRead transfer
@@ -118,12 +114,15 @@
     Response dfReadTransferCompleteOrAbort(
         const pldm_msg* request,
         const pldm_base_multipart_receive_req& decodedReq,
-        FileDescriptor& fileDescriptor);
+        uint16_t multipartSize, FileDescriptor& fileDescriptor);
 
     /**
      * @brief Handler for the DfRead command
      */
-    Response dfRead(const pldm_msg* request, size_t payloadLength);
+    Response dfRead(pldm_tid_t tid, const pldm_msg* request,
+                    size_t payloadLength);
+
+    responder::base::Handler* baseHandler;
 
     // PLDM Platform type handler. File pointers will be initialized during file
     // PDR descriptor handling. So This will allow the file transfer handler to
@@ -137,10 +136,6 @@
     // created. This is the simplest approach. We could change this later if we
     // need a better approach.
     uint16_t nextFileDescriptor;
-
-    // Transfer size for DfRead command. Should be set by the base type
-    // handlers. If not set, min part size will be used.
-    uint16_t multipartSize;
 };
 
 } // namespace file_transfer
diff --git a/libpldmresponder/test/libpldmresponder_base_test.cpp b/libpldmresponder/test/libpldmresponder_base_test.cpp
index f169dff..93a7c7d 100644
--- a/libpldmresponder/test/libpldmresponder_base_test.cpp
+++ b/libpldmresponder/test/libpldmresponder_base_test.cpp
@@ -4,11 +4,15 @@
 #include "test/test_instance_id.hpp"
 
 #include <libpldm/base.h>
+#include <libpldm/utils.h>
 
 #include <sdeventplus/event.hpp>
 
+#include <algorithm>
 #include <array>
 #include <cstring>
+#include <map>
+#include <vector>
 
 #include <gtest/gtest.h>
 
@@ -34,7 +38,7 @@
     auto responsePtr = new (response.data()) pldm_msg;
     uint8_t* payload_ptr = responsePtr->payload;
     ASSERT_EQ(payload_ptr[0], 0);
-    ASSERT_EQ(payload_ptr[1], 29); // 0b11101 see DSP0240 table11
+    ASSERT_EQ(payload_ptr[1], 157); // 0b10011101 see DSP0240 table11
     ASSERT_EQ(payload_ptr[2], 0);
 }
 
@@ -50,7 +54,7 @@
     auto responsePtr = new (response.data()) pldm_msg;
     uint8_t* payload_ptr = responsePtr->payload;
     ASSERT_EQ(payload_ptr[0], 0);
-    ASSERT_EQ(payload_ptr[1], 60); // 60 = 0b111100
+    ASSERT_EQ(payload_ptr[1], 188); // 188 = 0b10111100
     ASSERT_EQ(payload_ptr[2], 0);
 }
 
@@ -100,6 +104,13 @@
     ASSERT_EQ(0, memcmp(responsePtr->payload + sizeof(responsePtr->payload[0]) +
                             sizeof(transferHandle) + sizeof(flag),
                         &version, sizeof(version)));
+
+    uint32_t expectedChecksum = pldm_edac_crc32(&version, sizeof(version));
+    uint32_t responseChecksum = 0;
+    memcpy(&responseChecksum,
+           responsePtr->payload + PLDM_GET_VERSION_RESP_BYTES,
+           sizeof(responseChecksum));
+    ASSERT_EQ(expectedChecksum, le32toh(responseChecksum));
 }
 
 TEST_F(TestBaseCommands, testGetPLDMVersionBadRequest)
@@ -109,7 +120,7 @@
     auto request = new (requestPayload.data()) pldm_msg;
     size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr);
 
-    uint8_t pldmType = 7;
+    uint8_t pldmType = 6;
     uint32_t transferHandle = 0x0;
     uint8_t flag = PLDM_GET_FIRSTPART;
 
@@ -153,3 +164,194 @@
     ASSERT_EQ(payload[0], 0);
     ASSERT_EQ(payload[1], 1);
 }
+
+TEST_F(TestBaseCommands, testSetMultiPartSizeCallback)
+{
+    base::Handler handler(event);
+
+    // Prepare a NegotiateTransferParameters request
+    std::vector<uint8_t> requestMsg(
+        sizeof(pldm_msg_hdr) +
+        PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES);
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+
+    struct pldm_base_negotiate_transfer_params_req req{};
+    req.requester_part_size = 1024;
+    // PLDM_FILE is type 7. index = 7/8 = 0. bit = 7%8 = 7.
+    req.requester_protocol_support[0].byte |= (1 << 7);
+
+    EXPECT_EQ(encode_pldm_base_negotiate_transfer_params_req(
+                  /*instance_id=*/1, &req, request, &requestPayloadLength),
+              0);
+
+    // Call getNegotiateTransferParam to trigger the callback
+    requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+    auto response =
+        handler.getNegotiateTransferParam(1, request, requestPayloadLength);
+
+    // Verify the response
+    ASSERT_FALSE(response.empty());
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    struct pldm_base_negotiate_transfer_params_resp resp;
+    EXPECT_EQ(decode_pldm_base_negotiate_transfer_params_resp(
+                  responsePtr, response.size() - sizeof(pldm_msg_hdr), &resp),
+              0);
+
+    EXPECT_EQ(resp.completion_code, PLDM_SUCCESS);
+    EXPECT_EQ(resp.responder_part_size, PLDM_RESPONDER_PART_SIZE);
+    EXPECT_TRUE(resp.responder_protocol_support[0].byte & (1 << 7));
+}
+
+TEST_F(TestBaseCommands, testNegotiateTransferParamMultipleTIDs)
+{
+    base::Handler handler(event);
+
+    // Map of TIDs to their requested part sizes
+    std::map<pldm_tid_t, uint16_t> tidToPartSize = {
+        {10, 512},   // smaller than responder
+        {20, 8192},  // larger than responder
+        {30, 4096},  // equal to responder
+        {40, 1024}}; // smaller than responder
+
+    for (const auto& [tid, requester_part_size] : tidToPartSize)
+    {
+        // Prepare a NegotiateTransferParameters request
+        std::vector<uint8_t> requestMsg(
+            sizeof(pldm_msg_hdr) +
+            PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES);
+        auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+        size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+
+        // Fill in the request payload
+        auto payload =
+            reinterpret_cast<pldm_base_negotiate_transfer_params_req*>(
+                request->payload);
+        payload->requester_part_size = htole16(requester_part_size);
+
+        // Set the bit for PLDM_FILE_TRANSFER in requester_protocol_support
+        memset(payload->requester_protocol_support, 0,
+               sizeof(payload->requester_protocol_support));
+        // PLDM_FILE is type 7. index = 7/8 = 0. bit = 7%8 = 7.
+        payload->requester_protocol_support[0].byte |= (1 << 7);
+
+        // Call getNegotiateTransferParam
+        auto response = handler.getNegotiateTransferParam(tid, request,
+                                                          requestPayloadLength);
+
+        // Verify the response
+        ASSERT_FALSE(response.empty()) << "Failed for TID " << (int)tid;
+        auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+        struct pldm_base_negotiate_transfer_params_resp resp;
+        EXPECT_EQ(decode_pldm_base_negotiate_transfer_params_resp(
+                      responsePtr, response.size() - sizeof(pldm_msg_hdr),
+                      &resp),
+                  0);
+
+        EXPECT_EQ(resp.completion_code, PLDM_SUCCESS)
+            << "Failed for TID " << (int)tid;
+
+        // Verify the negotiated part size for this TID
+        uint16_t expectedNegotiatedSize =
+            std::min(requester_part_size, (uint16_t)PLDM_RESPONDER_PART_SIZE);
+        uint16_t negotiatedSize = handler.getNegotiatedPartSize(tid, PLDM_FILE);
+        EXPECT_EQ(negotiatedSize, expectedNegotiatedSize)
+            << "Failed for TID " << (int)tid;
+    }
+}
+
+TEST_F(TestBaseCommands, testNegotiateTransferParamInvalidLength)
+{
+    std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + 1);
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    size_t payloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+
+    base::Handler handler(event);
+    auto response =
+        handler.getNegotiateTransferParam(1, request, payloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    struct pldm_base_negotiate_transfer_params_resp resp;
+    EXPECT_EQ(decode_pldm_base_negotiate_transfer_params_resp(
+                  responsePtr, response.size() - sizeof(pldm_msg_hdr), &resp),
+              0);
+
+    EXPECT_EQ(resp.completion_code, PLDM_ERROR);
+}
+
+TEST_F(TestBaseCommands, testNegotiateTransferParamInvalidPartSizeZero)
+{
+    std::vector<uint8_t> requestMsg(
+        sizeof(pldm_msg_hdr) +
+        PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES);
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    size_t payloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+
+    auto payload = reinterpret_cast<pldm_base_negotiate_transfer_params_req*>(
+        request->payload);
+    payload->requester_part_size = 0;
+
+    base::Handler handler(event);
+    auto response =
+        handler.getNegotiateTransferParam(1, request, payloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    struct pldm_base_negotiate_transfer_params_resp resp;
+    EXPECT_EQ(decode_pldm_base_negotiate_transfer_params_resp(
+                  responsePtr, response.size() - sizeof(pldm_msg_hdr), &resp),
+              0);
+
+    EXPECT_EQ(resp.completion_code, PLDM_ERROR_INVALID_DATA);
+}
+
+TEST_F(TestBaseCommands, testNegotiateTransferParamInvalidPartSizeNotPowerOfTwo)
+{
+    std::vector<uint8_t> requestMsg(
+        sizeof(pldm_msg_hdr) +
+        PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES);
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    size_t payloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+
+    auto payload = reinterpret_cast<pldm_base_negotiate_transfer_params_req*>(
+        request->payload);
+    payload->requester_part_size = 3000;
+
+    base::Handler handler(event);
+    auto response =
+        handler.getNegotiateTransferParam(1, request, payloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    struct pldm_base_negotiate_transfer_params_resp resp;
+    EXPECT_EQ(decode_pldm_base_negotiate_transfer_params_resp(
+                  responsePtr, response.size() - sizeof(pldm_msg_hdr), &resp),
+              0);
+
+    EXPECT_EQ(resp.completion_code, PLDM_ERROR_INVALID_DATA);
+}
+
+TEST_F(TestBaseCommands, testNegotiateTransferParamGoodRequest)
+{
+    std::vector<uint8_t> requestMsg(
+        sizeof(pldm_msg_hdr) +
+        PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES);
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    size_t payloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+
+    auto payload = reinterpret_cast<pldm_base_negotiate_transfer_params_req*>(
+        request->payload);
+    payload->requester_part_size = htole16(4096);
+
+    base::Handler handler(event);
+    auto response =
+        handler.getNegotiateTransferParam(1, request, payloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    struct pldm_base_negotiate_transfer_params_resp resp;
+    EXPECT_EQ(decode_pldm_base_negotiate_transfer_params_resp(
+                  responsePtr, response.size() - sizeof(pldm_msg_hdr), &resp),
+              0);
+
+    EXPECT_EQ(resp.completion_code, PLDM_SUCCESS);
+    EXPECT_EQ(resp.responder_part_size, PLDM_RESPONDER_PART_SIZE);
+}
diff --git a/libpldmresponder/test/libpldmresponder_file_transfer_test.cpp b/libpldmresponder/test/libpldmresponder_file_transfer_test.cpp
index c3fc8f0..313f5d3 100644
--- a/libpldmresponder/test/libpldmresponder_file_transfer_test.cpp
+++ b/libpldmresponder/test/libpldmresponder_file_transfer_test.cpp
@@ -123,7 +123,7 @@
         pdrRepo(pldm_pdr_init(), pldm_pdr_destroy),
         platformHandler(nullptr, 0, nullptr, pdrDir, pdrRepo.get(), nullptr,
                         nullptr, nullptr, nullptr, nullptr, event, true),
-        handler(&platformHandler)
+        baseHandler(event), handler(&baseHandler, &platformHandler)
     {}
 
     void SetUp() override
@@ -189,6 +189,7 @@
     fs::path pdrDir;
     std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)> pdrRepo;
     pldm::responder::platform::Handler platformHandler;
+    pldm::responder::base::Handler baseHandler;
     pldm::responder::file_transfer::Handler handler;
     fs::path targetFileDir;
     fs::path targetFilePath;