xref: /openbmc/fb-ipmi-oem/src/selcommands.cpp (revision 2405ae98)
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         uint32_t timeStamp = data->timeStamp;
1190         std::tm* ts = localtime(reinterpret_cast<time_t*>(&timeStamp));
1191         std::string timeStr = std::asctime(ts);
1192 
1193         parseStdSel(data, errLog);
1194         ptr = &(data->eventData1);
1195         std::vector<uint8_t> evtData(ptr, ptr + 3);
1196         std::string eventData;
1197         toHexStr(evtData, eventData);
1198 
1199         std::stringstream senNumStream;
1200         senNumStream << std::hex << std::uppercase << std::setfill('0')
1201                      << std::setw(2) << (int)(data->sensorNum);
1202 
1203         msgLog += errType + " (0x" + recTypeStream.str() +
1204                   "), Time: " + timeStr + ", Sensor: " + sensorName + " (0x" +
1205                   senNumStream.str() + "), Event Data: (" + eventData + ") " +
1206                   errLog;
1207     }
1208     else if ((recType >= oemTSErrTypeMin) && (recType <= oemTSErrTypeMax))
1209     {
1210         /* timestamped OEM SEL records */
1211         TsOemSELEntry* data = reinterpret_cast<TsOemSELEntry*>(&reqData[0]);
1212         ptr = data->mfrId;
1213         std::vector<uint8_t> mfrIdData(ptr, ptr + 3);
1214         std::string mfrIdStr;
1215         toHexStr(mfrIdData, mfrIdStr);
1216 
1217         ptr = data->oemData;
1218         std::vector<uint8_t> oemData(ptr, ptr + 6);
1219         std::string oemDataStr;
1220         toHexStr(oemData, oemDataStr);
1221 
1222         uint32_t timeStamp = data->timeStamp;
1223         std::tm* ts = localtime(reinterpret_cast<time_t*>(&timeStamp));
1224         std::string timeStr = std::asctime(ts);
1225 
1226         errType = oemTSErr;
1227         parseOemSel(data, errLog);
1228 
1229         msgLog += errType + " (0x" + recTypeStream.str() +
1230                   "), Time: " + timeStr + ", MFG ID: " + mfrIdStr +
1231                   ", OEM Data: (" + oemDataStr + ") " + errLog;
1232     }
1233     else if (recType == fbUniErrType)
1234     {
1235         NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]);
1236         errType = fbUniSELErr;
1237         parseOemUnifiedSel(data, errLog);
1238         msgLog += errType + " (0x" + recTypeStream.str() + "), " + errLog;
1239     }
1240     else if ((recType >= oemNTSErrTypeMin) && (recType <= oemNTSErrTypeMax))
1241     {
1242         /* Non timestamped OEM SEL records */
1243         NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]);
1244         errType = oemNTSErr;
1245 
1246         ptr = data->oemData;
1247         std::vector<uint8_t> oemData(ptr, ptr + 13);
1248         std::string oemDataStr;
1249         toHexStr(oemData, oemDataStr);
1250 
1251         parseOemSel((TsOemSELEntry*)data, errLog);
1252         msgLog += errType + " (0x" + recTypeStream.str() + "), OEM Data: (" +
1253                   oemDataStr + ") " + errLog;
1254     }
1255     else
1256     {
1257         errType = unknownErr;
1258         toHexStr(reqData, errLog);
1259         msgLog += errType + " (0x" + recTypeStream.str() +
1260                   ") RawData: " + errLog;
1261     }
1262 }
1263 
1264 } // namespace fb_oem::ipmi::sel
1265 
1266 namespace ipmi
1267 {
1268 
1269 namespace storage
1270 {
1271 
1272 static void registerSELFunctions() __attribute__((constructor));
1273 static fb_oem::ipmi::sel::SELData selObj __attribute__((init_priority(101)));
1274 
1275 ipmi::RspType<uint8_t,  // SEL version
1276               uint16_t, // SEL entry count
1277               uint16_t, // free space
1278               uint32_t, // last add timestamp
1279               uint32_t, // last erase timestamp
1280               uint8_t>  // operation support
1281     ipmiStorageGetSELInfo()
1282 {
1283     fb_oem::ipmi::sel::GetSELInfoData info;
1284 
1285     selObj.getInfo(info);
1286     return ipmi::responseSuccess(info.selVersion, info.entries, info.freeSpace,
1287                                  info.addTimeStamp, info.eraseTimeStamp,
1288                                  info.operationSupport);
1289 }
1290 
1291 ipmi::RspType<uint16_t, std::vector<uint8_t>>
1292     ipmiStorageGetSELEntry(std::vector<uint8_t> data)
1293 {
1294     if (data.size() != sizeof(fb_oem::ipmi::sel::GetSELEntryRequest))
1295     {
1296         return ipmi::responseReqDataLenInvalid();
1297     }
1298 
1299     fb_oem::ipmi::sel::GetSELEntryRequest* reqData =
1300         reinterpret_cast<fb_oem::ipmi::sel::GetSELEntryRequest*>(&data[0]);
1301 
1302     if (reqData->reservID != 0)
1303     {
1304         if (!checkSELReservation(reqData->reservID))
1305         {
1306             return ipmi::responseInvalidReservationId();
1307         }
1308     }
1309 
1310     uint16_t selCnt = selObj.getCount();
1311     if (selCnt == 0)
1312     {
1313         return ipmi::responseSensorInvalid();
1314     }
1315 
1316     /* If it is asked for first entry */
1317     if (reqData->recordID == fb_oem::ipmi::sel::firstEntry)
1318     {
1319         /* First Entry (0x0000) as per Spec */
1320         reqData->recordID = 1;
1321     }
1322     else if (reqData->recordID == fb_oem::ipmi::sel::lastEntry)
1323     {
1324         /* Last entry (0xFFFF) as per Spec */
1325         reqData->recordID = selCnt;
1326     }
1327 
1328     std::string ipmiRaw;
1329 
1330     if (selObj.getEntry(reqData->recordID, ipmiRaw) < 0)
1331     {
1332         return ipmi::responseSensorInvalid();
1333     }
1334 
1335     std::vector<uint8_t> recDataBytes;
1336     if (fromHexStr(ipmiRaw, recDataBytes) < 0)
1337     {
1338         return ipmi::responseUnspecifiedError();
1339     }
1340 
1341     /* Identify the next SEL record ID. If recordID is same as
1342      * total SeL count then next id should be last entry else
1343      * it should be incremented by 1 to current RecordID
1344      */
1345     uint16_t nextRecord;
1346     if (reqData->recordID == selCnt)
1347     {
1348         nextRecord = fb_oem::ipmi::sel::lastEntry;
1349     }
1350     else
1351     {
1352         nextRecord = reqData->recordID + 1;
1353     }
1354 
1355     if (reqData->readLen == fb_oem::ipmi::sel::entireRecord)
1356     {
1357         return ipmi::responseSuccess(nextRecord, recDataBytes);
1358     }
1359     else
1360     {
1361         if (reqData->offset >= fb_oem::ipmi::sel::selRecordSize ||
1362             reqData->readLen > fb_oem::ipmi::sel::selRecordSize)
1363         {
1364             return ipmi::responseUnspecifiedError();
1365         }
1366         std::vector<uint8_t> recPartData;
1367 
1368         auto diff = fb_oem::ipmi::sel::selRecordSize - reqData->offset;
1369         auto readLength = std::min(diff, static_cast<int>(reqData->readLen));
1370 
1371         for (int i = 0; i < readLength; i++)
1372         {
1373             recPartData.push_back(recDataBytes[i + reqData->offset]);
1374         }
1375         return ipmi::responseSuccess(nextRecord, recPartData);
1376     }
1377 }
1378 
1379 ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(ipmi::Context::ptr ctx,
1380                                                std::vector<uint8_t> data)
1381 {
1382     /* Per the IPMI spec, need to cancel any reservation when a
1383      * SEL entry is added
1384      */
1385     cancelSELReservation();
1386 
1387     if (data.size() != fb_oem::ipmi::sel::selRecordSize)
1388     {
1389         return ipmi::responseReqDataLenInvalid();
1390     }
1391 
1392     std::string ipmiRaw, logErr;
1393     toHexStr(data, ipmiRaw);
1394 
1395     /* Parse sel data and get an error log to be filed */
1396     fb_oem::ipmi::sel::parseSelData((ctx->hostIdx + 1), data, logErr);
1397 
1398     static const std::string openBMCMessageRegistryVersion("0.1");
1399     std::string messageID = "OpenBMC." + openBMCMessageRegistryVersion +
1400                             ".SELEntryAdded";
1401 
1402     /* Log the Raw SEL message to the journal */
1403     std::string journalMsg = "SEL Entry Added: " + ipmiRaw;
1404 
1405     phosphor::logging::log<phosphor::logging::level::INFO>(
1406         journalMsg.c_str(),
1407         phosphor::logging::entry("IPMISEL_MESSAGE_ID=%s", messageID.c_str()),
1408         phosphor::logging::entry("IPMISEL_MESSAGE_ARGS=%s", logErr.c_str()));
1409 
1410     int responseID = selObj.addEntry(ipmiRaw.c_str());
1411     if (responseID < 0)
1412     {
1413         return ipmi::responseUnspecifiedError();
1414     }
1415     return ipmi::responseSuccess((uint16_t)responseID);
1416 }
1417 
1418 ipmi::RspType<uint8_t> ipmiStorageClearSEL(uint16_t reservationID,
1419                                            const std::array<uint8_t, 3>& clr,
1420                                            uint8_t eraseOperation)
1421 {
1422     if (!checkSELReservation(reservationID))
1423     {
1424         return ipmi::responseInvalidReservationId();
1425     }
1426 
1427     static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
1428     if (clr != clrExpected)
1429     {
1430         return ipmi::responseInvalidFieldRequest();
1431     }
1432 
1433     /* If there is no sel then return erase complete */
1434     if (selObj.getCount() == 0)
1435     {
1436         return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1437     }
1438 
1439     /* Erasure status cannot be fetched, so always return erasure
1440      * status as `erase completed`.
1441      */
1442     if (eraseOperation == fb_oem::ipmi::sel::getEraseStatus)
1443     {
1444         return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1445     }
1446 
1447     /* Check that initiate erase is correct */
1448     if (eraseOperation != fb_oem::ipmi::sel::initiateErase)
1449     {
1450         return ipmi::responseInvalidFieldRequest();
1451     }
1452 
1453     /* Per the IPMI spec, need to cancel any reservation when the
1454      * SEL is cleared
1455      */
1456     cancelSELReservation();
1457 
1458     /* Clear the complete Sel Json object */
1459     if (selObj.clear() < 0)
1460     {
1461         return ipmi::responseUnspecifiedError();
1462     }
1463 
1464     return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1465 }
1466 
1467 ipmi::RspType<uint32_t> ipmiStorageGetSELTime()
1468 {
1469     struct timespec selTime = {};
1470 
1471     if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
1472     {
1473         return ipmi::responseUnspecifiedError();
1474     }
1475 
1476     return ipmi::responseSuccess(selTime.tv_sec);
1477 }
1478 
1479 ipmi::RspType<> ipmiStorageSetSELTime(uint32_t)
1480 {
1481     // Set SEL Time is not supported
1482     return ipmi::responseInvalidCommand();
1483 }
1484 
1485 ipmi::RspType<uint16_t> ipmiStorageGetSELTimeUtcOffset()
1486 {
1487     /* TODO: For now, the SEL time stamp is based on UTC time,
1488      * so return 0x0000 as offset. Might need to change once
1489      * supporting zones in SEL time stamps
1490      */
1491 
1492     uint16_t utcOffset = 0x0000;
1493     return ipmi::responseSuccess(utcOffset);
1494 }
1495 
1496 void registerSELFunctions()
1497 {
1498     // <Get SEL Info>
1499     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1500                           ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
1501                           ipmiStorageGetSELInfo);
1502 
1503     // <Get SEL Entry>
1504     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1505                           ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
1506                           ipmiStorageGetSELEntry);
1507 
1508     // <Add SEL Entry>
1509     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1510                           ipmi::storage::cmdAddSelEntry,
1511                           ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
1512 
1513     // <Clear SEL>
1514     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1515                           ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
1516                           ipmiStorageClearSEL);
1517 
1518     // <Get SEL Time>
1519     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1520                           ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
1521                           ipmiStorageGetSELTime);
1522 
1523     // <Set SEL Time>
1524     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1525                           ipmi::storage::cmdSetSelTime,
1526                           ipmi::Privilege::Operator, ipmiStorageSetSELTime);
1527 
1528     // <Get SEL Time UTC Offset>
1529     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1530                           ipmi::storage::cmdGetSelTimeUtcOffset,
1531                           ipmi::Privilege::User,
1532                           ipmiStorageGetSELTimeUtcOffset);
1533 
1534     return;
1535 }
1536 
1537 } // namespace storage
1538 } // namespace ipmi
1539