1 #include "event_manager.hpp" 2 3 #include "libpldm/utils.h" 4 5 #include "terminus_manager.hpp" 6 7 #include <phosphor-logging/lg2.hpp> 8 #include <xyz/openbmc_project/Logging/Entry/server.hpp> 9 10 #include <cerrno> 11 #include <memory> 12 13 PHOSPHOR_LOG2_USING; 14 15 namespace pldm 16 { 17 namespace platform_mc 18 { 19 namespace fs = std::filesystem; 20 21 int EventManager::handlePlatformEvent( 22 pldm_tid_t tid, uint16_t eventId, uint8_t eventClass, 23 const uint8_t* eventData, size_t eventDataSize) 24 { 25 /* Only handle the event of the discovered termini*/ 26 if (!termini.contains(tid)) 27 { 28 lg2::error("Terminus ID {TID} is not in the managing list.", "TID", 29 tid); 30 return PLDM_ERROR; 31 } 32 33 /* EventClass sensorEvent `Table 11 - PLDM Event Types` DSP0248 */ 34 if (eventClass == PLDM_SENSOR_EVENT) 35 { 36 uint16_t sensorId = 0; 37 uint8_t sensorEventClassType = 0; 38 size_t eventClassDataOffset = 0; 39 auto rc = decode_sensor_event_data(eventData, eventDataSize, &sensorId, 40 &sensorEventClassType, 41 &eventClassDataOffset); 42 if (rc) 43 { 44 lg2::error( 45 "Failed to decode sensor event data from terminus ID {TID}, event class {CLASS}, event ID {EVENTID} with return code {RC}.", 46 "TID", tid, "CLASS", eventClass, "EVENTID", eventId, "RC", rc); 47 return rc; 48 } 49 switch (sensorEventClassType) 50 { 51 case PLDM_NUMERIC_SENSOR_STATE: 52 { 53 const uint8_t* sensorData = eventData + eventClassDataOffset; 54 size_t sensorDataLength = eventDataSize - eventClassDataOffset; 55 return processNumericSensorEvent(tid, sensorId, sensorData, 56 sensorDataLength); 57 } 58 case PLDM_STATE_SENSOR_STATE: 59 case PLDM_SENSOR_OP_STATE: 60 default: 61 lg2::info( 62 "Unsupported class type {CLASSTYPE} for the sensor event from terminus ID {TID} sensorId {SID}", 63 "CLASSTYPE", sensorEventClassType, "TID", tid, "SID", 64 sensorId); 65 return PLDM_ERROR; 66 } 67 } 68 69 /* EventClass CPEREvent as `Table 11 - PLDM Event Types` DSP0248 V1.3.0 */ 70 if (eventClass == PLDM_CPER_EVENT) 71 { 72 return processCperEvent(tid, eventId, eventData, eventDataSize); 73 } 74 75 lg2::info("Unsupported class type {CLASSTYPE}", "CLASSTYPE", eventClass); 76 77 return PLDM_ERROR; 78 } 79 80 int EventManager::processNumericSensorEvent(pldm_tid_t tid, uint16_t sensorId, 81 const uint8_t* sensorData, 82 size_t sensorDataLength) 83 { 84 uint8_t eventState = 0; 85 uint8_t previousEventState = 0; 86 uint8_t sensorDataSize = 0; 87 uint32_t presentReading; 88 auto rc = decode_numeric_sensor_data( 89 sensorData, sensorDataLength, &eventState, &previousEventState, 90 &sensorDataSize, &presentReading); 91 if (rc) 92 { 93 lg2::error( 94 "Failed to decode numericSensorState event for terminus ID {TID}, error {RC} ", 95 "TID", tid, "RC", rc); 96 return rc; 97 } 98 99 double value = static_cast<double>(presentReading); 100 lg2::error( 101 "processNumericSensorEvent tid {TID}, sensorID {SID} value {VAL} previousState {PSTATE} eventState {ESTATE}", 102 "TID", tid, "SID", sensorId, "VAL", value, "PSTATE", previousEventState, 103 "ESTATE", eventState); 104 105 if (!termini.contains(tid) || !termini[tid]) 106 { 107 lg2::error("Terminus ID {TID} is not in the managing list.", "TID", 108 tid); 109 return PLDM_ERROR; 110 } 111 112 auto& terminus = termini[tid]; 113 114 auto sensor = terminus->getSensorObject(sensorId); 115 if (!sensor) 116 { 117 lg2::error( 118 "Terminus ID {TID} has no sensor object with sensor ID {SID}.", 119 "TID", tid, "SID", sensorId); 120 return PLDM_ERROR; 121 } 122 123 switch (previousEventState) 124 { 125 case PLDM_SENSOR_UNKNOWN: 126 case PLDM_SENSOR_NORMAL: 127 { 128 switch (eventState) 129 { 130 case PLDM_SENSOR_UPPERFATAL: 131 case PLDM_SENSOR_UPPERCRITICAL: 132 { 133 sensor->triggerThresholdEvent(pldm::utils::Level::WARNING, 134 pldm::utils::Direction::HIGH, 135 value, true, true); 136 return sensor->triggerThresholdEvent( 137 pldm::utils::Level::CRITICAL, 138 pldm::utils::Direction::HIGH, value, true, true); 139 } 140 case PLDM_SENSOR_UPPERWARNING: 141 { 142 return sensor->triggerThresholdEvent( 143 pldm::utils::Level::WARNING, 144 pldm::utils::Direction::HIGH, value, true, true); 145 } 146 case PLDM_SENSOR_NORMAL: 147 break; 148 case PLDM_SENSOR_LOWERWARNING: 149 { 150 return sensor->triggerThresholdEvent( 151 pldm::utils::Level::WARNING, 152 pldm::utils::Direction::LOW, value, true, true); 153 } 154 case PLDM_SENSOR_LOWERCRITICAL: 155 case PLDM_SENSOR_LOWERFATAL: 156 { 157 sensor->triggerThresholdEvent(pldm::utils::Level::WARNING, 158 pldm::utils::Direction::LOW, 159 value, true, true); 160 return sensor->triggerThresholdEvent( 161 pldm::utils::Level::CRITICAL, 162 pldm::utils::Direction::LOW, value, true, true); 163 } 164 default: 165 break; 166 } 167 break; 168 } 169 case PLDM_SENSOR_LOWERWARNING: 170 { 171 switch (eventState) 172 { 173 case PLDM_SENSOR_UPPERFATAL: 174 case PLDM_SENSOR_UPPERCRITICAL: 175 break; 176 case PLDM_SENSOR_UPPERWARNING: 177 { 178 sensor->triggerThresholdEvent(pldm::utils::Level::WARNING, 179 pldm::utils::Direction::LOW, 180 value, false, false); 181 return sensor->triggerThresholdEvent( 182 pldm::utils::Level::WARNING, 183 pldm::utils::Direction::HIGH, value, true, true); 184 } 185 case PLDM_SENSOR_NORMAL: 186 { 187 return sensor->triggerThresholdEvent( 188 pldm::utils::Level::WARNING, 189 pldm::utils::Direction::LOW, value, false, false); 190 } 191 case PLDM_SENSOR_LOWERWARNING: 192 break; 193 case PLDM_SENSOR_LOWERCRITICAL: 194 case PLDM_SENSOR_LOWERFATAL: 195 { 196 return sensor->triggerThresholdEvent( 197 pldm::utils::Level::CRITICAL, 198 pldm::utils::Direction::LOW, value, true, true); 199 } 200 default: 201 break; 202 } 203 break; 204 } 205 case PLDM_SENSOR_LOWERCRITICAL: 206 case PLDM_SENSOR_LOWERFATAL: 207 { 208 switch (eventState) 209 { 210 case PLDM_SENSOR_UPPERFATAL: 211 case PLDM_SENSOR_UPPERCRITICAL: 212 case PLDM_SENSOR_UPPERWARNING: 213 break; 214 case PLDM_SENSOR_NORMAL: 215 { 216 sensor->triggerThresholdEvent(pldm::utils::Level::CRITICAL, 217 pldm::utils::Direction::LOW, 218 value, false, false); 219 sensor->triggerThresholdEvent(pldm::utils::Level::WARNING, 220 pldm::utils::Direction::LOW, 221 value, true, true); 222 return sensor->triggerThresholdEvent( 223 pldm::utils::Level::WARNING, 224 pldm::utils::Direction::LOW, value, false, false); 225 } 226 case PLDM_SENSOR_LOWERWARNING: 227 { 228 sensor->triggerThresholdEvent(pldm::utils::Level::CRITICAL, 229 pldm::utils::Direction::LOW, 230 value, false, false); 231 return sensor->triggerThresholdEvent( 232 pldm::utils::Level::WARNING, 233 pldm::utils::Direction::LOW, value, true, true); 234 } 235 case PLDM_SENSOR_LOWERCRITICAL: 236 case PLDM_SENSOR_LOWERFATAL: 237 default: 238 break; 239 } 240 break; 241 } 242 case PLDM_SENSOR_UPPERFATAL: 243 case PLDM_SENSOR_UPPERCRITICAL: 244 { 245 switch (eventState) 246 { 247 case PLDM_SENSOR_UPPERFATAL: 248 case PLDM_SENSOR_UPPERCRITICAL: 249 break; 250 case PLDM_SENSOR_UPPERWARNING: 251 { 252 sensor->triggerThresholdEvent(pldm::utils::Level::CRITICAL, 253 pldm::utils::Direction::HIGH, 254 value, false, false); 255 return sensor->triggerThresholdEvent( 256 pldm::utils::Level::WARNING, 257 pldm::utils::Direction::HIGH, value, true, true); 258 } 259 case PLDM_SENSOR_NORMAL: 260 { 261 sensor->triggerThresholdEvent(pldm::utils::Level::CRITICAL, 262 pldm::utils::Direction::HIGH, 263 value, false, false); 264 sensor->triggerThresholdEvent(pldm::utils::Level::WARNING, 265 pldm::utils::Direction::HIGH, 266 value, true, true); 267 return sensor->triggerThresholdEvent( 268 pldm::utils::Level::WARNING, 269 pldm::utils::Direction::HIGH, value, false, false); 270 } 271 case PLDM_SENSOR_LOWERWARNING: 272 case PLDM_SENSOR_LOWERCRITICAL: 273 case PLDM_SENSOR_LOWERFATAL: 274 default: 275 break; 276 } 277 break; 278 } 279 case PLDM_SENSOR_UPPERWARNING: 280 { 281 switch (eventState) 282 { 283 case PLDM_SENSOR_UPPERFATAL: 284 case PLDM_SENSOR_UPPERCRITICAL: 285 { 286 return sensor->triggerThresholdEvent( 287 pldm::utils::Level::CRITICAL, 288 pldm::utils::Direction::HIGH, value, true, true); 289 } 290 case PLDM_SENSOR_UPPERWARNING: 291 break; 292 case PLDM_SENSOR_NORMAL: 293 { 294 return sensor->triggerThresholdEvent( 295 pldm::utils::Level::WARNING, 296 pldm::utils::Direction::HIGH, value, false, false); 297 } 298 case PLDM_SENSOR_LOWERWARNING: 299 { 300 sensor->triggerThresholdEvent(pldm::utils::Level::WARNING, 301 pldm::utils::Direction::HIGH, 302 value, false, false); 303 return sensor->triggerThresholdEvent( 304 pldm::utils::Level::WARNING, 305 pldm::utils::Direction::LOW, value, true, true); 306 } 307 case PLDM_SENSOR_LOWERCRITICAL: 308 case PLDM_SENSOR_LOWERFATAL: 309 default: 310 break; 311 } 312 break; 313 } 314 default: 315 break; 316 } 317 318 return PLDM_SUCCESS; 319 } 320 321 int EventManager::processCperEvent(pldm_tid_t tid, uint16_t eventId, 322 const uint8_t* eventData, 323 const size_t eventDataSize) 324 { 325 if (eventDataSize < PLDM_PLATFORM_CPER_EVENT_MIN_LENGTH) 326 { 327 lg2::error( 328 "Error : Invalid CPER Event data length for eventId {EVENTID}.", 329 "EVENTID", eventId); 330 return PLDM_ERROR; 331 } 332 const size_t cperEventDataSize = 333 eventDataSize - PLDM_PLATFORM_CPER_EVENT_MIN_LENGTH; 334 const size_t msgDataLen = 335 sizeof(pldm_platform_cper_event) + cperEventDataSize; 336 std::string terminusName = ""; 337 auto msgData = std::make_unique<unsigned char[]>(msgDataLen); 338 auto cperEvent = new (msgData.get()) pldm_platform_cper_event; 339 340 auto rc = decode_pldm_platform_cper_event(eventData, eventDataSize, 341 cperEvent, msgDataLen); 342 343 if (rc) 344 { 345 lg2::error( 346 "Failed to decode CPER event for eventId {EVENTID} of terminus ID {TID} error {RC}.", 347 "EVENTID", eventId, "TID", tid, "RC", rc); 348 return rc; 349 } 350 351 if (termini.contains(tid) && !termini[tid]) 352 { 353 auto tmp = termini[tid]->getTerminusName(); 354 if (tmp && !tmp.value().empty()) 355 { 356 terminusName = static_cast<std::string>(tmp.value()); 357 } 358 } 359 else 360 { 361 lg2::error("Terminus ID {TID} is not in the managing list.", "TID", 362 tid); 363 return PLDM_ERROR; 364 } 365 366 // Save event data to file 367 std::filesystem::path dirName{"/var/cper"}; 368 if (!std::filesystem::exists(dirName)) 369 { 370 try 371 { 372 std::filesystem::create_directory(dirName); 373 } 374 catch (const std::filesystem::filesystem_error& e) 375 { 376 lg2::error("Failed to create /var/cper directory: {ERROR}", "ERROR", 377 e); 378 return PLDM_ERROR; 379 } 380 } 381 382 std::string fileName{dirName.string() + "/cper-XXXXXX"}; 383 auto fd = mkstemp(fileName.data()); 384 if (fd < 0) 385 { 386 lg2::error("Failed to generate temp file, error {ERRORNO}", "ERRORNO", 387 std::strerror(errno)); 388 return PLDM_ERROR; 389 } 390 close(fd); 391 392 std::ofstream ofs; 393 ofs.exceptions(std::ofstream::failbit | std::ofstream::badbit | 394 std::ofstream::eofbit); 395 396 try 397 { 398 ofs.open(fileName); 399 ofs.write(reinterpret_cast<const char*>( 400 pldm_platform_cper_event_event_data(cperEvent)), 401 cperEvent->event_data_length); 402 if (cperEvent->format_type == PLDM_PLATFORM_CPER_EVENT_WITH_HEADER) 403 { 404 rc = createCperDumpEntry("CPER", fileName, terminusName); 405 } 406 else 407 { 408 rc = createCperDumpEntry("CPERSection", fileName, terminusName); 409 } 410 ofs.close(); 411 } 412 catch (const std::ofstream::failure& e) 413 { 414 lg2::error("Failed to save CPER to '{FILENAME}', error - {ERROR}.", 415 "FILENAME", fileName, "ERROR", e); 416 return PLDM_ERROR; 417 } 418 return rc; 419 } 420 421 int EventManager::createCperDumpEntry(const std::string& dataType, 422 const std::string& dataPath, 423 const std::string& typeName) 424 { 425 auto createDump = 426 [](std::map<std::string, std::variant<std::string, uint64_t>>& 427 addData) { 428 static constexpr auto dumpObjPath = 429 "/xyz/openbmc_project/dump/faultlog"; 430 static constexpr auto dumpInterface = 431 "xyz.openbmc_project.Dump.Create"; 432 auto& bus = pldm::utils::DBusHandler::getBus(); 433 434 try 435 { 436 auto service = pldm::utils::DBusHandler().getService( 437 dumpObjPath, dumpInterface); 438 auto method = bus.new_method_call(service.c_str(), dumpObjPath, 439 dumpInterface, "CreateDump"); 440 method.append(addData); 441 bus.call_noreply(method); 442 } 443 catch (const std::exception& e) 444 { 445 lg2::error( 446 "Failed to create D-Bus Dump entry, error - {ERROR}.", 447 "ERROR", e); 448 } 449 }; 450 451 std::map<std::string, std::variant<std::string, uint64_t>> addData; 452 addData["Type"] = dataType; 453 addData["PrimaryLogId"] = dataPath; 454 addData["AdditionalTypeName"] = typeName; 455 createDump(addData); 456 return PLDM_SUCCESS; 457 } 458 459 } // namespace platform_mc 460 } // namespace pldm 461