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