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