1 /* 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 18 #include <boost/algorithm/string/join.hpp> 19 #include <boost/container/flat_map.hpp> 20 #include <ipmid/api.hpp> 21 #include <nlohmann/json.hpp> 22 #include <phosphor-logging/log.hpp> 23 #include <sdbusplus/message/types.hpp> 24 #include <sdbusplus/timer.hpp> 25 #include <storagecommands.hpp> 26 27 #include <fstream> 28 #include <iostream> 29 #include <sstream> 30 31 enum class MemErrType 32 { 33 memTrainErr = 0, 34 memPmicErr = 7 35 }; 36 37 enum class PostEvtType 38 { 39 pxeBootFail = 0, 40 httpBootFail = 6, 41 getCertFail = 7, 42 amdAblFail = 10 43 }; 44 45 enum class PcieEvtType 46 { 47 dpc = 0 48 }; 49 50 enum class MemEvtType 51 { 52 ppr = 0, 53 adddc = 5, 54 noDimm = 7 55 }; 56 57 //---------------------------------------------------------------------- 58 // Platform specific functions for storing app data 59 //---------------------------------------------------------------------- 60 61 static std::string byteToStr(uint8_t byte) 62 { 63 std::stringstream ss; 64 65 ss << std::hex << std::uppercase << std::setfill('0'); 66 ss << std::setw(2) << (int)byte; 67 68 return ss.str(); 69 } 70 71 static void toHexStr(std::vector<uint8_t>& bytes, std::string& hexStr) 72 { 73 std::stringstream stream; 74 stream << std::hex << std::uppercase << std::setfill('0'); 75 for (const uint8_t byte : bytes) 76 { 77 stream << std::setw(2) << static_cast<int>(byte); 78 } 79 hexStr = stream.str(); 80 } 81 82 static int fromHexStr(const std::string hexStr, std::vector<uint8_t>& data) 83 { 84 for (unsigned int i = 0; i < hexStr.size(); i += 2) 85 { 86 try 87 { 88 data.push_back(static_cast<uint8_t>( 89 std::stoul(hexStr.substr(i, 2), nullptr, 16))); 90 } 91 catch (const std::invalid_argument& e) 92 { 93 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 94 return -1; 95 } 96 catch (const std::out_of_range& e) 97 { 98 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 99 return -1; 100 } 101 } 102 return 0; 103 } 104 105 namespace fb_oem::ipmi::sel 106 { 107 108 class SELData 109 { 110 private: 111 nlohmann::json selDataObj; 112 113 void flush() 114 { 115 std::ofstream file(SEL_JSON_DATA_FILE); 116 file << selDataObj; 117 file.close(); 118 } 119 120 void init() 121 { 122 selDataObj[KEY_SEL_VER] = 0x51; 123 selDataObj[KEY_SEL_COUNT] = 0; 124 selDataObj[KEY_ADD_TIME] = 0xFFFFFFFF; 125 selDataObj[KEY_ERASE_TIME] = 0xFFFFFFFF; 126 selDataObj[KEY_OPER_SUPP] = 0x02; 127 /* Spec indicates that more than 64kB is free */ 128 selDataObj[KEY_FREE_SPACE] = 0xFFFF; 129 } 130 131 public: 132 SELData() 133 { 134 /* Get App data stored in json file */ 135 std::ifstream file(SEL_JSON_DATA_FILE); 136 if (file) 137 { 138 file >> selDataObj; 139 file.close(); 140 } 141 142 /* Initialize SelData object if no entries. */ 143 if (selDataObj.find(KEY_SEL_COUNT) == selDataObj.end()) 144 { 145 init(); 146 } 147 } 148 149 int clear() 150 { 151 /* Clear the complete Sel Json object */ 152 selDataObj.clear(); 153 /* Reinitialize it with basic data */ 154 init(); 155 /* Save the erase time */ 156 struct timespec selTime = {}; 157 if (clock_gettime(CLOCK_REALTIME, &selTime) < 0) 158 { 159 return -1; 160 } 161 selDataObj[KEY_ERASE_TIME] = selTime.tv_sec; 162 flush(); 163 return 0; 164 } 165 166 uint32_t getCount() 167 { 168 return selDataObj[KEY_SEL_COUNT]; 169 } 170 171 void getInfo(GetSELInfoData& info) 172 { 173 info.selVersion = selDataObj[KEY_SEL_VER]; 174 info.entries = selDataObj[KEY_SEL_COUNT]; 175 info.freeSpace = selDataObj[KEY_FREE_SPACE]; 176 info.addTimeStamp = selDataObj[KEY_ADD_TIME]; 177 info.eraseTimeStamp = selDataObj[KEY_ERASE_TIME]; 178 info.operationSupport = selDataObj[KEY_OPER_SUPP]; 179 } 180 181 int getEntry(uint32_t index, std::string& rawStr) 182 { 183 std::stringstream ss; 184 ss << std::hex; 185 ss << std::setw(2) << std::setfill('0') << index; 186 187 /* Check or the requested SEL Entry, if record is available */ 188 if (selDataObj.find(ss.str()) == selDataObj.end()) 189 { 190 return -1; 191 } 192 193 rawStr = selDataObj[ss.str()][KEY_SEL_ENTRY_RAW]; 194 return 0; 195 } 196 197 int addEntry(std::string keyStr) 198 { 199 struct timespec selTime = {}; 200 201 if (clock_gettime(CLOCK_REALTIME, &selTime) < 0) 202 { 203 return -1; 204 } 205 206 selDataObj[KEY_ADD_TIME] = selTime.tv_sec; 207 208 int selCount = selDataObj[KEY_SEL_COUNT]; 209 selDataObj[KEY_SEL_COUNT] = ++selCount; 210 211 std::stringstream ss; 212 ss << std::hex; 213 ss << std::setw(2) << std::setfill('0') << selCount; 214 215 selDataObj[ss.str()][KEY_SEL_ENTRY_RAW] = keyStr; 216 flush(); 217 return selCount; 218 } 219 }; 220 221 /* 222 * A Function to parse common SEL message, a helper function 223 * for parseStdSel. 224 * 225 * Note that this function __CANNOT__ be overridden. 226 * To add board specific routine, please override parseStdSel. 227 */ 228 229 /*Used by decoding ME event*/ 230 std::vector<std::string> nmDomName = { 231 "Entire Platform", "CPU Subsystem", 232 "Memory Subsystem", "HW Protection", 233 "High Power I/O subsystem", "Unknown"}; 234 235 /* Default log message for unknown type */ 236 static void logDefault(uint8_t*, std::string& errLog) 237 { 238 errLog = "Unknown"; 239 } 240 241 static void logSysEvent(uint8_t* data, std::string& errLog) 242 { 243 if (data[0] == 0xE5) 244 { 245 errLog = "Cause of Time change - "; 246 switch (data[2]) 247 { 248 case 0x00: 249 errLog += "NTP"; 250 break; 251 case 0x01: 252 errLog += "Host RTL"; 253 break; 254 case 0x02: 255 errLog += "Set SEL time cmd"; 256 break; 257 case 0x03: 258 errLog += "Set SEL time UTC offset cmd"; 259 break; 260 default: 261 errLog += "Unknown"; 262 } 263 264 if (data[1] == 0x00) 265 errLog += " - First Time"; 266 else if (data[1] == 0x80) 267 errLog += " - Second Time"; 268 } 269 else 270 { 271 errLog = "Unknown"; 272 } 273 } 274 275 static void logThermalEvent(uint8_t* data, std::string& errLog) 276 { 277 if (data[0] == 0x1) 278 { 279 errLog = "Limit Exceeded"; 280 } 281 else 282 { 283 errLog = "Unknown"; 284 } 285 } 286 287 static void logCritIrq(uint8_t* data, std::string& errLog) 288 { 289 if (data[0] == 0x0) 290 { 291 errLog = "NMI / Diagnostic Interrupt"; 292 } 293 else if (data[0] == 0x03) 294 { 295 errLog = "Software NMI"; 296 } 297 else 298 { 299 errLog = "Unknown"; 300 } 301 302 /* TODO: Call add_cri_sel for CRITICAL_IRQ */ 303 } 304 305 static void logPostErr(uint8_t* data, std::string& errLog) 306 { 307 if ((data[0] & 0x0F) == 0x0) 308 { 309 errLog = "System Firmware Error"; 310 } 311 else 312 { 313 errLog = "Unknown"; 314 } 315 316 if (((data[0] >> 6) & 0x03) == 0x3) 317 { 318 // TODO: Need to implement IPMI spec based Post Code 319 errLog += ", IPMI Post Code"; 320 } 321 else if (((data[0] >> 6) & 0x03) == 0x2) 322 { 323 errLog += ", OEM Post Code 0x" + byteToStr(data[2]) + 324 byteToStr(data[1]); 325 326 switch ((data[2] << 8) | data[1]) 327 { 328 case 0xA105: 329 errLog += ", BMC Failed (No Response)"; 330 break; 331 case 0xA106: 332 errLog += ", BMC Failed (Self Test Fail)"; 333 break; 334 case 0xA10A: 335 errLog += ", System Firmware Corruption Detected"; 336 break; 337 case 0xA10B: 338 errLog += ", TPM Self-Test FAIL Detected"; 339 } 340 } 341 } 342 343 static void logMchChkErr(uint8_t* data, std::string& errLog) 344 { 345 /* TODO: Call add_cri_sel for CRITICAL_IRQ */ 346 if ((data[0] & 0x0F) == 0x0B) 347 { 348 errLog = "Uncorrectable"; 349 } 350 else if ((data[0] & 0x0F) == 0x0C) 351 { 352 errLog = "Correctable"; 353 } 354 else 355 { 356 errLog = "Unknown"; 357 } 358 359 errLog += ", Machine Check bank Number " + std::to_string(data[1]) + 360 ", CPU " + std::to_string(data[2] >> 5) + ", Core " + 361 std::to_string(data[2] & 0x1F); 362 } 363 364 static void logPcieErr(uint8_t* data, std::string& errLog) 365 { 366 std::stringstream tmp1, tmp2; 367 tmp1 << std::hex << std::uppercase << std::setfill('0'); 368 tmp2 << std::hex << std::uppercase << std::setfill('0'); 369 tmp1 << " (Bus " << std::setw(2) << (int)(data[2]) << " / Dev " 370 << std::setw(2) << (int)(data[1] >> 3) << " / Fun " << std::setw(2) 371 << (int)(data[1] & 0x7) << ")"; 372 373 switch (data[0] & 0xF) 374 { 375 case 0x4: 376 errLog = "PCI PERR" + tmp1.str(); 377 break; 378 case 0x5: 379 errLog = "PCI SERR" + tmp1.str(); 380 break; 381 case 0x7: 382 errLog = "Correctable" + tmp1.str(); 383 break; 384 case 0x8: 385 errLog = "Uncorrectable" + tmp1.str(); 386 break; 387 case 0xA: 388 errLog = "Bus Fatal" + tmp1.str(); 389 break; 390 case 0xD: 391 { 392 uint32_t venId = (uint32_t)data[1] << 8 | (uint32_t)data[2]; 393 tmp2 << "Vendor ID: 0x" << std::setw(4) << venId; 394 errLog = tmp2.str(); 395 } 396 break; 397 case 0xE: 398 { 399 uint32_t devId = (uint32_t)data[1] << 8 | (uint32_t)data[2]; 400 tmp2 << "Device ID: 0x" << std::setw(4) << devId; 401 errLog = tmp2.str(); 402 } 403 break; 404 case 0xF: 405 tmp2 << "Error ID from downstream: 0x" << std::setw(2) 406 << (int)(data[1]) << std::setw(2) << (int)(data[2]); 407 errLog = tmp2.str(); 408 break; 409 default: 410 errLog = "Unknown"; 411 } 412 } 413 414 static void logIioErr(uint8_t* data, std::string& errLog) 415 { 416 std::vector<std::string> tmpStr = { 417 "IRP0", "IRP1", " IIO-Core", "VT-d", "Intel Quick Data", 418 "Misc", " DMA", "ITC", "OTC", "CI"}; 419 420 if ((data[0] & 0xF) == 0) 421 { 422 errLog += "CPU " + std::to_string(data[2] >> 5) + ", Error ID 0x" + 423 byteToStr(data[1]) + " - "; 424 425 if ((data[2] & 0xF) <= 0x9) 426 { 427 errLog += tmpStr[(data[2] & 0xF)]; 428 } 429 else 430 { 431 errLog += "Reserved"; 432 } 433 } 434 else 435 { 436 errLog = "Unknown"; 437 } 438 } 439 440 [[maybe_unused]] static void logMemErr(uint8_t* dataPtr, std::string& errLog) 441 { 442 uint8_t snrType = dataPtr[0]; 443 uint8_t snrNum = dataPtr[1]; 444 uint8_t* data = &(dataPtr[3]); 445 446 /* TODO: add pal_add_cri_sel */ 447 448 if (snrNum == memoryEccError) 449 { 450 /* SEL from MEMORY_ECC_ERR Sensor */ 451 switch (data[0] & 0x0F) 452 { 453 case 0x0: 454 if (snrType == 0x0C) 455 { 456 errLog = "Correctable"; 457 } 458 else if (snrType == 0x10) 459 { 460 errLog = "Correctable ECC error Logging Disabled"; 461 } 462 break; 463 case 0x1: 464 errLog = "Uncorrectable"; 465 break; 466 case 0x5: 467 errLog = "Correctable ECC error Logging Limit Disabled"; 468 break; 469 default: 470 errLog = "Unknown"; 471 } 472 } 473 else if (snrNum == memoryErrLogDIS) 474 { 475 // SEL from MEMORY_ERR_LOG_DIS Sensor 476 if ((data[0] & 0x0F) == 0x0) 477 { 478 errLog = "Correctable Memory Error Logging Disabled"; 479 } 480 else 481 { 482 errLog = "Unknown"; 483 } 484 } 485 else 486 { 487 errLog = "Unknown"; 488 return; 489 } 490 491 /* Common routine for both MEM_ECC_ERR and MEMORY_ERR_LOG_DIS */ 492 493 errLog += " (DIMM " + byteToStr(data[2]) + ") Logical Rank " + 494 std::to_string(data[1] & 0x03); 495 496 /* DIMM number (data[2]): 497 * Bit[7:5]: Socket number (Range: 0-7) 498 * Bit[4:3]: Channel number (Range: 0-3) 499 * Bit[2:0]: DIMM number (Range: 0-7) 500 */ 501 502 /* TODO: Verify these bits */ 503 std::string cpuStr = "CPU# " + std::to_string((data[2] & 0xE0) >> 5); 504 std::string chStr = "CHN# " + std::to_string((data[2] & 0x18) >> 3); 505 std::string dimmStr = "DIMM#" + std::to_string(data[2] & 0x7); 506 507 switch ((data[1] & 0xC) >> 2) 508 { 509 case 0x0: 510 { 511 /* All Info Valid */ 512 [[maybe_unused]] uint8_t chnNum = (data[2] & 0x1C) >> 2; 513 [[maybe_unused]] uint8_t dimmNum = data[2] & 0x3; 514 515 /* TODO: If critical SEL logging is available, do it */ 516 if (snrType == 0x0C) 517 { 518 if ((data[0] & 0x0F) == 0x0) 519 { 520 /* TODO: add_cri_sel */ 521 /* "DIMM"+ 'A'+ chnNum + dimmNum + " ECC err,FRU:1" 522 */ 523 } 524 else if ((data[0] & 0x0F) == 0x1) 525 { 526 /* TODO: add_cri_sel */ 527 /* "DIMM"+ 'A'+ chnNum + dimmNum + " UECC err,FRU:1" 528 */ 529 } 530 } 531 /* Continue to parse the error into a string. All Info Valid 532 */ 533 errLog += " (" + cpuStr + ", " + chStr + ", " + dimmStr + ")"; 534 } 535 536 break; 537 case 0x1: 538 539 /* DIMM info not valid */ 540 errLog += " (" + cpuStr + ", " + chStr + ")"; 541 break; 542 case 0x2: 543 544 /* CHN info not valid */ 545 errLog += " (" + cpuStr + ", " + dimmStr + ")"; 546 break; 547 case 0x3: 548 549 /* CPU info not valid */ 550 errLog += " (" + chStr + ", " + dimmStr + ")"; 551 break; 552 } 553 } 554 555 static void logPwrErr(uint8_t* data, std::string& errLog) 556 { 557 if (data[0] == 0x1) 558 { 559 errLog = "SYS_PWROK failure"; 560 /* Also try logging to Critical log file, if available */ 561 /* "SYS_PWROK failure,FRU:1" */ 562 } 563 else if (data[0] == 0x2) 564 { 565 errLog = "PCH_PWROK failure"; 566 /* Also try logging to Critical log file, if available */ 567 /* "PCH_PWROK failure,FRU:1" */ 568 } 569 else 570 { 571 errLog = "Unknown"; 572 } 573 } 574 575 static void logCatErr(uint8_t* data, std::string& errLog) 576 { 577 if (data[0] == 0x0) 578 { 579 errLog = "IERR/CATERR"; 580 /* Also try logging to Critical log file, if available */ 581 /* "IERR,FRU:1 */ 582 } 583 else if (data[0] == 0xB) 584 { 585 errLog = "MCERR/CATERR"; 586 /* Also try logging to Critical log file, if available */ 587 /* "MCERR,FRU:1 */ 588 } 589 else 590 { 591 errLog = "Unknown"; 592 } 593 } 594 595 static void logDimmHot(uint8_t* data, std::string& errLog) 596 { 597 if ((data[0] << 16 | data[1] << 8 | data[2]) == 0x01FFFF) 598 { 599 errLog = "SOC MEMHOT"; 600 } 601 else 602 { 603 errLog = "Unknown"; 604 /* Also try logging to Critical log file, if available */ 605 /* ""CPU_DIMM_HOT %s,FRU:1" */ 606 } 607 } 608 609 static void logSwNMI(uint8_t* data, std::string& errLog) 610 { 611 if ((data[0] << 16 | data[1] << 8 | data[2]) == 0x03FFFF) 612 { 613 errLog = "Software NMI"; 614 } 615 else 616 { 617 errLog = "Unknown SW NMI"; 618 } 619 } 620 621 static void logCPUThermalSts(uint8_t* data, std::string& errLog) 622 { 623 switch (data[0]) 624 { 625 case 0x0: 626 errLog = "CPU Critical Temperature"; 627 break; 628 case 0x1: 629 errLog = "PROCHOT#"; 630 break; 631 case 0x2: 632 errLog = "TCC Activation"; 633 break; 634 default: 635 errLog = "Unknown"; 636 } 637 } 638 639 static void logMEPwrState(uint8_t* data, std::string& errLog) 640 { 641 switch (data[0]) 642 { 643 case 0: 644 errLog = "RUNNING"; 645 break; 646 case 2: 647 errLog = "POWER_OFF"; 648 break; 649 default: 650 errLog = "Unknown[" + std::to_string(data[0]) + "]"; 651 break; 652 } 653 } 654 655 static void logSPSFwHealth(uint8_t* data, std::string& errLog) 656 { 657 if ((data[0] & 0x0F) == 0x00) 658 { 659 const std::vector<std::string> tmpStr = { 660 "Recovery GPIO forced", 661 "Image execution failed", 662 "Flash erase error", 663 "Flash state information", 664 "Internal error", 665 "BMC did not respond", 666 "Direct Flash update", 667 "Manufacturing error", 668 "Automatic Restore to Factory Presets", 669 "Firmware Exception", 670 "Flash Wear-Out Protection Warning", 671 "Unknown", 672 "Unknown", 673 "DMI interface error", 674 "MCTP interface error", 675 "Auto-configuration finished", 676 "Unsupported Segment Defined Feature", 677 "Unknown", 678 "CPU Debug Capability Disabled", 679 "UMA operation error"}; 680 681 if (data[1] < 0x14) 682 { 683 errLog = tmpStr[data[1]]; 684 } 685 else 686 { 687 errLog = "Unknown"; 688 } 689 } 690 else if ((data[0] & 0x0F) == 0x01) 691 { 692 errLog = "SMBus link failure"; 693 } 694 else 695 { 696 errLog = "Unknown"; 697 } 698 } 699 700 static void logNmExcA(uint8_t* data, std::string& errLog) 701 { 702 /*NM4.0 #550710, Revision 1.95, and turn to p.155*/ 703 if (data[0] == 0xA8) 704 { 705 errLog = "Policy Correction Time Exceeded"; 706 } 707 else 708 { 709 errLog = "Unknown"; 710 } 711 } 712 713 static void logPCHThermal(uint8_t* data, std::string& errLog) 714 { 715 const std::vector<std::string> thresEvtName = { 716 "Lower Non-critical", 717 "Unknown", 718 "Lower Critical", 719 "Unknown", 720 "Lower Non-recoverable", 721 "Unknown", 722 "Unknown", 723 "Upper Non-critical", 724 "Unknown", 725 "Upper Critical", 726 "Unknown", 727 "Upper Non-recoverable"}; 728 729 if ((data[0] & 0x0f) < 12) 730 { 731 errLog = thresEvtName[(data[0] & 0x0f)]; 732 } 733 else 734 { 735 errLog = "Unknown"; 736 } 737 738 errLog += ", curr_val: " + std::to_string(data[1]) + 739 " C, thresh_val: " + std::to_string(data[2]) + " C"; 740 } 741 742 static void logNmHealth(uint8_t* data, std::string& errLog) 743 { 744 std::vector<std::string> nmErrType = { 745 "Unknown", 746 "Unknown", 747 "Unknown", 748 "Unknown", 749 "Unknown", 750 "Unknown", 751 "Unknown", 752 "Extended Telemetry Device Reading Failure", 753 "Outlet Temperature Reading Failure", 754 "Volumetric Airflow Reading Failure", 755 "Policy Misconfiguration", 756 "Power Sensor Reading Failure", 757 "Inlet Temperature Reading Failure", 758 "Host Communication Error", 759 "Real-time Clock Synchronization Failure", 760 "Platform Shutdown Initiated by Intel NM Policy", 761 "Unknown"}; 762 uint8_t nmTypeIdx = (data[0] & 0xf); 763 uint8_t domIdx = (data[1] & 0xf); 764 uint8_t errIdx = ((data[1] >> 4) & 0xf); 765 766 if (nmTypeIdx == 2) 767 { 768 errLog = "SensorIntelNM"; 769 } 770 else 771 { 772 errLog = "Unknown"; 773 } 774 775 errLog += ", Domain:" + nmDomName[domIdx] + ", ErrType:" + 776 nmErrType[errIdx] + ", Err:0x" + byteToStr(data[2]); 777 } 778 779 static void logNmCap(uint8_t* data, std::string& errLog) 780 { 781 const std::vector<std::string> nmCapStsStr = {"Not Available", "Available"}; 782 if (data[0] & 0x7) // BIT1=policy, BIT2=monitoring, BIT3=pwr 783 // limit and the others are reserved 784 { 785 errLog = "PolicyInterface:" + nmCapStsStr[BIT(data[0], 0)] + 786 ",Monitoring:" + nmCapStsStr[BIT(data[0], 1)] + 787 ",PowerLimit:" + nmCapStsStr[BIT(data[0], 2)]; 788 } 789 else 790 { 791 errLog = "Unknown"; 792 } 793 } 794 795 static void logNmThreshold(uint8_t* data, std::string& errLog) 796 { 797 uint8_t thresNum = (data[0] & 0x3); 798 uint8_t domIdx = (data[1] & 0xf); 799 uint8_t polId = data[2]; 800 uint8_t polEvtIdx = BIT(data[0], 3); 801 const std::vector<std::string> polEvtStr = { 802 "Threshold Exceeded", "Policy Correction Time Exceeded"}; 803 804 errLog = "Threshold Number:" + std::to_string(thresNum) + "-" + 805 polEvtStr[polEvtIdx] + ", Domain:" + nmDomName[domIdx] + 806 ", PolicyID:0x" + byteToStr(polId); 807 } 808 809 static void logPwrThreshold(uint8_t* data, std::string& errLog) 810 { 811 if (data[0] == 0x00) 812 { 813 errLog = "Limit Not Exceeded"; 814 } 815 else if (data[0] == 0x01) 816 { 817 errLog = "Limit Exceeded"; 818 } 819 else 820 { 821 errLog = "Unknown"; 822 } 823 } 824 825 static void logMSMI(uint8_t* data, std::string& errLog) 826 { 827 if (data[0] == 0x0) 828 { 829 errLog = "IERR/MSMI"; 830 } 831 else if (data[0] == 0x0B) 832 { 833 errLog = "MCERR/MSMI"; 834 } 835 else 836 { 837 errLog = "Unknown"; 838 } 839 } 840 841 static void logHprWarn(uint8_t* data, std::string& errLog) 842 { 843 if (data[2] == 0x01) 844 { 845 if (data[1] == 0xFF) 846 { 847 errLog = "Infinite Time"; 848 } 849 else 850 { 851 errLog = std::to_string(data[1]) + " minutes"; 852 } 853 } 854 else 855 { 856 errLog = "Unknown"; 857 } 858 } 859 860 static const boost::container::flat_map< 861 uint8_t, 862 std::pair<std::string, std::function<void(uint8_t*, std::string&)>>> 863 sensorNameTable = { 864 {0xE9, {"SYSTEM_EVENT", logSysEvent}}, 865 {0x7D, {"THERM_THRESH_EVT", logThermalEvent}}, 866 {0xAA, {"BUTTON", logDefault}}, 867 {0xAB, {"POWER_STATE", logDefault}}, 868 {0xEA, {"CRITICAL_IRQ", logCritIrq}}, 869 {0x2B, {"POST_ERROR", logPostErr}}, 870 {0x40, {"MACHINE_CHK_ERR", logMchChkErr}}, 871 {0x41, {"PCIE_ERR", logPcieErr}}, 872 {0x43, {"IIO_ERR", logIioErr}}, 873 {0X63, {"MEMORY_ECC_ERR", logDefault}}, 874 {0X87, {"MEMORY_ERR_LOG_DIS", logDefault}}, 875 {0X51, {"PROCHOT_EXT", logDefault}}, 876 {0X56, {"PWR_ERR", logPwrErr}}, 877 {0xE6, {"CATERR_A", logCatErr}}, 878 {0xEB, {"CATERR_B", logCatErr}}, 879 {0xB3, {"CPU_DIMM_HOT", logDimmHot}}, 880 {0x90, {"SOFTWARE_NMI", logSwNMI}}, 881 {0x1C, {"CPU0_THERM_STATUS", logCPUThermalSts}}, 882 {0x1D, {"CPU1_THERM_STATUS", logCPUThermalSts}}, 883 {0x16, {"ME_POWER_STATE", logMEPwrState}}, 884 {0x17, {"SPS_FW_HEALTH", logSPSFwHealth}}, 885 {0x18, {"NM_EXCEPTION_A", logNmExcA}}, 886 {0x08, {"PCH_THERM_THRESHOLD", logPCHThermal}}, 887 {0x19, {"NM_HEALTH", logNmHealth}}, 888 {0x1A, {"NM_CAPABILITIES", logNmCap}}, 889 {0x1B, {"NM_THRESHOLD", logNmThreshold}}, 890 {0x3B, {"PWR_THRESH_EVT", logPwrThreshold}}, 891 {0xE7, {"MSMI", logMSMI}}, 892 {0xC5, {"HPR_WARNING", logHprWarn}}}; 893 894 static void parseSelHelper(StdSELEntry* data, std::string& errStr) 895 { 896 /* Check if sensor type is OS_BOOT (0x1f) */ 897 if (data->sensorType == 0x1F) 898 { 899 /* OS_BOOT used by OS */ 900 switch (data->eventData1 & 0xF) 901 { 902 case 0x07: 903 errStr = "Base OS/Hypervisor Installation started"; 904 break; 905 case 0x08: 906 errStr = "Base OS/Hypervisor Installation completed"; 907 break; 908 case 0x09: 909 errStr = "Base OS/Hypervisor Installation aborted"; 910 break; 911 case 0x0A: 912 errStr = "Base OS/Hypervisor Installation failed"; 913 break; 914 default: 915 errStr = "Unknown"; 916 } 917 return; 918 } 919 920 auto findSensorName = sensorNameTable.find(data->sensorNum); 921 if (findSensorName == sensorNameTable.end()) 922 { 923 errStr = "Unknown"; 924 return; 925 } 926 else 927 { 928 switch (data->sensorNum) 929 { 930 /* logMemErr function needs data from sensor type */ 931 case memoryEccError: 932 case memoryErrLogDIS: 933 findSensorName->second.second(&(data->sensorType), errStr); 934 break; 935 /* Other sensor function needs only event data for parsing */ 936 default: 937 findSensorName->second.second(&(data->eventData1), errStr); 938 } 939 } 940 941 if (((data->eventData3 & 0x80) >> 7) == 0) 942 { 943 errStr += " Assertion"; 944 } 945 else 946 { 947 errStr += " Deassertion"; 948 } 949 } 950 951 static void parseDimmPhyloc(StdSELEntry* data, std::string& errStr) 952 { 953 // Log when " All info available" 954 uint8_t chNum = (data->eventData3 & 0x18) >> 3; 955 uint8_t dimmNum = data->eventData3 & 0x7; 956 uint8_t rankNum = data->eventData2 & 0x03; 957 uint8_t nodeNum = (data->eventData3 & 0xE0) >> 5; 958 959 if (chNum == 3 && dimmNum == 0) 960 { 961 errStr += " Node: " + std::to_string(nodeNum) + "," + 962 " Card: " + std::to_string(chNum) + "," + 963 " Module: " + std::to_string(dimmNum) + "," + 964 " Rank Number: " + std::to_string(rankNum) + "," + 965 " Location: DIMM A0"; 966 } 967 else if (chNum == 2 && dimmNum == 0) 968 { 969 errStr += " Node: " + std::to_string(nodeNum) + "," + 970 " Card: " + std::to_string(chNum) + "," + 971 " Module: " + std::to_string(dimmNum) + "," + 972 " Rank Number: " + std::to_string(rankNum) + "," + 973 " Location: DIMM B0"; 974 } 975 else if (chNum == 4 && dimmNum == 0) 976 { 977 errStr += " Node: " + std::to_string(nodeNum) + "," + 978 " Card: " + std::to_string(chNum) + "," + 979 " Module: " + std::to_string(dimmNum) + "," + 980 " Rank Number: " + std::to_string(rankNum) + "," + 981 " Location: DIMM C0 "; 982 } 983 else if (chNum == 5 && dimmNum == 0) 984 { 985 errStr += " Node: " + std::to_string(nodeNum) + "," + 986 " Card: " + std::to_string(chNum) + "," + 987 " Module: " + std::to_string(dimmNum) + "," + 988 " Rank Number: " + std::to_string(rankNum) + "," + 989 " Location: DIMM D0"; 990 } 991 else 992 { 993 errStr += " Node: " + std::to_string(nodeNum) + "," + 994 " Card: " + std::to_string(chNum) + "," + 995 " Module: " + std::to_string(dimmNum) + "," + 996 " Rank Number: " + std::to_string(rankNum) + "," + 997 " Location: DIMM Unknown"; 998 } 999 } 1000 1001 static void parseStdSel(StdSELEntry* data, std::string& errStr) 1002 { 1003 std::stringstream tmpStream; 1004 tmpStream << std::hex << std::uppercase; 1005 1006 /* TODO: add pal_add_cri_sel */ 1007 switch (data->sensorNum) 1008 { 1009 case memoryEccError: 1010 switch (data->eventData1 & 0x0F) 1011 { 1012 case 0x00: 1013 errStr = "Correctable"; 1014 tmpStream << "DIMM" << std::setw(2) << std::setfill('0') 1015 << data->eventData3 << " ECC err"; 1016 parseDimmPhyloc(data, errStr); 1017 break; 1018 case 0x01: 1019 errStr = "Uncorrectable"; 1020 tmpStream << "DIMM" << std::setw(2) << std::setfill('0') 1021 << data->eventData3 << " UECC err"; 1022 parseDimmPhyloc(data, errStr); 1023 break; 1024 case 0x02: 1025 errStr = "Parity"; 1026 break; 1027 case 0x05: 1028 errStr = "Correctable ECC error Logging Limit Reached"; 1029 break; 1030 default: 1031 errStr = "Unknown"; 1032 } 1033 break; 1034 case memoryErrLogDIS: 1035 if ((data->eventData1 & 0x0F) == 0) 1036 { 1037 errStr = "Correctable Memory Error Logging Disabled"; 1038 } 1039 else 1040 { 1041 errStr = "Unknown"; 1042 } 1043 break; 1044 default: 1045 parseSelHelper(data, errStr); 1046 return; 1047 } 1048 1049 errStr += " (DIMM " + std::to_string(data->eventData3) + ")"; 1050 errStr += " Logical Rank " + std::to_string(data->eventData2 & 0x03); 1051 1052 switch ((data->eventData2 & 0x0C) >> 2) 1053 { 1054 case 0x00: 1055 // Ignore when " All info available" 1056 break; 1057 case 0x01: 1058 errStr += " DIMM info not valid"; 1059 break; 1060 case 0x02: 1061 errStr += " CHN info not valid"; 1062 break; 1063 case 0x03: 1064 errStr += " CPU info not valid"; 1065 break; 1066 default: 1067 errStr += " Unknown"; 1068 } 1069 1070 if (((data->eventType & 0x80) >> 7) == 0) 1071 { 1072 errStr += " Assertion"; 1073 } 1074 else 1075 { 1076 errStr += " Deassertion"; 1077 } 1078 1079 return; 1080 } 1081 1082 static void parseOemSel(TsOemSELEntry* data, std::string& errStr) 1083 { 1084 std::stringstream tmpStream; 1085 tmpStream << std::hex << std::uppercase << std::setfill('0'); 1086 1087 switch (data->recordType) 1088 { 1089 case 0xC0: 1090 tmpStream << "VID:0x" << std::setw(2) << (int)data->oemData[1] 1091 << std::setw(2) << (int)data->oemData[0] << " DID:0x" 1092 << std::setw(2) << (int)data->oemData[3] << std::setw(2) 1093 << (int)data->oemData[2] << " Slot:0x" << std::setw(2) 1094 << (int)data->oemData[4] << " Error ID:0x" << std::setw(2) 1095 << (int)data->oemData[5]; 1096 break; 1097 case 0xC2: 1098 tmpStream << "Extra info:0x" << std::setw(2) 1099 << (int)data->oemData[1] << " MSCOD:0x" << std::setw(2) 1100 << (int)data->oemData[3] << std::setw(2) 1101 << (int)data->oemData[2] << " MCACOD:0x" << std::setw(2) 1102 << (int)data->oemData[5] << std::setw(2) 1103 << (int)data->oemData[4]; 1104 break; 1105 case 0xC3: 1106 int bank = (data->oemData[1] & 0xf0) >> 4; 1107 int col = ((data->oemData[1] & 0x0f) << 8) | data->oemData[2]; 1108 1109 tmpStream << "Fail Device:0x" << std::setw(2) 1110 << (int)data->oemData[0] << " Bank:0x" << std::setw(2) 1111 << bank << " Column:0x" << std::setw(2) << col 1112 << " Failed Row:0x" << std::setw(2) 1113 << (int)data->oemData[3] << std::setw(2) 1114 << (int)data->oemData[4] << std::setw(2) 1115 << (int)data->oemData[5]; 1116 } 1117 1118 errStr = tmpStream.str(); 1119 1120 return; 1121 } 1122 1123 static std::string dimmLocationStr(uint8_t socket, uint8_t channel, 1124 uint8_t slot) 1125 { 1126 uint8_t sled = (socket >> 4) & 0x3; 1127 1128 socket &= 0xf; 1129 if (channel == 0xFF && slot == 0xFF) 1130 { 1131 return std::format( 1132 "DIMM Slot Location: Sled {:02}/Socket {:02}, Channel unknown" 1133 ", Slot unknown, DIMM unknown", 1134 sled, socket); 1135 } 1136 else 1137 { 1138 channel &= 0xf; 1139 slot &= 0xf; 1140 const char label[] = {'A', 'C', 'B', 'D'}; 1141 uint8_t idx = socket * 2 + slot; 1142 return std::format("DIMM Slot Location: Sled {:02}/Socket {:02}" 1143 ", Channel {:02}, Slot {:02} DIMM {}", 1144 sled, socket, channel, slot, 1145 (idx < sizeof(label)) 1146 ? label[idx] + std::to_string(channel) 1147 : "NA"); 1148 } 1149 } 1150 1151 static void parseOemUnifiedSel(NtsOemSELEntry* data, std::string& errStr) 1152 { 1153 uint8_t* ptr = data->oemData; 1154 uint8_t eventType = ptr[5] & 0xf; 1155 int genInfo = ptr[0]; 1156 int errType = genInfo & 0x0f; 1157 std::vector<std::string> dimmErr = { 1158 "Memory training failure", 1159 "Memory correctable error", 1160 "Memory uncorrectable error", 1161 "Memory correctable error (Patrol scrub)", 1162 "Memory uncorrectable error (Patrol scrub)", 1163 "Memory Parity Error (PCC=0)", 1164 "Memory Parity Error (PCC=1)", 1165 "Memory PMIC Error", 1166 "CXL Memory training error", 1167 "Reserved"}; 1168 std::vector<std::string> postEvent = { 1169 "System PXE boot fail", 1170 "CMOS/NVRAM configuration cleared", 1171 "TPM Self-Test Fail", 1172 "Boot Drive failure", 1173 "Data Drive failure", 1174 "Received invalid boot order request from BMC", 1175 "System HTTP boot fail", 1176 "BIOS fails to get the certificate from BMC", 1177 "Password cleared by jumper", 1178 "DXE FV check failure", 1179 "AMD ABL failure", 1180 "Reserved"}; 1181 std::vector<std::string> certErr = { 1182 "No certificate at BMC", "IPMI transaction fail", 1183 "Certificate data corrupted", "Reserved"}; 1184 std::vector<std::string> pcieEvent = { 1185 "PCIe DPC Event", 1186 "PCIe LER Event", 1187 "PCIe Link Retraining and Recovery", 1188 "PCIe Link CRC Error Check and Retry", 1189 "PCIe Corrupt Data Containment", 1190 "PCIe Express ECRC", 1191 "Reserved"}; 1192 std::vector<std::string> memEvent = { 1193 "Memory PPR event", 1194 "Memory Correctable Error logging limit reached", 1195 "Memory disable/map-out for FRB", 1196 "Memory SDDC", 1197 "Memory Address range/Partial mirroring", 1198 "Memory ADDDC", 1199 "Memory SMBus hang recovery", 1200 "No DIMM in System", 1201 "Reserved"}; 1202 std::vector<std::string> memPprTime = {"Boot time", "Autonomous", 1203 "Run time", "Reserved"}; 1204 std::vector<std::string> memPpr = {"PPR success", "PPR fail", "PPR request", 1205 "Reserved"}; 1206 std::vector<std::string> memAdddc = { 1207 "Bank VLS", "r-Bank VLS + re-buddy", "r-Bank VLS + Rank VLS", 1208 "r-Rank VLS + re-buddy", "Reserved"}; 1209 std::vector<std::string> pprEvent = {"PPR disable", "Soft PPR", "Hard PPR", 1210 "Reserved"}; 1211 1212 std::stringstream tmpStream; 1213 1214 switch (errType) 1215 { 1216 case unifiedPcieErr: 1217 tmpStream << std::format( 1218 "GeneralInfo: x86/PCIeErr(0x{:02X})" 1219 ", Bus {:02X}/Dev {:02X}/Fun {:02X}, TotalErrID1Cnt: 0x{:04X}" 1220 ", ErrID2: 0x{:02X}, ErrID1: 0x{:02X}", 1221 genInfo, ptr[8], ptr[7] >> 3, ptr[7] & 0x7, 1222 (ptr[10] << 8) | ptr[9], ptr[11], ptr[12]); 1223 break; 1224 case unifiedMemErr: 1225 eventType = ptr[9] & 0xf; 1226 tmpStream << std::format( 1227 "GeneralInfo: MemErr(0x{:02X}), {}, DIMM Failure Event: {}", 1228 genInfo, dimmLocationStr(ptr[5], ptr[6], ptr[7]), 1229 dimmErr[std::min(eventType, 1230 static_cast<uint8_t>(dimmErr.size() - 1))]); 1231 1232 if (static_cast<MemErrType>(eventType) == MemErrType::memTrainErr || 1233 static_cast<MemErrType>(eventType) == MemErrType::memPmicErr) 1234 { 1235 bool amd = ptr[9] & 0x80; 1236 tmpStream << std::format( 1237 ", Major Code: 0x{:02X}, Minor Code: 0x{:0{}X}", ptr[10], 1238 amd ? (ptr[12] << 8 | ptr[11]) : ptr[11], amd ? 4 : 2); 1239 } 1240 break; 1241 case unifiedIioErr: 1242 tmpStream << std::format( 1243 "GeneralInfo: IIOErr(0x{:02X})" 1244 ", IIO Port Location: Sled {:02}/Socket {:02}, Stack 0x{:02X}" 1245 ", Error Type: 0x{:02X}, Error Severity: 0x{:02X}" 1246 ", Error ID: 0x{:02X}", 1247 genInfo, (ptr[5] >> 4) & 0x3, ptr[5] & 0xf, ptr[6], ptr[10], 1248 ptr[11] & 0xf, ptr[12]); 1249 break; 1250 case unifiedPostEvt: 1251 tmpStream << std::format( 1252 "GeneralInfo: POST(0x{:02X}), POST Failure Event: {}", genInfo, 1253 postEvent[std::min( 1254 eventType, static_cast<uint8_t>(postEvent.size() - 1))]); 1255 1256 switch (static_cast<PostEvtType>(eventType)) 1257 { 1258 case PostEvtType::pxeBootFail: 1259 case PostEvtType::httpBootFail: 1260 { 1261 uint8_t failType = ptr[10] & 0xf; 1262 tmpStream 1263 << std::format(", Fail Type: {}, Error Code: 0x{:02X}", 1264 (failType == 4 || failType == 6) 1265 ? std::format("IPv{} fail", failType) 1266 : std::format("0x{:02X}", ptr[10]), 1267 ptr[11]); 1268 break; 1269 } 1270 case PostEvtType::getCertFail: 1271 tmpStream << std::format( 1272 ", Failure Detail: {}", 1273 certErr[std::min( 1274 ptr[9], static_cast<uint8_t>(certErr.size() - 1))]); 1275 break; 1276 case PostEvtType::amdAblFail: 1277 tmpStream << std::format(", ABL Error Code: 0x{:04X}", 1278 (ptr[12] << 8) | ptr[11]); 1279 break; 1280 } 1281 break; 1282 case unifiedPcieEvt: 1283 tmpStream << std::format( 1284 "GeneralInfo: PCIeEvent(0x{:02X}), PCIe Failure Event: {}", 1285 genInfo, 1286 pcieEvent[std::min( 1287 eventType, static_cast<uint8_t>(pcieEvent.size() - 1))]); 1288 1289 if (static_cast<PcieEvtType>(eventType) == PcieEvtType::dpc) 1290 { 1291 tmpStream << std::format( 1292 ", Status: 0x{:04X}, Source ID: 0x{:04X}", 1293 (ptr[8] << 8) | ptr[7], (ptr[10] << 8) | ptr[9]); 1294 } 1295 break; 1296 case unifiedMemEvt: 1297 eventType = ptr[9] & 0xf; 1298 tmpStream 1299 << std::format("GeneralInfo: MemEvent(0x{:02X})", genInfo) 1300 << (static_cast<MemEvtType>(eventType) != MemEvtType::noDimm 1301 ? std::format(", {}", 1302 dimmLocationStr(ptr[5], ptr[6], ptr[7])) 1303 : "") 1304 << ", DIMM Failure Event: "; 1305 1306 switch (static_cast<MemEvtType>(eventType)) 1307 { 1308 case MemEvtType::ppr: 1309 tmpStream << std::format("{} {}", 1310 memPprTime[(ptr[10] >> 2) & 0x3], 1311 memPpr[ptr[10] & 0x3]); 1312 break; 1313 case MemEvtType::adddc: 1314 tmpStream << std::format( 1315 "{} {}", 1316 memEvent[std::min(eventType, static_cast<uint8_t>( 1317 memEvent.size() - 1))], 1318 memAdddc[std::min( 1319 static_cast<uint8_t>(ptr[11] & 0xf), 1320 static_cast<uint8_t>(memAdddc.size() - 1))]); 1321 break; 1322 default: 1323 tmpStream << std::format( 1324 "{}", memEvent[std::min( 1325 eventType, 1326 static_cast<uint8_t>(memEvent.size() - 1))]); 1327 break; 1328 } 1329 break; 1330 case unifiedBootGuard: 1331 tmpStream << std::format( 1332 "GeneralInfo: Boot Guard ACM Failure Events(0x{:02X})" 1333 ", Error Class: 0x{:02X}, Error Code: 0x{:02X}", 1334 genInfo, ptr[9], ptr[10]); 1335 break; 1336 case unifiedPprEvt: 1337 tmpStream << std::format( 1338 "GeneralInfo: PPREvent(0x{:02X}), {}" 1339 ", DIMM Info: {:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}", 1340 genInfo, 1341 pprEvent[std::min(eventType, 1342 static_cast<uint8_t>(pprEvent.size() - 1))], 1343 ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12]); 1344 break; 1345 default: 1346 std::vector<uint8_t> oemData(ptr, ptr + 13); 1347 std::string oemDataStr; 1348 toHexStr(oemData, oemDataStr); 1349 tmpStream << std::format("Undefined Error Type(0x{:02X}), Raw: {}", 1350 errType, oemDataStr); 1351 } 1352 1353 errStr = tmpStream.str(); 1354 1355 return; 1356 } 1357 1358 static void parseSelData(uint8_t fruId, std::vector<uint8_t>& reqData, 1359 std::string& msgLog) 1360 { 1361 /* Get record type */ 1362 int recType = reqData[2]; 1363 std::string errType, errLog; 1364 1365 uint8_t* ptr = NULL; 1366 1367 std::stringstream recTypeStream; 1368 recTypeStream << std::hex << std::uppercase << std::setfill('0') 1369 << std::setw(2) << recType; 1370 1371 msgLog = "SEL Entry: FRU: " + std::to_string(fruId) + ", Record: "; 1372 1373 if (recType == stdErrType) 1374 { 1375 StdSELEntry* data = reinterpret_cast<StdSELEntry*>(&reqData[0]); 1376 std::string sensorName; 1377 1378 errType = stdErr; 1379 if (data->sensorType == 0x1F) 1380 { 1381 sensorName = "OS"; 1382 } 1383 else 1384 { 1385 auto findSensorName = sensorNameTable.find(data->sensorNum); 1386 if (findSensorName == sensorNameTable.end()) 1387 { 1388 sensorName = "Unknown"; 1389 } 1390 else 1391 { 1392 sensorName = findSensorName->second.first; 1393 } 1394 } 1395 1396 time_t timeStamp = static_cast<time_t>(data->timeStamp); 1397 std::string timeStr; 1398 std::tm ts; 1399 if (localtime_r(&timeStamp, &ts)) 1400 { 1401 char buf[64]; 1402 if (strftime(buf, sizeof(buf), "%c", &ts)) 1403 { 1404 timeStr = buf; 1405 } 1406 } 1407 1408 parseStdSel(data, errLog); 1409 ptr = &(data->eventData1); 1410 std::vector<uint8_t> evtData(ptr, ptr + 3); 1411 std::string eventData; 1412 toHexStr(evtData, eventData); 1413 1414 std::stringstream senNumStream; 1415 senNumStream << std::hex << std::uppercase << std::setfill('0') 1416 << std::setw(2) << (int)(data->sensorNum); 1417 1418 msgLog += errType + " (0x" + recTypeStream.str() + 1419 "), Time: " + timeStr + ", Sensor: " + sensorName + " (0x" + 1420 senNumStream.str() + "), Event Data: (" + eventData + ") " + 1421 errLog; 1422 } 1423 else if ((recType >= oemTSErrTypeMin) && (recType <= oemTSErrTypeMax)) 1424 { 1425 /* timestamped OEM SEL records */ 1426 TsOemSELEntry* data = reinterpret_cast<TsOemSELEntry*>(&reqData[0]); 1427 ptr = data->mfrId; 1428 std::vector<uint8_t> mfrIdData(ptr, ptr + 3); 1429 std::string mfrIdStr; 1430 toHexStr(mfrIdData, mfrIdStr); 1431 1432 ptr = data->oemData; 1433 std::vector<uint8_t> oemData(ptr, ptr + 6); 1434 std::string oemDataStr; 1435 toHexStr(oemData, oemDataStr); 1436 1437 time_t timeStamp = static_cast<time_t>(data->timeStamp); 1438 std::string timeStr; 1439 std::tm ts; 1440 if (localtime_r(&timeStamp, &ts)) 1441 { 1442 char buf[64]; 1443 if (strftime(buf, sizeof(buf), "%c", &ts)) 1444 { 1445 timeStr = buf; 1446 } 1447 } 1448 1449 errType = oemTSErr; 1450 parseOemSel(data, errLog); 1451 1452 msgLog += errType + " (0x" + recTypeStream.str() + 1453 "), Time: " + timeStr + ", MFG ID: " + mfrIdStr + 1454 ", OEM Data: (" + oemDataStr + ") " + errLog; 1455 } 1456 else if (recType == fbUniErrType) 1457 { 1458 NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]); 1459 errType = fbUniSELErr; 1460 parseOemUnifiedSel(data, errLog); 1461 msgLog += errType + " (0x" + recTypeStream.str() + "), " + errLog; 1462 } 1463 else if ((recType >= oemNTSErrTypeMin) && (recType <= oemNTSErrTypeMax)) 1464 { 1465 /* Non timestamped OEM SEL records */ 1466 NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]); 1467 errType = oemNTSErr; 1468 1469 ptr = data->oemData; 1470 std::vector<uint8_t> oemData(ptr, ptr + 13); 1471 std::string oemDataStr; 1472 toHexStr(oemData, oemDataStr); 1473 1474 parseOemSel((TsOemSELEntry*)data, errLog); 1475 msgLog += errType + " (0x" + recTypeStream.str() + "), OEM Data: (" + 1476 oemDataStr + ") " + errLog; 1477 } 1478 else 1479 { 1480 errType = unknownErr; 1481 toHexStr(reqData, errLog); 1482 msgLog += errType + " (0x" + recTypeStream.str() + 1483 ") RawData: " + errLog; 1484 } 1485 } 1486 1487 } // namespace fb_oem::ipmi::sel 1488 1489 namespace ipmi 1490 { 1491 1492 namespace storage 1493 { 1494 1495 static void registerSELFunctions() __attribute__((constructor)); 1496 static fb_oem::ipmi::sel::SELData selObj __attribute__((init_priority(101))); 1497 1498 ipmi::RspType<uint8_t, // SEL version 1499 uint16_t, // SEL entry count 1500 uint16_t, // free space 1501 uint32_t, // last add timestamp 1502 uint32_t, // last erase timestamp 1503 uint8_t> // operation support 1504 ipmiStorageGetSELInfo() 1505 { 1506 fb_oem::ipmi::sel::GetSELInfoData info; 1507 1508 selObj.getInfo(info); 1509 return ipmi::responseSuccess(info.selVersion, info.entries, info.freeSpace, 1510 info.addTimeStamp, info.eraseTimeStamp, 1511 info.operationSupport); 1512 } 1513 1514 ipmi::RspType<uint16_t, std::vector<uint8_t>> 1515 ipmiStorageGetSELEntry(std::vector<uint8_t> data) 1516 { 1517 if (data.size() != sizeof(fb_oem::ipmi::sel::GetSELEntryRequest)) 1518 { 1519 return ipmi::responseReqDataLenInvalid(); 1520 } 1521 1522 fb_oem::ipmi::sel::GetSELEntryRequest* reqData = 1523 reinterpret_cast<fb_oem::ipmi::sel::GetSELEntryRequest*>(&data[0]); 1524 1525 if (reqData->reservID != 0) 1526 { 1527 if (!checkSELReservation(reqData->reservID)) 1528 { 1529 return ipmi::responseInvalidReservationId(); 1530 } 1531 } 1532 1533 uint16_t selCnt = selObj.getCount(); 1534 if (selCnt == 0) 1535 { 1536 return ipmi::responseSensorInvalid(); 1537 } 1538 1539 /* If it is asked for first entry */ 1540 if (reqData->recordID == fb_oem::ipmi::sel::firstEntry) 1541 { 1542 /* First Entry (0x0000) as per Spec */ 1543 reqData->recordID = 1; 1544 } 1545 else if (reqData->recordID == fb_oem::ipmi::sel::lastEntry) 1546 { 1547 /* Last entry (0xFFFF) as per Spec */ 1548 reqData->recordID = selCnt; 1549 } 1550 1551 std::string ipmiRaw; 1552 1553 if (selObj.getEntry(reqData->recordID, ipmiRaw) < 0) 1554 { 1555 return ipmi::responseSensorInvalid(); 1556 } 1557 1558 std::vector<uint8_t> recDataBytes; 1559 if (fromHexStr(ipmiRaw, recDataBytes) < 0) 1560 { 1561 return ipmi::responseUnspecifiedError(); 1562 } 1563 1564 /* Identify the next SEL record ID. If recordID is same as 1565 * total SeL count then next id should be last entry else 1566 * it should be incremented by 1 to current RecordID 1567 */ 1568 uint16_t nextRecord; 1569 if (reqData->recordID == selCnt) 1570 { 1571 nextRecord = fb_oem::ipmi::sel::lastEntry; 1572 } 1573 else 1574 { 1575 nextRecord = reqData->recordID + 1; 1576 } 1577 1578 if (reqData->readLen == fb_oem::ipmi::sel::entireRecord) 1579 { 1580 return ipmi::responseSuccess(nextRecord, recDataBytes); 1581 } 1582 else 1583 { 1584 if (reqData->offset >= fb_oem::ipmi::sel::selRecordSize || 1585 reqData->readLen > fb_oem::ipmi::sel::selRecordSize) 1586 { 1587 return ipmi::responseUnspecifiedError(); 1588 } 1589 std::vector<uint8_t> recPartData; 1590 1591 auto diff = fb_oem::ipmi::sel::selRecordSize - reqData->offset; 1592 auto readLength = std::min(diff, static_cast<int>(reqData->readLen)); 1593 1594 for (int i = 0; i < readLength; i++) 1595 { 1596 recPartData.push_back(recDataBytes[i + reqData->offset]); 1597 } 1598 return ipmi::responseSuccess(nextRecord, recPartData); 1599 } 1600 } 1601 1602 ipmi::RspType<uint16_t> 1603 ipmiStorageAddSELEntry(ipmi::Context::ptr ctx, std::vector<uint8_t> data) 1604 { 1605 /* Per the IPMI spec, need to cancel any reservation when a 1606 * SEL entry is added 1607 */ 1608 cancelSELReservation(); 1609 1610 if (data.size() != fb_oem::ipmi::sel::selRecordSize) 1611 { 1612 return ipmi::responseReqDataLenInvalid(); 1613 } 1614 1615 std::string ipmiRaw, logErr; 1616 toHexStr(data, ipmiRaw); 1617 1618 /* Parse sel data and get an error log to be filed */ 1619 fb_oem::ipmi::sel::parseSelData((ctx->hostIdx + 1), data, logErr); 1620 1621 static const std::string openBMCMessageRegistryVersion("0.1"); 1622 std::string messageID = 1623 "OpenBMC." + openBMCMessageRegistryVersion + ".SELEntryAdded"; 1624 1625 /* Log the Raw SEL message to the journal */ 1626 std::string journalMsg = "SEL Entry Added: " + ipmiRaw; 1627 1628 phosphor::logging::log<phosphor::logging::level::INFO>( 1629 journalMsg.c_str(), 1630 phosphor::logging::entry("IPMISEL_MESSAGE_ID=%s", messageID.c_str()), 1631 phosphor::logging::entry("IPMISEL_MESSAGE_ARGS=%s", logErr.c_str())); 1632 1633 std::map<std::string, std::string> ad; 1634 std::string severity = "xyz.openbmc_project.Logging.Entry.Level.Critical"; 1635 ad.emplace("IPMI_RAW", ipmiRaw); 1636 1637 auto bus = sdbusplus::bus::new_default(); 1638 auto reqMsg = bus.new_method_call( 1639 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", 1640 "xyz.openbmc_project.Logging.Create", "Create"); 1641 reqMsg.append(logErr, severity, ad); 1642 1643 try 1644 { 1645 bus.call(reqMsg); 1646 } 1647 catch (sdbusplus::exception_t& e) 1648 { 1649 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 1650 } 1651 1652 int responseID = selObj.addEntry(ipmiRaw.c_str()); 1653 if (responseID < 0) 1654 { 1655 return ipmi::responseUnspecifiedError(); 1656 } 1657 return ipmi::responseSuccess((uint16_t)responseID); 1658 } 1659 1660 ipmi::RspType<uint8_t> ipmiStorageClearSEL(uint16_t reservationID, 1661 const std::array<uint8_t, 3>& clr, 1662 uint8_t eraseOperation) 1663 { 1664 if (!checkSELReservation(reservationID)) 1665 { 1666 return ipmi::responseInvalidReservationId(); 1667 } 1668 1669 static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'}; 1670 if (clr != clrExpected) 1671 { 1672 return ipmi::responseInvalidFieldRequest(); 1673 } 1674 1675 /* If there is no sel then return erase complete */ 1676 if (selObj.getCount() == 0) 1677 { 1678 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete); 1679 } 1680 1681 /* Erasure status cannot be fetched, so always return erasure 1682 * status as `erase completed`. 1683 */ 1684 if (eraseOperation == fb_oem::ipmi::sel::getEraseStatus) 1685 { 1686 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete); 1687 } 1688 1689 /* Check that initiate erase is correct */ 1690 if (eraseOperation != fb_oem::ipmi::sel::initiateErase) 1691 { 1692 return ipmi::responseInvalidFieldRequest(); 1693 } 1694 1695 /* Per the IPMI spec, need to cancel any reservation when the 1696 * SEL is cleared 1697 */ 1698 cancelSELReservation(); 1699 1700 /* Clear the complete Sel Json object */ 1701 if (selObj.clear() < 0) 1702 { 1703 return ipmi::responseUnspecifiedError(); 1704 } 1705 1706 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete); 1707 } 1708 1709 ipmi::RspType<uint32_t> ipmiStorageGetSELTime() 1710 { 1711 struct timespec selTime = {}; 1712 1713 if (clock_gettime(CLOCK_REALTIME, &selTime) < 0) 1714 { 1715 return ipmi::responseUnspecifiedError(); 1716 } 1717 1718 return ipmi::responseSuccess(selTime.tv_sec); 1719 } 1720 1721 ipmi::RspType<> ipmiStorageSetSELTime(uint32_t) 1722 { 1723 // Set SEL Time is not supported 1724 return ipmi::responseInvalidCommand(); 1725 } 1726 1727 ipmi::RspType<uint16_t> ipmiStorageGetSELTimeUtcOffset() 1728 { 1729 /* TODO: For now, the SEL time stamp is based on UTC time, 1730 * so return 0x0000 as offset. Might need to change once 1731 * supporting zones in SEL time stamps 1732 */ 1733 1734 uint16_t utcOffset = 0x0000; 1735 return ipmi::responseSuccess(utcOffset); 1736 } 1737 1738 void registerSELFunctions() 1739 { 1740 // <Get SEL Info> 1741 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1742 ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User, 1743 ipmiStorageGetSELInfo); 1744 1745 // <Get SEL Entry> 1746 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1747 ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User, 1748 ipmiStorageGetSELEntry); 1749 1750 // <Add SEL Entry> 1751 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1752 ipmi::storage::cmdAddSelEntry, 1753 ipmi::Privilege::Operator, ipmiStorageAddSELEntry); 1754 1755 // <Clear SEL> 1756 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1757 ipmi::storage::cmdClearSel, ipmi::Privilege::Operator, 1758 ipmiStorageClearSEL); 1759 1760 // <Get SEL Time> 1761 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1762 ipmi::storage::cmdGetSelTime, ipmi::Privilege::User, 1763 ipmiStorageGetSELTime); 1764 1765 // <Set SEL Time> 1766 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1767 ipmi::storage::cmdSetSelTime, 1768 ipmi::Privilege::Operator, ipmiStorageSetSELTime); 1769 1770 // <Get SEL Time UTC Offset> 1771 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1772 ipmi::storage::cmdGetSelTimeUtcOffset, 1773 ipmi::Privilege::User, 1774 ipmiStorageGetSELTimeUtcOffset); 1775 1776 return; 1777 } 1778 1779 } // namespace storage 1780 } // namespace ipmi 1781