xref: /openbmc/fb-ipmi-oem/src/selcommands.cpp (revision 0d053eff)
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 function
223  * for parseStdSel.
224  *
225  * Note that this function __CANNOT__ be overridden.
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 Critical 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 Critical 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 Critical 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 Critical 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 Critical 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 = {
716         "Lower Non-critical",
717         "Unknown",
718         "Lower Critical",
719         "Unknown",
720         "Lower Non-recoverable",
721         "Unknown",
722         "Unknown",
723         "Upper Non-critical",
724         "Unknown",
725         "Upper Critical",
726         "Unknown",
727         "Upper Non-recoverable"};
728 
729     if ((data[0] & 0x0f) < 12)
730     {
731         errLog = thresEvtName[(data[0] & 0x0f)];
732     }
733     else
734     {
735         errLog = "Unknown";
736     }
737 
738     errLog += ", curr_val: " + std::to_string(data[1]) +
739               " C, thresh_val: " + std::to_string(data[2]) + " C";
740 }
741 
logNmHealth(uint8_t * data,std::string & errLog)742 static void logNmHealth(uint8_t* data, std::string& errLog)
743 {
744     std::vector<std::string> nmErrType = {
745         "Unknown",
746         "Unknown",
747         "Unknown",
748         "Unknown",
749         "Unknown",
750         "Unknown",
751         "Unknown",
752         "Extended Telemetry Device Reading Failure",
753         "Outlet Temperature Reading Failure",
754         "Volumetric Airflow Reading Failure",
755         "Policy Misconfiguration",
756         "Power Sensor Reading Failure",
757         "Inlet Temperature Reading Failure",
758         "Host Communication Error",
759         "Real-time Clock Synchronization Failure",
760         "Platform Shutdown Initiated by Intel NM Policy",
761         "Unknown"};
762     uint8_t nmTypeIdx = (data[0] & 0xf);
763     uint8_t domIdx = (data[1] & 0xf);
764     uint8_t errIdx = ((data[1] >> 4) & 0xf);
765 
766     if (nmTypeIdx == 2)
767     {
768         errLog = "SensorIntelNM";
769     }
770     else
771     {
772         errLog = "Unknown";
773     }
774 
775     errLog += ", Domain:" + nmDomName[domIdx] + ", ErrType:" +
776               nmErrType[errIdx] + ", Err:0x" + 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 = {
864         {0xE9, {"SYSTEM_EVENT", logSysEvent}},
865         {0x7D, {"THERM_THRESH_EVT", logThermalEvent}},
866         {0xAA, {"BUTTON", logDefault}},
867         {0xAB, {"POWER_STATE", logDefault}},
868         {0xEA, {"CRITICAL_IRQ", logCritIrq}},
869         {0x2B, {"POST_ERROR", logPostErr}},
870         {0x40, {"MACHINE_CHK_ERR", logMchChkErr}},
871         {0x41, {"PCIE_ERR", logPcieErr}},
872         {0x43, {"IIO_ERR", logIioErr}},
873         {0X63, {"MEMORY_ECC_ERR", logDefault}},
874         {0X87, {"MEMORY_ERR_LOG_DIS", logDefault}},
875         {0X51, {"PROCHOT_EXT", logDefault}},
876         {0X56, {"PWR_ERR", logPwrErr}},
877         {0xE6, {"CATERR_A", logCatErr}},
878         {0xEB, {"CATERR_B", logCatErr}},
879         {0xB3, {"CPU_DIMM_HOT", logDimmHot}},
880         {0x90, {"SOFTWARE_NMI", logSwNMI}},
881         {0x1C, {"CPU0_THERM_STATUS", logCPUThermalSts}},
882         {0x1D, {"CPU1_THERM_STATUS", logCPUThermalSts}},
883         {0x16, {"ME_POWER_STATE", logMEPwrState}},
884         {0x17, {"SPS_FW_HEALTH", logSPSFwHealth}},
885         {0x18, {"NM_EXCEPTION_A", logNmExcA}},
886         {0x08, {"PCH_THERM_THRESHOLD", logPCHThermal}},
887         {0x19, {"NM_HEALTH", logNmHealth}},
888         {0x1A, {"NM_CAPABILITIES", logNmCap}},
889         {0x1B, {"NM_THRESHOLD", logNmThreshold}},
890         {0x3B, {"PWR_THRESH_EVT", logPwrThreshold}},
891         {0xE7, {"MSMI", logMSMI}},
892         {0xC5, {"HPR_WARNING", logHprWarn}}};
893 
parseSelHelper(StdSELEntry * data,std::string & errStr)894 static void parseSelHelper(StdSELEntry* data, std::string& errStr)
895 {
896     /* Check if sensor type is OS_BOOT (0x1f) */
897     if (data->sensorType == 0x1F)
898     {
899         /* OS_BOOT used by OS */
900         switch (data->eventData1 & 0xF)
901         {
902             case 0x07:
903                 errStr = "Base OS/Hypervisor Installation started";
904                 break;
905             case 0x08:
906                 errStr = "Base OS/Hypervisor Installation completed";
907                 break;
908             case 0x09:
909                 errStr = "Base OS/Hypervisor Installation aborted";
910                 break;
911             case 0x0A:
912                 errStr = "Base OS/Hypervisor Installation failed";
913                 break;
914             default:
915                 errStr = "Unknown";
916         }
917         return;
918     }
919 
920     auto findSensorName = sensorNameTable.find(data->sensorNum);
921     if (findSensorName == sensorNameTable.end())
922     {
923         errStr = "Unknown";
924         return;
925     }
926     else
927     {
928         switch (data->sensorNum)
929         {
930             /* logMemErr function needs data from sensor type */
931             case memoryEccError:
932             case memoryErrLogDIS:
933                 findSensorName->second.second(&(data->sensorType), errStr);
934                 break;
935             /* Other sensor function needs only event data for parsing */
936             default:
937                 findSensorName->second.second(&(data->eventData1), errStr);
938         }
939     }
940 
941     if (((data->eventData3 & 0x80) >> 7) == 0)
942     {
943         errStr += " Assertion";
944     }
945     else
946     {
947         errStr += " Deassertion";
948     }
949 }
950 
parseDimmPhyloc(StdSELEntry * data,std::string & errStr)951 static void parseDimmPhyloc(StdSELEntry* data, std::string& errStr)
952 {
953     // Log when " All info available"
954     uint8_t chNum = (data->eventData3 & 0x18) >> 3;
955     uint8_t dimmNum = data->eventData3 & 0x7;
956     uint8_t rankNum = data->eventData2 & 0x03;
957     uint8_t nodeNum = (data->eventData3 & 0xE0) >> 5;
958 
959     if (chNum == 3 && dimmNum == 0)
960     {
961         errStr += " Node: " + std::to_string(nodeNum) + "," +
962                   " Card: " + std::to_string(chNum) + "," +
963                   " Module: " + std::to_string(dimmNum) + "," +
964                   " Rank Number: " + std::to_string(rankNum) + "," +
965                   "  Location: DIMM A0";
966     }
967     else if (chNum == 2 && dimmNum == 0)
968     {
969         errStr += " Node: " + std::to_string(nodeNum) + "," +
970                   " Card: " + std::to_string(chNum) + "," +
971                   " Module: " + std::to_string(dimmNum) + "," +
972                   " Rank Number: " + std::to_string(rankNum) + "," +
973                   " Location: DIMM B0";
974     }
975     else if (chNum == 4 && dimmNum == 0)
976     {
977         errStr += " Node: " + std::to_string(nodeNum) + "," +
978                   " Card: " + std::to_string(chNum) + "," +
979                   " Module: " + std::to_string(dimmNum) + "," +
980                   " Rank Number: " + std::to_string(rankNum) + "," +
981                   " Location: DIMM C0 ";
982     }
983     else if (chNum == 5 && dimmNum == 0)
984     {
985         errStr += " Node: " + std::to_string(nodeNum) + "," +
986                   " Card: " + std::to_string(chNum) + "," +
987                   " Module: " + std::to_string(dimmNum) + "," +
988                   " Rank Number: " + std::to_string(rankNum) + "," +
989                   " Location: DIMM D0";
990     }
991     else
992     {
993         errStr += " Node: " + std::to_string(nodeNum) + "," +
994                   " Card: " + std::to_string(chNum) + "," +
995                   " Module: " + std::to_string(dimmNum) + "," +
996                   " Rank Number: " + std::to_string(rankNum) + "," +
997                   " Location: DIMM Unknown";
998     }
999 }
1000 
parseStdSel(StdSELEntry * data,std::string & errStr)1001 static void parseStdSel(StdSELEntry* data, std::string& errStr)
1002 {
1003     std::stringstream tmpStream;
1004     tmpStream << std::hex << std::uppercase;
1005 
1006     /* TODO: add pal_add_cri_sel */
1007     switch (data->sensorNum)
1008     {
1009         case memoryEccError:
1010             switch (data->eventData1 & 0x0F)
1011             {
1012                 case 0x00:
1013                     errStr = "Correctable";
1014                     tmpStream << "DIMM" << std::setw(2) << std::setfill('0')
1015                               << data->eventData3 << " ECC err";
1016                     parseDimmPhyloc(data, errStr);
1017                     break;
1018                 case 0x01:
1019                     errStr = "Uncorrectable";
1020                     tmpStream << "DIMM" << std::setw(2) << std::setfill('0')
1021                               << data->eventData3 << " UECC err";
1022                     parseDimmPhyloc(data, errStr);
1023                     break;
1024                 case 0x02:
1025                     errStr = "Parity";
1026                     break;
1027                 case 0x05:
1028                     errStr = "Correctable ECC error Logging Limit Reached";
1029                     break;
1030                 default:
1031                     errStr = "Unknown";
1032             }
1033             break;
1034         case memoryErrLogDIS:
1035             if ((data->eventData1 & 0x0F) == 0)
1036             {
1037                 errStr = "Correctable Memory Error Logging Disabled";
1038             }
1039             else
1040             {
1041                 errStr = "Unknown";
1042             }
1043             break;
1044         default:
1045             parseSelHelper(data, errStr);
1046             return;
1047     }
1048 
1049     errStr += " (DIMM " + std::to_string(data->eventData3) + ")";
1050     errStr += " Logical Rank " + std::to_string(data->eventData2 & 0x03);
1051 
1052     switch ((data->eventData2 & 0x0C) >> 2)
1053     {
1054         case 0x00:
1055             // Ignore when " All info available"
1056             break;
1057         case 0x01:
1058             errStr += " DIMM info not valid";
1059             break;
1060         case 0x02:
1061             errStr += " CHN info not valid";
1062             break;
1063         case 0x03:
1064             errStr += " CPU info not valid";
1065             break;
1066         default:
1067             errStr += " Unknown";
1068     }
1069 
1070     if (((data->eventType & 0x80) >> 7) == 0)
1071     {
1072         errStr += " Assertion";
1073     }
1074     else
1075     {
1076         errStr += " Deassertion";
1077     }
1078 
1079     return;
1080 }
1081 
parseOemSel(TsOemSELEntry * data,std::string & errStr)1082 static void parseOemSel(TsOemSELEntry* data, std::string& errStr)
1083 {
1084     std::stringstream tmpStream;
1085     tmpStream << std::hex << std::uppercase << std::setfill('0');
1086 
1087     switch (data->recordType)
1088     {
1089         case 0xC0:
1090             tmpStream << "VID:0x" << std::setw(2) << (int)data->oemData[1]
1091                       << std::setw(2) << (int)data->oemData[0] << " DID:0x"
1092                       << std::setw(2) << (int)data->oemData[3] << std::setw(2)
1093                       << (int)data->oemData[2] << " Slot:0x" << std::setw(2)
1094                       << (int)data->oemData[4] << " Error ID:0x" << std::setw(2)
1095                       << (int)data->oemData[5];
1096             break;
1097         case 0xC2:
1098             tmpStream << "Extra info:0x" << std::setw(2)
1099                       << (int)data->oemData[1] << " MSCOD:0x" << std::setw(2)
1100                       << (int)data->oemData[3] << std::setw(2)
1101                       << (int)data->oemData[2] << " MCACOD:0x" << std::setw(2)
1102                       << (int)data->oemData[5] << std::setw(2)
1103                       << (int)data->oemData[4];
1104             break;
1105         case 0xC3:
1106             int bank = (data->oemData[1] & 0xf0) >> 4;
1107             int col = ((data->oemData[1] & 0x0f) << 8) | data->oemData[2];
1108 
1109             tmpStream << "Fail Device:0x" << std::setw(2)
1110                       << (int)data->oemData[0] << " Bank:0x" << std::setw(2)
1111                       << bank << " Column:0x" << std::setw(2) << col
1112                       << " Failed Row:0x" << std::setw(2)
1113                       << (int)data->oemData[3] << std::setw(2)
1114                       << (int)data->oemData[4] << std::setw(2)
1115                       << (int)data->oemData[5];
1116     }
1117 
1118     errStr = tmpStream.str();
1119 
1120     return;
1121 }
1122 
dimmLocationStr(uint8_t socket,uint8_t channel,uint8_t slot)1123 static std::string dimmLocationStr(uint8_t socket, uint8_t channel,
1124                                    uint8_t slot)
1125 {
1126     uint8_t sled = (socket >> 4) & 0x3;
1127 
1128     socket &= 0xf;
1129     if (channel == 0xFF && slot == 0xFF)
1130     {
1131         return std::format(
1132             "DIMM Slot Location: Sled {:02}/Socket {:02}, Channel unknown"
1133             ", Slot unknown, DIMM unknown",
1134             sled, socket);
1135     }
1136     else
1137     {
1138         channel &= 0xf;
1139         slot &= 0xf;
1140         const char label[] = {'A', 'C', 'B', 'D'};
1141         uint8_t idx = socket * 2 + slot;
1142         return std::format("DIMM Slot Location: Sled {:02}/Socket {:02}"
1143                            ", Channel {:02}, Slot {:02} DIMM {}",
1144                            sled, socket, channel, slot,
1145                            (idx < sizeof(label))
1146                                ? label[idx] + std::to_string(channel)
1147                                : "NA");
1148     }
1149 }
1150 
parseOemUnifiedSel(NtsOemSELEntry * data,std::string & errStr)1151 static void parseOemUnifiedSel(NtsOemSELEntry* data, std::string& errStr)
1152 {
1153     uint8_t* ptr = data->oemData;
1154     uint8_t eventType = ptr[5] & 0xf;
1155     int genInfo = ptr[0];
1156     int errType = genInfo & 0x0f;
1157     std::vector<std::string> dimmErr = {
1158         "Memory training failure",
1159         "Memory correctable error",
1160         "Memory uncorrectable error",
1161         "Memory correctable error (Patrol scrub)",
1162         "Memory uncorrectable error (Patrol scrub)",
1163         "Memory Parity Error (PCC=0)",
1164         "Memory Parity Error (PCC=1)",
1165         "Memory PMIC Error",
1166         "CXL Memory training error",
1167         "Reserved"};
1168     std::vector<std::string> postEvent = {
1169         "System PXE boot fail",
1170         "CMOS/NVRAM configuration cleared",
1171         "TPM Self-Test Fail",
1172         "Boot Drive failure",
1173         "Data Drive failure",
1174         "Received invalid boot order request from BMC",
1175         "System HTTP boot fail",
1176         "BIOS fails to get the certificate from BMC",
1177         "Password cleared by jumper",
1178         "DXE FV check failure",
1179         "AMD ABL failure",
1180         "Reserved"};
1181     std::vector<std::string> certErr = {
1182         "No certificate at BMC", "IPMI transaction fail",
1183         "Certificate data corrupted", "Reserved"};
1184     std::vector<std::string> pcieEvent = {
1185         "PCIe DPC Event",
1186         "PCIe LER Event",
1187         "PCIe Link Retraining and Recovery",
1188         "PCIe Link CRC Error Check and Retry",
1189         "PCIe Corrupt Data Containment",
1190         "PCIe Express ECRC",
1191         "Reserved"};
1192     std::vector<std::string> memEvent = {
1193         "Memory PPR event",
1194         "Memory Correctable Error logging limit reached",
1195         "Memory disable/map-out for FRB",
1196         "Memory SDDC",
1197         "Memory Address range/Partial mirroring",
1198         "Memory ADDDC",
1199         "Memory SMBus hang recovery",
1200         "No DIMM in System",
1201         "Reserved"};
1202     std::vector<std::string> memPprTime = {"Boot time", "Autonomous",
1203                                            "Run time", "Reserved"};
1204     std::vector<std::string> memPpr = {"PPR success", "PPR fail", "PPR request",
1205                                        "Reserved"};
1206     std::vector<std::string> memAdddc = {
1207         "Bank VLS", "r-Bank VLS + re-buddy", "r-Bank VLS + Rank VLS",
1208         "r-Rank VLS + re-buddy", "Reserved"};
1209     std::vector<std::string> pprEvent = {"PPR disable", "Soft PPR", "Hard PPR",
1210                                          "Reserved"};
1211 
1212     std::stringstream tmpStream;
1213 
1214     switch (errType)
1215     {
1216         case unifiedPcieErr:
1217             tmpStream << std::format(
1218                 "GeneralInfo: x86/PCIeErr(0x{:02X})"
1219                 ", Bus {:02X}/Dev {:02X}/Fun {:02X}, TotalErrID1Cnt: 0x{:04X}"
1220                 ", ErrID2: 0x{:02X}, ErrID1: 0x{:02X}",
1221                 genInfo, ptr[8], ptr[7] >> 3, ptr[7] & 0x7,
1222                 (ptr[10] << 8) | ptr[9], ptr[11], ptr[12]);
1223             break;
1224         case unifiedMemErr:
1225             eventType = ptr[9] & 0xf;
1226             tmpStream << std::format(
1227                 "GeneralInfo: MemErr(0x{:02X}), {}, DIMM Failure Event: {}",
1228                 genInfo, dimmLocationStr(ptr[5], ptr[6], ptr[7]),
1229                 dimmErr[std::min(eventType,
1230                                  static_cast<uint8_t>(dimmErr.size() - 1))]);
1231 
1232             if (static_cast<MemErrType>(eventType) == MemErrType::memTrainErr ||
1233                 static_cast<MemErrType>(eventType) == MemErrType::memPmicErr)
1234             {
1235                 bool amd = ptr[9] & 0x80;
1236                 tmpStream << std::format(
1237                     ", Major Code: 0x{:02X}, Minor Code: 0x{:0{}X}", ptr[10],
1238                     amd ? (ptr[12] << 8 | ptr[11]) : ptr[11], amd ? 4 : 2);
1239             }
1240             break;
1241         case unifiedIioErr:
1242             tmpStream << std::format(
1243                 "GeneralInfo: IIOErr(0x{:02X})"
1244                 ", IIO Port Location: Sled {:02}/Socket {:02}, Stack 0x{:02X}"
1245                 ", Error Type: 0x{:02X}, Error Severity: 0x{:02X}"
1246                 ", Error ID: 0x{:02X}",
1247                 genInfo, (ptr[5] >> 4) & 0x3, ptr[5] & 0xf, ptr[6], ptr[10],
1248                 ptr[11] & 0xf, ptr[12]);
1249             break;
1250         case unifiedPostEvt:
1251             tmpStream << std::format(
1252                 "GeneralInfo: POST(0x{:02X}), POST Failure Event: {}", genInfo,
1253                 postEvent[std::min(
1254                     eventType, static_cast<uint8_t>(postEvent.size() - 1))]);
1255 
1256             switch (static_cast<PostEvtType>(eventType))
1257             {
1258                 case PostEvtType::pxeBootFail:
1259                 case PostEvtType::httpBootFail:
1260                 {
1261                     uint8_t failType = ptr[10] & 0xf;
1262                     tmpStream
1263                         << std::format(", Fail Type: {}, Error Code: 0x{:02X}",
1264                                        (failType == 4 || failType == 6)
1265                                            ? std::format("IPv{} fail", failType)
1266                                            : std::format("0x{:02X}", ptr[10]),
1267                                        ptr[11]);
1268                     break;
1269                 }
1270                 case PostEvtType::getCertFail:
1271                     tmpStream << std::format(
1272                         ", Failure Detail: {}",
1273                         certErr[std::min(
1274                             ptr[9], static_cast<uint8_t>(certErr.size() - 1))]);
1275                     break;
1276                 case PostEvtType::amdAblFail:
1277                     tmpStream << std::format(", ABL Error Code: 0x{:04X}",
1278                                              (ptr[12] << 8) | ptr[11]);
1279                     break;
1280             }
1281             break;
1282         case unifiedPcieEvt:
1283             tmpStream << std::format(
1284                 "GeneralInfo: PCIeEvent(0x{:02X}), PCIe Failure Event: {}",
1285                 genInfo,
1286                 pcieEvent[std::min(
1287                     eventType, static_cast<uint8_t>(pcieEvent.size() - 1))]);
1288 
1289             if (static_cast<PcieEvtType>(eventType) == PcieEvtType::dpc)
1290             {
1291                 tmpStream << std::format(
1292                     ", Status: 0x{:04X}, Source ID: 0x{:04X}",
1293                     (ptr[8] << 8) | ptr[7], (ptr[10] << 8) | ptr[9]);
1294             }
1295             break;
1296         case unifiedMemEvt:
1297             eventType = ptr[9] & 0xf;
1298             tmpStream
1299                 << std::format("GeneralInfo: MemEvent(0x{:02X})", genInfo)
1300                 << (static_cast<MemEvtType>(eventType) != MemEvtType::noDimm
1301                         ? std::format(", {}",
1302                                       dimmLocationStr(ptr[5], ptr[6], ptr[7]))
1303                         : "")
1304                 << ", DIMM Failure Event: ";
1305 
1306             switch (static_cast<MemEvtType>(eventType))
1307             {
1308                 case MemEvtType::ppr:
1309                     tmpStream << std::format("{} {}",
1310                                              memPprTime[(ptr[10] >> 2) & 0x3],
1311                                              memPpr[ptr[10] & 0x3]);
1312                     break;
1313                 case MemEvtType::adddc:
1314                     tmpStream << std::format(
1315                         "{} {}",
1316                         memEvent[std::min(eventType, static_cast<uint8_t>(
1317                                                          memEvent.size() - 1))],
1318                         memAdddc[std::min(
1319                             static_cast<uint8_t>(ptr[11] & 0xf),
1320                             static_cast<uint8_t>(memAdddc.size() - 1))]);
1321                     break;
1322                 default:
1323                     tmpStream << std::format(
1324                         "{}", memEvent[std::min(
1325                                   eventType,
1326                                   static_cast<uint8_t>(memEvent.size() - 1))]);
1327                     break;
1328             }
1329             break;
1330         case unifiedBootGuard:
1331             tmpStream << std::format(
1332                 "GeneralInfo: Boot Guard ACM Failure Events(0x{:02X})"
1333                 ", Error Class: 0x{:02X}, Error Code: 0x{:02X}",
1334                 genInfo, ptr[9], ptr[10]);
1335             break;
1336         case unifiedPprEvt:
1337             tmpStream << std::format(
1338                 "GeneralInfo: PPREvent(0x{:02X}), {}"
1339                 ", DIMM Info: {:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
1340                 genInfo,
1341                 pprEvent[std::min(eventType,
1342                                   static_cast<uint8_t>(pprEvent.size() - 1))],
1343                 ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12]);
1344             break;
1345         default:
1346             std::vector<uint8_t> oemData(ptr, ptr + 13);
1347             std::string oemDataStr;
1348             toHexStr(oemData, oemDataStr);
1349             tmpStream << std::format("Undefined Error Type(0x{:02X}), Raw: {}",
1350                                      errType, oemDataStr);
1351     }
1352 
1353     errStr = tmpStream.str();
1354 
1355     return;
1356 }
1357 
parseSelData(uint8_t fruId,std::vector<uint8_t> & reqData,std::string & msgLog)1358 static void parseSelData(uint8_t fruId, std::vector<uint8_t>& reqData,
1359                          std::string& msgLog)
1360 {
1361     /* Get record type */
1362     int recType = reqData[2];
1363     std::string errType, errLog;
1364 
1365     uint8_t* ptr = NULL;
1366 
1367     std::stringstream recTypeStream;
1368     recTypeStream << std::hex << std::uppercase << std::setfill('0')
1369                   << std::setw(2) << recType;
1370 
1371     msgLog = "SEL Entry: FRU: " + std::to_string(fruId) + ", Record: ";
1372 
1373     if (recType == stdErrType)
1374     {
1375         StdSELEntry* data = reinterpret_cast<StdSELEntry*>(&reqData[0]);
1376         std::string sensorName;
1377 
1378         errType = stdErr;
1379         if (data->sensorType == 0x1F)
1380         {
1381             sensorName = "OS";
1382         }
1383         else
1384         {
1385             auto findSensorName = sensorNameTable.find(data->sensorNum);
1386             if (findSensorName == sensorNameTable.end())
1387             {
1388                 sensorName = "Unknown";
1389             }
1390             else
1391             {
1392                 sensorName = findSensorName->second.first;
1393             }
1394         }
1395 
1396         parseStdSel(data, errLog);
1397         ptr = &(data->eventData1);
1398         std::vector<uint8_t> evtData(ptr, ptr + 3);
1399         std::string eventData;
1400         toHexStr(evtData, eventData);
1401 
1402         std::stringstream senNumStream;
1403         senNumStream << std::hex << std::uppercase << std::setfill('0')
1404                      << std::setw(2) << (int)(data->sensorNum);
1405 
1406         msgLog += errType + " (0x" + recTypeStream.str() +
1407                   "), Sensor: " + sensorName + " (0x" + senNumStream.str() +
1408                   "), Event Data: (" + eventData + ") " + errLog;
1409     }
1410     else if ((recType >= oemTSErrTypeMin) && (recType <= oemTSErrTypeMax))
1411     {
1412         /* timestamped OEM SEL records */
1413         TsOemSELEntry* data = reinterpret_cast<TsOemSELEntry*>(&reqData[0]);
1414         ptr = data->mfrId;
1415         std::vector<uint8_t> mfrIdData(ptr, ptr + 3);
1416         std::string mfrIdStr;
1417         toHexStr(mfrIdData, mfrIdStr);
1418 
1419         ptr = data->oemData;
1420         std::vector<uint8_t> oemData(ptr, ptr + 6);
1421         std::string oemDataStr;
1422         toHexStr(oemData, oemDataStr);
1423 
1424         errType = oemTSErr;
1425         parseOemSel(data, errLog);
1426 
1427         msgLog += errType + " (0x" + recTypeStream.str() + "), MFG ID: " +
1428                   mfrIdStr + ", OEM Data: (" + oemDataStr + ") " + errLog;
1429     }
1430     else if (recType == fbUniErrType)
1431     {
1432         NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]);
1433         errType = fbUniSELErr;
1434         parseOemUnifiedSel(data, errLog);
1435         msgLog += errType + " (0x" + recTypeStream.str() + "), " + errLog;
1436     }
1437     else if ((recType >= oemNTSErrTypeMin) && (recType <= oemNTSErrTypeMax))
1438     {
1439         /* Non timestamped OEM SEL records */
1440         NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]);
1441         errType = oemNTSErr;
1442 
1443         ptr = data->oemData;
1444         std::vector<uint8_t> oemData(ptr, ptr + 13);
1445         std::string oemDataStr;
1446         toHexStr(oemData, oemDataStr);
1447 
1448         parseOemSel((TsOemSELEntry*)data, errLog);
1449         msgLog += errType + " (0x" + recTypeStream.str() + "), OEM Data: (" +
1450                   oemDataStr + ") " + errLog;
1451     }
1452     else
1453     {
1454         errType = unknownErr;
1455         toHexStr(reqData, errLog);
1456         msgLog += errType + " (0x" + recTypeStream.str() +
1457                   ") RawData: " + errLog;
1458     }
1459 }
1460 
1461 } // namespace fb_oem::ipmi::sel
1462 
1463 namespace ipmi
1464 {
1465 
1466 namespace storage
1467 {
1468 
1469 static void registerSELFunctions() __attribute__((constructor));
1470 static fb_oem::ipmi::sel::SELData selObj __attribute__((init_priority(101)));
1471 
1472 ipmi::RspType<uint8_t,  // SEL version
1473               uint16_t, // SEL entry count
1474               uint16_t, // free space
1475               uint32_t, // last add timestamp
1476               uint32_t, // last erase timestamp
1477               uint8_t>  // operation support
ipmiStorageGetSELInfo()1478     ipmiStorageGetSELInfo()
1479 {
1480     fb_oem::ipmi::sel::GetSELInfoData info;
1481 
1482     selObj.getInfo(info);
1483     return ipmi::responseSuccess(info.selVersion, info.entries, info.freeSpace,
1484                                  info.addTimeStamp, info.eraseTimeStamp,
1485                                  info.operationSupport);
1486 }
1487 
1488 ipmi::RspType<uint16_t, std::vector<uint8_t>>
ipmiStorageGetSELEntry(std::vector<uint8_t> data)1489     ipmiStorageGetSELEntry(std::vector<uint8_t> data)
1490 {
1491     if (data.size() != sizeof(fb_oem::ipmi::sel::GetSELEntryRequest))
1492     {
1493         return ipmi::responseReqDataLenInvalid();
1494     }
1495 
1496     fb_oem::ipmi::sel::GetSELEntryRequest* reqData =
1497         reinterpret_cast<fb_oem::ipmi::sel::GetSELEntryRequest*>(&data[0]);
1498 
1499     if (reqData->reservID != 0)
1500     {
1501         if (!checkSELReservation(reqData->reservID))
1502         {
1503             return ipmi::responseInvalidReservationId();
1504         }
1505     }
1506 
1507     uint16_t selCnt = selObj.getCount();
1508     if (selCnt == 0)
1509     {
1510         return ipmi::responseSensorInvalid();
1511     }
1512 
1513     /* If it is asked for first entry */
1514     if (reqData->recordID == fb_oem::ipmi::sel::firstEntry)
1515     {
1516         /* First Entry (0x0000) as per Spec */
1517         reqData->recordID = 1;
1518     }
1519     else if (reqData->recordID == fb_oem::ipmi::sel::lastEntry)
1520     {
1521         /* Last entry (0xFFFF) as per Spec */
1522         reqData->recordID = selCnt;
1523     }
1524 
1525     std::string ipmiRaw;
1526 
1527     if (selObj.getEntry(reqData->recordID, ipmiRaw) < 0)
1528     {
1529         return ipmi::responseSensorInvalid();
1530     }
1531 
1532     std::vector<uint8_t> recDataBytes;
1533     if (fromHexStr(ipmiRaw, recDataBytes) < 0)
1534     {
1535         return ipmi::responseUnspecifiedError();
1536     }
1537 
1538     /* Identify the next SEL record ID. If recordID is same as
1539      * total SeL count then next id should be last entry else
1540      * it should be incremented by 1 to current RecordID
1541      */
1542     uint16_t nextRecord;
1543     if (reqData->recordID == selCnt)
1544     {
1545         nextRecord = fb_oem::ipmi::sel::lastEntry;
1546     }
1547     else
1548     {
1549         nextRecord = reqData->recordID + 1;
1550     }
1551 
1552     if (reqData->readLen == fb_oem::ipmi::sel::entireRecord)
1553     {
1554         return ipmi::responseSuccess(nextRecord, recDataBytes);
1555     }
1556     else
1557     {
1558         if (reqData->offset >= fb_oem::ipmi::sel::selRecordSize ||
1559             reqData->readLen > fb_oem::ipmi::sel::selRecordSize)
1560         {
1561             return ipmi::responseUnspecifiedError();
1562         }
1563         std::vector<uint8_t> recPartData;
1564 
1565         auto diff = fb_oem::ipmi::sel::selRecordSize - reqData->offset;
1566         auto readLength = std::min(diff, static_cast<int>(reqData->readLen));
1567 
1568         for (int i = 0; i < readLength; i++)
1569         {
1570             recPartData.push_back(recDataBytes[i + reqData->offset]);
1571         }
1572         return ipmi::responseSuccess(nextRecord, recPartData);
1573     }
1574 }
1575 
1576 ipmi::RspType<uint16_t>
ipmiStorageAddSELEntry(ipmi::Context::ptr ctx,std::vector<uint8_t> data)1577     ipmiStorageAddSELEntry(ipmi::Context::ptr ctx, std::vector<uint8_t> data)
1578 {
1579     /* Per the IPMI spec, need to cancel any reservation when a
1580      * SEL entry is added
1581      */
1582     cancelSELReservation();
1583 
1584     if (data.size() != fb_oem::ipmi::sel::selRecordSize)
1585     {
1586         return ipmi::responseReqDataLenInvalid();
1587     }
1588 
1589     std::string ipmiRaw, logErr;
1590     toHexStr(data, ipmiRaw);
1591 
1592     /* Parse sel data and get an error log to be filed */
1593     fb_oem::ipmi::sel::parseSelData((ctx->hostIdx + 1), data, logErr);
1594 
1595     static const std::string openBMCMessageRegistryVersion("0.1");
1596     std::string messageID =
1597         "OpenBMC." + openBMCMessageRegistryVersion + ".SELEntryAdded";
1598 
1599     /* Log the Raw SEL message to the journal */
1600     std::string journalMsg = "SEL Entry Added: " + ipmiRaw;
1601 
1602     phosphor::logging::log<phosphor::logging::level::INFO>(
1603         journalMsg.c_str(),
1604         phosphor::logging::entry("IPMISEL_MESSAGE_ID=%s", messageID.c_str()),
1605         phosphor::logging::entry("IPMISEL_MESSAGE_ARGS=%s", logErr.c_str()));
1606 
1607     std::map<std::string, std::string> ad;
1608     std::string severity = "xyz.openbmc_project.Logging.Entry.Level.Critical";
1609     ad.emplace("IPMI_RAW", ipmiRaw);
1610 
1611     auto bus = sdbusplus::bus::new_default();
1612     auto reqMsg = bus.new_method_call(
1613         "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1614         "xyz.openbmc_project.Logging.Create", "Create");
1615     reqMsg.append(logErr, severity, ad);
1616 
1617     try
1618     {
1619         bus.call(reqMsg);
1620     }
1621     catch (sdbusplus::exception_t& e)
1622     {
1623         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1624     }
1625 
1626     int responseID = selObj.addEntry(ipmiRaw.c_str());
1627     if (responseID < 0)
1628     {
1629         return ipmi::responseUnspecifiedError();
1630     }
1631     return ipmi::responseSuccess((uint16_t)responseID);
1632 }
1633 
ipmiStorageClearSEL(uint16_t reservationID,const std::array<uint8_t,3> & clr,uint8_t eraseOperation)1634 ipmi::RspType<uint8_t> ipmiStorageClearSEL(uint16_t reservationID,
1635                                            const std::array<uint8_t, 3>& clr,
1636                                            uint8_t eraseOperation)
1637 {
1638     if (!checkSELReservation(reservationID))
1639     {
1640         return ipmi::responseInvalidReservationId();
1641     }
1642 
1643     static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
1644     if (clr != clrExpected)
1645     {
1646         return ipmi::responseInvalidFieldRequest();
1647     }
1648 
1649     /* If there is no sel then return erase complete */
1650     if (selObj.getCount() == 0)
1651     {
1652         return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1653     }
1654 
1655     /* Erasure status cannot be fetched, so always return erasure
1656      * status as `erase completed`.
1657      */
1658     if (eraseOperation == fb_oem::ipmi::sel::getEraseStatus)
1659     {
1660         return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1661     }
1662 
1663     /* Check that initiate erase is correct */
1664     if (eraseOperation != fb_oem::ipmi::sel::initiateErase)
1665     {
1666         return ipmi::responseInvalidFieldRequest();
1667     }
1668 
1669     /* Per the IPMI spec, need to cancel any reservation when the
1670      * SEL is cleared
1671      */
1672     cancelSELReservation();
1673 
1674     /* Clear the complete Sel Json object */
1675     if (selObj.clear() < 0)
1676     {
1677         return ipmi::responseUnspecifiedError();
1678     }
1679 
1680     return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1681 }
1682 
ipmiStorageGetSELTime()1683 ipmi::RspType<uint32_t> ipmiStorageGetSELTime()
1684 {
1685     struct timespec selTime = {};
1686 
1687     if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
1688     {
1689         return ipmi::responseUnspecifiedError();
1690     }
1691 
1692     return ipmi::responseSuccess(selTime.tv_sec);
1693 }
1694 
ipmiStorageSetSELTime(uint32_t)1695 ipmi::RspType<> ipmiStorageSetSELTime(uint32_t)
1696 {
1697     // Set SEL Time is not supported
1698     return ipmi::responseInvalidCommand();
1699 }
1700 
ipmiStorageGetSELTimeUtcOffset()1701 ipmi::RspType<uint16_t> ipmiStorageGetSELTimeUtcOffset()
1702 {
1703     /* TODO: For now, the SEL time stamp is based on UTC time,
1704      * so return 0x0000 as offset. Might need to change once
1705      * supporting zones in SEL time stamps
1706      */
1707 
1708     uint16_t utcOffset = 0x0000;
1709     return ipmi::responseSuccess(utcOffset);
1710 }
1711 
registerSELFunctions()1712 void registerSELFunctions()
1713 {
1714     // <Get SEL Info>
1715     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1716                           ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
1717                           ipmiStorageGetSELInfo);
1718 
1719     // <Get SEL Entry>
1720     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1721                           ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
1722                           ipmiStorageGetSELEntry);
1723 
1724     // <Add SEL Entry>
1725     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1726                           ipmi::storage::cmdAddSelEntry,
1727                           ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
1728 
1729     // <Clear SEL>
1730     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1731                           ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
1732                           ipmiStorageClearSEL);
1733 
1734     // <Get SEL Time>
1735     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1736                           ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
1737                           ipmiStorageGetSELTime);
1738 
1739     // <Set SEL Time>
1740     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1741                           ipmi::storage::cmdSetSelTime,
1742                           ipmi::Privilege::Operator, ipmiStorageSetSELTime);
1743 
1744     // <Get SEL Time UTC Offset>
1745     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1746                           ipmi::storage::cmdGetSelTimeUtcOffset,
1747                           ipmi::Privilege::User,
1748                           ipmiStorageGetSELTimeUtcOffset);
1749 
1750     return;
1751 }
1752 
1753 } // namespace storage
1754 } // namespace ipmi
1755