blob: c6f0c9b4c6cf811c799453cb8616c1de735803ae [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{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700263 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{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700281 if ((data[0] & 0x0F) == 0x0)
282 {
283 errLog = "System Firmware Error";
284 }
285 else
286 {
287 errLog = "Unknown";
288 }
289
290 if (((data[0] >> 6) & 0x03) == 0x3)
291 {
292 // TODO: Need to implement IPMI spec based Post Code
293 errLog += ", IPMI Post Code";
294 }
295 else if (((data[0] >> 6) & 0x03) == 0x2)
296 {
Patrick Williams2405ae92023-05-10 07:50:09 -0500297 errLog += ", OEM Post Code 0x" + byteToStr(data[2]) +
298 byteToStr(data[1]);
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700299
300 switch ((data[2] << 8) | data[1])
301 {
302 case 0xA105:
303 errLog += ", BMC Failed (No Response)";
304 break;
305 case 0xA106:
306 errLog += ", BMC Failed (Self Test Fail)";
307 break;
308 case 0xA10A:
309 errLog += ", System Firmware Corruption Detected";
310 break;
311 case 0xA10B:
312 errLog += ", TPM Self-Test FAIL Detected";
313 }
314 }
315}
316
Vijay Khemka63c99be2020-05-27 19:14:35 -0700317static void logMchChkErr(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700318{
319 /* TODO: Call add_cri_sel for CRITICAL_IRQ */
320 if ((data[0] & 0x0F) == 0x0B)
321 {
322 errLog = "Uncorrectable";
323 }
324 else if ((data[0] & 0x0F) == 0x0C)
325 {
326 errLog = "Correctable";
327 }
328 else
329 {
330 errLog = "Unknown";
331 }
332
333 errLog += ", Machine Check bank Number " + std::to_string(data[1]) +
334 ", CPU " + std::to_string(data[2] >> 5) + ", Core " +
335 std::to_string(data[2] & 0x1F);
336}
337
Vijay Khemka63c99be2020-05-27 19:14:35 -0700338static void logPcieErr(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700339{
340 std::stringstream tmp1, tmp2;
341 tmp1 << std::hex << std::uppercase << std::setfill('0');
342 tmp2 << std::hex << std::uppercase << std::setfill('0');
343 tmp1 << " (Bus " << std::setw(2) << (int)(data[2]) << " / Dev "
344 << std::setw(2) << (int)(data[1] >> 3) << " / Fun " << std::setw(2)
345 << (int)(data[1] & 0x7) << ")";
346
347 switch (data[0] & 0xF)
348 {
349 case 0x4:
350 errLog = "PCI PERR" + tmp1.str();
351 break;
352 case 0x5:
353 errLog = "PCI SERR" + tmp1.str();
354 break;
355 case 0x7:
356 errLog = "Correctable" + tmp1.str();
357 break;
358 case 0x8:
359 errLog = "Uncorrectable" + tmp1.str();
360 break;
361 case 0xA:
362 errLog = "Bus Fatal" + tmp1.str();
363 break;
Vijay Khemkad1194022020-05-27 18:58:33 -0700364 case 0xD:
365 {
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700366 uint32_t venId = (uint32_t)data[1] << 8 | (uint32_t)data[2];
367 tmp2 << "Vendor ID: 0x" << std::setw(4) << venId;
368 errLog = tmp2.str();
369 }
370 break;
Vijay Khemkad1194022020-05-27 18:58:33 -0700371 case 0xE:
372 {
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700373 uint32_t devId = (uint32_t)data[1] << 8 | (uint32_t)data[2];
374 tmp2 << "Device ID: 0x" << std::setw(4) << devId;
375 errLog = tmp2.str();
376 }
377 break;
378 case 0xF:
379 tmp2 << "Error ID from downstream: 0x" << std::setw(2)
380 << (int)(data[1]) << std::setw(2) << (int)(data[2]);
381 errLog = tmp2.str();
382 break;
383 default:
384 errLog = "Unknown";
385 }
386}
387
Vijay Khemka63c99be2020-05-27 19:14:35 -0700388static void logIioErr(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700389{
390 std::vector<std::string> tmpStr = {
391 "IRP0", "IRP1", " IIO-Core", "VT-d", "Intel Quick Data",
392 "Misc", " DMA", "ITC", "OTC", "CI"};
393
394 if ((data[0] & 0xF) == 0)
395 {
396 errLog += "CPU " + std::to_string(data[2] >> 5) + ", Error ID 0x" +
397 byteToStr(data[1]) + " - ";
398
399 if ((data[2] & 0xF) <= 0x9)
400 {
401 errLog += tmpStr[(data[2] & 0xF)];
402 }
403 else
404 {
405 errLog += "Reserved";
406 }
407 }
408 else
409 {
410 errLog = "Unknown";
411 }
412}
413
Willy Tue39f9392022-06-15 13:24:20 -0700414[[maybe_unused]] static void logMemErr(uint8_t* dataPtr, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700415{
416 uint8_t snrType = dataPtr[0];
417 uint8_t snrNum = dataPtr[1];
Vijay Khemka63c99be2020-05-27 19:14:35 -0700418 uint8_t* data = &(dataPtr[3]);
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700419
420 /* TODO: add pal_add_cri_sel */
421
422 if (snrNum == memoryEccError)
423 {
424 /* SEL from MEMORY_ECC_ERR Sensor */
425 switch (data[0] & 0x0F)
426 {
427 case 0x0:
428 if (snrType == 0x0C)
429 {
430 errLog = "Correctable";
431 }
432 else if (snrType == 0x10)
433 {
434 errLog = "Correctable ECC error Logging Disabled";
435 }
436 break;
437 case 0x1:
438 errLog = "Uncorrectable";
439 break;
440 case 0x5:
441 errLog = "Correctable ECC error Logging Limit Disabled";
442 break;
443 default:
444 errLog = "Unknown";
445 }
446 }
447 else if (snrNum == memoryErrLogDIS)
448 {
449 // SEL from MEMORY_ERR_LOG_DIS Sensor
450 if ((data[0] & 0x0F) == 0x0)
451 {
452 errLog = "Correctable Memory Error Logging Disabled";
453 }
454 else
455 {
456 errLog = "Unknown";
457 }
458 }
459 else
460 {
461 errLog = "Unknown";
462 return;
463 }
464
465 /* Common routine for both MEM_ECC_ERR and MEMORY_ERR_LOG_DIS */
466
467 errLog += " (DIMM " + byteToStr(data[2]) + ") Logical Rank " +
468 std::to_string(data[1] & 0x03);
469
470 /* DIMM number (data[2]):
471 * Bit[7:5]: Socket number (Range: 0-7)
472 * Bit[4:3]: Channel number (Range: 0-3)
473 * Bit[2:0]: DIMM number (Range: 0-7)
474 */
475
476 /* TODO: Verify these bits */
477 std::string cpuStr = "CPU# " + std::to_string((data[2] & 0xE0) >> 5);
478 std::string chStr = "CHN# " + std::to_string((data[2] & 0x18) >> 3);
Manikandan Elumalaic056dc02020-12-11 06:20:32 +0530479 std::string dimmStr = "DIMM#" + std::to_string(data[2] & 0x7);
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700480
481 switch ((data[1] & 0xC) >> 2)
482 {
Vijay Khemkad1194022020-05-27 18:58:33 -0700483 case 0x0:
484 {
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700485 /* All Info Valid */
Willy Tue39f9392022-06-15 13:24:20 -0700486 [[maybe_unused]] uint8_t chnNum = (data[2] & 0x1C) >> 2;
487 [[maybe_unused]] uint8_t dimmNum = data[2] & 0x3;
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700488
489 /* TODO: If critical SEL logging is available, do it */
490 if (snrType == 0x0C)
491 {
492 if ((data[0] & 0x0F) == 0x0)
493 {
494 /* TODO: add_cri_sel */
495 /* "DIMM"+ 'A'+ chnNum + dimmNum + " ECC err,FRU:1"
496 */
497 }
498 else if ((data[0] & 0x0F) == 0x1)
499 {
500 /* TODO: add_cri_sel */
501 /* "DIMM"+ 'A'+ chnNum + dimmNum + " UECC err,FRU:1"
502 */
503 }
504 }
505 /* Continue to parse the error into a string. All Info Valid
506 */
507 errLog += " (" + cpuStr + ", " + chStr + ", " + dimmStr + ")";
508 }
509
510 break;
511 case 0x1:
512
513 /* DIMM info not valid */
514 errLog += " (" + cpuStr + ", " + chStr + ")";
515 break;
516 case 0x2:
517
518 /* CHN info not valid */
519 errLog += " (" + cpuStr + ", " + dimmStr + ")";
520 break;
521 case 0x3:
522
523 /* CPU info not valid */
524 errLog += " (" + chStr + ", " + dimmStr + ")";
525 break;
526 }
527}
528
Vijay Khemka63c99be2020-05-27 19:14:35 -0700529static void logPwrErr(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700530{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700531 if (data[0] == 0x1)
532 {
533 errLog = "SYS_PWROK failure";
534 /* Also try logging to Critial log file, if available */
535 /* "SYS_PWROK failure,FRU:1" */
536 }
537 else if (data[0] == 0x2)
538 {
539 errLog = "PCH_PWROK failure";
540 /* Also try logging to Critial log file, if available */
541 /* "PCH_PWROK failure,FRU:1" */
542 }
543 else
544 {
545 errLog = "Unknown";
546 }
547}
548
Vijay Khemka63c99be2020-05-27 19:14:35 -0700549static void logCatErr(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700550{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700551 if (data[0] == 0x0)
552 {
553 errLog = "IERR/CATERR";
554 /* Also try logging to Critial log file, if available */
555 /* "IERR,FRU:1 */
556 }
557 else if (data[0] == 0xB)
558 {
559 errLog = "MCERR/CATERR";
560 /* Also try logging to Critial log file, if available */
561 /* "MCERR,FRU:1 */
562 }
563 else
564 {
565 errLog = "Unknown";
566 }
567}
568
Vijay Khemka63c99be2020-05-27 19:14:35 -0700569static void logDimmHot(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700570{
571 if ((data[0] << 16 | data[1] << 8 | data[2]) == 0x01FFFF)
572 {
573 errLog = "SOC MEMHOT";
574 }
575 else
576 {
577 errLog = "Unknown";
578 /* Also try logging to Critial log file, if available */
579 /* ""CPU_DIMM_HOT %s,FRU:1" */
580 }
581}
582
Vijay Khemka63c99be2020-05-27 19:14:35 -0700583static void logSwNMI(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700584{
585 if ((data[0] << 16 | data[1] << 8 | data[2]) == 0x03FFFF)
586 {
587 errLog = "Software NMI";
588 }
589 else
590 {
591 errLog = "Unknown SW NMI";
592 }
593}
594
Vijay Khemka63c99be2020-05-27 19:14:35 -0700595static void logCPUThermalSts(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700596{
597 switch (data[0])
598 {
599 case 0x0:
600 errLog = "CPU Critical Temperature";
601 break;
602 case 0x1:
603 errLog = "PROCHOT#";
604 break;
605 case 0x2:
606 errLog = "TCC Activation";
607 break;
608 default:
609 errLog = "Unknown";
610 }
611}
612
Vijay Khemka63c99be2020-05-27 19:14:35 -0700613static void logMEPwrState(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700614{
615 switch (data[0])
616 {
617 case 0:
618 errLog = "RUNNING";
619 break;
620 case 2:
621 errLog = "POWER_OFF";
622 break;
623 default:
624 errLog = "Unknown[" + std::to_string(data[0]) + "]";
625 break;
626 }
627}
628
Vijay Khemka63c99be2020-05-27 19:14:35 -0700629static void logSPSFwHealth(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700630{
631 if ((data[0] & 0x0F) == 0x00)
632 {
633 const std::vector<std::string> tmpStr = {
634 "Recovery GPIO forced",
635 "Image execution failed",
636 "Flash erase error",
637 "Flash state information",
638 "Internal error",
639 "BMC did not respond",
640 "Direct Flash update",
641 "Manufacturing error",
642 "Automatic Restore to Factory Presets",
643 "Firmware Exception",
644 "Flash Wear-Out Protection Warning",
645 "Unknown",
646 "Unknown",
647 "DMI interface error",
648 "MCTP interface error",
649 "Auto-configuration finished",
650 "Unsupported Segment Defined Feature",
651 "Unknown",
652 "CPU Debug Capability Disabled",
653 "UMA operation error"};
654
655 if (data[1] < 0x14)
656 {
657 errLog = tmpStr[data[1]];
658 }
659 else
660 {
661 errLog = "Unknown";
662 }
663 }
664 else if ((data[0] & 0x0F) == 0x01)
665 {
666 errLog = "SMBus link failure";
667 }
668 else
669 {
670 errLog = "Unknown";
671 }
672}
673
Vijay Khemka63c99be2020-05-27 19:14:35 -0700674static void logNmExcA(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700675{
676 /*NM4.0 #550710, Revision 1.95, and turn to p.155*/
677 if (data[0] == 0xA8)
678 {
679 errLog = "Policy Correction Time Exceeded";
680 }
681 else
682 {
683 errLog = "Unknown";
684 }
685}
686
Vijay Khemka63c99be2020-05-27 19:14:35 -0700687static void logPCHThermal(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700688{
689 const std::vector<std::string> thresEvtName = {"Lower Non-critical",
690 "Unknown",
691 "Lower Critical",
692 "Unknown",
693 "Lower Non-recoverable",
694 "Unknown",
695 "Unknown",
696 "Upper Non-critical",
697 "Unknown",
698 "Upper Critical",
699 "Unknown",
700 "Upper Non-recoverable"};
701
702 if ((data[0] & 0x0f) < 12)
703 {
704 errLog = thresEvtName[(data[0] & 0x0f)];
705 }
706 else
707 {
708 errLog = "Unknown";
709 }
710
711 errLog += ", curr_val: " + std::to_string(data[1]) +
712 " C, thresh_val: " + std::to_string(data[2]) + " C";
713}
714
Vijay Khemka63c99be2020-05-27 19:14:35 -0700715static void logNmHealth(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700716{
717 std::vector<std::string> nmErrType = {
718 "Unknown",
719 "Unknown",
720 "Unknown",
721 "Unknown",
722 "Unknown",
723 "Unknown",
724 "Unknown",
725 "Extended Telemetry Device Reading Failure",
726 "Outlet Temperature Reading Failure",
727 "Volumetric Airflow Reading Failure",
728 "Policy Misconfiguration",
729 "Power Sensor Reading Failure",
730 "Inlet Temperature Reading Failure",
731 "Host Communication Error",
732 "Real-time Clock Synchronization Failure",
733 "Platform Shutdown Initiated by Intel NM Policy",
734 "Unknown"};
735 uint8_t nmTypeIdx = (data[0] & 0xf);
736 uint8_t domIdx = (data[1] & 0xf);
737 uint8_t errIdx = ((data[1] >> 4) & 0xf);
738
739 if (nmTypeIdx == 2)
740 {
741 errLog = "SensorIntelNM";
742 }
743 else
744 {
745 errLog = "Unknown";
746 }
747
748 errLog += ", Domain:" + nmDomName[domIdx] +
749 ", ErrType:" + nmErrType[errIdx] + ", Err:0x" +
750 byteToStr(data[2]);
751}
752
Vijay Khemka63c99be2020-05-27 19:14:35 -0700753static void logNmCap(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700754{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700755 const std::vector<std::string> nmCapStsStr = {"Not Available", "Available"};
756 if (data[0] & 0x7) // BIT1=policy, BIT2=monitoring, BIT3=pwr
757 // limit and the others are reserved
758 {
759 errLog = "PolicyInterface:" + nmCapStsStr[BIT(data[0], 0)] +
760 ",Monitoring:" + nmCapStsStr[BIT(data[0], 1)] +
761 ",PowerLimit:" + nmCapStsStr[BIT(data[0], 2)];
762 }
763 else
764 {
765 errLog = "Unknown";
766 }
767}
768
Vijay Khemka63c99be2020-05-27 19:14:35 -0700769static void logNmThreshold(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700770{
771 uint8_t thresNum = (data[0] & 0x3);
772 uint8_t domIdx = (data[1] & 0xf);
773 uint8_t polId = data[2];
774 uint8_t polEvtIdx = BIT(data[0], 3);
775 const std::vector<std::string> polEvtStr = {
776 "Threshold Exceeded", "Policy Correction Time Exceeded"};
777
778 errLog = "Threshold Number:" + std::to_string(thresNum) + "-" +
779 polEvtStr[polEvtIdx] + ", Domain:" + nmDomName[domIdx] +
780 ", PolicyID:0x" + byteToStr(polId);
781}
782
Vijay Khemka63c99be2020-05-27 19:14:35 -0700783static void logPwrThreshold(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700784{
785 if (data[0] == 0x00)
786 {
787 errLog = "Limit Not Exceeded";
788 }
789 else if (data[0] == 0x01)
790 {
791 errLog = "Limit Exceeded";
792 }
793 else
794 {
795 errLog = "Unknown";
796 }
797}
798
Vijay Khemka63c99be2020-05-27 19:14:35 -0700799static void logMSMI(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700800{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700801 if (data[0] == 0x0)
802 {
803 errLog = "IERR/MSMI";
804 }
805 else if (data[0] == 0x0B)
806 {
807 errLog = "MCERR/MSMI";
808 }
809 else
810 {
811 errLog = "Unknown";
812 }
813}
814
Vijay Khemka63c99be2020-05-27 19:14:35 -0700815static void logHprWarn(uint8_t* data, std::string& errLog)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700816{
817 if (data[2] == 0x01)
818 {
819 if (data[1] == 0xFF)
820 {
821 errLog = "Infinite Time";
822 }
823 else
824 {
825 errLog = std::to_string(data[1]) + " minutes";
826 }
827 }
828 else
829 {
830 errLog = "Unknown";
831 }
832}
833
834static const boost::container::flat_map<
835 uint8_t,
Vijay Khemka63c99be2020-05-27 19:14:35 -0700836 std::pair<std::string, std::function<void(uint8_t*, std::string&)>>>
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700837 sensorNameTable = {{0xE9, {"SYSTEM_EVENT", logSysEvent}},
838 {0x7D, {"THERM_THRESH_EVT", logThermalEvent}},
839 {0xAA, {"BUTTON", logDefault}},
840 {0xAB, {"POWER_STATE", logDefault}},
841 {0xEA, {"CRITICAL_IRQ", logCritIrq}},
842 {0x2B, {"POST_ERROR", logPostErr}},
843 {0x40, {"MACHINE_CHK_ERR", logMchChkErr}},
844 {0x41, {"PCIE_ERR", logPcieErr}},
845 {0x43, {"IIO_ERR", logIioErr}},
846 {0X63, {"MEMORY_ECC_ERR", logDefault}},
847 {0X87, {"MEMORY_ERR_LOG_DIS", logDefault}},
848 {0X51, {"PROCHOT_EXT", logDefault}},
849 {0X56, {"PWR_ERR", logPwrErr}},
850 {0xE6, {"CATERR_A", logCatErr}},
851 {0xEB, {"CATERR_B", logCatErr}},
852 {0xB3, {"CPU_DIMM_HOT", logDimmHot}},
853 {0x90, {"SOFTWARE_NMI", logSwNMI}},
854 {0x1C, {"CPU0_THERM_STATUS", logCPUThermalSts}},
855 {0x1D, {"CPU1_THERM_STATUS", logCPUThermalSts}},
856 {0x16, {"ME_POWER_STATE", logMEPwrState}},
857 {0x17, {"SPS_FW_HEALTH", logSPSFwHealth}},
858 {0x18, {"NM_EXCEPTION_A", logNmExcA}},
859 {0x08, {"PCH_THERM_THRESHOLD", logPCHThermal}},
860 {0x19, {"NM_HEALTH", logNmHealth}},
861 {0x1A, {"NM_CAPABILITIES", logNmCap}},
862 {0x1B, {"NM_THRESHOLD", logNmThreshold}},
863 {0x3B, {"PWR_THRESH_EVT", logPwrThreshold}},
864 {0xE7, {"MSMI", logMSMI}},
865 {0xC5, {"HPR_WARNING", logHprWarn}}};
866
Vijay Khemka63c99be2020-05-27 19:14:35 -0700867static void parseSelHelper(StdSELEntry* data, std::string& errStr)
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700868{
Vijay Khemka139aa4f2019-08-16 09:57:41 -0700869 /* Check if sensor type is OS_BOOT (0x1f) */
870 if (data->sensorType == 0x1F)
871 {
872 /* OS_BOOT used by OS */
873 switch (data->eventData1 & 0xF)
874 {
875 case 0x07:
876 errStr = "Base OS/Hypervisor Installation started";
877 break;
878 case 0x08:
879 errStr = "Base OS/Hypervisor Installation completed";
880 break;
881 case 0x09:
882 errStr = "Base OS/Hypervisor Installation aborted";
883 break;
884 case 0x0A:
885 errStr = "Base OS/Hypervisor Installation failed";
886 break;
887 default:
888 errStr = "Unknown";
889 }
890 return;
891 }
892
893 auto findSensorName = sensorNameTable.find(data->sensorNum);
894 if (findSensorName == sensorNameTable.end())
895 {
896 errStr = "Unknown";
897 return;
898 }
899 else
900 {
901 switch (data->sensorNum)
902 {
903 /* logMemErr function needs data from sensor type */
904 case memoryEccError:
905 case memoryErrLogDIS:
906 findSensorName->second.second(&(data->sensorType), errStr);
907 break;
908 /* Other sensor function needs only event data for parsing */
909 default:
910 findSensorName->second.second(&(data->eventData1), errStr);
911 }
912 }
913
914 if (((data->eventData3 & 0x80) >> 7) == 0)
915 {
916 errStr += " Assertion";
917 }
918 else
919 {
920 errStr += " Deassertion";
921 }
922}
923
Manikandan Elumalaic056dc02020-12-11 06:20:32 +0530924static void parseDimmPhyloc(StdSELEntry* data, std::string& errStr)
925{
926 // Log when " All info available"
927 uint8_t chNum = (data->eventData3 & 0x18) >> 3;
928 uint8_t dimmNum = data->eventData3 & 0x7;
929 uint8_t rankNum = data->eventData2 & 0x03;
930 uint8_t nodeNum = (data->eventData3 & 0xE0) >> 5;
931
932 if (chNum == 3 && dimmNum == 0)
933 {
934 errStr += " Node: " + std::to_string(nodeNum) + "," +
935 " Card: " + std::to_string(chNum) + "," +
936 " Module: " + std::to_string(dimmNum) + "," +
937 " Rank Number: " + std::to_string(rankNum) + "," +
938 " Location: DIMM A0";
939 }
940 else if (chNum == 2 && 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 B0";
947 }
948 else if (chNum == 4 && 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 C0 ";
955 }
956 else if (chNum == 5 && 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 D0";
963 }
964 else
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 Unknow";
971 }
972}
973
Vijay Khemka63c99be2020-05-27 19:14:35 -0700974static void parseStdSel(StdSELEntry* data, std::string& errStr)
Vijay Khemkaf36f3452019-08-09 13:24:45 -0700975{
976 std::stringstream tmpStream;
977 tmpStream << std::hex << std::uppercase;
978
979 /* TODO: add pal_add_cri_sel */
980 switch (data->sensorNum)
981 {
982 case memoryEccError:
983 switch (data->eventData1 & 0x0F)
984 {
985 case 0x00:
986 errStr = "Correctable";
987 tmpStream << "DIMM" << std::setw(2) << std::setfill('0')
988 << data->eventData3 << " ECC err";
Manikandan Elumalaic056dc02020-12-11 06:20:32 +0530989 parseDimmPhyloc(data, errStr);
Vijay Khemkaf36f3452019-08-09 13:24:45 -0700990 break;
991 case 0x01:
992 errStr = "Uncorrectable";
993 tmpStream << "DIMM" << std::setw(2) << std::setfill('0')
994 << data->eventData3 << " UECC err";
Manikandan Elumalaic056dc02020-12-11 06:20:32 +0530995 parseDimmPhyloc(data, errStr);
Vijay Khemkaf36f3452019-08-09 13:24:45 -0700996 break;
997 case 0x02:
998 errStr = "Parity";
999 break;
1000 case 0x05:
1001 errStr = "Correctable ECC error Logging Limit Reached";
1002 break;
1003 default:
1004 errStr = "Unknown";
1005 }
1006 break;
1007 case memoryErrLogDIS:
1008 if ((data->eventData1 & 0x0F) == 0)
1009 {
1010 errStr = "Correctable Memory Error Logging Disabled";
1011 }
1012 else
1013 {
1014 errStr = "Unknown";
1015 }
1016 break;
1017 default:
Vijay Khemka139aa4f2019-08-16 09:57:41 -07001018 parseSelHelper(data, errStr);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001019 return;
1020 }
1021
1022 errStr += " (DIMM " + std::to_string(data->eventData3) + ")";
1023 errStr += " Logical Rank " + std::to_string(data->eventData2 & 0x03);
1024
1025 switch ((data->eventData2 & 0x0C) >> 2)
1026 {
1027 case 0x00:
1028 // Ignore when " All info available"
1029 break;
1030 case 0x01:
1031 errStr += " DIMM info not valid";
1032 break;
1033 case 0x02:
1034 errStr += " CHN info not valid";
1035 break;
1036 case 0x03:
1037 errStr += " CPU info not valid";
1038 break;
1039 default:
1040 errStr += " Unknown";
1041 }
1042
1043 if (((data->eventType & 0x80) >> 7) == 0)
1044 {
1045 errStr += " Assertion";
1046 }
1047 else
1048 {
1049 errStr += " Deassertion";
1050 }
1051
1052 return;
1053}
1054
Vijay Khemka63c99be2020-05-27 19:14:35 -07001055static void parseOemSel(TsOemSELEntry* data, std::string& errStr)
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001056{
1057 std::stringstream tmpStream;
1058 tmpStream << std::hex << std::uppercase << std::setfill('0');
1059
1060 switch (data->recordType)
1061 {
1062 case 0xC0:
1063 tmpStream << "VID:0x" << std::setw(2) << (int)data->oemData[1]
1064 << std::setw(2) << (int)data->oemData[0] << " DID:0x"
1065 << std::setw(2) << (int)data->oemData[3] << std::setw(2)
1066 << (int)data->oemData[2] << " Slot:0x" << std::setw(2)
1067 << (int)data->oemData[4] << " Error ID:0x" << std::setw(2)
1068 << (int)data->oemData[5];
1069 break;
1070 case 0xC2:
1071 tmpStream << "Extra info:0x" << std::setw(2)
1072 << (int)data->oemData[1] << " MSCOD:0x" << std::setw(2)
1073 << (int)data->oemData[3] << std::setw(2)
1074 << (int)data->oemData[2] << " MCACOD:0x" << std::setw(2)
1075 << (int)data->oemData[5] << std::setw(2)
1076 << (int)data->oemData[4];
1077 break;
1078 case 0xC3:
1079 int bank = (data->oemData[1] & 0xf0) >> 4;
1080 int col = ((data->oemData[1] & 0x0f) << 8) | data->oemData[2];
1081
1082 tmpStream << "Fail Device:0x" << std::setw(2)
1083 << (int)data->oemData[0] << " Bank:0x" << std::setw(2)
1084 << bank << " Column:0x" << std::setw(2) << col
1085 << " Failed Row:0x" << std::setw(2)
1086 << (int)data->oemData[3] << std::setw(2)
1087 << (int)data->oemData[4] << std::setw(2)
1088 << (int)data->oemData[5];
1089 }
1090
1091 errStr = tmpStream.str();
1092
1093 return;
1094}
1095
Vijay Khemka63c99be2020-05-27 19:14:35 -07001096static void parseOemUnifiedSel(NtsOemSELEntry* data, std::string& errStr)
Vijay Khemka34a875f2019-08-09 15:08:15 -07001097{
Vijay Khemka63c99be2020-05-27 19:14:35 -07001098 uint8_t* ptr = data->oemData;
Vijay Khemka34a875f2019-08-09 15:08:15 -07001099 int genInfo = ptr[0];
1100 int errType = genInfo & 0x0f;
1101 std::vector<std::string> dimmEvent = {
1102 "Memory training failure", "Memory correctable error",
1103 "Memory uncorrectable error", "Reserved"};
1104
1105 std::stringstream tmpStream;
1106 tmpStream << std::hex << std::uppercase << std::setfill('0');
1107
1108 switch (errType)
1109 {
1110 case unifiedPcieErr:
1111 if (((genInfo & 0x10) >> 4) == 0) // x86
1112 {
1113 tmpStream << "GeneralInfo: x86/PCIeErr(0x" << std::setw(2)
1114 << genInfo << "),";
1115 }
1116
1117 tmpStream << " Bus " << std::setw(2) << (int)(ptr[8]) << "/Dev "
1118 << std::setw(2) << (int)(ptr[7] >> 3) << "/Fun "
1119 << std::setw(2) << (int)(ptr[7] & 0x7)
1120 << ", TotalErrID1Cnt: 0x" << std::setw(4)
1121 << (int)((ptr[10] << 8) | ptr[9]) << ", ErrID2: 0x"
1122 << std::setw(2) << (int)(ptr[11]) << ", ErrID1: 0x"
1123 << std::setw(2) << (int)(ptr[12]);
1124
1125 break;
1126 case unifiedMemErr:
1127 tmpStream << "GeneralInfo: MemErr(0x" << std::setw(2) << genInfo
1128 << "), DIMM Slot Location: Sled " << std::setw(2)
1129 << (int)((ptr[5] >> 4) & 0x03) << "/Socket "
1130 << std::setw(2) << (int)(ptr[5] & 0x0f) << ", Channel "
1131 << std::setw(2) << (int)(ptr[6] & 0x0f) << ", Slot "
1132 << std::setw(2) << (int)(ptr[7] & 0x0f)
1133 << ", DIMM Failure Event: " << dimmEvent[(ptr[9] & 0x03)]
1134 << ", Major Code: 0x" << std::setw(2) << (int)(ptr[10])
1135 << ", Minor Code: 0x" << std::setw(2) << (int)(ptr[11]);
1136
1137 break;
1138 default:
1139 std::vector<uint8_t> oemData(ptr, ptr + 13);
1140 std::string oemDataStr;
1141 toHexStr(oemData, oemDataStr);
1142 tmpStream << "Undefined Error Type(0x" << std::setw(2) << errType
1143 << "), Raw: " << oemDataStr;
1144 }
1145
1146 errStr = tmpStream.str();
1147
1148 return;
1149}
1150
Manikandan Elumalaic056dc02020-12-11 06:20:32 +05301151static void parseSelData(uint8_t fruId, std::vector<uint8_t>& reqData,
1152 std::string& msgLog)
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001153{
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001154 /* Get record type */
1155 int recType = reqData[2];
1156 std::string errType, errLog;
1157
Vijay Khemka63c99be2020-05-27 19:14:35 -07001158 uint8_t* ptr = NULL;
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001159
1160 std::stringstream recTypeStream;
1161 recTypeStream << std::hex << std::uppercase << std::setfill('0')
1162 << std::setw(2) << recType;
1163
Manikandan Elumalaic056dc02020-12-11 06:20:32 +05301164 msgLog = "SEL Entry: FRU: " + std::to_string(fruId) + ", Record: ";
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001165
1166 if (recType == stdErrType)
1167 {
Vijay Khemka63c99be2020-05-27 19:14:35 -07001168 StdSELEntry* data = reinterpret_cast<StdSELEntry*>(&reqData[0]);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001169 std::string sensorName;
1170
1171 errType = stdErr;
1172 if (data->sensorType == 0x1F)
1173 {
1174 sensorName = "OS";
1175 }
1176 else
1177 {
1178 auto findSensorName = sensorNameTable.find(data->sensorNum);
1179 if (findSensorName == sensorNameTable.end())
1180 {
1181 sensorName = "Unknown";
1182 }
1183 else
1184 {
Vijay Khemka139aa4f2019-08-16 09:57:41 -07001185 sensorName = findSensorName->second.first;
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001186 }
1187 }
1188
Willy Tue39f9392022-06-15 13:24:20 -07001189 uint32_t timeStamp = data->timeStamp;
1190 std::tm* ts = localtime(reinterpret_cast<time_t*>(&timeStamp));
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001191 std::string timeStr = std::asctime(ts);
1192
1193 parseStdSel(data, errLog);
1194 ptr = &(data->eventData1);
1195 std::vector<uint8_t> evtData(ptr, ptr + 3);
1196 std::string eventData;
1197 toHexStr(evtData, eventData);
1198
1199 std::stringstream senNumStream;
1200 senNumStream << std::hex << std::uppercase << std::setfill('0')
1201 << std::setw(2) << (int)(data->sensorNum);
1202
1203 msgLog += errType + " (0x" + recTypeStream.str() +
1204 "), Time: " + timeStr + ", Sensor: " + sensorName + " (0x" +
1205 senNumStream.str() + "), Event Data: (" + eventData + ") " +
1206 errLog;
1207 }
1208 else if ((recType >= oemTSErrTypeMin) && (recType <= oemTSErrTypeMax))
1209 {
1210 /* timestamped OEM SEL records */
Vijay Khemka63c99be2020-05-27 19:14:35 -07001211 TsOemSELEntry* data = reinterpret_cast<TsOemSELEntry*>(&reqData[0]);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001212 ptr = data->mfrId;
1213 std::vector<uint8_t> mfrIdData(ptr, ptr + 3);
1214 std::string mfrIdStr;
1215 toHexStr(mfrIdData, mfrIdStr);
1216
1217 ptr = data->oemData;
1218 std::vector<uint8_t> oemData(ptr, ptr + 6);
1219 std::string oemDataStr;
1220 toHexStr(oemData, oemDataStr);
1221
Willy Tue39f9392022-06-15 13:24:20 -07001222 uint32_t timeStamp = data->timeStamp;
1223 std::tm* ts = localtime(reinterpret_cast<time_t*>(&timeStamp));
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001224 std::string timeStr = std::asctime(ts);
1225
1226 errType = oemTSErr;
1227 parseOemSel(data, errLog);
1228
1229 msgLog += errType + " (0x" + recTypeStream.str() +
1230 "), Time: " + timeStr + ", MFG ID: " + mfrIdStr +
1231 ", OEM Data: (" + oemDataStr + ") " + errLog;
1232 }
Vijay Khemka34a875f2019-08-09 15:08:15 -07001233 else if (recType == fbUniErrType)
1234 {
Vijay Khemka63c99be2020-05-27 19:14:35 -07001235 NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]);
Vijay Khemka34a875f2019-08-09 15:08:15 -07001236 errType = fbUniSELErr;
1237 parseOemUnifiedSel(data, errLog);
1238 msgLog += errType + " (0x" + recTypeStream.str() + "), " + errLog;
1239 }
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001240 else if ((recType >= oemNTSErrTypeMin) && (recType <= oemNTSErrTypeMax))
1241 {
1242 /* Non timestamped OEM SEL records */
Vijay Khemka63c99be2020-05-27 19:14:35 -07001243 NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001244 errType = oemNTSErr;
1245
1246 ptr = data->oemData;
1247 std::vector<uint8_t> oemData(ptr, ptr + 13);
1248 std::string oemDataStr;
1249 toHexStr(oemData, oemDataStr);
1250
Vijay Khemka63c99be2020-05-27 19:14:35 -07001251 parseOemSel((TsOemSELEntry*)data, errLog);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001252 msgLog += errType + " (0x" + recTypeStream.str() + "), OEM Data: (" +
1253 oemDataStr + ") " + errLog;
1254 }
1255 else
1256 {
1257 errType = unknownErr;
1258 toHexStr(reqData, errLog);
Patrick Williams2405ae92023-05-10 07:50:09 -05001259 msgLog += errType + " (0x" + recTypeStream.str() +
1260 ") RawData: " + errLog;
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001261 }
1262}
1263
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001264} // namespace fb_oem::ipmi::sel
1265
1266namespace ipmi
1267{
1268
1269namespace storage
1270{
1271
1272static void registerSELFunctions() __attribute__((constructor));
1273static fb_oem::ipmi::sel::SELData selObj __attribute__((init_priority(101)));
1274
1275ipmi::RspType<uint8_t, // SEL version
1276 uint16_t, // SEL entry count
1277 uint16_t, // free space
1278 uint32_t, // last add timestamp
1279 uint32_t, // last erase timestamp
1280 uint8_t> // operation support
1281 ipmiStorageGetSELInfo()
1282{
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001283 fb_oem::ipmi::sel::GetSELInfoData info;
1284
1285 selObj.getInfo(info);
1286 return ipmi::responseSuccess(info.selVersion, info.entries, info.freeSpace,
1287 info.addTimeStamp, info.eraseTimeStamp,
1288 info.operationSupport);
1289}
1290
1291ipmi::RspType<uint16_t, std::vector<uint8_t>>
1292 ipmiStorageGetSELEntry(std::vector<uint8_t> data)
1293{
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001294 if (data.size() != sizeof(fb_oem::ipmi::sel::GetSELEntryRequest))
1295 {
1296 return ipmi::responseReqDataLenInvalid();
1297 }
1298
Vijay Khemka63c99be2020-05-27 19:14:35 -07001299 fb_oem::ipmi::sel::GetSELEntryRequest* reqData =
1300 reinterpret_cast<fb_oem::ipmi::sel::GetSELEntryRequest*>(&data[0]);
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001301
1302 if (reqData->reservID != 0)
1303 {
1304 if (!checkSELReservation(reqData->reservID))
1305 {
1306 return ipmi::responseInvalidReservationId();
1307 }
1308 }
1309
1310 uint16_t selCnt = selObj.getCount();
1311 if (selCnt == 0)
1312 {
1313 return ipmi::responseSensorInvalid();
1314 }
1315
1316 /* If it is asked for first entry */
1317 if (reqData->recordID == fb_oem::ipmi::sel::firstEntry)
1318 {
1319 /* First Entry (0x0000) as per Spec */
1320 reqData->recordID = 1;
1321 }
1322 else if (reqData->recordID == fb_oem::ipmi::sel::lastEntry)
1323 {
1324 /* Last entry (0xFFFF) as per Spec */
1325 reqData->recordID = selCnt;
1326 }
1327
1328 std::string ipmiRaw;
1329
1330 if (selObj.getEntry(reqData->recordID, ipmiRaw) < 0)
1331 {
1332 return ipmi::responseSensorInvalid();
1333 }
1334
1335 std::vector<uint8_t> recDataBytes;
1336 if (fromHexStr(ipmiRaw, recDataBytes) < 0)
1337 {
1338 return ipmi::responseUnspecifiedError();
1339 }
1340
1341 /* Identify the next SEL record ID. If recordID is same as
1342 * total SeL count then next id should be last entry else
1343 * it should be incremented by 1 to current RecordID
1344 */
1345 uint16_t nextRecord;
1346 if (reqData->recordID == selCnt)
1347 {
1348 nextRecord = fb_oem::ipmi::sel::lastEntry;
1349 }
1350 else
1351 {
1352 nextRecord = reqData->recordID + 1;
1353 }
1354
1355 if (reqData->readLen == fb_oem::ipmi::sel::entireRecord)
1356 {
1357 return ipmi::responseSuccess(nextRecord, recDataBytes);
1358 }
1359 else
1360 {
1361 if (reqData->offset >= fb_oem::ipmi::sel::selRecordSize ||
1362 reqData->readLen > fb_oem::ipmi::sel::selRecordSize)
1363 {
1364 return ipmi::responseUnspecifiedError();
1365 }
1366 std::vector<uint8_t> recPartData;
1367
1368 auto diff = fb_oem::ipmi::sel::selRecordSize - reqData->offset;
1369 auto readLength = std::min(diff, static_cast<int>(reqData->readLen));
1370
1371 for (int i = 0; i < readLength; i++)
1372 {
1373 recPartData.push_back(recDataBytes[i + reqData->offset]);
1374 }
1375 return ipmi::responseSuccess(nextRecord, recPartData);
1376 }
1377}
1378
Manikandan Elumalaic056dc02020-12-11 06:20:32 +05301379ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(ipmi::Context::ptr ctx,
1380 std::vector<uint8_t> data)
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001381{
1382 /* Per the IPMI spec, need to cancel any reservation when a
1383 * SEL entry is added
1384 */
1385 cancelSELReservation();
1386
1387 if (data.size() != fb_oem::ipmi::sel::selRecordSize)
1388 {
1389 return ipmi::responseReqDataLenInvalid();
1390 }
1391
1392 std::string ipmiRaw, logErr;
1393 toHexStr(data, ipmiRaw);
1394
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001395 /* Parse sel data and get an error log to be filed */
Manikandan Elumalaic056dc02020-12-11 06:20:32 +05301396 fb_oem::ipmi::sel::parseSelData((ctx->hostIdx + 1), data, logErr);
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001397
Vijay Khemka15a7ae82019-08-20 12:08:14 -07001398 static const std::string openBMCMessageRegistryVersion("0.1");
Patrick Williams2405ae92023-05-10 07:50:09 -05001399 std::string messageID = "OpenBMC." + openBMCMessageRegistryVersion +
1400 ".SELEntryAdded";
Vijay Khemka15a7ae82019-08-20 12:08:14 -07001401
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001402 /* Log the Raw SEL message to the journal */
1403 std::string journalMsg = "SEL Entry Added: " + ipmiRaw;
Vijay Khemkaf36f3452019-08-09 13:24:45 -07001404
Vijay Khemka15a7ae82019-08-20 12:08:14 -07001405 phosphor::logging::log<phosphor::logging::level::INFO>(
1406 journalMsg.c_str(),
1407 phosphor::logging::entry("IPMISEL_MESSAGE_ID=%s", messageID.c_str()),
1408 phosphor::logging::entry("IPMISEL_MESSAGE_ARGS=%s", logErr.c_str()));
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001409
1410 int responseID = selObj.addEntry(ipmiRaw.c_str());
1411 if (responseID < 0)
1412 {
1413 return ipmi::responseUnspecifiedError();
1414 }
1415 return ipmi::responseSuccess((uint16_t)responseID);
1416}
1417
Vijay Khemkac1921c62019-08-09 13:11:31 -07001418ipmi::RspType<uint8_t> ipmiStorageClearSEL(uint16_t reservationID,
Vijay Khemka63c99be2020-05-27 19:14:35 -07001419 const std::array<uint8_t, 3>& clr,
Vijay Khemkac1921c62019-08-09 13:11:31 -07001420 uint8_t eraseOperation)
1421{
1422 if (!checkSELReservation(reservationID))
1423 {
1424 return ipmi::responseInvalidReservationId();
1425 }
1426
1427 static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
1428 if (clr != clrExpected)
1429 {
1430 return ipmi::responseInvalidFieldRequest();
1431 }
1432
1433 /* If there is no sel then return erase complete */
1434 if (selObj.getCount() == 0)
1435 {
1436 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1437 }
1438
1439 /* Erasure status cannot be fetched, so always return erasure
1440 * status as `erase completed`.
1441 */
1442 if (eraseOperation == fb_oem::ipmi::sel::getEraseStatus)
1443 {
1444 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1445 }
1446
1447 /* Check that initiate erase is correct */
1448 if (eraseOperation != fb_oem::ipmi::sel::initiateErase)
1449 {
1450 return ipmi::responseInvalidFieldRequest();
1451 }
1452
1453 /* Per the IPMI spec, need to cancel any reservation when the
1454 * SEL is cleared
1455 */
1456 cancelSELReservation();
1457
1458 /* Clear the complete Sel Json object */
1459 if (selObj.clear() < 0)
1460 {
1461 return ipmi::responseUnspecifiedError();
1462 }
1463
1464 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete);
1465}
1466
1467ipmi::RspType<uint32_t> ipmiStorageGetSELTime()
1468{
1469 struct timespec selTime = {};
1470
1471 if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
1472 {
1473 return ipmi::responseUnspecifiedError();
1474 }
1475
1476 return ipmi::responseSuccess(selTime.tv_sec);
1477}
1478
Willy Tue39f9392022-06-15 13:24:20 -07001479ipmi::RspType<> ipmiStorageSetSELTime(uint32_t)
Vijay Khemkac1921c62019-08-09 13:11:31 -07001480{
1481 // Set SEL Time is not supported
1482 return ipmi::responseInvalidCommand();
1483}
1484
1485ipmi::RspType<uint16_t> ipmiStorageGetSELTimeUtcOffset()
1486{
1487 /* TODO: For now, the SEL time stamp is based on UTC time,
1488 * so return 0x0000 as offset. Might need to change once
1489 * supporting zones in SEL time stamps
1490 */
1491
1492 uint16_t utcOffset = 0x0000;
1493 return ipmi::responseSuccess(utcOffset);
1494}
1495
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001496void registerSELFunctions()
1497{
1498 // <Get SEL Info>
1499 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1500 ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
1501 ipmiStorageGetSELInfo);
1502
1503 // <Get SEL Entry>
1504 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1505 ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
1506 ipmiStorageGetSELEntry);
1507
1508 // <Add SEL Entry>
1509 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1510 ipmi::storage::cmdAddSelEntry,
1511 ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
1512
Vijay Khemkac1921c62019-08-09 13:11:31 -07001513 // <Clear SEL>
1514 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1515 ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
1516 ipmiStorageClearSEL);
1517
1518 // <Get SEL Time>
1519 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1520 ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
1521 ipmiStorageGetSELTime);
1522
1523 // <Set SEL Time>
1524 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1525 ipmi::storage::cmdSetSelTime,
1526 ipmi::Privilege::Operator, ipmiStorageSetSELTime);
1527
1528 // <Get SEL Time UTC Offset>
1529 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1530 ipmi::storage::cmdGetSelTimeUtcOffset,
1531 ipmi::Privilege::User,
1532 ipmiStorageGetSELTimeUtcOffset);
1533
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001534 return;
1535}
1536
1537} // namespace storage
1538} // namespace ipmi