xref: /openbmc/pldm/oem/ampere/event/oem_event_manager.cpp (revision 3de0d94ec859f11563bc98c82e43cd8e3a1cb5d8)
1 #include "oem_event_manager.hpp"
2 
3 #include "requester/handler.hpp"
4 #include "requester/request.hpp"
5 
6 #include <config.h>
7 #include <libpldm/pldm.h>
8 #include <libpldm/utils.h>
9 #include <systemd/sd-journal.h>
10 
11 #include <phosphor-logging/lg2.hpp>
12 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
13 
14 #include <algorithm>
15 #include <map>
16 #include <sstream>
17 #include <string>
18 #include <unordered_map>
19 
20 namespace pldm
21 {
22 namespace oem_ampere
23 {
24 namespace boot_stage = boot::stage;
25 
26 constexpr const char* ampereEventRegistry = "OpenBMC.0.1.AmpereEvent.OK";
27 constexpr const char* ampereWarningRegistry =
28     "OpenBMC.0.1.AmpereWarning.Warning";
29 constexpr const char* ampereCriticalRegistry =
30     "OpenBMC.0.1.AmpereCritical.Critical";
31 constexpr const char* BIOSFWPanicRegistry =
32     "OpenBMC.0.1.BIOSFirmwarePanicReason.Warning";
33 constexpr auto maxDIMMIdxBitNum = 24;
34 
35 /*
36     An array of possible boot status of a boot stage.
37     The index maps with byte 0 of boot code.
38 */
39 std::array<std::string, 3> bootStatMsg = {" booting", " completed", " failed"};
40 
41 /*
42     An array of possible boot status of DDR training stage.
43     The index maps with byte 0 of boot code.
44 */
45 std::array<std::string, 3> ddrTrainingMsg = {
46     " progress started", " in-progress", " progress completed"};
47 
48 /*
49     In Ampere systems, BMC only directly communicates with MCTP/PLDM SoC
50     EPs through SMBus and PCIe. When host boots up, SMBUS interface
51     comes up first. In this interface, BMC is bus owner.
52 
53     mctpd will set the EID 0x14 for S0 and 0x16 for S1 (if available).
54     pldmd will always use TID 1 for S0 and TID 2 for S1 (if available).
55 */
56 EventToMsgMap_t tidToSocketNameMap = {{1, "SOCKET 0"}, {2, "SOCKET 1"}};
57 
58 /*
59     A map between sensor IDs and their names in string.
60     Using pldm::oem::sensor_ids
61 */
62 EventToMsgMap_t sensorIdToStrMap = {{PCIE_HOT_PLUG, "PCIE_HOT_PLUG"},
63                                     {BOOT_OVERALL, "BOOT_OVERALL"}};
64 
65 /*
66     A map between the boot stages and logging strings.
67     Using pldm::oem::boot::stage::boot_stage
68 */
69 EventToMsgMap_t bootStageToMsgMap = {
70     {boot_stage::SECPRO, "SECpro"},
71     {boot_stage::MPRO, "Mpro"},
72     {boot_stage::ATF_BL1, "ATF BL1"},
73     {boot_stage::ATF_BL2, "ATF BL2"},
74     {boot_stage::DDR_INITIALIZATION, "DDR initialization"},
75     {boot_stage::DDR_TRAINING, "DDR training"},
76     {boot_stage::S0_DDR_TRAINING_FAILURE, "DDR training failure"},
77     {boot_stage::ATF_BL31, "ATF BL31"},
78     {boot_stage::ATF_BL32, "ATF BL32"},
79     {boot_stage::S1_DDR_TRAINING_FAILURE, "DDR training failure"},
80     {boot_stage::UEFI_STATUS_CLASS_CODE_MIN,
81      "ATF BL33 (UEFI) booting status = "}};
82 
83 /*
84     A map between log level and the registry used for Redfish SEL log
85     Using pldm::oem::log_level
86 */
87 std::unordered_map<log_level, std::string> logLevelToRedfishMsgIdMap = {
88     {log_level::OK, ampereEventRegistry},
89     {log_level::WARNING, ampereWarningRegistry},
90     {log_level::CRITICAL, ampereCriticalRegistry},
91     {log_level::BIOSFWPANIC, BIOSFWPanicRegistry}};
92 
93 std::string
94     OemEventManager::prefixMsgStrCreation(pldm_tid_t tid, uint16_t sensorId)
95 {
96     std::string description;
97     if (!tidToSocketNameMap.contains(tid))
98     {
99         description += "TID " + std::to_string(tid) + ": ";
100     }
101     else
102     {
103         description += tidToSocketNameMap[tid] + ": ";
104     }
105 
106     if (!sensorIdToStrMap.contains(sensorId))
107     {
108         description += "Sensor ID " + std::to_string(sensorId) + ": ";
109     }
110     else
111     {
112         description += sensorIdToStrMap[sensorId] + ": ";
113     }
114 
115     return description;
116 }
117 
118 void OemEventManager::sendJournalRedfish(const std::string& description,
119                                          log_level& logLevel)
120 {
121     if (description.empty())
122     {
123         return;
124     }
125 
126     if (!logLevelToRedfishMsgIdMap.contains(logLevel))
127     {
128         lg2::error("Invalid {LEVEL} Description {DES}", "LEVEL", logLevel,
129                    "DES", description);
130         return;
131     }
132     auto redfishMsgId = logLevelToRedfishMsgIdMap[logLevel];
133     lg2::info("MESSAGE={DES}", "DES", description, "REDFISH_MESSAGE_ID",
134               redfishMsgId, "REDFISH_MESSAGE_ARGS", description);
135 }
136 
137 std::string OemEventManager::dimmIdxsToString(uint32_t dimmIdxs)
138 {
139     std::string description;
140     for (const auto bitIdx : std::views::iota(0, maxDIMMIdxBitNum))
141     {
142         if (dimmIdxs & (static_cast<uint32_t>(1) << bitIdx))
143         {
144             description += " #" + std::to_string(bitIdx);
145         }
146     }
147     return description;
148 }
149 
150 void OemEventManager::handleBootOverallEvent(
151     pldm_tid_t /*tid*/, uint16_t /*sensorId*/, uint32_t presentReading)
152 {
153     log_level logLevel{log_level::OK};
154     std::string description;
155     std::stringstream strStream;
156 
157     uint8_t byte0 = (presentReading & 0x000000ff);
158     uint8_t byte1 = (presentReading & 0x0000ff00) >> 8;
159     uint8_t byte2 = (presentReading & 0x00ff0000) >> 16;
160     uint8_t byte3 = (presentReading & 0xff000000) >> 24;
161     /*
162      * Handle SECpro, Mpro, ATF BL1, ATF BL2, ATF BL31,
163      * ATF BL32 and DDR initialization
164      */
165     if (bootStageToMsgMap.contains(byte3))
166     {
167         // Boot stage adding
168         description += bootStageToMsgMap[byte3];
169 
170         switch (byte3)
171         {
172             case boot_stage::DDR_TRAINING:
173                 if (byte0 >= ddrTrainingMsg.size())
174                 {
175                     logLevel = log_level::BIOSFWPANIC;
176                     description += " unknown status";
177                 }
178                 else
179                 {
180                     description += ddrTrainingMsg[byte0];
181                 }
182                 if (0x01 == byte0)
183                 {
184                     // Add complete percentage
185                     description += " at " + std::to_string(byte1) + "%";
186                 }
187                 break;
188             case boot_stage::S0_DDR_TRAINING_FAILURE:
189             case boot_stage::S1_DDR_TRAINING_FAILURE:
190                 // ddr_training_status_msg()
191                 logLevel = log_level::BIOSFWPANIC;
192                 description += " at DIMMs:";
193                 // dimmIdxs = presentReading & 0x00ffffff;
194                 description += dimmIdxsToString(presentReading & 0x00ffffff);
195                 description += " of socket ";
196                 description +=
197                     (boot_stage::S0_DDR_TRAINING_FAILURE == byte3) ? "0" : "1";
198                 break;
199             default:
200                 if (byte0 >= bootStatMsg.size())
201                 {
202                     logLevel = log_level::BIOSFWPANIC;
203                     description += " unknown status";
204                 }
205                 else
206                 {
207                     description += bootStatMsg[byte0];
208                 }
209                 break;
210         }
211 
212         // Sensor report action is fail
213         if (boot::status::BOOT_STATUS_FAILURE == byte2)
214         {
215             logLevel = log_level::BIOSFWPANIC;
216         }
217     }
218     else
219     {
220         if (byte3 <= boot_stage::UEFI_STATUS_CLASS_CODE_MAX)
221         {
222             description +=
223                 bootStageToMsgMap[boot_stage::UEFI_STATUS_CLASS_CODE_MIN];
224 
225             strStream
226                 << "Segment (0x" << std::setfill('0') << std::hex
227                 << std::setw(8) << static_cast<uint32_t>(presentReading)
228                 << "); Status Class (0x" << std::setw(2)
229                 << static_cast<uint32_t>(byte3) << "); Status SubClass (0x"
230                 << std::setw(2) << static_cast<uint32_t>(byte2)
231                 << "); Operation Code (0x" << std::setw(4)
232                 << static_cast<uint32_t>((presentReading & 0xffff0000) >> 16)
233                 << ")" << std::dec;
234 
235             description += strStream.str();
236         }
237     }
238 
239     // Log to Redfish event
240     sendJournalRedfish(description, logLevel);
241 }
242 
243 int OemEventManager::processNumericSensorEvent(
244     pldm_tid_t tid, uint16_t sensorId, const uint8_t* sensorData,
245     size_t sensorDataLength)
246 {
247     uint8_t eventState = 0;
248     uint8_t previousEventState = 0;
249     uint8_t sensorDataSize = 0;
250     uint32_t presentReading;
251     auto rc = decode_numeric_sensor_data(
252         sensorData, sensorDataLength, &eventState, &previousEventState,
253         &sensorDataSize, &presentReading);
254     if (rc)
255     {
256         lg2::error(
257             "Failed to decode numericSensorState event for terminus ID {TID}, error {RC} ",
258             "TID", tid, "RC", rc);
259         return rc;
260     }
261 
262     switch (sensorId)
263     {
264         case BOOT_OVERALL:
265             handleBootOverallEvent(tid, sensorId, presentReading);
266             break;
267         case PCIE_HOT_PLUG:
268             handlePCIeHotPlugEvent(tid, sensorId, presentReading);
269             break;
270         default:
271             std::string description;
272             std::stringstream strStream;
273             log_level logLevel = log_level::OK;
274 
275             description += "SENSOR_EVENT : NUMERIC_SENSOR_STATE: ";
276             description += prefixMsgStrCreation(tid, sensorId);
277             strStream << std::setfill('0') << std::hex << "eventState 0x"
278                       << std::setw(2) << static_cast<uint32_t>(eventState)
279                       << " previousEventState 0x" << std::setw(2)
280                       << static_cast<uint32_t>(previousEventState)
281                       << " sensorDataSize 0x" << std::setw(2)
282                       << static_cast<uint32_t>(sensorDataSize)
283                       << " presentReading 0x" << std::setw(8)
284                       << static_cast<uint32_t>(presentReading) << std::dec;
285             description += strStream.str();
286 
287             sendJournalRedfish(description, logLevel);
288             break;
289     }
290     return PLDM_SUCCESS;
291 }
292 
293 int OemEventManager::processStateSensorEvent(pldm_tid_t tid, uint16_t sensorId,
294                                              const uint8_t* sensorData,
295                                              size_t sensorDataLength)
296 {
297     uint8_t sensorOffset = 0;
298     uint8_t eventState = 0;
299     uint8_t previousEventState = 0;
300 
301     auto rc =
302         decode_state_sensor_data(sensorData, sensorDataLength, &sensorOffset,
303                                  &eventState, &previousEventState);
304     if (rc)
305     {
306         lg2::error(
307             "Failed to decode stateSensorState event for terminus ID {TID}, error {RC}",
308             "TID", tid, "RC", rc);
309         return rc;
310     }
311 
312     std::string description;
313     std::stringstream strStream;
314     log_level logLevel = log_level::OK;
315 
316     description += "SENSOR_EVENT : STATE_SENSOR_STATE: ";
317     description += prefixMsgStrCreation(tid, sensorId);
318     strStream << std::setfill('0') << std::hex << "sensorOffset 0x"
319               << std::setw(2) << static_cast<uint32_t>(sensorOffset)
320               << "eventState 0x" << std::setw(2)
321               << static_cast<uint32_t>(eventState) << " previousEventState 0x"
322               << std::setw(2) << static_cast<uint32_t>(previousEventState)
323               << std::dec;
324     description += strStream.str();
325 
326     sendJournalRedfish(description, logLevel);
327 
328     return PLDM_SUCCESS;
329 }
330 
331 int OemEventManager::processSensorOpStateEvent(
332     pldm_tid_t tid, uint16_t sensorId, const uint8_t* sensorData,
333     size_t sensorDataLength)
334 {
335     uint8_t present_op_state = 0;
336     uint8_t previous_op_state = 0;
337 
338     auto rc = decode_sensor_op_data(sensorData, sensorDataLength,
339                                     &present_op_state, &previous_op_state);
340     if (rc)
341     {
342         lg2::error(
343             "Failed to decode sensorOpState event for terminus ID {TID}, error {RC}",
344             "TID", tid, "RC", rc);
345         return rc;
346     }
347 
348     std::string description;
349     std::stringstream strStream;
350     log_level logLevel = log_level::OK;
351 
352     description += "SENSOR_EVENT : SENSOR_OP_STATE: ";
353     description += prefixMsgStrCreation(tid, sensorId);
354     strStream << std::setfill('0') << std::hex << "present_op_state 0x"
355               << std::setw(2) << static_cast<uint32_t>(present_op_state)
356               << "previous_op_state 0x" << std::setw(2)
357               << static_cast<uint32_t>(previous_op_state) << std::dec;
358     description += strStream.str();
359 
360     sendJournalRedfish(description, logLevel);
361 
362     return PLDM_SUCCESS;
363 }
364 
365 int OemEventManager::handleSensorEvent(
366     const pldm_msg* request, size_t payloadLength, uint8_t /* formatVersion */,
367     pldm_tid_t tid, size_t eventDataOffset)
368 {
369     /* This OEM event handler is only used for SoC terminus*/
370     if (!tidToSocketNameMap.contains(tid))
371     {
372         return PLDM_SUCCESS;
373     }
374     auto eventData =
375         reinterpret_cast<const uint8_t*>(request->payload) + eventDataOffset;
376     auto eventDataSize = payloadLength - eventDataOffset;
377 
378     uint16_t sensorId = 0;
379     uint8_t sensorEventClassType = 0;
380     size_t eventClassDataOffset = 0;
381     auto rc =
382         decode_sensor_event_data(eventData, eventDataSize, &sensorId,
383                                  &sensorEventClassType, &eventClassDataOffset);
384     if (rc)
385     {
386         lg2::error("Failed to decode sensor event data return code {RC}.", "RC",
387                    rc);
388         return rc;
389     }
390     const uint8_t* sensorData = eventData + eventClassDataOffset;
391     size_t sensorDataLength = eventDataSize - eventClassDataOffset;
392 
393     switch (sensorEventClassType)
394     {
395         case PLDM_NUMERIC_SENSOR_STATE:
396         {
397             return processNumericSensorEvent(tid, sensorId, sensorData,
398                                              sensorDataLength);
399         }
400         case PLDM_STATE_SENSOR_STATE:
401         {
402             return processStateSensorEvent(tid, sensorId, sensorData,
403                                            sensorDataLength);
404         }
405         case PLDM_SENSOR_OP_STATE:
406         {
407             return processSensorOpStateEvent(tid, sensorId, sensorData,
408                                              sensorDataLength);
409         }
410         default:
411             std::string description;
412             std::stringstream strStream;
413             log_level logLevel = log_level::OK;
414 
415             description += "SENSOR_EVENT : Unsupported Sensor Class " +
416                            std::to_string(sensorEventClassType) + ": ";
417             description += prefixMsgStrCreation(tid, sensorId);
418             strStream << std::setfill('0') << std::hex
419                       << std::setw(sizeof(sensorData) * 2) << "Sensor data: ";
420 
421             auto dataPtr = sensorData;
422             for ([[maybe_unused]] const auto& i :
423                  std::views::iota(0, (int)sensorDataLength))
424             {
425                 strStream << "0x" << static_cast<uint32_t>(*dataPtr);
426                 dataPtr += sizeof(sensorData);
427             }
428 
429             description += strStream.str();
430 
431             sendJournalRedfish(description, logLevel);
432     }
433     lg2::info("Unsupported class type {CLASSTYPE}", "CLASSTYPE",
434               sensorEventClassType);
435     return PLDM_ERROR;
436 }
437 
438 void OemEventManager::handlePCIeHotPlugEvent(pldm_tid_t tid, uint16_t sensorId,
439                                              uint32_t presentReading)
440 {
441     std::string description;
442     std::stringstream strStream;
443     PCIeHotPlugEventRecord_t record{presentReading};
444 
445     std::string sAction = (!record.bits.action) ? "Insertion" : "Removal";
446     std::string sOpStatus = (!record.bits.opStatus) ? "Successful" : "Failed";
447     log_level logLevel =
448         (!record.bits.opStatus) ? log_level::OK : log_level::WARNING;
449 
450     description += prefixMsgStrCreation(tid, sensorId);
451 
452     strStream << "Segment (0x" << std::setfill('0') << std::hex << std::setw(2)
453               << static_cast<uint32_t>(record.bits.segment) << "); Bus (0x"
454               << std::setw(2) << static_cast<uint32_t>(record.bits.bus)
455               << "); Device (0x" << std::setw(2)
456               << static_cast<uint32_t>(record.bits.device) << "); Function (0x"
457               << std::setw(2) << static_cast<uint32_t>(record.bits.function)
458               << "); Action (" << sAction << "); Operation status ("
459               << sOpStatus << "); Media slot number (" << std::dec
460               << static_cast<uint32_t>(record.bits.mediaSlot) << ")";
461 
462     description += strStream.str();
463 
464     // Log to Redfish event
465     sendJournalRedfish(description, logLevel);
466 }
467 
468 } // namespace oem_ampere
469 } // namespace pldm
470