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