1 extern "C"
2 {
3 #include <libpdbg.h>
4 }
5 
6 #include "fapi_data_process.hpp"
7 
8 #include <attributes_info.H>
9 #include <fmt/format.h>
10 #include <libphal.H>
11 #include <phal_exception.H>
12 
13 #include <phosphor-logging/elog.hpp>
14 
15 #include <algorithm>
16 #include <cstdlib>
17 #include <cstring>
18 #include <iomanip>
19 #include <list>
20 #include <map>
21 #include <sstream>
22 #include <string>
23 
24 namespace openpower
25 {
26 namespace pels
27 {
28 namespace phal
29 {
30 
31 using namespace phosphor::logging;
32 using namespace openpower::phal::exception;
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     using namespace openpower::phal::pdbg;
79 
80     TargetInfo* targetInfo = static_cast<TargetInfo*>(appPrivData);
81 
82     ATTR_PHYS_BIN_PATH_Type physBinPath;
83     /**
84      * TODO: Issue: phal/pdata#16
85      * Should not use direct pdbg api to read attribute. Need to use DT_GET_PROP
86      * macro for bmc app's and this will call libdt-api api but, it will print
87      * "pdbg_target_get_attribute failed" trace if attribute is not found and
88      * this callback will call recursively by using pdbg_target_traverse() until
89      * find expected attribute based on return code from this callback. Because,
90      * need to do target iteration to get actual attribute (ATTR_PHYS_BIN_PATH)
91      * value when device tree target info doesn't know to read attribute from
92      * device tree. So, Due to this error trace user will get confusion while
93      * looking traces. Hence using pdbg api to avoid trace until libdt-api
94      * provides log level setup.
95      */
96     if (!pdbg_target_get_attribute(
97             target, "ATTR_PHYS_BIN_PATH",
98             std::stoi(dtAttr::fapi2::ATTR_PHYS_BIN_PATH_Spec),
99             dtAttr::fapi2::ATTR_PHYS_BIN_PATH_ElementCount, physBinPath))
100     {
101         return continueTgtTraversal;
102     }
103 
104     if (std::memcmp(physBinPath, targetInfo->physBinPath,
105                     sizeof(physBinPath)) != 0)
106     {
107         return continueTgtTraversal;
108     }
109 
110     // Found Target, now collect the required attributes associated to the
111     // target. Incase of any attribute read failure, initialize the data with
112     // default value.
113 
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 message and continue with default data
123         log<level::ERR>(fmt::format("getLocationCode({}): Exception({})",
124                                     pdbg_target_path(target), e.what())
125                             .c_str());
126     }
127 
128     if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, target, targetInfo->physDevPath))
129     {
130         log<level::ERR>(
131             fmt::format("Could not read({}) PHYS_DEV_PATH attribute",
132                         pdbg_target_path(target))
133                 .c_str());
134     }
135 
136     if (DT_GET_PROP(ATTR_MRU_ID, target, targetInfo->mruId))
137     {
138         log<level::ERR>(fmt::format("Could not read({}) ATTR_MRU_ID attribute",
139                                     pdbg_target_path(target))
140                             .c_str());
141     }
142 
143     return requireAttrFound;
144 }
145 
146 /**
147  * @brief Used to get target info (attributes data)
148  *
149  * To get target required attributes value using another attribute value
150  * ("PHYS_BIN_PATH" which is present in same target attributes list) by using
151  * "ipdbg_target_traverse" api because, here we have attribute value only and
152  * doesn't have respective device tree target info to get required attributes
153  * values from it attributes list.
154  *
155  * @param[in] physBinPath to pass PHYS_BIN_PATH value
156  * @param[out] targetInfo to pas buufer to fill with required attributes
157  *
158  * @return true on success otherwise false
159  */
160 bool getTgtReqAttrsVal(const std::vector<uint8_t>& physBinPath,
161                        TargetInfo& targetInfo)
162 {
163     std::memcpy(&targetInfo.physBinPath, physBinPath.data(),
164                 sizeof(targetInfo.physBinPath));
165 
166     int ret = pdbg_target_traverse(NULL, pdbgCallbackToGetTgtReqAttrsVal,
167                                    &targetInfo);
168     if (ret == 0)
169     {
170         log<level::ERR>(fmt::format("Given ATTR_PHYS_BIN_PATH value({:02x}) "
171                                     "not found in phal device tree",
172                                     fmt::join(targetInfo.physBinPath, ""))
173                             .c_str());
174         return false;
175     }
176     else if (ret == requireAttrNotFound)
177     {
178         return false;
179     }
180 
181     return true;
182 }
183 
184 /**
185  * @brief GET PEL priority from pHAL priority
186  *
187  * The pHAL callout priority is in different format than PEL format
188  * so, this api is used to return current phal supported priority into
189  * PEL expected format.
190  *
191  * @param[in] phalPriority used to pass phal priority format string
192  *
193  * @return pel priority format string else empty if failure
194  *
195  * @note For "NONE" returning "L" (LOW)
196  */
197 static std::string getPelPriority(const std::string& phalPriority)
198 {
199     const std::map<std::string, std::string> priorityMap = {
200         {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}};
201 
202     auto it = priorityMap.find(phalPriority);
203     if (it == priorityMap.end())
204     {
205         log<level::ERR>(fmt::format("Unsupported phal priority({}) is given "
206                                     "to get pel priority format",
207                                     phalPriority)
208                             .c_str());
209         return "H";
210     }
211 
212     return it->second;
213 }
214 
215 /**
216  * @brief addPlanarCallout
217  *
218  * This function will add a json for planar callout in the input json list.
219  * The caller can pass this json list into createErrorPEL to apply the callout.
220  *
221  * @param[in,out] jsonCalloutDataList - json list where callout json will be
222  *                  emplaced
223  * @param[in] priority - string indicating priority.
224  */
225 static void addPlanarCallout(json& jsonCalloutDataList,
226                              const std::string& priority)
227 {
228     json jsonCalloutData;
229 
230     // Inventory path for planar
231     jsonCalloutData["InventoryPath"] =
232         "/xyz/openbmc_project/inventory/system/chassis/motherboard";
233     jsonCalloutData["Deconfigured"] = false;
234     jsonCalloutData["Guarded"] = false;
235     jsonCalloutData["Priority"] = priority;
236 
237     jsonCalloutDataList.emplace_back(jsonCalloutData);
238 }
239 
240 /**
241  * @brief processClockInfoErrorHelper
242  *
243  * Creates informational PEL for spare clock failure
244  *
245  * @param[in]  ffdc FFDC data capturd by the HWP
246  * @param[out] pelJSONFmtCalloutDataList used to store collected callout
247  *            data into pel expected format
248  * @param[out] ffdcUserData used to store additional ffdc user data to
249  *              provided by the SBE FFDC packet.
250  *
251  * @return NULL
252  *
253  **/
254 void processClockInfoErrorHelper(const FFDC& ffdc,
255                                  json& pelJSONFmtCalloutDataList,
256                                  FFDCData& ffdcUserData)
257 {
258     log<level::INFO>(
259         fmt::format("processClockInfoErrorHelper: FFDC Message[{}]",
260                     ffdc.message)
261             .c_str());
262 
263     // Adding hardware procedures return code details
264     ffdcUserData.emplace_back("HWP_RC", ffdc.hwp_errorinfo.rc);
265     ffdcUserData.emplace_back("HWP_RC_DESC", ffdc.hwp_errorinfo.rc_desc);
266 
267     // Adding hardware procedures required ffdc data for debug
268     for_each(ffdc.hwp_errorinfo.ffdcs_data.cbegin(),
269              ffdc.hwp_errorinfo.ffdcs_data.cend(),
270              [&ffdcUserData](
271                  const std::pair<std::string, std::string>& ele) -> void {
272                  std::string keyWithPrefix("HWP_FFDC_");
273                  keyWithPrefix.append(ele.first);
274 
275                  ffdcUserData.emplace_back(keyWithPrefix, ele.second);
276              });
277     // get clock position information
278     auto clk_pos = 0xFF; // Invalid position.
279     for (auto& hwCallout : ffdc.hwp_errorinfo.hwcallouts)
280     {
281         if ((hwCallout.hwid == "PROC_REF_CLOCK") ||
282             (hwCallout.hwid == "PCI_REF_CLOCK"))
283         {
284             clk_pos = hwCallout.clkPos;
285             break;
286         }
287     }
288     // Adding CDG (Only deconfigure) targets details
289     for_each(ffdc.hwp_errorinfo.cdg_targets.begin(),
290              ffdc.hwp_errorinfo.cdg_targets.end(),
291              [&ffdcUserData, &pelJSONFmtCalloutDataList,
292               clk_pos](const CDG_Target& cdg_tgt) -> void {
293                  json jsonCalloutData;
294                  std::string pelPriority = "H";
295                  jsonCalloutData["Priority"] = pelPriority; // Not used
296                  jsonCalloutData["SymbolicFRU"] = "REFCLK" +
297                                                   std::to_string(clk_pos);
298                  jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
299                  jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path;
300                  pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData);
301              });
302 }
303 
304 void convertFAPItoPELformat(FFDC& ffdc, json& pelJSONFmtCalloutDataList,
305                             FFDCData& ffdcUserData)
306 {
307     if (ffdc.ffdc_type == FFDC_TYPE_SPARE_CLOCK_INFO)
308     {
309         processClockInfoErrorHelper(ffdc, pelJSONFmtCalloutDataList,
310                                     ffdcUserData);
311         return;
312     }
313 
314     if (ffdc.ffdc_type == FFDC_TYPE_HWP)
315     {
316         // Adding hardware procedures return code details
317         ffdcUserData.emplace_back("HWP_RC", ffdc.hwp_errorinfo.rc);
318         ffdcUserData.emplace_back("HWP_RC_DESC", ffdc.hwp_errorinfo.rc_desc);
319 
320         // Adding hardware procedures required ffdc data for debug
321         for_each(
322             ffdc.hwp_errorinfo.ffdcs_data.begin(),
323             ffdc.hwp_errorinfo.ffdcs_data.end(),
324             [&ffdcUserData](std::pair<std::string, std::string>& ele) -> void {
325                 std::string keyWithPrefix("HWP_FFDC_");
326                 keyWithPrefix.append(ele.first);
327 
328                 ffdcUserData.emplace_back(keyWithPrefix, ele.second);
329             });
330 
331         // Adding hardware callout details
332         int calloutCount = 0;
333         for_each(
334             ffdc.hwp_errorinfo.hwcallouts.begin(),
335             ffdc.hwp_errorinfo.hwcallouts.end(),
336             [&ffdcUserData, &calloutCount,
337              &pelJSONFmtCalloutDataList](const HWCallout& hwCallout) -> void {
338                 calloutCount++;
339                 std::stringstream keyPrefix;
340                 keyPrefix << "HWP_HW_CO_" << std::setfill('0') << std::setw(2)
341                           << calloutCount << "_";
342 
343                 ffdcUserData.emplace_back(
344                     std::string(keyPrefix.str()).append("HW_ID"),
345                     hwCallout.hwid);
346 
347                 ffdcUserData.emplace_back(
348                     std::string(keyPrefix.str()).append("PRIORITY"),
349                     hwCallout.callout_priority);
350 
351                 phal::TargetInfo targetInfo;
352                 phal::getTgtReqAttrsVal(hwCallout.target_entity_path,
353                                         targetInfo);
354 
355                 std::string locationCode = std::string(targetInfo.locationCode);
356                 ffdcUserData.emplace_back(
357                     std::string(keyPrefix.str()).append("LOC_CODE"),
358                     locationCode);
359 
360                 std::string physPath = std::string(targetInfo.physDevPath);
361                 ffdcUserData.emplace_back(
362                     std::string(keyPrefix.str()).append("PHYS_PATH"), physPath);
363 
364                 ffdcUserData.emplace_back(
365                     std::string(keyPrefix.str()).append("CLK_POS"),
366                     std::to_string(hwCallout.clkPos));
367 
368                 ffdcUserData.emplace_back(
369                     std::string(keyPrefix.str()).append("CALLOUT_PLANAR"),
370                     (hwCallout.isPlanarCallout == true ? "true" : "false"));
371 
372                 std::string pelPriority =
373                     getPelPriority(hwCallout.callout_priority);
374 
375                 if (hwCallout.isPlanarCallout)
376                 {
377                     addPlanarCallout(pelJSONFmtCalloutDataList, pelPriority);
378                 }
379             });
380 
381         // Adding CDG (callout, deconfigure and guard) targets details
382         calloutCount = 0;
383         for_each(
384             ffdc.hwp_errorinfo.cdg_targets.begin(),
385             ffdc.hwp_errorinfo.cdg_targets.end(),
386             [&ffdcUserData, &calloutCount,
387              &pelJSONFmtCalloutDataList](const CDG_Target& cdg_tgt) -> void {
388                 calloutCount++;
389                 std::stringstream keyPrefix;
390                 keyPrefix << "HWP_CDG_TGT_" << std::setfill('0') << std::setw(2)
391                           << calloutCount << "_";
392 
393                 phal::TargetInfo targetInfo;
394                 targetInfo.deconfigure = cdg_tgt.deconfigure;
395 
396                 phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path, targetInfo);
397 
398                 std::string locationCode = std::string(targetInfo.locationCode);
399                 ffdcUserData.emplace_back(
400                     std::string(keyPrefix.str()).append("LOC_CODE"),
401                     locationCode);
402                 std::string physPath = std::string(targetInfo.physDevPath);
403                 ffdcUserData.emplace_back(
404                     std::string(keyPrefix.str()).append("PHYS_PATH"), physPath);
405 
406                 ffdcUserData.emplace_back(
407                     std::string(keyPrefix.str()).append("CO_REQ"),
408                     (cdg_tgt.callout == true ? "true" : "false"));
409 
410                 ffdcUserData.emplace_back(
411                     std::string(keyPrefix.str()).append("CO_PRIORITY"),
412                     cdg_tgt.callout_priority);
413 
414                 ffdcUserData.emplace_back(
415                     std::string(keyPrefix.str()).append("DECONF_REQ"),
416                     (cdg_tgt.deconfigure == true ? "true" : "false"));
417 
418                 ffdcUserData.emplace_back(
419                     std::string(keyPrefix.str()).append("GUARD_REQ"),
420                     (cdg_tgt.guard == true ? "true" : "false"));
421 
422                 ffdcUserData.emplace_back(
423                     std::string(keyPrefix.str()).append("GUARD_TYPE"),
424                     cdg_tgt.guard_type);
425 
426                 json jsonCalloutData;
427                 jsonCalloutData["LocationCode"] = locationCode;
428                 std::string pelPriority =
429                     getPelPriority(cdg_tgt.callout_priority);
430                 jsonCalloutData["Priority"] = pelPriority;
431 
432                 if (targetInfo.mruId != 0)
433                 {
434                     jsonCalloutData["MRUs"] = json::array({
435                         {{"ID", targetInfo.mruId}, {"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"] = cdg_tgt.target_entity_path;
442 
443                 pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData);
444             });
445 
446         // Adding procedure callout
447         calloutCount = 0;
448         for_each(ffdc.hwp_errorinfo.procedures_callout.begin(),
449                  ffdc.hwp_errorinfo.procedures_callout.end(),
450                  [&ffdcUserData, &calloutCount, &pelJSONFmtCalloutDataList](
451                      const ProcedureCallout& procCallout) -> void {
452                      calloutCount++;
453                      std::stringstream keyPrefix;
454                      keyPrefix << "HWP_PROC_CO_" << std::setfill('0')
455                                << std::setw(2) << calloutCount << "_";
456                      ffdcUserData.emplace_back(
457                          std::string(keyPrefix.str()).append("PRIORITY"),
458                          procCallout.callout_priority);
459                      ffdcUserData.emplace_back(
460                          std::string(keyPrefix.str()).append("MAINT_PROCEDURE"),
461                          procCallout.proc_callout);
462                      json jsonCalloutData;
463                      jsonCalloutData["Procedure"] = procCallout.proc_callout;
464                      std::string pelPriority =
465                          getPelPriority(procCallout.callout_priority);
466                      jsonCalloutData["Priority"] = pelPriority;
467                      pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData);
468                  });
469     }
470     else if ((ffdc.ffdc_type != FFDC_TYPE_NONE) &&
471              (ffdc.ffdc_type != FFDC_TYPE_UNSUPPORTED))
472     {
473         log<level::ERR>(fmt::format("Unsupported phal FFDC type to create PEL. "
474                                     "MSG: {}",
475                                     ffdc.message)
476                             .c_str());
477     }
478 }
479 
480 } // namespace phal
481 } // namespace pels
482 } // namespace openpower
483