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