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