1 extern "C" 2 { 3 #include <libpdbg.h> 4 } 5 6 #include "create_pel.hpp" 7 #include "extensions/phal/common_utils.hpp" 8 #include "phal_error.hpp" 9 10 #include <attributes_info.H> 11 #include <fmt/format.h> 12 #include <libekb.H> 13 #include <libphal.H> 14 15 #include <nlohmann/json.hpp> 16 #include <phosphor-logging/elog.hpp> 17 18 #include <algorithm> 19 #include <cstdlib> 20 #include <cstring> 21 #include <iomanip> 22 #include <list> 23 #include <map> 24 #include <sstream> 25 #include <string> 26 27 namespace openpower 28 { 29 namespace phal 30 { 31 using namespace phosphor::logging; 32 33 /** 34 * Used to pass buffer to pdbg callback api to get required target 35 * data (attributes) based on given data (attribute). 36 */ 37 struct TargetInfo 38 { 39 ATTR_PHYS_BIN_PATH_Type physBinPath; 40 ATTR_LOCATION_CODE_Type locationCode; 41 ATTR_PHYS_DEV_PATH_Type physDevPath; 42 ATTR_MRU_ID_Type mruId; 43 44 bool deconfigure; 45 46 TargetInfo() 47 { 48 memset(&physBinPath, '\0', sizeof(physBinPath)); 49 memset(&locationCode, '\0', sizeof(locationCode)); 50 memset(&physDevPath, '\0', sizeof(physDevPath)); 51 mruId = 0; 52 deconfigure = false; 53 } 54 }; 55 56 /** 57 * Used to return in callback function which are used to get 58 * physical path value and it binary format value. 59 * 60 * The value for constexpr defined based on pdbg_target_traverse function usage. 61 */ 62 constexpr int continueTgtTraversal = 0; 63 constexpr int requireAttrFound = 1; 64 constexpr int requireAttrNotFound = 2; 65 66 /** 67 * @brief Used to get target location code from phal device tree 68 * 69 * @param[in] target current device tree target 70 * @param[out] appPrivData used for accessing|storing from|to application 71 * 72 * @return 0 to continue traverse, non-zero to stop traverse 73 */ 74 int pdbgCallbackToGetTgtReqAttrsVal(struct pdbg_target* target, 75 void* appPrivData) 76 { 77 TargetInfo* targetInfo = static_cast<TargetInfo*>(appPrivData); 78 79 ATTR_PHYS_BIN_PATH_Type physBinPath; 80 /** 81 * TODO: Issue: phal/pdata#16 82 * Should not use direct pdbg api to read attribute. Need to use DT_GET_PROP 83 * macro for bmc app's and this will call libdt-api api but, it will print 84 * "pdbg_target_get_attribute failed" trace if attribute is not found and 85 * this callback will call recursively by using pdbg_target_traverse() until 86 * find expected attribute based on return code from this callback. Because, 87 * need to do target iteration to get actual attribute (ATTR_PHYS_BIN_PATH) 88 * value when device tree target info doesn't know to read attribute from 89 * device tree. So, Due to this error trace user will get confusion while 90 * looking traces. Hence using pdbg api to avoid trace until libdt-api 91 * provides log level setup. 92 */ 93 if (!pdbg_target_get_attribute( 94 target, "ATTR_PHYS_BIN_PATH", 95 std::stoi(dtAttr::fapi2::ATTR_PHYS_BIN_PATH_Spec), 96 dtAttr::fapi2::ATTR_PHYS_BIN_PATH_ElementCount, physBinPath)) 97 { 98 return continueTgtTraversal; 99 } 100 101 if (std::memcmp(physBinPath, targetInfo->physBinPath, 102 sizeof(physBinPath)) != 0) 103 { 104 return continueTgtTraversal; 105 } 106 107 if (DT_GET_PROP(ATTR_LOCATION_CODE, target, targetInfo->locationCode)) 108 { 109 log<level::ERR>("Could not read LOCATION_CODE attribute"); 110 return requireAttrNotFound; 111 } 112 113 if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, target, targetInfo->physDevPath)) 114 { 115 log<level::ERR>("Could not read PHYS_DEV_PATH attribute"); 116 return requireAttrNotFound; 117 } 118 119 if (DT_GET_PROP(ATTR_MRU_ID, target, targetInfo->mruId)) 120 { 121 log<level::ERR>("Could not read MRU_ID attribute"); 122 return requireAttrNotFound; 123 } 124 125 if (targetInfo->deconfigure) 126 { 127 ATTR_HWAS_STATE_Type hwasState; 128 if (DT_GET_PROP(ATTR_HWAS_STATE, target, hwasState)) 129 { 130 log<level::ERR>("Could not read HWAS_STATE attribute"); 131 return requireAttrNotFound; 132 } 133 134 log<level::INFO>(fmt::format("Marking target({}) as Non-Functional", 135 targetInfo->physDevPath) 136 .c_str()); 137 hwasState.functional = 0; 138 139 if (DT_SET_PROP(ATTR_HWAS_STATE, target, hwasState)) 140 { 141 log<level::ERR>("Could not write HWAS_STATE attribute"); 142 return requireAttrNotFound; 143 } 144 } 145 146 return requireAttrFound; 147 } 148 149 /** 150 * @brief Used to get target info (attributes data) 151 * 152 * To get target required attributes value using another attribute value 153 * ("PHYS_BIN_PATH" which is present in same target attributes list) by using 154 * "ipdbg_target_traverse" api because, here we have attribute value only and 155 * doesn't have respective device tree target info to get required attributes 156 * values from it attributes list. 157 * 158 * @param[in] physBinPath to pass PHYS_BIN_PATH value 159 * @param[out] targetInfo to pas buufer to fill with required attributes 160 * 161 * @return true on success otherwise false 162 */ 163 bool getTgtReqAttrsVal(const std::vector<uint8_t>& physBinPath, 164 TargetInfo& targetInfo) 165 { 166 std::memcpy(&targetInfo.physBinPath, physBinPath.data(), 167 sizeof(targetInfo.physBinPath)); 168 169 int ret = pdbg_target_traverse(NULL, pdbgCallbackToGetTgtReqAttrsVal, 170 &targetInfo); 171 if (ret == 0) 172 { 173 log<level::ERR>(fmt::format("Given ATTR_PHYS_BIN_PATH value({}) " 174 "not found in phal device tree", 175 targetInfo.physBinPath) 176 .c_str()); 177 return false; 178 } 179 else if (ret == requireAttrNotFound) 180 { 181 return false; 182 } 183 184 return true; 185 } 186 } // namespace phal 187 188 namespace pel 189 { 190 using namespace phosphor::logging; 191 192 namespace detail 193 { 194 using json = nlohmann::json; 195 196 // keys need to be unique so using counter value to generate unique key 197 static int counter = 0; 198 199 // list of debug traces 200 static std::vector<std::pair<std::string, std::string>> traceLog; 201 202 void processLogTraceCallback(void*, const char* fmt, va_list ap) 203 { 204 va_list vap; 205 va_copy(vap, ap); 206 std::vector<char> logData(1 + std::vsnprintf(nullptr, 0, fmt, ap)); 207 std::vsnprintf(logData.data(), logData.size(), fmt, vap); 208 va_end(vap); 209 std::string logstr(logData.begin(), logData.end()); 210 211 log<level::INFO>(logstr.c_str()); 212 213 char timeBuf[80]; 214 time_t t = time(0); 215 tm myTm{}; 216 gmtime_r(&t, &myTm); 217 strftime(timeBuf, 80, "%Y-%m-%d %H:%M:%S", &myTm); 218 219 // key values need to be unique for PEL 220 // TODO #openbmc/dev/issues/1563 221 // If written to Json no need to worry about unique KEY 222 std::stringstream str; 223 str << std::setfill('0'); 224 str << "LOG" << std::setw(3) << counter; 225 str << " " << timeBuf; 226 traceLog.emplace_back(std::make_pair(str.str(), std::move(logstr))); 227 counter++; 228 } 229 230 /** 231 * @brief GET PEL priority from pHAL priority 232 * 233 * The pHAL callout priority is in different format than PEL format 234 * so, this api is used to return current phal supported priority into 235 * PEL expected format. 236 * 237 * @param[in] phalPriority used to pass phal priority format string 238 * 239 * @return pel priority format string else empty if failure 240 * 241 * @note For "NONE" returning "L" (LOW) 242 */ 243 static std::string getPelPriority(const std::string& phalPriority) 244 { 245 const std::map<std::string, std::string> priorityMap = { 246 {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}}; 247 248 auto it = priorityMap.find(phalPriority); 249 if (it == priorityMap.end()) 250 { 251 log<level::ERR>(fmt::format("Unsupported phal priority({}) is given " 252 "to get pel priority format", 253 phalPriority) 254 .c_str()); 255 return "H"; 256 } 257 258 return it->second; 259 } 260 261 void processIplErrorCallback(const ipl_error_info& errInfo) 262 { 263 log<level::INFO>( 264 fmt::format("processIplErrorCallback: Error type({})", errInfo.type) 265 .c_str()); 266 267 if (errInfo.type == IPL_ERR_OK) 268 { 269 // reset trace log and exit 270 reset(); 271 return; 272 } 273 274 if (errInfo.type == IPL_ERR_SBE_BOOT) 275 { 276 processSbeBootError(); 277 return; 278 } 279 280 // TODO: Keeping the existing behaviour now 281 // Handle errors based on special reason codes once support is available 282 processBootError(false); 283 } 284 285 void processBootError(bool status) 286 { 287 log<level::INFO>("processBootError ", entry("STATUS=%d", status)); 288 try 289 { 290 // return If no failure during hwp execution 291 if (status) 292 return; 293 294 // Collecting ffdc details from phal 295 FFDC ffdc; 296 libekb_get_ffdc(ffdc); 297 298 log<level::INFO>( 299 fmt::format("PHAL FFDC: Return Message[{}]", ffdc.message).c_str()); 300 301 // To store callouts details in json format as per pel expectation. 302 json jsonCalloutDataList; 303 jsonCalloutDataList = json::array(); 304 305 // To store phal trace and other additional data about ffdc. 306 FFDCData pelAdditionalData; 307 308 if (ffdc.ffdc_type == FFDC_TYPE_HWP) 309 { 310 // Adding hardware procedures return code details 311 pelAdditionalData.emplace_back("HWP_RC", ffdc.hwp_errorinfo.rc); 312 pelAdditionalData.emplace_back("HWP_RC_DESC", 313 ffdc.hwp_errorinfo.rc_desc); 314 315 // Adding hardware procedures required ffdc data for debug 316 for_each(ffdc.hwp_errorinfo.ffdcs_data.begin(), 317 ffdc.hwp_errorinfo.ffdcs_data.end(), 318 [&pelAdditionalData]( 319 std::pair<std::string, std::string>& ele) -> void { 320 std::string keyWithPrefix("HWP_FFDC_"); 321 keyWithPrefix.append(ele.first); 322 323 pelAdditionalData.emplace_back(keyWithPrefix, 324 ele.second); 325 }); 326 327 // Adding hardware callout details 328 int calloutCount = 0; 329 for_each(ffdc.hwp_errorinfo.hwcallouts.begin(), 330 ffdc.hwp_errorinfo.hwcallouts.end(), 331 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList]( 332 const HWCallout& hwCallout) -> void { 333 calloutCount++; 334 std::stringstream keyPrefix; 335 keyPrefix << "HWP_HW_CO_" << std::setfill('0') 336 << std::setw(2) << calloutCount << "_"; 337 338 pelAdditionalData.emplace_back( 339 std::string(keyPrefix.str()).append("HW_ID"), 340 hwCallout.hwid); 341 342 pelAdditionalData.emplace_back( 343 std::string(keyPrefix.str()).append("PRIORITY"), 344 hwCallout.callout_priority); 345 346 phal::TargetInfo targetInfo; 347 phal::getTgtReqAttrsVal(hwCallout.target_entity_path, 348 targetInfo); 349 350 std::string locationCode = 351 std::string(targetInfo.locationCode); 352 pelAdditionalData.emplace_back( 353 std::string(keyPrefix.str()).append("LOC_CODE"), 354 locationCode); 355 356 std::string physPath = 357 std::string(targetInfo.physDevPath); 358 pelAdditionalData.emplace_back( 359 std::string(keyPrefix.str()).append("PHYS_PATH"), 360 physPath); 361 362 pelAdditionalData.emplace_back( 363 std::string(keyPrefix.str()).append("CLK_POS"), 364 std::to_string(hwCallout.clkPos)); 365 366 json jsonCalloutData; 367 jsonCalloutData["LocationCode"] = locationCode; 368 std::string pelPriority = 369 getPelPriority(hwCallout.callout_priority); 370 jsonCalloutData["Priority"] = pelPriority; 371 372 if (targetInfo.mruId != 0) 373 { 374 jsonCalloutData["MRUs"] = json::array({ 375 {{"ID", targetInfo.mruId}, 376 {"Priority", pelPriority}}, 377 }); 378 } 379 380 jsonCalloutDataList.emplace_back(jsonCalloutData); 381 }); 382 383 // Adding CDG (callout, deconfigure and guard) targets details 384 calloutCount = 0; 385 for_each(ffdc.hwp_errorinfo.cdg_targets.begin(), 386 ffdc.hwp_errorinfo.cdg_targets.end(), 387 [&pelAdditionalData, &calloutCount, 388 &jsonCalloutDataList](const CDG_Target& cdg_tgt) -> void { 389 calloutCount++; 390 std::stringstream keyPrefix; 391 keyPrefix << "HWP_CDG_TGT_" << std::setfill('0') 392 << std::setw(2) << calloutCount << "_"; 393 394 phal::TargetInfo targetInfo; 395 targetInfo.deconfigure = cdg_tgt.deconfigure; 396 397 phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path, 398 targetInfo); 399 400 std::string locationCode = 401 std::string(targetInfo.locationCode); 402 pelAdditionalData.emplace_back( 403 std::string(keyPrefix.str()).append("LOC_CODE"), 404 locationCode); 405 std::string physPath = 406 std::string(targetInfo.physDevPath); 407 pelAdditionalData.emplace_back( 408 std::string(keyPrefix.str()).append("PHYS_PATH"), 409 physPath); 410 411 pelAdditionalData.emplace_back( 412 std::string(keyPrefix.str()).append("CO_REQ"), 413 (cdg_tgt.callout == true ? "true" : "false")); 414 415 pelAdditionalData.emplace_back( 416 std::string(keyPrefix.str()).append("CO_PRIORITY"), 417 cdg_tgt.callout_priority); 418 419 pelAdditionalData.emplace_back( 420 std::string(keyPrefix.str()).append("DECONF_REQ"), 421 (cdg_tgt.deconfigure == true ? "true" : "false")); 422 423 pelAdditionalData.emplace_back( 424 std::string(keyPrefix.str()).append("GUARD_REQ"), 425 (cdg_tgt.guard == true ? "true" : "false")); 426 427 pelAdditionalData.emplace_back( 428 std::string(keyPrefix.str()).append("GUARD_TYPE"), 429 cdg_tgt.guard_type); 430 431 json jsonCalloutData; 432 jsonCalloutData["LocationCode"] = locationCode; 433 std::string pelPriority = 434 getPelPriority(cdg_tgt.callout_priority); 435 jsonCalloutData["Priority"] = pelPriority; 436 437 if (targetInfo.mruId != 0) 438 { 439 jsonCalloutData["MRUs"] = json::array({ 440 {{"ID", targetInfo.mruId}, 441 {"Priority", pelPriority}}, 442 }); 443 } 444 jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure; 445 jsonCalloutData["Guarded"] = cdg_tgt.guard; 446 447 jsonCalloutDataList.emplace_back(jsonCalloutData); 448 }); 449 } 450 else if ((ffdc.ffdc_type != FFDC_TYPE_NONE) && 451 (ffdc.ffdc_type != FFDC_TYPE_UNSUPPORTED)) 452 { 453 log<level::ERR>( 454 fmt::format("Unsupported phal FFDC type to create PEL. " 455 "MSG: {}", 456 ffdc.message) 457 .c_str()); 458 } 459 460 // Adding collected phal logs into PEL additional data 461 for_each(traceLog.begin(), traceLog.end(), 462 [&pelAdditionalData]( 463 std::pair<std::string, std::string>& ele) -> void { 464 pelAdditionalData.emplace_back(ele.first, ele.second); 465 }); 466 467 // TODO: #ibm-openbmc/dev/issues/2595 : Once enabled this support, 468 // callout details is not required to sort in H,M and L orders which 469 // are expected by pel because, pel will take care for sorting callouts 470 // based on priority so, now adding support to send callout in order 471 // i.e High -> Medium -> Low. 472 std::sort( 473 jsonCalloutDataList.begin(), jsonCalloutDataList.end(), 474 [](const json& aEle, const json& bEle) -> bool { 475 // Considering b element having higher priority than a element 476 // or Both element will be same priorty (to keep same order 477 // which are given by phal when two callouts are having same 478 // priority) 479 if (((aEle["Priority"] == "M") && (bEle["Priority"] == "H")) || 480 ((aEle["Priority"] == "L") && 481 ((bEle["Priority"] == "H") || 482 (bEle["Priority"] == "M"))) || 483 (aEle["Priority"] == bEle["Priority"])) 484 { 485 return false; 486 } 487 488 // Considering a element having higher priority than b element 489 return true; 490 }); 491 492 openpower::pel::createBootErrorPEL(pelAdditionalData, 493 jsonCalloutDataList); 494 } 495 catch (const std::exception& ex) 496 { 497 reset(); 498 throw ex; 499 } 500 reset(); 501 } 502 503 void processSbeBootError() 504 { 505 log<level::INFO>("processSbeBootError : Entered "); 506 507 using namespace openpower::phal::sbe; 508 using namespace openpower::phal::exception; 509 510 // To store phal trace and other additional data about ffdc. 511 FFDCData pelAdditionalData; 512 513 // Adding collected phal logs into PEL additional data 514 for_each( 515 traceLog.begin(), traceLog.end(), 516 [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void { 517 pelAdditionalData.emplace_back(ele.first, ele.second); 518 }); 519 520 // reset the trace log and counter 521 reset(); 522 523 // get primary processor to collect FFDC/Dump information. 524 struct pdbg_target* procTarget; 525 pdbg_for_each_class_target("proc", procTarget) 526 { 527 if (openpower::phal::isPrimaryProc(procTarget)) 528 break; 529 procTarget = nullptr; 530 } 531 // check valid primary processor is available 532 if (procTarget == nullptr) 533 { 534 log<level::ERR>("processSbeBootError: fail to get primary processor"); 535 // Initialise the SRC6 with default data, not used in this use case. 536 pelAdditionalData.emplace_back("SRC6", "00000000"); 537 openpower::pel::createPEL( 538 "org.open_power.Processor.Error.SbeBootFailure", pelAdditionalData); 539 return; 540 } 541 // SBE error object. 542 sbeError_t sbeError; 543 bool dumpIsRequired = false; 544 545 try 546 { 547 // Capture FFDC information on primary processor 548 sbeError = captureFFDC(procTarget); 549 } 550 catch (const std::exception& e) 551 { 552 // Fail to collect FFDC information , trigger Dump 553 log<level::ERR>( 554 fmt::format("captureFFDC: Exception{}", e.what()).c_str()); 555 dumpIsRequired = true; 556 } 557 558 if ((sbeError.errType() == SBE_FFDC_NO_DATA) || 559 (sbeError.errType() == SBE_CMD_TIMEOUT) || (dumpIsRequired)) 560 { 561 // Create SBE Dump type error log and trigger Dump 562 openpower::pel::createPEL( 563 "org.open_power.Processor.Error.SbeBootTimeout", pelAdditionalData); 564 // TODO Add dump request 565 return; 566 } 567 // SRC6 : [0:15] chip position 568 uint32_t word6 = pdbg_target_index(procTarget); 569 pelAdditionalData.emplace_back("SRC6", std::to_string(word6 << 16)); 570 // Create SBE Error with FFDC data. 571 createSbeErrorPEL("org.open_power.Processor.Error.SbeBootFailure", sbeError, 572 pelAdditionalData); 573 } 574 575 void reset() 576 { 577 // reset the trace log and counter 578 traceLog.clear(); 579 counter = 0; 580 } 581 582 void pDBGLogTraceCallbackHelper(int, const char* fmt, va_list ap) 583 { 584 processLogTraceCallback(NULL, fmt, ap); 585 } 586 } // namespace detail 587 588 static inline uint8_t getLogLevelFromEnv(const char* env, const uint8_t dValue) 589 { 590 auto logLevel = dValue; 591 try 592 { 593 if (const char* env_p = std::getenv(env)) 594 { 595 logLevel = std::stoi(env_p); 596 } 597 } 598 catch (const std::exception& e) 599 { 600 log<level::ERR>(("Conversion Failure"), entry("ENVIRONMENT=%s", env), 601 entry("EXCEPTION=%s", e.what())); 602 } 603 return logLevel; 604 } 605 606 void addBootErrorCallbacks() 607 { 608 // Get individual phal repos log level from environment variable 609 // and update the log level. 610 pdbg_set_loglevel(getLogLevelFromEnv("PDBG_LOG", PDBG_INFO)); 611 libekb_set_loglevel(getLogLevelFromEnv("LIBEKB_LOG", LIBEKB_LOG_IMP)); 612 ipl_set_loglevel(getLogLevelFromEnv("IPL_LOG", IPL_INFO)); 613 614 // add callback for debug traces 615 pdbg_set_logfunc(detail::pDBGLogTraceCallbackHelper); 616 libekb_set_logfunc(detail::processLogTraceCallback, NULL); 617 ipl_set_logfunc(detail::processLogTraceCallback, NULL); 618 619 // add callback for ipl failures 620 ipl_set_error_callback_func(detail::processIplErrorCallback); 621 } 622 623 } // namespace pel 624 } // namespace openpower 625