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