blob: aade56115d74dd510f70111c2008f4cd9a930734 [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 */
Vijay Khemka63c99be2020-05-27 19:14:35 -0700209static void logDefault(uint8_t* data, 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
Vijay Khemka63c99be2020-05-27 19:14:35 -0700415static 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 */
488 uint8_t chnNum = (data[2] & 0x1C) >> 2;
489 uint8_t dimmNum = data[2] & 0x3;
490
491 /* TODO: If critical SEL logging is available, do it */
492 if (snrType == 0x0C)
493 {
494 if ((data[0] & 0x0F) == 0x0)
495 {
496 /* TODO: add_cri_sel */
497 /* "DIMM"+ 'A'+ chnNum + dimmNum + " ECC err,FRU:1"
498 */
499 }
500 else if ((data[0] & 0x0F) == 0x1)
501 {
502 /* TODO: add_cri_sel */
503 /* "DIMM"+ 'A'+ chnNum + dimmNum + " UECC err,FRU:1"
504 */
505 }
506 }
507 /* Continue to parse the error into a string. All Info Valid
508 */
509 errLog += " (" + cpuStr + ", " + chStr + ", " + dimmStr + ")";
510 }
511
512 break;
513 case 0x1:
514
515 /* DIMM info not valid */
516 errLog += " (" + cpuStr + ", " + chStr + ")";
517 break;
518 case 0x2:
519
520 /* CHN info not valid */
521 errLog += " (" + cpuStr + ", " + dimmStr + ")";
522 break;
523 case 0x3:
524
525 /* CPU info not valid */
526 errLog += " (" + chStr + ", " + dimmStr + ")";
527 break;
528 }
529}
530
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
Vijay Khemka63c99be2020-05-27 19:14:35 -07001197 std::tm* ts = localtime((time_t*)(&(data->timeStamp)));
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001198 std::string timeStr = std::asctime(ts);
1199
1200 parseStdSel(data, errLog);
1201 ptr = &(data->eventData1);
1202 std::vector<uint8_t> evtData(ptr, ptr + 3);
1203 std::string eventData;
1204 toHexStr(evtData, eventData);
1205
1206 std::stringstream senNumStream;
1207 senNumStream << std::hex << std::uppercase << std::setfill('0')
1208 << std::setw(2) << (int)(data->sensorNum);
1209
1210 msgLog += errType + " (0x" + recTypeStream.str() +
1211 "), Time: " + timeStr + ", Sensor: " + sensorName + " (0x" +
1212 senNumStream.str() + "), Event Data: (" + eventData + ") " +
1213 errLog;
1214 }
1215 else if ((recType >= oemTSErrTypeMin) && (recType <= oemTSErrTypeMax))
1216 {
1217 /* timestamped OEM SEL records */
Vijay Khemka63c99be2020-05-27 19:14:35 -07001218 TsOemSELEntry* data = reinterpret_cast<TsOemSELEntry*>(&reqData[0]);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001219 ptr = data->mfrId;
1220 std::vector<uint8_t> mfrIdData(ptr, ptr + 3);
1221 std::string mfrIdStr;
1222 toHexStr(mfrIdData, mfrIdStr);
1223
1224 ptr = data->oemData;
1225 std::vector<uint8_t> oemData(ptr, ptr + 6);
1226 std::string oemDataStr;
1227 toHexStr(oemData, oemDataStr);
1228
Vijay Khemka63c99be2020-05-27 19:14:35 -07001229 std::tm* ts = localtime((time_t*)(&(data->timeStamp)));
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001230 std::string timeStr = std::asctime(ts);
1231
1232 errType = oemTSErr;
1233 parseOemSel(data, errLog);
1234
1235 msgLog += errType + " (0x" + recTypeStream.str() +
1236 "), Time: " + timeStr + ", MFG ID: " + mfrIdStr +
1237 ", OEM Data: (" + oemDataStr + ") " + errLog;
1238 }
Vijay Khemka34a875f2019-08-09 15:08:15 -07001239 else if (recType == fbUniErrType)
1240 {
Vijay Khemka63c99be2020-05-27 19:14:35 -07001241 NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]);
Vijay Khemka34a875f2019-08-09 15:08:15 -07001242 errType = fbUniSELErr;
1243 parseOemUnifiedSel(data, errLog);
1244 msgLog += errType + " (0x" + recTypeStream.str() + "), " + errLog;
1245 }
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001246 else if ((recType >= oemNTSErrTypeMin) && (recType <= oemNTSErrTypeMax))
1247 {
1248 /* Non timestamped OEM SEL records */
Vijay Khemka63c99be2020-05-27 19:14:35 -07001249 NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001250 errType = oemNTSErr;
1251
1252 ptr = data->oemData;
1253 std::vector<uint8_t> oemData(ptr, ptr + 13);
1254 std::string oemDataStr;
1255 toHexStr(oemData, oemDataStr);
1256
Vijay Khemka63c99be2020-05-27 19:14:35 -07001257 parseOemSel((TsOemSELEntry*)data, errLog);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001258 msgLog += errType + " (0x" + recTypeStream.str() + "), OEM Data: (" +
1259 oemDataStr + ") " + errLog;
1260 }
1261 else
1262 {
1263 errType = unknownErr;
1264 toHexStr(reqData, errLog);
1265 msgLog +=
1266 errType + " (0x" + recTypeStream.str() + ") RawData: " + errLog;
1267 }
1268}
1269
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001270} // namespace fb_oem::ipmi::sel
1271
1272namespace ipmi
1273{
1274
1275namespace storage
1276{
1277
1278static void registerSELFunctions() __attribute__((constructor));
1279static fb_oem::ipmi::sel::SELData selObj __attribute__((init_priority(101)));
1280
1281ipmi::RspType<uint8_t, // SEL version
1282 uint16_t, // SEL entry count
1283 uint16_t, // free space
1284 uint32_t, // last add timestamp
1285 uint32_t, // last erase timestamp
1286 uint8_t> // operation support
1287 ipmiStorageGetSELInfo()
1288{
1289
1290 fb_oem::ipmi::sel::GetSELInfoData info;
1291
1292 selObj.getInfo(info);
1293 return ipmi::responseSuccess(info.selVersion, info.entries, info.freeSpace,
1294 info.addTimeStamp, info.eraseTimeStamp,
1295 info.operationSupport);
1296}
1297
1298ipmi::RspType<uint16_t, std::vector<uint8_t>>
1299 ipmiStorageGetSELEntry(std::vector<uint8_t> data)
1300{
1301
1302 if (data.size() != sizeof(fb_oem::ipmi::sel::GetSELEntryRequest))
1303 {
1304 return ipmi::responseReqDataLenInvalid();
1305 }
1306
Vijay Khemka63c99be2020-05-27 19:14:35 -07001307 fb_oem::ipmi::sel::GetSELEntryRequest* reqData =
1308 reinterpret_cast<fb_oem::ipmi::sel::GetSELEntryRequest*>(&data[0]);
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001309
1310 if (reqData->reservID != 0)
1311 {
1312 if (!checkSELReservation(reqData->reservID))
1313 {
1314 return ipmi::responseInvalidReservationId();
1315 }
1316 }
1317
1318 uint16_t selCnt = selObj.getCount();
1319 if (selCnt == 0)
1320 {
1321 return ipmi::responseSensorInvalid();
1322 }
1323
1324 /* If it is asked for first entry */
1325 if (reqData->recordID == fb_oem::ipmi::sel::firstEntry)
1326 {
1327 /* First Entry (0x0000) as per Spec */
1328 reqData->recordID = 1;
1329 }
1330 else if (reqData->recordID == fb_oem::ipmi::sel::lastEntry)
1331 {
1332 /* Last entry (0xFFFF) as per Spec */
1333 reqData->recordID = selCnt;
1334 }
1335
1336 std::string ipmiRaw;
1337
1338 if (selObj.getEntry(reqData->recordID, ipmiRaw) < 0)
1339 {
1340 return ipmi::responseSensorInvalid();
1341 }
1342
1343 std::vector<uint8_t> recDataBytes;
1344 if (fromHexStr(ipmiRaw, recDataBytes) < 0)
1345 {
1346 return ipmi::responseUnspecifiedError();
1347 }
1348
1349 /* Identify the next SEL record ID. If recordID is same as
1350 * total SeL count then next id should be last entry else
1351 * it should be incremented by 1 to current RecordID
1352 */
1353 uint16_t nextRecord;
1354 if (reqData->recordID == selCnt)
1355 {
1356 nextRecord = fb_oem::ipmi::sel::lastEntry;
1357 }
1358 else
1359 {
1360 nextRecord = reqData->recordID + 1;
1361 }
1362
1363 if (reqData->readLen == fb_oem::ipmi::sel::entireRecord)
1364 {
1365 return ipmi::responseSuccess(nextRecord, recDataBytes);
1366 }
1367 else
1368 {
1369 if (reqData->offset >= fb_oem::ipmi::sel::selRecordSize ||
1370 reqData->readLen > fb_oem::ipmi::sel::selRecordSize)
1371 {
1372 return ipmi::responseUnspecifiedError();
1373 }
1374 std::vector<uint8_t> recPartData;
1375
1376 auto diff = fb_oem::ipmi::sel::selRecordSize - reqData->offset;
1377 auto readLength = std::min(diff, static_cast<int>(reqData->readLen));
1378
1379 for (int i = 0; i < readLength; i++)
1380 {
1381 recPartData.push_back(recDataBytes[i + reqData->offset]);
1382 }
1383 return ipmi::responseSuccess(nextRecord, recPartData);
1384 }
1385}
1386
Manikandan Elumalaic056dc02020-12-11 06:20:32 +05301387ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(ipmi::Context::ptr ctx,
1388 std::vector<uint8_t> data)
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001389{
1390 /* Per the IPMI spec, need to cancel any reservation when a
1391 * SEL entry is added
1392 */
1393 cancelSELReservation();
1394
1395 if (data.size() != fb_oem::ipmi::sel::selRecordSize)
1396 {
1397 return ipmi::responseReqDataLenInvalid();
1398 }
1399
1400 std::string ipmiRaw, logErr;
1401 toHexStr(data, ipmiRaw);
1402
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001403 /* Parse sel data and get an error log to be filed */
Manikandan Elumalaic056dc02020-12-11 06:20:32 +05301404 fb_oem::ipmi::sel::parseSelData((ctx->hostIdx + 1), data, logErr);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001405
Vijay Khemka15a7ae82019-08-20 12:08:14 -07001406 static const std::string openBMCMessageRegistryVersion("0.1");
1407 std::string messageID =
1408 "OpenBMC." + openBMCMessageRegistryVersion + ".SELEntryAdded";
1409
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001410 /* Log the Raw SEL message to the journal */
1411 std::string journalMsg = "SEL Entry Added: " + ipmiRaw;
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001412
Vijay Khemka15a7ae82019-08-20 12:08:14 -07001413 phosphor::logging::log<phosphor::logging::level::INFO>(
1414 journalMsg.c_str(),
1415 phosphor::logging::entry("IPMISEL_MESSAGE_ID=%s", messageID.c_str()),
1416 phosphor::logging::entry("IPMISEL_MESSAGE_ARGS=%s", logErr.c_str()));
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001417
1418 int responseID = selObj.addEntry(ipmiRaw.c_str());
1419 if (responseID < 0)
1420 {
1421 return ipmi::responseUnspecifiedError();
1422 }
1423 return ipmi::responseSuccess((uint16_t)responseID);
1424}
1425
Vijay Khemkac1921c62019-08-09 13:11:31 -07001426ipmi::RspType<uint8_t> ipmiStorageClearSEL(uint16_t reservationID,
Vijay Khemka63c99be2020-05-27 19:14:35 -07001427 const std::array<uint8_t, 3>& clr,
Vijay Khemkac1921c62019-08-09 13:11:31 -07001428 uint8_t eraseOperation)
1429{
1430 if (!checkSELReservation(reservationID))
1431 {
1432 return ipmi::responseInvalidReservationId();
1433 }
1434
1435 static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
1436 if (clr != clrExpected)
1437 {
1438 return ipmi::responseInvalidFieldRequest();
1439 }
1440
1441 /* If there is no sel then return erase complete */
1442 if (selObj.getCount() == 0)
1443 {
1444 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1445 }
1446
1447 /* Erasure status cannot be fetched, so always return erasure
1448 * status as `erase completed`.
1449 */
1450 if (eraseOperation == fb_oem::ipmi::sel::getEraseStatus)
1451 {
1452 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1453 }
1454
1455 /* Check that initiate erase is correct */
1456 if (eraseOperation != fb_oem::ipmi::sel::initiateErase)
1457 {
1458 return ipmi::responseInvalidFieldRequest();
1459 }
1460
1461 /* Per the IPMI spec, need to cancel any reservation when the
1462 * SEL is cleared
1463 */
1464 cancelSELReservation();
1465
1466 /* Clear the complete Sel Json object */
1467 if (selObj.clear() < 0)
1468 {
1469 return ipmi::responseUnspecifiedError();
1470 }
1471
1472 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1473}
1474
1475ipmi::RspType<uint32_t> ipmiStorageGetSELTime()
1476{
1477 struct timespec selTime = {};
1478
1479 if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
1480 {
1481 return ipmi::responseUnspecifiedError();
1482 }
1483
1484 return ipmi::responseSuccess(selTime.tv_sec);
1485}
1486
1487ipmi::RspType<> ipmiStorageSetSELTime(uint32_t selTime)
1488{
1489 // Set SEL Time is not supported
1490 return ipmi::responseInvalidCommand();
1491}
1492
1493ipmi::RspType<uint16_t> ipmiStorageGetSELTimeUtcOffset()
1494{
1495 /* TODO: For now, the SEL time stamp is based on UTC time,
1496 * so return 0x0000 as offset. Might need to change once
1497 * supporting zones in SEL time stamps
1498 */
1499
1500 uint16_t utcOffset = 0x0000;
1501 return ipmi::responseSuccess(utcOffset);
1502}
1503
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001504void registerSELFunctions()
1505{
1506 // <Get SEL Info>
1507 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1508 ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
1509 ipmiStorageGetSELInfo);
1510
1511 // <Get SEL Entry>
1512 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1513 ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
1514 ipmiStorageGetSELEntry);
1515
1516 // <Add SEL Entry>
1517 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1518 ipmi::storage::cmdAddSelEntry,
1519 ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
1520
Vijay Khemkac1921c62019-08-09 13:11:31 -07001521 // <Clear SEL>
1522 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1523 ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
1524 ipmiStorageClearSEL);
1525
1526 // <Get SEL Time>
1527 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1528 ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
1529 ipmiStorageGetSELTime);
1530
1531 // <Set SEL Time>
1532 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1533 ipmi::storage::cmdSetSelTime,
1534 ipmi::Privilege::Operator, ipmiStorageSetSELTime);
1535
1536 // <Get SEL Time UTC Offset>
1537 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1538 ipmi::storage::cmdGetSelTimeUtcOffset,
1539 ipmi::Privilege::User,
1540 ipmiStorageGetSELTimeUtcOffset);
1541
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001542 return;
1543}
1544
1545} // namespace storage
1546} // namespace ipmi