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 
11 #include <attributes_info.H>
12 #include <fmt/format.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 <iomanip>
23 #include <list>
24 #include <map>
25 #include <sstream>
26 #include <string>
27 
28 namespace openpower
29 {
30 namespace phal
31 {
32 using namespace phosphor::logging;
33 using namespace openpower::phal::exception;
34 
35 /**
36  * Used to pass buffer to pdbg callback api to get required target
37  * data (attributes) based on given data (attribute).
38  */
39 struct TargetInfo
40 {
41     ATTR_PHYS_BIN_PATH_Type physBinPath;
42     ATTR_LOCATION_CODE_Type locationCode;
43     ATTR_PHYS_DEV_PATH_Type physDevPath;
44     ATTR_MRU_ID_Type mruId;
45 
46     bool deconfigure;
47 
48     TargetInfo()
49     {
50         memset(&physBinPath, '\0', sizeof(physBinPath));
51         memset(&locationCode, '\0', sizeof(locationCode));
52         memset(&physDevPath, '\0', sizeof(physDevPath));
53         mruId = 0;
54         deconfigure = false;
55     }
56 };
57 
58 /**
59  * Used to return in callback function which are used to get
60  * physical path value and it binary format value.
61  *
62  * The value for constexpr defined based on pdbg_target_traverse function usage.
63  */
64 constexpr int continueTgtTraversal = 0;
65 constexpr int requireAttrFound = 1;
66 constexpr int requireAttrNotFound = 2;
67 
68 /**
69  * @brief Used to get target location code from phal device tree
70  *
71  * @param[in] target current device tree target
72  * @param[out] appPrivData used for accessing|storing from|to application
73  *
74  * @return 0 to continue traverse, non-zero to stop traverse
75  */
76 int pdbgCallbackToGetTgtReqAttrsVal(struct pdbg_target* target,
77                                     void* appPrivData)
78 {
79     using namespace openpower::phal::pdbg;
80 
81     TargetInfo* targetInfo = static_cast<TargetInfo*>(appPrivData);
82 
83     ATTR_PHYS_BIN_PATH_Type physBinPath;
84     /**
85      * TODO: Issue: phal/pdata#16
86      * Should not use direct pdbg api to read attribute. Need to use DT_GET_PROP
87      * macro for bmc app's and this will call libdt-api api but, it will print
88      * "pdbg_target_get_attribute failed" trace if attribute is not found and
89      * this callback will call recursively by using pdbg_target_traverse() until
90      * find expected attribute based on return code from this callback. Because,
91      * need to do target iteration to get actual attribute (ATTR_PHYS_BIN_PATH)
92      * value when device tree target info doesn't know to read attribute from
93      * device tree. So, Due to this error trace user will get confusion while
94      * looking traces. Hence using pdbg api to avoid trace until libdt-api
95      * provides log level setup.
96      */
97     if (!pdbg_target_get_attribute(
98             target, "ATTR_PHYS_BIN_PATH",
99             std::stoi(dtAttr::fapi2::ATTR_PHYS_BIN_PATH_Spec),
100             dtAttr::fapi2::ATTR_PHYS_BIN_PATH_ElementCount, physBinPath))
101     {
102         return continueTgtTraversal;
103     }
104 
105     if (std::memcmp(physBinPath, targetInfo->physBinPath,
106                     sizeof(physBinPath)) != 0)
107     {
108         return continueTgtTraversal;
109     }
110 
111     // Found Target, now collect the required attributes associated to the
112     // target. Incase of any attribute read failure, initialize the data with
113     // default value.
114     try
115     {
116         // Get location code information
117         openpower::phal::pdbg::getLocationCode(target,
118                                                targetInfo->locationCode);
119     }
120     catch (const std::exception& e)
121     {
122         log<level::ERR>(fmt::format("getLocationCode({}): Exception({})",
123                                     pdbg_target_path(target), e.what())
124                             .c_str());
125     }
126 
127     if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, target, targetInfo->physDevPath))
128     {
129         log<level::ERR>(
130             fmt::format("Could not read({}) PHYS_DEV_PATH attribute",
131                         pdbg_target_path(target))
132                 .c_str());
133     }
134 
135     if (DT_GET_PROP(ATTR_MRU_ID, target, targetInfo->mruId))
136     {
137         log<level::ERR>(fmt::format("Could not read({}) ATTR_MRU_ID attribute",
138                                     pdbg_target_path(target))
139                             .c_str());
140     }
141 
142     return requireAttrFound;
143 }
144 
145 /**
146  * @brief Used to get target info (attributes data)
147  *
148  * To get target required attributes value using another attribute value
149  * ("PHYS_BIN_PATH" which is present in same target attributes list) by using
150  * "ipdbg_target_traverse" api because, here we have attribute value only and
151  * doesn't have respective device tree target info to get required attributes
152  * values from it attributes list.
153  *
154  * @param[in] physBinPath to pass PHYS_BIN_PATH value
155  * @param[out] targetInfo to pas buufer to fill with required attributes
156  *
157  * @return true on success otherwise false
158  */
159 bool getTgtReqAttrsVal(const std::vector<uint8_t>& physBinPath,
160                        TargetInfo& targetInfo)
161 {
162     std::memcpy(&targetInfo.physBinPath, physBinPath.data(),
163                 sizeof(targetInfo.physBinPath));
164 
165     int ret = pdbg_target_traverse(NULL, pdbgCallbackToGetTgtReqAttrsVal,
166                                    &targetInfo);
167     if (ret == 0)
168     {
169         log<level::ERR>(fmt::format("Given ATTR_PHYS_BIN_PATH value({}) "
170                                     "not found in phal device tree",
171                                     targetInfo.physBinPath)
172                             .c_str());
173         return false;
174     }
175     else if (ret == requireAttrNotFound)
176     {
177         return false;
178     }
179 
180     return true;
181 }
182 } // namespace phal
183 
184 namespace pel
185 {
186 using namespace phosphor::logging;
187 
188 namespace detail
189 {
190 using json = nlohmann::json;
191 
192 // keys need to be unique so using counter value to generate unique key
193 static int counter = 0;
194 
195 // list of debug traces
196 static std::vector<std::pair<std::string, std::string>> traceLog;
197 
198 /**
199  * @brief Process platform realted boot failure
200  *
201  * @param[in] errInfo - error details
202  */
203 static void processPlatBootError(const ipl_error_info& errInfo);
204 
205 void processLogTraceCallback(void*, const char* fmt, va_list ap)
206 {
207     va_list vap;
208     va_copy(vap, ap);
209     std::vector<char> logData(1 + std::vsnprintf(nullptr, 0, fmt, ap));
210     std::vsnprintf(logData.data(), logData.size(), fmt, vap);
211     va_end(vap);
212     std::string logstr(logData.begin(), logData.end());
213 
214     log<level::INFO>(logstr.c_str());
215 
216     char timeBuf[80];
217     time_t t = time(0);
218     tm myTm{};
219     gmtime_r(&t, &myTm);
220     strftime(timeBuf, 80, "%Y-%m-%d %H:%M:%S", &myTm);
221 
222     // key values need to be unique for PEL
223     // TODO #openbmc/dev/issues/1563
224     // If written to Json no need to worry about unique KEY
225     std::stringstream str;
226     str << std::setfill('0');
227     str << "LOG" << std::setw(3) << counter;
228     str << " " << timeBuf;
229     traceLog.emplace_back(std::make_pair(str.str(), std::move(logstr)));
230     counter++;
231 }
232 
233 /**
234  * @brief GET PEL priority from pHAL priority
235  *
236  * The pHAL callout priority is in different format than PEL format
237  * so, this api is used to return current phal supported priority into
238  * PEL expected format.
239  *
240  * @param[in] phalPriority used to pass phal priority format string
241  *
242  * @return pel priority format string else empty if failure
243  *
244  * @note For "NONE" returning "L" (LOW)
245  */
246 static std::string getPelPriority(const std::string& phalPriority)
247 {
248     const std::map<std::string, std::string> priorityMap = {
249         {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}};
250 
251     auto it = priorityMap.find(phalPriority);
252     if (it == priorityMap.end())
253     {
254         log<level::ERR>(fmt::format("Unsupported phal priority({}) is given "
255                                     "to get pel priority format",
256                                     phalPriority)
257                             .c_str());
258         return "H";
259     }
260 
261     return it->second;
262 }
263 
264 /**
265  * @brief Helper function to create PEL for non functional boot
266  *        processor related failure.
267  * This function adds the BMC code callout as priority 1 to fix
268  * devtree related software issue. Incase the issue still persist
269  * after reboot recommend to replacing the primary processor.
270  */
271 void processNonFunctionalBootProc()
272 {
273     json jsonCalloutDataList;
274     json jsonProcedCallout;
275     // Add BMC code callout
276     jsonProcedCallout["Procedure"] = "BMC0001";
277     jsonProcedCallout["Priority"] = "H";
278     jsonCalloutDataList.emplace_back(std::move(jsonProcedCallout));
279 
280     // get primary processor
281     struct pdbg_target* procTarget;
282     pdbg_for_each_class_target("proc", procTarget)
283     {
284         if (openpower::phal::isPrimaryProc(procTarget))
285             break;
286         procTarget = nullptr;
287     }
288     // check valid primary processor is available
289     if (procTarget == nullptr)
290     {
291         log<level::ERR>(
292             "processNonFunctionalBootProc: fail to get primary processor");
293     }
294     else
295     {
296         try
297         {
298             ATTR_LOCATION_CODE_Type locationCode = {'\0'};
299             // Get location code information
300             openpower::phal::pdbg::getLocationCode(procTarget, locationCode);
301             json jsonProcCallout;
302             jsonProcCallout["LocationCode"] = locationCode;
303             jsonProcCallout["Deconfigured"] = false;
304             jsonProcCallout["Guarded"] = false;
305             jsonProcCallout["Priority"] = "M";
306             jsonCalloutDataList.emplace_back(std::move(jsonProcCallout));
307         }
308         catch (const std::exception& e)
309         {
310             log<level::ERR>(fmt::format("getLocationCode({}): Exception({})",
311                                         pdbg_target_path(procTarget), e.what())
312                                 .c_str());
313         }
314     }
315     // Adding collected phal logs into PEL additional data
316     FFDCData pelAdditionalData;
317     for_each(
318         traceLog.begin(), traceLog.end(),
319         [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void {
320             pelAdditionalData.emplace_back(ele.first, ele.second);
321         });
322     openpower::pel::createErrorPEL(
323         "org.open_power.PHAL.Error.NonFunctionalBootProc", jsonCalloutDataList,
324         pelAdditionalData);
325     // reset trace log and exit
326     reset();
327 }
328 
329 void processIplErrorCallback(const ipl_error_info& errInfo)
330 {
331     log<level::INFO>(
332         fmt::format("processIplErrorCallback: Error type({})", errInfo.type)
333             .c_str());
334 
335     switch (errInfo.type)
336     {
337         case IPL_ERR_OK:
338             // reset trace log and exit
339             reset();
340             break;
341         case IPL_ERR_SBE_BOOT:
342         case IPL_ERR_SBE_CHIPOP:
343             // handle SBE related failures.
344             processSbeBootError();
345             break;
346         case IPL_ERR_HWP:
347             // Handle hwp failure
348             processBootError(false);
349             break;
350         case IPL_ERR_PLAT:
351             processPlatBootError(errInfo);
352             break;
353         case IPL_ERR_PRI_PROC_NON_FUNC:
354             // Handle non functional boot processor error.
355             processNonFunctionalBootProc();
356             break;
357         default:
358             createPEL("org.open_power.PHAL.Error.Boot");
359             // reset trace log and exit
360             reset();
361             break;
362     }
363 }
364 
365 /**
366  * @brief addPlanarCallout
367  *
368  * This function will add a json for planar callout in the input json list.
369  * The caller can pass this json list into createErrorPEL to apply the callout.
370  *
371  * @param[in,out] jsonCalloutDataList - json list where callout json will be
372  *                  emplaced
373  * @param[in] priority - string indicating priority.
374  */
375 static void addPlanarCallout(json& jsonCalloutDataList,
376                              const std::string& priority)
377 {
378     json jsonCalloutData;
379 
380     // Inventory path for planar
381     jsonCalloutData["InventoryPath"] =
382         "/xyz/openbmc_project/inventory/system/chassis/motherboard";
383     jsonCalloutData["Deconfigured"] = false;
384     jsonCalloutData["Guarded"] = false;
385     jsonCalloutData["Priority"] = priority;
386 
387     jsonCalloutDataList.emplace_back(jsonCalloutData);
388 }
389 
390 void processBootErrorHelper(FFDC* ffdc, const std::string& ffdc_prefix)
391 {
392     log<level::INFO>("processBootErrorHelper ");
393     try
394     {
395         log<level::INFO>(
396             fmt::format("PHAL FFDC: Return Message[{}]", ffdc->message)
397                 .c_str());
398 
399         // To store callouts details in json format as per pel expectation.
400         json jsonCalloutDataList;
401         jsonCalloutDataList = json::array();
402 
403         // To store phal trace and other additional data about ffdc.
404         FFDCData pelAdditionalData;
405 
406         if (ffdc->ffdc_type == FFDC_TYPE_HWP)
407         {
408             std::string keyWithPrefix(ffdc_prefix + "RC");
409             // Adding hardware procedures return code details
410             pelAdditionalData.emplace_back(keyWithPrefix,
411                                            ffdc->hwp_errorinfo.rc);
412             keyWithPrefix = ffdc_prefix + "RC_DESC";
413             pelAdditionalData.emplace_back(keyWithPrefix,
414                                            ffdc->hwp_errorinfo.rc_desc);
415 
416             // Adding hardware procedures required ffdc data for debug
417             for_each(ffdc->hwp_errorinfo.ffdcs_data.begin(),
418                      ffdc->hwp_errorinfo.ffdcs_data.end(),
419                      [&pelAdditionalData, &ffdc_prefix](
420                          std::pair<std::string, std::string>& ele) -> void {
421                          std::string keyWithPrefix(ffdc_prefix + "FFDC_");
422                          keyWithPrefix.append(ele.first);
423 
424                          pelAdditionalData.emplace_back(keyWithPrefix,
425                                                         ele.second);
426                      });
427 
428             // Adding hardware callout details
429             int calloutCount = 0;
430             for_each(
431                 ffdc->hwp_errorinfo.hwcallouts.begin(),
432                 ffdc->hwp_errorinfo.hwcallouts.end(),
433                 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList,
434                  &ffdc_prefix](const HWCallout& hwCallout) -> void {
435                     calloutCount++;
436                     std::stringstream keyPrefix;
437                     keyPrefix << ffdc_prefix << "HW_CO_" << std::setfill('0')
438                               << std::setw(2) << calloutCount << "_";
439 
440                     pelAdditionalData.emplace_back(
441                         std::string(keyPrefix.str()).append("HW_ID"),
442                         hwCallout.hwid);
443 
444                     pelAdditionalData.emplace_back(
445                         std::string(keyPrefix.str()).append("PRIORITY"),
446                         hwCallout.callout_priority);
447 
448                     // Log target details only if entity path is
449                     // available. For example target entity path will not
450                     // be available for non-hwp clock failure.
451                     if (!hwCallout.target_entity_path.empty())
452                     {
453                         phal::TargetInfo targetInfo;
454                         phal::getTgtReqAttrsVal(hwCallout.target_entity_path,
455                                                 targetInfo);
456 
457                         std::string locationCode =
458                             std::string(targetInfo.locationCode);
459                         pelAdditionalData.emplace_back(
460                             std::string(keyPrefix.str()).append("LOC_CODE"),
461                             locationCode);
462 
463                         std::string physPath =
464                             std::string(targetInfo.physDevPath);
465                         pelAdditionalData.emplace_back(
466                             std::string(keyPrefix.str()).append("PHYS_PATH"),
467                             physPath);
468                     }
469 
470                     pelAdditionalData.emplace_back(
471                         std::string(keyPrefix.str()).append("CLK_POS"),
472                         std::to_string(hwCallout.clkPos));
473 
474                     pelAdditionalData.emplace_back(
475                         std::string(keyPrefix.str()).append("CALLOUT_PLANAR"),
476                         (hwCallout.isPlanarCallout == true ? "true" : "false"));
477 
478                     std::string pelPriority =
479                         getPelPriority(hwCallout.callout_priority);
480 
481                     if (hwCallout.isPlanarCallout)
482                     {
483                         addPlanarCallout(jsonCalloutDataList, pelPriority);
484                     }
485                 });
486 
487             // Adding CDG (callout, deconfigure and guard) targets details
488             calloutCount = 0;
489             for_each(
490                 ffdc->hwp_errorinfo.cdg_targets.begin(),
491                 ffdc->hwp_errorinfo.cdg_targets.end(),
492                 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList,
493                  &ffdc_prefix](const CDG_Target& cdg_tgt) -> void {
494                     calloutCount++;
495                     std::stringstream keyPrefix;
496                     keyPrefix << ffdc_prefix << "CDG_TGT_" << std::setfill('0')
497                               << std::setw(2) << calloutCount << "_";
498 
499                     phal::TargetInfo targetInfo;
500                     targetInfo.deconfigure = cdg_tgt.deconfigure;
501 
502                     phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path,
503                                             targetInfo);
504 
505                     std::string locationCode =
506                         std::string(targetInfo.locationCode);
507                     pelAdditionalData.emplace_back(
508                         std::string(keyPrefix.str()).append("LOC_CODE"),
509                         locationCode);
510                     std::string physPath = std::string(targetInfo.physDevPath);
511                     pelAdditionalData.emplace_back(
512                         std::string(keyPrefix.str()).append("PHYS_PATH"),
513                         physPath);
514 
515                     pelAdditionalData.emplace_back(
516                         std::string(keyPrefix.str()).append("CO_REQ"),
517                         (cdg_tgt.callout == true ? "true" : "false"));
518 
519                     pelAdditionalData.emplace_back(
520                         std::string(keyPrefix.str()).append("CO_PRIORITY"),
521                         cdg_tgt.callout_priority);
522 
523                     pelAdditionalData.emplace_back(
524                         std::string(keyPrefix.str()).append("DECONF_REQ"),
525                         (cdg_tgt.deconfigure == true ? "true" : "false"));
526 
527                     pelAdditionalData.emplace_back(
528                         std::string(keyPrefix.str()).append("GUARD_REQ"),
529                         (cdg_tgt.guard == true ? "true" : "false"));
530 
531                     pelAdditionalData.emplace_back(
532                         std::string(keyPrefix.str()).append("GUARD_TYPE"),
533                         cdg_tgt.guard_type);
534 
535                     json jsonCalloutData;
536                     jsonCalloutData["LocationCode"] = locationCode;
537                     std::string pelPriority =
538                         getPelPriority(cdg_tgt.callout_priority);
539                     jsonCalloutData["Priority"] = pelPriority;
540 
541                     if (targetInfo.mruId != 0)
542                     {
543                         jsonCalloutData["MRUs"] = json::array({
544                             {{"ID", targetInfo.mruId},
545                              {"Priority", pelPriority}},
546                         });
547                     }
548                     jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
549                     jsonCalloutData["Guarded"] = cdg_tgt.guard;
550                     jsonCalloutData["GuardType"] = cdg_tgt.guard_type;
551                     jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path;
552 
553                     jsonCalloutDataList.emplace_back(jsonCalloutData);
554                 });
555             // Adding procedure callout
556             calloutCount = 0;
557             for_each(
558                 ffdc->hwp_errorinfo.procedures_callout.begin(),
559                 ffdc->hwp_errorinfo.procedures_callout.end(),
560                 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList,
561                  &ffdc_prefix](const ProcedureCallout& procCallout) -> void {
562                     calloutCount++;
563                     std::stringstream keyPrefix;
564                     keyPrefix << ffdc_prefix << "PROC_CO_" << std::setfill('0')
565                               << std::setw(2) << calloutCount << "_";
566 
567                     pelAdditionalData.emplace_back(
568                         std::string(keyPrefix.str()).append("PRIORITY"),
569                         procCallout.callout_priority);
570 
571                     pelAdditionalData.emplace_back(
572                         std::string(keyPrefix.str()).append("MAINT_PROCEDURE"),
573                         procCallout.proc_callout);
574 
575                     json jsonCalloutData;
576                     jsonCalloutData["Procedure"] = procCallout.proc_callout;
577                     std::string pelPriority =
578                         getPelPriority(procCallout.callout_priority);
579                     jsonCalloutData["Priority"] = pelPriority;
580                     jsonCalloutDataList.emplace_back(jsonCalloutData);
581                 });
582         }
583         else if ((ffdc->ffdc_type != FFDC_TYPE_NONE) &&
584                  (ffdc->ffdc_type != FFDC_TYPE_UNSUPPORTED))
585         {
586             log<level::ERR>(
587                 fmt::format("Unsupported phal FFDC type to create PEL. "
588                             "MSG: {}",
589                             ffdc->message)
590                     .c_str());
591         }
592 
593         // Adding collected phal logs into PEL additional data
594         for_each(traceLog.begin(), traceLog.end(),
595                  [&pelAdditionalData](
596                      std::pair<std::string, std::string>& ele) -> void {
597                      pelAdditionalData.emplace_back(ele.first, ele.second);
598                  });
599 
600         // TODO: #ibm-openbmc/dev/issues/2595 : Once enabled this support,
601         // callout details is not required to sort in H,M and L orders which
602         // are expected by pel because, pel will take care for sorting callouts
603         // based on priority so, now adding support to send callout in order
604         // i.e High -> Medium -> Low.
605         std::sort(
606             jsonCalloutDataList.begin(), jsonCalloutDataList.end(),
607             [](const json& aEle, const json& bEle) -> bool {
608                 // Considering b element having higher priority than a element
609                 // or Both element will be same priorty (to keep same order
610                 // which are given by phal when two callouts are having same
611                 // priority)
612                 if (((aEle["Priority"] == "M") && (bEle["Priority"] == "H")) ||
613                     ((aEle["Priority"] == "L") &&
614                      ((bEle["Priority"] == "H") ||
615                       (bEle["Priority"] == "M"))) ||
616                     (aEle["Priority"] == bEle["Priority"]))
617                 {
618                     return false;
619                 }
620 
621                 // Considering a element having higher priority than b element
622                 return true;
623             });
624         openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot",
625                                        jsonCalloutDataList, pelAdditionalData);
626     }
627     catch (const std::exception& ex)
628     {
629         reset();
630         throw ex;
631     }
632     reset();
633 }
634 
635 void processPlatBootError(const ipl_error_info& errInfo)
636 {
637     log<level::INFO>("processPlatBootError ");
638 
639     // Collecting ffdc details from phal
640     FFDC* ffdc = reinterpret_cast<FFDC*>(errInfo.private_data);
641     try
642     {
643         processBootErrorHelper(ffdc, "PLAT_");
644     }
645     catch (const std::exception& ex)
646     {
647         log<level::ERR>(
648             fmt::format("processPlatBootError: exception({})", ex.what())
649                 .c_str());
650         throw ex;
651     }
652 }
653 
654 void processBootError(bool status)
655 {
656     log<level::INFO>(
657         fmt::format("processBootError: status({})", status).c_str());
658 
659     try
660     {
661         // return If no failure during hwp execution
662         if (status)
663             return;
664 
665         // Collecting ffdc details from phal
666         FFDC ffdc;
667         libekb_get_ffdc(ffdc);
668 
669         processBootErrorHelper(&ffdc, "HWP_");
670     }
671     catch (const std::exception& ex)
672     {
673         log<level::ERR>(
674             fmt::format("processBootError: exception({})", ex.what()).c_str());
675         throw ex;
676     }
677 }
678 
679 void processSbeBootError()
680 {
681     log<level::INFO>("processSbeBootError : Entered ");
682 
683     using namespace openpower::phal::sbe;
684 
685     // To store phal trace and other additional data about ffdc.
686     FFDCData pelAdditionalData;
687 
688     // Adding collected phal logs into PEL additional data
689     for_each(
690         traceLog.begin(), traceLog.end(),
691         [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void {
692             pelAdditionalData.emplace_back(ele.first, ele.second);
693         });
694 
695     // reset the trace log and counter
696     reset();
697 
698     // get primary processor to collect FFDC/Dump information.
699     struct pdbg_target* procTarget;
700     pdbg_for_each_class_target("proc", procTarget)
701     {
702         if (openpower::phal::isPrimaryProc(procTarget))
703             break;
704         procTarget = nullptr;
705     }
706     // check valid primary processor is available
707     if (procTarget == nullptr)
708     {
709         log<level::ERR>("processSbeBootError: fail to get primary processor");
710         // Add BMC code callout and create PEL
711         json jsonCalloutDataList;
712         jsonCalloutDataList = json::array();
713         json jsonCalloutData;
714         jsonCalloutData["Procedure"] = "BMC0001";
715         jsonCalloutData["Priority"] = "H";
716         jsonCalloutDataList.emplace_back(jsonCalloutData);
717         openpower::pel::createErrorPEL(
718             "org.open_power.Processor.Error.SbeBootFailure",
719             jsonCalloutDataList);
720         return;
721     }
722     // SBE error object.
723     sbeError_t sbeError;
724     bool dumpIsRequired = false;
725 
726     try
727     {
728         // Capture FFDC information on primary processor
729         sbeError = captureFFDC(procTarget);
730     }
731     catch (const phalError_t& phalError)
732     {
733         // Fail to collect FFDC information , trigger Dump
734         log<level::ERR>(
735             fmt::format("captureFFDC: Exception({})", phalError.what())
736                 .c_str());
737         dumpIsRequired = true;
738     }
739 
740     std::string event;
741 
742     if ((sbeError.errType() == SBE_FFDC_NO_DATA) ||
743         (sbeError.errType() == SBE_CMD_TIMEOUT) || (dumpIsRequired))
744     {
745         event = "org.open_power.Processor.Error.SbeBootTimeout";
746         dumpIsRequired = true;
747     }
748     else
749     {
750         event = "org.open_power.Processor.Error.SbeBootFailure";
751     }
752     // SRC6 : [0:15] chip position
753     uint32_t index = pdbg_target_index(procTarget);
754     pelAdditionalData.emplace_back("SRC6", std::to_string(index << 16));
755     // Create SBE Error with FFDC data.
756     auto logId =
757         createSbeErrorPEL(event, sbeError, pelAdditionalData, procTarget);
758 
759     if (dumpIsRequired)
760     {
761         using namespace openpower::phal::dump;
762         DumpParameters dumpParameters = {logId, index, SBE_DUMP_TIMEOUT,
763                                          DumpType::SBE};
764         try
765         {
766             requestDump(dumpParameters);
767         }
768         catch (const std::runtime_error& e)
769         {
770             // Allowing call back to handle the error gracefully.
771             log<level::ERR>("Dump collection failed");
772             // TODO revist error handling.
773         }
774     }
775 }
776 
777 void reset()
778 {
779     // reset the trace log and counter
780     traceLog.clear();
781     counter = 0;
782 }
783 
784 void pDBGLogTraceCallbackHelper(int, const char* fmt, va_list ap)
785 {
786     processLogTraceCallback(NULL, fmt, ap);
787 }
788 } // namespace detail
789 
790 static inline uint8_t getLogLevelFromEnv(const char* env, const uint8_t dValue)
791 {
792     auto logLevel = dValue;
793     try
794     {
795         if (const char* env_p = std::getenv(env))
796         {
797             logLevel = std::stoi(env_p);
798         }
799     }
800     catch (const std::exception& e)
801     {
802         log<level::ERR>(("Conversion Failure"), entry("ENVIRONMENT=%s", env),
803                         entry("EXCEPTION=%s", e.what()));
804     }
805     return logLevel;
806 }
807 
808 void addBootErrorCallbacks()
809 {
810     // Get individual phal repos log level from environment variable
811     // and update the  log level.
812     pdbg_set_loglevel(getLogLevelFromEnv("PDBG_LOG", PDBG_INFO));
813     libekb_set_loglevel(getLogLevelFromEnv("LIBEKB_LOG", LIBEKB_LOG_IMP));
814     ipl_set_loglevel(getLogLevelFromEnv("IPL_LOG", IPL_INFO));
815 
816     // add callback for debug traces
817     pdbg_set_logfunc(detail::pDBGLogTraceCallbackHelper);
818     libekb_set_logfunc(detail::processLogTraceCallback, NULL);
819     ipl_set_logfunc(detail::processLogTraceCallback, NULL);
820 
821     // add callback for ipl failures
822     ipl_set_error_callback_func(detail::processIplErrorCallback);
823 }
824 
825 } // namespace pel
826 } // namespace openpower
827