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 
handlePlatformEvent(pldm_tid_t tid,uint16_t eventId,uint8_t eventClass,const uint8_t * eventData,size_t eventDataSize)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 
processNumericSensorEvent(pldm_tid_t tid,uint16_t sensorId,const uint8_t * sensorData,size_t sensorDataLength)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 
processCperEvent(pldm_tid_t tid,uint16_t eventId,const uint8_t * eventData,const size_t eventDataSize)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 
createCperDumpEntry(const std::string & dataType,const std::string & dataPath,const std::string & typeName)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