| extern "C" | 
 | { | 
 | #include <libpdbg.h> | 
 | } | 
 |  | 
 | #include "create_pel.hpp" | 
 | #include "dump_utils.hpp" | 
 | #include "extensions/phal/common_utils.hpp" | 
 | #include "phal_error.hpp" | 
 | #include "util.hpp" | 
 |  | 
 | #include <attributes_info.H> | 
 | #include <libekb.H> | 
 | #include <libphal.H> | 
 |  | 
 | #include <nlohmann/json.hpp> | 
 | #include <phosphor-logging/elog.hpp> | 
 |  | 
 | #include <algorithm> | 
 | #include <cstdlib> | 
 | #include <cstring> | 
 | #include <format> | 
 | #include <iomanip> | 
 | #include <list> | 
 | #include <map> | 
 | #include <sstream> | 
 | #include <string> | 
 |  | 
 | namespace openpower | 
 | { | 
 | namespace phal | 
 | { | 
 | using namespace phosphor::logging; | 
 | using namespace openpower::phal::exception; | 
 | using Severity = sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level; | 
 |  | 
 | /** | 
 |  * Used to pass buffer to pdbg callback api to get required target | 
 |  * data (attributes) based on given data (attribute). | 
 |  */ | 
 | struct TargetInfo | 
 | { | 
 |     ATTR_PHYS_BIN_PATH_Type physBinPath; | 
 |     ATTR_LOCATION_CODE_Type locationCode; | 
 |     ATTR_PHYS_DEV_PATH_Type physDevPath; | 
 |     ATTR_MRU_ID_Type mruId; | 
 |  | 
 |     bool deconfigure; | 
 |  | 
 |     TargetInfo() | 
 |     { | 
 |         memset(&physBinPath, '\0', sizeof(physBinPath)); | 
 |         memset(&locationCode, '\0', sizeof(locationCode)); | 
 |         memset(&physDevPath, '\0', sizeof(physDevPath)); | 
 |         mruId = 0; | 
 |         deconfigure = false; | 
 |     } | 
 | }; | 
 |  | 
 | /** | 
 |  * Used to return in callback function which are used to get | 
 |  * physical path value and it binary format value. | 
 |  * | 
 |  * The value for constexpr defined based on pdbg_target_traverse function usage. | 
 |  */ | 
 | constexpr int continueTgtTraversal = 0; | 
 | constexpr int requireAttrFound = 1; | 
 | constexpr int requireAttrNotFound = 2; | 
 |  | 
 | /** | 
 |  * @brief Used to get target location code from phal device tree | 
 |  * | 
 |  * @param[in] target current device tree target | 
 |  * @param[out] appPrivData used for accessing|storing from|to application | 
 |  * | 
 |  * @return 0 to continue traverse, non-zero to stop traverse | 
 |  */ | 
 | int pdbgCallbackToGetTgtReqAttrsVal(struct pdbg_target* target, | 
 |                                     void* appPrivData) | 
 | { | 
 |     using namespace openpower::phal::pdbg; | 
 |  | 
 |     TargetInfo* targetInfo = static_cast<TargetInfo*>(appPrivData); | 
 |  | 
 |     ATTR_PHYS_BIN_PATH_Type physBinPath; | 
 |     /** | 
 |      * TODO: Issue: phal/pdata#16 | 
 |      * Should not use direct pdbg api to read attribute. Need to use DT_GET_PROP | 
 |      * macro for bmc app's and this will call libdt-api api but, it will print | 
 |      * "pdbg_target_get_attribute failed" trace if attribute is not found and | 
 |      * this callback will call recursively by using pdbg_target_traverse() until | 
 |      * find expected attribute based on return code from this callback. Because, | 
 |      * need to do target iteration to get actual attribute (ATTR_PHYS_BIN_PATH) | 
 |      * value when device tree target info doesn't know to read attribute from | 
 |      * device tree. So, Due to this error trace user will get confusion while | 
 |      * looking traces. Hence using pdbg api to avoid trace until libdt-api | 
 |      * provides log level setup. | 
 |      */ | 
 |     if (!pdbg_target_get_attribute( | 
 |             target, "ATTR_PHYS_BIN_PATH", | 
 |             std::stoi(dtAttr::fapi2::ATTR_PHYS_BIN_PATH_Spec), | 
 |             dtAttr::fapi2::ATTR_PHYS_BIN_PATH_ElementCount, physBinPath)) | 
 |     { | 
 |         return continueTgtTraversal; | 
 |     } | 
 |  | 
 |     if (std::memcmp(physBinPath, targetInfo->physBinPath, | 
 |                     sizeof(physBinPath)) != 0) | 
 |     { | 
 |         return continueTgtTraversal; | 
 |     } | 
 |  | 
 |     // Found Target, now collect the required attributes associated to the | 
 |     // target. Incase of any attribute read failure, initialize the data with | 
 |     // default value. | 
 |     try | 
 |     { | 
 |         // Get location code information | 
 |         openpower::phal::pdbg::getLocationCode(target, | 
 |                                                targetInfo->locationCode); | 
 |     } | 
 |     catch (const std::exception& e) | 
 |     { | 
 |         log<level::ERR>(std::format("getLocationCode({}): Exception({})", | 
 |                                     pdbg_target_path(target), e.what()) | 
 |                             .c_str()); | 
 |     } | 
 |  | 
 |     if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, target, targetInfo->physDevPath)) | 
 |     { | 
 |         log<level::ERR>( | 
 |             std::format("Could not read({}) PHYS_DEV_PATH attribute", | 
 |                         pdbg_target_path(target)) | 
 |                 .c_str()); | 
 |     } | 
 |  | 
 |     if (DT_GET_PROP(ATTR_MRU_ID, target, targetInfo->mruId)) | 
 |     { | 
 |         log<level::ERR>(std::format("Could not read({}) ATTR_MRU_ID attribute", | 
 |                                     pdbg_target_path(target)) | 
 |                             .c_str()); | 
 |     } | 
 |  | 
 |     return requireAttrFound; | 
 | } | 
 |  | 
 | /** | 
 |  * @brief Used to get target info (attributes data) | 
 |  * | 
 |  * To get target required attributes value using another attribute value | 
 |  * ("PHYS_BIN_PATH" which is present in same target attributes list) by using | 
 |  * "ipdbg_target_traverse" api because, here we have attribute value only and | 
 |  * doesn't have respective device tree target info to get required attributes | 
 |  * values from it attributes list. | 
 |  * | 
 |  * @param[in] physBinPath to pass PHYS_BIN_PATH value | 
 |  * @param[out] targetInfo to pas buufer to fill with required attributes | 
 |  * | 
 |  * @return true on success otherwise false | 
 |  */ | 
 | bool getTgtReqAttrsVal(const std::vector<uint8_t>& physBinPath, | 
 |                        TargetInfo& targetInfo) | 
 | { | 
 |     std::memcpy(&targetInfo.physBinPath, physBinPath.data(), | 
 |                 sizeof(targetInfo.physBinPath)); | 
 |  | 
 |     int ret = pdbg_target_traverse(NULL, pdbgCallbackToGetTgtReqAttrsVal, | 
 |                                    &targetInfo); | 
 |     if (ret == 0) | 
 |     { | 
 |         std::string fmt; | 
 |         for (auto value : targetInfo.physBinPath) | 
 |         { | 
 |             fmt += std::format("{:02X} ", value); | 
 |         } | 
 |  | 
 |         log<level::ERR>(std::format("Given ATTR_PHYS_BIN_PATH value {} " | 
 |                                     "not found in phal device tree", | 
 |                                     fmt) | 
 |                             .c_str()); | 
 |         return false; | 
 |     } | 
 |     else if (ret == requireAttrNotFound) | 
 |     { | 
 |         return false; | 
 |     } | 
 |  | 
 |     return true; | 
 | } | 
 | } // namespace phal | 
 |  | 
 | namespace pel | 
 | { | 
 | using namespace phosphor::logging; | 
 |  | 
 | namespace detail | 
 | { | 
 | using json = nlohmann::json; | 
 |  | 
 | // keys need to be unique so using counter value to generate unique key | 
 | static int counter = 0; | 
 |  | 
 | // list of debug traces | 
 | static std::vector<std::pair<std::string, std::string>> traceLog; | 
 |  | 
 | /** | 
 |  * @brief Process platform realted boot failure | 
 |  * | 
 |  * @param[in] errInfo - error details | 
 |  */ | 
 | static void processPlatBootError(const ipl_error_info& errInfo); | 
 |  | 
 | void processLogTraceCallback(void*, const char* fmt, va_list ap) | 
 | { | 
 |     va_list vap; | 
 |     va_copy(vap, ap); | 
 |     std::vector<char> logData(1 + std::vsnprintf(nullptr, 0, fmt, ap)); | 
 |     std::vsnprintf(logData.data(), logData.size(), fmt, vap); | 
 |     va_end(vap); | 
 |     std::string logstr(logData.begin(), logData.end()); | 
 |  | 
 |     log<level::INFO>(logstr.c_str()); | 
 |  | 
 |     char timeBuf[80]; | 
 |     time_t t = time(0); | 
 |     tm myTm{}; | 
 |     gmtime_r(&t, &myTm); | 
 |     strftime(timeBuf, 80, "%Y-%m-%d %H:%M:%S", &myTm); | 
 |  | 
 |     // key values need to be unique for PEL | 
 |     // TODO #openbmc/dev/issues/1563 | 
 |     // If written to Json no need to worry about unique KEY | 
 |     std::stringstream str; | 
 |     str << std::setfill('0'); | 
 |     str << "LOG" << std::setw(3) << counter; | 
 |     str << " " << timeBuf; | 
 |     traceLog.emplace_back(std::make_pair(str.str(), std::move(logstr))); | 
 |     counter++; | 
 | } | 
 |  | 
 | /** | 
 |  * @brief GET PEL priority from pHAL priority | 
 |  * | 
 |  * The pHAL callout priority is in different format than PEL format | 
 |  * so, this api is used to return current phal supported priority into | 
 |  * PEL expected format. | 
 |  * | 
 |  * @param[in] phalPriority used to pass phal priority format string | 
 |  * | 
 |  * @return pel priority format string else empty if failure | 
 |  * | 
 |  * @note For "NONE" returning "L" (LOW) | 
 |  */ | 
 | static std::string getPelPriority(const std::string& phalPriority) | 
 | { | 
 |     const std::map<std::string, std::string> priorityMap = { | 
 |         {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}}; | 
 |  | 
 |     auto it = priorityMap.find(phalPriority); | 
 |     if (it == priorityMap.end()) | 
 |     { | 
 |         log<level::ERR>(std::format("Unsupported phal priority({}) is given " | 
 |                                     "to get pel priority format", | 
 |                                     phalPriority) | 
 |                             .c_str()); | 
 |         return "H"; | 
 |     } | 
 |  | 
 |     return it->second; | 
 | } | 
 |  | 
 | /** | 
 |  * @brief Helper function to create PEL for non functional boot | 
 |  *        processor related failure. | 
 |  * This function adds the BMC code callout as priority 1 to fix | 
 |  * devtree related software issue. Incase the issue still persist | 
 |  * after reboot recommend to replacing the primary processor. | 
 |  */ | 
 | void processNonFunctionalBootProc() | 
 | { | 
 |     json jsonCalloutDataList; | 
 |     json jsonProcedCallout; | 
 |     // Add BMC code callout | 
 |     jsonProcedCallout["Procedure"] = "BMC0001"; | 
 |     jsonProcedCallout["Priority"] = "H"; | 
 |     jsonCalloutDataList.emplace_back(std::move(jsonProcedCallout)); | 
 |  | 
 |     // get primary processor | 
 |     struct pdbg_target* procTarget; | 
 |     pdbg_for_each_class_target("proc", procTarget) | 
 |     { | 
 |         if (openpower::phal::isPrimaryProc(procTarget)) | 
 |             break; | 
 |         procTarget = nullptr; | 
 |     } | 
 |     // check valid primary processor is available | 
 |     if (procTarget == nullptr) | 
 |     { | 
 |         log<level::ERR>( | 
 |             "processNonFunctionalBootProc: fail to get primary processor"); | 
 |     } | 
 |     else | 
 |     { | 
 |         try | 
 |         { | 
 |             ATTR_LOCATION_CODE_Type locationCode = {'\0'}; | 
 |             // Get location code information | 
 |             openpower::phal::pdbg::getLocationCode(procTarget, locationCode); | 
 |             json jsonProcCallout; | 
 |             jsonProcCallout["LocationCode"] = locationCode; | 
 |             jsonProcCallout["Deconfigured"] = false; | 
 |             jsonProcCallout["Guarded"] = false; | 
 |             jsonProcCallout["Priority"] = "M"; | 
 |             jsonCalloutDataList.emplace_back(std::move(jsonProcCallout)); | 
 |         } | 
 |         catch (const std::exception& e) | 
 |         { | 
 |             log<level::ERR>(std::format("getLocationCode({}): Exception({})", | 
 |                                         pdbg_target_path(procTarget), e.what()) | 
 |                                 .c_str()); | 
 |         } | 
 |     } | 
 |     // Adding collected phal logs into PEL additional data | 
 |     FFDCData pelAdditionalData; | 
 |     for_each( | 
 |         traceLog.begin(), traceLog.end(), | 
 |         [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void { | 
 |             pelAdditionalData.emplace_back(ele.first, ele.second); | 
 |         }); | 
 |     openpower::pel::createErrorPEL( | 
 |         "org.open_power.PHAL.Error.NonFunctionalBootProc", jsonCalloutDataList, | 
 |         pelAdditionalData, Severity::Error); | 
 |     // reset trace log and exit | 
 |     reset(); | 
 | } | 
 |  | 
 | /** | 
 |  * @brief processClockInfoErrorHelper | 
 |  * | 
 |  * Creates informational PEL for spare clock failure | 
 |  * | 
 |  * @param[in] ffdc - FFDC data capturd by the HWP | 
 |  * @param[in] ffdc_prefix - prefix string for logging the data. | 
 |  */ | 
 | void processClockInfoErrorHelper(FFDC* ffdc, const std::string& ffdc_prefix) | 
 | { | 
 |     try | 
 |     { | 
 |         log<level::INFO>( | 
 |             std::format("processClockInfoErrorHelper: FFDC Message[{}]", | 
 |                         ffdc->message) | 
 |                 .c_str()); | 
 |  | 
 |         // To store callouts details in json format as per pel expectation. | 
 |         json jsonCalloutDataList; | 
 |         jsonCalloutDataList = json::array(); | 
 |  | 
 |         // To store phal trace and other additional data about ffdc. | 
 |         FFDCData pelAdditionalData; | 
 |  | 
 |         std::string keyWithPrefix(ffdc_prefix + "RC"); | 
 |         // Adding hardware procedures return code details | 
 |         pelAdditionalData.emplace_back(keyWithPrefix, ffdc->hwp_errorinfo.rc); | 
 |         keyWithPrefix = ffdc_prefix + "RC_DESC"; | 
 |         pelAdditionalData.emplace_back(keyWithPrefix, | 
 |                                        ffdc->hwp_errorinfo.rc_desc); | 
 |  | 
 |         // Adding hardware procedures required ffdc data for debug | 
 |         for_each(ffdc->hwp_errorinfo.ffdcs_data.begin(), | 
 |                  ffdc->hwp_errorinfo.ffdcs_data.end(), | 
 |                  [&pelAdditionalData, &ffdc_prefix]( | 
 |                      std::pair<std::string, std::string>& ele) -> void { | 
 |                      std::string keyWithPrefix(ffdc_prefix + "FFDC_"); | 
 |                      keyWithPrefix.append(ele.first); | 
 |  | 
 |                      pelAdditionalData.emplace_back(keyWithPrefix, ele.second); | 
 |                  }); | 
 |         // get clock position information | 
 |         auto clk_pos = 0xFF; // Invalid position. | 
 |         for (auto& hwCallout : ffdc->hwp_errorinfo.hwcallouts) | 
 |         { | 
 |             if ((hwCallout.hwid == "PROC_REF_CLOCK") || | 
 |                 (hwCallout.hwid == "PCI_REF_CLOCK")) | 
 |             { | 
 |                 clk_pos = hwCallout.clkPos; | 
 |                 break; | 
 |             } | 
 |         } | 
 |  | 
 |         // Adding CDG (Only deconfigure) targets details | 
 |         for_each(ffdc->hwp_errorinfo.cdg_targets.begin(), | 
 |                  ffdc->hwp_errorinfo.cdg_targets.end(), | 
 |                  [&pelAdditionalData, &jsonCalloutDataList, | 
 |                   clk_pos](const CDG_Target& cdg_tgt) -> void { | 
 |                      json jsonCalloutData; | 
 |                      std::string pelPriority = "L"; | 
 |                      jsonCalloutData["Priority"] = pelPriority; // Not used | 
 |                      jsonCalloutData["SymbolicFRU"] = "REFCLK" + | 
 |                                                       std::to_string(clk_pos); | 
 |                      jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure; | 
 |                      jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path; | 
 |                      jsonCalloutDataList.emplace_back(jsonCalloutData); | 
 |                  }); | 
 |  | 
 |         // Adding collected phal logs into PEL additional data | 
 |         for_each(traceLog.begin(), traceLog.end(), | 
 |                  [&pelAdditionalData]( | 
 |                      std::pair<std::string, std::string>& ele) -> void { | 
 |                      pelAdditionalData.emplace_back(ele.first, ele.second); | 
 |                  }); | 
 |  | 
 |         openpower::pel::createErrorPEL("org.open_power.PHAL.Error.SpareClock", | 
 |                                        jsonCalloutDataList, pelAdditionalData, | 
 |                                        Severity::Informational); | 
 |     } | 
 |     catch (const std::exception& ex) | 
 |     { | 
 |         reset(); | 
 |         throw ex; | 
 |     } | 
 |     reset(); | 
 | } | 
 |  | 
 | void processIplErrorCallback(const ipl_error_info& errInfo) | 
 | { | 
 |     log<level::INFO>( | 
 |         std::format("processIplErrorCallback: Error type({})", | 
 |                     static_cast<std::underlying_type<ipl_error_type>::type>( | 
 |                         errInfo.type)) | 
 |             .c_str()); | 
 |  | 
 |     switch (errInfo.type) | 
 |     { | 
 |         case IPL_ERR_OK: | 
 |             // reset trace log and exit | 
 |             reset(); | 
 |             break; | 
 |         case IPL_ERR_SBE_BOOT: | 
 |         case IPL_ERR_SBE_CHIPOP: | 
 |             // handle SBE related failures. | 
 |             processSbeBootError(); | 
 |             break; | 
 |         case IPL_ERR_HWP: | 
 |             // Handle hwp failure | 
 |             processBootError(false); | 
 |             break; | 
 |         case IPL_ERR_PLAT: | 
 |             processPlatBootError(errInfo); | 
 |             break; | 
 |         case IPL_ERR_PRI_PROC_NON_FUNC: | 
 |             // Handle non functional boot processor error. | 
 |             processNonFunctionalBootProc(); | 
 |             break; | 
 |         case IPL_ERR_GUARD_PARTITION_ACCESS: | 
 |             processGuardPartitionAccessError(); | 
 |             break; | 
 |         default: | 
 |             createPEL("org.open_power.PHAL.Error.Boot"); | 
 |             // reset trace log and exit | 
 |             reset(); | 
 |             break; | 
 |     } | 
 | } | 
 |  | 
 | /** | 
 |  * @brief addPlanarCallout | 
 |  * | 
 |  * This function will add a json for planar callout in the input json list. | 
 |  * The caller can pass this json list into createErrorPEL to apply the callout. | 
 |  * | 
 |  * @param[in,out] jsonCalloutDataList - json list where callout json will be | 
 |  *                  emplaced | 
 |  * @param[in] priority - string indicating priority. | 
 |  */ | 
 | static void addPlanarCallout(json& jsonCalloutDataList, | 
 |                              const std::string& priority) | 
 | { | 
 |     json jsonCalloutData; | 
 |  | 
 |     // Inventory path for planar | 
 |     jsonCalloutData["InventoryPath"] = | 
 |         "/xyz/openbmc_project/inventory/system/chassis/motherboard"; | 
 |     jsonCalloutData["Deconfigured"] = false; | 
 |     jsonCalloutData["Guarded"] = false; | 
 |     jsonCalloutData["Priority"] = priority; | 
 |  | 
 |     jsonCalloutDataList.emplace_back(jsonCalloutData); | 
 | } | 
 |  | 
 | /** | 
 |  * @brief processPoweroffError | 
 |  * | 
 |  * Creates informational PEL for the PLAT/HWP error occured during poweroff | 
 |  * | 
 |  * Not adding callouts from FFDC as the hardware errors in the poweroff path | 
 |  * should be non-visible.  so that we don't throw out extraneous callouts for | 
 |  * power errors or because the CEC is just not in an expected state. | 
 |  * | 
 |  * @param[in] ffdc - FFDC data capturd by the HWP | 
 |  * @param[in] ffdc_prefix - prefix string for logging the data. | 
 |  */ | 
 |  | 
 | void processPoweroffError(FFDC* ffdc, const std::string& ffdc_prefix) | 
 | { | 
 |     try | 
 |     { | 
 |         log<level::INFO>( | 
 |             std::format("processPoweroffError: Message[{}]", ffdc->message) | 
 |                 .c_str()); | 
 |  | 
 |         // To store phal trace and other additional data about ffdc. | 
 |         FFDCData pelAdditionalData; | 
 |  | 
 |         if (ffdc->ffdc_type == FFDC_TYPE_HWP) | 
 |         { | 
 |             std::string keyWithPrefix(ffdc_prefix + "RC"); | 
 |             // Adding hardware procedures return code details | 
 |             pelAdditionalData.emplace_back(keyWithPrefix, | 
 |                                            ffdc->hwp_errorinfo.rc); | 
 |             keyWithPrefix = ffdc_prefix + "RC_DESC"; | 
 |             pelAdditionalData.emplace_back(keyWithPrefix, | 
 |                                            ffdc->hwp_errorinfo.rc_desc); | 
 |         } | 
 |         else if ((ffdc->ffdc_type != FFDC_TYPE_NONE) && | 
 |                  (ffdc->ffdc_type != FFDC_TYPE_UNSUPPORTED)) | 
 |         { | 
 |             log<level::ERR>( | 
 |                 std::format("Unsupported phal FFDC type to create PEL. " | 
 |                             "MSG: {}", | 
 |                             ffdc->message) | 
 |                     .c_str()); | 
 |         } | 
 |  | 
 |         // Adding collected phal logs into PEL additional data | 
 |         for_each(traceLog.begin(), traceLog.end(), | 
 |                  [&pelAdditionalData]( | 
 |                      std::pair<std::string, std::string>& ele) -> void { | 
 |                      pelAdditionalData.emplace_back(ele.first, ele.second); | 
 |                  }); | 
 |  | 
 |         openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot", {}, | 
 |                                        pelAdditionalData, | 
 |                                        Severity::Informational); | 
 |     } | 
 |     catch (const std::exception& ex) | 
 |     { | 
 |         reset(); | 
 |         throw ex; | 
 |     } | 
 |     reset(); | 
 | } | 
 |  | 
 | void processBootErrorHelper(FFDC* ffdc, const std::string& ffdc_prefix) | 
 | { | 
 |     log<level::INFO>("processBootErrorHelper "); | 
 |     try | 
 |     { | 
 |         log<level::INFO>( | 
 |             std::format("PHAL FFDC: Return Message[{}]", ffdc->message) | 
 |                 .c_str()); | 
 |  | 
 |         // Special handling for spare clock related errors. | 
 |         if (ffdc->ffdc_type == FFDC_TYPE_SPARE_CLOCK_INFO) | 
 |         { | 
 |             processClockInfoErrorHelper(ffdc, ffdc_prefix); | 
 |             return; | 
 |         } | 
 |         // To store callouts details in json format as per pel expectation. | 
 |         json jsonCalloutDataList; | 
 |         jsonCalloutDataList = json::array(); | 
 |  | 
 |         // To store phal trace and other additional data about ffdc. | 
 |         FFDCData pelAdditionalData; | 
 |  | 
 |         if (ffdc->ffdc_type == FFDC_TYPE_HWP) | 
 |         { | 
 |             std::string keyWithPrefix(ffdc_prefix + "RC"); | 
 |             // Adding hardware procedures return code details | 
 |             pelAdditionalData.emplace_back(keyWithPrefix, | 
 |                                            ffdc->hwp_errorinfo.rc); | 
 |             keyWithPrefix = ffdc_prefix + "RC_DESC"; | 
 |             pelAdditionalData.emplace_back(keyWithPrefix, | 
 |                                            ffdc->hwp_errorinfo.rc_desc); | 
 |  | 
 |             // Adding hardware procedures required ffdc data for debug | 
 |             for_each(ffdc->hwp_errorinfo.ffdcs_data.begin(), | 
 |                      ffdc->hwp_errorinfo.ffdcs_data.end(), | 
 |                      [&pelAdditionalData, &ffdc_prefix]( | 
 |                          std::pair<std::string, std::string>& ele) -> void { | 
 |                          std::string keyWithPrefix(ffdc_prefix + "FFDC_"); | 
 |                          keyWithPrefix.append(ele.first); | 
 |  | 
 |                          pelAdditionalData.emplace_back(keyWithPrefix, | 
 |                                                         ele.second); | 
 |                      }); | 
 |  | 
 |             // Adding hardware callout details | 
 |             int calloutCount = 0; | 
 |             for_each( | 
 |                 ffdc->hwp_errorinfo.hwcallouts.begin(), | 
 |                 ffdc->hwp_errorinfo.hwcallouts.end(), | 
 |                 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList, | 
 |                  &ffdc_prefix](const HWCallout& hwCallout) -> void { | 
 |                     calloutCount++; | 
 |                     std::stringstream keyPrefix; | 
 |                     keyPrefix << ffdc_prefix << "HW_CO_" << std::setfill('0') | 
 |                               << std::setw(2) << calloutCount << "_"; | 
 |  | 
 |                     pelAdditionalData.emplace_back( | 
 |                         std::string(keyPrefix.str()).append("HW_ID"), | 
 |                         hwCallout.hwid); | 
 |  | 
 |                     pelAdditionalData.emplace_back( | 
 |                         std::string(keyPrefix.str()).append("PRIORITY"), | 
 |                         hwCallout.callout_priority); | 
 |  | 
 |                     // Log target details only if entity path is | 
 |                     // available. For example target entity path will not | 
 |                     // be available for non-hwp clock failure. | 
 |                     if (!hwCallout.target_entity_path.empty()) | 
 |                     { | 
 |                         phal::TargetInfo targetInfo; | 
 |                         phal::getTgtReqAttrsVal(hwCallout.target_entity_path, | 
 |                                                 targetInfo); | 
 |  | 
 |                         std::string locationCode = | 
 |                             std::string(targetInfo.locationCode); | 
 |                         pelAdditionalData.emplace_back( | 
 |                             std::string(keyPrefix.str()).append("LOC_CODE"), | 
 |                             locationCode); | 
 |  | 
 |                         std::string physPath = | 
 |                             std::string(targetInfo.physDevPath); | 
 |                         pelAdditionalData.emplace_back( | 
 |                             std::string(keyPrefix.str()).append("PHYS_PATH"), | 
 |                             physPath); | 
 |                     } | 
 |  | 
 |                     pelAdditionalData.emplace_back( | 
 |                         std::string(keyPrefix.str()).append("CLK_POS"), | 
 |                         std::to_string(hwCallout.clkPos)); | 
 |  | 
 |                     pelAdditionalData.emplace_back( | 
 |                         std::string(keyPrefix.str()).append("CALLOUT_PLANAR"), | 
 |                         (hwCallout.isPlanarCallout == true ? "true" : "false")); | 
 |  | 
 |                     std::string pelPriority = | 
 |                         getPelPriority(hwCallout.callout_priority); | 
 |  | 
 |                     if (hwCallout.isPlanarCallout) | 
 |                     { | 
 |                         addPlanarCallout(jsonCalloutDataList, pelPriority); | 
 |                     } | 
 |                 }); | 
 |  | 
 |             // Adding CDG (callout, deconfigure and guard) targets details | 
 |             calloutCount = 0; | 
 |             for_each( | 
 |                 ffdc->hwp_errorinfo.cdg_targets.begin(), | 
 |                 ffdc->hwp_errorinfo.cdg_targets.end(), | 
 |                 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList, | 
 |                  &ffdc_prefix](const CDG_Target& cdg_tgt) -> void { | 
 |                     calloutCount++; | 
 |                     std::stringstream keyPrefix; | 
 |                     keyPrefix << ffdc_prefix << "CDG_TGT_" << std::setfill('0') | 
 |                               << std::setw(2) << calloutCount << "_"; | 
 |  | 
 |                     phal::TargetInfo targetInfo; | 
 |                     targetInfo.deconfigure = cdg_tgt.deconfigure; | 
 |  | 
 |                     phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path, | 
 |                                             targetInfo); | 
 |  | 
 |                     std::string locationCode = | 
 |                         std::string(targetInfo.locationCode); | 
 |                     pelAdditionalData.emplace_back( | 
 |                         std::string(keyPrefix.str()).append("LOC_CODE"), | 
 |                         locationCode); | 
 |                     std::string physPath = std::string(targetInfo.physDevPath); | 
 |                     pelAdditionalData.emplace_back( | 
 |                         std::string(keyPrefix.str()).append("PHYS_PATH"), | 
 |                         physPath); | 
 |  | 
 |                     pelAdditionalData.emplace_back( | 
 |                         std::string(keyPrefix.str()).append("CO_REQ"), | 
 |                         (cdg_tgt.callout == true ? "true" : "false")); | 
 |  | 
 |                     pelAdditionalData.emplace_back( | 
 |                         std::string(keyPrefix.str()).append("CO_PRIORITY"), | 
 |                         cdg_tgt.callout_priority); | 
 |  | 
 |                     pelAdditionalData.emplace_back( | 
 |                         std::string(keyPrefix.str()).append("DECONF_REQ"), | 
 |                         (cdg_tgt.deconfigure == true ? "true" : "false")); | 
 |  | 
 |                     pelAdditionalData.emplace_back( | 
 |                         std::string(keyPrefix.str()).append("GUARD_REQ"), | 
 |                         (cdg_tgt.guard == true ? "true" : "false")); | 
 |  | 
 |                     pelAdditionalData.emplace_back( | 
 |                         std::string(keyPrefix.str()).append("GUARD_TYPE"), | 
 |                         cdg_tgt.guard_type); | 
 |  | 
 |                     json jsonCalloutData; | 
 |                     jsonCalloutData["LocationCode"] = locationCode; | 
 |                     std::string pelPriority = | 
 |                         getPelPriority(cdg_tgt.callout_priority); | 
 |                     jsonCalloutData["Priority"] = pelPriority; | 
 |  | 
 |                     if (targetInfo.mruId != 0) | 
 |                     { | 
 |                         jsonCalloutData["MRUs"] = json::array({ | 
 |                             {{"ID", targetInfo.mruId}, | 
 |                              {"Priority", pelPriority}}, | 
 |                         }); | 
 |                     } | 
 |                     jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure; | 
 |                     jsonCalloutData["Guarded"] = cdg_tgt.guard; | 
 |                     jsonCalloutData["GuardType"] = cdg_tgt.guard_type; | 
 |                     jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path; | 
 |  | 
 |                     jsonCalloutDataList.emplace_back(jsonCalloutData); | 
 |                 }); | 
 |             // Adding procedure callout | 
 |             calloutCount = 0; | 
 |             for_each( | 
 |                 ffdc->hwp_errorinfo.procedures_callout.begin(), | 
 |                 ffdc->hwp_errorinfo.procedures_callout.end(), | 
 |                 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList, | 
 |                  &ffdc_prefix](const ProcedureCallout& procCallout) -> void { | 
 |                     calloutCount++; | 
 |                     std::stringstream keyPrefix; | 
 |                     keyPrefix << ffdc_prefix << "PROC_CO_" << std::setfill('0') | 
 |                               << std::setw(2) << calloutCount << "_"; | 
 |  | 
 |                     pelAdditionalData.emplace_back( | 
 |                         std::string(keyPrefix.str()).append("PRIORITY"), | 
 |                         procCallout.callout_priority); | 
 |  | 
 |                     pelAdditionalData.emplace_back( | 
 |                         std::string(keyPrefix.str()).append("MAINT_PROCEDURE"), | 
 |                         procCallout.proc_callout); | 
 |  | 
 |                     json jsonCalloutData; | 
 |                     jsonCalloutData["Procedure"] = procCallout.proc_callout; | 
 |                     std::string pelPriority = | 
 |                         getPelPriority(procCallout.callout_priority); | 
 |                     jsonCalloutData["Priority"] = pelPriority; | 
 |                     jsonCalloutDataList.emplace_back(jsonCalloutData); | 
 |                 }); | 
 |         } | 
 |         else if ((ffdc->ffdc_type != FFDC_TYPE_NONE) && | 
 |                  (ffdc->ffdc_type != FFDC_TYPE_UNSUPPORTED)) | 
 |         { | 
 |             log<level::ERR>( | 
 |                 std::format("Unsupported phal FFDC type to create PEL. " | 
 |                             "MSG: {}", | 
 |                             ffdc->message) | 
 |                     .c_str()); | 
 |         } | 
 |  | 
 |         // Adding collected phal logs into PEL additional data | 
 |         for_each(traceLog.begin(), traceLog.end(), | 
 |                  [&pelAdditionalData]( | 
 |                      std::pair<std::string, std::string>& ele) -> void { | 
 |                      pelAdditionalData.emplace_back(ele.first, ele.second); | 
 |                  }); | 
 |  | 
 |         // TODO: #ibm-openbmc/dev/issues/2595 : Once enabled this support, | 
 |         // callout details is not required to sort in H,M and L orders which | 
 |         // are expected by pel because, pel will take care for sorting callouts | 
 |         // based on priority so, now adding support to send callout in order | 
 |         // i.e High -> Medium -> Low. | 
 |         std::sort( | 
 |             jsonCalloutDataList.begin(), jsonCalloutDataList.end(), | 
 |             [](const json& aEle, const json& bEle) -> bool { | 
 |                 // Considering b element having higher priority than a element | 
 |                 // or Both element will be same priorty (to keep same order | 
 |                 // which are given by phal when two callouts are having same | 
 |                 // priority) | 
 |                 if (((aEle["Priority"] == "M") && (bEle["Priority"] == "H")) || | 
 |                     ((aEle["Priority"] == "L") && | 
 |                      ((bEle["Priority"] == "H") || | 
 |                       (bEle["Priority"] == "M"))) || | 
 |                     (aEle["Priority"] == bEle["Priority"])) | 
 |                 { | 
 |                     return false; | 
 |                 } | 
 |  | 
 |                 // Considering a element having higher priority than b element | 
 |                 return true; | 
 |             }); | 
 |         openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot", | 
 |                                        jsonCalloutDataList, pelAdditionalData, | 
 |                                        Severity::Error); | 
 |     } | 
 |     catch (const std::exception& ex) | 
 |     { | 
 |         reset(); | 
 |         throw ex; | 
 |     } | 
 |     reset(); | 
 | } | 
 |  | 
 | void processPlatBootError(const ipl_error_info& errInfo) | 
 | { | 
 |     log<level::INFO>("processPlatBootError "); | 
 |  | 
 |     // Collecting ffdc details from phal | 
 |     FFDC* ffdc = reinterpret_cast<FFDC*>(errInfo.private_data); | 
 |     try | 
 |     { | 
 |         if (util::isHostPoweringOff()) | 
 |         { | 
 |             processPoweroffError(ffdc, "PLAT_"); | 
 |         } | 
 |         else | 
 |         { | 
 |             processBootErrorHelper(ffdc, "PLAT_"); | 
 |         } | 
 |     } | 
 |     catch (const std::exception& ex) | 
 |     { | 
 |         log<level::ERR>( | 
 |             std::format("processPlatBootError: exception({})", ex.what()) | 
 |                 .c_str()); | 
 |         throw ex; | 
 |     } | 
 | } | 
 |  | 
 | void processBootError(bool status) | 
 | { | 
 |     log<level::INFO>( | 
 |         std::format("processBootError: status({})", status).c_str()); | 
 |  | 
 |     try | 
 |     { | 
 |         // return If no failure during hwp execution | 
 |         if (status) | 
 |             return; | 
 |         // Collecting ffdc details from phal | 
 |         FFDC ffdc; | 
 |         libekb_get_ffdc(ffdc); | 
 |  | 
 |         if (util::isHostPoweringOff()) | 
 |         { | 
 |             processPoweroffError(&ffdc, "HWP_"); | 
 |         } | 
 |         else | 
 |         { | 
 |             processBootErrorHelper(&ffdc, "HWP_"); | 
 |         } | 
 |     } | 
 |     catch (const std::exception& ex) | 
 |     { | 
 |         log<level::ERR>( | 
 |             std::format("processBootError: exception({})", ex.what()).c_str()); | 
 |         throw ex; | 
 |     } | 
 | } | 
 |  | 
 | void processSbeBootError() | 
 | { | 
 |     log<level::INFO>("processSbeBootError : Entered "); | 
 |  | 
 |     using namespace openpower::phal::sbe; | 
 |  | 
 |     // To store phal trace and other additional data about ffdc. | 
 |     FFDCData pelAdditionalData; | 
 |  | 
 |     // Adding collected phal logs into PEL additional data | 
 |     for_each( | 
 |         traceLog.begin(), traceLog.end(), | 
 |         [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void { | 
 |             pelAdditionalData.emplace_back(ele.first, ele.second); | 
 |         }); | 
 |  | 
 |     // reset the trace log and counter | 
 |     reset(); | 
 |  | 
 |     // get primary processor to collect FFDC/Dump information. | 
 |     struct pdbg_target* procTarget; | 
 |     pdbg_for_each_class_target("proc", procTarget) | 
 |     { | 
 |         if (openpower::phal::isPrimaryProc(procTarget)) | 
 |             break; | 
 |         procTarget = nullptr; | 
 |     } | 
 |     // check valid primary processor is available | 
 |     if (procTarget == nullptr) | 
 |     { | 
 |         log<level::ERR>("processSbeBootError: fail to get primary processor"); | 
 |         // Add BMC code callout and create PEL | 
 |         json jsonCalloutDataList; | 
 |         jsonCalloutDataList = json::array(); | 
 |         json jsonCalloutData; | 
 |         jsonCalloutData["Procedure"] = "BMC0001"; | 
 |         jsonCalloutData["Priority"] = "H"; | 
 |         jsonCalloutDataList.emplace_back(jsonCalloutData); | 
 |         openpower::pel::createErrorPEL( | 
 |             "org.open_power.Processor.Error.SbeBootFailure", | 
 |             jsonCalloutDataList, {}, Severity::Error); | 
 |         return; | 
 |     } | 
 |     // SBE error object. | 
 |     sbeError_t sbeError; | 
 |     bool dumpIsRequired = false; | 
 |  | 
 |     try | 
 |     { | 
 |         // Capture FFDC information on primary processor | 
 |         sbeError = captureFFDC(procTarget); | 
 |     } | 
 |     catch (const phalError_t& phalError) | 
 |     { | 
 |         // Fail to collect FFDC information , trigger Dump | 
 |         log<level::ERR>( | 
 |             std::format("captureFFDC: Exception({})", phalError.what()) | 
 |                 .c_str()); | 
 |         dumpIsRequired = true; | 
 |     } | 
 |  | 
 |     std::string event; | 
 |  | 
 |     if ((sbeError.errType() == SBE_FFDC_NO_DATA) || | 
 |         (sbeError.errType() == SBE_CMD_TIMEOUT) || (dumpIsRequired)) | 
 |     { | 
 |         event = "org.open_power.Processor.Error.SbeBootTimeout"; | 
 |         dumpIsRequired = true; | 
 |     } | 
 |     else | 
 |     { | 
 |         event = "org.open_power.Processor.Error.SbeBootFailure"; | 
 |     } | 
 |     // SRC6 : [0:15] chip position | 
 |     uint32_t index = pdbg_target_index(procTarget); | 
 |     pelAdditionalData.emplace_back("SRC6", std::to_string(index << 16)); | 
 |     // Create SBE Error with FFDC data. | 
 |     auto logId = createSbeErrorPEL(event, sbeError, pelAdditionalData, | 
 |                                    procTarget); | 
 |  | 
 |     if (dumpIsRequired) | 
 |     { | 
 |         using namespace openpower::phal::dump; | 
 |         DumpParameters dumpParameters = {logId, index, SBE_DUMP_TIMEOUT, | 
 |                                          DumpType::SBE}; | 
 |         try | 
 |         { | 
 |             requestDump(dumpParameters); | 
 |         } | 
 |         catch (const std::runtime_error& e) | 
 |         { | 
 |             // Allowing call back to handle the error gracefully. | 
 |             log<level::ERR>("Dump collection failed"); | 
 |             // TODO revist error handling. | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void processGuardPartitionAccessError() | 
 | { | 
 |     // Adding collected phal logs into PEL additional data | 
 |     FFDCData pelAdditionalData; | 
 |  | 
 |     for_each( | 
 |         traceLog.begin(), traceLog.end(), | 
 |         [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void { | 
 |             pelAdditionalData.emplace_back(ele.first, ele.second); | 
 |         }); | 
 |  | 
 |     openpower::pel::createPEL("org.open_power.PHAL.Error.GuardPartitionAccess", | 
 |                               pelAdditionalData); | 
 | } | 
 |  | 
 | void reset() | 
 | { | 
 |     // reset the trace log and counter | 
 |     traceLog.clear(); | 
 |     counter = 0; | 
 | } | 
 |  | 
 | void pDBGLogTraceCallbackHelper(int, const char* fmt, va_list ap) | 
 | { | 
 |     processLogTraceCallback(NULL, fmt, ap); | 
 | } | 
 | } // namespace detail | 
 |  | 
 | static inline uint8_t getLogLevelFromEnv(const char* env, const uint8_t dValue) | 
 | { | 
 |     auto logLevel = dValue; | 
 |     try | 
 |     { | 
 |         if (const char* env_p = std::getenv(env)) | 
 |         { | 
 |             logLevel = std::stoi(env_p); | 
 |         } | 
 |     } | 
 |     catch (const std::exception& e) | 
 |     { | 
 |         log<level::ERR>(("Conversion Failure"), entry("ENVIRONMENT=%s", env), | 
 |                         entry("EXCEPTION=%s", e.what())); | 
 |     } | 
 |     return logLevel; | 
 | } | 
 |  | 
 | void addBootErrorCallbacks() | 
 | { | 
 |     // Get individual phal repos log level from environment variable | 
 |     // and update the  log level. | 
 |     pdbg_set_loglevel(getLogLevelFromEnv("PDBG_LOG", PDBG_INFO)); | 
 |     libekb_set_loglevel(getLogLevelFromEnv("LIBEKB_LOG", LIBEKB_LOG_IMP)); | 
 |     ipl_set_loglevel(getLogLevelFromEnv("IPL_LOG", IPL_INFO)); | 
 |  | 
 |     // add callback for debug traces | 
 |     pdbg_set_logfunc(detail::pDBGLogTraceCallbackHelper); | 
 |     libekb_set_logfunc(detail::processLogTraceCallback, NULL); | 
 |     ipl_set_logfunc(detail::processLogTraceCallback, NULL); | 
 |  | 
 |     // add callback for ipl failures | 
 |     ipl_set_error_callback_func(detail::processIplErrorCallback); | 
 | } | 
 |  | 
 | } // namespace pel | 
 | } // namespace openpower |