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