xref: /openbmc/openpower-proc-control/extensions/phal/phal_error.cpp (revision aaea68675bcc020ad360d3decbd6f9c9c5f6dd45)
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 /**
332  * @brief processClockInfoErrorHelper
333  *
334  * Creates informational PEL for spare clock failure
335  *
336  * @param[in] ffdc - FFDC data capturd by the HWP
337  * @param[in] ffdc_prefix - prefix string for logging the data.
338  */
339 void processClockInfoErrorHelper(FFDC* ffdc, const std::string& ffdc_prefix)
340 {
341     try
342     {
343         log<level::INFO>(
344             fmt::format("processClockInfoErrorHelper: FFDC Message[{}]",
345                         ffdc->message)
346                 .c_str());
347 
348         // To store callouts details in json format as per pel expectation.
349         json jsonCalloutDataList;
350         jsonCalloutDataList = json::array();
351 
352         // To store phal trace and other additional data about ffdc.
353         FFDCData pelAdditionalData;
354 
355         std::string keyWithPrefix(ffdc_prefix + "RC");
356         // Adding hardware procedures return code details
357         pelAdditionalData.emplace_back(keyWithPrefix, ffdc->hwp_errorinfo.rc);
358         keyWithPrefix = ffdc_prefix + "RC_DESC";
359         pelAdditionalData.emplace_back(keyWithPrefix,
360                                        ffdc->hwp_errorinfo.rc_desc);
361 
362         // Adding hardware procedures required ffdc data for debug
363         for_each(ffdc->hwp_errorinfo.ffdcs_data.begin(),
364                  ffdc->hwp_errorinfo.ffdcs_data.end(),
365                  [&pelAdditionalData, &ffdc_prefix](
366                      std::pair<std::string, std::string>& ele) -> void {
367                      std::string keyWithPrefix(ffdc_prefix + "FFDC_");
368                      keyWithPrefix.append(ele.first);
369 
370                      pelAdditionalData.emplace_back(keyWithPrefix, ele.second);
371                  });
372         // get clock position information
373         auto clk_pos = 0xFF; // Invalid position.
374         for (auto& hwCallout : ffdc->hwp_errorinfo.hwcallouts)
375         {
376             if ((hwCallout.hwid == "PROC_REF_CLOCK") ||
377                 (hwCallout.hwid == "PCI_REF_CLOCK"))
378             {
379                 clk_pos = hwCallout.clkPos;
380                 break;
381             }
382         }
383 
384         // Adding CDG (Only deconfigure) targets details
385         for_each(ffdc->hwp_errorinfo.cdg_targets.begin(),
386                  ffdc->hwp_errorinfo.cdg_targets.end(),
387                  [&pelAdditionalData, &jsonCalloutDataList,
388                   clk_pos](const CDG_Target& cdg_tgt) -> void {
389                      json jsonCalloutData;
390                      std::string pelPriority = "H";
391                      jsonCalloutData["Priority"] = pelPriority; // Not used
392                      jsonCalloutData["SymbolicFRU"] =
393                          "REFCLK" + std::to_string(clk_pos);
394                      jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
395                      jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path;
396                      jsonCalloutDataList.emplace_back(jsonCalloutData);
397                  });
398 
399         // Adding collected phal logs into PEL additional data
400         for_each(traceLog.begin(), traceLog.end(),
401                  [&pelAdditionalData](
402                      std::pair<std::string, std::string>& ele) -> void {
403                      pelAdditionalData.emplace_back(ele.first, ele.second);
404                  });
405 
406         openpower::pel::createErrorPEL("org.open_power.PHAL.Error.SpareClock",
407                                        jsonCalloutDataList, pelAdditionalData,
408                                        Severity::Informational);
409     }
410     catch (const std::exception& ex)
411     {
412         reset();
413         throw ex;
414     }
415     reset();
416 }
417 
418 void processIplErrorCallback(const ipl_error_info& errInfo)
419 {
420     log<level::INFO>(
421         fmt::format("processIplErrorCallback: Error type({})", errInfo.type)
422             .c_str());
423 
424     switch (errInfo.type)
425     {
426         case IPL_ERR_OK:
427             // reset trace log and exit
428             reset();
429             break;
430         case IPL_ERR_SBE_BOOT:
431         case IPL_ERR_SBE_CHIPOP:
432             // handle SBE related failures.
433             processSbeBootError();
434             break;
435         case IPL_ERR_HWP:
436             // Handle hwp failure
437             processBootError(false);
438             break;
439         case IPL_ERR_PLAT:
440             processPlatBootError(errInfo);
441             break;
442         case IPL_ERR_PRI_PROC_NON_FUNC:
443             // Handle non functional boot processor error.
444             processNonFunctionalBootProc();
445             break;
446         default:
447             createPEL("org.open_power.PHAL.Error.Boot");
448             // reset trace log and exit
449             reset();
450             break;
451     }
452 }
453 
454 /**
455  * @brief addPlanarCallout
456  *
457  * This function will add a json for planar callout in the input json list.
458  * The caller can pass this json list into createErrorPEL to apply the callout.
459  *
460  * @param[in,out] jsonCalloutDataList - json list where callout json will be
461  *                  emplaced
462  * @param[in] priority - string indicating priority.
463  */
464 static void addPlanarCallout(json& jsonCalloutDataList,
465                              const std::string& priority)
466 {
467     json jsonCalloutData;
468 
469     // Inventory path for planar
470     jsonCalloutData["InventoryPath"] =
471         "/xyz/openbmc_project/inventory/system/chassis/motherboard";
472     jsonCalloutData["Deconfigured"] = false;
473     jsonCalloutData["Guarded"] = false;
474     jsonCalloutData["Priority"] = priority;
475 
476     jsonCalloutDataList.emplace_back(jsonCalloutData);
477 }
478 
479 /**
480  * @brief processPoweroffError
481  *
482  * Creates informational PEL for the PLAT/HWP error occured during poweroff
483  *
484  * Not adding callouts from FFDC as the hardware errors in the poweroff path
485  * should be non-visible.  so that we don't throw out extraneous callouts for
486  * power errors or because the CEC is just not in an expected state.
487  *
488  * @param[in] ffdc - FFDC data capturd by the HWP
489  * @param[in] ffdc_prefix - prefix string for logging the data.
490  */
491 
492 void processPoweroffError(FFDC* ffdc, const std::string& ffdc_prefix)
493 {
494     try
495     {
496         log<level::INFO>(
497             fmt::format("processPoweroffError: Message[{}]", ffdc->message)
498                 .c_str());
499 
500         // To store phal trace and other additional data about ffdc.
501         FFDCData pelAdditionalData;
502 
503         if (ffdc->ffdc_type == FFDC_TYPE_HWP)
504         {
505             std::string keyWithPrefix(ffdc_prefix + "RC");
506             // Adding hardware procedures return code details
507             pelAdditionalData.emplace_back(keyWithPrefix,
508                                            ffdc->hwp_errorinfo.rc);
509             keyWithPrefix = ffdc_prefix + "RC_DESC";
510             pelAdditionalData.emplace_back(keyWithPrefix,
511                                            ffdc->hwp_errorinfo.rc_desc);
512         }
513         else if ((ffdc->ffdc_type != FFDC_TYPE_NONE) &&
514                  (ffdc->ffdc_type != FFDC_TYPE_UNSUPPORTED))
515         {
516             log<level::ERR>(
517                 fmt::format("Unsupported phal FFDC type to create PEL. "
518                             "MSG: {}",
519                             ffdc->message)
520                     .c_str());
521         }
522 
523         // Adding collected phal logs into PEL additional data
524         for_each(traceLog.begin(), traceLog.end(),
525                  [&pelAdditionalData](
526                      std::pair<std::string, std::string>& ele) -> void {
527                      pelAdditionalData.emplace_back(ele.first, ele.second);
528                  });
529 
530         openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot", {},
531                                        pelAdditionalData,
532                                        Severity::Informational);
533     }
534     catch (const std::exception& ex)
535     {
536         reset();
537         throw ex;
538     }
539     reset();
540 }
541 
542 void processBootErrorHelper(FFDC* ffdc, const std::string& ffdc_prefix)
543 {
544     log<level::INFO>("processBootErrorHelper ");
545     try
546     {
547         log<level::INFO>(
548             fmt::format("PHAL FFDC: Return Message[{}]", ffdc->message)
549                 .c_str());
550 
551         // Special handling for spare clock related errors.
552         if (ffdc->ffdc_type == FFDC_TYPE_SPARE_CLOCK_INFO)
553         {
554             processClockInfoErrorHelper(ffdc, ffdc_prefix);
555             return;
556         }
557         // To store callouts details in json format as per pel expectation.
558         json jsonCalloutDataList;
559         jsonCalloutDataList = json::array();
560 
561         // To store phal trace and other additional data about ffdc.
562         FFDCData pelAdditionalData;
563 
564         if (ffdc->ffdc_type == FFDC_TYPE_HWP)
565         {
566             std::string keyWithPrefix(ffdc_prefix + "RC");
567             // Adding hardware procedures return code details
568             pelAdditionalData.emplace_back(keyWithPrefix,
569                                            ffdc->hwp_errorinfo.rc);
570             keyWithPrefix = ffdc_prefix + "RC_DESC";
571             pelAdditionalData.emplace_back(keyWithPrefix,
572                                            ffdc->hwp_errorinfo.rc_desc);
573 
574             // Adding hardware procedures required ffdc data for debug
575             for_each(ffdc->hwp_errorinfo.ffdcs_data.begin(),
576                      ffdc->hwp_errorinfo.ffdcs_data.end(),
577                      [&pelAdditionalData, &ffdc_prefix](
578                          std::pair<std::string, std::string>& ele) -> void {
579                          std::string keyWithPrefix(ffdc_prefix + "FFDC_");
580                          keyWithPrefix.append(ele.first);
581 
582                          pelAdditionalData.emplace_back(keyWithPrefix,
583                                                         ele.second);
584                      });
585 
586             // Adding hardware callout details
587             int calloutCount = 0;
588             for_each(
589                 ffdc->hwp_errorinfo.hwcallouts.begin(),
590                 ffdc->hwp_errorinfo.hwcallouts.end(),
591                 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList,
592                  &ffdc_prefix](const HWCallout& hwCallout) -> void {
593                     calloutCount++;
594                     std::stringstream keyPrefix;
595                     keyPrefix << ffdc_prefix << "HW_CO_" << std::setfill('0')
596                               << std::setw(2) << calloutCount << "_";
597 
598                     pelAdditionalData.emplace_back(
599                         std::string(keyPrefix.str()).append("HW_ID"),
600                         hwCallout.hwid);
601 
602                     pelAdditionalData.emplace_back(
603                         std::string(keyPrefix.str()).append("PRIORITY"),
604                         hwCallout.callout_priority);
605 
606                     // Log target details only if entity path is
607                     // available. For example target entity path will not
608                     // be available for non-hwp clock failure.
609                     if (!hwCallout.target_entity_path.empty())
610                     {
611                         phal::TargetInfo targetInfo;
612                         phal::getTgtReqAttrsVal(hwCallout.target_entity_path,
613                                                 targetInfo);
614 
615                         std::string locationCode =
616                             std::string(targetInfo.locationCode);
617                         pelAdditionalData.emplace_back(
618                             std::string(keyPrefix.str()).append("LOC_CODE"),
619                             locationCode);
620 
621                         std::string physPath =
622                             std::string(targetInfo.physDevPath);
623                         pelAdditionalData.emplace_back(
624                             std::string(keyPrefix.str()).append("PHYS_PATH"),
625                             physPath);
626                     }
627 
628                     pelAdditionalData.emplace_back(
629                         std::string(keyPrefix.str()).append("CLK_POS"),
630                         std::to_string(hwCallout.clkPos));
631 
632                     pelAdditionalData.emplace_back(
633                         std::string(keyPrefix.str()).append("CALLOUT_PLANAR"),
634                         (hwCallout.isPlanarCallout == true ? "true" : "false"));
635 
636                     std::string pelPriority =
637                         getPelPriority(hwCallout.callout_priority);
638 
639                     if (hwCallout.isPlanarCallout)
640                     {
641                         addPlanarCallout(jsonCalloutDataList, pelPriority);
642                     }
643                 });
644 
645             // Adding CDG (callout, deconfigure and guard) targets details
646             calloutCount = 0;
647             for_each(
648                 ffdc->hwp_errorinfo.cdg_targets.begin(),
649                 ffdc->hwp_errorinfo.cdg_targets.end(),
650                 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList,
651                  &ffdc_prefix](const CDG_Target& cdg_tgt) -> void {
652                     calloutCount++;
653                     std::stringstream keyPrefix;
654                     keyPrefix << ffdc_prefix << "CDG_TGT_" << std::setfill('0')
655                               << std::setw(2) << calloutCount << "_";
656 
657                     phal::TargetInfo targetInfo;
658                     targetInfo.deconfigure = cdg_tgt.deconfigure;
659 
660                     phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path,
661                                             targetInfo);
662 
663                     std::string locationCode =
664                         std::string(targetInfo.locationCode);
665                     pelAdditionalData.emplace_back(
666                         std::string(keyPrefix.str()).append("LOC_CODE"),
667                         locationCode);
668                     std::string physPath = std::string(targetInfo.physDevPath);
669                     pelAdditionalData.emplace_back(
670                         std::string(keyPrefix.str()).append("PHYS_PATH"),
671                         physPath);
672 
673                     pelAdditionalData.emplace_back(
674                         std::string(keyPrefix.str()).append("CO_REQ"),
675                         (cdg_tgt.callout == true ? "true" : "false"));
676 
677                     pelAdditionalData.emplace_back(
678                         std::string(keyPrefix.str()).append("CO_PRIORITY"),
679                         cdg_tgt.callout_priority);
680 
681                     pelAdditionalData.emplace_back(
682                         std::string(keyPrefix.str()).append("DECONF_REQ"),
683                         (cdg_tgt.deconfigure == true ? "true" : "false"));
684 
685                     pelAdditionalData.emplace_back(
686                         std::string(keyPrefix.str()).append("GUARD_REQ"),
687                         (cdg_tgt.guard == true ? "true" : "false"));
688 
689                     pelAdditionalData.emplace_back(
690                         std::string(keyPrefix.str()).append("GUARD_TYPE"),
691                         cdg_tgt.guard_type);
692 
693                     json jsonCalloutData;
694                     jsonCalloutData["LocationCode"] = locationCode;
695                     std::string pelPriority =
696                         getPelPriority(cdg_tgt.callout_priority);
697                     jsonCalloutData["Priority"] = pelPriority;
698 
699                     if (targetInfo.mruId != 0)
700                     {
701                         jsonCalloutData["MRUs"] = json::array({
702                             {{"ID", targetInfo.mruId},
703                              {"Priority", pelPriority}},
704                         });
705                     }
706                     jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
707                     jsonCalloutData["Guarded"] = cdg_tgt.guard;
708                     jsonCalloutData["GuardType"] = cdg_tgt.guard_type;
709                     jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path;
710 
711                     jsonCalloutDataList.emplace_back(jsonCalloutData);
712                 });
713             // Adding procedure callout
714             calloutCount = 0;
715             for_each(
716                 ffdc->hwp_errorinfo.procedures_callout.begin(),
717                 ffdc->hwp_errorinfo.procedures_callout.end(),
718                 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList,
719                  &ffdc_prefix](const ProcedureCallout& procCallout) -> void {
720                     calloutCount++;
721                     std::stringstream keyPrefix;
722                     keyPrefix << ffdc_prefix << "PROC_CO_" << std::setfill('0')
723                               << std::setw(2) << calloutCount << "_";
724 
725                     pelAdditionalData.emplace_back(
726                         std::string(keyPrefix.str()).append("PRIORITY"),
727                         procCallout.callout_priority);
728 
729                     pelAdditionalData.emplace_back(
730                         std::string(keyPrefix.str()).append("MAINT_PROCEDURE"),
731                         procCallout.proc_callout);
732 
733                     json jsonCalloutData;
734                     jsonCalloutData["Procedure"] = procCallout.proc_callout;
735                     std::string pelPriority =
736                         getPelPriority(procCallout.callout_priority);
737                     jsonCalloutData["Priority"] = pelPriority;
738                     jsonCalloutDataList.emplace_back(jsonCalloutData);
739                 });
740         }
741         else if ((ffdc->ffdc_type != FFDC_TYPE_NONE) &&
742                  (ffdc->ffdc_type != FFDC_TYPE_UNSUPPORTED))
743         {
744             log<level::ERR>(
745                 fmt::format("Unsupported phal FFDC type to create PEL. "
746                             "MSG: {}",
747                             ffdc->message)
748                     .c_str());
749         }
750 
751         // Adding collected phal logs into PEL additional data
752         for_each(traceLog.begin(), traceLog.end(),
753                  [&pelAdditionalData](
754                      std::pair<std::string, std::string>& ele) -> void {
755                      pelAdditionalData.emplace_back(ele.first, ele.second);
756                  });
757 
758         // TODO: #ibm-openbmc/dev/issues/2595 : Once enabled this support,
759         // callout details is not required to sort in H,M and L orders which
760         // are expected by pel because, pel will take care for sorting callouts
761         // based on priority so, now adding support to send callout in order
762         // i.e High -> Medium -> Low.
763         std::sort(
764             jsonCalloutDataList.begin(), jsonCalloutDataList.end(),
765             [](const json& aEle, const json& bEle) -> bool {
766                 // Considering b element having higher priority than a element
767                 // or Both element will be same priorty (to keep same order
768                 // which are given by phal when two callouts are having same
769                 // priority)
770                 if (((aEle["Priority"] == "M") && (bEle["Priority"] == "H")) ||
771                     ((aEle["Priority"] == "L") &&
772                      ((bEle["Priority"] == "H") ||
773                       (bEle["Priority"] == "M"))) ||
774                     (aEle["Priority"] == bEle["Priority"]))
775                 {
776                     return false;
777                 }
778 
779                 // Considering a element having higher priority than b element
780                 return true;
781             });
782         openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot",
783                                        jsonCalloutDataList, pelAdditionalData,
784                                        Severity::Error);
785     }
786     catch (const std::exception& ex)
787     {
788         reset();
789         throw ex;
790     }
791     reset();
792 }
793 
794 void processPlatBootError(const ipl_error_info& errInfo)
795 {
796     log<level::INFO>("processPlatBootError ");
797 
798     // Collecting ffdc details from phal
799     FFDC* ffdc = reinterpret_cast<FFDC*>(errInfo.private_data);
800     try
801     {
802         if (util::isHostPoweringOff())
803         {
804             processPoweroffError(ffdc, "PLAT_");
805         }
806         else
807         {
808             processBootErrorHelper(ffdc, "PLAT_");
809         }
810     }
811     catch (const std::exception& ex)
812     {
813         log<level::ERR>(
814             fmt::format("processPlatBootError: exception({})", ex.what())
815                 .c_str());
816         throw ex;
817     }
818 }
819 
820 void processBootError(bool status)
821 {
822     log<level::INFO>(
823         fmt::format("processBootError: status({})", status).c_str());
824 
825     try
826     {
827         // return If no failure during hwp execution
828         if (status)
829             return;
830         // Collecting ffdc details from phal
831         FFDC ffdc;
832         libekb_get_ffdc(ffdc);
833 
834         if (util::isHostPoweringOff())
835         {
836             processPoweroffError(&ffdc, "HWP_");
837         }
838         else
839         {
840             processBootErrorHelper(&ffdc, "HWP_");
841         }
842     }
843     catch (const std::exception& ex)
844     {
845         log<level::ERR>(
846             fmt::format("processBootError: exception({})", ex.what()).c_str());
847         throw ex;
848     }
849 }
850 
851 void processSbeBootError()
852 {
853     log<level::INFO>("processSbeBootError : Entered ");
854 
855     using namespace openpower::phal::sbe;
856 
857     // To store phal trace and other additional data about ffdc.
858     FFDCData pelAdditionalData;
859 
860     // Adding collected phal logs into PEL additional data
861     for_each(
862         traceLog.begin(), traceLog.end(),
863         [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void {
864             pelAdditionalData.emplace_back(ele.first, ele.second);
865         });
866 
867     // reset the trace log and counter
868     reset();
869 
870     // get primary processor to collect FFDC/Dump information.
871     struct pdbg_target* procTarget;
872     pdbg_for_each_class_target("proc", procTarget)
873     {
874         if (openpower::phal::isPrimaryProc(procTarget))
875             break;
876         procTarget = nullptr;
877     }
878     // check valid primary processor is available
879     if (procTarget == nullptr)
880     {
881         log<level::ERR>("processSbeBootError: fail to get primary processor");
882         // Add BMC code callout and create PEL
883         json jsonCalloutDataList;
884         jsonCalloutDataList = json::array();
885         json jsonCalloutData;
886         jsonCalloutData["Procedure"] = "BMC0001";
887         jsonCalloutData["Priority"] = "H";
888         jsonCalloutDataList.emplace_back(jsonCalloutData);
889         openpower::pel::createErrorPEL(
890             "org.open_power.Processor.Error.SbeBootFailure",
891             jsonCalloutDataList, {}, Severity::Error);
892         return;
893     }
894     // SBE error object.
895     sbeError_t sbeError;
896     bool dumpIsRequired = false;
897 
898     try
899     {
900         // Capture FFDC information on primary processor
901         sbeError = captureFFDC(procTarget);
902     }
903     catch (const phalError_t& phalError)
904     {
905         // Fail to collect FFDC information , trigger Dump
906         log<level::ERR>(
907             fmt::format("captureFFDC: Exception({})", phalError.what())
908                 .c_str());
909         dumpIsRequired = true;
910     }
911 
912     std::string event;
913 
914     if ((sbeError.errType() == SBE_FFDC_NO_DATA) ||
915         (sbeError.errType() == SBE_CMD_TIMEOUT) || (dumpIsRequired))
916     {
917         event = "org.open_power.Processor.Error.SbeBootTimeout";
918         dumpIsRequired = true;
919     }
920     else
921     {
922         event = "org.open_power.Processor.Error.SbeBootFailure";
923     }
924     // SRC6 : [0:15] chip position
925     uint32_t index = pdbg_target_index(procTarget);
926     pelAdditionalData.emplace_back("SRC6", std::to_string(index << 16));
927     // Create SBE Error with FFDC data.
928     auto logId =
929         createSbeErrorPEL(event, sbeError, pelAdditionalData, procTarget);
930 
931     if (dumpIsRequired)
932     {
933         using namespace openpower::phal::dump;
934         DumpParameters dumpParameters = {logId, index, SBE_DUMP_TIMEOUT,
935                                          DumpType::SBE};
936         try
937         {
938             requestDump(dumpParameters);
939         }
940         catch (const std::runtime_error& e)
941         {
942             // Allowing call back to handle the error gracefully.
943             log<level::ERR>("Dump collection failed");
944             // TODO revist error handling.
945         }
946     }
947 }
948 
949 void reset()
950 {
951     // reset the trace log and counter
952     traceLog.clear();
953     counter = 0;
954 }
955 
956 void pDBGLogTraceCallbackHelper(int, const char* fmt, va_list ap)
957 {
958     processLogTraceCallback(NULL, fmt, ap);
959 }
960 } // namespace detail
961 
962 static inline uint8_t getLogLevelFromEnv(const char* env, const uint8_t dValue)
963 {
964     auto logLevel = dValue;
965     try
966     {
967         if (const char* env_p = std::getenv(env))
968         {
969             logLevel = std::stoi(env_p);
970         }
971     }
972     catch (const std::exception& e)
973     {
974         log<level::ERR>(("Conversion Failure"), entry("ENVIRONMENT=%s", env),
975                         entry("EXCEPTION=%s", e.what()));
976     }
977     return logLevel;
978 }
979 
980 void addBootErrorCallbacks()
981 {
982     // Get individual phal repos log level from environment variable
983     // and update the  log level.
984     pdbg_set_loglevel(getLogLevelFromEnv("PDBG_LOG", PDBG_INFO));
985     libekb_set_loglevel(getLogLevelFromEnv("LIBEKB_LOG", LIBEKB_LOG_IMP));
986     ipl_set_loglevel(getLogLevelFromEnv("IPL_LOG", IPL_INFO));
987 
988     // add callback for debug traces
989     pdbg_set_logfunc(detail::pDBGLogTraceCallbackHelper);
990     libekb_set_logfunc(detail::processLogTraceCallback, NULL);
991     ipl_set_logfunc(detail::processLogTraceCallback, NULL);
992 
993     // add callback for ipl failures
994     ipl_set_error_callback_func(detail::processIplErrorCallback);
995 }
996 
997 } // namespace pel
998 } // namespace openpower
999