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