| #include "base_disc_state_machine.hpp" |
| |
| #include "libpldm/base.h" |
| #include "libpldm/pldm.h" |
| |
| #include "interface/pldm_interface.hpp" |
| |
| #include <stdplus/print.hpp> |
| #include <util/common.hpp> |
| |
| BaseDiscoveryStateMachine::BaseDiscoveryStateMachine( |
| int fd, const std::string& deviceName, int netId) : |
| fd(fd), |
| deviceName(deviceName), netId(netId) |
| { |
| stdplus::print(stderr, "Initializing base discovery state machine...\n"); |
| this->initialized = true; |
| this->requesterStatus = StateMachineStatus::NoPendingAction; |
| this->eid = DEST_EID; |
| this->instanceId = INSTANCE_ID; // TODO(@harshtya): Make this dynamic |
| } |
| |
| OperationStatus BaseDiscoveryStateMachine::run() |
| { |
| stdplus::print(stderr, "Triggering PLDM Base discovery for device {}...\n", |
| this->deviceName); |
| if (!this->initialized && |
| this->requesterStatus != StateMachineStatus::NoPendingAction) |
| { |
| return OperationStatus::StateMachineInitializationError; |
| } |
| |
| // Setting up the initial command set |
| this->nextCommand = PLDM_GET_TID; |
| this->requesterStatus = StateMachineStatus::ReadyToPickNextRequest; |
| for (int processCounter = 0; processCounter < BASE_REQUEST_RETRIES; |
| processCounter++) |
| { |
| if (this->requesterStatus == StateMachineStatus::NoPendingAction) |
| { |
| stdplus::print(stderr, "Base discovery successfully completed\n"); |
| break; |
| } |
| OperationStatus status = triggerNextCommand(); |
| if (status != OperationStatus::Success) |
| { |
| stdplus::print(stderr, |
| "Base discovery failed at command {} with " |
| "status: {}\n", |
| std::to_string(this->nextCommand), |
| static_cast<int>(status)); |
| return status; |
| } |
| |
| if (processCounter == (BASE_REQUEST_RETRIES - 1)) |
| { |
| stdplus::print(stderr, "Base discovery failed due infinite loop\n"); |
| return OperationStatus::OperationFailure; |
| } |
| } |
| return OperationStatus::Success; |
| } |
| |
| OperationStatus BaseDiscoveryStateMachine::triggerNextCommand() |
| { |
| switch (this->nextCommand) |
| { |
| case PLDM_GET_TID: |
| return processGetTidRequest(); |
| case PLDM_GET_PLDM_TYPES: |
| return processGetPldmTypesRequest(); |
| case PLDM_GET_PLDM_VERSION: |
| return processGetPldmVersionRequest(); |
| case PLDM_GET_PLDM_COMMANDS: |
| return processGetPldmCommandsRequest(); |
| default: |
| return OperationStatus::NoNextCommandFound; |
| } |
| return OperationStatus::Success; |
| } |
| |
| OperationStatus BaseDiscoveryStateMachine::processGetTidRequest() |
| { |
| std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + |
| baseCommandRequestSize.at(PLDM_GET_TID)); |
| auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); |
| |
| int rc = encode_get_tid_req(this->instanceId, request); |
| if (rc) |
| { |
| stdplus::print( |
| stderr, |
| "Encoding TID request failed in base discovery with rc: {} for " |
| "device {}\n", |
| rc, this->deviceName); |
| return OperationStatus::EncodingRequestFailure; |
| } |
| |
| if (pldmSendAtNetwork(this->eid, this->netId, this->fd, requestMsg.data(), |
| requestMsg.size())) |
| { |
| this->requesterStatus = StateMachineStatus::RequestFailed; |
| return OperationStatus::PldmSendFailure; |
| } |
| std::vector<uint8_t> response( |
| sizeof(pldm_msg_hdr) + PLDM_GET_TID_RESP_BYTES, 0); |
| uint8_t* responseMsg = response.data(); |
| size_t responseMsgSize = sizeof(pldm_msg_hdr) + PLDM_GET_TID_RESP_BYTES; |
| auto responsePtr = reinterpret_cast<struct pldm_msg*>(responseMsg); |
| |
| if (pldmRecvAtNetwork(this->eid, this->netId, this->fd, this->instanceId, |
| &responseMsg, &responseMsgSize)) |
| { |
| this->requesterStatus = StateMachineStatus::RequestFailed; |
| return OperationStatus::PldmRecvFailure; |
| } |
| |
| stdplus::print(stderr, "Pushing response for GET_TID for device {}\n", |
| this->deviceName); |
| OperationStatus status = pushCommandResponse(responsePtr, responseMsgSize); |
| if (status != OperationStatus::Success) |
| { |
| stdplus::print(stderr, |
| "Failed to push response for Get TID in base " |
| "discovery with status: {} for device: {}\n", |
| static_cast<int>(status), this->deviceName); |
| } |
| return status; |
| } |
| |
| OperationStatus BaseDiscoveryStateMachine::processGetPldmTypesRequest() |
| { |
| std::vector<uint8_t> requestMsg( |
| sizeof(pldm_msg_hdr) + baseCommandRequestSize.at(PLDM_GET_PLDM_TYPES)); |
| auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); |
| |
| int rc = encode_get_types_req(this->instanceId, request); |
| if (rc) |
| { |
| stdplus::print( |
| stderr, |
| "Encoding PLDM Types request failed in base discovery with rc: {} " |
| "for device {}\n", |
| rc, this->deviceName); |
| return OperationStatus::EncodingRequestFailure; |
| } |
| |
| if (pldmSendAtNetwork(this->eid, this->netId, this->fd, requestMsg.data(), |
| requestMsg.size())) |
| { |
| this->requesterStatus = StateMachineStatus::RequestFailed; |
| return OperationStatus::PldmSendFailure; |
| } |
| std::vector<uint8_t> response( |
| sizeof(pldm_msg_hdr) + PLDM_GET_TYPES_RESP_BYTES, 0); |
| uint8_t* responseMsg = response.data(); |
| size_t responseMsgSize = response.size(); |
| auto responsePtr = reinterpret_cast<struct pldm_msg*>(responseMsg); |
| |
| if (pldmRecvAtNetwork(this->eid, this->netId, this->fd, this->instanceId, |
| &responseMsg, &responseMsgSize)) |
| { |
| this->requesterStatus = StateMachineStatus::RequestFailed; |
| return OperationStatus::PldmRecvFailure; |
| } |
| stdplus::print(stderr, |
| "Pushing response for GET_PLDM_TYPES for device: " |
| "{}\n", |
| this->deviceName); |
| OperationStatus status = pushCommandResponse(responsePtr, responseMsgSize); |
| if (status != OperationStatus::Success) |
| { |
| stdplus::print(stderr, |
| "Failed to push response for Get PLDM TYPES in base " |
| "discovery with status: {} for device: {}\n", |
| static_cast<int>(status), this->deviceName); |
| } |
| return status; |
| } |
| |
| OperationStatus BaseDiscoveryStateMachine::processGetPldmVersionRequest() |
| { |
| std::vector<uint8_t> requestMsg( |
| sizeof(pldm_msg_hdr) + |
| baseCommandRequestSize.at(PLDM_GET_PLDM_VERSION)); |
| auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); |
| |
| int rc = encode_get_version_req(this->instanceId, /*transfer_handle=*/0, |
| PLDM_GET_FIRSTPART, this->commandPldmType, |
| request); |
| if (rc) |
| { |
| stdplus::print( |
| stderr, |
| "Encoding PLDM Version request failed in base discovery with rc: " |
| "{} for device {} and pldmType: {}\n", |
| rc, this->deviceName, std::to_string(this->commandPldmType)); |
| return OperationStatus::EncodingRequestFailure; |
| } |
| |
| if (pldmSendAtNetwork(this->eid, this->netId, this->fd, requestMsg.data(), |
| requestMsg.size())) |
| { |
| this->requesterStatus = StateMachineStatus::RequestFailed; |
| return OperationStatus::PldmSendFailure; |
| } |
| |
| std::vector<uint8_t> response( |
| sizeof(pldm_msg_hdr) + PLDM_GET_VERSION_RESP_BYTES, 0); |
| uint8_t* responseMsg = response.data(); |
| size_t responseMsgSize = response.size(); |
| auto responsePtr = reinterpret_cast<struct pldm_msg*>(responseMsg); |
| |
| if (pldmRecvAtNetwork(this->eid, this->netId, this->fd, this->instanceId, |
| &responseMsg, &responseMsgSize)) |
| { |
| this->requesterStatus = StateMachineStatus::RequestFailed; |
| return OperationStatus::PldmRecvFailure; |
| } |
| stdplus::print(stderr, |
| "Pushing response for GET_PLDM_VERSION for device: " |
| "{} with pldmType: {}\n", |
| this->deviceName, std::to_string(this->commandPldmType)); |
| OperationStatus status = pushCommandResponse(responsePtr, responseMsgSize); |
| if (status != OperationStatus::Success) |
| { |
| stdplus::print(stderr, |
| "Failed to push response for Get PLDM Version in base " |
| "discovery with status: {} for device: {} for pldm Type:" |
| " {}\n", |
| static_cast<int>(status), this->deviceName, |
| std::to_string(this->commandPldmType)); |
| } |
| return status; |
| } |
| |
| OperationStatus BaseDiscoveryStateMachine::processGetPldmCommandsRequest() |
| { |
| std::vector<uint8_t> requestMsg( |
| sizeof(pldm_msg_hdr) + |
| baseCommandRequestSize.at(PLDM_GET_PLDM_COMMANDS)); |
| auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); |
| |
| int rc = encode_get_commands_req(this->instanceId, this->commandPldmType, |
| this->pldmVersions[this->commandPldmType], |
| request); |
| if (rc) |
| { |
| stdplus::print( |
| stderr, |
| "Encoding PLDM Commands request failed in base discovery with rc: " |
| "{} for device {} and pldmType: {}\n", |
| rc, this->deviceName, std::to_string(this->commandPldmType)); |
| return OperationStatus::EncodingRequestFailure; |
| } |
| |
| if (pldmSendAtNetwork(this->eid, this->netId, this->fd, requestMsg.data(), |
| requestMsg.size())) |
| { |
| this->requesterStatus = StateMachineStatus::RequestFailed; |
| return OperationStatus::PldmSendFailure; |
| } |
| std::vector<uint8_t> response( |
| sizeof(pldm_msg_hdr) + PLDM_GET_COMMANDS_RESP_BYTES, 0); |
| uint8_t* responseMsg = response.data(); |
| size_t responseMsgSize = response.size(); |
| auto responsePtr = reinterpret_cast<struct pldm_msg*>(responseMsg); |
| |
| if (pldmRecvAtNetwork(this->eid, this->netId, this->fd, this->instanceId, |
| &responseMsg, &responseMsgSize)) |
| { |
| this->requesterStatus = StateMachineStatus::RequestFailed; |
| return OperationStatus::PldmRecvFailure; |
| } |
| |
| stdplus::print(stderr, |
| "Pushing response for GET_PLDM_COMMANDS for device: " |
| "{} with pldmType: {}\n", |
| this->deviceName, std::to_string(this->commandPldmType)); |
| OperationStatus status = pushCommandResponse(responsePtr, responseMsgSize); |
| if (status != OperationStatus::Success) |
| { |
| stdplus::print(stderr, |
| "Failed to push response for Get PLDM Command in base " |
| "discovery with status: {} for device: {} for pldm Type:" |
| " {}\n", |
| static_cast<int>(status), this->deviceName, |
| std::to_string(this->commandPldmType)); |
| } |
| return status; |
| } |
| |
| OperationStatus BaseDiscoveryStateMachine::pushCommandResponse( |
| const struct pldm_msg* respMsg, size_t respSize) |
| { |
| switch (this->nextCommand) |
| { |
| case PLDM_GET_TID: |
| { |
| uint8_t completionCode = DEFAULT_INITIAL_VAL; |
| uint8_t tid = DEFAULT_INITIAL_VAL; |
| int rc = decode_get_tid_resp(respMsg, |
| respSize - sizeof(struct pldm_msg_hdr), |
| &completionCode, &tid); |
| if (rc || completionCode) |
| { |
| this->requesterStatus = StateMachineStatus::RequestFailed; |
| stdplus::print(stderr, |
| "Get TID response decode failed with rc:" |
| " {} and completion code: {}\n", |
| rc, std::to_string(completionCode)); |
| return OperationStatus::IncorrectResponseMsg; |
| } |
| this->tid = tid; |
| this->nextCommand = PLDM_GET_PLDM_TYPES; |
| this->requesterStatus = StateMachineStatus::ReadyToPickNextRequest; |
| return OperationStatus::Success; |
| } |
| |
| case PLDM_GET_PLDM_TYPES: |
| { |
| uint8_t completionCode = DEFAULT_INITIAL_VAL; |
| int rc = decode_get_types_resp( |
| respMsg, respSize - sizeof(struct pldm_msg_hdr), |
| &completionCode, this->pldmTypes.data()); |
| if (rc || completionCode) |
| { |
| this->requesterStatus = StateMachineStatus::RequestFailed; |
| stdplus::print( |
| stderr, |
| "Get PLDM Type response decoded for " |
| "pldm GetTypes failed with rc: {} and completion code: {}\n", |
| rc, std::to_string(completionCode)); |
| return OperationStatus::IncorrectResponseMsg; |
| } |
| |
| // Setting the initial pldm_type as PLDM_BASE |
| this->commandPldmType = PLDM_BASE; |
| this->nextCommand = PLDM_GET_PLDM_VERSION; |
| this->requesterStatus = StateMachineStatus::ReadyToPickNextRequest; |
| return OperationStatus::Success; |
| } |
| case PLDM_GET_PLDM_VERSION: |
| { |
| ver32_t versionOut; |
| uint8_t completionCode = DEFAULT_INITIAL_VAL; |
| uint8_t retFlag = DEFAULT_INITIAL_VAL; |
| uint32_t retTransferHandle = DEFAULT_INITIAL_VAL; |
| int rc = decode_get_version_resp( |
| respMsg, respSize - sizeof(struct pldm_msg_hdr), |
| &completionCode, &retTransferHandle, &retFlag, &versionOut); |
| |
| if (rc || completionCode) |
| { |
| this->requesterStatus = StateMachineStatus::RequestFailed; |
| stdplus::print( |
| stderr, |
| "Get PLDM Versions response decoded for " |
| "pldm get versions failed on pldm type: {} with rc: {} and " |
| "completion code: {}\n", |
| std::to_string(this->commandPldmType), rc, |
| std::to_string(completionCode)); |
| return OperationStatus::IncorrectResponseMsg; |
| } |
| uint8_t currentPldmType = this->commandPldmType; |
| this->pldmVersions[currentPldmType] = versionOut; |
| |
| std::optional<uint8_t> nextPldmType = |
| getNextPldmTypeToBeProcessed(currentPldmType); |
| |
| if (nextPldmType.has_value()) |
| { |
| // get the version for the next pldm type |
| this->commandPldmType = nextPldmType.value(); |
| this->nextCommand = PLDM_GET_PLDM_VERSION; |
| this->requesterStatus = |
| StateMachineStatus::ReadyToPickNextRequest; |
| } |
| else |
| { |
| // All available pldm types processed |
| this->nextCommand = PLDM_GET_PLDM_COMMANDS; |
| this->requesterStatus = |
| StateMachineStatus::ReadyToPickNextRequest; |
| this->commandPldmType = PLDM_BASE; |
| } |
| return OperationStatus::Success; |
| } |
| case PLDM_GET_PLDM_COMMANDS: |
| { |
| uint8_t completionCode = DEFAULT_INITIAL_VAL; |
| bitfield8_t pldmCmds[PLDM_MAX_CMDS_PER_TYPE / 8]; |
| int rc = decode_get_commands_resp( |
| respMsg, respSize - sizeof(struct pldm_msg_hdr), |
| &completionCode, pldmCmds); |
| if (rc || completionCode) |
| { |
| this->requesterStatus = StateMachineStatus::RequestFailed; |
| stdplus::print( |
| stderr, |
| "Get PLDM Cmds response decoded for " |
| "pldm get cmds failed on pldm type: {} with rc: {} and " |
| "completion code: {}\n", |
| std::to_string(this->commandPldmType), rc, |
| std::to_string(completionCode)); |
| return OperationStatus::IncorrectResponseMsg; |
| } |
| uint8_t currentPldmType = this->commandPldmType; |
| |
| for (int i = 0; i < (PLDM_MAX_CMDS_PER_TYPE / 8); i++) |
| { |
| this->pldmCommands[currentPldmType][i] = pldmCmds[i].byte; |
| } |
| |
| std::optional<uint8_t> nextPldmType = |
| getNextPldmTypeToBeProcessed(currentPldmType); |
| |
| if (nextPldmType.has_value()) |
| { |
| this->commandPldmType = nextPldmType.value(); |
| this->nextCommand = PLDM_GET_PLDM_COMMANDS; |
| this->requesterStatus = |
| StateMachineStatus::ReadyToPickNextRequest; |
| } |
| else |
| { |
| // All available pldm types processed |
| this->nextCommand = |
| static_cast<int>(OperationStatus::NoNextCommandFound); |
| this->requesterStatus = StateMachineStatus::NoPendingAction; |
| // Setting base PLDM type the PLDM Commands - default |
| this->commandPldmType = PLDM_BASE; |
| } |
| return OperationStatus::Success; |
| } |
| default: |
| return OperationStatus::OperationFailure; |
| } |
| } |
| |
| std::optional<uint8_t> |
| BaseDiscoveryStateMachine::getNextPldmTypeToBeProcessed(uint8_t currentType) |
| { |
| // Each bit in a byte returned in pldm type response represents a pldm type |
| // Hence we process each bit to get the corresponding versions and commands |
| // for that bit |
| int byte = currentType / 8; |
| int bit = currentType % 8; |
| bool isBitSet = false; |
| |
| while (byte < 8 && !isBitSet) |
| { |
| uint8_t currentByte = this->pldmTypes[byte].byte; |
| int index = bit + 1; |
| |
| // Skip already traversed bits of the current byte |
| currentByte = currentByte >> (bit + 1); |
| while (currentByte) |
| { |
| if (currentByte & 1) |
| { |
| isBitSet = true; |
| bit = index; |
| break; |
| } |
| index++; |
| currentByte = currentByte >> 1; |
| } |
| |
| if (!isBitSet) |
| { |
| byte++; |
| bit = -1; // We need to start from 0th bit of |
| // the next byte |
| } |
| } |
| if (byte == 8 && !isBitSet) |
| { |
| return std::nullopt; |
| } |
| return (bit + byte * 8); |
| } |
| |
| void BaseDiscoveryStateMachine::printState() |
| { |
| // TODO(@harshtya): Implement state print for debug |
| } |