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