blob: 684c0273ea7132bbc7f3af2682b3579c321d73f5 [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
Vijay Khemka11b9c3b2019-08-21 15:21:42 -070018#include <boost/algorithm/string/join.hpp>
Patrick Williams020ff3e2022-09-20 12:09:32 -050019#include <boost/container/flat_map.hpp>
Vijay Khemka63c99be2020-05-27 19:14:35 -070020#include <ipmid/api.hpp>
Vijay Khemka11b9c3b2019-08-21 15:21:42 -070021#include <nlohmann/json.hpp>
Vijay Khemka11b9c3b2019-08-21 15:21:42 -070022#include <phosphor-logging/log.hpp>
23#include <sdbusplus/message/types.hpp>
24#include <sdbusplus/timer.hpp>
25#include <storagecommands.hpp>
26
Vijay Khemka63c99be2020-05-27 19:14:35 -070027#include <fstream>
28#include <iostream>
29#include <sstream>
30
cchoux74519032024-02-04 20:35:29 +080031enum class MemErrType
32{
33 memTrainErr = 0,
34 memPmicErr = 7
35};
36
37enum class PostEvtType
38{
39 pxeBootFail = 0,
40 httpBootFail = 6,
41 getCertFail = 7,
42 amdAblFail = 10
43};
44
45enum class PcieEvtType
46{
47 dpc = 0
48};
49
50enum class MemEvtType
51{
52 ppr = 0,
53 adddc = 5,
54 noDimm = 7
55};
56
Vijay Khemka11b9c3b2019-08-21 15:21:42 -070057//----------------------------------------------------------------------
58// Platform specific functions for storing app data
59//----------------------------------------------------------------------
60
Vijay Khemka139aa4f2019-08-16 09:57:41 -070061static 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
Vijay Khemka63c99be2020-05-27 19:14:35 -070071static void toHexStr(std::vector<uint8_t>& bytes, std::string& hexStr)
Vijay Khemka11b9c3b2019-08-21 15:21:42 -070072{
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
Vijay Khemka63c99be2020-05-27 19:14:35 -070082static int fromHexStr(const std::string hexStr, std::vector<uint8_t>& data)
Vijay Khemka11b9c3b2019-08-21 15:21:42 -070083{
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 }
Patrick Williams35d12542021-10-06 11:21:13 -050091 catch (const std::invalid_argument& e)
Vijay Khemka11b9c3b2019-08-21 15:21:42 -070092 {
93 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
94 return -1;
95 }
Patrick Williams35d12542021-10-06 11:21:13 -050096 catch (const std::out_of_range& e)
Vijay Khemka11b9c3b2019-08-21 15:21:42 -070097 {
98 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
99 return -1;
100 }
101 }
102 return 0;
103}
104
105namespace fb_oem::ipmi::sel
106{
107
108class SELData
109{
110 private:
111 nlohmann::json selDataObj;
112
113 void flush()
114 {
115 std::ofstream file(SEL_JSON_DATA_FILE);
116 file << selDataObj;
117 file.close();
118 }
119
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:
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
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
166 uint32_t getCount()
167 {
168 return selDataObj[KEY_SEL_COUNT];
169 }
170
Vijay Khemka63c99be2020-05-27 19:14:35 -0700171 void getInfo(GetSELInfoData& info)
Vijay Khemka11b9c3b2019-08-21 15:21:42 -0700172 {
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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700181 int getEntry(uint32_t index, std::string& rawStr)
Vijay Khemka11b9c3b2019-08-21 15:21:42 -0700182 {
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
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
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700221/*
Manojkiran Eda519530b2024-06-17 11:46:15 +0530222 * A Function to parse common SEL message, a helper function
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700223 * for parseStdSel.
224 *
Manojkiran Eda519530b2024-06-17 11:46:15 +0530225 * Note that this function __CANNOT__ be overridden.
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700226 * To add board specific routine, please override parseStdSel.
227 */
228
229/*Used by decoding ME event*/
230std::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 */
Willy Tue39f9392022-06-15 13:24:20 -0700236static void logDefault(uint8_t*, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700237{
238 errLog = "Unknown";
239}
240
Vijay Khemka63c99be2020-05-27 19:14:35 -0700241static void logSysEvent(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700242{
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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700275static void logThermalEvent(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700276{
277 if (data[0] == 0x1)
278 {
279 errLog = "Limit Exceeded";
280 }
281 else
282 {
283 errLog = "Unknown";
284 }
285}
286
Vijay Khemka63c99be2020-05-27 19:14:35 -0700287static void logCritIrq(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700288{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700289 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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700305static void logPostErr(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700306{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700307 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 {
Patrick Williams2405ae92023-05-10 07:50:09 -0500323 errLog += ", OEM Post Code 0x" + byteToStr(data[2]) +
324 byteToStr(data[1]);
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700325
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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700343static void logMchChkErr(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700344{
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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700364static void logPcieErr(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700365{
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;
Vijay Khemkad1194022020-05-27 18:58:33 -0700390 case 0xD:
391 {
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700392 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;
Vijay Khemkad1194022020-05-27 18:58:33 -0700397 case 0xE:
398 {
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700399 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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700414static void logIioErr(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700415{
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
Willy Tue39f9392022-06-15 13:24:20 -0700440[[maybe_unused]] static void logMemErr(uint8_t* dataPtr, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700441{
442 uint8_t snrType = dataPtr[0];
443 uint8_t snrNum = dataPtr[1];
Vijay Khemka63c99be2020-05-27 19:14:35 -0700444 uint8_t* data = &(dataPtr[3]);
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700445
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);
Manikandan Elumalaic056dc02020-12-11 06:20:32 +0530505 std::string dimmStr = "DIMM#" + std::to_string(data[2] & 0x7);
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700506
507 switch ((data[1] & 0xC) >> 2)
508 {
Vijay Khemkad1194022020-05-27 18:58:33 -0700509 case 0x0:
510 {
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700511 /* All Info Valid */
Willy Tue39f9392022-06-15 13:24:20 -0700512 [[maybe_unused]] uint8_t chnNum = (data[2] & 0x1C) >> 2;
513 [[maybe_unused]] uint8_t dimmNum = data[2] & 0x3;
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700514
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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700555static void logPwrErr(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700556{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700557 if (data[0] == 0x1)
558 {
559 errLog = "SYS_PWROK failure";
Manojkiran Eda519530b2024-06-17 11:46:15 +0530560 /* Also try logging to Critical log file, if available */
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700561 /* "SYS_PWROK failure,FRU:1" */
562 }
563 else if (data[0] == 0x2)
564 {
565 errLog = "PCH_PWROK failure";
Manojkiran Eda519530b2024-06-17 11:46:15 +0530566 /* Also try logging to Critical log file, if available */
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700567 /* "PCH_PWROK failure,FRU:1" */
568 }
569 else
570 {
571 errLog = "Unknown";
572 }
573}
574
Vijay Khemka63c99be2020-05-27 19:14:35 -0700575static void logCatErr(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700576{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700577 if (data[0] == 0x0)
578 {
579 errLog = "IERR/CATERR";
Manojkiran Eda519530b2024-06-17 11:46:15 +0530580 /* Also try logging to Critical log file, if available */
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700581 /* "IERR,FRU:1 */
582 }
583 else if (data[0] == 0xB)
584 {
585 errLog = "MCERR/CATERR";
Manojkiran Eda519530b2024-06-17 11:46:15 +0530586 /* Also try logging to Critical log file, if available */
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700587 /* "MCERR,FRU:1 */
588 }
589 else
590 {
591 errLog = "Unknown";
592 }
593}
594
Vijay Khemka63c99be2020-05-27 19:14:35 -0700595static void logDimmHot(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700596{
597 if ((data[0] << 16 | data[1] << 8 | data[2]) == 0x01FFFF)
598 {
599 errLog = "SOC MEMHOT";
600 }
601 else
602 {
603 errLog = "Unknown";
Manojkiran Eda519530b2024-06-17 11:46:15 +0530604 /* Also try logging to Critical log file, if available */
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700605 /* ""CPU_DIMM_HOT %s,FRU:1" */
606 }
607}
608
Vijay Khemka63c99be2020-05-27 19:14:35 -0700609static void logSwNMI(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700610{
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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700621static void logCPUThermalSts(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700622{
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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700639static void logMEPwrState(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700640{
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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700655static void logSPSFwHealth(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700656{
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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700700static void logNmExcA(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700701{
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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700713static void logPCHThermal(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700714{
Patrick Williams010dee02024-08-16 15:19:44 -0400715 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"};
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700728
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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700742static void logNmHealth(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700743{
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
Patrick Williams010dee02024-08-16 15:19:44 -0400775 errLog += ", Domain:" + nmDomName[domIdx] + ", ErrType:" +
776 nmErrType[errIdx] + ", Err:0x" + byteToStr(data[2]);
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700777}
778
Vijay Khemka63c99be2020-05-27 19:14:35 -0700779static void logNmCap(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700780{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700781 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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700795static void logNmThreshold(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700796{
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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700809static void logPwrThreshold(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700810{
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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700825static void logMSMI(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700826{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700827 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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700841static void logHprWarn(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700842{
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
860static const boost::container::flat_map<
861 uint8_t,
Vijay Khemka63c99be2020-05-27 19:14:35 -0700862 std::pair<std::string, std::function<void(uint8_t*, std::string&)>>>
Patrick Williams010dee02024-08-16 15:19:44 -0400863 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}}};
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700893
Vijay Khemka63c99be2020-05-27 19:14:35 -0700894static void parseSelHelper(StdSELEntry* data, std::string& errStr)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700895{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700896 /* 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
Manikandan Elumalaic056dc02020-12-11 06:20:32 +0530951static 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) + "," +
Manojkiran Eda519530b2024-06-17 11:46:15 +0530997 " Location: DIMM Unknown";
Manikandan Elumalaic056dc02020-12-11 06:20:32 +0530998 }
999}
1000
Vijay Khemka63c99be2020-05-27 19:14:35 -07001001static void parseStdSel(StdSELEntry* data, std::string& errStr)
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001002{
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";
Manikandan Elumalaic056dc02020-12-11 06:20:32 +05301016 parseDimmPhyloc(data, errStr);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001017 break;
1018 case 0x01:
1019 errStr = "Uncorrectable";
1020 tmpStream << "DIMM" << std::setw(2) << std::setfill('0')
1021 << data->eventData3 << " UECC err";
Manikandan Elumalaic056dc02020-12-11 06:20:32 +05301022 parseDimmPhyloc(data, errStr);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001023 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:
Vijay Khemka139aa4f2019-08-16 09:57:41 -07001045 parseSelHelper(data, errStr);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001046 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
Vijay Khemka63c99be2020-05-27 19:14:35 -07001082static void parseOemSel(TsOemSELEntry* data, std::string& errStr)
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001083{
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
cchoux74519032024-02-04 20:35:29 +08001123static 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
Vijay Khemka63c99be2020-05-27 19:14:35 -07001151static void parseOemUnifiedSel(NtsOemSELEntry* data, std::string& errStr)
Vijay Khemka34a875f2019-08-09 15:08:15 -07001152{
Vijay Khemka63c99be2020-05-27 19:14:35 -07001153 uint8_t* ptr = data->oemData;
cchoux74519032024-02-04 20:35:29 +08001154 uint8_t eventType = ptr[5] & 0xf;
Vijay Khemka34a875f2019-08-09 15:08:15 -07001155 int genInfo = ptr[0];
1156 int errType = genInfo & 0x0f;
cchoux74519032024-02-04 20:35:29 +08001157 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"};
Patrick Williams010dee02024-08-16 15:19:44 -04001184 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"};
cchoux74519032024-02-04 20:35:29 +08001192 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"};
Patrick Williams010dee02024-08-16 15:19:44 -04001206 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"};
cchoux74519032024-02-04 20:35:29 +08001209 std::vector<std::string> pprEvent = {"PPR disable", "Soft PPR", "Hard PPR",
1210 "Reserved"};
Vijay Khemka34a875f2019-08-09 15:08:15 -07001211
1212 std::stringstream tmpStream;
Vijay Khemka34a875f2019-08-09 15:08:15 -07001213
1214 switch (errType)
1215 {
1216 case unifiedPcieErr:
cchoux74519032024-02-04 20:35:29 +08001217 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]);
Vijay Khemka34a875f2019-08-09 15:08:15 -07001223 break;
1224 case unifiedMemErr:
cchoux74519032024-02-04 20:35:29 +08001225 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))]);
Vijay Khemka34a875f2019-08-09 15:08:15 -07001231
cchoux74519032024-02-04 20:35:29 +08001232 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;
Patrick Williams010dee02024-08-16 15:19:44 -04001298 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: ";
cchoux74519032024-02-04 20:35:29 +08001305
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]);
Vijay Khemka34a875f2019-08-09 15:08:15 -07001344 break;
1345 default:
1346 std::vector<uint8_t> oemData(ptr, ptr + 13);
1347 std::string oemDataStr;
1348 toHexStr(oemData, oemDataStr);
cchoux74519032024-02-04 20:35:29 +08001349 tmpStream << std::format("Undefined Error Type(0x{:02X}), Raw: {}",
1350 errType, oemDataStr);
Vijay Khemka34a875f2019-08-09 15:08:15 -07001351 }
1352
1353 errStr = tmpStream.str();
1354
1355 return;
1356}
1357
Manikandan Elumalaic056dc02020-12-11 06:20:32 +05301358static void parseSelData(uint8_t fruId, std::vector<uint8_t>& reqData,
1359 std::string& msgLog)
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001360{
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001361 /* Get record type */
1362 int recType = reqData[2];
1363 std::string errType, errLog;
1364
Vijay Khemka63c99be2020-05-27 19:14:35 -07001365 uint8_t* ptr = NULL;
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001366
1367 std::stringstream recTypeStream;
1368 recTypeStream << std::hex << std::uppercase << std::setfill('0')
1369 << std::setw(2) << recType;
1370
Manikandan Elumalaic056dc02020-12-11 06:20:32 +05301371 msgLog = "SEL Entry: FRU: " + std::to_string(fruId) + ", Record: ";
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001372
1373 if (recType == stdErrType)
1374 {
Vijay Khemka63c99be2020-05-27 19:14:35 -07001375 StdSELEntry* data = reinterpret_cast<StdSELEntry*>(&reqData[0]);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001376 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 {
Vijay Khemka139aa4f2019-08-16 09:57:41 -07001392 sensorName = findSensorName->second.first;
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001393 }
1394 }
1395
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001396 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() +
Peter Yin0d053ef2024-10-11 23:39:58 +08001407 "), Sensor: " + sensorName + " (0x" + senNumStream.str() +
1408 "), Event Data: (" + eventData + ") " + errLog;
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001409 }
1410 else if ((recType >= oemTSErrTypeMin) && (recType <= oemTSErrTypeMax))
1411 {
1412 /* timestamped OEM SEL records */
Vijay Khemka63c99be2020-05-27 19:14:35 -07001413 TsOemSELEntry* data = reinterpret_cast<TsOemSELEntry*>(&reqData[0]);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001414 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
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001424 errType = oemTSErr;
1425 parseOemSel(data, errLog);
1426
Peter Yin0d053ef2024-10-11 23:39:58 +08001427 msgLog += errType + " (0x" + recTypeStream.str() + "), MFG ID: " +
1428 mfrIdStr + ", OEM Data: (" + oemDataStr + ") " + errLog;
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001429 }
Vijay Khemka34a875f2019-08-09 15:08:15 -07001430 else if (recType == fbUniErrType)
1431 {
Vijay Khemka63c99be2020-05-27 19:14:35 -07001432 NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]);
Vijay Khemka34a875f2019-08-09 15:08:15 -07001433 errType = fbUniSELErr;
1434 parseOemUnifiedSel(data, errLog);
1435 msgLog += errType + " (0x" + recTypeStream.str() + "), " + errLog;
1436 }
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001437 else if ((recType >= oemNTSErrTypeMin) && (recType <= oemNTSErrTypeMax))
1438 {
1439 /* Non timestamped OEM SEL records */
Vijay Khemka63c99be2020-05-27 19:14:35 -07001440 NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001441 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
Vijay Khemka63c99be2020-05-27 19:14:35 -07001448 parseOemSel((TsOemSELEntry*)data, errLog);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001449 msgLog += errType + " (0x" + recTypeStream.str() + "), OEM Data: (" +
1450 oemDataStr + ") " + errLog;
1451 }
1452 else
1453 {
1454 errType = unknownErr;
1455 toHexStr(reqData, errLog);
Patrick Williams2405ae92023-05-10 07:50:09 -05001456 msgLog += errType + " (0x" + recTypeStream.str() +
1457 ") RawData: " + errLog;
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001458 }
1459}
1460
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001461} // namespace fb_oem::ipmi::sel
1462
1463namespace ipmi
1464{
1465
1466namespace storage
1467{
1468
1469static void registerSELFunctions() __attribute__((constructor));
1470static fb_oem::ipmi::sel::SELData selObj __attribute__((init_priority(101)));
1471
1472ipmi::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
1478 ipmiStorageGetSELInfo()
1479{
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001480 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
1488ipmi::RspType<uint16_t, std::vector<uint8_t>>
1489 ipmiStorageGetSELEntry(std::vector<uint8_t> data)
1490{
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001491 if (data.size() != sizeof(fb_oem::ipmi::sel::GetSELEntryRequest))
1492 {
1493 return ipmi::responseReqDataLenInvalid();
1494 }
1495
Vijay Khemka63c99be2020-05-27 19:14:35 -07001496 fb_oem::ipmi::sel::GetSELEntryRequest* reqData =
1497 reinterpret_cast<fb_oem::ipmi::sel::GetSELEntryRequest*>(&data[0]);
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001498
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
Patrick Williams010dee02024-08-16 15:19:44 -04001576ipmi::RspType<uint16_t>
1577 ipmiStorageAddSELEntry(ipmi::Context::ptr ctx, std::vector<uint8_t> data)
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001578{
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
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001592 /* Parse sel data and get an error log to be filed */
Manikandan Elumalaic056dc02020-12-11 06:20:32 +05301593 fb_oem::ipmi::sel::parseSelData((ctx->hostIdx + 1), data, logErr);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001594
Vijay Khemka15a7ae82019-08-20 12:08:14 -07001595 static const std::string openBMCMessageRegistryVersion("0.1");
Patrick Williams010dee02024-08-16 15:19:44 -04001596 std::string messageID =
1597 "OpenBMC." + openBMCMessageRegistryVersion + ".SELEntryAdded";
Vijay Khemka15a7ae82019-08-20 12:08:14 -07001598
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001599 /* Log the Raw SEL message to the journal */
1600 std::string journalMsg = "SEL Entry Added: " + ipmiRaw;
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001601
Vijay Khemka15a7ae82019-08-20 12:08:14 -07001602 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()));
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001606
BonnieLo-wiwynn21a79232023-03-09 16:42:48 +08001607 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
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001626 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
Vijay Khemkac1921c62019-08-09 13:11:31 -07001634ipmi::RspType<uint8_t> ipmiStorageClearSEL(uint16_t reservationID,
Vijay Khemka63c99be2020-05-27 19:14:35 -07001635 const std::array<uint8_t, 3>& clr,
Vijay Khemkac1921c62019-08-09 13:11:31 -07001636 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
1683ipmi::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
Willy Tue39f9392022-06-15 13:24:20 -07001695ipmi::RspType<> ipmiStorageSetSELTime(uint32_t)
Vijay Khemkac1921c62019-08-09 13:11:31 -07001696{
1697 // Set SEL Time is not supported
1698 return ipmi::responseInvalidCommand();
1699}
1700
1701ipmi::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
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001712void 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
Vijay Khemkac1921c62019-08-09 13:11:31 -07001729 // <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
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001750 return;
1751}
1752
1753} // namespace storage
1754} // namespace ipmi