1 extern "C" { 2 #include <libpdbg.h> 3 } 4 5 #include "fapi_data_process.hpp" 6 7 #include <attributes_info.H> 8 #include <fmt/format.h> 9 #include <libphal.H> 10 #include <phal_exception.H> 11 12 #include <algorithm> 13 #include <cstdlib> 14 #include <cstring> 15 #include <iomanip> 16 #include <list> 17 #include <map> 18 #include <phosphor-logging/elog.hpp> 19 #include <sstream> 20 #include <string> 21 22 namespace openpower 23 { 24 namespace pels 25 { 26 namespace phal 27 { 28 29 using namespace phosphor::logging; 30 using namespace openpower::phal::exception; 31 32 /** 33 * Used to pass buffer to pdbg callback api to get required target 34 * data (attributes) based on given data (attribute). 35 */ 36 struct TargetInfo 37 { 38 ATTR_PHYS_BIN_PATH_Type physBinPath; 39 ATTR_LOCATION_CODE_Type locationCode; 40 ATTR_PHYS_DEV_PATH_Type physDevPath; 41 ATTR_MRU_ID_Type mruId; 42 43 bool deconfigure; 44 45 TargetInfo() 46 { 47 memset(&physBinPath, '\0', sizeof(physBinPath)); 48 memset(&locationCode, '\0', sizeof(locationCode)); 49 memset(&physDevPath, '\0', sizeof(physDevPath)); 50 mruId = 0; 51 deconfigure = false; 52 } 53 }; 54 55 /** 56 * Used to return in callback function which are used to get 57 * physical path value and it binary format value. 58 * 59 * The value for constexpr defined based on pdbg_target_traverse function usage. 60 */ 61 constexpr int continueTgtTraversal = 0; 62 constexpr int requireAttrFound = 1; 63 constexpr int requireAttrNotFound = 2; 64 65 /** 66 * @brief Used to get target location code from phal device tree 67 * 68 * @param[in] target current device tree target 69 * @param[out] appPrivData used for accessing|storing from|to application 70 * 71 * @return 0 to continue traverse, non-zero to stop traverse 72 */ 73 int pdbgCallbackToGetTgtReqAttrsVal(struct pdbg_target* target, 74 void* appPrivData) 75 { 76 using namespace openpower::phal::pdbg; 77 78 TargetInfo* targetInfo = static_cast<TargetInfo*>(appPrivData); 79 80 ATTR_PHYS_BIN_PATH_Type physBinPath; 81 /** 82 * TODO: Issue: phal/pdata#16 83 * Should not use direct pdbg api to read attribute. Need to use DT_GET_PROP 84 * macro for bmc app's and this will call libdt-api api but, it will print 85 * "pdbg_target_get_attribute failed" trace if attribute is not found and 86 * this callback will call recursively by using pdbg_target_traverse() until 87 * find expected attribute based on return code from this callback. Because, 88 * need to do target iteration to get actual attribute (ATTR_PHYS_BIN_PATH) 89 * value when device tree target info doesn't know to read attribute from 90 * device tree. So, Due to this error trace user will get confusion while 91 * looking traces. Hence using pdbg api to avoid trace until libdt-api 92 * provides log level setup. 93 */ 94 if (!pdbg_target_get_attribute( 95 target, "ATTR_PHYS_BIN_PATH", 96 std::stoi(dtAttr::fapi2::ATTR_PHYS_BIN_PATH_Spec), 97 dtAttr::fapi2::ATTR_PHYS_BIN_PATH_ElementCount, physBinPath)) 98 { 99 return continueTgtTraversal; 100 } 101 102 if (std::memcmp(physBinPath, targetInfo->physBinPath, 103 sizeof(physBinPath)) != 0) 104 { 105 return continueTgtTraversal; 106 } 107 108 // Found Target, now collect the required attributes associated to the 109 // target. Incase of any attribute read failure, initialize the data with 110 // default value. 111 112 try 113 { 114 // Get location code information 115 openpower::phal::pdbg::getLocationCode(target, 116 targetInfo->locationCode); 117 } 118 catch (const std::exception& e) 119 { 120 // log message and continue with default data 121 log<level::ERR>(fmt::format("getLocationCode({}): Exception({})", 122 pdbg_target_path(target), e.what()) 123 .c_str()); 124 } 125 126 if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, target, targetInfo->physDevPath)) 127 { 128 log<level::ERR>( 129 fmt::format("Could not read({}) PHYS_DEV_PATH attribute", 130 pdbg_target_path(target)) 131 .c_str()); 132 } 133 134 if (DT_GET_PROP(ATTR_MRU_ID, target, targetInfo->mruId)) 135 { 136 log<level::ERR>(fmt::format("Could not read({}) ATTR_MRU_ID attribute", 137 pdbg_target_path(target)) 138 .c_str()); 139 } 140 141 return requireAttrFound; 142 } 143 144 /** 145 * @brief Used to get target info (attributes data) 146 * 147 * To get target required attributes value using another attribute value 148 * ("PHYS_BIN_PATH" which is present in same target attributes list) by using 149 * "ipdbg_target_traverse" api because, here we have attribute value only and 150 * doesn't have respective device tree target info to get required attributes 151 * values from it attributes list. 152 * 153 * @param[in] physBinPath to pass PHYS_BIN_PATH value 154 * @param[out] targetInfo to pas buufer to fill with required attributes 155 * 156 * @return true on success otherwise false 157 */ 158 bool getTgtReqAttrsVal(const std::vector<uint8_t>& physBinPath, 159 TargetInfo& targetInfo) 160 { 161 std::memcpy(&targetInfo.physBinPath, physBinPath.data(), 162 sizeof(targetInfo.physBinPath)); 163 164 int ret = pdbg_target_traverse(NULL, pdbgCallbackToGetTgtReqAttrsVal, 165 &targetInfo); 166 if (ret == 0) 167 { 168 log<level::ERR>(fmt::format("Given ATTR_PHYS_BIN_PATH value({}) " 169 "not found in phal device tree", 170 targetInfo.physBinPath) 171 .c_str()); 172 return false; 173 } 174 else if (ret == requireAttrNotFound) 175 { 176 return false; 177 } 178 179 return true; 180 } 181 182 /** 183 * @brief GET PEL priority from pHAL priority 184 * 185 * The pHAL callout priority is in different format than PEL format 186 * so, this api is used to return current phal supported priority into 187 * PEL expected format. 188 * 189 * @param[in] phalPriority used to pass phal priority format string 190 * 191 * @return pel priority format string else empty if failure 192 * 193 * @note For "NONE" returning "L" (LOW) 194 */ 195 static std::string getPelPriority(const std::string& phalPriority) 196 { 197 const std::map<std::string, std::string> priorityMap = { 198 {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}}; 199 200 auto it = priorityMap.find(phalPriority); 201 if (it == priorityMap.end()) 202 { 203 log<level::ERR>(fmt::format("Unsupported phal priority({}) is given " 204 "to get pel priority format", 205 phalPriority) 206 .c_str()); 207 return "H"; 208 } 209 210 return it->second; 211 } 212 213 /** 214 * @brief addPlanarCallout 215 * 216 * This function will add a json for planar callout in the input json list. 217 * The caller can pass this json list into createErrorPEL to apply the callout. 218 * 219 * @param[in,out] jsonCalloutDataList - json list where callout json will be 220 * emplaced 221 * @param[in] priority - string indicating priority. 222 */ 223 static void addPlanarCallout(json& jsonCalloutDataList, 224 const std::string& priority) 225 { 226 json jsonCalloutData; 227 228 // Inventory path for planar 229 jsonCalloutData["InventoryPath"] = 230 "/xyz/openbmc_project/inventory/system/chassis/motherboard"; 231 jsonCalloutData["Deconfigured"] = false; 232 jsonCalloutData["Guarded"] = false; 233 jsonCalloutData["Priority"] = priority; 234 235 jsonCalloutDataList.emplace_back(jsonCalloutData); 236 } 237 238 /** 239 * @brief processClockInfoErrorHelper 240 * 241 * Creates informational PEL for spare clock failure 242 * 243 * @param[in] ffdc FFDC data capturd by the HWP 244 * @param[out] pelJSONFmtCalloutDataList used to store collected callout 245 * data into pel expected format 246 * @param[out] ffdcUserData used to store additional ffdc user data to 247 * provided by the SBE FFDC packet. 248 * 249 * @return NULL 250 * 251 **/ 252 void processClockInfoErrorHelper(const FFDC& ffdc, 253 json& pelJSONFmtCalloutDataList, 254 FFDCData& ffdcUserData) 255 { 256 log<level::INFO>( 257 fmt::format("processClockInfoErrorHelper: FFDC Message[{}]", 258 ffdc.message) 259 .c_str()); 260 261 // Adding hardware procedures return code details 262 ffdcUserData.emplace_back("HWP_RC", ffdc.hwp_errorinfo.rc); 263 ffdcUserData.emplace_back("HWP_RC_DESC", ffdc.hwp_errorinfo.rc_desc); 264 265 // Adding hardware procedures required ffdc data for debug 266 for_each(ffdc.hwp_errorinfo.ffdcs_data.cbegin(), 267 ffdc.hwp_errorinfo.ffdcs_data.cend(), 268 [&ffdcUserData]( 269 const std::pair<std::string, std::string>& ele) -> void { 270 std::string keyWithPrefix("HWP_FFDC_"); 271 keyWithPrefix.append(ele.first); 272 273 ffdcUserData.emplace_back(keyWithPrefix, ele.second); 274 }); 275 // get clock position information 276 auto clk_pos = 0xFF; // Invalid position. 277 for (auto& hwCallout : ffdc.hwp_errorinfo.hwcallouts) 278 { 279 if ((hwCallout.hwid == "PROC_REF_CLOCK") || 280 (hwCallout.hwid == "PCI_REF_CLOCK")) 281 { 282 clk_pos = hwCallout.clkPos; 283 break; 284 } 285 } 286 // Adding CDG (Only deconfigure) targets details 287 for_each(ffdc.hwp_errorinfo.cdg_targets.begin(), 288 ffdc.hwp_errorinfo.cdg_targets.end(), 289 [&ffdcUserData, &pelJSONFmtCalloutDataList, 290 clk_pos](const CDG_Target& cdg_tgt) -> void { 291 json jsonCalloutData; 292 std::string pelPriority = "H"; 293 jsonCalloutData["Priority"] = pelPriority; // Not used 294 jsonCalloutData["SymbolicFRU"] = 295 "REFCLK" + std::to_string(clk_pos); 296 jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure; 297 jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path; 298 pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData); 299 }); 300 } 301 302 void convertFAPItoPELformat(FFDC& ffdc, json& pelJSONFmtCalloutDataList, 303 FFDCData& ffdcUserData) 304 { 305 if (ffdc.ffdc_type == FFDC_TYPE_SPARE_CLOCK_INFO) 306 { 307 processClockInfoErrorHelper(ffdc, pelJSONFmtCalloutDataList, 308 ffdcUserData); 309 return; 310 } 311 312 if (ffdc.ffdc_type == FFDC_TYPE_HWP) 313 { 314 // Adding hardware procedures return code details 315 ffdcUserData.emplace_back("HWP_RC", ffdc.hwp_errorinfo.rc); 316 ffdcUserData.emplace_back("HWP_RC_DESC", ffdc.hwp_errorinfo.rc_desc); 317 318 // Adding hardware procedures required ffdc data for debug 319 for_each( 320 ffdc.hwp_errorinfo.ffdcs_data.begin(), 321 ffdc.hwp_errorinfo.ffdcs_data.end(), 322 [&ffdcUserData](std::pair<std::string, std::string>& ele) -> void { 323 std::string keyWithPrefix("HWP_FFDC_"); 324 keyWithPrefix.append(ele.first); 325 326 ffdcUserData.emplace_back(keyWithPrefix, ele.second); 327 }); 328 329 // Adding hardware callout details 330 int calloutCount = 0; 331 for_each( 332 ffdc.hwp_errorinfo.hwcallouts.begin(), 333 ffdc.hwp_errorinfo.hwcallouts.end(), 334 [&ffdcUserData, &calloutCount, 335 &pelJSONFmtCalloutDataList](const HWCallout& hwCallout) -> void { 336 calloutCount++; 337 std::stringstream keyPrefix; 338 keyPrefix << "HWP_HW_CO_" << std::setfill('0') << std::setw(2) 339 << calloutCount << "_"; 340 341 ffdcUserData.emplace_back( 342 std::string(keyPrefix.str()).append("HW_ID"), 343 hwCallout.hwid); 344 345 ffdcUserData.emplace_back( 346 std::string(keyPrefix.str()).append("PRIORITY"), 347 hwCallout.callout_priority); 348 349 phal::TargetInfo targetInfo; 350 phal::getTgtReqAttrsVal(hwCallout.target_entity_path, 351 targetInfo); 352 353 std::string locationCode = std::string(targetInfo.locationCode); 354 ffdcUserData.emplace_back( 355 std::string(keyPrefix.str()).append("LOC_CODE"), 356 locationCode); 357 358 std::string physPath = std::string(targetInfo.physDevPath); 359 ffdcUserData.emplace_back( 360 std::string(keyPrefix.str()).append("PHYS_PATH"), physPath); 361 362 ffdcUserData.emplace_back( 363 std::string(keyPrefix.str()).append("CLK_POS"), 364 std::to_string(hwCallout.clkPos)); 365 366 ffdcUserData.emplace_back( 367 std::string(keyPrefix.str()).append("CALLOUT_PLANAR"), 368 (hwCallout.isPlanarCallout == true ? "true" : "false")); 369 370 std::string pelPriority = 371 getPelPriority(hwCallout.callout_priority); 372 373 if (hwCallout.isPlanarCallout) 374 { 375 addPlanarCallout(pelJSONFmtCalloutDataList, pelPriority); 376 } 377 }); 378 379 // Adding CDG (callout, deconfigure and guard) targets details 380 calloutCount = 0; 381 for_each( 382 ffdc.hwp_errorinfo.cdg_targets.begin(), 383 ffdc.hwp_errorinfo.cdg_targets.end(), 384 [&ffdcUserData, &calloutCount, 385 &pelJSONFmtCalloutDataList](const CDG_Target& cdg_tgt) -> void { 386 calloutCount++; 387 std::stringstream keyPrefix; 388 keyPrefix << "HWP_CDG_TGT_" << std::setfill('0') << std::setw(2) 389 << calloutCount << "_"; 390 391 phal::TargetInfo targetInfo; 392 targetInfo.deconfigure = cdg_tgt.deconfigure; 393 394 phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path, targetInfo); 395 396 std::string locationCode = std::string(targetInfo.locationCode); 397 ffdcUserData.emplace_back( 398 std::string(keyPrefix.str()).append("LOC_CODE"), 399 locationCode); 400 std::string physPath = std::string(targetInfo.physDevPath); 401 ffdcUserData.emplace_back( 402 std::string(keyPrefix.str()).append("PHYS_PATH"), physPath); 403 404 ffdcUserData.emplace_back( 405 std::string(keyPrefix.str()).append("CO_REQ"), 406 (cdg_tgt.callout == true ? "true" : "false")); 407 408 ffdcUserData.emplace_back( 409 std::string(keyPrefix.str()).append("CO_PRIORITY"), 410 cdg_tgt.callout_priority); 411 412 ffdcUserData.emplace_back( 413 std::string(keyPrefix.str()).append("DECONF_REQ"), 414 (cdg_tgt.deconfigure == true ? "true" : "false")); 415 416 ffdcUserData.emplace_back( 417 std::string(keyPrefix.str()).append("GUARD_REQ"), 418 (cdg_tgt.guard == true ? "true" : "false")); 419 420 ffdcUserData.emplace_back( 421 std::string(keyPrefix.str()).append("GUARD_TYPE"), 422 cdg_tgt.guard_type); 423 424 json jsonCalloutData; 425 jsonCalloutData["LocationCode"] = locationCode; 426 std::string pelPriority = 427 getPelPriority(cdg_tgt.callout_priority); 428 jsonCalloutData["Priority"] = pelPriority; 429 430 if (targetInfo.mruId != 0) 431 { 432 jsonCalloutData["MRUs"] = json::array({ 433 {{"ID", targetInfo.mruId}, {"Priority", pelPriority}}, 434 }); 435 } 436 jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure; 437 jsonCalloutData["Guarded"] = cdg_tgt.guard; 438 jsonCalloutData["GuardType"] = cdg_tgt.guard_type; 439 jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path; 440 441 pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData); 442 }); 443 444 // Adding procedure callout 445 calloutCount = 0; 446 for_each(ffdc.hwp_errorinfo.procedures_callout.begin(), 447 ffdc.hwp_errorinfo.procedures_callout.end(), 448 [&ffdcUserData, &calloutCount, &pelJSONFmtCalloutDataList]( 449 const ProcedureCallout& procCallout) -> void { 450 calloutCount++; 451 std::stringstream keyPrefix; 452 keyPrefix << "HWP_PROC_CO_" << std::setfill('0') 453 << std::setw(2) << calloutCount << "_"; 454 ffdcUserData.emplace_back( 455 std::string(keyPrefix.str()).append("PRIORITY"), 456 procCallout.callout_priority); 457 ffdcUserData.emplace_back( 458 std::string(keyPrefix.str()).append("MAINT_PROCEDURE"), 459 procCallout.proc_callout); 460 json jsonCalloutData; 461 jsonCalloutData["Procedure"] = procCallout.proc_callout; 462 std::string pelPriority = 463 getPelPriority(procCallout.callout_priority); 464 jsonCalloutData["Priority"] = pelPriority; 465 pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData); 466 }); 467 } 468 else if ((ffdc.ffdc_type != FFDC_TYPE_NONE) && 469 (ffdc.ffdc_type != FFDC_TYPE_UNSUPPORTED)) 470 { 471 log<level::ERR>(fmt::format("Unsupported phal FFDC type to create PEL. " 472 "MSG: {}", 473 ffdc.message) 474 .c_str()); 475 } 476 } 477 478 } // namespace phal 479 } // namespace pels 480 } // namespace openpower 481