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