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