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