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 namespace ddr_status = ddr::status;
26 namespace dimm_status = dimm::status;
27 namespace dimm_syndrome = dimm::training_failure::dimm_syndrome;
28 namespace phy_syndrome = dimm::training_failure::phy_syndrome;
29 namespace training_failure = dimm::training_failure;
30 
31 constexpr const char* ampereEventRegistry = "OpenBMC.0.1.AmpereEvent.OK";
32 constexpr const char* ampereWarningRegistry =
33     "OpenBMC.0.1.AmpereWarning.Warning";
34 constexpr const char* ampereCriticalRegistry =
35     "OpenBMC.0.1.AmpereCritical.Critical";
36 constexpr const char* BIOSFWPanicRegistry =
37     "OpenBMC.0.1.BIOSFirmwarePanicReason.Warning";
38 constexpr auto maxDIMMIdxBitNum = 24;
39 constexpr auto maxDIMMInstantNum = 24;
40 
41 /*
42     An array of possible boot status of a boot stage.
43     The index maps with byte 0 of boot code.
44 */
45 std::array<std::string, 3> bootStatMsg = {" booting", " completed", " failed"};
46 
47 /*
48     An array of possible boot status of DDR training stage.
49     The index maps with byte 0 of boot code.
50 */
51 std::array<std::string, 3> ddrTrainingMsg = {
52     " progress started", " in-progress", " progress completed"};
53 
54 /*
55     A map between PMIC status and logging strings.
56 */
57 std::array<std::string, 8> pmicTempAlertMsg = {
58     "Below 85°C", "85°C",  "95°C",  "105°C",
59     "115°C",      "125°C", "135°C", "Equal or greater than 140°C"};
60 
61 /*
62     In Ampere systems, BMC only directly communicates with MCTP/PLDM SoC
63     EPs through SMBus and PCIe. When host boots up, SMBUS interface
64     comes up first. In this interface, BMC is bus owner.
65 
66     mctpd will set the EID 0x14 for S0 and 0x16 for S1 (if available).
67     pldmd will always use TID 1 for S0 and TID 2 for S1 (if available).
68 */
69 EventToMsgMap_t tidToSocketNameMap = {{1, "SOCKET 0"}, {2, "SOCKET 1"}};
70 
71 /*
72     A map between sensor IDs and their names in string.
73     Using pldm::oem::sensor_ids
74 */
75 EventToMsgMap_t sensorIdToStrMap = {{DDR_STATUS, "DDR_STATUS"},
76                                     {PCIE_HOT_PLUG, "PCIE_HOT_PLUG"},
77                                     {BOOT_OVERALL, "BOOT_OVERALL"}};
78 
79 /*
80     A map between the boot stages and logging strings.
81     Using pldm::oem::boot::stage::boot_stage
82 */
83 EventToMsgMap_t bootStageToMsgMap = {
84     {boot_stage::SECPRO, "SECpro"},
85     {boot_stage::MPRO, "Mpro"},
86     {boot_stage::ATF_BL1, "ATF BL1"},
87     {boot_stage::ATF_BL2, "ATF BL2"},
88     {boot_stage::DDR_INITIALIZATION, "DDR initialization"},
89     {boot_stage::DDR_TRAINING, "DDR training"},
90     {boot_stage::S0_DDR_TRAINING_FAILURE, "DDR training failure"},
91     {boot_stage::ATF_BL31, "ATF BL31"},
92     {boot_stage::ATF_BL32, "ATF BL32"},
93     {boot_stage::S1_DDR_TRAINING_FAILURE, "DDR training failure"},
94     {boot_stage::UEFI_STATUS_CLASS_CODE_MIN,
95      "ATF BL33 (UEFI) booting status = "}};
96 
97 /*
98     A map between DDR status and logging strings.
99     Using pldm::oem::ddr::status::ddr_status
100 */
101 EventToMsgMap_t ddrStatusToMsgMap = {
102     {ddr_status::NO_SYSTEM_LEVEL_ERROR, "has no system level error"},
103     {ddr_status::ECC_INITIALIZATION_FAILURE, "has ECC initialization failure"},
104     {ddr_status::CONFIGURATION_FAILURE, "has configuration failure at DIMMs:"},
105     {ddr_status::TRAINING_FAILURE, "has training failure at DIMMs:"},
106     {ddr_status::OTHER_FAILURE, "has other failure"},
107     {ddr_status::BOOT_FAILURE_NO_VALID_CONFIG,
108      "has boot failure due to no configuration"},
109     {ddr_status::FAILSAFE_ACTIVATED_NEXT_BOOT_SUCCESS,
110      "failsafe activated but boot success with the next valid configuration"}};
111 
112 /*
113     A map between DIMM status and logging strings.
114     Using pldm::oem::dimm::status::dimm_status
115 */
116 EventToMsgMap_t dimmStatusToMsgMap = {
117     {dimm_status::INSTALLED_NO_ERROR, "is installed and no error"},
118     {dimm_status::NOT_INSTALLED, "is not installed"},
119     {dimm_status::OTHER_FAILURE, "has other failure"},
120     {dimm_status::INSTALLED_BUT_DISABLED, "is installed but disabled"},
121     {dimm_status::TRAINING_FAILURE, "has training failure; "},
122     {dimm_status::PMIC_TEMP_ALERT, "has PMIC temperature alert"}};
123 
124 /*
125     A map between PHY training failure syndrome and logging strings.
126     Using
127    pldm::oem::dimm::training_faillure::phy_syndrome::phy_training_failure_syndrome
128 */
129 EventToMsgMap_t phyTrainingFailureSyndromeToMsgMap = {
130     {phy_syndrome::NA, "(N/A)"},
131     {phy_syndrome::PHY_TRAINING_SETUP_FAILURE, "(PHY training setup failure)"},
132     {phy_syndrome::CA_LEVELING, "(CA leveling)"},
133     {phy_syndrome::PHY_WRITE_LEVEL_FAILURE,
134      "(PHY write level failure - see syndrome 1)"},
135     {phy_syndrome::PHY_READ_GATE_LEVELING_FAILURE,
136      "(PHY read gate leveling failure)"},
137     {phy_syndrome::PHY_READ_LEVEL_FAILURE, "(PHY read level failure)"},
138     {phy_syndrome::WRITE_DQ_LEVELING, "(Write DQ leveling)"},
139     {phy_syndrome::PHY_SW_TRAINING_FAILURE, "(PHY SW training failure)"}};
140 
141 /*
142     A map between DIMM training failure syndrome and logging strings.
143     Using
144    pldm::oem::dimm::training_faillure::dimm_syndrome::dimm_training_failure_syndrome
145 */
146 EventToMsgMap_t dimmTrainingFailureSyndromeToMsgMap = {
147     {dimm_syndrome::NA, "(N/A)"},
148     {dimm_syndrome::DRAM_VREFDQ_TRAINING_FAILURE,
149      "(DRAM VREFDQ training failure)"},
150     {dimm_syndrome::LRDIMM_DB_TRAINING_FAILURE, "(LRDIMM DB training failure)"},
151     {dimm_syndrome::LRDRIMM_DB_SW_TRAINING_FAILURE,
152      "(LRDRIMM DB SW training failure)"}};
153 
154 /*
155     A map between DIMM training failure type and a pair of <logging strings -
156    syndrome map>. Using
157    pldm::oem::dimm::training_faillure::dimm_training_failure_type
158 */
159 std::unordered_map<uint8_t, std::pair<std::string, EventToMsgMap_t>>
160     dimmTrainingFailureTypeMap = {
161         {training_failure::PHY_TRAINING_FAILURE_TYPE,
162          std::make_pair("PHY training failure",
163                         phyTrainingFailureSyndromeToMsgMap)},
164         {training_failure::DIMM_TRAINING_FAILURE_TYPE,
165          std::make_pair("DIMM training failure",
166                         dimmTrainingFailureSyndromeToMsgMap)}};
167 
168 /*
169     A map between log level and the registry used for Redfish SEL log
170     Using pldm::oem::log_level
171 */
172 std::unordered_map<log_level, std::string> logLevelToRedfishMsgIdMap = {
173     {log_level::OK, ampereEventRegistry},
174     {log_level::WARNING, ampereWarningRegistry},
175     {log_level::CRITICAL, ampereCriticalRegistry},
176     {log_level::BIOSFWPANIC, BIOSFWPanicRegistry}};
177 
178 std::string
179     OemEventManager::prefixMsgStrCreation(pldm_tid_t tid, uint16_t sensorId)
180 {
181     std::string description;
182     if (!tidToSocketNameMap.contains(tid))
183     {
184         description += "TID " + std::to_string(tid) + ": ";
185     }
186     else
187     {
188         description += tidToSocketNameMap[tid] + ": ";
189     }
190 
191     if (!sensorIdToStrMap.contains(sensorId))
192     {
193         description += "Sensor ID " + std::to_string(sensorId) + ": ";
194     }
195     else
196     {
197         description += sensorIdToStrMap[sensorId] + ": ";
198     }
199 
200     return description;
201 }
202 
203 void OemEventManager::sendJournalRedfish(const std::string& description,
204                                          log_level& logLevel)
205 {
206     if (description.empty())
207     {
208         return;
209     }
210 
211     if (!logLevelToRedfishMsgIdMap.contains(logLevel))
212     {
213         lg2::error("Invalid {LEVEL} Description {DES}", "LEVEL", logLevel,
214                    "DES", description);
215         return;
216     }
217     auto redfishMsgId = logLevelToRedfishMsgIdMap[logLevel];
218     lg2::info("MESSAGE={DES}", "DES", description, "REDFISH_MESSAGE_ID",
219               redfishMsgId, "REDFISH_MESSAGE_ARGS", description);
220 }
221 
222 std::string OemEventManager::dimmIdxsToString(uint32_t dimmIdxs)
223 {
224     std::string description;
225     for (const auto bitIdx : std::views::iota(0, maxDIMMIdxBitNum))
226     {
227         if (dimmIdxs & (static_cast<uint32_t>(1) << bitIdx))
228         {
229             description += " #" + std::to_string(bitIdx);
230         }
231     }
232     return description;
233 }
234 
235 void OemEventManager::handleBootOverallEvent(
236     pldm_tid_t /*tid*/, uint16_t /*sensorId*/, uint32_t presentReading)
237 {
238     log_level logLevel{log_level::OK};
239     std::string description;
240     std::stringstream strStream;
241 
242     uint8_t byte0 = (presentReading & 0x000000ff);
243     uint8_t byte1 = (presentReading & 0x0000ff00) >> 8;
244     uint8_t byte2 = (presentReading & 0x00ff0000) >> 16;
245     uint8_t byte3 = (presentReading & 0xff000000) >> 24;
246     /*
247      * Handle SECpro, Mpro, ATF BL1, ATF BL2, ATF BL31,
248      * ATF BL32 and DDR initialization
249      */
250     if (bootStageToMsgMap.contains(byte3))
251     {
252         // Boot stage adding
253         description += bootStageToMsgMap[byte3];
254 
255         switch (byte3)
256         {
257             case boot_stage::DDR_TRAINING:
258                 if (byte0 >= ddrTrainingMsg.size())
259                 {
260                     logLevel = log_level::BIOSFWPANIC;
261                     description += " unknown status";
262                 }
263                 else
264                 {
265                     description += ddrTrainingMsg[byte0];
266                 }
267                 if (0x01 == byte0)
268                 {
269                     // Add complete percentage
270                     description += " at " + std::to_string(byte1) + "%";
271                 }
272                 break;
273             case boot_stage::S0_DDR_TRAINING_FAILURE:
274             case boot_stage::S1_DDR_TRAINING_FAILURE:
275                 // ddr_training_status_msg()
276                 logLevel = log_level::BIOSFWPANIC;
277                 description += " at DIMMs:";
278                 // dimmIdxs = presentReading & 0x00ffffff;
279                 description += dimmIdxsToString(presentReading & 0x00ffffff);
280                 description += " of socket ";
281                 description +=
282                     (boot_stage::S0_DDR_TRAINING_FAILURE == byte3) ? "0" : "1";
283                 break;
284             default:
285                 if (byte0 >= bootStatMsg.size())
286                 {
287                     logLevel = log_level::BIOSFWPANIC;
288                     description += " unknown status";
289                 }
290                 else
291                 {
292                     description += bootStatMsg[byte0];
293                 }
294                 break;
295         }
296 
297         // Sensor report action is fail
298         if (boot::status::BOOT_STATUS_FAILURE == byte2)
299         {
300             logLevel = log_level::BIOSFWPANIC;
301         }
302     }
303     else
304     {
305         if (byte3 <= boot_stage::UEFI_STATUS_CLASS_CODE_MAX)
306         {
307             description +=
308                 bootStageToMsgMap[boot_stage::UEFI_STATUS_CLASS_CODE_MIN];
309 
310             strStream
311                 << "Segment (0x" << std::setfill('0') << std::hex
312                 << std::setw(8) << static_cast<uint32_t>(presentReading)
313                 << "); Status Class (0x" << std::setw(2)
314                 << static_cast<uint32_t>(byte3) << "); Status SubClass (0x"
315                 << std::setw(2) << static_cast<uint32_t>(byte2)
316                 << "); Operation Code (0x" << std::setw(4)
317                 << static_cast<uint32_t>((presentReading & 0xffff0000) >> 16)
318                 << ")" << std::dec;
319 
320             description += strStream.str();
321         }
322     }
323 
324     // Log to Redfish event
325     sendJournalRedfish(description, logLevel);
326 }
327 
328 int OemEventManager::processNumericSensorEvent(
329     pldm_tid_t tid, uint16_t sensorId, const uint8_t* sensorData,
330     size_t sensorDataLength)
331 {
332     uint8_t eventState = 0;
333     uint8_t previousEventState = 0;
334     uint8_t sensorDataSize = 0;
335     uint32_t presentReading;
336     auto rc = decode_numeric_sensor_data(
337         sensorData, sensorDataLength, &eventState, &previousEventState,
338         &sensorDataSize, &presentReading);
339     if (rc)
340     {
341         lg2::error(
342             "Failed to decode numericSensorState event for terminus ID {TID}, error {RC} ",
343             "TID", tid, "RC", rc);
344         return rc;
345     }
346 
347     // DIMMx_Status sensorID 4+2*index (index 0 -> maxDIMMInstantNum-1)
348     if (auto dimmIdx = (sensorId - 4) / 2;
349         sensorId >= 4 && dimmIdx >= 0 && dimmIdx < maxDIMMInstantNum)
350     {
351         handleDIMMStatusEvent(tid, sensorId, presentReading);
352         return PLDM_SUCCESS;
353     }
354 
355     switch (sensorId)
356     {
357         case BOOT_OVERALL:
358             handleBootOverallEvent(tid, sensorId, presentReading);
359             break;
360         case PCIE_HOT_PLUG:
361             handlePCIeHotPlugEvent(tid, sensorId, presentReading);
362             break;
363         case DDR_STATUS:
364             handleDDRStatusEvent(tid, sensorId, presentReading);
365             break;
366         default:
367             std::string description;
368             std::stringstream strStream;
369             log_level logLevel = log_level::OK;
370 
371             description += "SENSOR_EVENT : NUMERIC_SENSOR_STATE: ";
372             description += prefixMsgStrCreation(tid, sensorId);
373             strStream << std::setfill('0') << std::hex << "eventState 0x"
374                       << std::setw(2) << static_cast<uint32_t>(eventState)
375                       << " previousEventState 0x" << std::setw(2)
376                       << static_cast<uint32_t>(previousEventState)
377                       << " sensorDataSize 0x" << std::setw(2)
378                       << static_cast<uint32_t>(sensorDataSize)
379                       << " presentReading 0x" << std::setw(8)
380                       << static_cast<uint32_t>(presentReading) << std::dec;
381             description += strStream.str();
382 
383             sendJournalRedfish(description, logLevel);
384             break;
385     }
386     return PLDM_SUCCESS;
387 }
388 
389 int OemEventManager::processStateSensorEvent(pldm_tid_t tid, uint16_t sensorId,
390                                              const uint8_t* sensorData,
391                                              size_t sensorDataLength)
392 {
393     uint8_t sensorOffset = 0;
394     uint8_t eventState = 0;
395     uint8_t previousEventState = 0;
396 
397     auto rc =
398         decode_state_sensor_data(sensorData, sensorDataLength, &sensorOffset,
399                                  &eventState, &previousEventState);
400     if (rc)
401     {
402         lg2::error(
403             "Failed to decode stateSensorState event for terminus ID {TID}, error {RC}",
404             "TID", tid, "RC", rc);
405         return rc;
406     }
407 
408     std::string description;
409     std::stringstream strStream;
410     log_level logLevel = log_level::OK;
411 
412     description += "SENSOR_EVENT : STATE_SENSOR_STATE: ";
413     description += prefixMsgStrCreation(tid, sensorId);
414     strStream << std::setfill('0') << std::hex << "sensorOffset 0x"
415               << std::setw(2) << static_cast<uint32_t>(sensorOffset)
416               << "eventState 0x" << std::setw(2)
417               << static_cast<uint32_t>(eventState) << " previousEventState 0x"
418               << std::setw(2) << static_cast<uint32_t>(previousEventState)
419               << std::dec;
420     description += strStream.str();
421 
422     sendJournalRedfish(description, logLevel);
423 
424     return PLDM_SUCCESS;
425 }
426 
427 int OemEventManager::processSensorOpStateEvent(
428     pldm_tid_t tid, uint16_t sensorId, const uint8_t* sensorData,
429     size_t sensorDataLength)
430 {
431     uint8_t present_op_state = 0;
432     uint8_t previous_op_state = 0;
433 
434     auto rc = decode_sensor_op_data(sensorData, sensorDataLength,
435                                     &present_op_state, &previous_op_state);
436     if (rc)
437     {
438         lg2::error(
439             "Failed to decode sensorOpState event for terminus ID {TID}, error {RC}",
440             "TID", tid, "RC", rc);
441         return rc;
442     }
443 
444     std::string description;
445     std::stringstream strStream;
446     log_level logLevel = log_level::OK;
447 
448     description += "SENSOR_EVENT : SENSOR_OP_STATE: ";
449     description += prefixMsgStrCreation(tid, sensorId);
450     strStream << std::setfill('0') << std::hex << "present_op_state 0x"
451               << std::setw(2) << static_cast<uint32_t>(present_op_state)
452               << "previous_op_state 0x" << std::setw(2)
453               << static_cast<uint32_t>(previous_op_state) << std::dec;
454     description += strStream.str();
455 
456     sendJournalRedfish(description, logLevel);
457 
458     return PLDM_SUCCESS;
459 }
460 
461 int OemEventManager::handleSensorEvent(
462     const pldm_msg* request, size_t payloadLength, uint8_t /* formatVersion */,
463     pldm_tid_t tid, size_t eventDataOffset)
464 {
465     /* This OEM event handler is only used for SoC terminus*/
466     if (!tidToSocketNameMap.contains(tid))
467     {
468         return PLDM_SUCCESS;
469     }
470     auto eventData =
471         reinterpret_cast<const uint8_t*>(request->payload) + eventDataOffset;
472     auto eventDataSize = payloadLength - eventDataOffset;
473 
474     uint16_t sensorId = 0;
475     uint8_t sensorEventClassType = 0;
476     size_t eventClassDataOffset = 0;
477     auto rc =
478         decode_sensor_event_data(eventData, eventDataSize, &sensorId,
479                                  &sensorEventClassType, &eventClassDataOffset);
480     if (rc)
481     {
482         lg2::error("Failed to decode sensor event data return code {RC}.", "RC",
483                    rc);
484         return rc;
485     }
486     const uint8_t* sensorData = eventData + eventClassDataOffset;
487     size_t sensorDataLength = eventDataSize - eventClassDataOffset;
488 
489     switch (sensorEventClassType)
490     {
491         case PLDM_NUMERIC_SENSOR_STATE:
492         {
493             return processNumericSensorEvent(tid, sensorId, sensorData,
494                                              sensorDataLength);
495         }
496         case PLDM_STATE_SENSOR_STATE:
497         {
498             return processStateSensorEvent(tid, sensorId, sensorData,
499                                            sensorDataLength);
500         }
501         case PLDM_SENSOR_OP_STATE:
502         {
503             return processSensorOpStateEvent(tid, sensorId, sensorData,
504                                              sensorDataLength);
505         }
506         default:
507             std::string description;
508             std::stringstream strStream;
509             log_level logLevel = log_level::OK;
510 
511             description += "SENSOR_EVENT : Unsupported Sensor Class " +
512                            std::to_string(sensorEventClassType) + ": ";
513             description += prefixMsgStrCreation(tid, sensorId);
514             strStream << std::setfill('0') << std::hex
515                       << std::setw(sizeof(sensorData) * 2) << "Sensor data: ";
516 
517             auto dataPtr = sensorData;
518             for ([[maybe_unused]] const auto& i :
519                  std::views::iota(0, (int)sensorDataLength))
520             {
521                 strStream << "0x" << static_cast<uint32_t>(*dataPtr);
522                 dataPtr += sizeof(sensorData);
523             }
524 
525             description += strStream.str();
526 
527             sendJournalRedfish(description, logLevel);
528     }
529     lg2::info("Unsupported class type {CLASSTYPE}", "CLASSTYPE",
530               sensorEventClassType);
531     return PLDM_ERROR;
532 }
533 
534 void OemEventManager::handlePCIeHotPlugEvent(pldm_tid_t tid, uint16_t sensorId,
535                                              uint32_t presentReading)
536 {
537     std::string description;
538     std::stringstream strStream;
539     PCIeHotPlugEventRecord_t record{presentReading};
540 
541     std::string sAction = (!record.bits.action) ? "Insertion" : "Removal";
542     std::string sOpStatus = (!record.bits.opStatus) ? "Successful" : "Failed";
543     log_level logLevel =
544         (!record.bits.opStatus) ? log_level::OK : log_level::WARNING;
545 
546     description += prefixMsgStrCreation(tid, sensorId);
547 
548     strStream << "Segment (0x" << std::setfill('0') << std::hex << std::setw(2)
549               << static_cast<uint32_t>(record.bits.segment) << "); Bus (0x"
550               << std::setw(2) << static_cast<uint32_t>(record.bits.bus)
551               << "); Device (0x" << std::setw(2)
552               << static_cast<uint32_t>(record.bits.device) << "); Function (0x"
553               << std::setw(2) << static_cast<uint32_t>(record.bits.function)
554               << "); Action (" << sAction << "); Operation status ("
555               << sOpStatus << "); Media slot number (" << std::dec
556               << static_cast<uint32_t>(record.bits.mediaSlot) << ")";
557 
558     description += strStream.str();
559 
560     // Log to Redfish event
561     sendJournalRedfish(description, logLevel);
562 }
563 
564 std::string OemEventManager::dimmTrainingFailureToMsg(uint32_t failureInfo)
565 {
566     std::string description;
567     DIMMTrainingFailure_t failure{failureInfo};
568 
569     if (dimmTrainingFailureTypeMap.contains(failure.bits.type))
570     {
571         auto failureInfoMap = dimmTrainingFailureTypeMap[failure.bits.type];
572 
573         description += std::get<0>(failureInfoMap);
574 
575         description += "; MCU rank index " +
576                        std::to_string(failure.bits.mcuRankIdx);
577 
578         description += "; Slice number " +
579                        std::to_string(failure.bits.sliceNum);
580 
581         description += "; Upper nibble error status: ";
582         description += (!failure.bits.upperNibbStatErr)
583                            ? "No error"
584                            : "Found no rising edge";
585 
586         description += "; Lower nibble error status: ";
587         description += (!failure.bits.lowerNibbStatErr)
588                            ? "No error"
589                            : "Found no rising edge";
590 
591         description += "; Failure syndrome 0: ";
592 
593         auto& syndromeMap = std::get<1>(failureInfoMap);
594         if (syndromeMap.contains(failure.bits.syndrome))
595         {
596             description += syndromeMap[failure.bits.syndrome];
597         }
598         else
599         {
600             description += "(Unknown syndrome)";
601         }
602     }
603     else
604     {
605         description += "Unknown training failure type " +
606                        std::to_string(failure.bits.type);
607     }
608 
609     return description;
610 }
611 
612 void OemEventManager::handleDIMMStatusEvent(pldm_tid_t tid, uint16_t sensorId,
613                                             uint32_t presentReading)
614 {
615     log_level logLevel{log_level::WARNING};
616     std::string description;
617     uint8_t byte3 = (presentReading & 0xff000000) >> 24;
618     uint32_t byte012 = presentReading & 0xffffff;
619 
620     description += prefixMsgStrCreation(tid, sensorId);
621 
622     uint8_t dimmIdx = (sensorId - 4) / 2;
623 
624     description += "DIMM " + std::to_string(dimmIdx) + " ";
625 
626     if (dimmStatusToMsgMap.contains(byte3))
627     {
628         if (byte3 == dimm_status::INSTALLED_NO_ERROR ||
629             byte3 == dimm_status::INSTALLED_BUT_DISABLED)
630         {
631             logLevel = log_level::OK;
632         }
633 
634         description += dimmStatusToMsgMap[byte3];
635 
636         if (byte3 == dimm_status::TRAINING_FAILURE)
637         {
638             description += "; " + dimmTrainingFailureToMsg(byte012);
639         }
640         else if (byte3 == dimm_status::PMIC_TEMP_ALERT)
641         {
642             uint8_t byte0 = (byte012 & 0xff);
643             if (byte0 < pmicTempAlertMsg.size())
644             {
645                 description += ": " + pmicTempAlertMsg[byte0];
646             }
647         }
648     }
649     else
650     {
651         switch (byte3)
652         {
653             case dimm_status::PMIC_HIGH_TEMP:
654                 if (byte012 == 0x01)
655                 {
656                     description += "has PMIC high temp condition";
657                 }
658                 break;
659             case dimm_status::TSx_HIGH_TEMP:
660                 switch (byte012)
661                 {
662                     case 0x01:
663                         description += "has TS0";
664                         break;
665                     case 0x02:
666                         description += "has TS1";
667                         break;
668                     case 0x03:
669                         description += "has TS0 and TS1";
670                         break;
671                 }
672                 description += " exceeding their high temperature threshold";
673                 break;
674             case dimm_status::SPD_HUB_HIGH_TEMP:
675                 if (byte012 == 0x01)
676                 {
677                     description += "has SPD/HUB high temp condition";
678                 }
679                 break;
680             default:
681                 description += "has unsupported status " +
682                                std::to_string(byte3);
683                 break;
684         }
685     }
686 
687     // Log to Redfish event
688     sendJournalRedfish(description, logLevel);
689 }
690 
691 void OemEventManager::handleDDRStatusEvent(pldm_tid_t tid, uint16_t sensorId,
692                                            uint32_t presentReading)
693 {
694     log_level logLevel{log_level::WARNING};
695     std::string description;
696     uint8_t byte3 = (presentReading & 0xff000000) >> 24;
697     uint32_t byte012 = presentReading & 0xffffff;
698 
699     description += prefixMsgStrCreation(tid, sensorId);
700 
701     description += "DDR ";
702     if (ddrStatusToMsgMap.contains(byte3))
703     {
704         if (byte3 == ddr_status::NO_SYSTEM_LEVEL_ERROR)
705         {
706             logLevel = log_level::OK;
707         }
708 
709         description += ddrStatusToMsgMap[byte3];
710 
711         if (byte3 == ddr_status::CONFIGURATION_FAILURE ||
712             byte3 == ddr_status::TRAINING_FAILURE)
713         {
714             // List out failed DIMMs
715             description += dimmIdxsToString(byte012);
716         }
717     }
718     else
719     {
720         description += "has unsupported status " + std::to_string(byte3);
721     }
722 
723     // Log to Redfish event
724     sendJournalRedfish(description, logLevel);
725 }
726 
727 } // namespace oem_ampere
728 } // namespace pldm
729