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