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