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