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