| #include "common/instance_id.hpp" |
| #include "common/utils.hpp" |
| #include "libpldmresponder/base.hpp" |
| #include "test/test_instance_id.hpp" |
| |
| #include <libpldm/base.h> |
| #include <libpldm/edac.h> |
| |
| #include <sdeventplus/event.hpp> |
| |
| #include <algorithm> |
| #include <array> |
| #include <cstring> |
| #include <map> |
| #include <vector> |
| |
| #include <gtest/gtest.h> |
| |
| using namespace pldm::responder; |
| |
| class TestBaseCommands : public testing::Test |
| { |
| protected: |
| TestBaseCommands() : event(sdeventplus::Event::get_default()) {} |
| |
| sdeventplus::Event event; |
| }; |
| |
| TEST_F(TestBaseCommands, testPLDMTypesGoodRequest) |
| { |
| std::array<uint8_t, sizeof(pldm_msg_hdr)> requestPayload{}; |
| auto request = new (requestPayload.data()) pldm_msg; |
| // payload length will be 0 in this case |
| size_t requestPayloadLength = 0; |
| base::Handler handler(event); |
| auto response = handler.getPLDMTypes(request, requestPayloadLength); |
| // Need to support OEM type. |
| auto responsePtr = new (response.data()) pldm_msg; |
| uint8_t* payload_ptr = responsePtr->payload; |
| ASSERT_EQ(payload_ptr[0], 0); |
| ASSERT_EQ(payload_ptr[1], 157); // 0b10011101 see DSP0240 table11 |
| ASSERT_EQ(payload_ptr[2], 0); |
| } |
| |
| TEST_F(TestBaseCommands, testGetPLDMCommandsGoodRequest) |
| { |
| // Need to support OEM type commands. |
| std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_COMMANDS_REQ_BYTES> |
| requestPayload{}; |
| auto request = new (requestPayload.data()) pldm_msg; |
| size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr); |
| base::Handler handler(event); |
| auto response = handler.getPLDMCommands(request, requestPayloadLength); |
| auto responsePtr = new (response.data()) pldm_msg; |
| uint8_t* payload_ptr = responsePtr->payload; |
| ASSERT_EQ(payload_ptr[0], 0); |
| ASSERT_EQ(payload_ptr[1], 188); // 188 = 0b10111100 |
| ASSERT_EQ(payload_ptr[2], 2); // 2 = 0b00000010 |
| } |
| |
| TEST_F(TestBaseCommands, testGetPLDMCommandsBadRequest) |
| { |
| std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_COMMANDS_REQ_BYTES> |
| requestPayload{}; |
| auto request = new (requestPayload.data()) pldm_msg; |
| |
| request->payload[0] = 0xFF; |
| size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr); |
| base::Handler handler(event); |
| auto response = handler.getPLDMCommands(request, requestPayloadLength); |
| auto responsePtr = new (response.data()) pldm_msg; |
| uint8_t* payload_ptr = responsePtr->payload; |
| ASSERT_EQ(payload_ptr[0], PLDM_ERROR_INVALID_PLDM_TYPE); |
| } |
| |
| TEST_F(TestBaseCommands, testGetPLDMVersionGoodRequest) |
| { |
| std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_VERSION_REQ_BYTES> |
| requestPayload{}; |
| auto request = new (requestPayload.data()) pldm_msg; |
| size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr); |
| |
| uint8_t pldmType = PLDM_BASE; |
| uint32_t transferHandle = 0x0; |
| uint8_t flag = PLDM_GET_FIRSTPART; |
| uint8_t retFlag = PLDM_START_AND_END; |
| ver32_t version = {0x00, 0xF0, 0xF0, 0xF1}; |
| |
| auto rc = |
| encode_get_version_req(0, transferHandle, flag, pldmType, request); |
| |
| ASSERT_EQ(0, rc); |
| |
| base::Handler handler(event); |
| auto response = handler.getPLDMVersion(request, requestPayloadLength); |
| auto responsePtr = new (response.data()) pldm_msg; |
| |
| ASSERT_EQ(responsePtr->payload[0], 0); |
| ASSERT_EQ(0, memcmp(responsePtr->payload + sizeof(responsePtr->payload[0]), |
| &transferHandle, sizeof(transferHandle))); |
| ASSERT_EQ(0, memcmp(responsePtr->payload + sizeof(responsePtr->payload[0]) + |
| sizeof(transferHandle), |
| &retFlag, sizeof(flag))); |
| 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) |
| { |
| std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_VERSION_REQ_BYTES> |
| requestPayload{}; |
| auto request = new (requestPayload.data()) pldm_msg; |
| size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr); |
| |
| uint8_t pldmType = 6; |
| uint32_t transferHandle = 0x0; |
| uint8_t flag = PLDM_GET_FIRSTPART; |
| |
| auto rc = |
| encode_get_version_req(0, transferHandle, flag, pldmType, request); |
| |
| ASSERT_EQ(0, rc); |
| |
| base::Handler handler(event); |
| auto response = handler.getPLDMVersion(request, requestPayloadLength - 1); |
| auto responsePtr = new (response.data()) pldm_msg; |
| |
| ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH); |
| |
| request = new (requestPayload.data()) pldm_msg; |
| requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr); |
| |
| rc = encode_get_version_req(0, transferHandle, flag, pldmType, request); |
| |
| ASSERT_EQ(0, rc); |
| |
| response = handler.getPLDMVersion(request, requestPayloadLength); |
| responsePtr = new (response.data()) pldm_msg; |
| |
| ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_PLDM_TYPE); |
| } |
| |
| TEST_F(TestBaseCommands, testGetTIDGoodRequest) |
| { |
| std::array<uint8_t, sizeof(pldm_msg_hdr)> requestPayload{}; |
| auto request = new (requestPayload.data()) pldm_msg; |
| size_t requestPayloadLength = 0; |
| |
| base::Handler handler(event); |
| handler.setOemPlatformHandler(nullptr); |
| auto response = handler.getTID(request, requestPayloadLength); |
| |
| auto responsePtr = new (response.data()) pldm_msg; |
| uint8_t* payload = responsePtr->payload; |
| |
| 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 |
| struct pldm_base_negotiate_transfer_params_req req{}; |
| req.requester_part_size = requester_part_size; |
| // 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 |
| 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); |
| |
| struct pldm_base_negotiate_transfer_params_req req{}; |
| req.requester_part_size = 0; |
| EXPECT_EQ(encode_pldm_base_negotiate_transfer_params_req(1, &req, request, |
| &payloadLength), |
| 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); |
| |
| struct pldm_base_negotiate_transfer_params_req req{}; |
| req.requester_part_size = 3000; |
| EXPECT_EQ(encode_pldm_base_negotiate_transfer_params_req(1, &req, request, |
| &payloadLength), |
| 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, 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); |
| |
| struct pldm_base_negotiate_transfer_params_req req{}; |
| req.requester_part_size = 4096; |
| EXPECT_EQ(encode_pldm_base_negotiate_transfer_params_req(1, &req, request, |
| &payloadLength), |
| 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_SUCCESS); |
| EXPECT_EQ(resp.responder_part_size, PLDM_RESPONDER_PART_SIZE); |
| } |