blob: 3e4cd69abd56f7e357cf93df551f66cd6ddc1414 [file]
#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