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