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