| #include "event_manager.hpp" |
| |
| #include "terminus_manager.hpp" |
| |
| #include <libpldm/edac.h> |
| #include <libpldm/platform.h> |
| |
| #include <phosphor-logging/lg2.hpp> |
| #include <xyz/openbmc_project/Dump/Create/common.hpp> |
| #include <xyz/openbmc_project/Logging/Entry/server.hpp> |
| |
| #include <cerrno> |
| #include <memory> |
| |
| PHOSPHOR_LOG2_USING; |
| |
| using DumpCreate = sdbusplus::common::xyz::openbmc_project::dump::Create; |
| |
| namespace pldm |
| { |
| namespace platform_mc |
| { |
| namespace fs = std::filesystem; |
| |
| int EventManager::handlePlatformEvent( |
| pldm_tid_t tid, uint16_t eventId, uint8_t eventClass, |
| const uint8_t* eventData, size_t eventDataSize) |
| { |
| /* Only handle the event of the discovered termini*/ |
| if (!termini.contains(tid)) |
| { |
| return PLDM_SUCCESS; |
| } |
| /* EventClass sensorEvent `Table 11 - PLDM Event Types` DSP0248 */ |
| if (eventClass == PLDM_SENSOR_EVENT) |
| { |
| uint16_t sensorId = 0; |
| uint8_t sensorEventClassType = 0; |
| size_t eventClassDataOffset = 0; |
| auto rc = decode_sensor_event_data(eventData, eventDataSize, &sensorId, |
| &sensorEventClassType, |
| &eventClassDataOffset); |
| if (rc) |
| { |
| lg2::error( |
| "Failed to decode sensor event data from terminus ID {TID}, event class {CLASS}, event ID {EVENTID} with return code {RC}.", |
| "TID", tid, "CLASS", eventClass, "EVENTID", eventId, "RC", rc); |
| return rc; |
| } |
| switch (sensorEventClassType) |
| { |
| case PLDM_NUMERIC_SENSOR_STATE: |
| { |
| const uint8_t* sensorData = eventData + eventClassDataOffset; |
| size_t sensorDataLength = eventDataSize - eventClassDataOffset; |
| return processNumericSensorEvent(tid, sensorId, sensorData, |
| sensorDataLength); |
| } |
| case PLDM_STATE_SENSOR_STATE: |
| case PLDM_SENSOR_OP_STATE: |
| default: |
| lg2::info( |
| "Unsupported class type {CLASSTYPE} for the sensor event from terminus ID {TID} sensorId {SID}", |
| "CLASSTYPE", sensorEventClassType, "TID", tid, "SID", |
| sensorId); |
| return PLDM_ERROR; |
| } |
| } |
| |
| /* EventClass CPEREvent as `Table 11 - PLDM Event Types` DSP0248 V1.3.0 */ |
| if (eventClass == PLDM_CPER_EVENT) |
| { |
| return processCperEvent(tid, eventId, eventData, eventDataSize); |
| } |
| |
| /* EventClass pldmMessagePollEvent `Table 11 - PLDM Event Types` DSP0248 */ |
| if (eventClass == PLDM_MESSAGE_POLL_EVENT) |
| { |
| lg2::warning( |
| "PLDM message poll event is not supported on this OpenBMC baseline for terminus {TID}", |
| "TID", tid); |
| return PLDM_ERROR_UNSUPPORTED_PLDM_CMD; |
| } |
| |
| lg2::info("Unsupported class type {CLASSTYPE}", "CLASSTYPE", eventClass); |
| |
| return PLDM_ERROR; |
| } |
| |
| int triggerNumericSensorThresholdEvent(NumericSensorThresholdHandler handler, |
| uint8_t previousEventState, |
| uint8_t nextEventState, double value) |
| { |
| static const std::map< |
| uint8_t, std::tuple<pldm::utils::Level, pldm::utils::Direction>> |
| sensorEventMap = { |
| {PLDM_SENSOR_UPPERFATAL, |
| {pldm::utils::Level::HARDSHUTDOWN, pldm::utils::Direction::HIGH}}, |
| {PLDM_SENSOR_UPPERCRITICAL, |
| {pldm::utils::Level::CRITICAL, pldm::utils::Direction::HIGH}}, |
| {PLDM_SENSOR_UPPERWARNING, |
| {pldm::utils::Level::WARNING, pldm::utils::Direction::HIGH}}, |
| {PLDM_SENSOR_LOWERWARNING, |
| {pldm::utils::Level::WARNING, pldm::utils::Direction::LOW}}, |
| {PLDM_SENSOR_LOWERCRITICAL, |
| {pldm::utils::Level::CRITICAL, pldm::utils::Direction::LOW}}, |
| {PLDM_SENSOR_LOWERFATAL, |
| {pldm::utils::Level::HARDSHUTDOWN, pldm::utils::Direction::LOW}}, |
| }; |
| static const std::array<uint8_t, 7> stateOrder = { |
| PLDM_SENSOR_LOWERFATAL, PLDM_SENSOR_LOWERCRITICAL, |
| PLDM_SENSOR_LOWERWARNING, PLDM_SENSOR_NORMAL, |
| PLDM_SENSOR_UPPERWARNING, PLDM_SENSOR_UPPERCRITICAL, |
| PLDM_SENSOR_UPPERFATAL}; |
| constexpr auto normalIt = stateOrder.begin() + 3; |
| const auto normalState = *normalIt; |
| auto prevIt = |
| std::find(stateOrder.begin(), stateOrder.end(), previousEventState); |
| if (prevIt == stateOrder.end()) |
| { |
| lg2::error("Undefined previous threshold event state: {EVENT}", "EVENT", |
| previousEventState); |
| return PLDM_ERROR; |
| } |
| auto nextIt = |
| std::find(stateOrder.begin(), stateOrder.end(), nextEventState); |
| if (nextIt == stateOrder.end()) |
| { |
| lg2::error("Undefined current threshold event state: {EVENT}", "EVENT", |
| nextEventState); |
| return PLDM_ERROR; |
| } |
| if (prevIt == nextIt) |
| { |
| // Nothing to do. Return success early |
| return PLDM_SUCCESS; |
| } |
| |
| // This is a rare condition. But if we are going wildy across the thresholds |
| // crossing PLDM_SENSOR_NORMAL (Example going from PLDM_SENSOR_LOWERFATAL to |
| // PLDM_SENSOR_UPPERFATAL in one event, then split it up into two) |
| if ((prevIt < normalIt && nextIt > normalIt) || |
| (prevIt > normalIt && nextIt < normalIt)) |
| { |
| int rc = triggerNumericSensorThresholdEvent(handler, *prevIt, |
| normalState, value); |
| if (rc != PLDM_SUCCESS) |
| { |
| lg2::error("Error handling {PREV} to current for numeric sensor", |
| "PREV", int(*prevIt)); |
| return rc; |
| } |
| return triggerNumericSensorThresholdEvent(handler, normalState, *nextIt, |
| value); |
| } |
| bool goingUp = prevIt < nextIt; |
| bool gettingBetter = (prevIt < nextIt && prevIt < normalIt) || |
| (prevIt > nextIt && prevIt > normalIt); |
| |
| for (auto currIt = prevIt; currIt != nextIt; goingUp ? ++currIt : --currIt) |
| { |
| if (currIt == normalIt) |
| { |
| continue; |
| } |
| const auto& event = sensorEventMap.at(*currIt); |
| |
| if (gettingBetter) |
| { |
| if (currIt != prevIt) |
| { |
| // Just trigger an event in case it was missed before |
| // we deassert. The implementation of triggerThresholdEvent |
| // should just no-op if it is already triggered. |
| handler(std::get<0>(event), std::get<1>(event), value, true, |
| true); |
| } |
| handler(std::get<0>(event), std::get<1>(event), value, false, |
| false); |
| } |
| else |
| { |
| handler(std::get<0>(event), std::get<1>(event), value, true, true); |
| } |
| } |
| if (nextIt != normalIt) |
| { |
| const auto& event = sensorEventMap.at(*nextIt); |
| return handler(std::get<0>(event), std::get<1>(event), value, true, |
| true); |
| } |
| return PLDM_SUCCESS; |
| } |
| |
| int EventManager::processNumericSensorEvent(pldm_tid_t tid, uint16_t sensorId, |
| const uint8_t* sensorData, |
| size_t sensorDataLength) |
| { |
| uint8_t eventState = 0; |
| uint8_t previousEventState = 0; |
| uint8_t sensorDataSize = 0; |
| uint32_t presentReading; |
| auto rc = decode_numeric_sensor_data( |
| sensorData, sensorDataLength, &eventState, &previousEventState, |
| &sensorDataSize, &presentReading); |
| if (rc) |
| { |
| lg2::error( |
| "Failed to decode numericSensorState event for terminus ID {TID}, error {RC} ", |
| "TID", tid, "RC", rc); |
| return rc; |
| } |
| |
| double value = static_cast<double>(presentReading); |
| lg2::error( |
| "processNumericSensorEvent tid {TID}, sensorID {SID} value {VAL} previousState {PSTATE} eventState {ESTATE}", |
| "TID", tid, "SID", sensorId, "VAL", value, "PSTATE", previousEventState, |
| "ESTATE", eventState); |
| |
| if (!termini.contains(tid) || !termini[tid]) |
| { |
| lg2::error("Terminus ID {TID} is not in the managing list.", "TID", |
| tid); |
| return PLDM_ERROR; |
| } |
| |
| auto& terminus = termini[tid]; |
| |
| auto sensor = terminus->getSensorObject(sensorId); |
| if (!sensor) |
| { |
| lg2::error( |
| "Terminus ID {TID} has no sensor object with sensor ID {SID}.", |
| "TID", tid, "SID", sensorId); |
| return PLDM_ERROR; |
| } |
| auto sensorHandler = |
| [&sensor](pldm::utils::Level level, pldm::utils::Direction direction, |
| double rawValue, bool newAlarm, bool assert) { |
| return sensor->triggerThresholdEvent(level, direction, rawValue, |
| newAlarm, assert); |
| }; |
| rc = triggerNumericSensorThresholdEvent(sensorHandler, previousEventState, |
| eventState, value); |
| if (rc) |
| { |
| lg2::error( |
| "Terminus ID {TID} sensor {SID} threshold handling had errors", |
| "TID", tid, "SID", sensorId); |
| return rc; |
| } |
| return PLDM_SUCCESS; |
| } |
| |
| int EventManager::processCperEvent(pldm_tid_t tid, uint16_t eventId, |
| const uint8_t* eventData, |
| const size_t eventDataSize) |
| { |
| (void)tid; |
| (void)eventId; |
| (void)eventData; |
| (void)eventDataSize; |
| // Old libpldm in this OpenBMC baseline has no CPER event decoder support. |
| return PLDM_ERROR_UNSUPPORTED_PLDM_CMD; |
| } |
| |
| int EventManager::createCperDumpEntry(const std::string& dataType, |
| const std::string& dataPath, |
| const std::string& typeName) |
| { |
| auto createDump = |
| [](std::map<std::string, std::variant<std::string, uint64_t>>& |
| addData) { |
| static constexpr auto dumpObjPath = |
| "/xyz/openbmc_project/dump/faultlog"; |
| auto& bus = pldm::utils::DBusHandler::getBus(); |
| |
| try |
| { |
| auto service = pldm::utils::DBusHandler().getService( |
| dumpObjPath, DumpCreate::interface); |
| auto method = |
| bus.new_method_call(service.c_str(), dumpObjPath, |
| DumpCreate::interface, "CreateDump"); |
| method.append(addData); |
| bus.call_noreply(method); |
| } |
| catch (const std::exception& e) |
| { |
| lg2::error( |
| "Failed to create D-Bus Dump entry, error - {ERROR}.", |
| "ERROR", e); |
| } |
| }; |
| |
| std::map<std::string, std::variant<std::string, uint64_t>> addData; |
| addData["Type"] = dataType; |
| addData["PrimaryLogId"] = dataPath; |
| addData["AdditionalTypeName"] = typeName; |
| createDump(addData); |
| return PLDM_SUCCESS; |
| } |
| |
| int EventManager::getNextPartParameters( |
| uint16_t eventId, std::vector<uint8_t> eventMessage, uint8_t transferFlag, |
| uint32_t eventDataIntegrityChecksum, uint32_t nextDataTransferHandle, |
| uint8_t* transferOperationFlag, uint32_t* dataTransferHandle, |
| uint32_t* eventIdToAcknowledge) |
| { |
| (void)eventMessage; |
| if (transferFlag != PLDM_START_AND_END && transferFlag != PLDM_END) |
| { |
| *transferOperationFlag = PLDM_GET_NEXTPART; |
| *dataTransferHandle = nextDataTransferHandle; |
| *eventIdToAcknowledge = PLDM_PLATFORM_EVENT_ID_FRAGMENT; |
| return PLDM_SUCCESS; |
| } |
| |
| if (transferFlag == PLDM_END) |
| { |
| // Old libpldm in this OpenBMC baseline has no pldm_edac_crc32. |
| (void)eventDataIntegrityChecksum; |
| } |
| |
| /* End of one event. Set request transfer flag to ACK */ |
| *transferOperationFlag = PLDM_ACKNOWLEDGEMENT_ONLY; |
| *dataTransferHandle = 0; |
| *eventIdToAcknowledge = eventId; |
| |
| return PLDM_SUCCESS; |
| } |
| |
| void EventManager::callPolledEventHandlers(pldm_tid_t tid, uint8_t eventClass, |
| uint16_t eventId, |
| std::vector<uint8_t>& eventMessage) |
| { |
| try |
| { |
| const auto& handlers = eventHandlers.at(eventClass); |
| for (const auto& handler : handlers) |
| { |
| auto rc = |
| handler(tid, eventId, eventMessage.data(), eventMessage.size()); |
| if (rc != PLDM_SUCCESS) |
| { |
| lg2::error( |
| "Failed to handle platform event msg for terminus {TID}, event {EVENTID} return {RET}", |
| "TID", tid, "EVENTID", eventId, "RET", rc); |
| } |
| } |
| } |
| catch (const std::out_of_range& e) |
| { |
| lg2::error( |
| "Failed to handle platform event msg for terminus {TID}, event {EVENTID} error - {ERROR}", |
| "TID", tid, "EVENTID", eventId, "ERROR", e); |
| } |
| } |
| |
| exec::task<int> EventManager::pollForPlatformEventTask( |
| pldm_tid_t tid, uint32_t pollDataTransferHandle) |
| { |
| (void)tid; |
| (void)pollDataTransferHandle; |
| // Old libpldm in this OpenBMC baseline lacks required poll-event |
| // structs/APIs. |
| co_return PLDM_ERROR_UNSUPPORTED_PLDM_CMD; |
| } |
| |
| exec::task<int> EventManager::pollForPlatformEventMessage( |
| pldm_tid_t tid, uint8_t formatVersion, uint8_t transferOperationFlag, |
| uint32_t dataTransferHandle, uint16_t eventIdToAcknowledge, |
| uint8_t& completionCode, uint8_t& eventTid, uint16_t& eventId, |
| uint32_t& nextDataTransferHandle, uint8_t& transferFlag, |
| uint8_t& eventClass, uint32_t& eventDataSize, uint8_t*& eventData, |
| uint32_t& eventDataIntegrityChecksum) |
| { |
| Request request( |
| sizeof(pldm_msg_hdr) + PLDM_POLL_FOR_PLATFORM_EVENT_MESSAGE_REQ_BYTES); |
| auto requestMsg = new (request.data()) pldm_msg; |
| auto rc = encode_poll_for_platform_event_message_req( |
| 0, formatVersion, transferOperationFlag, dataTransferHandle, |
| eventIdToAcknowledge, requestMsg, request.size()); |
| if (rc) |
| { |
| lg2::error( |
| "Failed to encode request PollForPlatformEventMessage for terminus ID {TID}, error {RC} ", |
| "TID", tid, "RC", rc); |
| co_return rc; |
| } |
| |
| /* Stop event polling */ |
| if (!getAvailableState(tid)) |
| { |
| lg2::info( |
| "Terminus ID {TID} is not available for PLDM request from {NOW}.", |
| "TID", tid, "NOW", pldm::utils::getCurrentSystemTime()); |
| co_await stdexec::just_stopped(); |
| } |
| |
| const pldm_msg* responseMsg = nullptr; |
| size_t responseLen = 0; |
| rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg, |
| &responseLen); |
| if (rc) |
| { |
| lg2::error( |
| "Failed to send PollForPlatformEventMessage message for terminus {TID}, error {RC}", |
| "TID", tid, "RC", rc); |
| co_return rc; |
| } |
| |
| rc = decode_poll_for_platform_event_message_resp( |
| responseMsg, responseLen, &completionCode, &eventTid, &eventId, |
| &nextDataTransferHandle, &transferFlag, &eventClass, &eventDataSize, |
| (void**)&eventData, &eventDataIntegrityChecksum); |
| if (rc) |
| { |
| lg2::error( |
| "Failed to decode response PollForPlatformEventMessage for terminus ID {TID}, error {RC} ", |
| "TID", tid, "RC", rc); |
| co_return rc; |
| } |
| if (completionCode != PLDM_SUCCESS) |
| { |
| lg2::error( |
| "Error : PollForPlatformEventMessage for terminus ID {TID}, complete code {CC}.", |
| "TID", tid, "CC", completionCode); |
| co_return rc; |
| } |
| |
| co_return completionCode; |
| } |
| |
| } // namespace platform_mc |
| } // namespace pldm |