blob: 8d2bbbe52eea9d17c5013e846ca002c0e95925cd [file]
#include "libbej/bej_dictionary.h"
#include "libpldm/base.h"
#include "libpldm/pldm.h"
#include "libpldm/pldm_rde.h"
#include "libpldm/requester/pldm_rde_requester.h"
#include "libpldm/utils.h"
#include "helper/common.hpp"
#include "helper/discovery/base_discovery.hpp"
#include "helper/discovery/rde_discovery.hpp"
#include "json.hpp"
#include "libbej/bej_decoder_json.hpp"
#include "libbej/bej_encoder_json.hpp"
#include "rde_update_utils.hpp"
#include <chrono>
#include <cstring>
#include <ctime>
#include <iostream>
#include <map>
#include <tuple>
#include <vector>
using json = nlohmann::json;
constexpr int maxOperationEnumerateStructSize =
(PLDM_MAX_REQUEST_BYTES - PLDM_RDE_OPERATION_ENUMERATE_RESP_HDR_SIZE) /
sizeof(struct pldm_rde_operation_enumerate_operation_data);
std::map<uint8_t, std::vector<uint8_t>> responseRequestMap;
std::map<uint8_t, int> rdeOpCommandRequestSize = {
{PLDM_RDE_OPERATION_INIT, 18},
{PLDM_SUPPLY_CUSTOM_REQUEST_PARAMETERS, 46},
{PLDM_RDE_OPERATION_STATUS, 18},
{PLDM_RDE_OPERATION_COMPLETE, 6},
{PLDM_RDE_MULTIPART_RECEIVE, 7},
{PLDM_RDE_OPERATION_KILL, 7}};
int cleanupRequestId(uint8_t requestId, struct pldm_rde_requester_context* ctx)
{
auto it = responseRequestMap.find(requestId);
if (it != responseRequestMap.end())
{
responseRequestMap.erase(requestId);
}
free_rde_op_init_context(ctx);
return 0;
}
bool extractExpandParameters(const std::string& url, std::string& expand,
int& levels)
{
size_t expandPos = url.find("$expand=");
if (expandPos == std::string::npos)
{
return false;
}
expandPos += 8; // Move past "$expand="
size_t parenStart = url.find('(', expandPos);
// Extract the expand parameter
if (parenStart != std::string::npos)
{
expand = url.substr(expandPos, parenStart - expandPos);
}
else
{
expand = url.substr(expandPos);
}
levels = 1;
// Extract the levels parameter if present
if (parenStart != std::string::npos)
{
size_t levelsPos = url.find("$levels=", parenStart);
if (levelsPos != std::string::npos)
{
levelsPos += 8; // Move past "$levels="
levels = stoi(url.substr(levelsPos)); // Convert to integer
}
}
return true;
}
static int processOperationEnumerate(
int fd, uint8_t eid, int instanceId,
struct pldm_rde_requester_manager* manager, uint16_t* operationCount,
struct pldm_rde_operation_enumerate_operation_data* operationData,
uint16_t operationDataArraySize)
{
// Op Enumerate does not have any request bytes
std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr));
auto request = reinterpret_cast<struct pldm_msg*>(requestMsg.data());
std::cerr << "Sending operation enumerate request" << std::endl;
int rc = 0;
rc = get_pldm_rde_operation_enumerate_request(instanceId, request);
if (rc != PLDM_RDE_REQUESTER_SUCCESS)
{
std::cerr << "Failed to get encoded rde operation enumerate request"
<< std::endl;
return rc;
}
int i = 0;
while (i < MAX_RETRIES_MCTP_SOCK_FAILURE)
{
rc = pldm_send_at_network(eid, manager->net_id, fd, requestMsg.data(),
requestMsg.size());
if (rc != PLDM_SUCCESS)
{
std::cerr
<< "Message failed with rc : " << rc
<< " while sending Op Enumerate request on network id in op kill/complete˝: "
<< std::to_string(manager->net_id) << " with retry: " << i
<< std::endl;
}
else
{
break;
}
i++;
if (i == MAX_RETRIES_MCTP_SOCK_FAILURE)
{
std::cerr << "Op enumerate send failed with all retries"
<< std::endl;
return rc;
}
}
const size_t responsePayloadLength = sizeof(pldm_msg_hdr) +
PLDM_MAX_REQUEST_BYTES;
std::vector<uint8_t> responseMsg(responsePayloadLength);
size_t responseMsgSize = responsePayloadLength;
uint8_t* responseDataPtr = responseMsg.data();
rc = pldm_recv_at_network(eid, fd, instanceId, &responseDataPtr,
&responseMsgSize, manager->net_id);
if (rc != PLDM_SUCCESS)
{
std::cerr
<< "Message failed with rc : " << rc
<< " while receiving OpEnumerate response in the workaround on network id: "
<< std::to_string(manager->net_id) << "\n";
return rc;
}
auto response = reinterpret_cast<struct pldm_msg*>(responseMsg.data());
uint8_t completion_code = 0;
pldm_rde_requester_rc_t ret = get_pldm_rde_operation_enumerate_response(
response, &completion_code, operationCount, operationData,
operationDataArraySize);
if (ret != PLDM_RDE_REQUESTER_SUCCESS)
{
std::cerr << "Failed to decode rde operation enumerate response"
<< std::endl;
return PLDM_ERROR;
}
if (completion_code != PLDM_SUCCESS)
{
std::cerr << "Rde operation enumerate request failed" << std::endl;
return PLDM_ERROR;
}
return 0;
}
void processOperationCompleteOrKill(int fd, uint8_t eid, int instanceId,
struct pldm_rde_requester_manager* manager,
struct pldm_rde_requester_context* ctx,
uint32_t resource_id, uint16_t operation_id,
uint8_t operation)
{
int requestBytes;
if (rdeOpCommandRequestSize.find(operation) !=
rdeOpCommandRequestSize.end())
{
requestBytes = rdeOpCommandRequestSize[operation];
}
else
{
requestBytes = PLDM_MAX_REQUEST_BYTES;
}
std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + requestBytes);
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
if (operation == PLDM_RDE_OPERATION_KILL)
{
std::cerr << "Sending operation kill request " << std::endl;
std::cerr << "with request bytes: " << requestBytes << std::endl;
get_pldm_rde_operation_kill_request(instanceId, resource_id,
operation_id, request);
}
else if (operation == PLDM_RDE_OPERATION_COMPLETE)
{
std::cerr << "Sending operation complete request" << std::endl;
std::cerr << "with request bytes: " << requestBytes << std::endl;
get_pldm_rde_operation_complete_request(instanceId, resource_id,
operation_id, request);
}
else
{
std::cerr << "Wrong number of bytes in workaround!" << std::endl;
}
int rc = ctx->context_status = CONTEXT_BUSY;
ctx->requester_status = PLDM_RDE_REQUESTER_WAITING_FOR_RESPONSE;
int i = 0;
while (i < MAX_RETRIES_MCTP_SOCK_FAILURE)
{
rc = pldm_send_at_network(eid, manager->net_id, fd, requestMsg.data(),
requestMsg.size());
if (rc)
{
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
std::cerr
<< "Message failed while sending on network id in op kill/complete˝: "
<< std::to_string(manager->net_id) << " with retry: " << i
<< std::endl;
}
else
{
break;
}
i++;
if (i == MAX_RETRIES_MCTP_SOCK_FAILURE)
{
std::cerr << "Op Kill send failed with all retries" << std::endl;
return;
}
}
std::vector<uint8_t> response(sizeof(pldm_msg_hdr) +
PLDM_MAX_REQUEST_BYTES);
uint8_t* responseMsg = response.data();
size_t responseMsgSize = sizeof(pldm_msg_hdr) + PLDM_MAX_REQUEST_BYTES;
// auto responsePtr = reinterpret_cast<struct pldm_msg*>(responseMsg);
rc = pldm_recv_at_network(eid, fd, instanceId, &responseMsg,
&responseMsgSize, manager->net_id);
if (rc)
{
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
std::cerr
<< "Message failed while receiving in the workaround on network id: "
<< std::to_string(manager->net_id) << "\n";
}
std::cerr << "Operation request sent/received successfully" << std::endl;
}
void processWorkaroundForFailures(int fd, uint8_t eid, int instanceId,
struct pldm_rde_requester_manager* manager,
struct pldm_rde_requester_context* ctx)
{
// operation Kill with context get_rde_op_kill_request()
// Get bytes for operation complete
std::cerr << "Processing from workaround!" << std::endl;
uint16_t operationCount = 0;
// Calculate the size of struct operationData[] array, based on
// the max available PLDM_MAX_REQUEST_BYTES bytes
struct pldm_rde_operation_enumerate_operation_data
operationData[maxOperationEnumerateStructSize];
// Get the list of active operations in RDE device
int rc = processOperationEnumerate(fd, eid, instanceId, manager,
&operationCount, operationData,
maxOperationEnumerateStructSize);
if (rc != 0)
{
ctx->next_command = PLDM_RDE_REQUESTER_NO_NEXT_COMMAND_FOUND;
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
return;
}
// If RDE device can serve multiple parallel operations at a time,
// then it is not possible to identify which operation got stuck.
// So kill all the ongoing operations.
for (int i = 0; i < operationCount; i++)
{
std::cerr << "Sending operation kill and complete request "
<< "for resource id: " << operationData[i].resource_id
<< " and operation id: " << operationData[i].operation_id
<< std::endl;
processOperationCompleteOrKill(
fd, eid, instanceId, manager, ctx, operationData[i].resource_id,
operationData[i].operation_id, PLDM_RDE_OPERATION_KILL);
processOperationCompleteOrKill(
fd, eid, instanceId, manager, ctx, operationData[i].resource_id,
operationData[i].operation_id, PLDM_RDE_OPERATION_COMPLETE);
}
ctx->next_command = PLDM_RDE_REQUESTER_NO_NEXT_COMMAND_FOUND;
ctx->context_status = CONTEXT_FREE;
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
std::cerr << "Workaround complete~ mBMC should be unstuck!" << std::endl;
return;
}
int getResponseForRequestId(uint8_t requestId, uint8_t** payload,
uint32_t* length)
{
auto it = responseRequestMap.find(requestId);
if (it != responseRequestMap.end())
{
*payload = &it->second.front();
*length = it->second.size();
return 0;
}
std::cerr << "Request id not found\n";
return 1;
}
int verifyChecksumForMultipartRecv2(const std::vector<uint8_t>& payload)
{
uint32_t payloadLength = payload.size();
auto calculatedChecksum = crc32(&(payload.front()), payloadLength - 4);
uint8_t byte0 = payload.at(payloadLength - 4);
uint8_t byte1 = payload.at(payloadLength - 3);
uint8_t byte2 = payload.at(payloadLength - 2);
uint8_t byte3 = payload.at(payloadLength - 1);
uint32_t checksumReceived = byte0 | byte1 << 8 | byte2 << 16 | byte3 << 24;
if (calculatedChecksum == checksumReceived)
{
if (DEBUG)
{
std::cerr << "For RDE Op: Checksum Verified, Calculated Checksum: "
<< std::to_string(calculatedChecksum)
<< " , Received Checksum: "
<< std::to_string(checksumReceived) << std::endl;
}
return PLDM_RDE_REQUESTER_SUCCESS;
}
else
{
std::cerr
<< "Checksum mismatch for request id/resource id in multipart receive"
<< std::endl;
return PLDM_RDE_REQUESTER_RECV_FAIL;
}
return PLDM_RDE_REQUESTER_RECV_FAIL;
}
void readRequestCallback(struct pldm_rde_requester_manager* manager,
struct pldm_rde_requester_context* ctx,
/*payload_array*/ uint8_t** payload,
/*payloadLength*/ uint32_t payloadLength,
bool hasChecksum)
{
if (DEBUG)
{
std::cerr << "Processing resp callback for netid: " << manager->net_id
<< std::endl;
}
struct rde_operation* operationCtx =
(struct rde_operation*)ctx->operation_ctx;
uint8_t requestId = operationCtx->request_id;
auto it = responseRequestMap.find(requestId);
std::vector<uint8_t>* collectedPayload;
if (it != responseRequestMap.end())
{
collectedPayload = &(it->second);
collectedPayload->insert(it->second.end(), *payload,
(*payload) + payloadLength);
}
else
{
responseRequestMap[requestId] =
std::vector<uint8_t>(*payload, *payload + payloadLength);
collectedPayload = &(responseRequestMap[requestId]);
}
if (DEBUG)
{
std::cerr << "Payload Length inside creating map: "
<< std::to_string(payloadLength) << "\n";
}
if (hasChecksum)
{
int rc = verifyChecksumForMultipartRecv2(*collectedPayload);
if (rc)
{
std::cerr << "Checksum failed for request id: " +
std::to_string(requestId);
}
else
{
if (DEBUG)
{
std::cerr << "Checksum verifies for request id: "
<< std::to_string(requestId) << "\n";
}
// Remove the checksum from the payload
(*collectedPayload)
.erase((*collectedPayload).begin() +
((*collectedPayload).size() - 4),
(*collectedPayload).end());
}
}
}
bool extractAction(const std::string& uri, std::string& resourceUri,
std::string& action)
{
std::string actionString = "/Actions";
size_t index;
if ((index = uri.find(actionString)) != std::string::npos)
{
resourceUri = uri.substr(0, index);
size_t resourceIndex = resourceUri.rfind("/");
if (resourceIndex != std::string::npos)
{
action = uri.substr(index + 9, uri.length());
}
return 0;
}
return index;
}
// Resolve operation locator dynamically based on the action path
int createOperationLocator(const std::string& actionPath,
std::vector<uint8_t>* operationLocator,
uint8_t** schemaDict, uint16_t* finalOffset,
bool isAction = true)
{
int rc;
std::vector<uint8_t> temp;
uint16_t rootOffset = bejDictGetPropertyHeadOffset();
uint16_t currentOffset;
const struct BejDictionaryProperty* property = nullptr;
// 1. Find "Drive" (root of Drive resource)
rc = bejDictGetPropertyByName(*schemaDict, rootOffset, "Drive", &property,
&currentOffset);
if (rc)
{
std::cerr << "Failed to find dictionary sequence for Drive\n";
return rc;
}
temp.emplace_back(property->sequenceNumber);
// 2. Find "Actions"
rootOffset = property->childPointerOffset;
rc = bejDictGetPropertyByName(*schemaDict, rootOffset, "Actions", &property,
&currentOffset);
if (rc)
{
std::cerr << "Failed to find dictionary sequence for Actions\n";
return rc;
}
temp.emplace_back(property->sequenceNumber);
// 3. Resolve action path components recursively (e.g.
// "Oem/GoogleDrive.Unlock" -> ["Oem", "#GoogleDrive.Unlock"])
rootOffset = property->childPointerOffset;
if (!actionPath.empty())
{
std::vector<std::string> components;
size_t start = 0;
size_t end = actionPath.find('/');
while (end != std::string::npos)
{
components.push_back(actionPath.substr(start, end - start));
start = end + 1;
end = actionPath.find('/', start);
}
components.push_back(actionPath.substr(start));
for (size_t i = 0; i < components.size(); ++i)
{
std::string component = components[i];
if (isAction && (i == components.size() - 1))
{
// Prepend '#' to the last component if it doesn't have it
if (component.empty() || component[0] != '#')
{
component = "#" + component;
}
}
rc = bejDictGetPropertyByName(*schemaDict, rootOffset,
component.c_str(), &property,
&currentOffset);
if (rc)
{
std::cerr
<< "Failed to find dictionary sequence for action component: "
<< component << "\n";
return rc;
}
temp.emplace_back(property->sequenceNumber);
rootOffset = property->childPointerOffset;
}
}
*finalOffset = currentOffset;
for (uint8_t sequenceNum : temp)
{
uint16_t byte0 = sequenceNum << 1;
uint8_t byte1 = static_cast<uint8_t>(byte0 >> 8);
uint8_t byte2 = static_cast<uint8_t>(byte0 & 0xFF);
if (byte1 != 0)
{
operationLocator->emplace_back(0x2);
operationLocator->emplace_back(byte1);
operationLocator->emplace_back(byte2);
}
else
{
operationLocator->emplace_back(0x1);
operationLocator->emplace_back(byte2);
}
}
int size = operationLocator->size();
operationLocator->insert(operationLocator->begin(), size);
operationLocator->insert(operationLocator->begin(), 0x1);
return 0;
}
int encodePayload(const std::string& requestPayload, const std::string& action,
uint16_t offset, uint8_t** schemaDict,
uint8_t** annotationDict,
std::vector<uint8_t>& encodedPayload)
{
return encodePayloadForActionOp(requestPayload, action, offset, schemaDict,
annotationDict, encodedPayload);
}
int processRdeOperationInit(int fd, uint8_t eid, int instanceId,
struct pldm_rde_requester_manager* manager,
struct pldm_rde_requester_context* ctx,
const std::vector<uint8_t>& requestMsg)
{
auto start = std::chrono::system_clock::now();
std::time_t start_time = std::chrono::system_clock::to_time_t(start);
int rc;
ctx->context_status = CONTEXT_BUSY;
ctx->requester_status = PLDM_RDE_REQUESTER_WAITING_FOR_RESPONSE;
if (DEBUG)
{
std::cerr << "Sending on network for op init: "
<< std::to_string(manager->net_id) << "\n";
}
int i = 0;
while (i < MAX_RETRIES_MCTP_SOCK_FAILURE)
{
rc = pldm_send_at_network(eid, manager->net_id, fd, requestMsg.data(),
requestMsg.size());
if (rc)
{
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
std::cerr
<< "Message failed while sending for op init on network id: "
<< std::to_string(manager->net_id) << "with retry: " << i
<< "\n";
}
else
{
break;
}
i++;
if (i == MAX_RETRIES_MCTP_SOCK_FAILURE)
{
return PLDM_RDE_REQUESTER_SEND_FAIL;
}
}
std::vector<uint8_t> response(sizeof(pldm_msg_hdr) +
PLDM_MAX_REQUEST_BYTES);
uint8_t* responseMsg = response.data();
size_t responseMsgSize = sizeof(pldm_msg_hdr) + PLDM_MAX_REQUEST_BYTES;
auto responsePtr = reinterpret_cast<struct pldm_msg*>(responseMsg);
if (DEBUG)
{
std::cerr << "Received response from network id for op init: "
<< std::to_string(manager->net_id) << "\n";
}
rc = pldm_recv_at_network(eid, fd, instanceId, &responseMsg,
&responseMsgSize, manager->net_id);
if (rc)
{
// Add workaround -
processWorkaroundForFailures(fd, eid, instanceId, manager, ctx);
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
std::cerr
<< "Message failed while receiving OpInit response on network id: "
<< std::to_string(manager->net_id) << "\n";
return PLDM_RDE_REQUESTER_RECV_FAIL;
}
auto end = std::chrono::system_clock::now();
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
std::chrono::duration<double> elapsed_seconds = end - start;
if (DEBUG)
{
std::cout << "Started RDE Operation Init at: "
<< std::ctime(&start_time) << "\nRDE Operation Init at "
<< std::ctime(&end_time)
<< "\nTotal time: " << elapsed_seconds.count() << "s"
<< std::endl;
}
rc = pldm_rde_push_read_operation_response(
manager, ctx, responsePtr, responseMsgSize, &readRequestCallback);
if (rc)
{
std::cout << "Failed response msg with size in op init: "
<< std::to_string(responseMsgSize) << std::endl;
processWorkaroundForFailures(fd, eid, instanceId, manager, ctx);
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
std::cerr << "Error in pushing response and updating context with rc: "
<< rc << std::endl;
return rc;
}
return PLDM_RDE_REQUESTER_SUCCESS;
}
int processSupplyCustomRequestParameters(
int fd, uint8_t eid, int instanceId,
struct pldm_rde_requester_manager* manager,
struct pldm_rde_requester_context* ctx,
const std::vector<uint8_t>& requestMsg)
{
auto start = std::chrono::system_clock::now();
std::time_t start_time = std::chrono::system_clock::to_time_t(start);
int rc;
ctx->context_status = CONTEXT_BUSY;
ctx->requester_status = PLDM_RDE_REQUESTER_WAITING_FOR_RESPONSE;
if (DEBUG)
{
std::cerr << "Sending on network for op init: "
<< std::to_string(manager->net_id) << "\n";
}
int i = 0;
while (i < MAX_RETRIES_MCTP_SOCK_FAILURE)
{
rc = pldm_send_at_network(eid, manager->net_id, fd, requestMsg.data(),
requestMsg.size());
if (rc)
{
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
std::cerr
<< "Message failed while sending for op init on network id: "
<< std::to_string(manager->net_id) << "with retry: " << i
<< "\n";
}
else
{
break;
}
i++;
if (i == MAX_RETRIES_MCTP_SOCK_FAILURE)
{
return PLDM_RDE_REQUESTER_SEND_FAIL;
}
}
std::vector<uint8_t> response(sizeof(pldm_msg_hdr) +
PLDM_MAX_REQUEST_BYTES);
uint8_t* responseMsg = response.data();
size_t responseMsgSize = sizeof(pldm_msg_hdr) + PLDM_MAX_REQUEST_BYTES;
auto responsePtr = reinterpret_cast<struct pldm_msg*>(responseMsg);
if (DEBUG)
{
std::cerr << "Received response from network id for op init: "
<< std::to_string(manager->net_id) << "\n";
}
rc = pldm_recv_at_network(eid, fd, instanceId, &responseMsg,
&responseMsgSize, manager->net_id);
if (rc)
{
// Add workaround -
processWorkaroundForFailures(fd, eid, instanceId, manager, ctx);
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
std::cerr << "Message failed while receiving on network id: "
<< std::to_string(manager->net_id) << "\n";
return PLDM_RDE_REQUESTER_RECV_FAIL;
}
auto end = std::chrono::system_clock::now();
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
std::chrono::duration<double> elapsed_seconds = end - start;
if (DEBUG)
{
std::cout << "Started processSupplyCustomRequestParameters at: "
<< std::ctime(&start_time)
<< "\nprocessSupplyCustomRequestParameters "
<< std::ctime(&end_time)
<< "\nTotal time: " << elapsed_seconds.count() << "s"
<< std::endl;
}
rc = pldm_rde_push_read_operation_response(
manager, ctx, responsePtr, responseMsgSize, &readRequestCallback);
if (rc)
{
std::cout << "Failed response msg with size in op init: "
<< std::to_string(responseMsgSize) << std::endl;
processWorkaroundForFailures(fd, eid, instanceId, manager, ctx);
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
std::cerr << "Error in pushing response and updating context with rc: "
<< rc << std::endl;
return rc;
}
return PLDM_RDE_REQUESTER_SUCCESS;
}
int processRdeOperationComplete(int fd, uint8_t eid, int instanceId,
struct pldm_rde_requester_manager* manager,
struct pldm_rde_requester_context* ctx,
const std::vector<uint8_t>& requestMsg)
{
int rc;
ctx->context_status = CONTEXT_BUSY;
ctx->requester_status = PLDM_RDE_REQUESTER_WAITING_FOR_RESPONSE;
auto start = std::chrono::system_clock::now();
std::time_t start_time = std::chrono::system_clock::to_time_t(start);
if (DEBUG)
{
std::cerr << "Sending complete op on network: "
<< std::to_string(manager->net_id) << "\n";
}
int i = 0;
while (i < MAX_RETRIES_MCTP_SOCK_FAILURE)
{
rc = pldm_send_at_network(eid, manager->net_id, fd, requestMsg.data(),
requestMsg.size());
if (rc)
{
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
std::cerr << "Message failed while sending on op comp. network id: "
<< std::to_string(manager->net_id) << "with retry: " << i
<< "\n";
}
else
{
break;
}
i++;
if (i == MAX_RETRIES_MCTP_SOCK_FAILURE)
{
return PLDM_RDE_REQUESTER_SEND_FAIL;
}
}
std::vector<uint8_t> response(sizeof(pldm_msg_hdr) +
PLDM_MAX_REQUEST_BYTES);
uint8_t* responseMsg = response.data();
size_t responseMsgSize = sizeof(pldm_msg_hdr) + PLDM_MAX_REQUEST_BYTES;
auto responsePtr = reinterpret_cast<struct pldm_msg*>(responseMsg);
if (DEBUG)
{
std::cerr << "Receiving complete op on network: "
<< std::to_string(manager->net_id) << "\n";
}
rc = pldm_recv_at_network(eid, fd, instanceId, &responseMsg,
&responseMsgSize, manager->net_id);
if (rc)
{
// Add workaround
processWorkaroundForFailures(fd, eid, instanceId, manager, ctx);
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
std::cerr
<< "Message failed while receiving OpComplete response on network id: "
<< std::to_string(manager->net_id) << "\n";
return PLDM_RDE_REQUESTER_RECV_FAIL;
}
auto end = std::chrono::system_clock::now();
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
std::chrono::duration<double> elapsed_seconds = end - start;
if (DEBUG)
{
std::cout << "Started processRdeOperationComplete Complete at: "
<< std::ctime(&start_time)
<< "\nprocessRdeOperationComplete at "
<< std::ctime(&end_time)
<< "\nTotal time for complete: " << elapsed_seconds.count()
<< "s" << std::endl;
}
rc = pldm_rde_push_read_operation_response(manager, ctx, responsePtr,
responseMsgSize, NULL);
if (rc)
{
std::cerr << "Error in pushing response and updating context\n";
processWorkaroundForFailures(fd, eid, instanceId, manager, ctx);
return rc;
}
return PLDM_RDE_REQUESTER_SUCCESS;
}
int processRdeOpMultipartReceive(int fd, uint8_t eid, int instanceId,
struct pldm_rde_requester_manager* manager,
struct pldm_rde_requester_context* ctx,
const std::vector<uint8_t>& requestMsg)
{
auto start = std::chrono::system_clock::now();
std::time_t start_time = std::chrono::system_clock::to_time_t(start);
int rc;
ctx->context_status = CONTEXT_BUSY;
ctx->requester_status = PLDM_RDE_REQUESTER_WAITING_FOR_RESPONSE;
if (DEBUG)
{
std::cerr << "Sending multipart on network: "
<< std::to_string(manager->net_id) << "\n";
}
int i = 0;
while (i < MAX_RETRIES_MCTP_SOCK_FAILURE)
{
rc = pldm_send_at_network(eid, manager->net_id, fd, requestMsg.data(),
requestMsg.size());
if (rc)
{
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
std::cerr << "Message sending multipart failed on network: "
<< std::to_string(manager->net_id) << "with retry: " << i
<< "\n";
}
else
{
break;
}
i++;
if (i == MAX_RETRIES_MCTP_SOCK_FAILURE)
{
return PLDM_RDE_REQUESTER_SEND_FAIL;
}
}
std::vector<uint8_t> response(sizeof(pldm_msg_hdr) +
PLDM_MAX_REQUEST_BYTES);
uint8_t* responseMsg = response.data();
size_t responseMsgSize = sizeof(pldm_msg_hdr) + PLDM_MAX_REQUEST_BYTES;
auto responsePtr = reinterpret_cast<struct pldm_msg*>(responseMsg);
if (DEBUG)
{
std::cerr << "Receiving multipart on network: "
<< std::to_string(manager->net_id) << "\n";
}
rc = pldm_recv_at_network(eid, fd, instanceId, &responseMsg,
&responseMsgSize, manager->net_id);
if (rc)
{
// Add workaround
processWorkaroundForFailures(fd, eid, instanceId, manager, ctx);
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
std::cerr
<< "Message receiving failed while receiving OpMultipart response on network: "
<< std::to_string(manager->net_id) << "\n";
return PLDM_RDE_REQUESTER_RECV_FAIL;
}
auto end = std::chrono::system_clock::now();
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
std::chrono::duration<double> elapsed_seconds = end - start;
if (DEBUG)
{
std::cout << "Started processRdeOpMultipartReceive at: "
<< std::ctime(&start_time)
<< "\nprocessRdeOpMultipartReceive at "
<< std::ctime(&end_time)
<< "\nTotal time: " << elapsed_seconds.count() << "s"
<< std::endl;
}
rc = pldm_rde_push_read_operation_response(
manager, ctx, responsePtr, responseMsgSize, &readRequestCallback);
if (rc)
{
std::cout << "Failed response msg with size in multipart: "
<< std::to_string(responseMsgSize) << std::endl;
processWorkaroundForFailures(fd, eid, instanceId, manager, ctx);
ctx->requester_status = PLDM_RDE_REQUESTER_REQUEST_FAILED;
ctx->context_status = CONTEXT_FREE;
std::cerr << "Error in pushing response and updating context with rc: "
<< rc << std::endl;
return rc;
}
return 0;
}
int processNextRdeOperation(int fd, uint8_t eid, int instanceId,
struct pldm_rde_requester_manager* manager,
struct pldm_rde_requester_context* ctx,
const std::vector<uint8_t>& requestMsg)
{
switch (ctx->next_command)
{
case PLDM_RDE_OPERATION_INIT:
return processRdeOperationInit(fd, eid, instanceId, manager, ctx,
requestMsg);
case PLDM_SUPPLY_CUSTOM_REQUEST_PARAMETERS:
return processSupplyCustomRequestParameters(
fd, eid, instanceId, manager, ctx, requestMsg);
case PLDM_RDE_OPERATION_COMPLETE:
return processRdeOperationComplete(fd, eid, instanceId, manager,
ctx, requestMsg);
case PLDM_RDE_MULTIPART_RECEIVE:
return processRdeOpMultipartReceive(fd, eid, instanceId, manager,
ctx, requestMsg);
default:
return PLDM_RDE_REQUESTER_NOT_PLDM_RDE_MSG;
}
return 0;
}
int executeRdeOperation(int fd, int eid, int instanceId, std::string rawUri,
std::string uri, std::string udevid,
uint16_t operationId, uint8_t operationType,
uint8_t requestId,
struct pldm_rde_requester_manager* manager,
struct pldm_rde_requester_context* ctx,
uint32_t resourceId, const std::string& reqJsonPayload)
{
union pldm_rde_operation_flags op_flags;
op_flags.byte = 0;
std::vector<uint8_t> operationLocator;
struct rde_query_options query_options;
std::vector<std::string> headerNames;
std::vector<std::string> headerParams;
std::vector<uint8_t> encodedPayload;
int payloadLength;
int operation_locator_length;
int rc;
// Enocde request payload for operations of type ACTION, CREATE and UPDATE
if (operationType == PLDM_RDE_OPERATION_ACTION)
{
op_flags.bits.contains_request_payload = 1;
op_flags.bits.locator_valid = 1;
std::string resource;
std::string action;
rc = extractAction(uri, resource, action);
if (rc)
{
std::cerr << "No action URI found" << uri << std::endl;
return rc;
}
// Get RID for dictionary
uint32_t ridSchema = resourceId >> 16 << 16;
uint32_t schemaDictLen;
uint8_t* schemaDict;
rc = getDictionaryForRidDev(udevid, ridSchema, &schemaDict,
&schemaDictLen);
if (rc)
{
std::cerr << "Unable to get dictionary for resource";
return rc;
}
uint32_t annotationDictLen;
uint8_t* annotationDict;
rc = getDictionaryForRidDev(udevid, 0xffffffff, &annotationDict,
&annotationDictLen);
if (rc)
{
std::cerr << "Unable to get annotation dictionary for resource";
return rc;
}
// Extract the parent action path and the leaf action name.
// For example: "Oem/GoogleDrive.SetMasterPassword" -> parent="Oem",
// leaf="GoogleDrive.SetMasterPassword" "Reset" -> parent="",
// leaf="Reset"
std::string parentActionPath = "";
std::string leafActionName = action;
size_t lastSlash = action.rfind('/');
if (lastSlash != std::string::npos)
{
parentActionPath = action.substr(0, lastSlash);
leafActionName = action.substr(lastSlash + 1);
}
if (leafActionName.empty() || leafActionName[0] != '#')
{
leafActionName = "#" + leafActionName;
}
// Wrap the original JSON payload inside the leaf action name so that
// the action name is passed as a property inside the BEJ payload and
// can be decoded/distinguished by miniBMC.
std::string wrappedPayload = reqJsonPayload;
if (!reqJsonPayload.empty())
{
try
{
json originalJson = json::parse(reqJsonPayload);
json wrappedJson;
wrappedJson[leafActionName] = originalJson;
wrappedPayload = wrappedJson.dump();
}
catch (const json::parse_error& e)
{
std::cerr << "JSON parsing error in executeRdeOperation: "
<< e.what() << std::endl;
}
}
else
{
json wrappedJson;
wrappedJson[leafActionName] = json::object();
wrappedPayload = wrappedJson.dump();
}
uint16_t offset;
rc = createOperationLocator(parentActionPath, &operationLocator,
&schemaDict, &offset, /*isAction=*/false);
if (rc)
{
std::cerr
<< "Failed to create operation locator for action parent: "
<< parentActionPath << std::endl;
return rc;
}
rc = encodePayload(
wrappedPayload,
parentActionPath.empty() ? "Actions" : parentActionPath, offset,
&schemaDict, &annotationDict, encodedPayload);
if (rc)
{
std::cerr << "Failed to encode payload for action: " << action
<< std::endl;
return rc;
}
}
else if (operationType == PLDM_RDE_OPERATION_UPDATE)
{
op_flags.bits.contains_request_payload = 1;
int rc;
uint32_t annotationDictLen;
uint8_t* annotationDict;
// get_dictioanry_for_rid from particular resource id
rc = getDictionaryForRidDev(udevid, 0xffffffff, &annotationDict,
&annotationDictLen);
if (rc)
{
std::cerr << "Unable to fetch annotation dictionary\n";
return rc;
}
uint32_t ridSchema = resourceId >> 16 << 16;
uint32_t schemaDictLen;
uint8_t* schemaDict;
rc = getDictionaryForRidDev(udevid, ridSchema, &schemaDict,
&schemaDictLen);
if (rc)
{
std::cerr << "Unable to fetch schema dictionary\n";
return rc;
}
rc = encodePayloadForUpdateOp(reqJsonPayload, &schemaDict,
&annotationDict, encodedPayload);
if (rc)
{
std::cerr << "Failed to bej encode payload for Rde Update operation"
<< std::endl;
return rc;
}
}
else
{
query_options.expand = false;
// Check if expand feature is supported by RDE Device
if (rde_expand_enabled &&
manager->device.device_capabilities_flag.bits.bit1 == true)
{
if (DEBUG)
{
std::cerr << " expand feature is supported by RDE Device \n";
}
std::string expandParam = EXPAND_DOT;
int levelsParam = 0;
if (extractExpandParameters(rawUri, expandParam, levelsParam))
{
if (DEBUG)
{
std::cerr << "Query has $expand " << rawUri << "\n";
}
// Send custom headers to Rde Device
op_flags.bits.contains_custom_request_parameters = 1;
query_options.expand = true;
query_options.skip_param = 0;
query_options.top_param = 0xFFFF;
query_options.header_count = 1;
query_options.hdrname_formats[0] = PLDM_RDE_VARSTRING_UTF_8;
headerNames.push_back(PLDM_RDE_EXPAND_TYPE);
headerParams.push_back(expandParam);
query_options.hdrnames[0] = headerNames.back().data();
query_options.hdrparams[0] = headerParams.back().data();
query_options.expand_levels = levelsParam;
query_options.etag_operation = PLDM_RDE_ETAG_IGNORE;
query_options.etag_count = 0;
if (DEBUG)
{
std::cerr << " query_options.expand "
<< query_options.expand << "\n";
std::cerr << " query_options.hdrnames[0] "
<< query_options.hdrnames[0] << "\n";
std::cerr << " query_options.hdrparams[0] "
<< query_options.hdrparams[0] << "\n";
std::cerr << " query_options.expand_levels ["
<< (int)query_options.expand_levels << "]\n";
std::cerr << " query_options.headr_count ["
<< (int)query_options.header_count << "]\n";
}
}
}
}
rc = pldm_rde_init_rde_operation_context(
ctx, requestId, resourceId, operationId, operationType, op_flags.byte,
&query_options,
/*send_transfer_handle*/ (uint32_t)0,
/*operation_loc_length*/ operationLocator.size(),
/*payloadLength*/ encodedPayload.size(),
/*operation_locator_ptr*/ operationLocator.data(),
/*requestPayload*/ encodedPayload.data());
payloadLength = encodedPayload.size();
operation_locator_length = operationLocator.size();
if (rc)
{
std::cerr << "RDE Read operation init context failed\n";
return rc;
}
int processCounter = 0;
while (true)
{
if (ctx->requester_status == PLDM_RDE_REQUESTER_REQUEST_FAILED)
{
return PLDM_RDE_REQUESTER_REQUEST_FAILED;
}
if (ctx->requester_status == PLDM_RDE_REQUESTER_NO_PENDING_ACTION)
{
break;
}
int requestBytes;
if (rdeOpCommandRequestSize.find(ctx->next_command) !=
rdeOpCommandRequestSize.end())
{
requestBytes = rdeOpCommandRequestSize[ctx->next_command];
}
else
{
requestBytes = PLDM_MAX_REQUEST_BYTES;
}
// If operation init request add payload
// length to the request bytes
if (ctx->next_command == PLDM_RDE_OPERATION_INIT)
{
requestBytes = requestBytes + payloadLength +
operation_locator_length;
}
std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + requestBytes);
auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
if (DEBUG)
{
std::cerr << "Getting next read operation... size: "
<< requestMsg.size() << "\n";
}
int rc = pldm_rde_get_next_rde_operation(instanceId, manager, ctx,
request);
if (rc)
{
if (DEBUG)
{
std::cerr << "No next operation found\n";
}
return rc;
}
rc = processNextRdeOperation(fd, eid, instanceId, manager, ctx,
requestMsg);
if (rc)
{
if (DEBUG)
{
std::cerr << "Not a valid request\n";
}
return rc;
}
if (processCounter > MAX_LOOP_ITERATION_STATE_MACHINE)
{
return -1;
}
processCounter++;
}
return 0;
}