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 <libekb.H>
14 #include <libphal.H>
15 
16 #include <nlohmann/json.hpp>
17 #include <phosphor-logging/elog.hpp>
18 
19 #include <algorithm>
20 #include <cstdlib>
21 #include <cstring>
22 #include <format>
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>(std::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             std::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>(std::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         std::string fmt;
172         for (auto value : targetInfo.physBinPath)
173         {
174             fmt += std::format("{:02X} ", value);
175         }
176 
177         log<level::ERR>(std::format("Given ATTR_PHYS_BIN_PATH value {} "
178                                     "not found in phal device tree",
179                                     fmt)
180                             .c_str());
181         return false;
182     }
183     else if (ret == requireAttrNotFound)
184     {
185         return false;
186     }
187 
188     return true;
189 }
190 } // namespace phal
191 
192 namespace pel
193 {
194 using namespace phosphor::logging;
195 
196 namespace detail
197 {
198 using json = nlohmann::json;
199 
200 // keys need to be unique so using counter value to generate unique key
201 static int counter = 0;
202 
203 // list of debug traces
204 static std::vector<std::pair<std::string, std::string>> traceLog;
205 
206 /**
207  * @brief Process platform realted boot failure
208  *
209  * @param[in] errInfo - error details
210  */
211 static void processPlatBootError(const ipl_error_info& errInfo);
212 
213 void processLogTraceCallback(void*, const char* fmt, va_list ap)
214 {
215     va_list vap;
216     va_copy(vap, ap);
217     std::vector<char> logData(1 + std::vsnprintf(nullptr, 0, fmt, ap));
218     std::vsnprintf(logData.data(), logData.size(), fmt, vap);
219     va_end(vap);
220     std::string logstr(logData.begin(), logData.end());
221 
222     log<level::INFO>(logstr.c_str());
223 
224     char timeBuf[80];
225     time_t t = time(0);
226     tm myTm{};
227     gmtime_r(&t, &myTm);
228     strftime(timeBuf, 80, "%Y-%m-%d %H:%M:%S", &myTm);
229 
230     // key values need to be unique for PEL
231     // TODO #openbmc/dev/issues/1563
232     // If written to Json no need to worry about unique KEY
233     std::stringstream str;
234     str << std::setfill('0');
235     str << "LOG" << std::setw(3) << counter;
236     str << " " << timeBuf;
237     traceLog.emplace_back(std::make_pair(str.str(), std::move(logstr)));
238     counter++;
239 }
240 
241 /**
242  * @brief GET PEL priority from pHAL priority
243  *
244  * The pHAL callout priority is in different format than PEL format
245  * so, this api is used to return current phal supported priority into
246  * PEL expected format.
247  *
248  * @param[in] phalPriority used to pass phal priority format string
249  *
250  * @return pel priority format string else empty if failure
251  *
252  * @note For "NONE" returning "L" (LOW)
253  */
254 static std::string getPelPriority(const std::string& phalPriority)
255 {
256     const std::map<std::string, std::string> priorityMap = {
257         {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}};
258 
259     auto it = priorityMap.find(phalPriority);
260     if (it == priorityMap.end())
261     {
262         log<level::ERR>(std::format("Unsupported phal priority({}) is given "
263                                     "to get pel priority format",
264                                     phalPriority)
265                             .c_str());
266         return "H";
267     }
268 
269     return it->second;
270 }
271 
272 /**
273  * @brief Helper function to create PEL for non functional boot
274  *        processor related failure.
275  * This function adds the BMC code callout as priority 1 to fix
276  * devtree related software issue. Incase the issue still persist
277  * after reboot recommend to replacing the primary processor.
278  */
279 void processNonFunctionalBootProc()
280 {
281     json jsonCalloutDataList;
282     json jsonProcedCallout;
283     // Add BMC code callout
284     jsonProcedCallout["Procedure"] = "BMC0001";
285     jsonProcedCallout["Priority"] = "H";
286     jsonCalloutDataList.emplace_back(std::move(jsonProcedCallout));
287 
288     // get primary processor
289     struct pdbg_target* procTarget;
290     pdbg_for_each_class_target("proc", procTarget)
291     {
292         if (openpower::phal::isPrimaryProc(procTarget))
293             break;
294         procTarget = nullptr;
295     }
296     // check valid primary processor is available
297     if (procTarget == nullptr)
298     {
299         log<level::ERR>(
300             "processNonFunctionalBootProc: fail to get primary processor");
301     }
302     else
303     {
304         try
305         {
306             ATTR_LOCATION_CODE_Type locationCode = {'\0'};
307             // Get location code information
308             openpower::phal::pdbg::getLocationCode(procTarget, locationCode);
309             json jsonProcCallout;
310             jsonProcCallout["LocationCode"] = locationCode;
311             jsonProcCallout["Deconfigured"] = false;
312             jsonProcCallout["Guarded"] = false;
313             jsonProcCallout["Priority"] = "M";
314             jsonCalloutDataList.emplace_back(std::move(jsonProcCallout));
315         }
316         catch (const std::exception& e)
317         {
318             log<level::ERR>(std::format("getLocationCode({}): Exception({})",
319                                         pdbg_target_path(procTarget), e.what())
320                                 .c_str());
321         }
322     }
323     // Adding collected phal logs into PEL additional data
324     FFDCData pelAdditionalData;
325     for_each(
326         traceLog.begin(), traceLog.end(),
327         [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void {
328         pelAdditionalData.emplace_back(ele.first, ele.second);
329     });
330     openpower::pel::createErrorPEL(
331         "org.open_power.PHAL.Error.NonFunctionalBootProc", jsonCalloutDataList,
332         pelAdditionalData, Severity::Error);
333     // reset trace log and exit
334     reset();
335 }
336 
337 /**
338  * @brief processClockInfoErrorHelper
339  *
340  * Creates informational PEL for spare clock failure
341  *
342  * @param[in] ffdc - FFDC data capturd by the HWP
343  * @param[in] ffdc_prefix - prefix string for logging the data.
344  */
345 void processClockInfoErrorHelper(FFDC* ffdc, const std::string& ffdc_prefix)
346 {
347     try
348     {
349         log<level::INFO>(
350             std::format("processClockInfoErrorHelper: FFDC Message[{}]",
351                         ffdc->message)
352                 .c_str());
353 
354         // To store callouts details in json format as per pel expectation.
355         json jsonCalloutDataList;
356         jsonCalloutDataList = json::array();
357 
358         // To store phal trace and other additional data about ffdc.
359         FFDCData pelAdditionalData;
360 
361         std::string keyWithPrefix(ffdc_prefix + "RC");
362         // Adding hardware procedures return code details
363         pelAdditionalData.emplace_back(keyWithPrefix, ffdc->hwp_errorinfo.rc);
364         keyWithPrefix = ffdc_prefix + "RC_DESC";
365         pelAdditionalData.emplace_back(keyWithPrefix,
366                                        ffdc->hwp_errorinfo.rc_desc);
367 
368         // Adding hardware procedures required ffdc data for debug
369         for_each(ffdc->hwp_errorinfo.ffdcs_data.begin(),
370                  ffdc->hwp_errorinfo.ffdcs_data.end(),
371                  [&pelAdditionalData, &ffdc_prefix](
372                      std::pair<std::string, std::string>& ele) -> void {
373             std::string keyWithPrefix(ffdc_prefix + "FFDC_");
374             keyWithPrefix.append(ele.first);
375 
376             pelAdditionalData.emplace_back(keyWithPrefix, ele.second);
377         });
378         // get clock position information
379         auto clk_pos = 0xFF; // Invalid position.
380         for (auto& hwCallout : ffdc->hwp_errorinfo.hwcallouts)
381         {
382             if ((hwCallout.hwid == "PROC_REF_CLOCK") ||
383                 (hwCallout.hwid == "PCI_REF_CLOCK"))
384             {
385                 clk_pos = hwCallout.clkPos;
386                 break;
387             }
388         }
389 
390         // Adding CDG (Only deconfigure) targets details
391         for_each(ffdc->hwp_errorinfo.cdg_targets.begin(),
392                  ffdc->hwp_errorinfo.cdg_targets.end(),
393                  [&pelAdditionalData, &jsonCalloutDataList,
394                   clk_pos](const CDG_Target& cdg_tgt) -> void {
395             json jsonCalloutData;
396             std::string pelPriority = "L";
397             jsonCalloutData["Priority"] = pelPriority; // Not used
398             jsonCalloutData["SymbolicFRU"] = "REFCLK" + std::to_string(clk_pos);
399             jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
400             jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path;
401             jsonCalloutDataList.emplace_back(jsonCalloutData);
402         });
403 
404         // Adding collected phal logs into PEL additional data
405         for_each(traceLog.begin(), traceLog.end(),
406                  [&pelAdditionalData](
407                      std::pair<std::string, std::string>& ele) -> void {
408             pelAdditionalData.emplace_back(ele.first, ele.second);
409         });
410 
411         openpower::pel::createErrorPEL("org.open_power.PHAL.Error.SpareClock",
412                                        jsonCalloutDataList, pelAdditionalData,
413                                        Severity::Informational);
414     }
415     catch (const std::exception& ex)
416     {
417         reset();
418         throw ex;
419     }
420     reset();
421 }
422 
423 void processIplErrorCallback(const ipl_error_info& errInfo)
424 {
425     log<level::INFO>(
426         std::format("processIplErrorCallback: Error type({})",
427                     static_cast<std::underlying_type<ipl_error_type>::type>(
428                         errInfo.type))
429             .c_str());
430 
431     switch (errInfo.type)
432     {
433         case IPL_ERR_OK:
434             // reset trace log and exit
435             reset();
436             break;
437         case IPL_ERR_SBE_BOOT:
438         case IPL_ERR_SBE_CHIPOP:
439             // handle SBE related failures.
440             processSbeBootError();
441             break;
442         case IPL_ERR_HWP:
443             // Handle hwp failure
444             processBootError(false);
445             break;
446         case IPL_ERR_PLAT:
447             processPlatBootError(errInfo);
448             break;
449         case IPL_ERR_PRI_PROC_NON_FUNC:
450             // Handle non functional boot processor error.
451             processNonFunctionalBootProc();
452             break;
453         case IPL_ERR_GUARD_PARTITION_ACCESS:
454             processGuardPartitionAccessError();
455             break;
456         default:
457             createPEL("org.open_power.PHAL.Error.Boot");
458             // reset trace log and exit
459             reset();
460             break;
461     }
462 }
463 
464 /**
465  * @brief addPlanarCallout
466  *
467  * This function will add a json for planar callout in the input json list.
468  * The caller can pass this json list into createErrorPEL to apply the callout.
469  *
470  * @param[in,out] jsonCalloutDataList - json list where callout json will be
471  *                  emplaced
472  * @param[in] priority - string indicating priority.
473  */
474 static void addPlanarCallout(json& jsonCalloutDataList,
475                              const std::string& priority)
476 {
477     json jsonCalloutData;
478 
479     // Inventory path for planar
480     jsonCalloutData["InventoryPath"] =
481         "/xyz/openbmc_project/inventory/system/chassis/motherboard";
482     jsonCalloutData["Deconfigured"] = false;
483     jsonCalloutData["Guarded"] = false;
484     jsonCalloutData["Priority"] = priority;
485 
486     jsonCalloutDataList.emplace_back(jsonCalloutData);
487 }
488 
489 /**
490  * @brief processPoweroffError
491  *
492  * Creates informational PEL for the PLAT/HWP error occured during poweroff
493  *
494  * Not adding callouts from FFDC as the hardware errors in the poweroff path
495  * should be non-visible.  so that we don't throw out extraneous callouts for
496  * power errors or because the CEC is just not in an expected state.
497  *
498  * @param[in] ffdc - FFDC data capturd by the HWP
499  * @param[in] ffdc_prefix - prefix string for logging the data.
500  */
501 
502 void processPoweroffError(FFDC* ffdc, const std::string& ffdc_prefix)
503 {
504     try
505     {
506         log<level::INFO>(
507             std::format("processPoweroffError: Message[{}]", ffdc->message)
508                 .c_str());
509 
510         // To store phal trace and other additional data about ffdc.
511         FFDCData pelAdditionalData;
512 
513         if (ffdc->ffdc_type == FFDC_TYPE_HWP)
514         {
515             std::string keyWithPrefix(ffdc_prefix + "RC");
516             // Adding hardware procedures return code details
517             pelAdditionalData.emplace_back(keyWithPrefix,
518                                            ffdc->hwp_errorinfo.rc);
519             keyWithPrefix = ffdc_prefix + "RC_DESC";
520             pelAdditionalData.emplace_back(keyWithPrefix,
521                                            ffdc->hwp_errorinfo.rc_desc);
522         }
523         else if ((ffdc->ffdc_type != FFDC_TYPE_NONE) &&
524                  (ffdc->ffdc_type != FFDC_TYPE_UNSUPPORTED))
525         {
526             log<level::ERR>(
527                 std::format("Unsupported phal FFDC type to create PEL. "
528                             "MSG: {}",
529                             ffdc->message)
530                     .c_str());
531         }
532 
533         // Adding collected phal logs into PEL additional data
534         for_each(traceLog.begin(), traceLog.end(),
535                  [&pelAdditionalData](
536                      std::pair<std::string, std::string>& ele) -> void {
537             pelAdditionalData.emplace_back(ele.first, ele.second);
538         });
539 
540         openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot", {},
541                                        pelAdditionalData,
542                                        Severity::Informational);
543     }
544     catch (const std::exception& ex)
545     {
546         reset();
547         throw ex;
548     }
549     reset();
550 }
551 
552 void processBootErrorHelper(FFDC* ffdc, const std::string& ffdc_prefix)
553 {
554     log<level::INFO>("processBootErrorHelper ");
555     try
556     {
557         log<level::INFO>(
558             std::format("PHAL FFDC: Return Message[{}]", ffdc->message)
559                 .c_str());
560 
561         // Special handling for spare clock related errors.
562         if (ffdc->ffdc_type == FFDC_TYPE_SPARE_CLOCK_INFO)
563         {
564             processClockInfoErrorHelper(ffdc, ffdc_prefix);
565             return;
566         }
567         // To store callouts details in json format as per pel expectation.
568         json jsonCalloutDataList;
569         jsonCalloutDataList = json::array();
570 
571         // To store phal trace and other additional data about ffdc.
572         FFDCData pelAdditionalData;
573 
574         if (ffdc->ffdc_type == FFDC_TYPE_HWP)
575         {
576             std::string keyWithPrefix(ffdc_prefix + "RC");
577             // Adding hardware procedures return code details
578             pelAdditionalData.emplace_back(keyWithPrefix,
579                                            ffdc->hwp_errorinfo.rc);
580             keyWithPrefix = ffdc_prefix + "RC_DESC";
581             pelAdditionalData.emplace_back(keyWithPrefix,
582                                            ffdc->hwp_errorinfo.rc_desc);
583 
584             // Adding hardware procedures required ffdc data for debug
585             for_each(ffdc->hwp_errorinfo.ffdcs_data.begin(),
586                      ffdc->hwp_errorinfo.ffdcs_data.end(),
587                      [&pelAdditionalData, &ffdc_prefix](
588                          std::pair<std::string, std::string>& ele) -> void {
589                 std::string keyWithPrefix(ffdc_prefix + "FFDC_");
590                 keyWithPrefix.append(ele.first);
591 
592                 pelAdditionalData.emplace_back(keyWithPrefix, ele.second);
593             });
594 
595             // Adding hardware callout details
596             int calloutCount = 0;
597             for_each(ffdc->hwp_errorinfo.hwcallouts.begin(),
598                      ffdc->hwp_errorinfo.hwcallouts.end(),
599                      [&pelAdditionalData, &calloutCount, &jsonCalloutDataList,
600                       &ffdc_prefix](const HWCallout& hwCallout) -> void {
601                 calloutCount++;
602                 std::stringstream keyPrefix;
603                 keyPrefix << ffdc_prefix << "HW_CO_" << std::setfill('0')
604                           << std::setw(2) << calloutCount << "_";
605 
606                 pelAdditionalData.emplace_back(
607                     std::string(keyPrefix.str()).append("HW_ID"),
608                     hwCallout.hwid);
609 
610                 pelAdditionalData.emplace_back(
611                     std::string(keyPrefix.str()).append("PRIORITY"),
612                     hwCallout.callout_priority);
613 
614                 // Log target details only if entity path is
615                 // available. For example target entity path will not
616                 // be available for non-hwp clock failure.
617                 if (!hwCallout.target_entity_path.empty())
618                 {
619                     phal::TargetInfo targetInfo;
620                     phal::getTgtReqAttrsVal(hwCallout.target_entity_path,
621                                             targetInfo);
622 
623                     std::string locationCode =
624                         std::string(targetInfo.locationCode);
625                     pelAdditionalData.emplace_back(
626                         std::string(keyPrefix.str()).append("LOC_CODE"),
627                         locationCode);
628 
629                     std::string physPath = std::string(targetInfo.physDevPath);
630                     pelAdditionalData.emplace_back(
631                         std::string(keyPrefix.str()).append("PHYS_PATH"),
632                         physPath);
633                 }
634 
635                 pelAdditionalData.emplace_back(
636                     std::string(keyPrefix.str()).append("CLK_POS"),
637                     std::to_string(hwCallout.clkPos));
638 
639                 pelAdditionalData.emplace_back(
640                     std::string(keyPrefix.str()).append("CALLOUT_PLANAR"),
641                     (hwCallout.isPlanarCallout == true ? "true" : "false"));
642 
643                 std::string pelPriority =
644                     getPelPriority(hwCallout.callout_priority);
645 
646                 if (hwCallout.isPlanarCallout)
647                 {
648                     addPlanarCallout(jsonCalloutDataList, pelPriority);
649                 }
650             });
651 
652             // Adding CDG (callout, deconfigure and guard) targets details
653             calloutCount = 0;
654             for_each(ffdc->hwp_errorinfo.cdg_targets.begin(),
655                      ffdc->hwp_errorinfo.cdg_targets.end(),
656                      [&pelAdditionalData, &calloutCount, &jsonCalloutDataList,
657                       &ffdc_prefix](const CDG_Target& cdg_tgt) -> void {
658                 calloutCount++;
659                 std::stringstream keyPrefix;
660                 keyPrefix << ffdc_prefix << "CDG_TGT_" << std::setfill('0')
661                           << std::setw(2) << calloutCount << "_";
662 
663                 phal::TargetInfo targetInfo;
664                 targetInfo.deconfigure = cdg_tgt.deconfigure;
665 
666                 phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path, targetInfo);
667 
668                 std::string locationCode = std::string(targetInfo.locationCode);
669                 pelAdditionalData.emplace_back(
670                     std::string(keyPrefix.str()).append("LOC_CODE"),
671                     locationCode);
672                 std::string physPath = std::string(targetInfo.physDevPath);
673                 pelAdditionalData.emplace_back(
674                     std::string(keyPrefix.str()).append("PHYS_PATH"), 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}, {"Priority", pelPriority}},
706                     });
707                 }
708                 jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
709                 jsonCalloutData["Guarded"] = cdg_tgt.guard;
710                 jsonCalloutData["GuardType"] = cdg_tgt.guard_type;
711                 jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path;
712 
713                 jsonCalloutDataList.emplace_back(jsonCalloutData);
714             });
715             // Adding procedure callout
716             calloutCount = 0;
717             for_each(
718                 ffdc->hwp_errorinfo.procedures_callout.begin(),
719                 ffdc->hwp_errorinfo.procedures_callout.end(),
720                 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList,
721                  &ffdc_prefix](const ProcedureCallout& procCallout) -> void {
722                 calloutCount++;
723                 std::stringstream keyPrefix;
724                 keyPrefix << ffdc_prefix << "PROC_CO_" << std::setfill('0')
725                           << std::setw(2) << calloutCount << "_";
726 
727                 pelAdditionalData.emplace_back(
728                     std::string(keyPrefix.str()).append("PRIORITY"),
729                     procCallout.callout_priority);
730 
731                 pelAdditionalData.emplace_back(
732                     std::string(keyPrefix.str()).append("MAINT_PROCEDURE"),
733                     procCallout.proc_callout);
734 
735                 json jsonCalloutData;
736                 jsonCalloutData["Procedure"] = procCallout.proc_callout;
737                 std::string pelPriority =
738                     getPelPriority(procCallout.callout_priority);
739                 jsonCalloutData["Priority"] = pelPriority;
740                 jsonCalloutDataList.emplace_back(jsonCalloutData);
741             });
742         }
743         else if ((ffdc->ffdc_type != FFDC_TYPE_NONE) &&
744                  (ffdc->ffdc_type != FFDC_TYPE_UNSUPPORTED))
745         {
746             log<level::ERR>(
747                 std::format("Unsupported phal FFDC type to create PEL. "
748                             "MSG: {}",
749                             ffdc->message)
750                     .c_str());
751         }
752 
753         // Adding collected phal logs into PEL additional data
754         for_each(traceLog.begin(), traceLog.end(),
755                  [&pelAdditionalData](
756                      std::pair<std::string, std::string>& ele) -> void {
757             pelAdditionalData.emplace_back(ele.first, ele.second);
758         });
759 
760         // TODO: #ibm-openbmc/dev/issues/2595 : Once enabled this support,
761         // callout details is not required to sort in H,M and L orders which
762         // are expected by pel because, pel will take care for sorting callouts
763         // based on priority so, now adding support to send callout in order
764         // i.e High -> Medium -> Low.
765         std::sort(jsonCalloutDataList.begin(), jsonCalloutDataList.end(),
766                   [](const json& aEle, const json& bEle) -> bool {
767             // Considering b element having higher priority than a element
768             // or Both element will be same priorty (to keep same order
769             // which are given by phal when two callouts are having same
770             // priority)
771             if (((aEle["Priority"] == "M") && (bEle["Priority"] == "H")) ||
772                 ((aEle["Priority"] == "L") &&
773                  ((bEle["Priority"] == "H") || (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             std::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         std::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             std::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             std::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 = createSbeErrorPEL(event, sbeError, pelAdditionalData,
929                                    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 processGuardPartitionAccessError()
950 {
951     // Adding collected phal logs into PEL additional data
952     FFDCData pelAdditionalData;
953 
954     for_each(
955         traceLog.begin(), traceLog.end(),
956         [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void {
957         pelAdditionalData.emplace_back(ele.first, ele.second);
958     });
959 
960     openpower::pel::createPEL("org.open_power.PHAL.Error.GuardPartitionAccess",
961                               pelAdditionalData);
962 }
963 
964 void reset()
965 {
966     // reset the trace log and counter
967     traceLog.clear();
968     counter = 0;
969 }
970 
971 void pDBGLogTraceCallbackHelper(int, const char* fmt, va_list ap)
972 {
973     processLogTraceCallback(NULL, fmt, ap);
974 }
975 } // namespace detail
976 
977 static inline uint8_t getLogLevelFromEnv(const char* env, const uint8_t dValue)
978 {
979     auto logLevel = dValue;
980     try
981     {
982         if (const char* env_p = std::getenv(env))
983         {
984             logLevel = std::stoi(env_p);
985         }
986     }
987     catch (const std::exception& e)
988     {
989         log<level::ERR>(("Conversion Failure"), entry("ENVIRONMENT=%s", env),
990                         entry("EXCEPTION=%s", e.what()));
991     }
992     return logLevel;
993 }
994 
995 void addBootErrorCallbacks()
996 {
997     // Get individual phal repos log level from environment variable
998     // and update the  log level.
999     pdbg_set_loglevel(getLogLevelFromEnv("PDBG_LOG", PDBG_INFO));
1000     libekb_set_loglevel(getLogLevelFromEnv("LIBEKB_LOG", LIBEKB_LOG_IMP));
1001     ipl_set_loglevel(getLogLevelFromEnv("IPL_LOG", IPL_INFO));
1002 
1003     // add callback for debug traces
1004     pdbg_set_logfunc(detail::pDBGLogTraceCallbackHelper);
1005     libekb_set_logfunc(detail::processLogTraceCallback, NULL);
1006     ipl_set_logfunc(detail::processLogTraceCallback, NULL);
1007 
1008     // add callback for ipl failures
1009     ipl_set_error_callback_func(detail::processIplErrorCallback);
1010 }
1011 
1012 } // namespace pel
1013 } // namespace openpower
1014