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 time_t timeStamp = static_cast<time_t>(data->timeStamp); 1190 std::string timeStr; 1191 std::tm ts; 1192 if (localtime_r(&timeStamp, &ts)) 1193 { 1194 char buf[64]; 1195 if (strftime(buf, sizeof(buf), "%c", &ts)) 1196 { 1197 timeStr = buf; 1198 } 1199 } 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 time_t timeStamp = static_cast<time_t>(data->timeStamp); 1231 std::string timeStr; 1232 std::tm ts; 1233 if (localtime_r(&timeStamp, &ts)) 1234 { 1235 char buf[64]; 1236 if (strftime(buf, sizeof(buf), "%c", &ts)) 1237 { 1238 timeStr = buf; 1239 } 1240 } 1241 1242 errType = oemTSErr; 1243 parseOemSel(data, errLog); 1244 1245 msgLog += errType + " (0x" + recTypeStream.str() + 1246 "), Time: " + timeStr + ", MFG ID: " + mfrIdStr + 1247 ", OEM Data: (" + oemDataStr + ") " + errLog; 1248 } 1249 else if (recType == fbUniErrType) 1250 { 1251 NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]); 1252 errType = fbUniSELErr; 1253 parseOemUnifiedSel(data, errLog); 1254 msgLog += errType + " (0x" + recTypeStream.str() + "), " + errLog; 1255 } 1256 else if ((recType >= oemNTSErrTypeMin) && (recType <= oemNTSErrTypeMax)) 1257 { 1258 /* Non timestamped OEM SEL records */ 1259 NtsOemSELEntry* data = reinterpret_cast<NtsOemSELEntry*>(&reqData[0]); 1260 errType = oemNTSErr; 1261 1262 ptr = data->oemData; 1263 std::vector<uint8_t> oemData(ptr, ptr + 13); 1264 std::string oemDataStr; 1265 toHexStr(oemData, oemDataStr); 1266 1267 parseOemSel((TsOemSELEntry*)data, errLog); 1268 msgLog += errType + " (0x" + recTypeStream.str() + "), OEM Data: (" + 1269 oemDataStr + ") " + errLog; 1270 } 1271 else 1272 { 1273 errType = unknownErr; 1274 toHexStr(reqData, errLog); 1275 msgLog += errType + " (0x" + recTypeStream.str() + 1276 ") RawData: " + errLog; 1277 } 1278 } 1279 1280 } // namespace fb_oem::ipmi::sel 1281 1282 namespace ipmi 1283 { 1284 1285 namespace storage 1286 { 1287 1288 static void registerSELFunctions() __attribute__((constructor)); 1289 static fb_oem::ipmi::sel::SELData selObj __attribute__((init_priority(101))); 1290 1291 ipmi::RspType<uint8_t, // SEL version 1292 uint16_t, // SEL entry count 1293 uint16_t, // free space 1294 uint32_t, // last add timestamp 1295 uint32_t, // last erase timestamp 1296 uint8_t> // operation support 1297 ipmiStorageGetSELInfo() 1298 { 1299 fb_oem::ipmi::sel::GetSELInfoData info; 1300 1301 selObj.getInfo(info); 1302 return ipmi::responseSuccess(info.selVersion, info.entries, info.freeSpace, 1303 info.addTimeStamp, info.eraseTimeStamp, 1304 info.operationSupport); 1305 } 1306 1307 ipmi::RspType<uint16_t, std::vector<uint8_t>> 1308 ipmiStorageGetSELEntry(std::vector<uint8_t> data) 1309 { 1310 if (data.size() != sizeof(fb_oem::ipmi::sel::GetSELEntryRequest)) 1311 { 1312 return ipmi::responseReqDataLenInvalid(); 1313 } 1314 1315 fb_oem::ipmi::sel::GetSELEntryRequest* reqData = 1316 reinterpret_cast<fb_oem::ipmi::sel::GetSELEntryRequest*>(&data[0]); 1317 1318 if (reqData->reservID != 0) 1319 { 1320 if (!checkSELReservation(reqData->reservID)) 1321 { 1322 return ipmi::responseInvalidReservationId(); 1323 } 1324 } 1325 1326 uint16_t selCnt = selObj.getCount(); 1327 if (selCnt == 0) 1328 { 1329 return ipmi::responseSensorInvalid(); 1330 } 1331 1332 /* If it is asked for first entry */ 1333 if (reqData->recordID == fb_oem::ipmi::sel::firstEntry) 1334 { 1335 /* First Entry (0x0000) as per Spec */ 1336 reqData->recordID = 1; 1337 } 1338 else if (reqData->recordID == fb_oem::ipmi::sel::lastEntry) 1339 { 1340 /* Last entry (0xFFFF) as per Spec */ 1341 reqData->recordID = selCnt; 1342 } 1343 1344 std::string ipmiRaw; 1345 1346 if (selObj.getEntry(reqData->recordID, ipmiRaw) < 0) 1347 { 1348 return ipmi::responseSensorInvalid(); 1349 } 1350 1351 std::vector<uint8_t> recDataBytes; 1352 if (fromHexStr(ipmiRaw, recDataBytes) < 0) 1353 { 1354 return ipmi::responseUnspecifiedError(); 1355 } 1356 1357 /* Identify the next SEL record ID. If recordID is same as 1358 * total SeL count then next id should be last entry else 1359 * it should be incremented by 1 to current RecordID 1360 */ 1361 uint16_t nextRecord; 1362 if (reqData->recordID == selCnt) 1363 { 1364 nextRecord = fb_oem::ipmi::sel::lastEntry; 1365 } 1366 else 1367 { 1368 nextRecord = reqData->recordID + 1; 1369 } 1370 1371 if (reqData->readLen == fb_oem::ipmi::sel::entireRecord) 1372 { 1373 return ipmi::responseSuccess(nextRecord, recDataBytes); 1374 } 1375 else 1376 { 1377 if (reqData->offset >= fb_oem::ipmi::sel::selRecordSize || 1378 reqData->readLen > fb_oem::ipmi::sel::selRecordSize) 1379 { 1380 return ipmi::responseUnspecifiedError(); 1381 } 1382 std::vector<uint8_t> recPartData; 1383 1384 auto diff = fb_oem::ipmi::sel::selRecordSize - reqData->offset; 1385 auto readLength = std::min(diff, static_cast<int>(reqData->readLen)); 1386 1387 for (int i = 0; i < readLength; i++) 1388 { 1389 recPartData.push_back(recDataBytes[i + reqData->offset]); 1390 } 1391 return ipmi::responseSuccess(nextRecord, recPartData); 1392 } 1393 } 1394 1395 ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(ipmi::Context::ptr ctx, 1396 std::vector<uint8_t> data) 1397 { 1398 /* Per the IPMI spec, need to cancel any reservation when a 1399 * SEL entry is added 1400 */ 1401 cancelSELReservation(); 1402 1403 if (data.size() != fb_oem::ipmi::sel::selRecordSize) 1404 { 1405 return ipmi::responseReqDataLenInvalid(); 1406 } 1407 1408 std::string ipmiRaw, logErr; 1409 toHexStr(data, ipmiRaw); 1410 1411 /* Parse sel data and get an error log to be filed */ 1412 fb_oem::ipmi::sel::parseSelData((ctx->hostIdx + 1), data, logErr); 1413 1414 static const std::string openBMCMessageRegistryVersion("0.1"); 1415 std::string messageID = "OpenBMC." + openBMCMessageRegistryVersion + 1416 ".SELEntryAdded"; 1417 1418 /* Log the Raw SEL message to the journal */ 1419 std::string journalMsg = "SEL Entry Added: " + ipmiRaw; 1420 1421 phosphor::logging::log<phosphor::logging::level::INFO>( 1422 journalMsg.c_str(), 1423 phosphor::logging::entry("IPMISEL_MESSAGE_ID=%s", messageID.c_str()), 1424 phosphor::logging::entry("IPMISEL_MESSAGE_ARGS=%s", logErr.c_str())); 1425 1426 std::map<std::string, std::string> ad; 1427 std::string severity = "xyz.openbmc_project.Logging.Entry.Level.Critical"; 1428 ad.emplace("IPMI_RAW", ipmiRaw); 1429 1430 auto bus = sdbusplus::bus::new_default(); 1431 auto reqMsg = bus.new_method_call( 1432 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", 1433 "xyz.openbmc_project.Logging.Create", "Create"); 1434 reqMsg.append(logErr, severity, ad); 1435 1436 try 1437 { 1438 bus.call(reqMsg); 1439 } 1440 catch (sdbusplus::exception_t& e) 1441 { 1442 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 1443 } 1444 1445 int responseID = selObj.addEntry(ipmiRaw.c_str()); 1446 if (responseID < 0) 1447 { 1448 return ipmi::responseUnspecifiedError(); 1449 } 1450 return ipmi::responseSuccess((uint16_t)responseID); 1451 } 1452 1453 ipmi::RspType<uint8_t> ipmiStorageClearSEL(uint16_t reservationID, 1454 const std::array<uint8_t, 3>& clr, 1455 uint8_t eraseOperation) 1456 { 1457 if (!checkSELReservation(reservationID)) 1458 { 1459 return ipmi::responseInvalidReservationId(); 1460 } 1461 1462 static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'}; 1463 if (clr != clrExpected) 1464 { 1465 return ipmi::responseInvalidFieldRequest(); 1466 } 1467 1468 /* If there is no sel then return erase complete */ 1469 if (selObj.getCount() == 0) 1470 { 1471 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete); 1472 } 1473 1474 /* Erasure status cannot be fetched, so always return erasure 1475 * status as `erase completed`. 1476 */ 1477 if (eraseOperation == fb_oem::ipmi::sel::getEraseStatus) 1478 { 1479 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete); 1480 } 1481 1482 /* Check that initiate erase is correct */ 1483 if (eraseOperation != fb_oem::ipmi::sel::initiateErase) 1484 { 1485 return ipmi::responseInvalidFieldRequest(); 1486 } 1487 1488 /* Per the IPMI spec, need to cancel any reservation when the 1489 * SEL is cleared 1490 */ 1491 cancelSELReservation(); 1492 1493 /* Clear the complete Sel Json object */ 1494 if (selObj.clear() < 0) 1495 { 1496 return ipmi::responseUnspecifiedError(); 1497 } 1498 1499 return ipmi::responseSuccess(fb_oem::ipmi::sel::eraseComplete); 1500 } 1501 1502 ipmi::RspType<uint32_t> ipmiStorageGetSELTime() 1503 { 1504 struct timespec selTime = {}; 1505 1506 if (clock_gettime(CLOCK_REALTIME, &selTime) < 0) 1507 { 1508 return ipmi::responseUnspecifiedError(); 1509 } 1510 1511 return ipmi::responseSuccess(selTime.tv_sec); 1512 } 1513 1514 ipmi::RspType<> ipmiStorageSetSELTime(uint32_t) 1515 { 1516 // Set SEL Time is not supported 1517 return ipmi::responseInvalidCommand(); 1518 } 1519 1520 ipmi::RspType<uint16_t> ipmiStorageGetSELTimeUtcOffset() 1521 { 1522 /* TODO: For now, the SEL time stamp is based on UTC time, 1523 * so return 0x0000 as offset. Might need to change once 1524 * supporting zones in SEL time stamps 1525 */ 1526 1527 uint16_t utcOffset = 0x0000; 1528 return ipmi::responseSuccess(utcOffset); 1529 } 1530 1531 void registerSELFunctions() 1532 { 1533 // <Get SEL Info> 1534 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1535 ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User, 1536 ipmiStorageGetSELInfo); 1537 1538 // <Get SEL Entry> 1539 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1540 ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User, 1541 ipmiStorageGetSELEntry); 1542 1543 // <Add SEL Entry> 1544 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1545 ipmi::storage::cmdAddSelEntry, 1546 ipmi::Privilege::Operator, ipmiStorageAddSELEntry); 1547 1548 // <Clear SEL> 1549 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1550 ipmi::storage::cmdClearSel, ipmi::Privilege::Operator, 1551 ipmiStorageClearSEL); 1552 1553 // <Get SEL Time> 1554 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1555 ipmi::storage::cmdGetSelTime, ipmi::Privilege::User, 1556 ipmiStorageGetSELTime); 1557 1558 // <Set SEL Time> 1559 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1560 ipmi::storage::cmdSetSelTime, 1561 ipmi::Privilege::Operator, ipmiStorageSetSELTime); 1562 1563 // <Get SEL Time UTC Offset> 1564 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1565 ipmi::storage::cmdGetSelTimeUtcOffset, 1566 ipmi::Privilege::User, 1567 ipmiStorageGetSELTimeUtcOffset); 1568 1569 return; 1570 } 1571 1572 } // namespace storage 1573 } // namespace ipmi 1574