1 extern "C"
2 {
3 #include <libpdbg.h>
4 }
5 
6 #include "fapi_data_process.hpp"
7 
8 #include <attributes_info.H>
9 #include <libphal.H>
10 #include <phal_exception.H>
11 
12 #include <phosphor-logging/elog.hpp>
13 #include <phosphor-logging/lg2.hpp>
14 
15 #include <algorithm>
16 #include <cstdlib>
17 #include <cstring>
18 #include <format>
19 #include <iomanip>
20 #include <list>
21 #include <map>
22 #include <sstream>
23 #include <string>
24 
25 namespace openpower
26 {
27 namespace pels
28 {
29 namespace phal
30 {
31 
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 
TargetInfoopenpower::pels::phal::TargetInfo47     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  */
pdbgCallbackToGetTgtReqAttrsVal(struct pdbg_target * target,void * appPrivData)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         lg2::error("getLocationCode({TARGET_PATH}): Exception({EXCEPTION})",
124                    "TARGET_PATH", pdbg_target_path(target), "EXCEPTION", e);
125     }
126 
127     if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, target, targetInfo->physDevPath))
128     {
129         lg2::error("Could not read({TARGET_PATH}) PHYS_DEV_PATH attribute",
130                    "TARGET_PATH", pdbg_target_path(target));
131     }
132 
133     if (DT_GET_PROP(ATTR_MRU_ID, target, targetInfo->mruId))
134     {
135         lg2::error("Could not read({TARGET_PATH}) ATTR_MRU_ID attribute",
136                    "TARGET_PATH", pdbg_target_path(target));
137     }
138 
139     return requireAttrFound;
140 }
141 
142 /**
143  * @brief Used to get target info (attributes data)
144  *
145  * To get target required attributes value using another attribute value
146  * ("PHYS_BIN_PATH" which is present in same target attributes list) by using
147  * "ipdbg_target_traverse" api because, here we have attribute value only and
148  * doesn't have respective device tree target info to get required attributes
149  * values from it attributes list.
150  *
151  * @param[in] physBinPath to pass PHYS_BIN_PATH value
152  * @param[out] targetInfo to pas buufer to fill with required attributes
153  *
154  * @return true on success otherwise false
155  */
getTgtReqAttrsVal(const std::vector<uint8_t> & physBinPath,TargetInfo & targetInfo)156 bool getTgtReqAttrsVal(const std::vector<uint8_t>& physBinPath,
157                        TargetInfo& targetInfo)
158 {
159     std::memcpy(&targetInfo.physBinPath, physBinPath.data(),
160                 sizeof(targetInfo.physBinPath));
161 
162     int ret = pdbg_target_traverse(NULL, pdbgCallbackToGetTgtReqAttrsVal,
163                                    &targetInfo);
164     if (ret == 0)
165     {
166         std::string fmt;
167         for (auto value : targetInfo.physBinPath)
168         {
169             fmt += std::format("{:02X} ", value);
170         }
171 
172         lg2::error(
173             "Given ATTR_PHYS_BIN_PATH value {ATTR_PHYS_BIN_PATH} not found in phal device tree",
174             "ATTR_PHYS_BIN_PATH", fmt);
175         return false;
176     }
177     else if (ret == requireAttrNotFound)
178     {
179         return false;
180     }
181 
182     return true;
183 }
184 
185 /**
186  * @brief GET PEL priority from pHAL priority
187  *
188  * The pHAL callout priority is in different format than PEL format
189  * so, this api is used to return current phal supported priority into
190  * PEL expected format.
191  *
192  * @param[in] phalPriority used to pass phal priority format string
193  *
194  * @return pel priority format string else empty if failure
195  *
196  * @note For "NONE" returning "L" (LOW)
197  */
getPelPriority(const std::string & phalPriority)198 static std::string getPelPriority(const std::string& phalPriority)
199 {
200     const std::map<std::string, std::string> priorityMap = {
201         {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}};
202 
203     auto it = priorityMap.find(phalPriority);
204     if (it == priorityMap.end())
205     {
206         lg2::error(
207             "Unsupported phal priority({PHAL_PRIORITY}) is given to get pel priority format",
208             "PHAL_PRIORITY", phalPriority);
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  */
addPlanarCallout(json & jsonCalloutDataList,const std::string & priority)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  **/
processClockInfoErrorHelper(const FFDC & ffdc,json & pelJSONFmtCalloutDataList,FFDCData & ffdcUserData)254 void processClockInfoErrorHelper(const FFDC& ffdc,
255                                  json& pelJSONFmtCalloutDataList,
256                                  FFDCData& ffdcUserData)
257 {
258     lg2::info("processClockInfoErrorHelper: FFDC Message[{FFDC_MSG}]",
259               "FFDC_MSG", ffdc.message);
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"] = "REFCLK" + std::to_string(clk_pos);
295         jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
296         jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path;
297         pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData);
298     });
299 }
300 
convertFAPItoPELformat(FFDC & ffdc,json & pelJSONFmtCalloutDataList,FFDCData & ffdcUserData)301 void convertFAPItoPELformat(FFDC& ffdc, json& pelJSONFmtCalloutDataList,
302                             FFDCData& ffdcUserData)
303 {
304     if (ffdc.ffdc_type == FFDC_TYPE_SPARE_CLOCK_INFO)
305     {
306         processClockInfoErrorHelper(ffdc, pelJSONFmtCalloutDataList,
307                                     ffdcUserData);
308         return;
309     }
310 
311     if (ffdc.ffdc_type == FFDC_TYPE_HWP)
312     {
313         // Adding hardware procedures return code details
314         ffdcUserData.emplace_back("HWP_RC", ffdc.hwp_errorinfo.rc);
315         ffdcUserData.emplace_back("HWP_RC_DESC", ffdc.hwp_errorinfo.rc_desc);
316 
317         // Adding hardware procedures required ffdc data for debug
318         for_each(
319             ffdc.hwp_errorinfo.ffdcs_data.begin(),
320             ffdc.hwp_errorinfo.ffdcs_data.end(),
321             [&ffdcUserData](std::pair<std::string, std::string>& ele) -> void {
322             std::string keyWithPrefix("HWP_FFDC_");
323             keyWithPrefix.append(ele.first);
324 
325             ffdcUserData.emplace_back(keyWithPrefix, ele.second);
326         });
327 
328         // Adding hardware callout details
329         int calloutCount = 0;
330         for_each(ffdc.hwp_errorinfo.hwcallouts.begin(),
331                  ffdc.hwp_errorinfo.hwcallouts.end(),
332                  [&ffdcUserData, &calloutCount, &pelJSONFmtCalloutDataList](
333                      const HWCallout& hwCallout) -> void {
334             calloutCount++;
335             std::stringstream keyPrefix;
336             keyPrefix << "HWP_HW_CO_" << std::setfill('0') << std::setw(2)
337                       << calloutCount << "_";
338 
339             ffdcUserData.emplace_back(
340                 std::string(keyPrefix.str()).append("HW_ID"), hwCallout.hwid);
341 
342             ffdcUserData.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, targetInfo);
348 
349             std::string locationCode = std::string(targetInfo.locationCode);
350             ffdcUserData.emplace_back(
351                 std::string(keyPrefix.str()).append("LOC_CODE"), locationCode);
352 
353             std::string physPath = std::string(targetInfo.physDevPath);
354             ffdcUserData.emplace_back(
355                 std::string(keyPrefix.str()).append("PHYS_PATH"), physPath);
356 
357             ffdcUserData.emplace_back(
358                 std::string(keyPrefix.str()).append("CLK_POS"),
359                 std::to_string(hwCallout.clkPos));
360 
361             ffdcUserData.emplace_back(
362                 std::string(keyPrefix.str()).append("CALLOUT_PLANAR"),
363                 (hwCallout.isPlanarCallout == true ? "true" : "false"));
364 
365             std::string pelPriority =
366                 getPelPriority(hwCallout.callout_priority);
367 
368             if (hwCallout.isPlanarCallout)
369             {
370                 addPlanarCallout(pelJSONFmtCalloutDataList, pelPriority);
371             }
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                  [&ffdcUserData, &calloutCount, &pelJSONFmtCalloutDataList](
379                      const CDG_Target& cdg_tgt) -> void {
380             calloutCount++;
381             std::stringstream keyPrefix;
382             keyPrefix << "HWP_CDG_TGT_" << std::setfill('0') << std::setw(2)
383                       << calloutCount << "_";
384 
385             phal::TargetInfo targetInfo;
386             targetInfo.deconfigure = cdg_tgt.deconfigure;
387 
388             phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path, targetInfo);
389 
390             std::string locationCode = std::string(targetInfo.locationCode);
391             ffdcUserData.emplace_back(
392                 std::string(keyPrefix.str()).append("LOC_CODE"), locationCode);
393             std::string physPath = std::string(targetInfo.physDevPath);
394             ffdcUserData.emplace_back(
395                 std::string(keyPrefix.str()).append("PHYS_PATH"), physPath);
396 
397             ffdcUserData.emplace_back(
398                 std::string(keyPrefix.str()).append("CO_REQ"),
399                 (cdg_tgt.callout == true ? "true" : "false"));
400 
401             ffdcUserData.emplace_back(
402                 std::string(keyPrefix.str()).append("CO_PRIORITY"),
403                 cdg_tgt.callout_priority);
404 
405             ffdcUserData.emplace_back(
406                 std::string(keyPrefix.str()).append("DECONF_REQ"),
407                 (cdg_tgt.deconfigure == true ? "true" : "false"));
408 
409             ffdcUserData.emplace_back(
410                 std::string(keyPrefix.str()).append("GUARD_REQ"),
411                 (cdg_tgt.guard == true ? "true" : "false"));
412 
413             ffdcUserData.emplace_back(
414                 std::string(keyPrefix.str()).append("GUARD_TYPE"),
415                 cdg_tgt.guard_type);
416 
417             json jsonCalloutData;
418             jsonCalloutData["LocationCode"] = locationCode;
419             std::string pelPriority = getPelPriority(cdg_tgt.callout_priority);
420             jsonCalloutData["Priority"] = pelPriority;
421 
422             if (targetInfo.mruId != 0)
423             {
424                 jsonCalloutData["MRUs"] = json::array({
425                     {{"ID", targetInfo.mruId}, {"Priority", pelPriority}},
426                 });
427             }
428             jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
429             jsonCalloutData["Guarded"] = cdg_tgt.guard;
430             jsonCalloutData["GuardType"] = cdg_tgt.guard_type;
431             jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path;
432 
433             pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData);
434         });
435 
436         // Adding procedure callout
437         calloutCount = 0;
438         for_each(ffdc.hwp_errorinfo.procedures_callout.begin(),
439                  ffdc.hwp_errorinfo.procedures_callout.end(),
440                  [&ffdcUserData, &calloutCount, &pelJSONFmtCalloutDataList](
441                      const ProcedureCallout& procCallout) -> void {
442             calloutCount++;
443             std::stringstream keyPrefix;
444             keyPrefix << "HWP_PROC_CO_" << std::setfill('0') << std::setw(2)
445                       << calloutCount << "_";
446             ffdcUserData.emplace_back(
447                 std::string(keyPrefix.str()).append("PRIORITY"),
448                 procCallout.callout_priority);
449             ffdcUserData.emplace_back(
450                 std::string(keyPrefix.str()).append("MAINT_PROCEDURE"),
451                 procCallout.proc_callout);
452             json jsonCalloutData;
453             jsonCalloutData["Procedure"] = procCallout.proc_callout;
454             std::string pelPriority =
455                 getPelPriority(procCallout.callout_priority);
456             jsonCalloutData["Priority"] = pelPriority;
457             pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData);
458         });
459     }
460     else if ((ffdc.ffdc_type != FFDC_TYPE_NONE) &&
461              (ffdc.ffdc_type != FFDC_TYPE_UNSUPPORTED))
462     {
463         lg2::error("Unsupported phal FFDC type to create PEL. MSG: {FFDC_MSG}",
464                    "FFDC_MSG", ffdc.message);
465     }
466 }
467 
468 } // namespace phal
469 } // namespace pels
470 } // namespace openpower
471