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