1 extern "C" 2 { 3 #include <libpdbg.h> 4 } 5 6 #include "create_pel.hpp" 7 #include "dump_utils.hpp" 8 #include "extensions/phal/common_utils.hpp" 9 #include "phal_error.hpp" 10 #include "util.hpp" 11 12 #include <attributes_info.H> 13 #include <fmt/format.h> 14 #include <libekb.H> 15 #include <libphal.H> 16 17 #include <nlohmann/json.hpp> 18 #include <phosphor-logging/elog.hpp> 19 20 #include <algorithm> 21 #include <cstdlib> 22 #include <cstring> 23 #include <iomanip> 24 #include <list> 25 #include <map> 26 #include <sstream> 27 #include <string> 28 29 namespace openpower 30 { 31 namespace phal 32 { 33 using namespace phosphor::logging; 34 using namespace openpower::phal::exception; 35 using Severity = sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level; 36 37 /** 38 * Used to pass buffer to pdbg callback api to get required target 39 * data (attributes) based on given data (attribute). 40 */ 41 struct TargetInfo 42 { 43 ATTR_PHYS_BIN_PATH_Type physBinPath; 44 ATTR_LOCATION_CODE_Type locationCode; 45 ATTR_PHYS_DEV_PATH_Type physDevPath; 46 ATTR_MRU_ID_Type mruId; 47 48 bool deconfigure; 49 50 TargetInfo() 51 { 52 memset(&physBinPath, '\0', sizeof(physBinPath)); 53 memset(&locationCode, '\0', sizeof(locationCode)); 54 memset(&physDevPath, '\0', sizeof(physDevPath)); 55 mruId = 0; 56 deconfigure = false; 57 } 58 }; 59 60 /** 61 * Used to return in callback function which are used to get 62 * physical path value and it binary format value. 63 * 64 * The value for constexpr defined based on pdbg_target_traverse function usage. 65 */ 66 constexpr int continueTgtTraversal = 0; 67 constexpr int requireAttrFound = 1; 68 constexpr int requireAttrNotFound = 2; 69 70 /** 71 * @brief Used to get target location code from phal device tree 72 * 73 * @param[in] target current device tree target 74 * @param[out] appPrivData used for accessing|storing from|to application 75 * 76 * @return 0 to continue traverse, non-zero to stop traverse 77 */ 78 int pdbgCallbackToGetTgtReqAttrsVal(struct pdbg_target* target, 79 void* appPrivData) 80 { 81 using namespace openpower::phal::pdbg; 82 83 TargetInfo* targetInfo = static_cast<TargetInfo*>(appPrivData); 84 85 ATTR_PHYS_BIN_PATH_Type physBinPath; 86 /** 87 * TODO: Issue: phal/pdata#16 88 * Should not use direct pdbg api to read attribute. Need to use DT_GET_PROP 89 * macro for bmc app's and this will call libdt-api api but, it will print 90 * "pdbg_target_get_attribute failed" trace if attribute is not found and 91 * this callback will call recursively by using pdbg_target_traverse() until 92 * find expected attribute based on return code from this callback. Because, 93 * need to do target iteration to get actual attribute (ATTR_PHYS_BIN_PATH) 94 * value when device tree target info doesn't know to read attribute from 95 * device tree. So, Due to this error trace user will get confusion while 96 * looking traces. Hence using pdbg api to avoid trace until libdt-api 97 * provides log level setup. 98 */ 99 if (!pdbg_target_get_attribute( 100 target, "ATTR_PHYS_BIN_PATH", 101 std::stoi(dtAttr::fapi2::ATTR_PHYS_BIN_PATH_Spec), 102 dtAttr::fapi2::ATTR_PHYS_BIN_PATH_ElementCount, physBinPath)) 103 { 104 return continueTgtTraversal; 105 } 106 107 if (std::memcmp(physBinPath, targetInfo->physBinPath, 108 sizeof(physBinPath)) != 0) 109 { 110 return continueTgtTraversal; 111 } 112 113 // Found Target, now collect the required attributes associated to the 114 // target. Incase of any attribute read failure, initialize the data with 115 // default value. 116 try 117 { 118 // Get location code information 119 openpower::phal::pdbg::getLocationCode(target, 120 targetInfo->locationCode); 121 } 122 catch (const std::exception& e) 123 { 124 log<level::ERR>(fmt::format("getLocationCode({}): Exception({})", 125 pdbg_target_path(target), e.what()) 126 .c_str()); 127 } 128 129 if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, target, targetInfo->physDevPath)) 130 { 131 log<level::ERR>( 132 fmt::format("Could not read({}) PHYS_DEV_PATH attribute", 133 pdbg_target_path(target)) 134 .c_str()); 135 } 136 137 if (DT_GET_PROP(ATTR_MRU_ID, target, targetInfo->mruId)) 138 { 139 log<level::ERR>(fmt::format("Could not read({}) ATTR_MRU_ID attribute", 140 pdbg_target_path(target)) 141 .c_str()); 142 } 143 144 return requireAttrFound; 145 } 146 147 /** 148 * @brief Used to get target info (attributes data) 149 * 150 * To get target required attributes value using another attribute value 151 * ("PHYS_BIN_PATH" which is present in same target attributes list) by using 152 * "ipdbg_target_traverse" api because, here we have attribute value only and 153 * doesn't have respective device tree target info to get required attributes 154 * values from it attributes list. 155 * 156 * @param[in] physBinPath to pass PHYS_BIN_PATH value 157 * @param[out] targetInfo to pas buufer to fill with required attributes 158 * 159 * @return true on success otherwise false 160 */ 161 bool getTgtReqAttrsVal(const std::vector<uint8_t>& physBinPath, 162 TargetInfo& targetInfo) 163 { 164 std::memcpy(&targetInfo.physBinPath, physBinPath.data(), 165 sizeof(targetInfo.physBinPath)); 166 167 int ret = pdbg_target_traverse(NULL, pdbgCallbackToGetTgtReqAttrsVal, 168 &targetInfo); 169 if (ret == 0) 170 { 171 log<level::ERR>(fmt::format("Given ATTR_PHYS_BIN_PATH value({}) " 172 "not found in phal device tree", 173 targetInfo.physBinPath) 174 .c_str()); 175 return false; 176 } 177 else if (ret == requireAttrNotFound) 178 { 179 return false; 180 } 181 182 return true; 183 } 184 } // namespace phal 185 186 namespace pel 187 { 188 using namespace phosphor::logging; 189 190 namespace detail 191 { 192 using json = nlohmann::json; 193 194 // keys need to be unique so using counter value to generate unique key 195 static int counter = 0; 196 197 // list of debug traces 198 static std::vector<std::pair<std::string, std::string>> traceLog; 199 200 /** 201 * @brief Process platform realted boot failure 202 * 203 * @param[in] errInfo - error details 204 */ 205 static void processPlatBootError(const ipl_error_info& errInfo); 206 207 void processLogTraceCallback(void*, const char* fmt, va_list ap) 208 { 209 va_list vap; 210 va_copy(vap, ap); 211 std::vector<char> logData(1 + std::vsnprintf(nullptr, 0, fmt, ap)); 212 std::vsnprintf(logData.data(), logData.size(), fmt, vap); 213 va_end(vap); 214 std::string logstr(logData.begin(), logData.end()); 215 216 log<level::INFO>(logstr.c_str()); 217 218 char timeBuf[80]; 219 time_t t = time(0); 220 tm myTm{}; 221 gmtime_r(&t, &myTm); 222 strftime(timeBuf, 80, "%Y-%m-%d %H:%M:%S", &myTm); 223 224 // key values need to be unique for PEL 225 // TODO #openbmc/dev/issues/1563 226 // If written to Json no need to worry about unique KEY 227 std::stringstream str; 228 str << std::setfill('0'); 229 str << "LOG" << std::setw(3) << counter; 230 str << " " << timeBuf; 231 traceLog.emplace_back(std::make_pair(str.str(), std::move(logstr))); 232 counter++; 233 } 234 235 /** 236 * @brief GET PEL priority from pHAL priority 237 * 238 * The pHAL callout priority is in different format than PEL format 239 * so, this api is used to return current phal supported priority into 240 * PEL expected format. 241 * 242 * @param[in] phalPriority used to pass phal priority format string 243 * 244 * @return pel priority format string else empty if failure 245 * 246 * @note For "NONE" returning "L" (LOW) 247 */ 248 static std::string getPelPriority(const std::string& phalPriority) 249 { 250 const std::map<std::string, std::string> priorityMap = { 251 {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}}; 252 253 auto it = priorityMap.find(phalPriority); 254 if (it == priorityMap.end()) 255 { 256 log<level::ERR>(fmt::format("Unsupported phal priority({}) is given " 257 "to get pel priority format", 258 phalPriority) 259 .c_str()); 260 return "H"; 261 } 262 263 return it->second; 264 } 265 266 /** 267 * @brief Helper function to create PEL for non functional boot 268 * processor related failure. 269 * This function adds the BMC code callout as priority 1 to fix 270 * devtree related software issue. Incase the issue still persist 271 * after reboot recommend to replacing the primary processor. 272 */ 273 void processNonFunctionalBootProc() 274 { 275 json jsonCalloutDataList; 276 json jsonProcedCallout; 277 // Add BMC code callout 278 jsonProcedCallout["Procedure"] = "BMC0001"; 279 jsonProcedCallout["Priority"] = "H"; 280 jsonCalloutDataList.emplace_back(std::move(jsonProcedCallout)); 281 282 // get primary processor 283 struct pdbg_target* procTarget; 284 pdbg_for_each_class_target("proc", procTarget) 285 { 286 if (openpower::phal::isPrimaryProc(procTarget)) 287 break; 288 procTarget = nullptr; 289 } 290 // check valid primary processor is available 291 if (procTarget == nullptr) 292 { 293 log<level::ERR>( 294 "processNonFunctionalBootProc: fail to get primary processor"); 295 } 296 else 297 { 298 try 299 { 300 ATTR_LOCATION_CODE_Type locationCode = {'\0'}; 301 // Get location code information 302 openpower::phal::pdbg::getLocationCode(procTarget, locationCode); 303 json jsonProcCallout; 304 jsonProcCallout["LocationCode"] = locationCode; 305 jsonProcCallout["Deconfigured"] = false; 306 jsonProcCallout["Guarded"] = false; 307 jsonProcCallout["Priority"] = "M"; 308 jsonCalloutDataList.emplace_back(std::move(jsonProcCallout)); 309 } 310 catch (const std::exception& e) 311 { 312 log<level::ERR>(fmt::format("getLocationCode({}): Exception({})", 313 pdbg_target_path(procTarget), e.what()) 314 .c_str()); 315 } 316 } 317 // Adding collected phal logs into PEL additional data 318 FFDCData pelAdditionalData; 319 for_each( 320 traceLog.begin(), traceLog.end(), 321 [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void { 322 pelAdditionalData.emplace_back(ele.first, ele.second); 323 }); 324 openpower::pel::createErrorPEL( 325 "org.open_power.PHAL.Error.NonFunctionalBootProc", jsonCalloutDataList, 326 pelAdditionalData, Severity::Error); 327 // reset trace log and exit 328 reset(); 329 } 330 331 /** 332 * @brief processClockInfoErrorHelper 333 * 334 * Creates informational PEL for spare clock failure 335 * 336 * @param[in] ffdc - FFDC data capturd by the HWP 337 * @param[in] ffdc_prefix - prefix string for logging the data. 338 */ 339 void processClockInfoErrorHelper(FFDC* ffdc, const std::string& ffdc_prefix) 340 { 341 try 342 { 343 log<level::INFO>( 344 fmt::format("processClockInfoErrorHelper: FFDC Message[{}]", 345 ffdc->message) 346 .c_str()); 347 348 // To store callouts details in json format as per pel expectation. 349 json jsonCalloutDataList; 350 jsonCalloutDataList = json::array(); 351 352 // To store phal trace and other additional data about ffdc. 353 FFDCData pelAdditionalData; 354 355 std::string keyWithPrefix(ffdc_prefix + "RC"); 356 // Adding hardware procedures return code details 357 pelAdditionalData.emplace_back(keyWithPrefix, ffdc->hwp_errorinfo.rc); 358 keyWithPrefix = ffdc_prefix + "RC_DESC"; 359 pelAdditionalData.emplace_back(keyWithPrefix, 360 ffdc->hwp_errorinfo.rc_desc); 361 362 // Adding hardware procedures required ffdc data for debug 363 for_each(ffdc->hwp_errorinfo.ffdcs_data.begin(), 364 ffdc->hwp_errorinfo.ffdcs_data.end(), 365 [&pelAdditionalData, &ffdc_prefix]( 366 std::pair<std::string, std::string>& ele) -> void { 367 std::string keyWithPrefix(ffdc_prefix + "FFDC_"); 368 keyWithPrefix.append(ele.first); 369 370 pelAdditionalData.emplace_back(keyWithPrefix, ele.second); 371 }); 372 // get clock position information 373 auto clk_pos = 0xFF; // Invalid position. 374 for (auto& hwCallout : ffdc->hwp_errorinfo.hwcallouts) 375 { 376 if ((hwCallout.hwid == "PROC_REF_CLOCK") || 377 (hwCallout.hwid == "PCI_REF_CLOCK")) 378 { 379 clk_pos = hwCallout.clkPos; 380 break; 381 } 382 } 383 384 // Adding CDG (Only deconfigure) targets details 385 for_each(ffdc->hwp_errorinfo.cdg_targets.begin(), 386 ffdc->hwp_errorinfo.cdg_targets.end(), 387 [&pelAdditionalData, &jsonCalloutDataList, 388 clk_pos](const CDG_Target& cdg_tgt) -> void { 389 json jsonCalloutData; 390 std::string pelPriority = "H"; 391 jsonCalloutData["Priority"] = pelPriority; // Not used 392 jsonCalloutData["SymbolicFRU"] = 393 "REFCLK" + std::to_string(clk_pos); 394 jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure; 395 jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path; 396 jsonCalloutDataList.emplace_back(jsonCalloutData); 397 }); 398 399 // Adding collected phal logs into PEL additional data 400 for_each(traceLog.begin(), traceLog.end(), 401 [&pelAdditionalData]( 402 std::pair<std::string, std::string>& ele) -> void { 403 pelAdditionalData.emplace_back(ele.first, ele.second); 404 }); 405 406 openpower::pel::createErrorPEL("org.open_power.PHAL.Error.SpareClock", 407 jsonCalloutDataList, pelAdditionalData, 408 Severity::Informational); 409 } 410 catch (const std::exception& ex) 411 { 412 reset(); 413 throw ex; 414 } 415 reset(); 416 } 417 418 void processIplErrorCallback(const ipl_error_info& errInfo) 419 { 420 log<level::INFO>( 421 fmt::format("processIplErrorCallback: Error type({})", errInfo.type) 422 .c_str()); 423 424 switch (errInfo.type) 425 { 426 case IPL_ERR_OK: 427 // reset trace log and exit 428 reset(); 429 break; 430 case IPL_ERR_SBE_BOOT: 431 case IPL_ERR_SBE_CHIPOP: 432 // handle SBE related failures. 433 processSbeBootError(); 434 break; 435 case IPL_ERR_HWP: 436 // Handle hwp failure 437 processBootError(false); 438 break; 439 case IPL_ERR_PLAT: 440 processPlatBootError(errInfo); 441 break; 442 case IPL_ERR_PRI_PROC_NON_FUNC: 443 // Handle non functional boot processor error. 444 processNonFunctionalBootProc(); 445 break; 446 default: 447 createPEL("org.open_power.PHAL.Error.Boot"); 448 // reset trace log and exit 449 reset(); 450 break; 451 } 452 } 453 454 /** 455 * @brief addPlanarCallout 456 * 457 * This function will add a json for planar callout in the input json list. 458 * The caller can pass this json list into createErrorPEL to apply the callout. 459 * 460 * @param[in,out] jsonCalloutDataList - json list where callout json will be 461 * emplaced 462 * @param[in] priority - string indicating priority. 463 */ 464 static void addPlanarCallout(json& jsonCalloutDataList, 465 const std::string& priority) 466 { 467 json jsonCalloutData; 468 469 // Inventory path for planar 470 jsonCalloutData["InventoryPath"] = 471 "/xyz/openbmc_project/inventory/system/chassis/motherboard"; 472 jsonCalloutData["Deconfigured"] = false; 473 jsonCalloutData["Guarded"] = false; 474 jsonCalloutData["Priority"] = priority; 475 476 jsonCalloutDataList.emplace_back(jsonCalloutData); 477 } 478 479 /** 480 * @brief processPoweroffError 481 * 482 * Creates informational PEL for the PLAT/HWP error occured during poweroff 483 * 484 * Not adding callouts from FFDC as the hardware errors in the poweroff path 485 * should be non-visible. so that we don't throw out extraneous callouts for 486 * power errors or because the CEC is just not in an expected state. 487 * 488 * @param[in] ffdc - FFDC data capturd by the HWP 489 * @param[in] ffdc_prefix - prefix string for logging the data. 490 */ 491 492 void processPoweroffError(FFDC* ffdc, const std::string& ffdc_prefix) 493 { 494 try 495 { 496 log<level::INFO>( 497 fmt::format("processPoweroffError: Message[{}]", ffdc->message) 498 .c_str()); 499 500 // To store phal trace and other additional data about ffdc. 501 FFDCData pelAdditionalData; 502 503 if (ffdc->ffdc_type == FFDC_TYPE_HWP) 504 { 505 std::string keyWithPrefix(ffdc_prefix + "RC"); 506 // Adding hardware procedures return code details 507 pelAdditionalData.emplace_back(keyWithPrefix, 508 ffdc->hwp_errorinfo.rc); 509 keyWithPrefix = ffdc_prefix + "RC_DESC"; 510 pelAdditionalData.emplace_back(keyWithPrefix, 511 ffdc->hwp_errorinfo.rc_desc); 512 } 513 else if ((ffdc->ffdc_type != FFDC_TYPE_NONE) && 514 (ffdc->ffdc_type != FFDC_TYPE_UNSUPPORTED)) 515 { 516 log<level::ERR>( 517 fmt::format("Unsupported phal FFDC type to create PEL. " 518 "MSG: {}", 519 ffdc->message) 520 .c_str()); 521 } 522 523 // Adding collected phal logs into PEL additional data 524 for_each(traceLog.begin(), traceLog.end(), 525 [&pelAdditionalData]( 526 std::pair<std::string, std::string>& ele) -> void { 527 pelAdditionalData.emplace_back(ele.first, ele.second); 528 }); 529 530 openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot", {}, 531 pelAdditionalData, 532 Severity::Informational); 533 } 534 catch (const std::exception& ex) 535 { 536 reset(); 537 throw ex; 538 } 539 reset(); 540 } 541 542 void processBootErrorHelper(FFDC* ffdc, const std::string& ffdc_prefix) 543 { 544 log<level::INFO>("processBootErrorHelper "); 545 try 546 { 547 log<level::INFO>( 548 fmt::format("PHAL FFDC: Return Message[{}]", ffdc->message) 549 .c_str()); 550 551 // Special handling for spare clock related errors. 552 if (ffdc->ffdc_type == FFDC_TYPE_SPARE_CLOCK_INFO) 553 { 554 processClockInfoErrorHelper(ffdc, ffdc_prefix); 555 return; 556 } 557 // To store callouts details in json format as per pel expectation. 558 json jsonCalloutDataList; 559 jsonCalloutDataList = json::array(); 560 561 // To store phal trace and other additional data about ffdc. 562 FFDCData pelAdditionalData; 563 564 if (ffdc->ffdc_type == FFDC_TYPE_HWP) 565 { 566 std::string keyWithPrefix(ffdc_prefix + "RC"); 567 // Adding hardware procedures return code details 568 pelAdditionalData.emplace_back(keyWithPrefix, 569 ffdc->hwp_errorinfo.rc); 570 keyWithPrefix = ffdc_prefix + "RC_DESC"; 571 pelAdditionalData.emplace_back(keyWithPrefix, 572 ffdc->hwp_errorinfo.rc_desc); 573 574 // Adding hardware procedures required ffdc data for debug 575 for_each(ffdc->hwp_errorinfo.ffdcs_data.begin(), 576 ffdc->hwp_errorinfo.ffdcs_data.end(), 577 [&pelAdditionalData, &ffdc_prefix]( 578 std::pair<std::string, std::string>& ele) -> void { 579 std::string keyWithPrefix(ffdc_prefix + "FFDC_"); 580 keyWithPrefix.append(ele.first); 581 582 pelAdditionalData.emplace_back(keyWithPrefix, 583 ele.second); 584 }); 585 586 // Adding hardware callout details 587 int calloutCount = 0; 588 for_each( 589 ffdc->hwp_errorinfo.hwcallouts.begin(), 590 ffdc->hwp_errorinfo.hwcallouts.end(), 591 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList, 592 &ffdc_prefix](const HWCallout& hwCallout) -> void { 593 calloutCount++; 594 std::stringstream keyPrefix; 595 keyPrefix << ffdc_prefix << "HW_CO_" << std::setfill('0') 596 << std::setw(2) << calloutCount << "_"; 597 598 pelAdditionalData.emplace_back( 599 std::string(keyPrefix.str()).append("HW_ID"), 600 hwCallout.hwid); 601 602 pelAdditionalData.emplace_back( 603 std::string(keyPrefix.str()).append("PRIORITY"), 604 hwCallout.callout_priority); 605 606 // Log target details only if entity path is 607 // available. For example target entity path will not 608 // be available for non-hwp clock failure. 609 if (!hwCallout.target_entity_path.empty()) 610 { 611 phal::TargetInfo targetInfo; 612 phal::getTgtReqAttrsVal(hwCallout.target_entity_path, 613 targetInfo); 614 615 std::string locationCode = 616 std::string(targetInfo.locationCode); 617 pelAdditionalData.emplace_back( 618 std::string(keyPrefix.str()).append("LOC_CODE"), 619 locationCode); 620 621 std::string physPath = 622 std::string(targetInfo.physDevPath); 623 pelAdditionalData.emplace_back( 624 std::string(keyPrefix.str()).append("PHYS_PATH"), 625 physPath); 626 } 627 628 pelAdditionalData.emplace_back( 629 std::string(keyPrefix.str()).append("CLK_POS"), 630 std::to_string(hwCallout.clkPos)); 631 632 pelAdditionalData.emplace_back( 633 std::string(keyPrefix.str()).append("CALLOUT_PLANAR"), 634 (hwCallout.isPlanarCallout == true ? "true" : "false")); 635 636 std::string pelPriority = 637 getPelPriority(hwCallout.callout_priority); 638 639 if (hwCallout.isPlanarCallout) 640 { 641 addPlanarCallout(jsonCalloutDataList, pelPriority); 642 } 643 }); 644 645 // Adding CDG (callout, deconfigure and guard) targets details 646 calloutCount = 0; 647 for_each( 648 ffdc->hwp_errorinfo.cdg_targets.begin(), 649 ffdc->hwp_errorinfo.cdg_targets.end(), 650 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList, 651 &ffdc_prefix](const CDG_Target& cdg_tgt) -> void { 652 calloutCount++; 653 std::stringstream keyPrefix; 654 keyPrefix << ffdc_prefix << "CDG_TGT_" << std::setfill('0') 655 << std::setw(2) << calloutCount << "_"; 656 657 phal::TargetInfo targetInfo; 658 targetInfo.deconfigure = cdg_tgt.deconfigure; 659 660 phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path, 661 targetInfo); 662 663 std::string locationCode = 664 std::string(targetInfo.locationCode); 665 pelAdditionalData.emplace_back( 666 std::string(keyPrefix.str()).append("LOC_CODE"), 667 locationCode); 668 std::string physPath = std::string(targetInfo.physDevPath); 669 pelAdditionalData.emplace_back( 670 std::string(keyPrefix.str()).append("PHYS_PATH"), 671 physPath); 672 673 pelAdditionalData.emplace_back( 674 std::string(keyPrefix.str()).append("CO_REQ"), 675 (cdg_tgt.callout == true ? "true" : "false")); 676 677 pelAdditionalData.emplace_back( 678 std::string(keyPrefix.str()).append("CO_PRIORITY"), 679 cdg_tgt.callout_priority); 680 681 pelAdditionalData.emplace_back( 682 std::string(keyPrefix.str()).append("DECONF_REQ"), 683 (cdg_tgt.deconfigure == true ? "true" : "false")); 684 685 pelAdditionalData.emplace_back( 686 std::string(keyPrefix.str()).append("GUARD_REQ"), 687 (cdg_tgt.guard == true ? "true" : "false")); 688 689 pelAdditionalData.emplace_back( 690 std::string(keyPrefix.str()).append("GUARD_TYPE"), 691 cdg_tgt.guard_type); 692 693 json jsonCalloutData; 694 jsonCalloutData["LocationCode"] = locationCode; 695 std::string pelPriority = 696 getPelPriority(cdg_tgt.callout_priority); 697 jsonCalloutData["Priority"] = pelPriority; 698 699 if (targetInfo.mruId != 0) 700 { 701 jsonCalloutData["MRUs"] = json::array({ 702 {{"ID", targetInfo.mruId}, 703 {"Priority", pelPriority}}, 704 }); 705 } 706 jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure; 707 jsonCalloutData["Guarded"] = cdg_tgt.guard; 708 jsonCalloutData["GuardType"] = cdg_tgt.guard_type; 709 jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path; 710 711 jsonCalloutDataList.emplace_back(jsonCalloutData); 712 }); 713 // Adding procedure callout 714 calloutCount = 0; 715 for_each( 716 ffdc->hwp_errorinfo.procedures_callout.begin(), 717 ffdc->hwp_errorinfo.procedures_callout.end(), 718 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList, 719 &ffdc_prefix](const ProcedureCallout& procCallout) -> void { 720 calloutCount++; 721 std::stringstream keyPrefix; 722 keyPrefix << ffdc_prefix << "PROC_CO_" << std::setfill('0') 723 << std::setw(2) << calloutCount << "_"; 724 725 pelAdditionalData.emplace_back( 726 std::string(keyPrefix.str()).append("PRIORITY"), 727 procCallout.callout_priority); 728 729 pelAdditionalData.emplace_back( 730 std::string(keyPrefix.str()).append("MAINT_PROCEDURE"), 731 procCallout.proc_callout); 732 733 json jsonCalloutData; 734 jsonCalloutData["Procedure"] = procCallout.proc_callout; 735 std::string pelPriority = 736 getPelPriority(procCallout.callout_priority); 737 jsonCalloutData["Priority"] = pelPriority; 738 jsonCalloutDataList.emplace_back(jsonCalloutData); 739 }); 740 } 741 else if ((ffdc->ffdc_type != FFDC_TYPE_NONE) && 742 (ffdc->ffdc_type != FFDC_TYPE_UNSUPPORTED)) 743 { 744 log<level::ERR>( 745 fmt::format("Unsupported phal FFDC type to create PEL. " 746 "MSG: {}", 747 ffdc->message) 748 .c_str()); 749 } 750 751 // Adding collected phal logs into PEL additional data 752 for_each(traceLog.begin(), traceLog.end(), 753 [&pelAdditionalData]( 754 std::pair<std::string, std::string>& ele) -> void { 755 pelAdditionalData.emplace_back(ele.first, ele.second); 756 }); 757 758 // TODO: #ibm-openbmc/dev/issues/2595 : Once enabled this support, 759 // callout details is not required to sort in H,M and L orders which 760 // are expected by pel because, pel will take care for sorting callouts 761 // based on priority so, now adding support to send callout in order 762 // i.e High -> Medium -> Low. 763 std::sort( 764 jsonCalloutDataList.begin(), jsonCalloutDataList.end(), 765 [](const json& aEle, const json& bEle) -> bool { 766 // Considering b element having higher priority than a element 767 // or Both element will be same priorty (to keep same order 768 // which are given by phal when two callouts are having same 769 // priority) 770 if (((aEle["Priority"] == "M") && (bEle["Priority"] == "H")) || 771 ((aEle["Priority"] == "L") && 772 ((bEle["Priority"] == "H") || 773 (bEle["Priority"] == "M"))) || 774 (aEle["Priority"] == bEle["Priority"])) 775 { 776 return false; 777 } 778 779 // Considering a element having higher priority than b element 780 return true; 781 }); 782 openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot", 783 jsonCalloutDataList, pelAdditionalData, 784 Severity::Error); 785 } 786 catch (const std::exception& ex) 787 { 788 reset(); 789 throw ex; 790 } 791 reset(); 792 } 793 794 void processPlatBootError(const ipl_error_info& errInfo) 795 { 796 log<level::INFO>("processPlatBootError "); 797 798 // Collecting ffdc details from phal 799 FFDC* ffdc = reinterpret_cast<FFDC*>(errInfo.private_data); 800 try 801 { 802 if (util::isHostPoweringOff()) 803 { 804 processPoweroffError(ffdc, "PLAT_"); 805 } 806 else 807 { 808 processBootErrorHelper(ffdc, "PLAT_"); 809 } 810 } 811 catch (const std::exception& ex) 812 { 813 log<level::ERR>( 814 fmt::format("processPlatBootError: exception({})", ex.what()) 815 .c_str()); 816 throw ex; 817 } 818 } 819 820 void processBootError(bool status) 821 { 822 log<level::INFO>( 823 fmt::format("processBootError: status({})", status).c_str()); 824 825 try 826 { 827 // return If no failure during hwp execution 828 if (status) 829 return; 830 // Collecting ffdc details from phal 831 FFDC ffdc; 832 libekb_get_ffdc(ffdc); 833 834 if (util::isHostPoweringOff()) 835 { 836 processPoweroffError(&ffdc, "HWP_"); 837 } 838 else 839 { 840 processBootErrorHelper(&ffdc, "HWP_"); 841 } 842 } 843 catch (const std::exception& ex) 844 { 845 log<level::ERR>( 846 fmt::format("processBootError: exception({})", ex.what()).c_str()); 847 throw ex; 848 } 849 } 850 851 void processSbeBootError() 852 { 853 log<level::INFO>("processSbeBootError : Entered "); 854 855 using namespace openpower::phal::sbe; 856 857 // To store phal trace and other additional data about ffdc. 858 FFDCData pelAdditionalData; 859 860 // Adding collected phal logs into PEL additional data 861 for_each( 862 traceLog.begin(), traceLog.end(), 863 [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void { 864 pelAdditionalData.emplace_back(ele.first, ele.second); 865 }); 866 867 // reset the trace log and counter 868 reset(); 869 870 // get primary processor to collect FFDC/Dump information. 871 struct pdbg_target* procTarget; 872 pdbg_for_each_class_target("proc", procTarget) 873 { 874 if (openpower::phal::isPrimaryProc(procTarget)) 875 break; 876 procTarget = nullptr; 877 } 878 // check valid primary processor is available 879 if (procTarget == nullptr) 880 { 881 log<level::ERR>("processSbeBootError: fail to get primary processor"); 882 // Add BMC code callout and create PEL 883 json jsonCalloutDataList; 884 jsonCalloutDataList = json::array(); 885 json jsonCalloutData; 886 jsonCalloutData["Procedure"] = "BMC0001"; 887 jsonCalloutData["Priority"] = "H"; 888 jsonCalloutDataList.emplace_back(jsonCalloutData); 889 openpower::pel::createErrorPEL( 890 "org.open_power.Processor.Error.SbeBootFailure", 891 jsonCalloutDataList, {}, Severity::Error); 892 return; 893 } 894 // SBE error object. 895 sbeError_t sbeError; 896 bool dumpIsRequired = false; 897 898 try 899 { 900 // Capture FFDC information on primary processor 901 sbeError = captureFFDC(procTarget); 902 } 903 catch (const phalError_t& phalError) 904 { 905 // Fail to collect FFDC information , trigger Dump 906 log<level::ERR>( 907 fmt::format("captureFFDC: Exception({})", phalError.what()) 908 .c_str()); 909 dumpIsRequired = true; 910 } 911 912 std::string event; 913 914 if ((sbeError.errType() == SBE_FFDC_NO_DATA) || 915 (sbeError.errType() == SBE_CMD_TIMEOUT) || (dumpIsRequired)) 916 { 917 event = "org.open_power.Processor.Error.SbeBootTimeout"; 918 dumpIsRequired = true; 919 } 920 else 921 { 922 event = "org.open_power.Processor.Error.SbeBootFailure"; 923 } 924 // SRC6 : [0:15] chip position 925 uint32_t index = pdbg_target_index(procTarget); 926 pelAdditionalData.emplace_back("SRC6", std::to_string(index << 16)); 927 // Create SBE Error with FFDC data. 928 auto logId = 929 createSbeErrorPEL(event, sbeError, pelAdditionalData, procTarget); 930 931 if (dumpIsRequired) 932 { 933 using namespace openpower::phal::dump; 934 DumpParameters dumpParameters = {logId, index, SBE_DUMP_TIMEOUT, 935 DumpType::SBE}; 936 try 937 { 938 requestDump(dumpParameters); 939 } 940 catch (const std::runtime_error& e) 941 { 942 // Allowing call back to handle the error gracefully. 943 log<level::ERR>("Dump collection failed"); 944 // TODO revist error handling. 945 } 946 } 947 } 948 949 void reset() 950 { 951 // reset the trace log and counter 952 traceLog.clear(); 953 counter = 0; 954 } 955 956 void pDBGLogTraceCallbackHelper(int, const char* fmt, va_list ap) 957 { 958 processLogTraceCallback(NULL, fmt, ap); 959 } 960 } // namespace detail 961 962 static inline uint8_t getLogLevelFromEnv(const char* env, const uint8_t dValue) 963 { 964 auto logLevel = dValue; 965 try 966 { 967 if (const char* env_p = std::getenv(env)) 968 { 969 logLevel = std::stoi(env_p); 970 } 971 } 972 catch (const std::exception& e) 973 { 974 log<level::ERR>(("Conversion Failure"), entry("ENVIRONMENT=%s", env), 975 entry("EXCEPTION=%s", e.what())); 976 } 977 return logLevel; 978 } 979 980 void addBootErrorCallbacks() 981 { 982 // Get individual phal repos log level from environment variable 983 // and update the log level. 984 pdbg_set_loglevel(getLogLevelFromEnv("PDBG_LOG", PDBG_INFO)); 985 libekb_set_loglevel(getLogLevelFromEnv("LIBEKB_LOG", LIBEKB_LOG_IMP)); 986 ipl_set_loglevel(getLogLevelFromEnv("IPL_LOG", IPL_INFO)); 987 988 // add callback for debug traces 989 pdbg_set_logfunc(detail::pDBGLogTraceCallbackHelper); 990 libekb_set_logfunc(detail::processLogTraceCallback, NULL); 991 ipl_set_logfunc(detail::processLogTraceCallback, NULL); 992 993 // add callback for ipl failures 994 ipl_set_error_callback_func(detail::processIplErrorCallback); 995 } 996 997 } // namespace pel 998 } // namespace openpower 999