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