#include "pldm.hpp" #include "libpldm/instance-id.h" #include "file.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace pldm { using namespace sdeventplus; using namespace sdeventplus::source; constexpr auto clockId = sdeventplus::ClockId::RealTime; using Clock = sdeventplus::Clock; using Timer = Time; bool Interface::throttleTraces = false; enum pldm_msg_type Interface::msgType = MSG_UNDEFINED; open_power::occ::instanceID Interface::resetInstance = 0; void Interface::fetchSensorInfo(uint16_t stateSetId, SensorToInstance& sensorInstanceMap, SensorOffset& sensorOffset) { PdrList pdrs{}; static bool tracedError = false; auto& bus = open_power::occ::utils::getBus(); try { auto method = bus.new_method_call( "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm", "xyz.openbmc_project.PLDM.PDR", "FindStateSensorPDR"); method.append(tid, static_cast(PLDM_ENTITY_PROC), stateSetId); auto responseMsg = bus.call(method); responseMsg.read(pdrs); } catch (const sdbusplus::exception_t& e) { if (!tracedError) { lg2::error( "fetchSensorInfo: Failed to find stateSetID:{ID} PDR: {ERR}", "ID", stateSetId, "ERR", e.what()); tracedError = true; } } if (pdrs.empty()) { if (!tracedError) { lg2::error("fetchSensorInfo: state sensor PDRs ({ID}) not present", "ID", stateSetId); tracedError = true; } return; } // Found PDR if (tracedError) { lg2::info("fetchSensorInfo: found {NUM} PDRs", "NUM", pdrs.size()); tracedError = false; } bool offsetFound = false; auto stateSensorPDR = reinterpret_cast(pdrs.front().data()); auto possibleStatesPtr = stateSensorPDR->possible_states; for (auto offset = 0; offset < stateSensorPDR->composite_sensor_count; offset++) { auto possibleStates = reinterpret_cast( possibleStatesPtr); if (possibleStates->state_set_id == stateSetId) { sensorOffset = offset; offsetFound = true; break; } possibleStatesPtr += sizeof(possibleStates->state_set_id) + sizeof(possibleStates->possible_states_size) + possibleStates->possible_states_size; } if (!offsetFound) { lg2::error("pldm: state sensor PDR not found"); return; } // To order SensorID based on the EntityInstance. // Note that when a proc is on a DCM, the PDRs for these sensors // could have the same instance IDs but different container IDs. std::map entityInstMap{}; for (auto& pdr : pdrs) { auto pdrPtr = reinterpret_cast(pdr.data()); uint32_t key = pdrPtr->sensor_id; entityInstMap.emplace(key, static_cast(pdrPtr->sensor_id)); } open_power::occ::instanceID count = start; for (const auto& pair : entityInstMap) { sensorInstanceMap.emplace(pair.second, count); count++; } } void Interface::sensorEvent(sdbusplus::message_t& msg) { if (!isOCCSensorCacheValid()) { fetchSensorInfo(PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS, sensorToOCCInstance, OCCSensorOffset); } if (sensorToSBEInstance.empty()) { fetchSensorInfo(PLDM_OEM_IBM_SBE_HRESET_STATE, sensorToSBEInstance, SBESensorOffset); } TerminusID sensorTid{}; SensorID sensorId{}; SensorOffset msgSensorOffset{}; EventState eventState{}; EventState previousEventState{}; msg.read(sensorTid, sensorId, msgSensorOffset, eventState, previousEventState); if (msgSensorOffset == OCCSensorOffset) { auto sensorEntry = sensorToOCCInstance.find(sensorId); if (sensorEntry != sensorToOCCInstance.end()) { const uint8_t instance = sensorEntry->second; bool validEvent = true; bool isRunning = false; if (eventState == static_cast( PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_IN_SERVICE)) { lg2::info("PLDM: OCC{INST} is RUNNING", "INST", instance); isRunning = true; } else if (eventState == static_cast( PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_STOPPED)) { lg2::info("PLDM: OCC{INST} has now STOPPED", "INST", instance); } else if (eventState == static_cast( PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_DORMANT)) { lg2::error( "PLDM: OCC{INST} has now STOPPED and system is in SAFE MODE", "INST", instance); // Setting safe mode true safeModeCallBack(true); } else { lg2::warning( "PLDM: Unexpected OCC Active sensor state {STATE} for OCC{INST}", "STATE", eventState, "INST", instance); validEvent = false; } if (validEvent) { if ((pldmFd > 0) && (instance == pldmResponseOcc)) { // Waiting for a response for this OCC, can stop waiting pldmClose(); } occActiveCallBack(instance, isRunning); } return; } } if (msgSensorOffset == SBESensorOffset) { auto sensorEntry = sensorToSBEInstance.find(sensorId); if (sensorEntry != sensorToSBEInstance.end()) { const uint8_t instance = sensorEntry->second; auto match = std::find(outstandingHResets.begin(), outstandingHResets.end(), instance); if (match != outstandingHResets.end()) { outstandingHResets.erase(match); if (eventState == static_cast(SBE_HRESET_NOT_READY)) { lg2::error("pldm: HRESET is NOT READY (OCC{INST})", "INST", instance); // Stop OCC comm - OCC not usable until it becomes READY occActiveCallBack(instance, false); // Collect SBE FFDC sbeCallBack(instance, false); // Try PM Complex reset lg2::error( "sensorEvent: Requesting OCC reset for OCC{INST}", "INST", instance); resetOCC(resetInstance); } else if (eventState == static_cast(SBE_HRESET_READY)) { sbeCallBack(instance, true); } else if (eventState == static_cast(SBE_HRESET_FAILED)) { sbeCallBack(instance, false); } else { if (eventState == static_cast(SBE_HRESET_FAILED)) lg2::error( "pldm: Unexpected HRESET state {STATE} (OCC{INST})", "STATE", eventState, "INST", instance); sbeCallBack(instance, false); } } // else request was not from us } } } void Interface::hostStateEvent(sdbusplus::message_t& msg) { std::map> properties{}; std::string interface; msg.read(interface, properties); const auto stateEntry = properties.find("CurrentHostState"); if (stateEntry != properties.end()) { auto stateEntryValue = stateEntry->second; auto propVal = std::get(stateEntryValue); if (propVal == "xyz.openbmc_project.State.Host.HostState.Off") { clearData(); } } } void Interface::clearData() { if (!sensorToOCCInstance.empty()) { lg2::info("clearData: Clearing sensorToOCCInstance ({NUM} entries)", "NUM", sensorToOCCInstance.size()); for (auto entry : sensorToOCCInstance) { lg2::info("clearData: OCC{INST} / sensorID: {ID}", "INST", entry.second, "ID", lg2::hex, entry.first); occActiveCallBack(entry.second, false); } sensorToOCCInstance.clear(); } if (!occInstanceToEffecter.empty()) { lg2::debug("clearData: Clearing occInstanceToEffecter ({NUM} entries)", "NUM", occInstanceToEffecter.size()); occInstanceToEffecter.clear(); } if (!sensorToSBEInstance.empty()) { lg2::debug("clearData: Clearing sensorToSBEInstance ({NUM} entries)", "NUM", sensorToSBEInstance.size()); sensorToSBEInstance.clear(); } if (!sbeInstanceToEffecter.empty()) { lg2::debug("clearData: Clearing sbeInstanceToEffecter ({NUM} entries)", "NUM", sbeInstanceToEffecter.size()); sbeInstanceToEffecter.clear(); } } void Interface::fetchEffecterInfo( uint16_t stateSetId, InstanceToEffecter& instanceToEffecterMap, CompositeEffecterCount& effecterCount, uint8_t& stateIdPos) { PdrList pdrs{}; auto& bus = open_power::occ::utils::getBus(); try { auto method = bus.new_method_call( "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm", "xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR"); method.append(tid, static_cast(PLDM_ENTITY_PROC), stateSetId); auto responseMsg = bus.call(method); responseMsg.read(pdrs); } catch (const sdbusplus::exception_t& e) { lg2::error("pldm: Failed to fetch the state effecter PDRs: {ERR}", "ERR", e.what()); } if (!pdrs.size()) { lg2::error("pldm: state effecter PDRs not present"); return; } bool offsetFound = false; auto stateEffecterPDR = reinterpret_cast(pdrs.front().data()); auto possibleStatesPtr = stateEffecterPDR->possible_states; for (auto offset = 0; offset < stateEffecterPDR->composite_effecter_count; offset++) { auto possibleStates = reinterpret_cast( possibleStatesPtr); if (possibleStates->state_set_id == stateSetId) { stateIdPos = offset; effecterCount = stateEffecterPDR->composite_effecter_count; offsetFound = true; break; } possibleStatesPtr += sizeof(possibleStates->state_set_id) + sizeof(possibleStates->possible_states_size) + possibleStates->possible_states_size; } if (!offsetFound) { return; } std::map entityInstMap{}; for (auto& pdr : pdrs) { auto pdrPtr = reinterpret_cast(pdr.data()); uint32_t key = pdrPtr->effecter_id; entityInstMap.emplace(key, static_cast(pdrPtr->effecter_id)); } open_power::occ::instanceID position = start; for (const auto& pair : entityInstMap) { instanceToEffecterMap.emplace(position, pair.second); position++; } } std::vector Interface::prepareSetEffecterReq( EffecterID effecterId, CompositeEffecterCount effecterCount, uint8_t stateIdPos, uint8_t stateSetValue) { if (!getPldmInstanceId()) { return std::vector(); } std::vector request( sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(effecterCount) + (effecterCount * sizeof(set_effecter_state_field))); auto requestMsg = reinterpret_cast(request.data()); std::vector stateField; for (uint8_t effecterPos = 0; effecterPos < effecterCount; effecterPos++) { if (effecterPos == stateIdPos) { stateField.emplace_back( set_effecter_state_field{PLDM_REQUEST_SET, stateSetValue}); } else { stateField.emplace_back( set_effecter_state_field{PLDM_NO_CHANGE, 0}); } } auto rc = encode_set_state_effecter_states_req( pldmInstanceID.value(), effecterId, effecterCount, stateField.data(), requestMsg); if (rc != PLDM_SUCCESS) { lg2::error("encode set effecter states request returned error rc={RC}", "RC", rc); request.clear(); } return request; } void Interface::resetOCC(open_power::occ::instanceID occInstanceId) { if (open_power::occ::utils::isHostRunning()) { if (!isPDREffecterCacheValid()) { fetchEffecterInfo(PLDM_STATE_SET_BOOT_RESTART_CAUSE, occInstanceToEffecter, OCCEffecterCount, bootRestartPosition); } // Find the matching effecter for the OCC instance auto effecterEntry = occInstanceToEffecter.find(occInstanceId); if (effecterEntry == occInstanceToEffecter.end()) { lg2::error( "pldm: Failed to find a matching effecter for OCC instance {INST}", "INST", occInstanceId); return; } // Prepare the SetStateEffecterStates request to reset the OCC auto request = prepareSetEffecterReq( effecterEntry->second, OCCEffecterCount, bootRestartPosition, PLDM_STATE_SET_BOOT_RESTART_CAUSE_WARM_RESET); if (request.empty()) { lg2::error("pldm: SetStateEffecterStates OCC reset request empty"); return; } // Send request to reset the OCCs/PM Complex (and wait for response) msgType = MSG_OCC_RESET; resetInstance = occInstanceId; sendPldm(request, occInstanceId, true); } else { lg2::error("resetOCC: HOST is not running (OCC{INST})", "INST", occInstanceId); clearData(); } } void Interface::sendHRESET(open_power::occ::instanceID sbeInstanceId) { if (open_power::occ::utils::isHostRunning()) { if (sbeInstanceToEffecter.empty()) { fetchEffecterInfo(PLDM_OEM_IBM_SBE_MAINTENANCE_STATE, sbeInstanceToEffecter, SBEEffecterCount, sbeMaintenanceStatePosition); } auto effecterEntry = sbeInstanceToEffecter.find(sbeInstanceId); if (effecterEntry == sbeInstanceToEffecter.end()) { lg2::error( "pldm: Failed to find a matching effecter for SBE instance {INST}", "INST", sbeInstanceId); return; } // Prepare the SetStateEffecterStates request to HRESET the SBE auto request = prepareSetEffecterReq( effecterEntry->second, SBEEffecterCount, sbeMaintenanceStatePosition, SBE_RETRY_REQUIRED); if (request.empty()) { lg2::error("pldm: SetStateEffecterStates HRESET request empty"); return; } // Send request to issue HRESET of SBE (and wait for response) msgType = MSG_HRESET; resetInstance = sbeInstanceId; sendPldm(request, sbeInstanceId, true); outstandingHResets.insert(sbeInstanceId); } else { lg2::error("sendHRESET: HOST is not running (OCC{INST})", "INST", sbeInstanceId); clearData(); } } bool Interface::getPldmInstanceId() { pldm_instance_id_t id; if (!pldmInstanceID) { // Request new instance ID int rc = pldm_instance_id_alloc(pldmInstanceIdDb, tid, &id); if (rc == -EAGAIN) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); rc = pldm_instance_id_alloc(pldmInstanceIdDb, tid, &id); } if (rc) { lg2::error( "getPldmInstanceId: Failed to alloc ID for TID {TID}. RC{RC}", "TID", tid, "RC", rc); return false; } pldmInstanceID.emplace(id); if (!throttleTraces) { lg2::info("got id {ID} and set PldmInstanceId to {INST}", "ID", id, "INST", pldmInstanceID.value()); } } return true; } void Interface::freePldmInstanceId() { if (pldmInstanceID) { int rc = pldm_instance_id_free(pldmInstanceIdDb, tid, pldmInstanceID.value()); if (rc) { lg2::error( "freePldmInstanceId: Failed to free ID {ID} for TID {TID}. RC{RC}", "ID", pldmInstanceID.value(), "TID", tid, "RC", rc); return; } if (!throttleTraces) { lg2::info("Freed PLDM instance ID {ID}", "ID", pldmInstanceID.value()); } pldmInstanceID = std::nullopt; } } [[maybe_unused]] int Interface::openMctpDemuxTransport() { impl.mctpDemux = nullptr; int rc = pldm_transport_mctp_demux_init(&impl.mctpDemux); if (rc) { lg2::error( "openMctpDemuxTransport: Failed to init MCTP demux transport, errno={ERR}/{STR}", "ERR", rc, "STR", strerror(rc)); return -1; } if (pldm_transport_mctp_demux_map_tid(impl.mctpDemux, mctpEid, mctpEid)) { lg2::error( "openMctpDemuxTransport: Failed to setup tid to eid mapping, errno={ERR}/{STR}", "ERR", errno, "STR", strerror(errno)); pldmClose(); return -1; } pldmTransport = pldm_transport_mctp_demux_core(impl.mctpDemux); struct pollfd pollfd; if (pldm_transport_mctp_demux_init_pollfd(pldmTransport, &pollfd)) { lg2::error( "openMctpDemuxTransport: Failed to get pollfd , errno={ERR}/{STR}", "ERR", errno, "STR", strerror(errno)); pldmClose(); return -1; } pldmFd = pollfd.fd; if (!throttleTraces) { lg2::info("openMctpDemuxTransport: pldmFd has fd={FD}", "FD", pldmFd); } return 0; } [[maybe_unused]] int Interface::openAfMctpTransport() { impl.afMctp = nullptr; int rc = pldm_transport_af_mctp_init(&impl.afMctp); if (rc) { lg2::error( "openAfMctpTransport: Failed to init af MCTP transport, errno={ERR}/{STR}", "ERR", rc, "STR", strerror(rc)); return -1; } if (pldm_transport_af_mctp_map_tid(impl.afMctp, mctpEid, mctpEid)) { lg2::error( "openAfMctpTransport: Failed to setup tid to eid mapping, errno={ERR}/{STR}", "ERR", errno, "STR", strerror(errno)); pldmClose(); return -1; } pldmTransport = pldm_transport_af_mctp_core(impl.afMctp); struct pollfd pollfd; if (pldm_transport_af_mctp_init_pollfd(pldmTransport, &pollfd)) { lg2::error( "openAfMctpTransport: Failed to get pollfd , errno={ERR}/{STR}", "ERR", errno, "STR", strerror(errno)); pldmClose(); return -1; } pldmFd = pollfd.fd; if (!throttleTraces) { lg2::info("openAfMctpTransport: pldmFd has fd={FD}", "FD", pldmFd); } return 0; } int Interface::pldmOpen() { if (pldmTransport) { lg2::error("pldmOpen: pldmTransport already setup!, errno={ERR}/{STR}", "ERR", errno, "STR", strerror(errno)); return -1; } #if defined(PLDM_TRANSPORT_WITH_MCTP_DEMUX) return openMctpDemuxTransport(); #elif defined(PLDM_TRANSPORT_WITH_AF_MCTP) return openAfMctpTransport(); #else lg2::error("pldmOpen: Undefined pldmTransport!, errno={ERR}/{STR}", "ERR", errno, "STR", strerror(errno)); return -1; #endif return 0; } void Interface::sendPldm(const std::vector& request, const uint8_t instance, const bool rspExpected) { if (!pldmInstanceID) { lg2::error("sendPldm: No PLDM Instance ID found!"); return; } auto rc = pldmOpen(); if (rc) { if (!throttleTraces) { lg2::error("sendPldm: pldmOpen failed rc={RC}", "RC", rc); } freePldmInstanceId(); return; } pldm_tid_t pldmTID = static_cast(mctpEid); // Send the PLDM request message to HBRT if (rspExpected) { // Register callback when response is available registerPldmRspCallback(); using namespace std::literals::chrono_literals; std::chrono::duration timeout = 8s; if ((msgType == MSG_OCC_RESET) || (msgType == MSG_HRESET)) { timeout = 30s; } // Send PLDM request if (!throttleTraces) { lg2::info("sendPldm: calling pldm_transport_send_msg(OCC{INST}, " "instance:{ID}, {LEN} bytes, timeout {TO})", "INST", instance, "ID", pldmInstanceID.value(), "LEN", request.size(), "TO", timeout.count()); } pldmResponseReceived = false; pldmResponseTimeout = false; pldmResponseOcc = instance; auto pldmRc = pldm_transport_send_msg(pldmTransport, pldmTID, request.data(), request.size()); auto sendErrno = errno; if (pldmRc != PLDM_REQUESTER_SUCCESS) { lg2::error( "sendPldm: pldm_transport_send_msg failed with rc={RC} and errno={ERR}/{STR}", "RC", static_cast>( pldmRc), "ERR", sendErrno, "STR", strerror(sendErrno)); pldmClose(); return; } // start timer waiting for the response pldmRspTimer.restartOnce(timeout); // Wait for response/timeout } else // not expecting the response { if (!throttleTraces) { lg2::info( "sendPldm: calling pldm_transport_send_msg(mctpID:{ID}, fd:{FD}, " "{LEN} bytes) for OCC{INST}", "ID", mctpEid, "FD", pldmFd, "LEN", request.size(), "INST", instance); } auto rc = pldm_transport_send_msg(pldmTransport, pldmTID, request.data(), request.size()); auto sendErrno = errno; if (rc) { lg2::error( "sendPldm: pldm_transport_send_msg(mctpID:{ID}, fd:{FD}, {LEN} bytes) " "failed with rc={RC} and errno={ERR}/{STR}", "ID", mctpEid, "FD", pldmFd, "LEN", request.size(), "RC", static_cast>( rc), "ERR", sendErrno, "STR", strerror(sendErrno)); } pldmClose(); } } // Attaches the FD to event loop and registers the callback handler void Interface::registerPldmRspCallback() { decltype(eventSource.get()) sourcePtr = nullptr; int rc = 0; if ((msgType == MSG_OCC_RESET) || (msgType == MSG_HRESET)) { rc = sd_event_add_io(event.get(), &sourcePtr, pldmFd, EPOLLIN, pldmResetCallback, this); } else { rc = sd_event_add_io(event.get(), &sourcePtr, pldmFd, EPOLLIN, pldmRspCallback, this); } if (rc < 0) { lg2::error( "registerPldmRspCallback: sd_event_add_io: Error({ERR})={STR} : fd={FD} (msgType={MSG})", "ERR", rc, "STR", strerror(-rc), "FD", pldmFd, "MSG", msgType); } else { // puts sourcePtr in the event source. eventSource.reset(sourcePtr); } } // Add a timer to the event loop, default 30s. void Interface::pldmRspExpired() { if (!pldmResponseReceived) { if (!throttleTraces) { lg2::warning( "pldmRspExpired: timerCallback - timeout waiting for pldm " "response to msg:{MSG} for OCC{INST}", "MSG", msgType, "INST", pldmResponseOcc); } pldmResponseTimeout = true; if (pldmFd) { pldmClose(); } if (msgType == MSG_OCC_RESET) { // reset not acked, try again lg2::error("pldmRspExpired: retrying reset request for OCC{INST}", "INST", pldmResponseOcc); resetOCC(pldmResponseOcc); } } return; }; void Interface::pldmClose() { freePldmInstanceId(); if (pldmRspTimer.isEnabled()) { // stop PLDM response timer pldmRspTimer.setEnabled(false); } #if defined(PLDM_TRANSPORT_WITH_MCTP_DEMUX) pldm_transport_mctp_demux_destroy(impl.mctpDemux); impl.mctpDemux = NULL; #elif defined(PLDM_TRANSPORT_WITH_AF_MCTP) pldm_transport_af_mctp_destroy(impl.afMctp); impl.afMctp = NULL; #endif pldmFd = -1; pldmTransport = NULL; eventSource.reset(); } int Interface::pldmRspCallback(sd_event_source* /*es*/, __attribute__((unused)) int fd, uint32_t revents, void* userData) { if (!(revents & EPOLLIN)) { lg2::info("pldmRspCallback - revents={NUM}", "NUM", lg2::hex, revents); return -1; } auto pldmIface = static_cast(userData); if (!pldmIface->pldmInstanceID) { lg2::error("pldmRspCallback: No outstanding PLDM Instance ID found"); return -1; } uint8_t* responseMsg = nullptr; size_t responseMsgSize{}; pldm_tid_t pldmTID = static_cast(mctpEid); if (!throttleTraces) { lg2::info( "pldmRspCallback: calling pldm_transport_recv_msg() instance:{INST}", "INST", pldmIface->pldmInstanceID.value()); } auto rc = pldm_transport_recv_msg(pldmIface->pldmTransport, &pldmTID, (void**)&responseMsg, &responseMsgSize); int lastErrno = errno; if (rc) { if (!throttleTraces) { lg2::error( "pldmRspCallback: pldm_transport_recv_msg failed with rc={RC}, errno={ERR}/{STR}", "RC", static_cast>( rc), "ERR", lastErrno, "STR", strerror(lastErrno)); } return -1; } // We got the response for the PLDM request msg that was sent if (!throttleTraces) { lg2::info( "pldmRspCallback: pldm_transport_recv_msg() rsp was {LEN} bytes", "LEN", responseMsgSize); } if (pldmIface->pldmRspTimer.isEnabled()) { // stop PLDM response timer pldmIface->pldmRspTimer.setEnabled(false); } // instance ID should be freed pldmIface->pldmInstanceID = std::nullopt; // Set pointer to autodelete std::unique_ptr responseMsgPtr{ responseMsg, std::free}; auto response = reinterpret_cast(responseMsgPtr.get()); if (response->payload[0] != PLDM_SUCCESS) { lg2::error("pldmRspCallback: payload[0] was not success: {STATUS}", "STATUS", response->payload[0]); pldmIface->pldmClose(); return -1; } // Decode the response uint8_t compCode = 0, sensorCount = 1; get_sensor_state_field field[6]; responseMsgSize -= sizeof(pldm_msg_hdr); auto msgRc = decode_get_state_sensor_readings_resp( response, responseMsgSize, &compCode, &sensorCount, field); if ((msgRc != PLDM_SUCCESS) || (compCode != PLDM_SUCCESS)) { lg2::error( "pldmRspCallback: decode_get_state_sensor_readings failed with rc={RC} and compCode={CC}", "RC", msgRc, "CC", compCode); pldmIface->pldmClose(); return -1; } pldmIface->pldmClose(); const uint8_t instance = pldmIface->pldmResponseOcc; const uint8_t occSensorState = field[0].present_state; pldmIface->pldmResponseReceived = true; if (occSensorState == PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_IN_SERVICE) { lg2::info("pldmRspCallback: OCC{INST} is RUNNING", "INST", instance); pldmIface->occActiveCallBack(instance, true); } else if (occSensorState == PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_DORMANT) { lg2::error( "pldmRspCallback: OCC{INST} has now STOPPED and system is in SAFE MODE", "INST", instance); // Setting safe mode true pldmIface->safeModeCallBack(true); pldmIface->occActiveCallBack(instance, false); } else if (occSensorState == PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_STOPPED) { lg2::info("pldmRspCallback: OCC{INST} is not running", "INST", instance); pldmIface->occActiveCallBack(instance, false); } else { const size_t rspLength = responseMsgSize + sizeof(pldm_msg_hdr); std::vector pldmResponse(rspLength); memcpy(&pldmResponse[0], reinterpret_cast(response), rspLength); if (!throttleTraces) { lg2::warning( "pldmRspCallback: Unexpected State: {STATE} - PLDM response " "({LEN} bytes) for OCC{INST}:", "STATE", occSensorState, "LEN", rspLength, "INST", instance); dump_hex(pldmResponse); } } return 0; }; int Interface::pldmResetCallback(sd_event_source* /*es*/, __attribute__((unused)) int fd, uint32_t revents, void* userData) { if (!(revents & EPOLLIN)) { lg2::info("pldmResetCallback - revents={NUM}", "NUM", lg2::hex, revents); return -1; } auto pldmIface = static_cast(userData); if (!pldmIface->pldmInstanceID) { lg2::error("pldmResetCallback: No outstanding PLDM Instance ID found"); return -1; } uint8_t* responseMsg = nullptr; size_t responseMsgSize{}; pldm_tid_t pldmTID = static_cast(mctpEid); if (!throttleTraces) { lg2::info( "pldmResetCallback: calling pldm_transport_recv_msg() instance:{ID}", "ID", pldmIface->pldmInstanceID.value()); } auto rc = pldm_transport_recv_msg(pldmIface->pldmTransport, &pldmTID, (void**)&responseMsg, &responseMsgSize); int lastErrno = errno; if (rc) { if (!throttleTraces) { lg2::error( "pldmResetCallback: pldm_transport_recv_msg failed with rc={RC}, errno={ERR}/{STR}", "RC", static_cast>( rc), "ERR", lastErrno, "STR", strerror(lastErrno)); } return -1; } // We got the response for the PLDM request msg that was sent if (!throttleTraces) { lg2::info( "pldmResetCallback: pldm_transport_recv_msg() rsp was {LEN} bytes", "LEN", responseMsgSize); } if (pldmIface->pldmRspTimer.isEnabled()) { // stop PLDM response timer pldmIface->pldmRspTimer.setEnabled(false); } // instance ID should be freed pldmIface->pldmInstanceID = std::nullopt; // Set pointer to autodelete std::unique_ptr responseMsgPtr{ responseMsg, std::free}; auto response = reinterpret_cast(responseMsgPtr.get()); if (response->payload[0] != PLDM_SUCCESS) { lg2::error( "pldmResetCallback: Reset FAILED ({MSG}) - payload[0] was not success: {STATUS}", "MSG", msgType, "STATUS", response->payload[0]); pldmIface->pldmClose(); if (msgType == MSG_OCC_RESET) { // Retry reset request lg2::error( "pldmResetCallback: retrying reset request for OCC{INST}", "INST", resetInstance); pldmIface->resetOCC(resetInstance); } return -1; } else { lg2::info("pldmResetCallback: Reset has been successfully started"); } pldmIface->pldmClose(); pldmIface->pldmResponseReceived = true; return 0; } std::vector Interface::encodeGetStateSensorRequest(uint8_t instance, uint16_t sensorId) { if (!getPldmInstanceId()) { lg2::error("encodeGetStateSensorRequest: failed to getPldmInstanceId"); return std::vector(); } bitfield8_t sRearm = {0}; const size_t msgSize = sizeof(pldm_msg_hdr) + PLDM_GET_STATE_SENSOR_READINGS_REQ_BYTES; std::vector request(msgSize); auto msg = reinterpret_cast(request.data()); auto msgRc = encode_get_state_sensor_readings_req(pldmInstanceID.value(), sensorId, sRearm, 0, msg); if (msgRc != PLDM_SUCCESS) { lg2::error( "encodeGetStateSensorRequest: Failed to encode sensorId:{ID} for OCC{INST} (rc={RC})", "ID", lg2::hex, sensorId, "INST", instance, "RC", msgRc); } return request; } // Initiate query of the specified OCC Active Sensor void Interface::checkActiveSensor(uint8_t instance) { static bool tracedOnce = false; if (pldmFd > 0) { if (!throttleTraces && !tracedOnce) { lg2::warning( "checkActiveSensor: already waiting on OCC{INST} (fd={FD})", "INST", pldmResponseOcc, "FD", pldmFd); tracedOnce = true; } return; } tracedOnce = false; if (!isOCCSensorCacheValid()) { fetchSensorInfo(PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS, sensorToOCCInstance, OCCSensorOffset); } // look up sensor id (key) based on instance auto entry = std::find_if( sensorToOCCInstance.begin(), sensorToOCCInstance.end(), [instance](const auto& entry) { return instance == entry.second; }); if (entry != sensorToOCCInstance.end()) { // Query the OCC Active Sensor state for this instance if (!throttleTraces) { lg2::info("checkActiveSensor: OCC{INST} / sensorID: {ID}", "INST", instance, "ID", lg2::hex, entry->first); } // Encode GetStateSensorReadings PLDM message auto request = encodeGetStateSensorRequest(instance, entry->first); if (request.empty()) { return; } // Send request to PLDM and setup callback for response msgType = MSG_SENSOR_STATUS; sendPldm(request, instance, true); } else { if (!throttleTraces) { lg2::error( "checkActiveSensor: Unable to find PLDM sensor for OCC{INST}", "INST", instance); lg2::info( "checkActiveSensor: fetching STATE_SET_OPERATIONAL_RUNNING_STATUS"); } fetchSensorInfo(PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS, sensorToOCCInstance, OCCSensorOffset); } } void Interface::setTraceThrottle(const bool throttle) { if (throttle != throttleTraces) { if (throttle) { lg2::warning("PLDM traces being throttled"); } else { lg2::info("PLDM traces no longer being throttled"); } throttleTraces = throttle; } } } // namespace pldm