| #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, |
| ¤tOffset); |
| 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, |
| ¤tOffset); |
| 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, |
| ¤tOffset); |
| 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; |
| } |