xref: /openbmc/fb-ipmi-oem/src/selcommands.cpp (revision d17c356e)
1 /*
2  * Copyright (c)  2018 Intel Corporation.
3  * Copyright (c)  2018-present Facebook.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include <boost/algorithm/string/join.hpp>
19 #include <boost/container/flat_map.hpp>
20 #include <ipmid/api.hpp>
21 #include <nlohmann/json.hpp>
22 #include <phosphor-logging/log.hpp>
23 #include <sdbusplus/message/types.hpp>
24 #include <sdbusplus/timer.hpp>
25 #include <storagecommands.hpp>
26 
27 #include <fstream>
28 #include <iostream>
29 #include <sstream>
30 
31 enum class MemErrType
32 {
33     memTrainErr = 0,
34     memPmicErr = 7
35 };
36 
37 enum class PostEvtType
38 {
39     pxeBootFail = 0,
40     httpBootFail = 6,
41     getCertFail = 7,
42     amdAblFail = 10
43 };
44 
45 enum class PcieEvtType
46 {
47     dpc = 0
48 };
49 
50 enum class MemEvtType
51 {
52     ppr = 0,
53     adddc = 5,
54     noDimm = 7
55 };
56 
57 //----------------------------------------------------------------------
58 // Platform specific functions for storing app data
59 //----------------------------------------------------------------------
60 
61 static std::string byteToStr(uint8_t byte)
62 {
63     std::stringstream ss;
64 
65     ss << std::hex << std::uppercase << std::setfill('0');
66     ss << std::setw(2) << (int)byte;
67 
68     return ss.str();
69 }
70 
71 static void toHexStr(std::vector<uint8_t>& bytes, std::string& hexStr)
72 {
73     std::stringstream stream;
74     stream << std::hex << std::uppercase << std::setfill('0');
75     for (const uint8_t byte : bytes)
76     {
77         stream << std::setw(2) << static_cast<int>(byte);
78     }
79     hexStr = stream.str();
80 }
81 
82 static int fromHexStr(const std::string hexStr, std::vector<uint8_t>& data)
83 {
84     for (unsigned int i = 0; i < hexStr.size(); i += 2)
85     {
86         try
87         {
88             data.push_back(static_cast<uint8_t>(
89                 std::stoul(hexStr.substr(i, 2), nullptr, 16)));
90         }
91         catch (const std::invalid_argument& e)
92         {
93             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
94             return -1;
95         }
96         catch (const std::out_of_range& e)
97         {
98             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
99             return -1;
100         }
101     }
102     return 0;
103 }
104 
105 namespace fb_oem::ipmi::sel
106 {
107 
108 class SELData
109 {
110   private:
111     nlohmann::json selDataObj;
112 
113     void flush()
114     {
115         std::ofstream file(SEL_JSON_DATA_FILE);
116         file << selDataObj;
117         file.close();
118     }
119 
120     void init()
121     {
122         selDataObj[KEY_SEL_VER] = 0x51;
123         selDataObj[KEY_SEL_COUNT] = 0;
124         selDataObj[KEY_ADD_TIME] = 0xFFFFFFFF;
125         selDataObj[KEY_ERASE_TIME] = 0xFFFFFFFF;
126         selDataObj[KEY_OPER_SUPP] = 0x02;
127         /* Spec indicates that more than 64kB is free */
128         selDataObj[KEY_FREE_SPACE] = 0xFFFF;
129     }
130 
131   public:
132     SELData()
133     {
134         /* Get App data stored in json file */
135         std::ifstream file(SEL_JSON_DATA_FILE);
136         if (file)
137         {
138             file >> selDataObj;
139             file.close();
140         }
141 
142         /* Initialize SelData object if no entries. */
143         if (selDataObj.find(KEY_SEL_COUNT) == selDataObj.end())
144         {
145             init();
146         }
147     }
148 
149     int clear()
150     {
151         /* Clear the complete Sel Json object */
152         selDataObj.clear();
153         /* Reinitialize it with basic data */
154         init();
155         /* Save the erase time */
156         struct timespec selTime = {};
157         if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
158         {
159             return -1;
160         }
161         selDataObj[KEY_ERASE_TIME] = selTime.tv_sec;
162         flush();
163         return 0;
164     }
165 
166     uint32_t getCount()
167     {
168         return selDataObj[KEY_SEL_COUNT];
169     }
170 
171     void getInfo(GetSELInfoData& info)
172     {
173         info.selVersion = selDataObj[KEY_SEL_VER];
174         info.entries = selDataObj[KEY_SEL_COUNT];
175         info.freeSpace = selDataObj[KEY_FREE_SPACE];
176         info.addTimeStamp = selDataObj[KEY_ADD_TIME];
177         info.eraseTimeStamp = selDataObj[KEY_ERASE_TIME];
178         info.operationSupport = selDataObj[KEY_OPER_SUPP];
179     }
180 
181     int getEntry(uint32_t index, std::string& rawStr)
182     {
183         std::stringstream ss;
184         ss << std::hex;
185         ss << std::setw(2) << std::setfill('0') << index;
186 
187         /* Check or the requested SEL Entry, if record is available */
188         if (selDataObj.find(ss.str()) == selDataObj.end())
189         {
190             return -1;
191         }
192 
193         rawStr = selDataObj[ss.str()][KEY_SEL_ENTRY_RAW];
194         return 0;
195     }
196 
197     int addEntry(std::string keyStr)
198     {
199         struct timespec selTime = {};
200 
201         if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
202         {
203             return -1;
204         }
205 
206         selDataObj[KEY_ADD_TIME] = selTime.tv_sec;
207 
208         int selCount = selDataObj[KEY_SEL_COUNT];
209         selDataObj[KEY_SEL_COUNT] = ++selCount;
210 
211         std::stringstream ss;
212         ss << std::hex;
213         ss << std::setw(2) << std::setfill('0') << selCount;
214 
215         selDataObj[ss.str()][KEY_SEL_ENTRY_RAW] = keyStr;
216         flush();
217         return selCount;
218     }
219 };
220 
221 /*
222  * A Function to parse common SEL message, a helper function
223  * for parseStdSel.
224  *
225  * Note that this function __CANNOT__ be overridden.
226  * To add board specific routine, please override parseStdSel.
227  */
228 
229 /*Used by decoding ME event*/
230 std::vector<std::string> nmDomName = {
231     "Entire Platform",          "CPU Subsystem",
232     "Memory Subsystem",         "HW Protection",
233     "High Power I/O subsystem", "Unknown"};
234 
235 /* Default log message for unknown type */
236 static void logDefault(uint8_t*, std::string& errLog)
237 {
238     errLog = "Unknown";
239 }
240 
241 static void logSysEvent(uint8_t* data, std::string& errLog)
242 {
243     if (data[0] == 0xE5)
244     {
245         errLog = "Cause of Time change - ";
246         switch (data[2])
247         {
248             case 0x00:
249                 errLog += "NTP";
250                 break;
251             case 0x01:
252                 errLog += "Host RTL";
253                 break;
254             case 0x02:
255                 errLog += "Set SEL time cmd";
256                 break;
257             case 0x03:
258                 errLog += "Set SEL time UTC offset cmd";
259                 break;
260             default:
261                 errLog += "Unknown";
262         }
263 
264         if (data[1] == 0x00)
265             errLog += " - First Time";
266         else if (data[1] == 0x80)
267             errLog += " - Second Time";
268     }
269     else
270     {
271         errLog = "Unknown";
272     }
273 }
274 
275 static void logThermalEvent(uint8_t* data, std::string& errLog)
276 {
277     if (data[0] == 0x1)
278     {
279         errLog = "Limit Exceeded";
280     }
281     else
282     {
283         errLog = "Unknown";
284     }
285 }
286 
287 static void logCritIrq(uint8_t* data, std::string& errLog)
288 {
289     if (data[0] == 0x0)
290     {
291         errLog = "NMI / Diagnostic Interrupt";
292     }
293     else if (data[0] == 0x03)
294     {
295         errLog = "Software NMI";
296     }
297     else
298     {
299         errLog = "Unknown";
300     }
301 
302     /* TODO: Call add_cri_sel for CRITICAL_IRQ */
303 }
304 
305 static void logPostErr(uint8_t* data, std::string& errLog)
306 {
307     if ((data[0] & 0x0F) == 0x0)
308     {
309         errLog = "System Firmware Error";
310     }
311     else
312     {
313         errLog = "Unknown";
314     }
315 
316     if (((data[0] >> 6) & 0x03) == 0x3)
317     {
318         // TODO: Need to implement IPMI spec based Post Code
319         errLog += ", IPMI Post Code";
320     }
321     else if (((data[0] >> 6) & 0x03) == 0x2)
322     {
323         errLog += ", OEM Post Code 0x" + byteToStr(data[2]) +
324                   byteToStr(data[1]);
325 
326         switch ((data[2] << 8) | data[1])
327         {
328             case 0xA105:
329                 errLog += ", BMC Failed (No Response)";
330                 break;
331             case 0xA106:
332                 errLog += ", BMC Failed (Self Test Fail)";
333                 break;
334             case 0xA10A:
335                 errLog += ", System Firmware Corruption Detected";
336                 break;
337             case 0xA10B:
338                 errLog += ", TPM Self-Test FAIL Detected";
339         }
340     }
341 }
342 
343 static void logMchChkErr(uint8_t* data, std::string& errLog)
344 {
345     /* TODO: Call add_cri_sel for CRITICAL_IRQ */
346     switch (data[0] & 0x0F)
347     {
348         case 0x0B:
349             switch ((data[1] >> 5) & 0x03)
350             {
351                 case 0x00:
352                     errLog = "Uncorrected Recoverable Error";
353                     break;
354                 case 0x01:
355                     errLog = "Uncorrected Thread Fatal Error";
356                     break;
357                 case 0x02:
358                     errLog = "Uncorrected System Fatal Error";
359                     break;
360                 default:
361                     errLog = "Unknown";
362             }
363             break;
364         case 0x0C:
365             switch ((data[1] >> 5) & 0x03)
366             {
367                 case 0x00:
368                     errLog = "Correctable Error";
369                     break;
370                 case 0x01:
371                     errLog = "Deferred Error";
372                     break;
373                 default:
374                     errLog = "Unknown";
375             }
376             break;
377         default:
378             errLog = "Unknown";
379     }
380 
381     errLog += ", Machine Check bank Number " + std::to_string(data[1]) +
382               ", CPU " + std::to_string(data[2] >> 5) + ", Core " +
383               std::to_string(data[2] & 0x1F);
384 }
385 
386 static void logPcieErr(uint8_t* data, std::string& errLog)
387 {
388     std::stringstream tmp1, tmp2;
389     tmp1 << std::hex << std::uppercase << std::setfill('0');
390     tmp2 << std::hex << std::uppercase << std::setfill('0');
391     tmp1 << " (Bus " << std::setw(2) << (int)(data[2]) << " / Dev "
392          << std::setw(2) << (int)(data[1] >> 3) << " / Fun " << std::setw(2)
393          << (int)(data[1] & 0x7) << ")";
394 
395     switch (data[0] & 0xF)
396     {
397         case 0x4:
398             errLog = "PCI PERR" + tmp1.str();
399             break;
400         case 0x5:
401             errLog = "PCI SERR" + tmp1.str();
402             break;
403         case 0x7:
404             errLog = "Correctable" + tmp1.str();
405             break;
406         case 0x8:
407             errLog = "Uncorrectable" + tmp1.str();
408             break;
409         case 0xA:
410             errLog = "Bus Fatal" + tmp1.str();
411             break;
412         case 0xD:
413         {
414             uint32_t venId = (uint32_t)data[1] << 8 | (uint32_t)data[2];
415             tmp2 << "Vendor ID: 0x" << std::setw(4) << venId;
416             errLog = tmp2.str();
417         }
418         break;
419         case 0xE:
420         {
421             uint32_t devId = (uint32_t)data[1] << 8 | (uint32_t)data[2];
422             tmp2 << "Device ID: 0x" << std::setw(4) << devId;
423             errLog = tmp2.str();
424         }
425         break;
426         case 0xF:
427             tmp2 << "Error ID from downstream: 0x" << std::setw(2)
428                  << (int)(data[1]) << std::setw(2) << (int)(data[2]);
429             errLog = tmp2.str();
430             break;
431         default:
432             errLog = "Unknown";
433     }
434 }
435 
436 static void logIioErr(uint8_t* data, std::string& errLog)
437 {
438     std::vector<std::string> tmpStr = {
439         "IRP0", "IRP1", " IIO-Core", "VT-d", "Intel Quick Data",
440         "Misc", " DMA", "ITC",       "OTC",  "CI"};
441 
442     if ((data[0] & 0xF) == 0)
443     {
444         errLog += "CPU " + std::to_string(data[2] >> 5) + ", Error ID 0x" +
445                   byteToStr(data[1]) + " - ";
446 
447         if ((data[2] & 0xF) <= 0x9)
448         {
449             errLog += tmpStr[(data[2] & 0xF)];
450         }
451         else
452         {
453             errLog += "Reserved";
454         }
455     }
456     else
457     {
458         errLog = "Unknown";
459     }
460 }
461 
462 [[maybe_unused]] static void logMemErr(uint8_t* dataPtr, std::string& errLog)
463 {
464     uint8_t snrType = dataPtr[0];
465     uint8_t snrNum = dataPtr[1];
466     uint8_t* data = &(dataPtr[3]);
467 
468     /* TODO: add pal_add_cri_sel */
469 
470     if (snrNum == memoryEccError)
471     {
472         /* SEL from MEMORY_ECC_ERR Sensor */
473         switch (data[0] & 0x0F)
474         {
475             case 0x0:
476                 if (snrType == 0x0C)
477                 {
478                     errLog = "Correctable";
479                 }
480                 else if (snrType == 0x10)
481                 {
482                     errLog = "Correctable ECC error Logging Disabled";
483                 }
484                 break;
485             case 0x1:
486                 errLog = "Uncorrectable";
487                 break;
488             case 0x5:
489                 errLog = "Correctable ECC error Logging Limit Disabled";
490                 break;
491             default:
492                 errLog = "Unknown";
493         }
494     }
495     else if (snrNum == memoryErrLogDIS)
496     {
497         // SEL from MEMORY_ERR_LOG_DIS Sensor
498         if ((data[0] & 0x0F) == 0x0)
499         {
500             errLog = "Correctable Memory Error Logging Disabled";
501         }
502         else
503         {
504             errLog = "Unknown";
505         }
506     }
507     else
508     {
509         errLog = "Unknown";
510         return;
511     }
512 
513     /* Common routine for both MEM_ECC_ERR and MEMORY_ERR_LOG_DIS */
514 
515     errLog += " (DIMM " + byteToStr(data[2]) + ") Logical Rank " +
516               std::to_string(data[1] & 0x03);
517 
518     /* DIMM number (data[2]):
519      * Bit[7:5]: Socket number  (Range: 0-7)
520      * Bit[4:3]: Channel number (Range: 0-3)
521      * Bit[2:0]: DIMM number    (Range: 0-7)
522      */
523 
524     /* TODO: Verify these bits */
525     std::string cpuStr = "CPU# " + std::to_string((data[2] & 0xE0) >> 5);
526     std::string chStr = "CHN# " + std::to_string((data[2] & 0x18) >> 3);
527     std::string dimmStr = "DIMM#" + std::to_string(data[2] & 0x7);
528 
529     switch ((data[1] & 0xC) >> 2)
530     {
531         case 0x0:
532         {
533             /* All Info Valid */
534             [[maybe_unused]] uint8_t chnNum = (data[2] & 0x1C) >> 2;
535             [[maybe_unused]] uint8_t dimmNum = data[2] & 0x3;
536 
537             /* TODO: If critical SEL logging is available, do it */
538             if (snrType == 0x0C)
539             {
540                 if ((data[0] & 0x0F) == 0x0)
541                 {
542                     /* TODO: add_cri_sel */
543                     /* "DIMM"+ 'A'+ chnNum + dimmNum + " ECC err,FRU:1"
544                      */
545                 }
546                 else if ((data[0] & 0x0F) == 0x1)
547                 {
548                     /* TODO: add_cri_sel */
549                     /* "DIMM"+ 'A'+ chnNum + dimmNum + " UECC err,FRU:1"
550                      */
551                 }
552             }
553             /* Continue to parse the error into a string. All Info Valid
554              */
555             errLog += " (" + cpuStr + ", " + chStr + ", " + dimmStr + ")";
556         }
557 
558         break;
559         case 0x1:
560 
561             /* DIMM info not valid */
562             errLog += " (" + cpuStr + ", " + chStr + ")";
563             break;
564         case 0x2:
565 
566             /* CHN info not valid */
567             errLog += " (" + cpuStr + ", " + dimmStr + ")";
568             break;
569         case 0x3:
570 
571             /* CPU info not valid */
572             errLog += " (" + chStr + ", " + dimmStr + ")";
573             break;
574     }
575 }
576 
577 static void logPwrErr(uint8_t* data, std::string& errLog)
578 {
579     if (data[0] == 0x1)
580     {
581         errLog = "SYS_PWROK failure";
582         /* Also try logging to Critical log file, if available */
583         /* "SYS_PWROK failure,FRU:1" */
584     }
585     else if (data[0] == 0x2)
586     {
587         errLog = "PCH_PWROK failure";
588         /* Also try logging to Critical log file, if available */
589         /* "PCH_PWROK failure,FRU:1" */
590     }
591     else
592     {
593         errLog = "Unknown";
594     }
595 }
596 
597 static void logCatErr(uint8_t* data, std::string& errLog)
598 {
599     if (data[0] == 0x0)
600     {
601         errLog = "IERR/CATERR";
602         /* Also try logging to Critical log file, if available */
603         /* "IERR,FRU:1 */
604     }
605     else if (data[0] == 0xB)
606     {
607         errLog = "MCERR/CATERR";
608         /* Also try logging to Critical log file, if available */
609         /* "MCERR,FRU:1 */
610     }
611     else
612     {
613         errLog = "Unknown";
614     }
615 }
616 
617 static void logDimmHot(uint8_t* data, std::string& errLog)
618 {
619     if ((data[0] << 16 | data[1] << 8 | data[2]) == 0x01FFFF)
620     {
621         errLog = "SOC MEMHOT";
622     }
623     else
624     {
625         errLog = "Unknown";
626         /* Also try logging to Critical log file, if available */
627         /* ""CPU_DIMM_HOT %s,FRU:1" */
628     }
629 }
630 
631 static void logSwNMI(uint8_t* data, std::string& errLog)
632 {
633     if ((data[0] << 16 | data[1] << 8 | data[2]) == 0x03FFFF)
634     {
635         errLog = "Software NMI";
636     }
637     else
638     {
639         errLog = "Unknown SW NMI";
640     }
641 }
642 
643 static void logCPUThermalSts(uint8_t* data, std::string& errLog)
644 {
645     switch (data[0])
646     {
647         case 0x0:
648             errLog = "CPU Critical Temperature";
649             break;
650         case 0x1:
651             errLog = "PROCHOT#";
652             break;
653         case 0x2:
654             errLog = "TCC Activation";
655             break;
656         default:
657             errLog = "Unknown";
658     }
659 }
660 
661 static void logMEPwrState(uint8_t* data, std::string& errLog)
662 {
663     switch (data[0])
664     {
665         case 0:
666             errLog = "RUNNING";
667             break;
668         case 2:
669             errLog = "POWER_OFF";
670             break;
671         default:
672             errLog = "Unknown[" + std::to_string(data[0]) + "]";
673             break;
674     }
675 }
676 
677 static void logSPSFwHealth(uint8_t* data, std::string& errLog)
678 {
679     if ((data[0] & 0x0F) == 0x00)
680     {
681         const std::vector<std::string> tmpStr = {
682             "Recovery GPIO forced",
683             "Image execution failed",
684             "Flash erase error",
685             "Flash state information",
686             "Internal error",
687             "BMC did not respond",
688             "Direct Flash update",
689             "Manufacturing error",
690             "Automatic Restore to Factory Presets",
691             "Firmware Exception",
692             "Flash Wear-Out Protection Warning",
693             "Unknown",
694             "Unknown",
695             "DMI interface error",
696             "MCTP interface error",
697             "Auto-configuration finished",
698             "Unsupported Segment Defined Feature",
699             "Unknown",
700             "CPU Debug Capability Disabled",
701             "UMA operation error"};
702 
703         if (data[1] < 0x14)
704         {
705             errLog = tmpStr[data[1]];
706         }
707         else
708         {
709             errLog = "Unknown";
710         }
711     }
712     else if ((data[0] & 0x0F) == 0x01)
713     {
714         errLog = "SMBus link failure";
715     }
716     else
717     {
718         errLog = "Unknown";
719     }
720 }
721 
722 static void logNmExcA(uint8_t* data, std::string& errLog)
723 {
724     /*NM4.0 #550710, Revision 1.95, and turn to p.155*/
725     if (data[0] == 0xA8)
726     {
727         errLog = "Policy Correction Time Exceeded";
728     }
729     else
730     {
731         errLog = "Unknown";
732     }
733 }
734 
735 static void logPCHThermal(uint8_t* data, std::string& errLog)
736 {
737     const std::vector<std::string> thresEvtName = {
738         "Lower Non-critical",
739         "Unknown",
740         "Lower Critical",
741         "Unknown",
742         "Lower Non-recoverable",
743         "Unknown",
744         "Unknown",
745         "Upper Non-critical",
746         "Unknown",
747         "Upper Critical",
748         "Unknown",
749         "Upper Non-recoverable"};
750 
751     if ((data[0] & 0x0f) < 12)
752     {
753         errLog = thresEvtName[(data[0] & 0x0f)];
754     }
755     else
756     {
757         errLog = "Unknown";
758     }
759 
760     errLog += ", curr_val: " + std::to_string(data[1]) +
761               " C, thresh_val: " + std::to_string(data[2]) + " C";
762 }
763 
764 static void logNmHealth(uint8_t* data, std::string& errLog)
765 {
766     std::vector<std::string> nmErrType = {
767         "Unknown",
768         "Unknown",
769         "Unknown",
770         "Unknown",
771         "Unknown",
772         "Unknown",
773         "Unknown",
774         "Extended Telemetry Device Reading Failure",
775         "Outlet Temperature Reading Failure",
776         "Volumetric Airflow Reading Failure",
777         "Policy Misconfiguration",
778         "Power Sensor Reading Failure",
779         "Inlet Temperature Reading Failure",
780         "Host Communication Error",
781         "Real-time Clock Synchronization Failure",
782         "Platform Shutdown Initiated by Intel NM Policy",
783         "Unknown"};
784     uint8_t nmTypeIdx = (data[0] & 0xf);
785     uint8_t domIdx = (data[1] & 0xf);
786     uint8_t errIdx = ((data[1] >> 4) & 0xf);
787 
788     if (nmTypeIdx == 2)
789     {
790         errLog = "SensorIntelNM";
791     }
792     else
793     {
794         errLog = "Unknown";
795     }
796 
797     errLog += ", Domain:" + nmDomName[domIdx] + ", ErrType:" +
798               nmErrType[errIdx] + ", Err:0x" + byteToStr(data[2]);
799 }
800 
801 static void logNmCap(uint8_t* data, std::string& errLog)
802 {
803     const std::vector<std::string> nmCapStsStr = {"Not Available", "Available"};
804     if (data[0] & 0x7) // BIT1=policy, BIT2=monitoring, BIT3=pwr
805                        // limit and the others are reserved
806     {
807         errLog = "PolicyInterface:" + nmCapStsStr[BIT(data[0], 0)] +
808                  ",Monitoring:" + nmCapStsStr[BIT(data[0], 1)] +
809                  ",PowerLimit:" + nmCapStsStr[BIT(data[0], 2)];
810     }
811     else
812     {
813         errLog = "Unknown";
814     }
815 }
816 
817 static void logNmThreshold(uint8_t* data, std::string& errLog)
818 {
819     uint8_t thresNum = (data[0] & 0x3);
820     uint8_t domIdx = (data[1] & 0xf);
821     uint8_t polId = data[2];
822     uint8_t polEvtIdx = BIT(data[0], 3);
823     const std::vector<std::string> polEvtStr = {
824         "Threshold Exceeded", "Policy Correction Time Exceeded"};
825 
826     errLog = "Threshold Number:" + std::to_string(thresNum) + "-" +
827              polEvtStr[polEvtIdx] + ", Domain:" + nmDomName[domIdx] +
828              ", PolicyID:0x" + byteToStr(polId);
829 }
830 
831 static void logPwrThreshold(uint8_t* data, std::string& errLog)
832 {
833     if (data[0] == 0x00)
834     {
835         errLog = "Limit Not Exceeded";
836     }
837     else if (data[0] == 0x01)
838     {
839         errLog = "Limit Exceeded";
840     }
841     else
842     {
843         errLog = "Unknown";
844     }
845 }
846 
847 static void logMSMI(uint8_t* data, std::string& errLog)
848 {
849     if (data[0] == 0x0)
850     {
851         errLog = "IERR/MSMI";
852     }
853     else if (data[0] == 0x0B)
854     {
855         errLog = "MCERR/MSMI";
856     }
857     else
858     {
859         errLog = "Unknown";
860     }
861 }
862 
863 static void logHprWarn(uint8_t* data, std::string& errLog)
864 {
865     if (data[2] == 0x01)
866     {
867         if (data[1] == 0xFF)
868         {
869             errLog = "Infinite Time";
870         }
871         else
872         {
873             errLog = std::to_string(data[1]) + " minutes";
874         }
875     }
876     else
877     {
878         errLog = "Unknown";
879     }
880 }
881 
882 static const boost::container::flat_map<
883     uint8_t,
884     std::pair<std::string, std::function<void(uint8_t*, std::string&)>>>
885     sensorNameTable = {
886         {0xE9, {"SYSTEM_EVENT", logSysEvent}},
887         {0x7D, {"THERM_THRESH_EVT", logThermalEvent}},
888         {0xAA, {"BUTTON", logDefault}},
889         {0xAB, {"POWER_STATE", logDefault}},
890         {0xEA, {"CRITICAL_IRQ", logCritIrq}},
891         {0x2B, {"POST_ERROR", logPostErr}},
892         {0x40, {"MACHINE_CHK_ERR", logMchChkErr}},
893         {0x41, {"PCIE_ERR", logPcieErr}},
894         {0x43, {"IIO_ERR", logIioErr}},
895         {0X63, {"MEMORY_ECC_ERR", logDefault}},
896         {0X87, {"MEMORY_ERR_LOG_DIS", logDefault}},
897         {0X51, {"PROCHOT_EXT", logDefault}},
898         {0X56, {"PWR_ERR", logPwrErr}},
899         {0xE6, {"CATERR_A", logCatErr}},
900         {0xEB, {"CATERR_B", logCatErr}},
901         {0xB3, {"CPU_DIMM_HOT", logDimmHot}},
902         {0x90, {"SOFTWARE_NMI", logSwNMI}},
903         {0x1C, {"CPU0_THERM_STATUS", logCPUThermalSts}},
904         {0x1D, {"CPU1_THERM_STATUS", logCPUThermalSts}},
905         {0x16, {"ME_POWER_STATE", logMEPwrState}},
906         {0x17, {"SPS_FW_HEALTH", logSPSFwHealth}},
907         {0x18, {"NM_EXCEPTION_A", logNmExcA}},
908         {0x08, {"PCH_THERM_THRESHOLD", logPCHThermal}},
909         {0x19, {"NM_HEALTH", logNmHealth}},
910         {0x1A, {"NM_CAPABILITIES", logNmCap}},
911         {0x1B, {"NM_THRESHOLD", logNmThreshold}},
912         {0x3B, {"PWR_THRESH_EVT", logPwrThreshold}},
913         {0xE7, {"MSMI", logMSMI}},
914         {0xC5, {"HPR_WARNING", logHprWarn}}};
915 
916 static void parseSelHelper(StdSELEntry* data, std::string& errStr)
917 {
918     /* Check if sensor type is OS_BOOT (0x1f) */
919     if (data->sensorType == 0x1F)
920     {
921         /* OS_BOOT used by OS */
922         switch (data->eventData1 & 0xF)
923         {
924             case 0x07:
925                 errStr = "Base OS/Hypervisor Installation started";
926                 break;
927             case 0x08:
928                 errStr = "Base OS/Hypervisor Installation completed";
929                 break;
930             case 0x09:
931                 errStr = "Base OS/Hypervisor Installation aborted";
932                 break;
933             case 0x0A:
934                 errStr = "Base OS/Hypervisor Installation failed";
935                 break;
936             default:
937                 errStr = "Unknown";
938         }
939         return;
940     }
941 
942     auto findSensorName = sensorNameTable.find(data->sensorNum);
943     if (findSensorName == sensorNameTable.end())
944     {
945         errStr = "Unknown";
946         return;
947     }
948     else
949     {
950         switch (data->sensorNum)
951         {
952             /* logMemErr function needs data from sensor type */
953             case memoryEccError:
954             case memoryErrLogDIS:
955                 findSensorName->second.second(&(data->sensorType), errStr);
956                 break;
957             /* Other sensor function needs only event data for parsing */
958             default:
959                 findSensorName->second.second(&(data->eventData1), errStr);
960         }
961     }
962 
963     if (((data->eventData3 & 0x80) >> 7) == 0)
964     {
965         errStr += " Assertion";
966     }
967     else
968     {
969         errStr += " Deassertion";
970     }
971 }
972 
973 static void parseDimmPhyloc(StdSELEntry* data, std::string& errStr)
974 {
975     // Log when " All info available"
976     uint8_t chNum = (data->eventData3 & 0x18) >> 3;
977     uint8_t dimmNum = data->eventData3 & 0x7;
978     uint8_t rankNum = data->eventData2 & 0x03;
979     uint8_t nodeNum = (data->eventData3 & 0xE0) >> 5;
980 
981     if (chNum == 3 && dimmNum == 0)
982     {
983         errStr += " Node: " + std::to_string(nodeNum) + "," +
984                   " Card: " + std::to_string(chNum) + "," +
985                   " Module: " + std::to_string(dimmNum) + "," +
986                   " Rank Number: " + std::to_string(rankNum) + "," +
987                   "  Location: DIMM A0";
988     }
989     else if (chNum == 2 && dimmNum == 0)
990     {
991         errStr += " Node: " + std::to_string(nodeNum) + "," +
992                   " Card: " + std::to_string(chNum) + "," +
993                   " Module: " + std::to_string(dimmNum) + "," +
994                   " Rank Number: " + std::to_string(rankNum) + "," +
995                   " Location: DIMM B0";
996     }
997     else if (chNum == 4 && dimmNum == 0)
998     {
999         errStr += " Node: " + std::to_string(nodeNum) + "," +
1000                   " Card: " + std::to_string(chNum) + "," +
1001                   " Module: " + std::to_string(dimmNum) + "," +
1002                   " Rank Number: " + std::to_string(rankNum) + "," +
1003                   " Location: DIMM C0 ";
1004     }
1005     else if (chNum == 5 && dimmNum == 0)
1006     {
1007         errStr += " Node: " + std::to_string(nodeNum) + "," +
1008                   " Card: " + std::to_string(chNum) + "," +
1009                   " Module: " + std::to_string(dimmNum) + "," +
1010                   " Rank Number: " + std::to_string(rankNum) + "," +
1011                   " Location: DIMM D0";
1012     }
1013     else
1014     {
1015         errStr += " Node: " + std::to_string(nodeNum) + "," +
1016                   " Card: " + std::to_string(chNum) + "," +
1017                   " Module: " + std::to_string(dimmNum) + "," +
1018                   " Rank Number: " + std::to_string(rankNum) + "," +
1019                   " Location: DIMM Unknown";
1020     }
1021 }
1022 
1023 static void parseStdSel(StdSELEntry* data, std::string& errStr)
1024 {
1025     std::stringstream tmpStream;
1026     tmpStream << std::hex << std::uppercase;
1027 
1028     /* TODO: add pal_add_cri_sel */
1029     switch (data->sensorNum)
1030     {
1031         case memoryEccError:
1032             switch (data->eventData1 & 0x0F)
1033             {
1034                 case 0x00:
1035                     errStr = "Correctable";
1036                     tmpStream << "DIMM" << std::setw(2) << std::setfill('0')
1037                               << data->eventData3 << " ECC err";
1038                     parseDimmPhyloc(data, errStr);
1039                     break;
1040                 case 0x01:
1041                     errStr = "Uncorrectable";
1042                     tmpStream << "DIMM" << std::setw(2) << std::setfill('0')
1043                               << data->eventData3 << " UECC err";
1044                     parseDimmPhyloc(data, errStr);
1045                     break;
1046                 case 0x02:
1047                     errStr = "Parity";
1048                     break;
1049                 case 0x05:
1050                     errStr = "Correctable ECC error Logging Limit Reached";
1051                     break;
1052                 default:
1053                     errStr = "Unknown";
1054             }
1055             break;
1056         case memoryErrLogDIS:
1057             if ((data->eventData1 & 0x0F) == 0)
1058             {
1059                 errStr = "Correctable Memory Error Logging Disabled";
1060             }
1061             else
1062             {
1063                 errStr = "Unknown";
1064             }
1065             break;
1066         default:
1067             parseSelHelper(data, errStr);
1068             return;
1069     }
1070 
1071     errStr += " (DIMM " + std::to_string(data->eventData3) + ")";
1072     errStr += " Logical Rank " + std::to_string(data->eventData2 & 0x03);
1073 
1074     switch ((data->eventData2 & 0x0C) >> 2)
1075     {
1076         case 0x00:
1077             // Ignore when " All info available"
1078             break;
1079         case 0x01:
1080             errStr += " DIMM info not valid";
1081             break;
1082         case 0x02:
1083             errStr += " CHN info not valid";
1084             break;
1085         case 0x03:
1086             errStr += " CPU info not valid";
1087             break;
1088         default:
1089             errStr += " Unknown";
1090     }
1091 
1092     if (((data->eventType & 0x80) >> 7) == 0)
1093     {
1094         errStr += " Assertion";
1095     }
1096     else
1097     {
1098         errStr += " Deassertion";
1099     }
1100 
1101     return;
1102 }
1103 
1104 static void parseOemSel(TsOemSELEntry* data, std::string& errStr)
1105 {
1106     std::stringstream tmpStream;
1107     tmpStream << std::hex << std::uppercase << std::setfill('0');
1108 
1109     switch (data->recordType)
1110     {
1111         case 0xC0:
1112             tmpStream << "VID:0x" << std::setw(2) << (int)data->oemData[1]
1113                       << std::setw(2) << (int)data->oemData[0] << " DID:0x"
1114                       << std::setw(2) << (int)data->oemData[3] << std::setw(2)
1115                       << (int)data->oemData[2] << " Slot:0x" << std::setw(2)
1116                       << (int)data->oemData[4] << " Error ID:0x" << std::setw(2)
1117                       << (int)data->oemData[5];
1118             break;
1119         case 0xC2:
1120             tmpStream << "Extra info:0x" << std::setw(2)
1121                       << (int)data->oemData[1] << " MSCOD:0x" << std::setw(2)
1122                       << (int)data->oemData[3] << std::setw(2)
1123                       << (int)data->oemData[2] << " MCACOD:0x" << std::setw(2)
1124                       << (int)data->oemData[5] << std::setw(2)
1125                       << (int)data->oemData[4];
1126             break;
1127         case 0xC3:
1128             int bank = (data->oemData[1] & 0xf0) >> 4;
1129             int col = ((data->oemData[1] & 0x0f) << 8) | data->oemData[2];
1130 
1131             tmpStream << "Fail Device:0x" << std::setw(2)
1132                       << (int)data->oemData[0] << " Bank:0x" << std::setw(2)
1133                       << bank << " Column:0x" << std::setw(2) << col
1134                       << " Failed Row:0x" << std::setw(2)
1135                       << (int)data->oemData[3] << std::setw(2)
1136                       << (int)data->oemData[4] << std::setw(2)
1137                       << (int)data->oemData[5];
1138     }
1139 
1140     errStr = tmpStream.str();
1141 
1142     return;
1143 }
1144 
1145 static std::string dimmLocationStr(uint8_t socket, uint8_t channel,
1146                                    uint8_t slot)
1147 {
1148     uint8_t sled = (socket >> 4) & 0x3;
1149 
1150     socket &= 0xf;
1151     if (channel == 0xFF && slot == 0xFF)
1152     {
1153         return std::format(
1154             "DIMM Slot Location: Sled {:02}/Socket {:02}, Channel unknown"
1155             ", Slot unknown, DIMM unknown",
1156             sled, socket);
1157     }
1158     else
1159     {
1160         channel &= 0xf;
1161         slot &= 0xf;
1162         const char label[] = {'A', 'C', 'B', 'D'};
1163         uint8_t idx = socket * 2 + slot;
1164         return std::format("DIMM Slot Location: Sled {:02}/Socket {:02}"
1165                            ", Channel {:02}, Slot {:02} DIMM {}",
1166                            sled, socket, channel, slot,
1167                            (idx < sizeof(label))
1168                                ? label[idx] + std::to_string(channel)
1169                                : "NA");
1170     }
1171 }
1172 
1173 static void parseOemUnifiedSel(NtsOemSELEntry* data, std::string& errStr)
1174 {
1175     uint8_t* ptr = data->oemData;
1176     uint8_t eventType = ptr[5] & 0xf;
1177     int genInfo = ptr[0];
1178     int errType = genInfo & 0x0f;
1179     std::vector<std::string> dimmErr = {
1180         "Memory training failure",
1181         "Memory correctable error",
1182         "Memory uncorrectable error",
1183         "Memory correctable error (Patrol scrub)",
1184         "Memory uncorrectable error (Patrol scrub)",
1185         "Memory Parity Error (PCC=0)",
1186         "Memory Parity Error (PCC=1)",
1187         "Memory PMIC Error",
1188         "CXL Memory training error",
1189         "Reserved"};
1190     std::vector<std::string> postEvent = {
1191         "System PXE boot fail",
1192         "CMOS/NVRAM configuration cleared",
1193         "TPM Self-Test Fail",
1194         "Boot Drive failure",
1195         "Data Drive failure",
1196         "Received invalid boot order request from BMC",
1197         "System HTTP boot fail",
1198         "BIOS fails to get the certificate from BMC",
1199         "Password cleared by jumper",
1200         "DXE FV check failure",
1201         "AMD ABL failure",
1202         "Reserved"};
1203     std::vector<std::string> certErr = {
1204         "No certificate at BMC", "IPMI transaction fail",
1205         "Certificate data corrupted", "Reserved"};
1206     std::vector<std::string> pcieEvent = {
1207         "PCIe DPC Event",
1208         "PCIe LER Event",
1209         "PCIe Link Retraining and Recovery",
1210         "PCIe Link CRC Error Check and Retry",
1211         "PCIe Corrupt Data Containment",
1212         "PCIe Express ECRC",
1213         "Reserved"};
1214     std::vector<std::string> memEvent = {
1215         "Memory PPR event",
1216         "Memory Correctable Error logging limit reached",
1217         "Memory disable/map-out for FRB",
1218         "Memory SDDC",
1219         "Memory Address range/Partial mirroring",
1220         "Memory ADDDC",
1221         "Memory SMBus hang recovery",
1222         "No DIMM in System",
1223         "Reserved"};
1224     std::vector<std::string> memPprTime = {"Boot time", "Autonomous",
1225                                            "Run time", "Reserved"};
1226     std::vector<std::string> memPpr = {"PPR success", "PPR fail", "PPR request",
1227                                        "Reserved"};
1228     std::vector<std::string> memAdddc = {
1229         "Bank VLS", "r-Bank VLS + re-buddy", "r-Bank VLS + Rank VLS",
1230         "r-Rank VLS + re-buddy", "Reserved"};
1231     std::vector<std::string> pprEvent = {"PPR disable", "Soft PPR", "Hard PPR",
1232                                          "Reserved"};
1233 
1234     std::stringstream tmpStream;
1235 
1236     switch (errType)
1237     {
1238         case unifiedPcieErr:
1239             tmpStream << std::format(
1240                 "GeneralInfo: x86/PCIeErr(0x{:02X})"
1241                 ", Bus {:02X}/Dev {:02X}/Fun {:02X}, TotalErrID1Cnt: 0x{:04X}"
1242                 ", ErrID2: 0x{:02X}, ErrID1: 0x{:02X}",
1243                 genInfo, ptr[8], ptr[7] >> 3, ptr[7] & 0x7,
1244                 (ptr[10] << 8) | ptr[9], ptr[11], ptr[12]);
1245             break;
1246         case unifiedMemErr:
1247             eventType = ptr[9] & 0xf;
1248             tmpStream << std::format(
1249                 "GeneralInfo: MemErr(0x{:02X}), {}, DIMM Failure Event: {}",
1250                 genInfo, dimmLocationStr(ptr[5], ptr[6], ptr[7]),
1251                 dimmErr[std::min(eventType,
1252                                  static_cast<uint8_t>(dimmErr.size() - 1))]);
1253 
1254             if (static_cast<MemErrType>(eventType) == MemErrType::memTrainErr ||
1255                 static_cast<MemErrType>(eventType) == MemErrType::memPmicErr)
1256             {
1257                 bool amd = ptr[9] & 0x80;
1258                 tmpStream << std::format(
1259                     ", Major Code: 0x{:02X}, Minor Code: 0x{:0{}X}", ptr[10],
1260                     amd ? (ptr[12] << 8 | ptr[11]) : ptr[11], amd ? 4 : 2);
1261             }
1262             break;
1263         case unifiedIioErr:
1264             tmpStream << std::format(
1265                 "GeneralInfo: IIOErr(0x{:02X})"
1266                 ", IIO Port Location: Sled {:02}/Socket {:02}, Stack 0x{:02X}"
1267                 ", Error Type: 0x{:02X}, Error Severity: 0x{:02X}"
1268                 ", Error ID: 0x{:02X}",
1269                 genInfo, (ptr[5] >> 4) & 0x3, ptr[5] & 0xf, ptr[6], ptr[10],
1270                 ptr[11] & 0xf, ptr[12]);
1271             break;
1272         case unifiedPostEvt:
1273             tmpStream << std::format(
1274                 "GeneralInfo: POST(0x{:02X}), POST Failure Event: {}", genInfo,
1275                 postEvent[std::min(
1276                     eventType, static_cast<uint8_t>(postEvent.size() - 1))]);
1277 
1278             switch (static_cast<PostEvtType>(eventType))
1279             {
1280                 case PostEvtType::pxeBootFail:
1281                 case PostEvtType::httpBootFail:
1282                 {
1283                     uint8_t failType = ptr[10] & 0xf;
1284                     tmpStream
1285                         << std::format(", Fail Type: {}, Error Code: 0x{:02X}",
1286                                        (failType == 4 || failType == 6)
1287                                            ? std::format("IPv{} fail", failType)
1288                                            : std::format("0x{:02X}", ptr[10]),
1289                                        ptr[11]);
1290                     break;
1291                 }
1292                 case PostEvtType::getCertFail:
1293                     tmpStream << std::format(
1294                         ", Failure Detail: {}",
1295                         certErr[std::min(
1296                             ptr[9], static_cast<uint8_t>(certErr.size() - 1))]);
1297                     break;
1298                 case PostEvtType::amdAblFail:
1299                     tmpStream << std::format(", ABL Error Code: 0x{:04X}",
1300                                              (ptr[12] << 8) | ptr[11]);
1301                     break;
1302             }
1303             break;
1304         case unifiedPcieEvt:
1305             tmpStream << std::format(
1306                 "GeneralInfo: PCIeEvent(0x{:02X}), PCIe Failure Event: {}",
1307                 genInfo,
1308                 pcieEvent[std::min(
1309                     eventType, static_cast<uint8_t>(pcieEvent.size() - 1))]);
1310 
1311             if (static_cast<PcieEvtType>(eventType) == PcieEvtType::dpc)
1312             {
1313                 tmpStream << std::format(
1314                     ", Status: 0x{:04X}, Source ID: 0x{:04X}",
1315                     (ptr[8] << 8) | ptr[7], (ptr[10] << 8) | ptr[9]);
1316             }
1317             break;
1318         case unifiedMemEvt:
1319             eventType = ptr[9] & 0xf;
1320             tmpStream
1321                 << std::format("GeneralInfo: MemEvent(0x{:02X})", genInfo)
1322                 << (static_cast<MemEvtType>(eventType) != MemEvtType::noDimm
1323                         ? std::format(", {}",
1324                                       dimmLocationStr(ptr[5], ptr[6], ptr[7]))
1325                         : "")
1326                 << ", DIMM Failure Event: ";
1327 
1328             switch (static_cast<MemEvtType>(eventType))
1329             {
1330                 case MemEvtType::ppr:
1331                     tmpStream << std::format("{} {}",
1332                                              memPprTime[(ptr[10] >> 2) & 0x3],
1333                                              memPpr[ptr[10] & 0x3]);
1334                     break;
1335                 case MemEvtType::adddc:
1336                     tmpStream << std::format(
1337                         "{} {}",
1338                         memEvent[std::min(eventType, static_cast<uint8_t>(
1339                                                          memEvent.size() - 1))],
1340                         memAdddc[std::min(
1341                             static_cast<uint8_t>(ptr[11] & 0xf),
1342                             static_cast<uint8_t>(memAdddc.size() - 1))]);
1343                     break;
1344                 default:
1345                     tmpStream << std::format(
1346                         "{}", memEvent[std::min(
1347                                   eventType,
1348                                   static_cast<uint8_t>(memEvent.size() - 1))]);
1349                     break;
1350             }
1351             break;
1352         case unifiedBootGuard:
1353             tmpStream << std::format(
1354                 "GeneralInfo: Boot Guard ACM Failure Events(0x{:02X})"
1355                 ", Error Class: 0x{:02X}, Error Code: 0x{:02X}",
1356                 genInfo, ptr[9], ptr[10]);
1357             break;
1358         case unifiedPprEvt:
1359             tmpStream << std::format(
1360                 "GeneralInfo: PPREvent(0x{:02X}), {}"
1361                 ", DIMM Info: {:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
1362                 genInfo,
1363                 pprEvent[std::min(eventType,
1364                                   static_cast<uint8_t>(pprEvent.size() - 1))],
1365                 ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12]);
1366             break;
1367         default:
1368             std::vector<uint8_t> oemData(ptr, ptr + 13);
1369             std::string oemDataStr;
1370             toHexStr(oemData, oemDataStr);
1371             tmpStream << std::format("Undefined Error Type(0x{:02X}), Raw: {}",
1372                                      errType, oemDataStr);
1373     }
1374 
1375     errStr = tmpStream.str();
1376 
1377     return;
1378 }
1379 
1380 static void parseSelData(uint8_t fruId, std::vector<uint8_t>& reqData,
1381                          std::string& msgLog)
1382 {
1383     /* Get record type */
1384     int recType = reqData[2];
1385     std::string errType, errLog;
1386 
1387     uint8_t* ptr = NULL;
1388 
1389     std::stringstream recTypeStream;
1390     recTypeStream << std::hex << std::uppercase << std::setfill('0')
1391                   << std::setw(2) << recType;
1392 
1393     msgLog = "SEL Entry: FRU: " + std::to_string(fruId) + ", Record: ";
1394 
1395     if (recType == stdErrType)
1396     {
1397         StdSELEntry* data = reinterpret_cast<StdSELEntry*>(&reqData[0]);
1398         std::string sensorName;
1399 
1400         errType = stdErr;
1401         if (data->sensorType == 0x1F)
1402         {
1403             sensorName = "OS";
1404         }
1405         else
1406         {
1407             auto findSensorName = sensorNameTable.find(data->sensorNum);
1408             if (findSensorName == sensorNameTable.end())
1409             {
1410                 sensorName = "Unknown";
1411             }
1412             else
1413             {
1414                 sensorName = findSensorName->second.first;
1415             }
1416         }
1417 
1418         parseStdSel(data, errLog);
1419         ptr = &(data->eventData1);
1420         std::vector<uint8_t> evtData(ptr, ptr + 3);
1421         std::string eventData;
1422         toHexStr(evtData, eventData);
1423 
1424         std::stringstream senNumStream;
1425         senNumStream << std::hex << std::uppercase << std::setfill('0')
1426                      << std::setw(2) << (int)(data->sensorNum);
1427 
1428         msgLog += errType + " (0x" + recTypeStream.str() +
1429                   "), Sensor: " + sensorName + " (0x" + senNumStream.str() +
1430                   "), Event Data: (" + eventData + ") " + errLog;
1431     }
1432     else if ((recType >= oemTSErrTypeMin) && (recType <= oemTSErrTypeMax))
1433     {
1434         /* timestamped OEM SEL records */
1435         TsOemSELEntry* data = reinterpret_cast<TsOemSELEntry*>(&reqData[0]);
1436         ptr = data->mfrId;
1437         std::vector<uint8_t> mfrIdData(ptr, ptr + 3);
1438         std::string mfrIdStr;
1439         toHexStr(mfrIdData, mfrIdStr);
1440 
1441         ptr = data->oemData;
1442         std::vector<uint8_t> oemData(ptr, ptr + 6);
1443         std::string oemDataStr;
1444         toHexStr(oemData, oemDataStr);
1445 
1446         errType = oemTSErr;
1447         parseOemSel(data, errLog);
1448 
1449         msgLog += errType + " (0x" + recTypeStream.str() + "), MFG ID: " +
1450                   mfrIdStr + ", OEM Data: (" + oemDataStr + ") " + errLog;
1451     }
1452     else if (recType == fbUniErrType)
1453     {
1454         NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]);
1455         errType = fbUniSELErr;
1456         parseOemUnifiedSel(data, errLog);
1457         msgLog += errType + " (0x" + recTypeStream.str() + "), " + errLog;
1458     }
1459     else if ((recType >= oemNTSErrTypeMin) && (recType <= oemNTSErrTypeMax))
1460     {
1461         /* Non timestamped OEM SEL records */
1462         NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]);
1463         errType = oemNTSErr;
1464 
1465         ptr = data->oemData;
1466         std::vector<uint8_t> oemData(ptr, ptr + 13);
1467         std::string oemDataStr;
1468         toHexStr(oemData, oemDataStr);
1469 
1470         parseOemSel((TsOemSELEntry*)data, errLog);
1471         msgLog += errType + " (0x" + recTypeStream.str() + "), OEM Data: (" +
1472                   oemDataStr + ") " + errLog;
1473     }
1474     else
1475     {
1476         errType = unknownErr;
1477         toHexStr(reqData, errLog);
1478         msgLog += errType + " (0x" + recTypeStream.str() +
1479                   ") RawData: " + errLog;
1480     }
1481 }
1482 
1483 } // namespace fb_oem::ipmi::sel
1484 
1485 namespace ipmi
1486 {
1487 
1488 namespace storage
1489 {
1490 
1491 static void registerSELFunctions() __attribute__((constructor));
1492 static fb_oem::ipmi::sel::SELData selObj __attribute__((init_priority(101)));
1493 
1494 ipmi::RspType<uint8_t,  // SEL version
1495               uint16_t, // SEL entry count
1496               uint16_t, // free space
1497               uint32_t, // last add timestamp
1498               uint32_t, // last erase timestamp
1499               uint8_t>  // operation support
1500     ipmiStorageGetSELInfo()
1501 {
1502     fb_oem::ipmi::sel::GetSELInfoData info;
1503 
1504     selObj.getInfo(info);
1505     return ipmi::responseSuccess(info.selVersion, info.entries, info.freeSpace,
1506                                  info.addTimeStamp, info.eraseTimeStamp,
1507                                  info.operationSupport);
1508 }
1509 
1510 ipmi::RspType<uint16_t, std::vector<uint8_t>>
1511     ipmiStorageGetSELEntry(std::vector<uint8_t> data)
1512 {
1513     if (data.size() != sizeof(fb_oem::ipmi::sel::GetSELEntryRequest))
1514     {
1515         return ipmi::responseReqDataLenInvalid();
1516     }
1517 
1518     fb_oem::ipmi::sel::GetSELEntryRequest* reqData =
1519         reinterpret_cast<fb_oem::ipmi::sel::GetSELEntryRequest*>(&data[0]);
1520 
1521     if (reqData->reservID != 0)
1522     {
1523         if (!checkSELReservation(reqData->reservID))
1524         {
1525             return ipmi::responseInvalidReservationId();
1526         }
1527     }
1528 
1529     uint16_t selCnt = selObj.getCount();
1530     if (selCnt == 0)
1531     {
1532         return ipmi::responseSensorInvalid();
1533     }
1534 
1535     /* If it is asked for first entry */
1536     if (reqData->recordID == fb_oem::ipmi::sel::firstEntry)
1537     {
1538         /* First Entry (0x0000) as per Spec */
1539         reqData->recordID = 1;
1540     }
1541     else if (reqData->recordID == fb_oem::ipmi::sel::lastEntry)
1542     {
1543         /* Last entry (0xFFFF) as per Spec */
1544         reqData->recordID = selCnt;
1545     }
1546 
1547     std::string ipmiRaw;
1548 
1549     if (selObj.getEntry(reqData->recordID, ipmiRaw) < 0)
1550     {
1551         return ipmi::responseSensorInvalid();
1552     }
1553 
1554     std::vector<uint8_t> recDataBytes;
1555     if (fromHexStr(ipmiRaw, recDataBytes) < 0)
1556     {
1557         return ipmi::responseUnspecifiedError();
1558     }
1559 
1560     /* Identify the next SEL record ID. If recordID is same as
1561      * total SeL count then next id should be last entry else
1562      * it should be incremented by 1 to current RecordID
1563      */
1564     uint16_t nextRecord;
1565     if (reqData->recordID == selCnt)
1566     {
1567         nextRecord = fb_oem::ipmi::sel::lastEntry;
1568     }
1569     else
1570     {
1571         nextRecord = reqData->recordID + 1;
1572     }
1573 
1574     if (reqData->readLen == fb_oem::ipmi::sel::entireRecord)
1575     {
1576         return ipmi::responseSuccess(nextRecord, recDataBytes);
1577     }
1578     else
1579     {
1580         if (reqData->offset >= fb_oem::ipmi::sel::selRecordSize ||
1581             reqData->readLen > fb_oem::ipmi::sel::selRecordSize)
1582         {
1583             return ipmi::responseUnspecifiedError();
1584         }
1585         std::vector<uint8_t> recPartData;
1586 
1587         auto diff = fb_oem::ipmi::sel::selRecordSize - reqData->offset;
1588         auto readLength = std::min(diff, static_cast<int>(reqData->readLen));
1589 
1590         for (int i = 0; i < readLength; i++)
1591         {
1592             recPartData.push_back(recDataBytes[i + reqData->offset]);
1593         }
1594         return ipmi::responseSuccess(nextRecord, recPartData);
1595     }
1596 }
1597 
1598 ipmi::RspType<uint16_t>
1599     ipmiStorageAddSELEntry(ipmi::Context::ptr ctx, std::vector<uint8_t> data)
1600 {
1601     /* Per the IPMI spec, need to cancel any reservation when a
1602      * SEL entry is added
1603      */
1604     cancelSELReservation();
1605 
1606     if (data.size() != fb_oem::ipmi::sel::selRecordSize)
1607     {
1608         return ipmi::responseReqDataLenInvalid();
1609     }
1610 
1611     std::string ipmiRaw, logErr;
1612     toHexStr(data, ipmiRaw);
1613 
1614     /* Parse sel data and get an error log to be filed */
1615     fb_oem::ipmi::sel::parseSelData((ctx->hostIdx + 1), data, logErr);
1616 
1617     static const std::string openBMCMessageRegistryVersion("0.1");
1618     std::string messageID =
1619         "OpenBMC." + openBMCMessageRegistryVersion + ".SELEntryAdded";
1620 
1621     /* Log the Raw SEL message to the journal */
1622     std::string journalMsg = "SEL Entry Added: " + ipmiRaw;
1623 
1624     phosphor::logging::log<phosphor::logging::level::INFO>(
1625         journalMsg.c_str(),
1626         phosphor::logging::entry("IPMISEL_MESSAGE_ID=%s", messageID.c_str()),
1627         phosphor::logging::entry("IPMISEL_MESSAGE_ARGS=%s", logErr.c_str()));
1628 
1629     std::map<std::string, std::string> ad;
1630     std::string severity = "xyz.openbmc_project.Logging.Entry.Level.Critical";
1631     ad.emplace("IPMI_RAW", ipmiRaw);
1632 
1633     auto bus = sdbusplus::bus::new_default();
1634     auto reqMsg = bus.new_method_call(
1635         "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1636         "xyz.openbmc_project.Logging.Create", "Create");
1637     reqMsg.append(logErr, severity, ad);
1638 
1639     try
1640     {
1641         bus.call(reqMsg);
1642     }
1643     catch (sdbusplus::exception_t& e)
1644     {
1645         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1646     }
1647 
1648     int responseID = selObj.addEntry(ipmiRaw.c_str());
1649     if (responseID < 0)
1650     {
1651         return ipmi::responseUnspecifiedError();
1652     }
1653     return ipmi::responseSuccess((uint16_t)responseID);
1654 }
1655 
1656 ipmi::RspType<uint8_t> ipmiStorageClearSEL(uint16_t reservationID,
1657                                            const std::array<uint8_t, 3>& clr,
1658                                            uint8_t eraseOperation)
1659 {
1660     if (!checkSELReservation(reservationID))
1661     {
1662         return ipmi::responseInvalidReservationId();
1663     }
1664 
1665     static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
1666     if (clr != clrExpected)
1667     {
1668         return ipmi::responseInvalidFieldRequest();
1669     }
1670 
1671     /* If there is no sel then return erase complete */
1672     if (selObj.getCount() == 0)
1673     {
1674         return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1675     }
1676 
1677     /* Erasure status cannot be fetched, so always return erasure
1678      * status as `erase completed`.
1679      */
1680     if (eraseOperation == fb_oem::ipmi::sel::getEraseStatus)
1681     {
1682         return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1683     }
1684 
1685     /* Check that initiate erase is correct */
1686     if (eraseOperation != fb_oem::ipmi::sel::initiateErase)
1687     {
1688         return ipmi::responseInvalidFieldRequest();
1689     }
1690 
1691     /* Per the IPMI spec, need to cancel any reservation when the
1692      * SEL is cleared
1693      */
1694     cancelSELReservation();
1695 
1696     /* Clear the complete Sel Json object */
1697     if (selObj.clear() < 0)
1698     {
1699         return ipmi::responseUnspecifiedError();
1700     }
1701 
1702     return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1703 }
1704 
1705 ipmi::RspType<uint32_t> ipmiStorageGetSELTime()
1706 {
1707     struct timespec selTime = {};
1708 
1709     if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
1710     {
1711         return ipmi::responseUnspecifiedError();
1712     }
1713 
1714     return ipmi::responseSuccess(selTime.tv_sec);
1715 }
1716 
1717 ipmi::RspType<> ipmiStorageSetSELTime(uint32_t)
1718 {
1719     // Set SEL Time is not supported
1720     return ipmi::responseInvalidCommand();
1721 }
1722 
1723 ipmi::RspType<uint16_t> ipmiStorageGetSELTimeUtcOffset()
1724 {
1725     /* TODO: For now, the SEL time stamp is based on UTC time,
1726      * so return 0x0000 as offset. Might need to change once
1727      * supporting zones in SEL time stamps
1728      */
1729 
1730     uint16_t utcOffset = 0x0000;
1731     return ipmi::responseSuccess(utcOffset);
1732 }
1733 
1734 void registerSELFunctions()
1735 {
1736     // <Get SEL Info>
1737     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1738                           ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
1739                           ipmiStorageGetSELInfo);
1740 
1741     // <Get SEL Entry>
1742     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1743                           ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
1744                           ipmiStorageGetSELEntry);
1745 
1746     // <Add SEL Entry>
1747     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1748                           ipmi::storage::cmdAddSelEntry,
1749                           ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
1750 
1751     // <Clear SEL>
1752     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1753                           ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
1754                           ipmiStorageClearSEL);
1755 
1756     // <Get SEL Time>
1757     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1758                           ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
1759                           ipmiStorageGetSELTime);
1760 
1761     // <Set SEL Time>
1762     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1763                           ipmi::storage::cmdSetSelTime,
1764                           ipmi::Privilege::Operator, ipmiStorageSetSELTime);
1765 
1766     // <Get SEL Time UTC Offset>
1767     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1768                           ipmi::storage::cmdGetSelTimeUtcOffset,
1769                           ipmi::Privilege::User,
1770                           ipmiStorageGetSELTimeUtcOffset);
1771 
1772     return;
1773 }
1774 
1775 } // namespace storage
1776 } // namespace ipmi
1777