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