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