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