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