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