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